aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.dir-locals.el9
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml33
-rw-r--r--CONTRIBUTING.md98
-rw-r--r--HOWTO/INSTALL-WIN32.md2
-rw-r--r--HOWTO/INSTALL.md8
-rw-r--r--OTP_VERSION2
-rw-r--r--README.md2
-rw-r--r--bootstrap/bin/start.bootbin5391 -> 5457 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin5391 -> 5457 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11428 -> 11428 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin9612 -> 9188 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bool.beambin15720 -> 15720 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bsm.beambin12632 -> 12600 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_clean.beambin8948 -> 8948 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dead.beambin13092 -> 12992 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dict.beambin5292 -> 5292 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin9524 -> 10072 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_split.beambin2496 -> 2312 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin13700 -> 13768 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin30824 -> 30816 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin32080 -> 32080 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin39028 -> 39580 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.app2
-rw-r--r--bootstrap/lib/compiler/ebin/core_lint.beambin13572 -> 13572 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_parse.beambin57124 -> 62620 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_scan.beambin6636 -> 6636 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_dsetel.beambin7180 -> 7232 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin52400 -> 52532 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_codegen.beambin56552 -> 56552 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin54484 -> 54484 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin46676 -> 46680 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_life.beambin19044 -> 19044 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin31932 -> 31932 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_master.beambin6632 -> 6636 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/auth.beambin6516 -> 6520 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin24808 -> 25132 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log.beambin35832 -> 35836 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin24884 -> 24976 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_server.beambin6624 -> 6624 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_ac.beambin26328 -> 26336 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin10672 -> 11220 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_epmd.beambin7180 -> 7244 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_logger.beambin5932 -> 6004 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file.beambin14456 -> 14476 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global.beambin32244 -> 32244 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin17548 -> 17548 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group.beambin14032 -> 14032 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/heart.beambin5540 -> 5444 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin13792 -> 13796 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin24156 -> 24160 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp_dist.beambin768 -> 872 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_res.beambin14868 -> 14868 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp_dist.beambin7212 -> 7616 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app4
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.appup8
-rw-r--r--bootstrap/lib/kernel/ebin/kernel_config.beambin2764 -> 2764 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_kernel.beambin22968 -> 24856 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin3776 -> 4304 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/rpc.beambin8132 -> 8176 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user.beambin11572 -> 11576 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_drv.beambin11408 -> 11420 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/wrap_log_reader.beambin3320 -> 3320 bytes
-rw-r--r--bootstrap/lib/kernel/include/dist_util.hrl8
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin2828 -> 2828 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin53676 -> 53688 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_server.beambin6984 -> 6992 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_utils.beambin28748 -> 28748 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v8.beambin27488 -> 27488 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin_expand.beambin3156 -> 3916 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin29276 -> 29280 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_eval.beambin30632 -> 30636 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin90264 -> 90276 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin82652 -> 89624 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin26856 -> 26856 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_file_h.beambin4656 -> 4684 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_tty_h.beambin4936 -> 4968 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/escript.beambin17576 -> 17564 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/file_sorter.beambin30400 -> 30400 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filename.beambin14884 -> 15224 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_fsm.beambin9480 -> 9492 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin12448 -> 12452 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_statem.beambin13492 -> 17148 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_pretty.beambin15152 -> 17732 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/log_mf_h.beambin2636 -> 2636 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ms_transform.beambin20460 -> 20488 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/pool.beambin3836 -> 3840 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc.beambin70112 -> 70384 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin76228 -> 76228 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell.beambin30232 -> 30300 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/slave.beambin4852 -> 4856 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sofs.beambin40624 -> 40624 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.app2
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.appup8
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin23616 -> 23696 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor_bridge.beambin2072 -> 2072 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/zip.beambin26632 -> 27304 bytes
-rw-r--r--configure.in58
-rw-r--r--erts/aclocal.m4103
-rw-r--r--erts/configure.in119
-rw-r--r--erts/doc/src/absform.xml1299
-rw-r--r--erts/doc/src/alt_dist.xml1304
-rw-r--r--erts/doc/src/communication.xml67
-rw-r--r--erts/doc/src/crash_dump.xml955
-rw-r--r--erts/doc/src/driver.xml451
-rw-r--r--erts/doc/src/driver_entry.xml641
-rw-r--r--erts/doc/src/epmd.xml445
-rw-r--r--erts/doc/src/erl.xml2236
-rw-r--r--erts/doc/src/erl_dist_protocol.xml1976
-rw-r--r--erts/doc/src/erl_driver.xml4916
-rw-r--r--erts/doc/src/erl_ext_dist.xml891
-rw-r--r--erts/doc/src/erl_nif.xml4271
-rw-r--r--erts/doc/src/erl_prim_loader.xml115
-rw-r--r--erts/doc/src/erl_tracer.xml882
-rw-r--r--erts/doc/src/erlang.xml7551
-rw-r--r--erts/doc/src/erlc.xml274
-rw-r--r--erts/doc/src/erlsrv.xml613
-rw-r--r--erts/doc/src/erts_alloc.xml1086
-rw-r--r--erts/doc/src/escript.xml400
-rw-r--r--erts/doc/src/inet_cfg.xml437
-rw-r--r--erts/doc/src/init.xml226
-rw-r--r--erts/doc/src/introduction.xml56
-rw-r--r--erts/doc/src/match_spec.xml966
-rw-r--r--erts/doc/src/notes.xml1184
-rw-r--r--erts/doc/src/part.xml2
-rw-r--r--erts/doc/src/ref_man.xml8
-rw-r--r--erts/doc/src/run_erl.xml256
-rw-r--r--erts/doc/src/start.xml36
-rw-r--r--erts/doc/src/start_erl.xml201
-rw-r--r--erts/doc/src/time_correction.xml288
-rw-r--r--erts/doc/src/tty.xml67
-rw-r--r--erts/doc/src/werl.xml105
-rw-r--r--erts/doc/src/zlib.xml693
-rw-r--r--erts/emulator/Makefile.in40
-rw-r--r--erts/emulator/beam/atom.c6
-rw-r--r--erts/emulator/beam/atom.h7
-rw-r--r--erts/emulator/beam/atom.names13
-rw-r--r--erts/emulator/beam/beam_bif_load.c1087
-rw-r--r--erts/emulator/beam/beam_bp.c81
-rw-r--r--erts/emulator/beam/beam_bp.h14
-rw-r--r--erts/emulator/beam/beam_debug.c4
-rw-r--r--erts/emulator/beam/beam_emu.c381
-rw-r--r--erts/emulator/beam/beam_load.c201
-rw-r--r--erts/emulator/beam/beam_load.h11
-rw-r--r--erts/emulator/beam/benchmark.c301
-rw-r--r--erts/emulator/beam/benchmark.h295
-rw-r--r--erts/emulator/beam/bif.c6
-rw-r--r--erts/emulator/beam/bif.tab8
-rw-r--r--erts/emulator/beam/big.c11
-rw-r--r--erts/emulator/beam/big.h9
-rw-r--r--erts/emulator/beam/break.c196
-rw-r--r--erts/emulator/beam/copy.c14
-rw-r--r--erts/emulator/beam/dist.c54
-rw-r--r--erts/emulator/beam/erl_afit_alloc.c4
-rw-r--r--erts/emulator/beam/erl_alloc.c12
-rw-r--r--erts/emulator/beam/erl_alloc.h6
-rw-r--r--erts/emulator/beam/erl_alloc.types4
-rw-r--r--erts/emulator/beam/erl_alloc_util.c41
-rw-r--r--erts/emulator/beam/erl_alloc_util.h10
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.c4
-rw-r--r--erts/emulator/beam/erl_bestfit_alloc.c4
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c24
-rw-r--r--erts/emulator/beam/erl_bif_guard.c7
-rw-r--r--erts/emulator/beam/erl_bif_info.c64
-rw-r--r--erts/emulator/beam/erl_bif_trace.c19
-rw-r--r--erts/emulator/beam/erl_db.c32
-rw-r--r--erts/emulator/beam/erl_db.h2
-rw-r--r--erts/emulator/beam/erl_db_hash.c48
-rw-r--r--erts/emulator/beam/erl_db_tree.c4
-rw-r--r--erts/emulator/beam/erl_db_util.c4
-rw-r--r--erts/emulator/beam/erl_db_util.h9
-rw-r--r--erts/emulator/beam/erl_debug.c10
-rw-r--r--erts/emulator/beam/erl_fun.c76
-rw-r--r--erts/emulator/beam/erl_fun.h14
-rw-r--r--erts/emulator/beam/erl_gc.c410
-rw-r--r--erts/emulator/beam/erl_gc.h7
-rw-r--r--erts/emulator/beam/erl_goodfit_alloc.c4
-rw-r--r--erts/emulator/beam/erl_hl_timer.c34
-rw-r--r--erts/emulator/beam/erl_hl_timer.h4
-rw-r--r--erts/emulator/beam/erl_init.c71
-rw-r--r--erts/emulator/beam/erl_instrument.c74
-rw-r--r--erts/emulator/beam/erl_instrument.h4
-rw-r--r--erts/emulator/beam/erl_lock_check.c8
-rw-r--r--erts/emulator/beam/erl_lock_count.c4
-rw-r--r--erts/emulator/beam/erl_map.c48
-rw-r--r--erts/emulator/beam/erl_map.h8
-rw-r--r--erts/emulator/beam/erl_message.c55
-rw-r--r--erts/emulator/beam/erl_message.h11
-rw-r--r--erts/emulator/beam/erl_monitors.c4
-rw-r--r--erts/emulator/beam/erl_msacc.c192
-rw-r--r--erts/emulator/beam/erl_msacc.h71
-rw-r--r--erts/emulator/beam/erl_nif.c284
-rw-r--r--erts/emulator/beam/erl_node_tables.c60
-rw-r--r--erts/emulator/beam/erl_node_tables.h16
-rw-r--r--erts/emulator/beam/erl_port.h2
-rw-r--r--erts/emulator/beam/erl_port_task.c46
-rw-r--r--erts/emulator/beam/erl_process.c690
-rw-r--r--erts/emulator/beam/erl_process.h62
-rw-r--r--erts/emulator/beam/erl_process_dict.c6
-rw-r--r--erts/emulator/beam/erl_process_dict.h6
-rw-r--r--erts/emulator/beam/erl_process_dump.c55
-rw-r--r--erts/emulator/beam/erl_sched_spec_pre_alloc.c2
-rw-r--r--erts/emulator/beam/erl_thr_progress.c9
-rw-r--r--erts/emulator/beam/erl_time_sup.c56
-rw-r--r--erts/emulator/beam/erl_trace.c47
-rw-r--r--erts/emulator/beam/erl_utils.h1
-rw-r--r--erts/emulator/beam/erl_vm.h19
-rw-r--r--erts/emulator/beam/export.c6
-rw-r--r--erts/emulator/beam/export.h8
-rw-r--r--erts/emulator/beam/external.c29
-rw-r--r--erts/emulator/beam/global.h70
-rw-r--r--erts/emulator/beam/hash.c23
-rw-r--r--erts/emulator/beam/hash.h6
-rw-r--r--erts/emulator/beam/index.c13
-rw-r--r--erts/emulator/beam/index.h23
-rw-r--r--erts/emulator/beam/io.c141
-rw-r--r--erts/emulator/beam/module.c5
-rw-r--r--erts/emulator/beam/module.h5
-rw-r--r--erts/emulator/beam/register.c2
-rw-r--r--erts/emulator/beam/register.h2
-rw-r--r--erts/emulator/beam/safe_hash.h5
-rw-r--r--erts/emulator/beam/sys.h168
-rw-r--r--erts/emulator/beam/utils.c197
-rw-r--r--erts/emulator/drivers/common/inet_drv.c108
-rw-r--r--erts/emulator/drivers/unix/unix_efile.c90
-rw-r--r--erts/emulator/hipe/hipe_amd64_asm.m422
-rw-r--r--erts/emulator/hipe/hipe_amd64_bifs.m43
-rw-r--r--erts/emulator/hipe/hipe_amd64_glue.S2
-rw-r--r--erts/emulator/hipe/hipe_arm_asm.m432
-rw-r--r--erts/emulator/hipe/hipe_arm_bifs.m433
-rw-r--r--erts/emulator/hipe/hipe_arm_glue.S4
-rw-r--r--erts/emulator/hipe/hipe_bif0.c8
-rw-r--r--erts/emulator/hipe/hipe_bif1.c754
-rw-r--r--erts/emulator/hipe/hipe_bif1.tab21
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m447
-rw-r--r--erts/emulator/hipe/hipe_gc.c123
-rw-r--r--erts/emulator/hipe/hipe_mkliterals.c6
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.h6
-rw-r--r--erts/emulator/hipe/hipe_native_bif.c14
-rw-r--r--erts/emulator/hipe/hipe_ppc_bifs.m436
-rw-r--r--erts/emulator/hipe/hipe_process.h6
-rw-r--r--erts/emulator/hipe/hipe_risc_gc.h13
-rw-r--r--erts/emulator/hipe/hipe_sparc_bifs.m434
-rw-r--r--erts/emulator/hipe/hipe_stack.h3
-rw-r--r--erts/emulator/hipe/hipe_x86_bifs.m43
-rw-r--r--erts/emulator/hipe/hipe_x86_gc.h17
-rw-r--r--erts/emulator/internal_doc/Tracing.md2
-rw-r--r--erts/emulator/nifs/common/erl_tracer_nif.c2
-rw-r--r--erts/emulator/sys/common/erl_check_io.c10
-rw-r--r--erts/emulator/sys/common/erl_mmap.c8
-rw-r--r--erts/emulator/sys/common/erl_mmap.h5
-rw-r--r--erts/emulator/sys/common/erl_mseg.c27
-rw-r--r--erts/emulator/sys/common/erl_mseg.h4
-rw-r--r--erts/emulator/sys/common/erl_poll.c14
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c117
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys.h3
-rw-r--r--erts/emulator/sys/unix/sys.c103
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c56
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h2
-rw-r--r--erts/emulator/sys/win32/sys.c9
-rw-r--r--erts/emulator/test/Makefile4
-rw-r--r--erts/emulator/test/after_SUITE.erl4
-rw-r--r--erts/emulator/test/alloc_SUITE.erl2
-rw-r--r--erts/emulator/test/bif_SUITE.erl203
-rw-r--r--erts/emulator/test/big_SUITE.erl53
-rw-r--r--erts/emulator/test/big_SUITE_data/borders.dat35
-rw-r--r--erts/emulator/test/binary_SUITE.erl3
-rw-r--r--erts/emulator/test/busy_port_SUITE.erl6
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl6
-rw-r--r--erts/emulator/test/code_SUITE.erl269
-rw-r--r--erts/emulator/test/code_SUITE_data/my_code_test.erl2
-rw-r--r--erts/emulator/test/code_SUITE_data/my_code_test2.erl32
-rw-r--r--erts/emulator/test/dirty_nif_SUITE.erl119
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c29
-rw-r--r--erts/emulator/test/distribution_SUITE.erl178
-rw-r--r--erts/emulator/test/driver_SUITE.erl18
-rw-r--r--erts/emulator/test/driver_SUITE_data/timer_drv.c44
-rw-r--r--erts/emulator/test/emulator_smoke.spec12
-rw-r--r--erts/emulator/test/erl_link_SUITE.erl2
-rw-r--r--erts/emulator/test/estone_SUITE.erl2
-rw-r--r--erts/emulator/test/float_SUITE.erl2
-rw-r--r--erts/emulator/test/guard_SUITE.erl3
-rw-r--r--erts/emulator/test/hipe_SUITE.erl67
-rw-r--r--erts/emulator/test/hipe_SUITE_data/literals.erl26
-rw-r--r--erts/emulator/test/hipe_SUITE_data/ref_cell.erl64
-rw-r--r--erts/emulator/test/long_timers_test.erl188
-rw-r--r--erts/emulator/test/lttng_SUITE.erl2
-rw-r--r--erts/emulator/test/map_SUITE.erl35
-rw-r--r--erts/emulator/test/message_queue_data_SUITE.erl2
-rw-r--r--erts/emulator/test/monitor_SUITE.erl2
-rw-r--r--erts/emulator/test/mtx_SUITE.erl2
-rw-r--r--erts/emulator/test/nif_SUITE.erl211
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c48
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.erl10
-rw-r--r--erts/emulator/test/node_container_SUITE.erl2
-rw-r--r--erts/emulator/test/num_bif_SUITE.erl5
-rw-r--r--erts/emulator/test/old_scheduler_SUITE.erl4
-rw-r--r--erts/emulator/test/port_SUITE.erl221
-rw-r--r--erts/emulator/test/port_SUITE_data/Makefile.src6
-rw-r--r--erts/emulator/test/port_SUITE_data/echo_drv.c31
-rw-r--r--erts/emulator/test/port_SUITE_data/port_test.c16
-rw-r--r--erts/emulator/test/port_trace_SUITE.erl2
-rw-r--r--erts/emulator/test/port_trace_SUITE_data/echo_drv.c39
-rw-r--r--erts/emulator/test/prim_eval_SUITE.erl78
-rw-r--r--erts/emulator/test/process_SUITE.erl29
-rw-r--r--erts/emulator/test/ref_SUITE.erl28
-rw-r--r--erts/emulator/test/scheduler_SUITE.erl73
-rw-r--r--erts/emulator/test/signal_SUITE.erl2
-rw-r--r--erts/emulator/test/smoke_test_SUITE.erl2
-rw-r--r--erts/emulator/test/statistics_SUITE.erl17
-rw-r--r--erts/emulator/test/system_info_SUITE.erl2
-rw-r--r--erts/emulator/test/time_SUITE.erl12
-rw-r--r--erts/emulator/test/timer_bif_SUITE.erl20
-rw-r--r--erts/emulator/test/trace_SUITE.erl32
-rw-r--r--erts/emulator/test/trace_bif_SUITE.erl4
-rw-r--r--erts/emulator/test/trace_call_time_SUITE.erl6
-rw-r--r--erts/emulator/test/trace_local_SUITE.erl8
-rw-r--r--erts/emulator/test/tracer_SUITE.erl2
-rw-r--r--erts/emulator/test/tracer_SUITE_data/tracer_test.c2
-rw-r--r--erts/emulator/test/tracer_test.erl2
-rw-r--r--erts/emulator/test/z_SUITE.erl8
-rw-r--r--erts/etc/common/heart.c40
-rw-r--r--erts/etc/unix/cerl.src18
-rw-r--r--erts/etc/unix/etp-commands.in224
-rw-r--r--erts/etc/unix/format_man_pages2
-rw-r--r--erts/etc/unix/run_erl.c31
-rw-r--r--erts/example/time_compat.erl6
-rw-r--r--erts/include/internal/erl_misc_utils.h29
-rw-r--r--erts/include/internal/erl_printf.h7
-rw-r--r--erts/include/internal/erl_printf_format.h3
-rw-r--r--erts/lib_src/common/erl_printf.c37
-rw-r--r--erts/lib_src/common/ethr_aux.c2
-rw-r--r--erts/lib_src/pthread/ethr_event.c74
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin55752 -> 55808 bytes
-rw-r--r--erts/preloaded/ebin/erl_tracer.beambin2200 -> 2204 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin104816 -> 105140 bytes
-rw-r--r--erts/preloaded/ebin/erts_code_purger.beambin8696 -> 12088 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_code_checker.beambin0 -> 2144 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin10536 -> 11156 bytes
-rw-r--r--erts/preloaded/ebin/erts_literal_area_collector.beambin0 -> 3304 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin50048 -> 50064 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1444 -> 1448 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1312 -> 1448 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin44764 -> 44768 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin76236 -> 76496 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin23152 -> 23156 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin14136 -> 14140 bytes
-rw-r--r--erts/preloaded/src/Makefile4
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl23
-rw-r--r--erts/preloaded/src/erlang.erl34
-rw-r--r--erts/preloaded/src/erts.app.src2
-rw-r--r--erts/preloaded/src/erts_code_purger.erl170
-rw-r--r--erts/preloaded/src/erts_dirty_process_code_checker.erl82
-rw-r--r--erts/preloaded/src/erts_internal.erl55
-rw-r--r--erts/preloaded/src/erts_literal_area_collector.erl113
-rw-r--r--erts/preloaded/src/init.erl13
-rw-r--r--erts/preloaded/src/prim_eval.S43
-rw-r--r--erts/preloaded/src/prim_inet.erl21
-rw-r--r--erts/start_scripts/Makefile5
-rw-r--r--erts/test/install_SUITE.erl46
-rw-r--r--erts/test/run_erl_SUITE.erl19
-rw-r--r--erts/test/system_smoke.spec7
-rw-r--r--erts/test/upgrade_SUITE.erl6
-rw-r--r--erts/test/z_SUITE.erl14
-rw-r--r--erts/vsn.mk4
-rw-r--r--lib/asn1/doc/src/asn1_getting_started.xml2
-rw-r--r--lib/asn1/doc/src/asn1_introduction.xml2
-rw-r--r--lib/asn1/doc/src/notes.xml20
-rw-r--r--lib/asn1/src/asn1_db.erl4
-rw-r--r--lib/asn1/src/asn1ct.erl3
-rw-r--r--lib/asn1/test/asn1_test_lib.erl53
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/Makefile3
-rw-r--r--lib/common_test/doc/src/common_test_app.xml30
-rw-r--r--lib/common_test/doc/src/ct.xml122
-rw-r--r--lib/common_test/doc/src/ct_hooks.xml94
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml84
-rw-r--r--lib/common_test/doc/src/ct_ssh.xml106
-rw-r--r--lib/common_test/doc/src/ct_telnet.xml4
-rw-r--r--lib/common_test/doc/src/ct_testspec.xml84
-rw-r--r--lib/common_test/doc/src/event_handler_chapter.xml6
-rw-r--r--lib/common_test/doc/src/introduction.xml2
-rw-r--r--lib/common_test/doc/src/notes.xml212
-rw-r--r--lib/common_test/doc/src/ref_man.xml1
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml6
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml60
-rw-r--r--lib/common_test/src/Makefile1
-rw-r--r--lib/common_test/src/common_test.app.src3
-rw-r--r--lib/common_test/src/ct.erl115
-rw-r--r--lib/common_test/src/ct_default_gl.erl83
-rw-r--r--lib/common_test/src/ct_framework.erl177
-rw-r--r--lib/common_test/src/ct_gen_conn.erl10
-rw-r--r--lib/common_test/src/ct_groups.erl16
-rw-r--r--lib/common_test/src/ct_hooks.erl143
-rw-r--r--lib/common_test/src/ct_logs.erl108
-rw-r--r--lib/common_test/src/ct_make.erl2
-rw-r--r--lib/common_test/src/ct_run.erl44
-rw-r--r--lib/common_test/src/ct_snmp.erl2
-rw-r--r--lib/common_test/src/ct_telnet.erl90
-rw-r--r--lib/common_test/src/ct_testspec.erl37
-rw-r--r--lib/common_test/src/ct_util.erl18
-rw-r--r--lib/common_test/src/ct_util.hrl1
-rw-r--r--lib/common_test/src/cth_conn_log.erl10
-rw-r--r--lib/common_test/src/cth_log_redirect.erl30
-rw-r--r--lib/common_test/src/cth_surefire.erl56
-rw-r--r--lib/common_test/src/erl2html2.erl2
-rw-r--r--lib/common_test/src/test_server.erl12
-rw-r--r--lib/common_test/src/test_server_ctrl.erl118
-rw-r--r--lib/common_test/src/test_server_gl.erl14
-rw-r--r--lib/common_test/src/test_server_io.erl6
-rw-r--r--lib/common_test/test/Makefile3
-rw-r--r--lib/common_test/test/ct_SUITE.erl53
-rw-r--r--lib/common_test/test/ct_auto_compile_SUITE.erl1
-rw-r--r--lib/common_test/test/ct_config_SUITE.erl53
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl10
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl1203
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_hook_callbacks_SUITE.erl62
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/config_clash_SUITE.erl43
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_end_config_SUITE.erl51
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_init_config_SUITE.erl54
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_init_suite_config_SUITE.erl39
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl124
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl34
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fallback_cth.erl81
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl8
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/prio_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/repeat_SUITE.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/seq_SUITE.erl45
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip.spec8
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_case_SUITE.erl106
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_cth.erl182
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_fail_SUITE.erl53
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_group_SUITE.erl64
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_init_SUITE.erl53
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_end_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_init_tc_cth.erl79
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_req_SUITE.erl53
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl42
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl42
-rw-r--r--lib/common_test/test/ct_log_SUITE.erl134
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/ns.erl6
-rw-r--r--lib/common_test/test/ct_repeat_testrun_SUITE.erl7
-rw-r--r--lib/common_test/test/ct_surefire_SUITE.erl131
-rw-r--r--lib/common_test/test/ct_surefire_SUITE_data/skip_one_case.spec2
-rw-r--r--lib/common_test/test/ct_surefire_SUITE_data/skip_one_suite.spec2
-rw-r--r--lib/common_test/test/ct_test_server_if_1_SUITE.erl3
-rw-r--r--lib/common_test/test/ct_test_support.erl26
-rw-r--r--lib/common_test/test/ct_testspec_2_SUITE.erl82
-rw-r--r--lib/common_test/test_server/ts_run.erl2
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/compile.xml10
-rw-r--r--lib/compiler/doc/src/notes.xml126
-rw-r--r--lib/compiler/doc/src/ref_man.xml2
-rw-r--r--lib/compiler/src/beam_block.erl108
-rw-r--r--lib/compiler/src/beam_bsm.erl31
-rw-r--r--lib/compiler/src/beam_dead.erl7
-rw-r--r--lib/compiler/src/beam_flatten.erl1
-rw-r--r--lib/compiler/src/beam_jump.erl22
-rw-r--r--lib/compiler/src/beam_split.erl3
-rw-r--r--lib/compiler/src/beam_utils.erl73
-rw-r--r--lib/compiler/src/beam_validator.erl8
-rw-r--r--lib/compiler/src/compile.erl65
-rw-r--r--lib/compiler/src/sys_core_fold.erl13
-rw-r--r--lib/compiler/test/beam_block_SUITE.erl49
-rw-r--r--lib/compiler/test/beam_jump_SUITE.erl19
-rw-r--r--lib/compiler/test/beam_reorder_SUITE.erl2
-rw-r--r--lib/compiler/test/beam_type_SUITE.erl2
-rw-r--r--lib/compiler/test/beam_utils_SUITE.erl19
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl25
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl26
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl16
-rw-r--r--lib/compiler/test/guard_SUITE.erl13
-rw-r--r--lib/compiler/test/lc_SUITE.erl9
-rw-r--r--lib/compiler/test/match_SUITE.erl13
-rw-r--r--lib/compiler/test/warnings_SUITE.erl28
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/crypto/c_src/crypto.c171
-rw-r--r--lib/crypto/doc/src/crypto.xml8
-rw-r--r--lib/crypto/doc/src/notes.xml102
-rw-r--r--lib/crypto/src/crypto.erl45
-rw-r--r--lib/crypto/test/crypto_SUITE.erl35
-rw-r--r--lib/crypto/test/old_crypto_SUITE.erl47
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/doc/src/Makefile2
-rw-r--r--lib/debugger/doc/src/i.xml4
-rw-r--r--lib/debugger/doc/src/notes.xml16
-rw-r--r--lib/debugger/src/dbg_iserver.erl40
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl13
-rw-r--r--lib/debugger/src/dbg_wx_view.erl12
-rw-r--r--lib/debugger/src/dbg_wx_win.erl2
-rw-r--r--lib/debugger/vsn.mk2
-rw-r--r--lib/dialyzer/RELEASE_NOTES2
-rw-r--r--lib/dialyzer/doc/manual.txt2
-rw-r--r--lib/dialyzer/doc/src/book.xml2
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml828
-rw-r--r--lib/dialyzer/doc/src/dialyzer_chapter.xml303
-rw-r--r--lib/dialyzer/doc/src/notes.xml130
-rw-r--r--lib/dialyzer/doc/src/part.xml3
-rw-r--r--lib/dialyzer/doc/src/ref_man.xml3
-rw-r--r--lib/dialyzer/src/dialyzer.app.src4
-rw-r--r--lib/dialyzer/src/dialyzer.erl18
-rw-r--r--lib/dialyzer/src/dialyzer.hrl2
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl161
-rw-r--r--lib/dialyzer/src/dialyzer_behaviours.erl15
-rw-r--r--lib/dialyzer/src/dialyzer_callgraph.erl96
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl14
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl4
-rw-r--r--lib/dialyzer/src/dialyzer_codeserver.erl206
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl101
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl159
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_options.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_plt.erl140
-rw-r--r--lib/dialyzer/src/dialyzer_succ_typings.erl35
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl99
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl141
-rw-r--r--lib/dialyzer/test/Makefile1
-rw-r--r--lib/dialyzer/test/abstract_SUITE.erl106
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/dialyzer_common.erl11
-rw-r--r--lib/dialyzer/test/map_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/map_in_guard24
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/opaque_key8
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl9
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl3
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl13
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/array4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/dict18
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/ets6
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/my_queue2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/opaque2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/para10
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/queue6
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/rec4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/simple46
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/timer4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/union8
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/weird6
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/wings8
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/hipe_vectors/hipe_ig_moves.erl83
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/hipe_vectors/hipe_vectors.erl136
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/recrec/cerl.erl4602
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/recrec/core_parse.hrl122
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer.hrl180
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer_dataflow.erl3802
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer_races.erl2494
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/recrec/erl_types.erl5741
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/simple/is_rec.erl6
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/simple/rec_api.erl8
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/simple/simple1_api.erl26
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/timer/timer_use.erl4
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl18
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl14
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl19
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl4
-rw-r--r--lib/dialyzer/test/options2_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/options2_SUITE_data/results/unknown_type2
-rw-r--r--lib/dialyzer/test/options2_SUITE_data/src/unknown_type.erl7
-rw-r--r--lib/dialyzer/test/plt_SUITE.erl47
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/chars4
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/guards2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/anno.erl18
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/chars.erl32
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/ms.erl8
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/doc/src/diameter.xml155
-rw-r--r--lib/diameter/doc/src/diameter_app.xml39
-rw-r--r--lib/diameter/doc/src/diameter_codec.xml56
-rw-r--r--lib/diameter/doc/src/diameter_dict.xml69
-rw-r--r--lib/diameter/doc/src/diameter_examples.xml1
-rw-r--r--lib/diameter/doc/src/diameter_tcp.xml2
-rw-r--r--lib/diameter/doc/src/diameter_transport.xml9
-rw-r--r--lib/diameter/doc/src/diameterc.xml6
-rw-r--r--lib/diameter/doc/src/notes.xml74
-rw-r--r--lib/diameter/examples/code/relay.erl4
-rw-r--r--lib/diameter/src/Makefile2
-rw-r--r--lib/diameter/src/base/diameter.erl3
-rw-r--r--lib/diameter/src/base/diameter_config.erl5
-rw-r--r--lib/diameter/src/base/diameter_lib.erl24
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl71
-rw-r--r--lib/diameter/src/base/diameter_service.erl18
-rw-r--r--lib/diameter/src/base/diameter_sup.erl4
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl283
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl28
-rw-r--r--lib/diameter/src/compiler/diameter_codegen.erl17
-rw-r--r--lib/diameter/src/diameter.appup.src10
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl76
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl104
-rw-r--r--lib/diameter/test/diameter_codec_test.erl2
-rw-r--r--lib/diameter/test/diameter_relay_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl7
-rw-r--r--lib/diameter/test/diameter_util.erl22
-rw-r--r--lib/diameter/vsn.mk4
-rw-r--r--lib/edoc/doc/overview.edoc10
-rw-r--r--lib/edoc/doc/src/notes.xml30
-rw-r--r--lib/edoc/include/edoc_doclet.hrl2
-rw-r--r--lib/edoc/src/edoc.erl2
-rw-r--r--lib/edoc/src/edoc.hrl2
-rw-r--r--lib/edoc/src/edoc_data.erl2
-rw-r--r--lib/edoc/src/edoc_doclet.erl2
-rw-r--r--lib/edoc/src/edoc_extract.erl16
-rw-r--r--lib/edoc/src/edoc_layout.erl2
-rw-r--r--lib/edoc/src/edoc_lib.erl4
-rw-r--r--lib/edoc/src/edoc_macros.erl2
-rw-r--r--lib/edoc/src/edoc_refs.erl2
-rw-r--r--lib/edoc/src/edoc_report.erl2
-rw-r--r--lib/edoc/src/edoc_run.erl2
-rw-r--r--lib/edoc/src/edoc_tags.erl13
-rw-r--r--lib/edoc/src/edoc_types.erl2
-rw-r--r--lib/edoc/src/edoc_types.hrl2
-rw-r--r--lib/edoc/src/edoc_wiki.erl2
-rw-r--r--lib/edoc/src/otpsgml_layout.erl2
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/eldap/doc/src/eldap.xml22
-rw-r--r--lib/eldap/test/Makefile2
-rw-r--r--lib/eldap/test/eldap.cover3
-rw-r--r--lib/erl_docgen/doc/src/notes.xml34
-rwxr-xr-xlib/erl_docgen/priv/bin/xref_mod_app.escript2
-rw-r--r--lib/erl_docgen/priv/dtd/erlref.dtd2
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl4
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl153
-rw-r--r--lib/erl_docgen/vsn.mk2
-rw-r--r--lib/erl_interface/doc/src/book.xml11
-rw-r--r--lib/erl_interface/doc/src/ei.xml1181
-rw-r--r--lib/erl_interface/doc/src/ei_connect.xml1057
-rw-r--r--lib/erl_interface/doc/src/ei_users_guide.xml715
-rw-r--r--lib/erl_interface/doc/src/erl_call.xml221
-rw-r--r--lib/erl_interface/doc/src/erl_connect.xml803
-rw-r--r--lib/erl_interface/doc/src/erl_error.xml94
-rw-r--r--lib/erl_interface/doc/src/erl_eterm.xml795
-rw-r--r--lib/erl_interface/doc/src/erl_format.xml102
-rw-r--r--lib/erl_interface/doc/src/erl_global.xml132
-rw-r--r--lib/erl_interface/doc/src/erl_interface.xml209
-rw-r--r--lib/erl_interface/doc/src/erl_malloc.xml181
-rw-r--r--lib/erl_interface/doc/src/erl_marshal.xml287
-rw-r--r--lib/erl_interface/doc/src/fascicules.xml24
-rw-r--r--lib/erl_interface/doc/src/notes.xml61
-rw-r--r--lib/erl_interface/doc/src/part.xml2
-rw-r--r--lib/erl_interface/doc/src/part_erl_interface.xml5
-rw-r--r--lib/erl_interface/doc/src/ref_man.xml18
-rw-r--r--lib/erl_interface/doc/src/ref_man_ei.xml13
-rw-r--r--lib/erl_interface/doc/src/ref_man_erl_interface.xml5
-rw-r--r--lib/erl_interface/doc/src/registry.xml869
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c44
-rw-r--r--lib/erl_interface/src/misc/ei_portio.h2
-rw-r--r--lib/erl_interface/src/prog/erl_start.c73
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/eunit/doc/src/notes.xml33
-rw-r--r--lib/eunit/include/eunit.hrl6
-rw-r--r--lib/eunit/src/eunit.erl2
-rw-r--r--lib/eunit/src/eunit_autoexport.erl2
-rw-r--r--lib/eunit/src/eunit_data.erl2
-rw-r--r--lib/eunit/src/eunit_lib.erl2
-rw-r--r--lib/eunit/src/eunit_proc.erl2
-rw-r--r--lib/eunit/src/eunit_serial.erl2
-rw-r--r--lib/eunit/src/eunit_server.erl2
-rw-r--r--lib/eunit/src/eunit_striptests.erl2
-rw-r--r--lib/eunit/src/eunit_surefire.erl2
-rw-r--r--lib/eunit/src/eunit_test.erl2
-rw-r--r--lib/eunit/src/eunit_tests.erl2
-rw-r--r--lib/eunit/src/eunit_tty.erl2
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/gs/doc/src/Makefile2
-rw-r--r--lib/gs/doc/src/notes.xml18
-rw-r--r--lib/gs/vsn.mk2
-rw-r--r--lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl16
-rw-r--r--lib/hipe/arm/hipe_arm_registers.erl8
-rw-r--r--lib/hipe/arm/hipe_rtl_to_arm.erl9
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl6
-rw-r--r--lib/hipe/cerl/erl_types.erl403
-rw-r--r--lib/hipe/doc/src/notes.xml113
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl28
-rw-r--r--lib/hipe/icode/hipe_icode.erl2
-rw-r--r--lib/hipe/icode/hipe_icode_call_elim.erl3
-rw-r--r--lib/hipe/icode/hipe_icode_primops.erl8
-rw-r--r--lib/hipe/llvm/hipe_llvm_main.erl14
-rw-r--r--lib/hipe/llvm/hipe_rtl_to_llvm.erl103
-rw-r--r--lib/hipe/main/hipe.erl91
-rw-r--r--lib/hipe/main/hipe.hrl.src12
-rw-r--r--lib/hipe/main/hipe_main.erl2
-rw-r--r--lib/hipe/ppc/hipe_ppc.erl12
-rw-r--r--lib/hipe/ppc/hipe_ppc_assemble.erl7
-rw-r--r--lib/hipe/rtl/hipe_rtl_binary.erl2
-rw-r--r--lib/hipe/rtl/hipe_rtl_binary_match.erl37
-rw-r--r--lib/hipe/sparc/hipe_rtl_to_sparc.erl40
-rw-r--r--lib/hipe/sparc/hipe_sparc_registers.erl8
-rw-r--r--lib/hipe/test/basic_SUITE_data/basic_num_bif.erl217
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_pmatch_bugs.erl48
-rw-r--r--lib/hipe/test/hipe_SUITE.erl6
-rw-r--r--lib/hipe/test/hipe_testsuite_driver.erl2
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_redundant_branch_is_key.erl14
-rw-r--r--lib/hipe/test/opt_verify_SUITE.erl39
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/hipe/x86/hipe_x86_postpass.erl3
-rw-r--r--lib/ic/doc/src/Makefile2
-rw-r--r--lib/ic/doc/src/notes.xml24
-rw-r--r--lib/ic/src/ic_codegen.erl8
-rw-r--r--lib/ic/vsn.mk2
-rw-r--r--lib/inets/doc/src/http_server.xml36
-rw-r--r--lib/inets/doc/src/http_uri.xml4
-rw-r--r--lib/inets/doc/src/httpc.xml8
-rw-r--r--lib/inets/doc/src/httpd.xml18
-rw-r--r--lib/inets/doc/src/mod_auth.xml2
-rw-r--r--lib/inets/doc/src/mod_esi.xml4
-rw-r--r--lib/inets/doc/src/notes.xml218
-rw-r--r--lib/inets/src/ftp/ftp.erl181
-rw-r--r--lib/inets/src/ftp/ftp_progress.erl26
-rw-r--r--lib/inets/src/ftp/ftp_response.erl18
-rw-r--r--lib/inets/src/http_client/httpc.erl102
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl768
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl51
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl4
-rw-r--r--lib/inets/src/http_client/httpc_request.erl6
-rw-r--r--lib/inets/src/http_client/httpc_response.erl38
-rw-r--r--lib/inets/src/http_lib/http_internal.hrl5
-rw-r--r--lib/inets/src/http_lib/http_request.erl18
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl12
-rw-r--r--lib/inets/src/http_server/httpd_example.erl11
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl17
-rw-r--r--lib/inets/src/http_server/httpd_response.erl2
-rw-r--r--lib/inets/src/http_server/mod_auth_server.erl2
-rw-r--r--lib/inets/src/http_server/mod_esi.erl66
-rw-r--r--lib/inets/src/inets_app/inets.appup.src6
-rw-r--r--lib/inets/src/inets_app/inets_lib.erl2
-rw-r--r--lib/inets/test/ftp_SUITE.erl105
-rw-r--r--lib/inets/test/ftp_format_SUITE.erl13
-rw-r--r--lib/inets/test/http_format_SUITE.erl2
-rw-r--r--lib/inets/test/httpc_SUITE.erl108
-rw-r--r--lib/inets/test/httpd_SUITE.erl67
-rw-r--r--lib/inets/test/inets_SUITE.erl14
-rw-r--r--lib/inets/test/inets_socketwrap_SUITE.erl2
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl38
-rw-r--r--lib/inets/vsn.mk4
-rw-r--r--lib/jinterface/doc/src/Makefile2
-rw-r--r--lib/jinterface/doc/src/notes.xml16
-rw-r--r--lib/jinterface/vsn.mk2
-rw-r--r--lib/kernel/doc/src/app.xml6
-rw-r--r--lib/kernel/doc/src/application.xml6
-rw-r--r--lib/kernel/doc/src/auth.xml4
-rw-r--r--lib/kernel/doc/src/code.xml33
-rw-r--r--lib/kernel/doc/src/erl_boot_server.xml4
-rw-r--r--lib/kernel/doc/src/erl_ddll.xml6
-rw-r--r--lib/kernel/doc/src/error_logger.xml30
-rw-r--r--lib/kernel/doc/src/file.xml36
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml12
-rw-r--r--lib/kernel/doc/src/gen_udp.xml6
-rw-r--r--lib/kernel/doc/src/heart.xml19
-rw-r--r--lib/kernel/doc/src/inet.xml45
-rw-r--r--lib/kernel/doc/src/init_stub.xml2
-rw-r--r--lib/kernel/doc/src/kernel_app.xml22
-rw-r--r--lib/kernel/doc/src/net_kernel.xml48
-rw-r--r--lib/kernel/doc/src/notes.xml197
-rw-r--r--lib/kernel/doc/src/os.xml43
-rw-r--r--lib/kernel/doc/src/rpc.xml20
-rw-r--r--lib/kernel/doc/src/zlib_stub.xml2
-rw-r--r--lib/kernel/include/dist_util.hrl8
-rw-r--r--lib/kernel/src/code_server.erl41
-rw-r--r--lib/kernel/src/dist_util.erl71
-rw-r--r--lib/kernel/src/erl_epmd.erl6
-rw-r--r--lib/kernel/src/error_logger.erl10
-rw-r--r--lib/kernel/src/file.erl2
-rw-r--r--lib/kernel/src/gen_sctp.erl2
-rw-r--r--lib/kernel/src/gen_tcp.erl2
-rw-r--r--lib/kernel/src/gen_udp.erl2
-rw-r--r--lib/kernel/src/global_group.erl2
-rw-r--r--lib/kernel/src/heart.erl15
-rw-r--r--lib/kernel/src/inet.erl11
-rw-r--r--lib/kernel/src/inet6_tcp_dist.erl7
-rw-r--r--lib/kernel/src/inet_int.hrl1
-rw-r--r--lib/kernel/src/inet_tcp_dist.erl31
-rw-r--r--lib/kernel/src/kernel.app.src2
-rw-r--r--lib/kernel/src/kernel.appup.src6
-rw-r--r--lib/kernel/src/net_kernel.erl128
-rw-r--r--lib/kernel/src/os.erl103
-rw-r--r--lib/kernel/src/rpc.erl16
-rw-r--r--lib/kernel/test/code_SUITE.erl182
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl163
-rw-r--r--lib/kernel/test/error_logger_SUITE.erl15
-rw-r--r--lib/kernel/test/file_SUITE.erl54
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl148
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl21
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl41
-rw-r--r--lib/kernel/test/init_SUITE.erl61
-rw-r--r--lib/kernel/test/os_SUITE.erl73
-rw-r--r--lib/kernel/test/os_SUITE_data/Makefile.src8
-rw-r--r--lib/kernel/test/os_SUITE_data/my_fds.c9
-rw-r--r--lib/kernel/test/rpc_SUITE.erl14
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/megaco/src/app/megaco.appup.src2
-rw-r--r--lib/megaco/test/megaco_app_test.erl308
-rw-r--r--lib/megaco/test/megaco_appup_test.erl498
-rw-r--r--lib/megaco/vsn.mk2
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap1.xml28
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap8.xml8
-rw-r--r--lib/mnesia/doc/src/Mnesia_overview.xml42
-rw-r--r--lib/mnesia/doc/src/mnesia.xml336
-rw-r--r--lib/mnesia/doc/src/mnesia_frag_hash.xml12
-rw-r--r--lib/mnesia/doc/src/mnesia_registry.xml12
-rw-r--r--lib/mnesia/doc/src/notes.xml89
-rw-r--r--lib/mnesia/doc/src/part.xml2
-rw-r--r--lib/mnesia/doc/src/ref_man.xml2
-rw-r--r--lib/mnesia/src/mnesia.erl15
-rw-r--r--lib/mnesia/src/mnesia_checkpoint.erl7
-rw-r--r--lib/mnesia/src/mnesia_controller.erl7
-rw-r--r--lib/mnesia/src/mnesia_event.erl3
-rw-r--r--lib/mnesia/src/mnesia_loader.erl17
-rw-r--r--lib/mnesia/src/mnesia_log.erl4
-rw-r--r--lib/mnesia/src/mnesia_schema.erl16
-rw-r--r--lib/mnesia/src/mnesia_tm.erl3
-rw-r--r--lib/mnesia/test/ext_test.erl2
-rw-r--r--lib/mnesia/test/mnesia_evil_backup.erl43
-rw-r--r--lib/mnesia/test/mnesia_test_lib.erl1
-rw-r--r--lib/mnesia/test/mnesia_trans_access_test.erl10
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml128
-rw-r--r--lib/observer/doc/src/ttb.xml18
-rwxr-xr-xlib/observer/priv/bin/cdv2
-rw-r--r--lib/observer/priv/bin/cdv.bat2
-rw-r--r--lib/observer/priv/crashdump_viewer.tool2
-rw-r--r--lib/observer/priv/crashdump_viewer/collapsd.gifbin141 -> 0 bytes
-rw-r--r--lib/observer/priv/crashdump_viewer/exploded.gifbin143 -> 0 bytes
-rw-r--r--lib/observer/src/Makefile10
-rw-r--r--lib/observer/src/cdv_bin_cb.erl4
-rw-r--r--lib/observer/src/cdv_detail_wx.erl4
-rw-r--r--lib/observer/src/cdv_ets_cb.erl7
-rw-r--r--lib/observer/src/cdv_mem_cb.erl4
-rw-r--r--lib/observer/src/crashdump_viewer.erl94
-rw-r--r--lib/observer/src/etop.erl24
-rw-r--r--lib/observer/src/etop_txt.erl24
-rw-r--r--lib/observer/src/observer_alloc_wx.erl2
-rw-r--r--lib/observer/src/observer_app_wx.erl10
-rw-r--r--lib/observer/src/observer_lib.erl44
-rw-r--r--lib/observer/src/observer_perf_wx.erl48
-rw-r--r--lib/observer/src/observer_port_wx.erl132
-rw-r--r--lib/observer/src/observer_pro_wx.erl8
-rw-r--r--lib/observer/src/observer_procinfo.erl6
-rw-r--r--lib/observer/src/observer_tv_wx.erl31
-rw-r--r--lib/observer/src/observer_wx.erl20
-rw-r--r--lib/observer/src/ttb.erl9
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl10
-rw-r--r--lib/observer/test/observer_SUITE.erl19
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/configure.in24
-rw-r--r--lib/odbc/doc/src/notes.xml41
-rw-r--r--lib/odbc/test/odbc_connect_SUITE.erl188
-rw-r--r--lib/odbc/test/odbc_data_type_SUITE.erl421
-rw-r--r--lib/odbc/test/odbc_query_SUITE.erl464
-rw-r--r--lib/odbc/test/odbc_start_SUITE.erl37
-rw-r--r--lib/odbc/test/odbc_test_lib.erl6
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/os_mon/doc/src/notes.xml15
-rw-r--r--lib/os_mon/src/memsup.erl3
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/parsetools/doc/src/notes.xml36
-rw-r--r--lib/parsetools/doc/src/yecc.xml2
-rw-r--r--lib/parsetools/include/leexinc.hrl35
-rw-r--r--lib/parsetools/src/leex.erl4
-rw-r--r--lib/parsetools/src/yecc.erl3
-rw-r--r--lib/parsetools/test/leex_SUITE.erl52
-rw-r--r--lib/parsetools/test/yecc_SUITE.erl10
-rw-r--r--lib/parsetools/vsn.mk2
-rw-r--r--lib/public_key/doc/src/notes.xml45
-rw-r--r--lib/public_key/doc/src/public_key.xml70
-rw-r--r--lib/public_key/doc/src/public_key_app.xml4
-rw-r--r--lib/public_key/doc/src/using_public_key.xml253
-rw-r--r--lib/public_key/src/public_key.erl215
-rw-r--r--lib/public_key/test/pbe_SUITE.erl7
-rw-r--r--lib/public_key/test/public_key.cover2
-rw-r--r--lib/public_key/test/public_key_SUITE.erl205
-rw-r--r--lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_cn.pem17
-rw-r--r--lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem14
-rw-r--r--lib/public_key/test/public_key_SUITE_data/verify_hostname.conf16
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/reltool/doc/src/notes.xml52
-rw-r--r--lib/reltool/doc/src/part.xml2
-rw-r--r--lib/reltool/doc/src/ref_man.xml2
-rw-r--r--lib/reltool/doc/src/reltool.xml8
-rw-r--r--lib/reltool/doc/src/reltool_intro.xml4
-rw-r--r--lib/reltool/src/reltool.hrl28
-rw-r--r--lib/reltool/src/reltool_server.erl151
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl37
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app7
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl5
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app8
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl5
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app7
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl5
-rw-r--r--lib/reltool/vsn.mk2
-rw-r--r--lib/runtime_tools/doc/src/LTTng.xml4
-rw-r--r--lib/runtime_tools/doc/src/dbg.xml4
-rw-r--r--lib/runtime_tools/doc/src/notes.xml61
-rw-r--r--lib/runtime_tools/src/dbg.erl2
-rw-r--r--lib/runtime_tools/src/msacc.erl2
-rw-r--r--lib/runtime_tools/src/observer_backend.erl60
-rw-r--r--lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c2
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/alarm_handler.xml4
-rw-r--r--lib/sasl/doc/src/appup.xml10
-rw-r--r--lib/sasl/doc/src/error_logging.xml18
-rw-r--r--lib/sasl/doc/src/notes.xml95
-rw-r--r--lib/sasl/doc/src/part.xml2
-rw-r--r--lib/sasl/doc/src/rb.xml6
-rw-r--r--lib/sasl/doc/src/ref_man.xml2
-rw-r--r--lib/sasl/doc/src/rel.xml6
-rw-r--r--lib/sasl/doc/src/release_handler.xml20
-rw-r--r--lib/sasl/doc/src/sasl_app.xml24
-rw-r--r--lib/sasl/doc/src/sasl_intro.xml2
-rw-r--r--lib/sasl/doc/src/script.xml2
-rw-r--r--lib/sasl/doc/src/systools.xml14
-rw-r--r--lib/sasl/src/sasl.app.src2
-rw-r--r--lib/sasl/src/systools_make.erl175
-rw-r--r--lib/sasl/src/systools_relup.erl152
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl18
-rwxr-xr-xlib/sasl/test/release_handler_SUITE_data/start3
-rwxr-xr-xlib/sasl/test/release_handler_SUITE_data/start_client3
-rw-r--r--lib/sasl/test/rh_test_lib.erl2
-rw-r--r--lib/sasl/test/systools_SUITE.erl118
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/snmp/doc/src/notes.xml50
-rw-r--r--lib/snmp/src/agent/snmpa_conf.erl16
-rw-r--r--lib/snmp/src/app/snmp.app.src14
-rw-r--r--lib/snmp/src/app/snmp.appup.src8
-rw-r--r--lib/snmp/src/app/snmp.erl80
-rw-r--r--lib/snmp/src/compile/snmpc.erl13
-rw-r--r--lib/snmp/src/compile/snmpc_lib.erl4
-rw-r--r--lib/snmp/src/compile/snmpc_mib_gram.yrl6
-rw-r--r--lib/snmp/src/compile/snmpc_misc.erl20
-rw-r--r--lib/snmp/test/modules.mk1
-rw-r--r--lib/snmp/test/snmp_SUITE.erl4
-rw-r--r--lib/snmp/test/snmp_agent_conf_test.erl210
-rw-r--r--lib/snmp/test/snmp_agent_test.erl26
-rw-r--r--lib/snmp/test/snmp_app_test.erl381
-rw-r--r--lib/snmp/test/snmp_compiler_test.erl32
-rw-r--r--lib/snmp/test/snmp_manager_test.erl26
-rw-r--r--lib/snmp/test/snmp_test_data/OTP14145-MIB.mib44
-rw-r--r--lib/snmp/test/snmp_to_snmpnet_SUITE.erl10
-rw-r--r--lib/snmp/vsn.mk4
-rw-r--r--lib/ssh/doc/src/introduction.xml2
-rw-r--r--lib/ssh/doc/src/notes.xml387
-rw-r--r--lib/ssh/doc/src/ssh.xml56
-rw-r--r--lib/ssh/doc/src/ssh_app.xml4
-rw-r--r--lib/ssh/doc/src/ssh_channel.xml2
-rw-r--r--lib/ssh/doc/src/ssh_protocol.xml4
-rw-r--r--lib/ssh/doc/src/using_ssh.xml4
-rw-r--r--lib/ssh/src/Makefile2
-rw-r--r--lib/ssh/src/ssh.app.src7
-rw-r--r--lib/ssh/src/ssh.appup.src6
-rw-r--r--lib/ssh/src/ssh.erl175
-rw-r--r--lib/ssh/src/ssh.hrl8
-rw-r--r--lib/ssh/src/ssh_acceptor.erl2
-rw-r--r--lib/ssh/src/ssh_auth.erl359
-rw-r--r--lib/ssh/src/ssh_channel.erl9
-rw-r--r--lib/ssh/src/ssh_cli.erl46
-rw-r--r--lib/ssh/src/ssh_connection.erl5
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl260
-rw-r--r--lib/ssh/src/ssh_dbg.erl89
-rw-r--r--lib/ssh/src/ssh_dbg.hrl27
-rw-r--r--lib/ssh/src/ssh_info.erl2
-rw-r--r--lib/ssh/src/ssh_io.erl52
-rw-r--r--lib/ssh/src/ssh_sftpd.erl63
-rw-r--r--lib/ssh/src/ssh_sftpd_file_api.erl4
-rw-r--r--lib/ssh/src/ssh_transport.erl170
-rw-r--r--lib/ssh/test/Makefile3
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl92
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_encode_decode.erl372
-rw-r--r--lib/ssh/test/ssh.cover1
-rw-r--r--lib/ssh/test/ssh_algorithms_SUITE.erl50
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl148
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE.erl4
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl215
-rw-r--r--lib/ssh/test/ssh_eqc_event_handler.erl43
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl166
-rw-r--r--lib/ssh/test/ssh_property_test_SUITE.erl10
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl74
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test4
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test.moduli5
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE.erl40
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl24
-rw-r--r--lib/ssh/test/ssh_sftpd_SUITE.erl163
-rw-r--r--lib/ssh/test/ssh_test_lib.erl153
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl147
-rw-r--r--lib/ssh/test/ssh_trpt_test_lib.erl2
-rw-r--r--lib/ssh/test/ssh_upgrade_SUITE.erl4
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml287
-rw-r--r--lib/ssl/doc/src/ssl.xml41
-rw-r--r--lib/ssl/doc/src/ssl_app.xml10
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache_api.xml2
-rw-r--r--lib/ssl/doc/src/ssl_distribution.xml6
-rw-r--r--lib/ssl/doc/src/ssl_session_cache_api.xml31
-rw-r--r--lib/ssl/src/Makefile20
-rw-r--r--lib/ssl/src/dtls_connection.erl745
-rw-r--r--lib/ssl/src/dtls_connection.hrl20
-rw-r--r--lib/ssl/src/dtls_connection_sup.erl2
-rw-r--r--lib/ssl/src/dtls_handshake.erl575
-rw-r--r--lib/ssl/src/dtls_handshake.hrl15
-rw-r--r--lib/ssl/src/dtls_record.erl391
-rw-r--r--lib/ssl/src/dtls_record.hrl9
-rw-r--r--lib/ssl/src/dtls_socket.erl155
-rw-r--r--lib/ssl/src/dtls_udp_listener.erl249
-rw-r--r--lib/ssl/src/dtls_udp_sup.erl62
-rw-r--r--lib/ssl/src/dtls_v1.erl27
-rw-r--r--lib/ssl/src/ssl.app.src20
-rw-r--r--lib/ssl/src/ssl.appup.src3
-rw-r--r--lib/ssl/src/ssl.erl339
-rw-r--r--lib/ssl/src/ssl_admin_sup.erl95
-rw-r--r--lib/ssl/src/ssl_alert.erl13
-rw-r--r--lib/ssl/src/ssl_alert.hrl2
-rw-r--r--lib/ssl/src/ssl_certificate.erl52
-rw-r--r--lib/ssl/src/ssl_cipher.erl142
-rw-r--r--lib/ssl/src/ssl_config.erl54
-rw-r--r--lib/ssl/src/ssl_connection.erl801
-rw-r--r--lib/ssl/src/ssl_connection.hrl24
-rw-r--r--lib/ssl/src/ssl_connection_sup.erl101
-rw-r--r--lib/ssl/src/ssl_crl.erl64
-rw-r--r--lib/ssl/src/ssl_crl_cache.erl2
-rw-r--r--lib/ssl/src/ssl_crl_cache_api.erl2
-rw-r--r--lib/ssl/src/ssl_dist_admin_sup.erl74
-rw-r--r--lib/ssl/src/ssl_dist_connection_sup.erl79
-rw-r--r--lib/ssl/src/ssl_dist_sup.erl42
-rw-r--r--lib/ssl/src/ssl_handshake.erl370
-rw-r--r--lib/ssl/src/ssl_handshake.hrl3
-rw-r--r--lib/ssl/src/ssl_internal.hrl12
-rw-r--r--lib/ssl/src/ssl_listen_tracker_sup.erl4
-rw-r--r--lib/ssl/src/ssl_manager.erl180
-rw-r--r--lib/ssl/src/ssl_pem_cache.erl266
-rw-r--r--lib/ssl/src/ssl_pkix_db.erl135
-rw-r--r--lib/ssl/src/ssl_record.erl542
-rw-r--r--lib/ssl/src/ssl_record.hrl42
-rw-r--r--lib/ssl/src/ssl_sup.erl76
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl2
-rw-r--r--lib/ssl/src/tls_connection.erl657
-rw-r--r--lib/ssl/src/tls_handshake.erl127
-rw-r--r--lib/ssl/src/tls_record.erl419
-rw-r--r--lib/ssl/src/tls_socket.erl (renamed from lib/ssl/src/ssl_socket.erl)93
-rw-r--r--lib/ssl/src/tls_v1.erl53
-rw-r--r--lib/ssl/test/make_certs.erl6
-rw-r--r--lib/ssl/test/ssl.spec3
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE.erl320
-rw-r--r--lib/ssl/test/ssl_alpn_handshake_SUITE.erl4
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl269
-rw-r--r--lib/ssl/test/ssl_bench_SUITE.erl102
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl176
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl98
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl350
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl13
-rw-r--r--lib/ssl/test/ssl_npn_handshake_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl28
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl66
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl11
-rw-r--r--lib/ssl/test/ssl_pem_cache_SUITE.erl11
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl4
-rw-r--r--lib/ssl/test/ssl_sni_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_test_lib.erl357
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl121
-rw-r--r--lib/ssl/test/ssl_upgrade_SUITE.erl22
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/assert_hrl.xml2
-rw-r--r--lib/stdlib/doc/src/dets.xml2
-rw-r--r--lib/stdlib/doc/src/ets.xml8
-rw-r--r--lib/stdlib/doc/src/filename.xml29
-rw-r--r--lib/stdlib/doc/src/gb_sets.xml2
-rw-r--r--lib/stdlib/doc/src/gb_trees.xml2
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml901
-rw-r--r--lib/stdlib/doc/src/introduction.xml2
-rw-r--r--lib/stdlib/doc/src/maps.xml2
-rw-r--r--lib/stdlib/doc/src/notes.xml225
-rw-r--r--lib/stdlib/doc/src/orddict.xml2
-rw-r--r--lib/stdlib/doc/src/proc_lib.xml4
-rw-r--r--lib/stdlib/doc/src/queue.xml2
-rw-r--r--lib/stdlib/doc/src/rand.xml2
-rw-r--r--lib/stdlib/doc/src/sets.xml2
-rw-r--r--lib/stdlib/doc/src/shell_default.xml2
-rw-r--r--lib/stdlib/doc/src/stdlib_app.xml8
-rw-r--r--lib/stdlib/doc/src/sys.xml6
-rw-r--r--lib/stdlib/doc/src/timer.xml2
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml8
-rw-r--r--lib/stdlib/doc/src/zip.xml6
-rw-r--r--lib/stdlib/src/beam_lib.erl2
-rw-r--r--lib/stdlib/src/edlin_expand.erl95
-rw-r--r--lib/stdlib/src/erl_eval.erl1
-rw-r--r--lib/stdlib/src/erl_parse.yrl42
-rw-r--r--lib/stdlib/src/escript.erl85
-rw-r--r--lib/stdlib/src/ets.erl8
-rw-r--r--lib/stdlib/src/filename.erl38
-rw-r--r--lib/stdlib/src/gen_event.erl4
-rw-r--r--lib/stdlib/src/gen_statem.erl1537
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl131
-rw-r--r--lib/stdlib/src/ms_transform.erl2
-rw-r--r--lib/stdlib/src/qlc.erl6
-rw-r--r--lib/stdlib/src/shell.erl4
-rw-r--r--lib/stdlib/src/stdlib.appup.src6
-rw-r--r--lib/stdlib/src/supervisor.erl4
-rw-r--r--lib/stdlib/src/zip.erl146
-rw-r--r--lib/stdlib/test/base64_SUITE.erl41
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE.erl86
-rw-r--r--lib/stdlib/test/escript_SUITE.erl15
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/two_lines2
-rw-r--r--lib/stdlib/test/ets_SUITE.erl295
-rw-r--r--lib/stdlib/test/filename_SUITE.erl71
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl408
-rw-r--r--lib/stdlib/test/io_SUITE.erl241
-rw-r--r--lib/stdlib/test/ms_transform_SUITE.erl2
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl18
-rw-r--r--lib/stdlib/test/rand_SUITE.erl32
-rw-r--r--lib/stdlib/test/shell_SUITE.erl19
-rw-r--r--lib/stdlib/test/tar_SUITE.erl26
-rw-r--r--lib/stdlib/test/zip_SUITE.erl86
-rw-r--r--lib/stdlib/test/zip_SUITE_data/exploit.zipbin0 -> 797 bytes
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/doc/specs/.gitignore1
-rw-r--r--lib/syntax_tools/doc/src/Makefile8
-rw-r--r--lib/syntax_tools/doc/src/notes.xml64
-rw-r--r--lib/syntax_tools/doc/src/specs.xml14
-rw-r--r--lib/syntax_tools/src/epp_dodger.erl2
-rw-r--r--lib/syntax_tools/src/erl_comment_scan.erl12
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl23
-rw-r--r--lib/syntax_tools/src/erl_recomment.erl6
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl6
-rw-r--r--lib/syntax_tools/src/erl_syntax_lib.erl6
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl15
-rw-r--r--lib/syntax_tools/src/igor.erl8
-rw-r--r--lib/syntax_tools/src/merl_tests.erl15
-rw-r--r--lib/syntax_tools/src/merl_transform.erl13
-rw-r--r--lib/syntax_tools/src/prettypr.erl2
-rw-r--r--lib/syntax_tools/test/merl_SUITE.erl21
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl34
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl13
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/tools/doc/src/erlang_mode.xml9
-rw-r--r--lib/tools/doc/src/notes.xml98
-rw-r--r--lib/tools/doc/src/xref_chapter.xml6
-rw-r--r--lib/tools/emacs/Makefile2
-rw-r--r--lib/tools/emacs/erlang-edoc.el172
-rw-r--r--lib/tools/emacs/erlang-skels.el169
-rw-r--r--lib/tools/emacs/erlang-start.el15
-rw-r--r--lib/tools/emacs/erlang-test.el106
-rw-r--r--lib/tools/emacs/erlang.el3636
-rw-r--r--lib/tools/emacs/erldoc.el508
-rw-r--r--lib/tools/emacs/test.erl.indented810
-rw-r--r--lib/tools/emacs/test.erl.orig210
-rw-r--r--lib/tools/examples/xref_examples.erl2
-rw-r--r--lib/tools/src/fprof.erl17
-rw-r--r--lib/tools/src/make.erl2
-rw-r--r--lib/tools/src/tags.erl2
-rw-r--r--lib/tools/src/tools.app.src4
-rw-r--r--lib/tools/src/xref_base.erl4
-rw-r--r--lib/tools/test/cover_SUITE.erl33
-rw-r--r--lib/tools/test/cover_SUITE_data/otp_6115/f1.erl15
-rw-r--r--lib/tools/test/cover_SUITE_data/otp_6115/f2.erl14
-rw-r--r--lib/tools/test/make_SUITE_data/test1.erl2
-rw-r--r--lib/tools/test/xref_SUITE.erl17
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/typer/doc/src/notes.xml14
-rw-r--r--lib/typer/src/typer.app.src4
-rw-r--r--lib/typer/src/typer.erl24
-rw-r--r--lib/typer/vsn.mk2
-rw-r--r--lib/wx/api_gen/gl_gen.erl33
-rw-r--r--lib/wx/api_gen/gl_gen_c.erl31
-rw-r--r--lib/wx/api_gen/gl_gen_erl.erl55
-rw-r--r--lib/wx/api_gen/glapi.conf6
-rw-r--r--lib/wx/api_gen/wx_doxygen.conf2
-rw-r--r--lib/wx/api_gen/wx_extra/added_func.h11
-rw-r--r--lib/wx/api_gen/wx_extra/wxListCtrl.erl62
-rw-r--r--lib/wx/api_gen/wx_extra/wxXmlResource.erl6
-rw-r--r--lib/wx/api_gen/wx_gen.erl34
-rw-r--r--lib/wx/api_gen/wx_gen_cpp.erl10
-rw-r--r--lib/wx/api_gen/wx_gen_erl.erl13
-rw-r--r--lib/wx/api_gen/wxapi.conf15
-rw-r--r--lib/wx/c_src/gen/gl_funcs.cpp1052
-rw-r--r--lib/wx/c_src/gen/wxe_events.cpp17
-rw-r--r--lib/wx/c_src/gen/wxe_funcs.cpp40
-rw-r--r--lib/wx/c_src/gen/wxe_macros.h6659
-rw-r--r--lib/wx/c_src/wxe_main.cpp6
-rw-r--r--lib/wx/doc/specs/.gitignore1
-rw-r--r--lib/wx/doc/src/Makefile6
-rw-r--r--lib/wx/doc/src/notes.xml46
-rw-r--r--lib/wx/doc/src/specs.xml232
-rw-r--r--lib/wx/examples/demo/demo.erl19
-rw-r--r--lib/wx/include/wx.hrl465
-rw-r--r--lib/wx/src/gen/gl.erl1223
-rw-r--r--lib/wx/src/gen/glu.erl4
-rw-r--r--lib/wx/src/gen/wxArtProvider.erl8
-rw-r--r--lib/wx/src/gen/wxAuiManager.erl8
-rw-r--r--lib/wx/src/gen/wxAuiNotebook.erl78
-rw-r--r--lib/wx/src/gen/wxAuiPaneInfo.erl4
-rw-r--r--lib/wx/src/gen/wxBitmap.erl12
-rw-r--r--lib/wx/src/gen/wxBitmapButton.erl69
-rw-r--r--lib/wx/src/gen/wxButton.erl54
-rw-r--r--lib/wx/src/gen/wxCalendarCtrl.erl50
-rw-r--r--lib/wx/src/gen/wxCheckBox.erl58
-rw-r--r--lib/wx/src/gen/wxCheckListBox.erl19
-rw-r--r--lib/wx/src/gen/wxChoice.erl73
-rw-r--r--lib/wx/src/gen/wxChoicebook.erl60
-rw-r--r--lib/wx/src/gen/wxColourDialog.erl72
-rw-r--r--lib/wx/src/gen/wxColourPickerCtrl.erl29
-rw-r--r--lib/wx/src/gen/wxComboBox.erl84
-rw-r--r--lib/wx/src/gen/wxCommandEvent.erl2
-rw-r--r--lib/wx/src/gen/wxControl.erl54
-rw-r--r--lib/wx/src/gen/wxControlWithItems.erl66
-rw-r--r--lib/wx/src/gen/wxDC.erl20
-rw-r--r--lib/wx/src/gen/wxDatePickerCtrl.erl27
-rw-r--r--lib/wx/src/gen/wxDialog.erl64
-rw-r--r--lib/wx/src/gen/wxDirDialog.erl76
-rw-r--r--lib/wx/src/gen/wxDirPickerCtrl.erl29
-rw-r--r--lib/wx/src/gen/wxDropFilesEvent.erl89
-rw-r--r--lib/wx/src/gen/wxFileDataObject.erl2
-rw-r--r--lib/wx/src/gen/wxFileDialog.erl82
-rw-r--r--lib/wx/src/gen/wxFilePickerCtrl.erl29
-rw-r--r--lib/wx/src/gen/wxFindReplaceData.erl4
-rw-r--r--lib/wx/src/gen/wxFindReplaceDialog.erl80
-rw-r--r--lib/wx/src/gen/wxFont.erl4
-rw-r--r--lib/wx/src/gen/wxFontDialog.erl72
-rw-r--r--lib/wx/src/gen/wxFontPickerCtrl.erl27
-rw-r--r--lib/wx/src/gen/wxFrame.erl68
-rw-r--r--lib/wx/src/gen/wxGLCanvas.erl50
-rw-r--r--lib/wx/src/gen/wxGauge.erl50
-rw-r--r--lib/wx/src/gen/wxGenericDirCtrl.erl58
-rw-r--r--lib/wx/src/gen/wxGraphicsContext.erl12
-rw-r--r--lib/wx/src/gen/wxGrid.erl84
-rw-r--r--lib/wx/src/gen/wxGridCellBoolEditor.erl2
-rw-r--r--lib/wx/src/gen/wxGridCellChoiceEditor.erl2
-rw-r--r--lib/wx/src/gen/wxGridCellFloatEditor.erl2
-rw-r--r--lib/wx/src/gen/wxGridCellFloatRenderer.erl2
-rw-r--r--lib/wx/src/gen/wxGridCellNumberEditor.erl2
-rw-r--r--lib/wx/src/gen/wxGridCellTextEditor.erl2
-rw-r--r--lib/wx/src/gen/wxHtmlEasyPrinting.erl24
-rw-r--r--lib/wx/src/gen/wxHtmlWindow.erl78
-rw-r--r--lib/wx/src/gen/wxIcon.erl4
-rw-r--r--lib/wx/src/gen/wxIconBundle.erl4
-rw-r--r--lib/wx/src/gen/wxImage.erl34
-rw-r--r--lib/wx/src/gen/wxListBox.erl75
-rw-r--r--lib/wx/src/gen/wxListCtrl.erl148
-rw-r--r--lib/wx/src/gen/wxListItem.erl2
-rw-r--r--lib/wx/src/gen/wxListView.erl50
-rw-r--r--lib/wx/src/gen/wxListbook.erl60
-rw-r--r--lib/wx/src/gen/wxLocale.erl20
-rw-r--r--lib/wx/src/gen/wxMDIChildFrame.erl90
-rw-r--r--lib/wx/src/gen/wxMDIClientWindow.erl50
-rw-r--r--lib/wx/src/gen/wxMDIParentFrame.erl55
-rw-r--r--lib/wx/src/gen/wxMenu.erl56
-rw-r--r--lib/wx/src/gen/wxMenuBar.erl63
-rw-r--r--lib/wx/src/gen/wxMenuItem.erl6
-rw-r--r--lib/wx/src/gen/wxMessageDialog.erl76
-rw-r--r--lib/wx/src/gen/wxMiniFrame.erl55
-rw-r--r--lib/wx/src/gen/wxMultiChoiceDialog.erl76
-rw-r--r--lib/wx/src/gen/wxNotebook.erl60
-rw-r--r--lib/wx/src/gen/wxPanel.erl50
-rw-r--r--lib/wx/src/gen/wxPasswordEntryDialog.erl64
-rw-r--r--lib/wx/src/gen/wxPickerBase.erl50
-rw-r--r--lib/wx/src/gen/wxPopupTransientWindow.erl69
-rw-r--r--lib/wx/src/gen/wxPopupWindow.erl50
-rw-r--r--lib/wx/src/gen/wxPreviewCanvas.erl64
-rw-r--r--lib/wx/src/gen/wxPreviewControlBar.erl69
-rw-r--r--lib/wx/src/gen/wxPreviewFrame.erl47
-rw-r--r--lib/wx/src/gen/wxPrintData.erl2
-rw-r--r--lib/wx/src/gen/wxPrintDialog.erl72
-rw-r--r--lib/wx/src/gen/wxPrinter.erl2
-rw-r--r--lib/wx/src/gen/wxProgressDialog.erl76
-rw-r--r--lib/wx/src/gen/wxRadioBox.erl29
-rw-r--r--lib/wx/src/gen/wxRadioButton.erl58
-rw-r--r--lib/wx/src/gen/wxSashLayoutWindow.erl53
-rw-r--r--lib/wx/src/gen/wxSashWindow.erl50
-rw-r--r--lib/wx/src/gen/wxScrollBar.erl50
-rw-r--r--lib/wx/src/gen/wxScrolledWindow.erl69
-rw-r--r--lib/wx/src/gen/wxSingleChoiceDialog.erl76
-rw-r--r--lib/wx/src/gen/wxSlider.erl50
-rw-r--r--lib/wx/src/gen/wxSpinButton.erl50
-rw-r--r--lib/wx/src/gen/wxSpinCtrl.erl52
-rw-r--r--lib/wx/src/gen/wxSplashScreen.erl47
-rw-r--r--lib/wx/src/gen/wxSplitterWindow.erl50
-rw-r--r--lib/wx/src/gen/wxStaticBitmap.erl50
-rw-r--r--lib/wx/src/gen/wxStaticBox.erl58
-rw-r--r--lib/wx/src/gen/wxStaticLine.erl50
-rw-r--r--lib/wx/src/gen/wxStaticText.erl62
-rw-r--r--lib/wx/src/gen/wxStatusBar.erl58
-rw-r--r--lib/wx/src/gen/wxStyledTextCtrl.erl113
-rw-r--r--lib/wx/src/gen/wxSystemOptions.erl12
-rw-r--r--lib/wx/src/gen/wxTextCtrl.erl64
-rw-r--r--lib/wx/src/gen/wxTextDataObject.erl2
-rw-r--r--lib/wx/src/gen/wxTextEntryDialog.erl78
-rw-r--r--lib/wx/src/gen/wxToggleButton.erl58
-rw-r--r--lib/wx/src/gen/wxToolBar.erl74
-rw-r--r--lib/wx/src/gen/wxToolTip.erl4
-rw-r--r--lib/wx/src/gen/wxToolbook.erl60
-rw-r--r--lib/wx/src/gen/wxTopLevelWindow.erl52
-rw-r--r--lib/wx/src/gen/wxTreeCtrl.erl68
-rw-r--r--lib/wx/src/gen/wxTreeEvent.erl2
-rw-r--r--lib/wx/src/gen/wxTreebook.erl64
-rw-r--r--lib/wx/src/gen/wxUpdateUIEvent.erl2
-rw-r--r--lib/wx/src/gen/wxWindow.erl90
-rw-r--r--lib/wx/src/gen/wxXmlResource.erl40
-rw-r--r--lib/wx/src/gen/wx_misc.erl6
-rw-r--r--lib/wx/src/gen/wxe_debug.hrl6659
-rw-r--r--lib/wx/src/gen/wxe_funcs.hrl6659
-rw-r--r--lib/wx/src/wx_object.erl84
-rw-r--r--lib/wx/src/wxe.hrl2
-rw-r--r--lib/wx/test/wx_opengl_SUITE.erl2
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--lib/xmerl/doc/src/notes.xml91
-rw-r--r--lib/xmerl/src/xmerl_eventp.erl84
-rw-r--r--lib/xmerl/src/xmerl_sax_old_dom.erl7
-rw-r--r--lib/xmerl/src/xmerl_sax_parser.erl198
-rw-r--r--lib/xmerl/src/xmerl_sax_parser.hrl11
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_base.erlsrc411
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_latin1.erlsrc38
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_list.erlsrc21
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_utf16be.erlsrc50
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_utf16le.erlsrc50
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_utf8.erlsrc50
-rw-r--r--lib/xmerl/src/xmerl_sax_simple_dom.erl5
-rw-r--r--lib/xmerl/src/xmerl_scan.erl38
-rw-r--r--lib/xmerl/src/xmerl_xpath.erl30
-rw-r--r--lib/xmerl/src/xmerl_xs.erl7
-rw-r--r--lib/xmerl/src/xmerl_xsd.erl13
-rw-r--r--lib/xmerl/test/Makefile7
-rw-r--r--lib/xmerl/test/xmerl_SUITE.erl49
-rw-r--r--lib/xmerl/test/xmerl_sax_SUITE.erl84
-rw-r--r--lib/xmerl/test/xmerl_sax_SUITE_data/test_data_1.xml4
-rw-r--r--lib/xmerl/test/xmerl_sax_std_SUITE.erl100
-rw-r--r--lib/xmerl/test/xmerl_sax_stream_SUITE.erl260
-rw-r--r--lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_one.xml17
-rw-r--r--lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_one_junk.xml18
-rw-r--r--lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_two.xml34
-rw-r--r--lib/xmerl/vsn.mk2
-rwxr-xr-x[-rw-r--r--]make/emd2exml.in2
-rwxr-xr-xmake/make_emakefile.in (renamed from make/make_emakefile)2
-rw-r--r--make/otp.mk.in5
-rwxr-xr-xotp_build35
-rw-r--r--otp_versions.table42
-rw-r--r--scripts/Dockerfile.329
-rw-r--r--scripts/Dockerfile.32.ubuntu5
-rw-r--r--scripts/Dockerfile.649
-rw-r--r--scripts/Dockerfile.64.ubuntu5
-rwxr-xr-xscripts/build-docker-otp15
-rwxr-xr-xscripts/build-otp43
-rwxr-xr-xscripts/run-smoke-tests21
-rw-r--r--system/README78
-rw-r--r--system/doc/design_principles/applications.xml160
-rw-r--r--system/doc/design_principles/code_lock.diabin2932 -> 2945 bytes
-rw-r--r--system/doc/design_principles/code_lock.pngbin59160 -> 59827 bytes
-rw-r--r--system/doc/design_principles/code_lock_2.diabin2621 -> 2956 bytes
-rw-r--r--system/doc/design_principles/code_lock_2.pngbin48927 -> 55553 bytes
-rw-r--r--system/doc/design_principles/statem.xml882
-rw-r--r--system/doc/efficiency_guide/advanced.xml16
-rw-r--r--system/doc/efficiency_guide/profiling.xml8
-rw-r--r--system/doc/embedded/embedded_nt.xml4
-rw-r--r--system/doc/embedded/embedded_solaris.xml14
-rw-r--r--system/doc/embedded/starting.xml8
-rw-r--r--system/doc/oam/oam_intro.xml10
-rw-r--r--system/doc/programming_examples/records.xml2
-rw-r--r--system/doc/reference_manual/code_loading.xml2
-rw-r--r--system/doc/reference_manual/expressions.xml11
-rw-r--r--system/doc/reference_manual/macros.xml2
-rw-r--r--system/doc/reference_manual/typespec.xml102
-rw-r--r--system/doc/top/Makefile2
-rw-r--r--system/doc/tutorial/c_port.xmlsrc6
-rw-r--r--system/doc/tutorial/c_portdriver.xmlsrc6
1371 files changed, 96538 insertions, 52520 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644
index 0000000000..17bf4b636c
--- /dev/null
+++ b/.dir-locals.el
@@ -0,0 +1,9 @@
+;; Project-wide Emacs settings
+(
+ ;; `nil' settings apply to all language modes
+ (nil
+ ;; Use only spaces for indentation
+ (indent-tabs-mode . nil))
+ (c-mode
+ ;; In C code, indentation is four spaces
+ (c-basic-offset . 4)))
diff --git a/.gitignore b/.gitignore
index 3fc95170aa..71350ca1b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,6 +143,7 @@ JAVADOC-GENERATED
/make/output.mk
/make/emd2exml
+/make/make_emakefile
# Created by "out_build update_primary"
/bootstrap/primary_compiler/
diff --git a/.travis.yml b/.travis.yml
index 48d8031bd0..dfa01a89e6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,10 @@
-language: erlang
-
-otp_release:
- - 18.0
+language: c
sudo: false
+os:
+ - linux
+
addons:
apt:
packages:
@@ -20,21 +20,32 @@ addons:
- g++
- xsltproc
+matrix:
+ include:
+ - env: Linux32
+ os: linux
+ services:
+ - docker
+ script:
+ - ./scripts/build-docker-otp 32 sh -c "scripts/build-otp && ./otp_build tests && scripts/run-smoke-tests"
+ after_success:
+ after_script:
+
before_script:
- set -e
- export ERL_TOP=$PWD
- export PATH=$ERL_TOP/bin:$PATH
- export ERL_LIBS=''
- - export MAKEFLAGS=-j6
- - kerl_deactivate
+ - export MAKEFLAGS=-j4
script:
- - ./otp_build all -a
-
+ - ./scripts/build-otp
+
after_success:
+ - $ERL_TOP/bin/dialyzer --build_plt --apps asn1 compiler crypto dialyzer edoc erts et hipe inets kernel mnesia observer public_key runtime_tools snmp ssh ssl stdlib syntax_tools wx xmerl --statistics
+ - $ERL_TOP/bin/dialyzer -n -Wunknown -Wunmatched_returns --apps compiler erts kernel stdlib asn1 crypto dialyzer hipe parsetools public_key runtime_tools sasl tools --statistics
+ - $ERL_TOP/bin/dialyzer -n --apps common_test debugger edoc inets mnesia observer ssh ssl syntax_tools tools wx xmerl --statistics
- ./otp_build tests && make release_docs
after_script:
- - cd $ERL_TOP/release/tests/test_server && $ERL_TOP/bin/erl -s ts install -s ts smoke_test batch -s init stop
-
-
+ - ./scripts/run-smoke-tests
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000000..328b9f7859
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,98 @@
+# Contributing to Erlang/OTP
+
+## Reporting a bug
+
+Report bugs at https://bugs.erlang.org. See [Bug reports](https://github.com/erlang/otp/wiki/Bug-reports)
+for more information.
+
+## Submitting Pull Requests
+
+You can contribute to Erlang/OTP by opening a Pull Request.
+
+## Fixing a bug
+
+* In most cases, pull requests for bug fixes should be based on the `maint` branch.
+There are exceptions, for example corrections to bugs that have been introduced in the `master` branch.
+
+* Include a test case to ensure that the bug is fixed **and that it stays fixed**.
+
+* TIP: Write the test case **before** fixing the bug so that you can know that it catches the bug.
+
+* For applications without a test suite in the git repository, it would be appreciated if you provide a
+small code sample in the commit message or email a module that will provoke the failure.
+
+## Adding a new feature
+
+* In most cases, pull requests for new features should be based on the `master` branch.
+
+* It is recommended to discuss new features on
+[the erlang-questions mailing list](http://erlang.org/mailman/listinfo/erlang-questions),
+especially for major new features or any new features in ERTS, Kernel, or STDLIB.
+
+* It is important to write a good commit message explaining **why** the feature is needed.
+We prefer that the information is in the commit message, so that anyone that want to know
+two years later why a particular feature can easily find out. It does no harm to provide
+the same information in the pull request (if the pull request consists of a single commit,
+the commit message will be added to the pull request automatically).
+
+* With few exceptions, it is mandatory to write a new test case that tests the feature.
+The test case is needed to ensure that the features does not stop working in the future.
+
+* Update the [Documentation](https://github.com/erlang/otp/wiki/Documentation) to describe the feature.
+
+* Make sure that the new feature builds and works on all major platforms. Exceptions are features
+that only makes sense one some platforms, for example the `win32reg` module for accessing the Windows registry.
+
+* Make sure that your feature does not break backward compatibility. In general, we only break backward
+compatibility in major releases and only for a very good reason. Usually we first deprecate the
+feature one or two releases beforehand.
+
+* In general, language changes/extensions require an
+[EEP (Erlang Enhancement Proposal)](https://github.com/erlang/eep) to be written and approved before they
+can be included in OTP. Major changes or new features in ERTS, Kernel, or STDLIB will need an EEP or at least
+a discussion on the mailing list.
+
+## Before you submit your pull request
+
+* Make sure existing test cases don't fail. It is not necessary to run all tests (that would take many hours),
+but you should at least run the tests for the application you have changed.
+See [Running tests](https://github.com/erlang/otp/wiki/Running-tests).
+
+Make sure that your branch contains clean commits:
+
+* Don't make the first line in the commit message longer than 72 characters.
+**Don't end the first line with a period.**
+
+* Follow the guidelines for [Writing good commit messages](https://github.com/erlang/otp/wiki/Writing-good-commit-messages).
+
+* Don't merge `maint` or `master` into your branch. Use `git rebase` if you need to resolve merge
+conflicts or include the latest changes.
+
+* To make it possible to use the powerful `git bisect` command, make sure that each commit can be
+compiled and that it works.
+
+* Check for unnecessary whitespace before committing with `git diff --check`.
+
+Check your coding style:
+
+* Make sure your changes follow the coding and indentation style of the code surrounding your changes.
+
+* Do not commit commented-out code or files that are no longer needed. Remove the code or the files.
+
+* In most code (Erlang and C), indentation is 4 steps. Indentation using only spaces is **strongly recommended**.
+
+### Configuring Emacs
+
+If you use Emacs, use the Erlang mode, and add the following lines to `.emacs`:
+
+ (setq-default indent-tabs-mode nil)
+ (setq c-basic-offset 4)
+
+If you want to change the setting only for the Erlang mode, you can use a hook like this:
+
+```
+(add-hook 'erlang-mode-hook 'my-erlang-hook)
+
+(defun my-erlang-hook ()
+ (setq indent-tabs-mode nil))
+```
diff --git a/HOWTO/INSTALL-WIN32.md b/HOWTO/INSTALL-WIN32.md
index d7be255e9f..c74107d749 100644
--- a/HOWTO/INSTALL-WIN32.md
+++ b/HOWTO/INSTALL-WIN32.md
@@ -392,7 +392,7 @@ tools:
LIB="$WIN_VISUAL_STUDIO_ROOT\\VC\\lib\\;$WIN_SDK\\lib\\winv6.3\\um\\x86"
- INCLUDE="$WIN_VISUAL_STUDIO_ROOT\\VC\\include\\;$WIN_SDK\\include\\shared\\;
+ INCLUDE="$WIN_VISUAL_STUDIO_ROOT\\VC\\include\\;$WIN_SDK\\include\\shared\\;\
$WIN_SDK\\include\\um;$WIN_SDK\\include\\winrt\\;$WIN_SDK\\include\\um\\gl"
export CYGWIN PATH LIBPATH LIB INCLUDE
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md
index 8632f46264..447a23128f 100644
--- a/HOWTO/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -429,6 +429,10 @@ Some of the available `configure` options are:
and **not supported**. This functionality **will** be subject to backward
incompatible changes. Note that you should **not** enable the dirty scheduler
functionality on production systems. It is only provided for testing.
+ This switch also imply `--enable-new-purge-strategy` (see below).
+* `--enable-new-purge-strategy` - Enable the purge strategy that will be
+ introduced in ERTS version 9.0 (OTP 20). Note that this switch will be
+ removed in OTP 20.
If you or your system has special requirements please read the `Makefile` for
additional configuration information.
@@ -564,6 +568,10 @@ as before, but the build process will take a much longer time.
> automatically when `make` is invoked from `$ERL_TOP` with either the
> `clean` target, or the default target. It is also automatically invoked
> if `./otp_build remove_prebuilt_files` is invoked.
+>
+> If you need to verify the bootstrap beam files match the provided
+> source files, use `./otp_build update_primary` to create a new commit that
+> contains differences, if any exist.
#### How to Build a Debug Enabled Erlang RunTime System ####
diff --git a/OTP_VERSION b/OTP_VERSION
index 6ea9a3bd47..f034955106 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-19.0
+19.3.6.11
diff --git a/README.md b/README.md
index 127ac9cad7..8c77f0b39a 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,7 @@ In short:
* Go to the JIRA issue tracker at [bugs.erlang.org] [7] to
see reported issues which you can contribute to.
- Search for issues with the status *Contribution Needed*.
+ Search for issues with the status *Help Wanted*.
Bug Reports
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index ef962190a4..60fc76f2e2 100644
--- a/bootstrap/bin/start.boot
+++ b/bootstrap/bin/start.boot
Binary files differ
diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot
index ef962190a4..60fc76f2e2 100644
--- a/bootstrap/bin/start_clean.boot
+++ b/bootstrap/bin/start_clean.boot
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam
index c8bc82022c..d717d81492 100644
--- a/bootstrap/lib/compiler/ebin/beam_asm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_asm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam
index ea91a62b56..95846cdda9 100644
--- a/bootstrap/lib/compiler/ebin/beam_block.beam
+++ b/bootstrap/lib/compiler/ebin/beam_block.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bool.beam b/bootstrap/lib/compiler/ebin/beam_bool.beam
index 1673c4aadb..37f368190e 100644
--- a/bootstrap/lib/compiler/ebin/beam_bool.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bool.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bsm.beam b/bootstrap/lib/compiler/ebin/beam_bsm.beam
index 51abd68b00..fbea0b152b 100644
--- a/bootstrap/lib/compiler/ebin/beam_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam
index 0f24e040e3..ed0827953c 100644
--- a/bootstrap/lib/compiler/ebin/beam_clean.beam
+++ b/bootstrap/lib/compiler/ebin/beam_clean.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dead.beam b/bootstrap/lib/compiler/ebin/beam_dead.beam
index d2dc2f7688..c473003687 100644
--- a/bootstrap/lib/compiler/ebin/beam_dead.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dead.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dict.beam b/bootstrap/lib/compiler/ebin/beam_dict.beam
index 4e12260e65..5561744faa 100644
--- a/bootstrap/lib/compiler/ebin/beam_dict.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dict.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index 2d1961570f..bceb7936d6 100644
--- a/bootstrap/lib/compiler/ebin/beam_jump.beam
+++ b/bootstrap/lib/compiler/ebin/beam_jump.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_split.beam b/bootstrap/lib/compiler/ebin/beam_split.beam
index 4a63468419..524e9a3078 100644
--- a/bootstrap/lib/compiler/ebin/beam_split.beam
+++ b/bootstrap/lib/compiler/ebin/beam_split.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index 25cd7ce430..36f23e98cc 100644
--- a/bootstrap/lib/compiler/ebin/beam_utils.beam
+++ b/bootstrap/lib/compiler/ebin/beam_utils.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam
index fcb2202d2a..07cfa5a87a 100644
--- a/bootstrap/lib/compiler/ebin/beam_validator.beam
+++ b/bootstrap/lib/compiler/ebin/beam_validator.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index dea6cb4ee2..eecdd4e2dd 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/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index 8003d590c9..ce38838715 100644
--- a/bootstrap/lib/compiler/ebin/compile.beam
+++ b/bootstrap/lib/compiler/ebin/compile.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app
index 53891c7260..010d344669 100644
--- a/bootstrap/lib/compiler/ebin/compiler.app
+++ b/bootstrap/lib/compiler/ebin/compiler.app
@@ -19,7 +19,7 @@
{application, compiler,
[{description, "ERTS CXC 138 10"},
- {vsn, "6.0.3"},
+ {vsn, "7.0.3"},
{modules, [
beam_a,
beam_asm,
diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam
index 0e47e00b15..47f9f53546 100644
--- a/bootstrap/lib/compiler/ebin/core_lint.beam
+++ b/bootstrap/lib/compiler/ebin/core_lint.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_parse.beam b/bootstrap/lib/compiler/ebin/core_parse.beam
index 920dbc922f..c85f2ec140 100644
--- a/bootstrap/lib/compiler/ebin/core_parse.beam
+++ b/bootstrap/lib/compiler/ebin/core_parse.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_scan.beam b/bootstrap/lib/compiler/ebin/core_scan.beam
index 5f7b22ba8b..3017a4e026 100644
--- a/bootstrap/lib/compiler/ebin/core_scan.beam
+++ b/bootstrap/lib/compiler/ebin/core_scan.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam b/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
index 81da8b62c5..47b804d61d 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
index 68cc4e3ff3..f3bbc3401c 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_fold.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam
index 6afb674a9f..cb64a1a2ac 100644
--- a/bootstrap/lib/compiler/ebin/v3_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/v3_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index e8e120e145..54f507218f 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/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam
index 47eaf3b5e8..60cc6503eb 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_life.beam b/bootstrap/lib/compiler/ebin/v3_life.beam
index b220e475ee..d4b3f2a5e6 100644
--- a/bootstrap/lib/compiler/ebin/v3_life.beam
+++ b/bootstrap/lib/compiler/ebin/v3_life.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam
index 2b3c8cf454..d46de4527a 100644
--- a/bootstrap/lib/kernel/ebin/application_controller.beam
+++ b/bootstrap/lib/kernel/ebin/application_controller.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_master.beam b/bootstrap/lib/kernel/ebin/application_master.beam
index 24492a6771..da770c5a84 100644
--- a/bootstrap/lib/kernel/ebin/application_master.beam
+++ b/bootstrap/lib/kernel/ebin/application_master.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/auth.beam b/bootstrap/lib/kernel/ebin/auth.beam
index cbe477830b..1ccad1a5a3 100644
--- a/bootstrap/lib/kernel/ebin/auth.beam
+++ b/bootstrap/lib/kernel/ebin/auth.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index 6f99487ca7..8cd118bc9d 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/disk_log.beam b/bootstrap/lib/kernel/ebin/disk_log.beam
index 5aa8bf7dc1..31fb2d609d 100644
--- a/bootstrap/lib/kernel/ebin/disk_log.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_1.beam b/bootstrap/lib/kernel/ebin/disk_log_1.beam
index cb627c10a5..4e819c4899 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_1.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_1.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_server.beam b/bootstrap/lib/kernel/ebin/disk_log_server.beam
index 50154045be..67cda715c4 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_server.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_ac.beam b/bootstrap/lib/kernel/ebin/dist_ac.beam
index d2c186f03a..56868dfe6d 100644
--- a/bootstrap/lib/kernel/ebin/dist_ac.beam
+++ b/bootstrap/lib/kernel/ebin/dist_ac.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_util.beam b/bootstrap/lib/kernel/ebin/dist_util.beam
index eb09a06785..bf3572fe23 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/erl_epmd.beam b/bootstrap/lib/kernel/ebin/erl_epmd.beam
index 8e8460514c..19be6babd1 100644
--- a/bootstrap/lib/kernel/ebin/erl_epmd.beam
+++ b/bootstrap/lib/kernel/ebin/erl_epmd.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_logger.beam b/bootstrap/lib/kernel/ebin/error_logger.beam
index 73ab813430..0bb657841c 100644
--- a/bootstrap/lib/kernel/ebin/error_logger.beam
+++ b/bootstrap/lib/kernel/ebin/error_logger.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file.beam b/bootstrap/lib/kernel/ebin/file.beam
index 28d72dfafc..7b2f0b48d0 100644
--- a/bootstrap/lib/kernel/ebin/file.beam
+++ b/bootstrap/lib/kernel/ebin/file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam
index 465dab3a52..601cb9176b 100644
--- a/bootstrap/lib/kernel/ebin/global.beam
+++ b/bootstrap/lib/kernel/ebin/global.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global_group.beam b/bootstrap/lib/kernel/ebin/global_group.beam
index 03202f68eb..8bd51c2424 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/group.beam b/bootstrap/lib/kernel/ebin/group.beam
index 38eb0e7a6c..c2c66f77ec 100644
--- a/bootstrap/lib/kernel/ebin/group.beam
+++ b/bootstrap/lib/kernel/ebin/group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/heart.beam b/bootstrap/lib/kernel/ebin/heart.beam
index 86044153af..c0eaa5825d 100644
--- a/bootstrap/lib/kernel/ebin/heart.beam
+++ b/bootstrap/lib/kernel/ebin/heart.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
index 5a2f294951..fde99e2072 100644
--- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
+++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam
index 5ae4090a61..b11c8fa56d 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
index 1be1dc1c57..98f70db13c 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_res.beam b/bootstrap/lib/kernel/ebin/inet_res.beam
index 2713c7fef6..b1bbf771a3 100644
--- a/bootstrap/lib/kernel/ebin/inet_res.beam
+++ b/bootstrap/lib/kernel/ebin/inet_res.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
index d06bdf709a..087274e89d 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app
index a4600713e5..8e0622f72e 100644
--- a/bootstrap/lib/kernel/ebin/kernel.app
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -22,7 +22,7 @@
{application, kernel,
[
{description, "ERTS CXC 138 10"},
- {vsn, "5.0"},
+ {vsn, "5.1.1"},
{modules, [application,
application_controller,
application_master,
@@ -118,6 +118,6 @@
{applications, []},
{env, [{error_logger, tty}]},
{mod, {kernel, []}},
- {runtime_dependencies, ["erts-8.0", "stdlib-3.0", "sasl-3.0"]}
+ {runtime_dependencies, ["erts-8.1", "stdlib-3.0", "sasl-3.0"]}
]
}.
diff --git a/bootstrap/lib/kernel/ebin/kernel.appup b/bootstrap/lib/kernel/ebin/kernel.appup
index 1ed9771492..f1a6f2dbe2 100644
--- a/bootstrap/lib/kernel/ebin/kernel.appup
+++ b/bootstrap/lib/kernel/ebin/kernel.appup
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2017. 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.
@@ -16,11 +16,11 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
-{"5.0",
+{"5.1.1",
%% Up from - max one major revision back
- [{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.*
%% Down to - max one major revision back
- [{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.*
}.
diff --git a/bootstrap/lib/kernel/ebin/kernel_config.beam b/bootstrap/lib/kernel/ebin/kernel_config.beam
index 8379f77e94..5e59fc2f82 100644
--- a/bootstrap/lib/kernel/ebin/kernel_config.beam
+++ b/bootstrap/lib/kernel/ebin/kernel_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_kernel.beam b/bootstrap/lib/kernel/ebin/net_kernel.beam
index 8afe1c0388..1197b4f681 100644
--- a/bootstrap/lib/kernel/ebin/net_kernel.beam
+++ b/bootstrap/lib/kernel/ebin/net_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/os.beam b/bootstrap/lib/kernel/ebin/os.beam
index 9ebe6c770f..24f0952c87 100644
--- a/bootstrap/lib/kernel/ebin/os.beam
+++ b/bootstrap/lib/kernel/ebin/os.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam
index 684d95afa0..798acfd4d4 100644
--- a/bootstrap/lib/kernel/ebin/rpc.beam
+++ b/bootstrap/lib/kernel/ebin/rpc.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user.beam b/bootstrap/lib/kernel/ebin/user.beam
index c2b2808555..cded4b87be 100644
--- a/bootstrap/lib/kernel/ebin/user.beam
+++ b/bootstrap/lib/kernel/ebin/user.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user_drv.beam b/bootstrap/lib/kernel/ebin/user_drv.beam
index 1d72927105..bd9b1a3bce 100644
--- a/bootstrap/lib/kernel/ebin/user_drv.beam
+++ b/bootstrap/lib/kernel/ebin/user_drv.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
index 91ef523b99..93f1f1ba72 100644
--- a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
+++ b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/include/dist_util.hrl b/bootstrap/lib/kernel/include/dist_util.hrl
index 43e50d4325..320e916c04 100644
--- a/bootstrap/lib/kernel/include/dist_util.hrl
+++ b/bootstrap/lib/kernel/include/dist_util.hrl
@@ -63,7 +63,7 @@
f_getll, %% Get low level port or pid.
f_address, %% The address of the "socket",
%% generated from Socket,Node
- %% These two are used in the tick loop,
+ %% These three are used in the tick loop,
%% so they are not fun's to avoid holding old code.
mf_tick, %% Takes the socket as parameters and
%% sends a tick, this is no fun, it
@@ -74,7 +74,11 @@
%% {ok, RecvCnt, SendCnt, SendPend} for
%% a given socket. This is a {M,F},
%% returning {error, Reason on failure}
- request_type = normal
+ request_type = normal,
+
+ %% New in kernel-5.1 (OTP 19.1):
+ mf_setopts, %% netkernel:setopts on active connection
+ mf_getopts %% netkernel:getopts on active connection
}).
diff --git a/bootstrap/lib/stdlib/ebin/binary.beam b/bootstrap/lib/stdlib/ebin/binary.beam
index 538d169565..ff9022388e 100644
--- a/bootstrap/lib/stdlib/ebin/binary.beam
+++ b/bootstrap/lib/stdlib/ebin/binary.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam
index 0dfaca7a48..8b9049c855 100644
--- a/bootstrap/lib/stdlib/ebin/dets.beam
+++ b/bootstrap/lib/stdlib/ebin/dets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_server.beam b/bootstrap/lib/stdlib/ebin/dets_server.beam
index 502eb98318..0b0783bf9d 100644
--- a/bootstrap/lib/stdlib/ebin/dets_server.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_utils.beam b/bootstrap/lib/stdlib/ebin/dets_utils.beam
index 37247c7176..2a3b22b7e0 100644
--- a/bootstrap/lib/stdlib/ebin/dets_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_v8.beam b/bootstrap/lib/stdlib/ebin/dets_v8.beam
index 9a1416a316..39ddeebf85 100644
--- a/bootstrap/lib/stdlib/ebin/dets_v8.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_v8.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin_expand.beam b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
index adc0f4ba1c..30b52ee407 100644
--- a/bootstrap/lib/stdlib/ebin/edlin_expand.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index 5bcc7cf8d9..b1a75a54eb 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_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam
index d5bd46593c..37d6e46426 100644
--- a/bootstrap/lib/stdlib/ebin/erl_eval.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_eval.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index ebe80a32a8..6653cb9f49 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 33a0753965..0cfc15a94f 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 e845392e4b..c8798a6094 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/error_logger_file_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
index 0e61e87708..b845a64248 100644
--- a/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
+++ b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
index 46eafce18e..af784cf79d 100644
--- a/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
+++ b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/escript.beam b/bootstrap/lib/stdlib/ebin/escript.beam
index 70ac06c476..ef809f3ebd 100644
--- a/bootstrap/lib/stdlib/ebin/escript.beam
+++ b/bootstrap/lib/stdlib/ebin/escript.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/file_sorter.beam b/bootstrap/lib/stdlib/ebin/file_sorter.beam
index 03d9020bb5..693c0b523d 100644
--- a/bootstrap/lib/stdlib/ebin/file_sorter.beam
+++ b/bootstrap/lib/stdlib/ebin/file_sorter.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/filename.beam b/bootstrap/lib/stdlib/ebin/filename.beam
index c129ac4f97..251ea5d1e3 100644
--- a/bootstrap/lib/stdlib/ebin/filename.beam
+++ b/bootstrap/lib/stdlib/ebin/filename.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_fsm.beam b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
index 595574585b..0370d430b2 100644
--- a/bootstrap/lib/stdlib/ebin/gen_fsm.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_server.beam b/bootstrap/lib/stdlib/ebin/gen_server.beam
index 3097207512..aa22d6aae4 100644
--- a/bootstrap/lib/stdlib/ebin/gen_server.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_statem.beam b/bootstrap/lib/stdlib/ebin/gen_statem.beam
index 3dc104f1c3..5dcb27b08b 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/io_lib_pretty.beam b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
index 8488125227..6cfbd90ecb 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/log_mf_h.beam b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
index 0c4b22c3df..d0e18955c7 100644
--- a/bootstrap/lib/stdlib/ebin/log_mf_h.beam
+++ b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam
index 2c0eb8a742..c72dd5c717 100644
--- a/bootstrap/lib/stdlib/ebin/ms_transform.beam
+++ b/bootstrap/lib/stdlib/ebin/ms_transform.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/pool.beam b/bootstrap/lib/stdlib/ebin/pool.beam
index a06ae1717a..cc6e536ccd 100644
--- a/bootstrap/lib/stdlib/ebin/pool.beam
+++ b/bootstrap/lib/stdlib/ebin/pool.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam
index fa3240810d..79f16b9e39 100644
--- a/bootstrap/lib/stdlib/ebin/qlc.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc_pt.beam b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
index 4a065ec57f..e9c760ba45 100644
--- a/bootstrap/lib/stdlib/ebin/qlc_pt.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell.beam b/bootstrap/lib/stdlib/ebin/shell.beam
index b0c98f4b98..3d7b53387b 100644
--- a/bootstrap/lib/stdlib/ebin/shell.beam
+++ b/bootstrap/lib/stdlib/ebin/shell.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/slave.beam b/bootstrap/lib/stdlib/ebin/slave.beam
index 373d65f198..fa0fa8c960 100644
--- a/bootstrap/lib/stdlib/ebin/slave.beam
+++ b/bootstrap/lib/stdlib/ebin/slave.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sofs.beam b/bootstrap/lib/stdlib/ebin/sofs.beam
index 5099d4ecc0..7b8ab95876 100644
--- a/bootstrap/lib/stdlib/ebin/sofs.beam
+++ b/bootstrap/lib/stdlib/ebin/sofs.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
index ceb55c66fc..6102b72d2d 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.app
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -20,7 +20,7 @@
%%
{application, stdlib,
[{description, "ERTS CXC 138 10"},
- {vsn, "3.0"},
+ {vsn, "3.2"},
{modules, [array,
base64,
beam_lib,
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup
index 737486d3c5..d806b8b5b7 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.appup
+++ b/bootstrap/lib/stdlib/ebin/stdlib.appup
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2017. 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.
@@ -16,11 +16,11 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
-{"3.0",
+{"3.2",
%% Up from - max one major revision back
- [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"3\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.*
%% Down to - max one major revision back
- [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"3\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.*
}.
diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam
index 3ae911ab29..a05ae66941 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
index d2bfe6eea8..f52d5c407b 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam
index 6ef3732965..5632fd226c 100644
--- a/bootstrap/lib/stdlib/ebin/zip.beam
+++ b/bootstrap/lib/stdlib/ebin/zip.beam
Binary files differ
diff --git a/configure.in b/configure.in
index 8a7f372a50..a149acbb55 100644
--- a/configure.in
+++ b/configure.in
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script.
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2014. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2016. All Rights Reserved.
dnl
dnl Licensed under the Apache License, Version 2.0 (the "License");
dnl you may not use this file except in compliance with the License.
@@ -105,7 +105,6 @@ else
fi
AC_SUBST(CROSS_COMPILING)
-
AC_ARG_ENABLE(bootstrap-only,
AS_HELP_STRING([--enable-bootstrap-only],
[enable bootstrap only configuration]),
@@ -129,6 +128,13 @@ AC_PROG_CC
AC_PROG_CXX
AC_CHECK_TOOL(LD, [ld])
+_search_path=/bin:/usr/bin:/usr/local/bin:$PATH
+
+AC_PATH_PROG(ENV, [env], false, $_search_path)
+if test "$ac_cv_path_ENV" = false; then
+ AC_MSG_ERROR([No 'env' command found])
+fi
+
#
# We need GNU make, complain if we can't find it
#
@@ -222,7 +228,10 @@ AS_HELP_STRING([--enable-kernel-poll], [enable kernel poll support])
AS_HELP_STRING([--disable-kernel-poll], [disable kernel poll support]))
AC_ARG_ENABLE(sctp,
-AS_HELP_STRING([--enable-sctp], [enable sctp support])
+AS_HELP_STRING([--enable-sctp], [enable sctp support (default)
+to on demand load the SCTP library in runtime])
+AS_HELP_STRING([--enable-sctp=lib], [enable sctp support
+to link against the SCTP library])
AS_HELP_STRING([--disable-sctp], [disable sctp support]))
AC_ARG_ENABLE(hipe,
@@ -361,6 +370,49 @@ if test X${enable_native_libs} = Xyes -a X${enable_hipe} != Xno; then
fi
AC_SUBST(NATIVE_LIBS_ENABLED)
+if test $CROSS_COMPILING = no; then
+ case $host_os in
+ darwin*)
+ macosx_version=`sw_vers -productVersion`
+ test $? -eq 0 || {
+ AC_MSG_ERROR([Failed to execute 'sw_vers'; please provide it in PATH])
+ }
+ [case "$macosx_version" in
+ [1-9][0-9].[0-9])
+ int_macosx_version=`echo $macosx_version | sed 's|\([^\.]*\)\.\([^\.]*\)|\1\2|'`;;
+ [1-9][0-9].[0-9].[0-9])
+ int_macosx_version=`echo $macosx_version | sed 's|\([^\.]*\)\.\([^\.]*\)\.\([^\.]*\)|\1\2\3|'`;;
+ [1-9][0-9].[1-9][0-9])
+ int_macosx_version=`echo $macosx_version | sed 's|\([^\.]*\)\.\([^\.]*\)|\1\200|'`;;
+ [1-9][0-9].[1-9][0-9].[0-9])
+ int_macosx_version=`echo $macosx_version | sed 's|\([^\.]*\)\.\([^\.]*\)\.\([^\.]*\)|\1\20\3|'`;;
+ [1-9][0-9].[1-9][0-9].[1-9][0-9])
+ int_macosx_version=`echo $macosx_version | sed 's|\([^\.]*\)\.\([^\.]*\)\.\([^\.]*\)|\1\2\3|'`;;
+ *)
+ int_macosx_version=unexpected;;
+ esac]
+ test $int_macosx_version != unexpected || {
+ AC_MSG_ERROR([Unexpected MacOSX version ($macosx_version) returned by 'sw_vers -productVersion'; this configure script probably needs to be updated])
+ }
+ AC_TRY_COMPILE([
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > $int_macosx_version
+#error Compiling for a newer MacOSX version...
+#endif
+ ], [;],
+ [],
+ [AC_MSG_ERROR([
+
+ You are natively building Erlang/OTP for a later version of MacOSX
+ than current version ($macosx_version). You either need to
+ cross-build Erlang/OTP, or set the environment variable
+ MACOSX_DEPLOYMENT_TARGET to $macosx_version (or a lower version).
+
+])])
+ ;;
+ *)
+ ;;
+ esac
+fi
rm -f $ERL_TOP/lib/SKIP-APPLICATIONS
for app in `cd lib && ls -d *`; do
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4
index 013bfe5652..80bf236188 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -736,10 +736,23 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
;;
esac
+ clock_gettime_lib=""
+ AC_CHECK_LIB(rt, clock_gettime, [clock_gettime_lib="-lrt"])
+
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $clock_gettime_lib"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ trust_test="#error May not be there due to weak linking"
+ else
+ trust_test=""
+ fi
+
AC_CACHE_CHECK([for clock_gettime(CLOCK_MONOTONIC_RAW, _)], erl_cv_clock_gettime_monotonic_raw,
[
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <time.h>
+$trust_test
],
[
struct timespec ts;
@@ -755,8 +768,9 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
AC_CACHE_CHECK([for clock_gettime() with ${check_msg}monotonic clock type], erl_cv_clock_gettime_monotonic_$1,
[
for clock_type in $prefer_resolution_clock_gettime_monotonic $default_resolution_clock_gettime_monotonic $high_resolution_clock_gettime_monotonic $low_resolution_clock_gettime_monotonic; do
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <time.h>
+$trust_test
],
[
struct timespec ts;
@@ -771,7 +785,15 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
done
])
- AC_CHECK_FUNCS([clock_getres clock_get_attributes gethrtime])
+ LIBS="$save_LIBS"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ check_for_clock_getres=
+ else
+ check_for_clock_getres=clock_getres
+ fi
+
+ AC_CHECK_FUNCS([$check_for_clock_getres clock_get_attributes gethrtime])
AC_CACHE_CHECK([for mach clock_get_time() with monotonic clock type], erl_cv_mach_clock_get_time_monotonic,
[
@@ -840,7 +862,7 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK,
break
fi
done
- AC_CHECK_LIB(rt, clock_gettime, [erl_monotonic_clock_lib="-lrt"])
+ erl_monotonic_clock_lib=$clock_gettime_lib
;;
mach_clock_get_time)
erl_monotonic_clock_id=SYSTEM_CLOCK
@@ -879,11 +901,24 @@ AC_DEFUN(ERL_WALL_CLOCK,
;;
esac
+ clock_gettime_lib=""
+ AC_CHECK_LIB(rt, clock_gettime, [clock_gettime_lib="-lrt"])
+
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $clock_gettime_lib"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ trust_test="#error May not be there due to weak linking"
+ else
+ trust_test=""
+ fi
+
AC_CACHE_CHECK([for clock_gettime() with ${check_msg}wall clock type], erl_cv_clock_gettime_wall_$1,
[
for clock_type in $prefer_resolution_clock_gettime_wall $default_resolution_clock_gettime_wall $high_resolution_clock_gettime_wall $low_resolution_clock_gettime_wall; do
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <time.h>
+$trust_test
],
[
struct timespec ts;
@@ -898,7 +933,15 @@ AC_DEFUN(ERL_WALL_CLOCK,
done
])
- AC_CHECK_FUNCS([clock_getres clock_get_attributes gettimeofday])
+ LIBS="$save_LIBS"
+
+ if test "$LD_MAY_BE_WEAK" != "no"; then
+ check_for_clock_getres=
+ else
+ check_for_clock_getres=clock_getres
+ fi
+
+ AC_CHECK_FUNCS([$check_for_clock_getres clock_get_attributes gettimeofday])
AC_CACHE_CHECK([for mach clock_get_time() with wall clock type], erl_cv_mach_clock_get_time_wall,
[
@@ -919,6 +962,7 @@ AC_DEFUN(ERL_WALL_CLOCK,
erl_cv_mach_clock_get_time_wall=no)
])
+ erl_wall_clock_lib=
erl_wall_clock_low_resolution=no
erl_wall_clock_id=
case $1-$erl_cv_clock_gettime_wall_$1-$erl_cv_mach_clock_get_time_wall-$ac_cv_func_gettimeofday-$host_os in
@@ -932,6 +976,7 @@ AC_DEFUN(ERL_WALL_CLOCK,
;;
*-CLOCK_*-*-*-*)
erl_wall_clock_func=clock_gettime
+ erl_wall_clock_lib=$clock_gettime_lib
erl_wall_clock_id=$erl_cv_clock_gettime_wall_$1
for low_res_id in $low_resolution_clock_gettime_wall; do
if test $erl_wall_clock_id = $low_res_id; then
@@ -1479,6 +1524,13 @@ ETHR_LIB_NAME=
ethr_modified_default_stack_size=
+AC_ARG_WITH(threadnames,
+AS_HELP_STRING([--with-threadnames], [use pthread_setname to set the thread names (default)])
+AS_HELP_STRING([--without-threadnames],
+ [do not set any thread names]),
+[],
+[with_threadnames=yes])
+
dnl Name of lib where ethread implementation is located
ethr_lib_name=ethread
@@ -1659,6 +1711,25 @@ case "$THR_LIB_NAME" in
AC_DEFINE(ETHR_TIME_WITH_SYS_TIME, 1, \
[Define if you can safely include both <sys/time.h> and <time.h>.]))
+ AC_MSG_CHECKING([for usable PTHREAD_STACK_MIN])
+ pthread_stack_min=no
+ AC_TRY_COMPILE([
+#include <limits.h>
+#if defined(ETHR_NEED_NPTL_PTHREAD_H)
+#include <nptl/pthread.h>
+#elif defined(ETHR_HAVE_MIT_PTHREAD_H)
+#include <pthread/mit/pthread.h>
+#elif defined(ETHR_HAVE_PTHREAD_H)
+#include <pthread.h>
+#endif
+ ],
+ [return PTHREAD_STACK_MIN;],
+ [pthread_stack_min=yes])
+
+ AC_MSG_RESULT([$pthread_stack_min])
+ test $pthread_stack_min != yes || {
+ AC_DEFINE(ETHR_HAVE_USABLE_PTHREAD_STACK_MIN, 1, [Define if you can use PTHREAD_STACK_MIN])
+ }
dnl
dnl Check for functions
@@ -1680,7 +1751,7 @@ case "$THR_LIB_NAME" in
AC_DEFINE(ETHR_HAVE_SCHED_YIELD, 1, [Define if you have the sched_yield() function.])
AC_MSG_CHECKING([whether sched_yield() returns an int])
sched_yield_ret_int=no
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#ifdef ETHR_HAVE_SCHED_H
#include <sched.h>
#endif
@@ -1699,7 +1770,7 @@ case "$THR_LIB_NAME" in
AC_DEFINE(ETHR_HAVE_PTHREAD_YIELD, 1, [Define if you have the pthread_yield() function.])
AC_MSG_CHECKING([whether pthread_yield() returns an int])
pthread_yield_ret_int=no
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#if defined(ETHR_NEED_NPTL_PTHREAD_H)
#include <nptl/pthread.h>
#elif defined(ETHR_HAVE_MIT_PTHREAD_H)
@@ -1850,12 +1921,12 @@ case "$THR_LIB_NAME" in
[pthread_setname_np("name");],
pthread_setname=darwin)
AC_MSG_RESULT([$pthread_setname])
- case $pthread_setname in
- linux) AC_DEFINE(ETHR_HAVE_PTHREAD_SETNAME_NP_2, 1,
+ case $with_threadnames-$pthread_setname in
+ yes-linux) AC_DEFINE(ETHR_HAVE_PTHREAD_SETNAME_NP_2, 1,
[Define if you have linux style pthread_setname_np]);;
- bsd) AC_DEFINE(ETHR_HAVE_PTHREAD_SET_NAME_NP_2, 1,
+ yes-bsd) AC_DEFINE(ETHR_HAVE_PTHREAD_SET_NAME_NP_2, 1,
[Define if you have bsd style pthread_set_name_np]);;
- darwin) AC_DEFINE(ETHR_HAVE_PTHREAD_SETNAME_NP_1, 1,
+ yes-darwin) AC_DEFINE(ETHR_HAVE_PTHREAD_SETNAME_NP_1, 1,
[Define if you have darwin style pthread_setname_np]);;
*) ;;
esac
@@ -2436,7 +2507,13 @@ if test $erl_monotonic_clock_low_resolution = yes; then
AC_DEFINE(ERTS_HAVE_LOW_RESOLUTION_OS_MONOTONIC_LOW, [1], [Define if you have a low resolution OS monotonic clock])
fi
-xrtlib="$erl_monotonic_clock_lib"
+xrtlib=
+if test "$erl_monotonic_clock_lib" != ""; then
+ xrtlib="$erl_monotonic_clock_lib"
+fi
+if test "$erl_wall_clock_lib" != ""; then
+ xrtlib="$erl_wall_clock_lib"
+fi
if test "x$erl_monotonic_clock_id" != "x"; then
AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID_STR, ["$erl_monotonic_clock_id"], [Define as a string of monotonic clock id to use])
AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID, [$erl_monotonic_clock_id], [Define to monotonic clock id to use])
diff --git a/erts/configure.in b/erts/configure.in
index 81ecad4f51..ad9a66126f 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -121,6 +121,7 @@ AS_HELP_STRING([--enable-bootstrap-only],
enable_hipe=no
enable_sctp=no
enable_dirty_schedulers=no
+ enable_new_purge=no
fi
])
@@ -139,6 +140,13 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]),
*) enable_dirty_schedulers=yes ;;
esac ], enable_dirty_schedulers=default)
+AC_ARG_ENABLE(new-purge-strategy,
+AS_HELP_STRING([--enable-new-purge-strategy], [enable new code purge strategy]),
+[ case "$enableval" in
+ no) enable_new_purge=no ;;
+ *) enable_new_purge=yes ;;
+ esac ], enable_new_purge=default)
+
AC_ARG_ENABLE(smp-support,
AS_HELP_STRING([--enable-smp-support], [enable smp support])
AS_HELP_STRING([--disable-smp-support], [disable smp support]),
@@ -188,12 +196,18 @@ AS_HELP_STRING([--disable-kernel-poll], [disable kernel poll support]),
AC_ARG_ENABLE(sctp,
-AS_HELP_STRING([--enable-sctp], [enable sctp support (default)])
+AS_HELP_STRING([--enable-sctp], [enable sctp support (default)
+to on demand load the SCTP library in runtime if needed])
+AS_HELP_STRING([--enable-sctp=lib], [enable sctp support
+to link against the SCTP library])
AS_HELP_STRING([--disable-sctp], [disable sctp support]),
-[ case "$enableval" in
- no) enable_sctp=no ;;
- *) enable_sctp=yes ;;
- esac ], enable_sctp=unknown)
+[ case "x$enableval" in
+ xno|xyes|xlib|x)
+ ;;
+ x*)
+ AC_MSG_ERROR("invalid value --enable-sctp=$enableval")
+ ;;
+ esac ])
AC_ARG_ENABLE(hipe,
AS_HELP_STRING([--enable-hipe], [enable hipe support])
@@ -738,6 +752,11 @@ if test "$ac_cv_path_MKDIR" = false; then
AC_MSG_ERROR([No 'mkdir' command found])
fi
+AC_PATH_PROG(CP, cp, false, $_search_path)
+if test "$ac_cv_path_CP" = false; then
+ AC_MSG_ERROR([No 'cp' command found])
+fi
+
_search_path=
@@ -862,6 +881,22 @@ dnl for now that is the way we do it.
USER_LD=$LD
USER_LDFLAGS="$LDFLAGS"
LD='$(CC)'
+case $host_os in
+ darwin*)
+ saved_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -Wl,-no_weak_imports"
+ AC_TRY_LINK([],[],
+ [
+ LD_MAY_BE_WEAK=no
+ ],
+ [
+ LD_MAY_BE_WEAK=yes
+ LDFLAGS="$saved_LDFLAGS"
+ ]);;
+ *)
+ LD_MAY_BE_WEAK=no;;
+esac
+
AC_SUBST(LD)
LDFLAG_RUNTIME_LIBRARY_PATH="$CFLAG_RUNTIME_LIBRARY_PATH"
@@ -1023,6 +1058,27 @@ esac
AC_MSG_RESULT($DIRTY_SCHEDULER_SUPPORT)
AC_SUBST(DIRTY_SCHEDULER_SUPPORT)
+AC_MSG_CHECKING(whether the new code purge strategy should be enabled)
+case $enable_new_purge-$enable_dirty_schedulers in
+ yes-*)
+ AC_MSG_RESULT(yes)
+ enable_new_purge=yes;;
+ default-yes)
+ AC_MSG_RESULT(yes; forced by dirty scheduler support)
+ enable_new_purge=yes;;
+ no-yes)
+ AC_MSG_ERROR([Dirty schedulers enabled, but new code purge strategy disabled]);;
+ *)
+ AC_MSG_RESULT(no)
+ enable_new_purge=no;;
+esac
+
+if test $enable_new_purge = yes; then
+ AC_DEFINE(ERTS_NEW_PURGE_STRATEGY, 1, [Define if you want to use the new code purge strategy])
+fi
+NEW_PURGE_STRATEGY=$enable_new_purge
+AC_SUBST(NEW_PURGE_STRATEGY)
+
if test $ERTS_BUILD_SMP_EMU = yes; then
if test $found_threads = no; then
@@ -1676,6 +1732,8 @@ if test "x$enable_sctp" != "xno" ; then
fi
if test x"$ac_cv_header_netinet_sctp_h" = x"yes"; then
+ AS_IF([test "x$enable_sctp" = "xlib"],
+ AC_CHECK_LIB(sctp, sctp_bindx))
AC_CHECK_FUNCS([sctp_bindx sctp_peeloff sctp_getladdrs sctp_freeladdrs sctp_getpaddrs sctp_freepaddrs])
AC_CHECK_DECLS([SCTP_UNORDERED, SCTP_ADDR_OVER, SCTP_ABORT,
SCTP_EOF, SCTP_SENDALL, SCTP_ADDR_CONFIRMED,
@@ -1929,7 +1987,7 @@ getaddrinfo("","",NULL,NULL);
if test $have_getaddrinfo = yes; then
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([whether getaddrinfo accepts enough flags])
- AC_TRY_COMPILE([
+ AC_TRY_LINK([
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_WINSOCK2_H
@@ -2044,7 +2102,7 @@ int main(void) {
fi]);;
esac
-if test $have_posix_memalign = yes; then
+if test "$have_posix_memalign" = "yes"; then
AC_DEFINE(HAVE_POSIX_MEMALIGN,[1],
[Define to 1 if you have the `posix_memalign' function.])
fi
@@ -2186,7 +2244,7 @@ dnl Checks for features/quirks in the system that affects Erlang.
dnl ----------------------------------------------------------------------
AC_MSG_CHECKING([for sched_getaffinity/sched_setaffinity])
-AC_TRY_COMPILE([#include <sched.h>],
+AC_TRY_LINK([#include <sched.h>],
[
#ifndef CPU_SETSIZE
#error no CPU_SETSIZE
@@ -2209,7 +2267,7 @@ fi
AC_MSG_CHECKING([for pset functionality])
-AC_TRY_COMPILE([#include <sys/pset.h>],
+AC_TRY_LINK([#include <sys/pset.h>],
[
int res;
psetid_t id = PS_MYID;
@@ -2227,7 +2285,7 @@ if test $pset_functionality = yes; then
fi
AC_MSG_CHECKING([for processor_bind functionality])
-AC_TRY_COMPILE([
+AC_TRY_LINK([
#include <sys/types.h>
#include <sys/processor.h>
#include <sys/procset.h>
@@ -2243,7 +2301,7 @@ if test $processor_bind_functionality = yes; then
fi
AC_MSG_CHECKING([for cpuset_getaffinity/cpuset_setaffinity])
-AC_TRY_COMPILE([
+AC_TRY_LINK([
#include <sys/param.h>
#include <sys/cpuset.h>
],
@@ -2442,7 +2500,7 @@ if test "x$ac_cv_func_sbrk" = "xyes"; then
for rtype in $ret_types; do
for atype in $arg_types; do
IFS=$save_ifs
- AC_TRY_COMPILE([#include <sys/types.h>
+ AC_TRY_LINK([#include <sys/types.h>
#include <unistd.h>],
[$rtype sbrk($atype incr);],
[erts_cv_sbrk_ret_arg_types="$rtype,$atype"])
@@ -2479,7 +2537,7 @@ if test $ac_cv_func_brk = yes; then
for rtype in $ret_types; do
for atype in $arg_types; do
IFS=$save_ifs
- AC_TRY_COMPILE([#include <sys/types.h>
+ AC_TRY_LINK([#include <sys/types.h>
#include <unistd.h>],
[$rtype brk($atype endds);],
[erts_cv_brk_ret_arg_types="$rtype,$atype"])
@@ -2736,6 +2794,23 @@ if test "$cross_compiling" != "yes" && test X${enable_hipe} != Xno; then
fi
fi
+dnl Check to disable -fPIE and friends for HiPE on amd64
+if test X${enable_hipe} = Xyes && test X$ARCH = Xamd64; then
+ AC_TRY_COMPILE(, [#if defined(__pie__) || defined(__PIE__)
+ #error -fPIE is enabled by default
+ #endif],
+ [AC_MSG_NOTICE([No -fPIE enabled by default])],
+ [AC_MSG_WARN([Security feature -fPIE will be disabled for HiPE])
+ STATIC_CFLAGS="-fno-PIE $STATIC_CFLAGS"
+ saved_LDFLAGS=$LDFLAGS
+ LDFLAGS="-no-pie $LDFLAGS"
+ AC_TRY_LINK(,, [],
+ [AC_MSG_WARN([Linked does not accept option -no-pie])
+ LDFLAGS=$saved_LDFLAGS])])
+
+fi
+
+
if test X${enable_fp_exceptions} = Xauto ; then
case $host_os in
*linux*)
@@ -2763,7 +2838,7 @@ if test X${enable_fp_exceptions} != Xyes ; then
FPE=unreliable
else
- AC_MSG_CHECKING([for unreliable floating point execptions])
+ AC_MSG_CHECKING([for unreliable floating point exceptions])
AC_TRY_RUN([
@@ -3501,7 +3576,7 @@ fi
#
if test $have_kernel_poll = epoll; then
AC_MSG_CHECKING([whether epoll is level triggered])
- AC_TRY_COMPILE([#include <sys/epoll.h>],[
+ AC_TRY_LINK([#include <sys/epoll.h>],[
#ifdef EPOLLET
/* Edge triggered option exist, assume level triggered
is default */
@@ -4816,8 +4891,7 @@ dnl Output the result.
dnl ----------------------------------------------------------------------
dnl Note that the output files are relative to $srcdir
-
-AC_OUTPUT(
+AC_CONFIG_FILES([
emulator/$host/Makefile:emulator/Makefile.in
epmd/src/$host/Makefile:epmd/src/Makefile.in
etc/common/$host/Makefile:etc/common/Makefile.in
@@ -4827,15 +4901,22 @@ AC_OUTPUT(
Makefile:Makefile.in
../make/$host/otp.mk:../make/otp.mk.in
../make/$host/otp_ded.mk:../make/otp_ded.mk.in
+])
+
+AC_CONFIG_FILES([../make/make_emakefile:../make/make_emakefile.in],
+ [chmod +x ../make/make_emakefile])
+
dnl
dnl The ones below should be moved to their respective lib
dnl
+dnl ../lib/ssl/c_src/$host/Makefile:../lib/ssl/c_src/Makefile.in
+AC_CONFIG_FILES([
../lib/ic/c_src/$host/Makefile:../lib/ic/c_src/Makefile.in
../lib/os_mon/c_src/$host/Makefile:../lib/os_mon/c_src/Makefile.in
-dnl ../lib/ssl/c_src/$host/Makefile:../lib/ssl/c_src/Makefile.in
../lib/crypto/c_src/$host/Makefile:../lib/crypto/c_src/Makefile.in
../lib/orber/c_src/$host/Makefile:../lib/orber/c_src/Makefile.in
../lib/runtime_tools/c_src/$host/Makefile:../lib/runtime_tools/c_src/Makefile.in
../lib/tools/c_src/$host/Makefile:../lib/tools/c_src/Makefile.in
- )
+ ])
+AC_OUTPUT
diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml
index 0b04f8f70e..ab00d47425 100644
--- a/erts/doc/src/absform.xml
+++ b/erts/doc/src/absform.xml
@@ -26,144 +26,198 @@
<prepared>Arndt Jonasson</prepared>
<responsible>Kenneth Lundin</responsible>
<docno>1</docno>
- <approved>Jultomten</approved>
+ <approved></approved>
<checked></checked>
- <date>00-12-01</date>
+ <date>2000-12-01</date>
<rev>A</rev>
<file>absform.xml</file>
</header>
- <p></p>
- <p>This document describes the standard representation of parse trees for Erlang
- programs as Erlang terms. This representation is known as the <em>abstract format</em>.
- Functions dealing with such parse trees are <c>compile:forms/[1,2]</c>
- and functions in the modules
- <c>epp</c>,
- <c>erl_eval</c>,
- <c>erl_lint</c>,
- <c>erl_pp</c>,
- <c>erl_parse</c>,
- and
- <c>io</c>.
- They are also used as input and output for parse transforms (see the module
- <c>compile</c>).</p>
+ <p>This section describes the standard representation of parse trees for Erlang
+ programs as Erlang terms. This representation is known as the <em>abstract
+ format</em>. Functions dealing with such parse trees are
+ <seealso marker="compiler:compile#forms/1">
+ <c>compile:forms/1,2</c></seealso> and functions in the following
+ modules:</p>
+
+ <list type="bulleted">
+ <item><seealso marker="stdlib:epp">
+ <c>epp(3)</c></seealso></item>
+ <item><seealso marker="stdlib:erl_eval">
+ <c>erl_eval(3)</c></seealso></item>
+ <item><seealso marker="stdlib:erl_lint">
+ <c>erl_lint(3)</c></seealso></item>
+ <item><seealso marker="stdlib:erl_parse">
+ <c>erl_parse(3)</c></seealso></item>
+ <item><seealso marker="stdlib:erl_pp">
+ <c>erl_pp(3)</c></seealso></item>
+ <item><seealso marker="stdlib:io">
+ <c>io(3)</c></seealso></item>
+ </list>
+
+ <p>The functions are also used as input and output for parse transforms, see
+ the <seealso marker="compiler:compile"><c>compile(3)</c></seealso>
+ module.</p>
+
<p>We use the function <c>Rep</c> to denote the mapping from an Erlang source
construct <c>C</c> to its abstract format representation <c>R</c>, and write
- <c>R = Rep(C)</c>.
- </p>
- <p>The word <c>LINE</c> below represents an integer, and denotes the
+ <c>R = Rep(C)</c>.</p>
+
+ <p>The word <c>LINE</c> in this section represents an integer, and denotes the
number of the line in the source file where the construction occurred.
- Several instances of <c>LINE</c> in the same construction may denote
+ Several instances of <c>LINE</c> in the same construction can denote
different lines.</p>
- <p>Since operators are not terms in their own right, when operators are
- mentioned below, the representation of an operator should be taken to
+
+ <p>As operators are not terms in their own right, when operators are
+ mentioned below, the representation of an operator is to be taken to
be the atom with a printname consisting of the same characters as the
- operator.
- </p>
+ operator.</p>
<section>
<title>Module Declarations and Forms</title>
- <p>A module declaration consists of a sequence of forms that are either
+ <p>A module declaration consists of a sequence of forms, which are either
function declarations or attributes.</p>
+
<list type="bulleted">
- <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>-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>-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>-file(File,Line)</c>, then
- Rep(F) = <c>{attribute,LINE,file,{File,Line}}</c>.</item>
- <item>If F is a function declaration
- <c>Name Fc_1 ; ... ; Name Fc_k</c>,
- where each <c>Fc_i</c> is a function clause with a
- pattern sequence of the same length <c>Arity</c>, then
- Rep(F) = <c>{function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}</c>.
- </item>
- <item>If F is a function specification
- <c>-Spec Name Ft_1; ...; Ft_k</c>,
- where <c>Spec</c> is either the atom <c>spec</c> or the atom
- <c>callback</c>, and each <c>Ft_i</c> is a possibly constrained
- function type with an argument sequence of the same length
- <c>Arity</c>, then Rep(F) =
- <c>{attribute,Line,Spec,{{Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}</c>.
- </item>
- <item>If F is a function specification
- <c>-spec Mod:Name Ft_1; ...; Ft_k</c>,
- where each <c>Ft_i</c> is a possibly constrained
- function type with an argument sequence of the same length
- <c>Arity</c>, then Rep(F) =
- <c>{attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}</c>.
- </item>
- <item>If F is a record declaration
- <c>-record(Name,{V_1, ..., V_k})</c>,
- where each <c>V_i</c> is a record field, then Rep(F) =
- <c>{attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}</c>.
- For Rep(V), see below.</item>
- <item>If F is a type declaration
- <c>-Type Name(V_1, ..., V_k) :: T</c>, where
- <c>Type</c> is either the atom <c>type</c> or the atom <c>opaque</c>,
- each <c>V_i</c> is a variable, and <c>T</c> is a type, then Rep(F) =
- <c>{attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ..., Rep(V_k)]}}</c>.
- </item>
- <item>If F is a wild attribute <c>-A(T)</c>, then
- Rep(F) = <c>{attribute,LINE,A,T}</c>.
- <br></br></item>
+ <item>
+ <p>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>.</p>
+ </item>
+ <item>
+ <p>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>.</p>
+ </item>
+ <item>
+ <p>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>.</p>
+ </item>
+ <item>
+ <p>If F is an attribute <c>-module(Mod)</c>, then
+ Rep(F) = <c>{attribute,LINE,module,Mod}</c>.</p>
+ </item>
+ <item>
+ <p>If F is an attribute <c>-file(File,Line)</c>, then
+ Rep(F) = <c>{attribute,LINE,file,{File,Line}}</c>.</p>
+ </item>
+ <item>
+ <p>If F is a function declaration <c>Name Fc_1 ; ... ; Name Fc_k</c>,
+ where each <c>Fc_i</c> is a function clause with a pattern sequence of
+ the same length <c>Arity</c>, then Rep(F) =
+ <c>{function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If F is a function specification <c>-Spec Name Ft_1; ...; Ft_k</c>,
+ where <c>Spec</c> is either the atom <c>spec</c> or the atom
+ <c>callback</c>, and each <c>Ft_i</c> is a possibly constrained
+ function type with an argument sequence of the same length
+ <c>Arity</c>, then Rep(F) =
+ <c>{attribute,Line,Spec,{{Name,Arity},[Rep(Ft_1), ...,
+ Rep(Ft_k)]}}</c>.</p>
+ </item>
+ <item>
+ <p>If F is a function specification
+ <c>-spec Mod:Name Ft_1; ...; Ft_k</c>, where each <c>Ft_i</c> is a
+ possibly constrained function type with an argument sequence of the
+ same length <c>Arity</c>, then Rep(F) =
+ <c>{attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ...,
+ Rep(Ft_k)]}}</c>.</p>
+ </item>
+ <item>
+ <p>If F is a record declaration <c>-record(Name,{V_1, ..., V_k})</c>,
+ where each <c>V_i</c> is a record field, then Rep(F) =
+ <c>{attribute,LINE,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}</c>.
+ For Rep(V), see below.</p>
+ </item>
+ <item>
+ <p>If F is a type declaration <c>-Type Name(V_1, ..., V_k) :: T</c>,
+ where <c>Type</c> is either the atom <c>type</c> or the atom
+ <c>opaque</c>, each <c>V_i</c> is a variable, and <c>T</c> is a type,
+ then Rep(F) =
+ <c>{attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ...,
+ Rep(V_k)]}}</c>.</p>
+ </item>
+ <item>
+ <p>If F is a wild attribute <c>-A(T)</c>, then
+ Rep(F) = <c>{attribute,LINE,A,T}</c>.</p>
+ </item>
</list>
<section>
<title>Record Fields</title>
- <p>Each field in a record declaration may have an optional
- explicit default initializer expression, as well as an
+ <p>Each field in a record declaration can have an optional,
+ explicit, default initializer expression, and an
optional type.</p>
+
<list type="bulleted">
- <item>If V is <c>A</c>, then
- Rep(V) = <c>{record_field,LINE,Rep(A)}</c>.</item>
- <item>If V is <c>A = E</c>,
- where <c>E</c> is an expression, then
- Rep(V) = <c>{record_field,LINE,Rep(A),Rep(E)}</c>.</item>
- <item>If V is <c>A :: T</c>, where <c>T</c> is a type, then Rep(V) =
- <c>{typed_record_field,{record_field,LINE,Rep(A)},Rep(T)}</c>.
- </item>
- <item>If V is <c>A = E :: T</c>, where
- <c>E</c> is an expression and <c>T</c> is a type, then Rep(V) =
- <c>{typed_record_field,{record_field,LINE,Rep(A),Rep(E)},Rep(T)}</c>.
+ <item>
+ <p>If V is <c>A</c>, then
+ Rep(V) = <c>{record_field,LINE,Rep(A)}</c>.</p>
+ </item>
+ <item>
+ <p>If V is <c>A = E</c>, where <c>E</c> is an expression, then
+ Rep(V) = <c>{record_field,LINE,Rep(A),Rep(E)}</c>.</p>
+ </item>
+ <item>
+ <p>If V is <c>A :: T</c>, where <c>T</c> is a type, then Rep(V) =
+ <c>{typed_record_field,{record_field,LINE,Rep(A)},Rep(T)}</c>.</p>
+ </item>
+ <item>
+ <p>If V is <c>A = E :: T</c>, where
+ <c>E</c> is an expression and <c>T</c> is a type, then Rep(V) =
+ <c>{typed_record_field,{record_field,LINE,Rep(A),Rep(E)},Rep(T)}</c>.
+ </p>
</item>
</list>
</section>
<section>
- <title>Representation of Parse Errors and End-of-file</title>
+ <title>Representation of Parse Errors and End-of-File</title>
<p>In addition to the representations of forms, the list that represents
- a module declaration (as returned by functions in <c>erl_parse</c> and
- <c>epp</c>) may contain tuples <c>{error,E}</c> and
- <c>{warning,W}</c>, denoting syntactically incorrect forms and
- warnings, and <c>{eof,LINE}</c>, denoting an end-of-stream
- encountered before a complete form had been parsed.</p>
+ a module declaration (as returned by functions in
+ <seealso marker="stdlib:epp"><c>epp(3)</c></seealso> and
+ <seealso marker="stdlib:erl_parse"><c>erl_parse(3)</c></seealso>)
+ can contain the following:</p>
+
+ <list type="bulleted">
+ <item>Tuples <c>{error,E}</c> and <c>{warning,W}</c>, denoting
+ syntactically incorrect forms and warnings</item>
+ <item><c>{eof,LINE}</c>, denoting an end-of-stream
+ encountered before a complete form had been parsed</item>
+ </list>
</section>
</section>
<section>
<title>Atomic Literals</title>
<p>There are five kinds of atomic literals, which are represented in the
- same way in patterns, expressions and guards:</p>
+ same way in patterns, expressions, and guards:</p>
+
<list type="bulleted">
- <item>If L is an atom literal, then
- Rep(L) = <c>{atom,LINE,L}</c>.</item>
- <item>If L is a character literal, then
- Rep(L) = <c>{char,LINE,L}</c>.</item>
- <item>If L is a float literal, then
- Rep(L) = <c>{float,LINE,L}</c>.</item>
- <item>If L is an integer literal, then
- Rep(L) = <c>{integer,LINE,L}</c>.</item>
- <item>If L is a string literal consisting of the characters
- <c>C_1</c>, ..., <c>C_k</c>, then
- Rep(L) = <c>{string,LINE,[C_1, ..., C_k]}</c>.</item>
+ <item>
+ <p>If L is an atom literal, then Rep(L) = <c>{atom,LINE,L}</c>.</p>
+ </item>
+ <item>
+ <p>If L is a character literal, then Rep(L) = <c>{char,LINE,L}</c>.</p>
+ </item>
+ <item>
+ <p>If L is a float literal, then Rep(L) = <c>{float,LINE,L}</c>.</p>
+ </item>
+ <item>
+ <p>If L is an integer literal, then
+ Rep(L) = <c>{integer,LINE,L}</c>.</p>
+ </item>
+ <item>
+ <p>If L is a string literal consisting of the characters
+ <c>C_1</c>, ..., <c>C_k</c>, then
+ Rep(L) = <c>{string,LINE,[C_1, ..., C_k]}</c>.</p>
+ </item>
</list>
- <p>Note that negative integer and float literals do not occur as such; they are
- parsed as an application of the unary negation operator.</p>
+
+ <p>Notice that negative integer and float literals do not occur as such;
+ they are parsed as an application of the unary negation operator.</p>
</section>
<section>
@@ -171,288 +225,424 @@
<p>If Ps is a sequence of patterns <c>P_1, ..., P_k</c>, then
Rep(Ps) = <c>[Rep(P_1), ..., Rep(P_k)]</c>. Such sequences occur as the
list of arguments to a function or fun.</p>
+
<p>Individual patterns are represented as follows:</p>
+
<list type="bulleted">
- <item>If P is an atomic literal <c>L</c>, then Rep(P) = Rep(L).</item>
- <item>If P is a bit string pattern
- <c>&lt;&lt;P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>></c>, where each
- <c>Size_i</c> is an expression that can be evaluated to an integer
- and each <c>TSL_i</c> is a type specificer list, then
- Rep(P) = <c>{bin,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
- For Rep(TSL), see below.
- An omitted <c>Size_i</c> is represented by <c>default</c>.
- An omitted <c>TSL_i</c> is represented by <c>default</c>.</item>
- <item>If P is a compound pattern <c>P_1 = P_2</c>, then
- Rep(P) = <c>{match,LINE,Rep(P_1),Rep(P_2)}</c>.</item>
- <item>If P is a cons pattern <c>[P_h | P_t]</c>, then
- Rep(P) = <c>{cons,LINE,Rep(P_h),Rep(P_t)}</c>.</item>
- <item>If P is a map pattern <c>#{A_1, ..., A_k}</c>, where each
- <c>A_i</c> is an association <c>P_i_1 := P_i_2</c>, then Rep(P) =
- <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>. For Rep(A), see
- below.</item>
- <item>If P is a nil pattern <c>[]</c>, then
- Rep(P) = <c>{nil,LINE}</c>.</item>
- <item>If P is an operator pattern <c>P_1 Op P_2</c>,
- where <c>Op</c> is a binary operator (this is either an occurrence
- of <c>++</c> applied to a literal string or character
- list, or an occurrence of an expression that can be evaluated to a number
- at compile time),
- then Rep(P) = <c>{op,LINE,Op,Rep(P_1),Rep(P_2)}</c>.</item>
- <item>If P is an operator pattern <c>Op P_0</c>,
- where <c>Op</c> is a unary operator (this is an occurrence of
- an expression that can be evaluated to a number at compile
- time), then Rep(P) = <c>{op,LINE,Op,Rep(P_0)}</c>.</item>
- <item>If P is a parenthesized pattern <c>( P_0 )</c>, then
- Rep(P) = <c>Rep(P_0)</c>,
- that is, parenthesized patterns cannot be distinguished from their
- bodies.</item>
- <item>If P is a record field index pattern <c>#Name.Field</c>,
- where <c>Field</c> is an atom, then
- Rep(P) = <c>{record_index,LINE,Name,Rep(Field)}</c>.</item>
- <item>If P is a record pattern
- <c>#Name{Field_1=P_1, ..., Field_k=P_k}</c>,
- where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(P) =
- <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(P_k)}]}</c>.</item>
- <item>If P is a tuple pattern <c>{P_1, ..., P_k}</c>, then
- Rep(P) = <c>{tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}</c>.</item>
- <item>If P is a universal pattern <c>_</c>, then
- Rep(P) = <c>{var,LINE,'_'}</c>.</item>
- <item>If P is a variable pattern <c>V</c>, then
- Rep(P) = <c>{var,LINE,A}</c>,
- where A is an atom with a printname consisting of the same characters as
- <c>V</c>.</item>
+ <item>
+ <p>If P is an atomic literal <c>L</c>, then Rep(P) = Rep(L).</p>
+ </item>
+ <item>
+ <p>If P is a bitstring pattern
+ <c>&lt;&lt;P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>></c>, where each
+ <c>Size_i</c> is an expression that can be evaluated to an integer,
+ and each <c>TSL_i</c> is a type specificer list, then Rep(P) =
+ <c>{bin,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)},
+ ..., {bin_element,LINE,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
+ For Rep(TSL), see below.
+ An omitted <c>Size_i</c> is represented by <c>default</c>.
+ An omitted <c>TSL_i</c> is represented by <c>default</c>.</p>
+ </item>
+ <item>
+ <p>If P is a compound pattern <c>P_1 = P_2</c>, then Rep(P) =
+ <c>{match,LINE,Rep(P_1),Rep(P_2)}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a cons pattern <c>[P_h | P_t]</c>, then Rep(P) =
+ <c>{cons,LINE,Rep(P_h),Rep(P_t)}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a map pattern <c>#{A_1, ..., A_k}</c>, where each
+ <c>A_i</c> is an association <c>P_i_1 := P_i_2</c>, then Rep(P) =
+ <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see below.</p>
+ </item>
+ <item>
+ <p>If P is a nil pattern <c>[]</c>, then Rep(P) =
+ <c>{nil,LINE}</c>.</p>
+ </item>
+ <item>
+ <p>If P is an operator pattern <c>P_1 Op P_2</c>, where <c>Op</c> is a
+ binary operator (this is either an occurrence of <c>++</c> applied to
+ a literal string or character list, or an occurrence of an expression
+ that can be evaluated to a number at compile time), then Rep(P) =
+ <c>{op,LINE,Op,Rep(P_1),Rep(P_2)}</c>.</p>
+ </item>
+ <item>
+ <p>If P is an operator pattern <c>Op P_0</c>, where <c>Op</c> is a
+ unary operator (this is an occurrence of an expression that can be
+ evaluated to a number at compile time), then Rep(P) =
+ <c>{op,LINE,Op,Rep(P_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a parenthesized pattern <c>( P_0 )</c>, then Rep(P) =
+ <c>Rep(P_0)</c>, that is, parenthesized patterns cannot be
+ distinguished from their bodies.</p>
+ </item>
+ <item>
+ <p>If P is a record field index pattern <c>#Name.Field</c>,
+ where <c>Field</c> is an atom, then Rep(P) =
+ <c>{record_index,LINE,Name,Rep(Field)}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a record pattern <c>#Name{Field_1=P_1, ..., Field_k=P_k}</c>,
+ where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(P) =
+ <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ...,
+ {record_field,LINE,Rep(Field_k),Rep(P_k)}]}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a tuple pattern <c>{P_1, ..., P_k}</c>, then Rep(P) =
+ <c>{tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If P is a universal pattern <c>_</c>, then Rep(P) =
+ <c>{var,LINE,'_'}</c>.</p></item>
+ <item>
+ <p>If P is a variable pattern <c>V</c>, then Rep(P) =
+ <c>{var,LINE,A}</c>, where A is an atom with a printname consisting
+ of the same characters as <c>V</c>.</p>
+ </item>
</list>
- <p>Note that every pattern has the same source form as some expression, and is
- represented the same way as the corresponding expression.</p>
+
+ <p>Notice that every pattern has the same source form as some expression,
+ and is represented in the same way as the corresponding expression.</p>
</section>
<section>
<title>Expressions</title>
- <p>A body B is a nonempty sequence of expressions <c>E_1, ..., E_k</c>,
+ <p>A body B is a non-empty sequence of expressions <c>E_1, ..., E_k</c>,
and Rep(B) = <c>[Rep(E_1), ..., Rep(E_k)]</c>.</p>
- <p>An expression E is one of the following alternatives:</p>
+
+ <p>An expression E is one of the following:</p>
+
<list type="bulleted">
- <item>If E is an atomic literal <c>L</c>, then Rep(E) = Rep(L).</item>
- <item>If E is a bit string comprehension
- <c>&lt;&lt;E_0 || Q_1, ..., Q_k>></c>,
- where each <c>Q_i</c> is a qualifier, then
- Rep(E) = <c>{bc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}</c>.
- For Rep(Q), see below.</item>
- <item>If E is a bit string constructor
- <c>&lt;&lt;E_1:Size_1/TSL_1, ..., E_k:Size_k/TSL_k>></c>,
- where each <c>Size_i</c> is an expression and each
- <c>TSL_i</c> is a type specificer list, then Rep(E) =
- <c>{bin,LINE,[{bin_element,LINE,Rep(E_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(E_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
- For Rep(TSL), see below.
- An omitted <c>Size_i</c> is represented by <c>default</c>.
- An omitted <c>TSL_i</c> is represented by <c>default</c>.</item>
- <item>If E is a block expression <c>begin B end</c>,
- where <c>B</c> is a body, then
- Rep(E) = <c>{block,LINE,Rep(B)}</c>.</item>
- <item>If E is a case expression <c>case E_0 of Cc_1 ; ... ; Cc_k end</c>,
- where <c>E_0</c> is an expression and each <c>Cc_i</c> is a
- case clause then Rep(E) =
- <c>{'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</item>
- <item>If E is a catch expression <c>catch E_0</c>, then
- Rep(E) = <c>{'catch',LINE,Rep(E_0)}</c>.</item>
- <item>If E is a cons skeleton <c>[E_h | E_t]</c>, then
- Rep(E) = <c>{cons,LINE,Rep(E_h),Rep(E_t)}</c>.</item>
- <item>If E is a fun expression <c>fun Name/Arity</c>, then
- Rep(E) = <c>{'fun',LINE,{function,Name,Arity}}</c>.</item>
- <item>If E is a fun expression
- <c>fun Module:Name/Arity</c>, then Rep(E) =
- <c>{'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}</c>.
- (Before the R15 release: Rep(E) =
- <c>{'fun',LINE,{function,Module,Name,Arity}}</c>.)</item>
- <item>If E is a fun expression <c>fun Fc_1 ; ... ; Fc_k end</c>,
- where each <c>Fc_i</c> is a function clause then Rep(E) =
- <c>{'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}</c>.</item>
- <item>If E is a fun expression
- <c>fun Name Fc_1 ; ... ; Name Fc_k end</c>,
- where <c>Name</c> is a variable and each
- <c>Fc_i</c> is a function clause then Rep(E) =
- <c>{named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}</c>.
- </item>
- <item>If E is a function call <c>E_0(E_1, ..., E_k)</c>, then
- Rep(E) = <c>{call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}</c>.</item>
- <item>If E is a function call <c>E_m:E_0(E_1, ..., E_k)</c>,
- then Rep(E) =
- <c>{call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ..., Rep(E_k)]}</c>.
- </item>
- <item>If E is an if expression <c>if Ic_1 ; ... ; Ic_k end</c>,
- where each <c>Ic_i</c> is an if clause then Rep(E) =
- <c>{'if',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}</c>.</item>
- <item>If E is a list comprehension <c>[E_0 || Q_1, ..., Q_k]</c>,
- where each <c>Q_i</c> is a qualifier, then Rep(E) =
- <c>{lc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}</c>. For Rep(Q), see
- below.</item>
- <item>If E is a map creation <c>#{A_1, ..., A_k}</c>,
- where each <c>A_i</c> is an association <c>E_i_1 => E_i_2</c>
- or <c>E_i_1 := E_i_2</c>, then Rep(E) =
- <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>. For Rep(A), see
- below.</item>
- <item>If E is a map update <c>E_0#{A_1, ..., A_k}</c>,
- where each <c>A_i</c> is an association <c>E_i_1 => E_i_2</c>
- or <c>E_i_1 := E_i_2</c>, then Rep(E) =
- <c>{map,LINE,Rep(E_0),[Rep(A_1), ..., Rep(A_k)]}</c>.
- For Rep(A), see below.</item>
- <item>If E is a match operator expression <c>P = E_0</c>,
- where <c>P</c> is a pattern, then
- Rep(E) = <c>{match,LINE,Rep(P),Rep(E_0)}</c>.</item>
- <item>If E is nil, <c>[]</c>, then
- Rep(E) = <c>{nil,LINE}</c>.</item>
- <item>If E is an operator expression <c>E_1 Op E_2</c>,
- where <c>Op</c> is a binary operator other than the match
- operator <c>=</c>, then
- Rep(E) = <c>{op,LINE,Op,Rep(E_1),Rep(E_2)}</c>.</item>
- <item>If E is an operator expression <c>Op E_0</c>,
- where <c>Op</c> is a unary operator, then
- Rep(E) = <c>{op,LINE,Op,Rep(E_0)}</c>.</item>
- <item>If E is a parenthesized expression <c>( E_0 )</c>, then
- Rep(E) = <c>Rep(E_0)</c>, that is, parenthesized
- expressions cannot be distinguished from their bodies.</item>
- <item>If E is a receive expression <c>receive Cc_1 ; ... ; Cc_k end</c>,
- where each <c>Cc_i</c> is a case clause then Rep(E) =
- <c>{'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</item>
- <item>If E is a receive expression
- <c>receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end</c>,
- where each <c>Cc_i</c> is a case clause,
- <c>E_0</c> is an expression and <c>B_t</c> is a body, then Rep(E) =
- <c>{'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)],Rep(E_0),Rep(B_t)}</c>.</item>
- <item>If E is a record creation
- <c>#Name{Field_1=E_1, ..., Field_k=E_k}</c>,
- where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(E) =
- <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</item>
- <item>If E is a record field access <c>E_0#Name.Field</c>,
- where <c>Field</c> is an atom, then
- Rep(E) = <c>{record_field,LINE,Rep(E_0),Name,Rep(Field)}</c>.</item>
- <item>If E is a record field index <c>#Name.Field</c>,
- where <c>Field</c> is an atom, then
- Rep(E) = <c>{record_index,LINE,Name,Rep(Field)}</c>.</item>
- <item>If E is a record update
- <c>E_0#Name{Field_1=E_1, ..., Field_k=E_k}</c>,
- where each <c>Field_i</c> is an atom, then Rep(E) =
- <c>{record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</item>
- <item>If E is a tuple skeleton <c>{E_1, ..., E_k}</c>, then
- Rep(E) = <c>{tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}</c>.</item>
- <item>If E is a try expression <c>try B catch Tc_1 ; ... ; Tc_k end</c>,
- where <c>B</c> is a body and each <c>Tc_i</c> is a catch clause then
- Rep(E) =
- <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}</c>.</item>
- <item>If E is a try expression
- <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end</c>,
- where <c>B</c> is a body,
- each <c>Cc_i</c> is a case clause and
- each <c>Tc_j</c> is a catch clause then Rep(E) =
- <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],[]}</c>.</item>
- <item>If E is a try expression <c>try B after A end</c>,
- where <c>B</c> and <c>A</c> are bodies then Rep(E) =
- <c>{'try',LINE,Rep(B),[],[],Rep(A)}</c>.</item>
- <item>If E is a try expression
- <c>try B of Cc_1 ; ... ; Cc_k after A end</c>,
- where <c>B</c> and <c>A</c> are a bodies and
- each <c>Cc_i</c> is a case clause then Rep(E) =
- <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[],Rep(A)}</c>.</item>
- <item>If E is a try expression
- <c>try B catch Tc_1 ; ... ; Tc_k after A end</c>,
- where <c>B</c> and <c>A</c> are bodies and
- each <c>Tc_i</c> is a catch clause then Rep(E) =
- <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],Rep(A)}</c>.</item>
- <item>If E is a try expression
- <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A end</c>,
- where <c>B</c> and <c>A</c> are a bodies,
- each <c>Cc_i</c> is a case clause, and
- each <c>Tc_j</c> is a catch clause then
- Rep(E) =
- <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],Rep(A)}</c>.</item>
- <item>If E is a variable <c>V</c>, then Rep(E) = <c>{var,LINE,A}</c>,
- where <c>A</c> is an atom with a printname consisting of the same
- characters as <c>V</c>.</item>
+ <item>
+ <p>If E is an atomic literal <c>L</c>, then Rep(E) = Rep(L).</p>
+ </item>
+ <item>
+ <p>If E is a bitstring comprehension
+ <c>&lt;&lt;E_0 || Q_1, ..., Q_k>></c>,
+ where each <c>Q_i</c> is a qualifier, then Rep(E) =
+ <c>{bc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}</c>.
+ For Rep(Q), see below.</p>
+ </item>
+ <item>
+ <p>If E is a bitstring constructor
+ <c>&lt;&lt;E_1:Size_1/TSL_1, ..., E_k:Size_k/TSL_k>></c>,
+ where each <c>Size_i</c> is an expression and each
+ <c>TSL_i</c> is a type specificer list, then Rep(E) =
+ <c>{bin,LINE,[{bin_element,LINE,Rep(E_1),Rep(Size_1),Rep(TSL_1)},
+ ..., {bin_element,LINE,Rep(E_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
+ For Rep(TSL), see below.
+ An omitted <c>Size_i</c> is represented by <c>default</c>.
+ An omitted <c>TSL_i</c> is represented by <c>default</c>.</p>
+ </item>
+ <item>
+ <p>If E is a block expression <c>begin B end</c>,
+ where <c>B</c> is a body, then Rep(E) =
+ <c>{block,LINE,Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a case expression <c>case E_0 of Cc_1 ; ... ; Cc_k end</c>,
+ where <c>E_0</c> is an expression and each <c>Cc_i</c> is a
+ case clause, then Rep(E) =
+ <c>{'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a catch expression <c>catch E_0</c>, then Rep(E) =
+ <c>{'catch',LINE,Rep(E_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a cons skeleton <c>[E_h | E_t]</c>, then Rep(E) =
+ <c>{cons,LINE,Rep(E_h),Rep(E_t)}</c>.</p>
+ </item>
+ <item>
+ <p>>If E is a fun expression <c>fun Name/Arity</c>, then Rep(E) =
+ <c>{'fun',LINE,{function,Name,Arity}}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a fun expression <c>fun Module:Name/Arity</c>, then Rep(E) =
+ <c>{'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}</c>.
+ (Before Erlang/OTP R15: Rep(E) =
+ <c>{'fun',LINE,{function,Module,Name,Arity}}</c>.)</p>
+ </item>
+ <item>
+ <p>If E is a fun expression <c>fun Fc_1 ; ... ; Fc_k end</c>,
+ where each <c>Fc_i</c> is a function clause, then Rep(E) =
+ <c>{'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a fun expression <c>fun Name Fc_1 ; ... ; Name Fc_k end</c>,
+ where <c>Name</c> is a variable and each
+ <c>Fc_i</c> is a function clause, then Rep(E) =
+ <c>{named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a function call <c>E_0(E_1, ..., E_k)</c>, then Rep(E) =
+ <c>{call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a function call <c>E_m:E_0(E_1, ..., E_k)</c>, then Rep(E) =
+ <c>{call,LINE,{remote,LINE,Rep(E_m),Rep(E_0)},[Rep(E_1), ...,
+ Rep(E_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is an if expression <c>if Ic_1 ; ... ; Ic_k end</c>,
+ where each <c>Ic_i</c> is an if clause, then Rep(E) =
+ <c>{'if',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a list comprehension <c>[E_0 || Q_1, ..., Q_k]</c>,
+ where each <c>Q_i</c> is a qualifier, then Rep(E) =
+ <c>{lc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}</c>.
+ For Rep(Q), see below.</p>
+ </item>
+ <item>
+ <p>If E is a map creation <c>#{A_1, ..., A_k}</c>,
+ where each <c>A_i</c> is an association <c>E_i_1 => E_i_2</c>
+ or <c>E_i_1 := E_i_2</c>, then Rep(E) =
+ <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see below.</p>
+ </item>
+ <item>
+ <p>If E is a map update <c>E_0#{A_1, ..., A_k}</c>,
+ where each <c>A_i</c> is an association <c>E_i_1 => E_i_2</c>
+ or <c>E_i_1 := E_i_2</c>, then Rep(E) =
+ <c>{map,LINE,Rep(E_0),[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see below.</p>
+ </item>
+ <item>
+ <p>If E is a match operator expression <c>P = E_0</c>,
+ where <c>P</c> is a pattern, then Rep(E) =
+ <c>{match,LINE,Rep(P),Rep(E_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is nil, <c>[]</c>, then Rep(E) = <c>{nil,LINE}</c>.</p>
+ </item>
+ <item>
+ <p>If E is an operator expression <c>E_1 Op E_2</c>,
+ where <c>Op</c> is a binary operator other than match operator
+ <c>=</c>, then Rep(E) =
+ <c>{op,LINE,Op,Rep(E_1),Rep(E_2)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is an operator expression <c>Op E_0</c>,
+ where <c>Op</c> is a unary operator, then Rep(E) =
+ <c>{op,LINE,Op,Rep(E_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a parenthesized expression <c>( E_0 )</c>, then Rep(E) =
+ <c>Rep(E_0)</c>, that is, parenthesized expressions cannot be
+ distinguished from their bodies.</p>
+ </item>
+ <item>
+ <p>If E is a receive expression <c>receive Cc_1 ; ... ; Cc_k end</c>,
+ where each <c>Cc_i</c> is a case clause, then Rep(E) =
+ <c>{'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a receive expression
+ <c>receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end</c>,
+ where each <c>Cc_i</c> is a case clause, <c>E_0</c> is an expression,
+ and <c>B_t</c> is a body, then Rep(E) =
+ <c>{'receive',LINE,[Rep(Cc_1), ...,
+ Rep(Cc_k)],Rep(E_0),Rep(B_t)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a record creation
+ <c>#Name{Field_1=E_1, ..., Field_k=E_k}</c>,
+ where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(E) =
+ <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)},
+ ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a record field access <c>E_0#Name.Field</c>,
+ where <c>Field</c> is an atom, then Rep(E) =
+ <c>{record_field,LINE,Rep(E_0),Name,Rep(Field)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a record field index <c>#Name.Field</c>,
+ where <c>Field</c> is an atom, then Rep(E) =
+ <c>{record_index,LINE,Name,Rep(Field)}</c>.</p></item>
+ <item>
+ <p>If E is a record update
+ <c>E_0#Name{Field_1=E_1, ..., Field_k=E_k}</c>,
+ where each <c>Field_i</c> is an atom, then Rep(E) =
+ <c>{record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)},
+ ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a tuple skeleton <c>{E_1, ..., E_k}</c>, then Rep(E) =
+ <c>{tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression <c>try B catch Tc_1 ; ... ; Tc_k end</c>,
+ where <c>B</c> is a body and each <c>Tc_i</c> is a catch clause,
+ then Rep(E) =
+ <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression
+ <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end</c>,
+ where <c>B</c> is a body, each <c>Cc_i</c> is a case clause, and
+ each <c>Tc_j</c> is a catch clause, then Rep(E) =
+ <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ...,
+ Rep(Tc_n)],[]}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression <c>try B after A end</c>,
+ where <c>B</c> and <c>A</c> are bodies, then Rep(E) =
+ <c>{'try',LINE,Rep(B),[],[],Rep(A)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression
+ <c>try B of Cc_1 ; ... ; Cc_k after A end</c>,
+ where <c>B</c> and <c>A</c> are a bodies,
+ and each <c>Cc_i</c> is a case clause, then Rep(E) =
+ <c>{'try',LINE,Rep(B),[Rep(Cc_1), ...,
+ Rep(Cc_k)],[],Rep(A)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression
+ <c>try B catch Tc_1 ; ... ; Tc_k after A end</c>,
+ where <c>B</c> and <c>A</c> are bodies,
+ and each <c>Tc_i</c> is a catch clause, then Rep(E) =
+ <c>{'try',LINE,Rep(B),[],[Rep(Tc_1), ...,
+ Rep(Tc_k)],Rep(A)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a try expression
+ <c>try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A
+ end</c>, where <c>B</c> and <c>A</c> are a bodies,
+ each <c>Cc_i</c> is a case clause,
+ and each <c>Tc_j</c> is a catch clause, then Rep(E) =
+ <c>{'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ...,
+ Rep(Tc_n)],Rep(A)}</c>.</p>
+ </item>
+ <item>
+ <p>If E is a variable <c>V</c>, then Rep(E) = <c>{var,LINE,A}</c>,
+ where <c>A</c> is an atom with a printname consisting of the same
+ characters as <c>V</c>.</p>
+ </item>
</list>
<section>
<title>Qualifiers</title>
- <p>A qualifier Q is one of the following alternatives:</p>
+ <p>A qualifier Q is one of the following:</p>
+
<list type="bulleted">
- <item>If Q is a filter <c>E</c>, where <c>E</c> is an expression, then
- Rep(Q) = <c>Rep(E)</c>.</item>
- <item>If Q is a generator <c>P &lt;- E</c>, where <c>P</c> is
- a pattern and <c>E</c> is an expression, then
- Rep(Q) = <c>{generate,LINE,Rep(P),Rep(E)}</c>.</item>
- <item>If Q is a bit string generator
- <c>P &lt;= E</c>, where <c>P</c> is
- a pattern and <c>E</c> is an expression, then
- Rep(Q) = <c>{b_generate,LINE,Rep(P),Rep(E)}</c>.</item>
+ <item>
+ <p>If Q is a filter <c>E</c>, where <c>E</c> is an expression, then
+ Rep(Q) = <c>Rep(E)</c>.</p>
+ </item>
+ <item>
+ <p>If Q is a generator <c>P &lt;- E</c>, where <c>P</c> is
+ a pattern and <c>E</c> is an expression, then Rep(Q) =
+ <c>{generate,LINE,Rep(P),Rep(E)}</c>.</p>
+ </item>
+ <item>
+ <p>If Q is a bitstring generator <c>P &lt;= E</c>, where <c>P</c> is
+ a pattern and <c>E</c> is an expression, then Rep(Q) =
+ <c>{b_generate,LINE,Rep(P),Rep(E)}</c>.</p>
+ </item>
</list>
</section>
<section>
- <title>Bit String Element Type Specifiers</title>
- <p>A type specifier list TSL for a bit string element is a sequence
+ <title>Bitstring Element Type Specifiers</title>
+ <p>A type specifier list TSL for a bitstring element is a sequence
of type specifiers <c>TS_1 - ... - TS_k</c>, and
Rep(TSL) = <c>[Rep(TS_1), ..., Rep(TS_k)]</c>.</p>
+
<list type="bulleted">
- <item>If TS is a type specifier <c>A</c>, where <c>A</c> is an atom,
- then Rep(TS) = <c>A</c>.</item>
- <item>If TS is a type specifier <c>A:Value</c>,
- where <c>A</c> is an atom and <c>Value</c> is an integer,
- then Rep(TS) = <c>{A,Value}</c>.</item>
+ <item>
+ <p>If TS is a type specifier <c>A</c>, where <c>A</c> is an atom,
+ then Rep(TS) = <c>A</c>.</p>
+ </item>
+ <item>
+ <p>If TS is a type specifier <c>A:Value</c>,
+ where <c>A</c> is an atom and <c>Value</c> is an integer,
+ then Rep(TS) = <c>{A,Value}</c>.</p>
+ </item>
</list>
</section>
<section>
<title>Associations</title>
- <p>An association A is one of the following alternatives:</p>
+ <p>An association A is one of the following:</p>
+
<list type="bulleted">
- <item>If A is an association <c>K => V</c>,
- then Rep(A) = <c>{map_field_assoc,LINE,Rep(K),Rep(V)}</c>.
- </item>
- <item>If A is an association <c>K := V</c>,
- then Rep(A) = <c>{map_field_exact,LINE,Rep(K),Rep(V)}</c>.
- </item>
+ <item>
+ <p>If A is an association <c>K => V</c>,
+ then Rep(A) = <c>{map_field_assoc,LINE,Rep(K),Rep(V)}</c>.</p>
+ </item>
+ <item>
+ <p>If A is an association <c>K := V</c>,
+ then Rep(A) = <c>{map_field_exact,LINE,Rep(K),Rep(V)}</c>.</p>
+ </item>
</list>
</section>
</section>
<section>
<title>Clauses</title>
- <p>There are function clauses, if clauses, case clauses
+ <p>There are function clauses, if clauses, case clauses,
and catch clauses.</p>
- <p>A clause <c>C</c> is one of the following alternatives:</p>
+
+ <p>A clause C is one of the following:</p>
+
<list type="bulleted">
- <item>If C is a case clause <c>P -> B</c>,
- where <c>P</c> is a pattern and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep(P)],[],Rep(B)}</c>.</item>
- <item>If C is a case clause <c>P when Gs -> B</c>,
- where <c>P</c> is a pattern,
- <c>Gs</c> is a guard sequence and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep(P)],Rep(Gs),Rep(B)}</c>.</item>
- <item>If C is a catch clause <c>P -> B</c>,
- where <c>P</c> is a pattern and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],[],Rep(B)}</c>.</item>
- <item>If C is a catch clause <c>X : P -> B</c>,
- where <c>X</c> is an atomic literal or a variable pattern,
- <c>P</c> is a pattern, and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],[],Rep(B)}</c>.</item>
- <item>If C is a catch clause <c>P when Gs -> B</c>,
- where <c>P</c> is a pattern, <c>Gs</c> is a guard sequence,
- and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}</c>.</item>
- <item>If C is a catch clause <c>X : P when Gs -> B</c>,
- where <c>X</c> is an atomic literal or a variable pattern,
- <c>P</c> is a pattern, <c>Gs</c> is a guard sequence,
- and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}</c>.</item>
- <item>If C is a function clause <c>( Ps ) -> B</c>,
- where <c>Ps</c> is a pattern sequence and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,Rep(Ps),[],Rep(B)}</c>.</item>
- <item>If C is a function clause <c>( Ps ) when Gs -> B</c>,
- where <c>Ps</c> is a pattern sequence,
- <c>Gs</c> is a guard sequence and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}</c>.</item>
- <item>If C is an if clause <c>Gs -> B</c>,
- where <c>Gs</c> is a guard sequence and <c>B</c> is a body, then
- Rep(C) = <c>{clause,LINE,[],Rep(Gs),Rep(B)}</c>.</item>
+ <item>
+ <p>If C is a case clause <c>P -> B</c>,
+ where <c>P</c> is a pattern and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep(P)],[],Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a case clause <c>P when Gs -> B</c>,
+ where <c>P</c> is a pattern,
+ <c>Gs</c> is a guard sequence, and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep(P)],Rep(Gs),Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a catch clause <c>P -> B</c>,
+ where <c>P</c> is a pattern and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],[],Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a catch clause <c>X : P -> B</c>,
+ where <c>X</c> is an atomic literal or a variable pattern,
+ <c>P</c> is a pattern, and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],[],Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a catch clause <c>P when Gs -> B</c>,
+ where <c>P</c> is a pattern, <c>Gs</c> is a guard sequence,
+ and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a catch clause <c>X : P when Gs -> B</c>,
+ where <c>X</c> is an atomic literal or a variable pattern,
+ <c>P</c> is a pattern, <c>Gs</c> is a guard sequence,
+ and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a function clause <c>( Ps ) -> B</c>,
+ where <c>Ps</c> is a pattern sequence and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,Rep(Ps),[],Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is a function clause <c>( Ps ) when Gs -> B</c>,
+ where <c>Ps</c> is a pattern sequence,
+ <c>Gs</c> is a guard sequence and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}</c>.</p>
+ </item>
+ <item>
+ <p>If C is an if clause <c>Gs -> B</c>,
+ where <c>Gs</c> is a guard sequence and <c>B</c> is a body, then
+ Rep(C) = <c>{clause,LINE,[],Rep(Gs),Rep(B)}</c>.</p>
+ </item>
</list>
</section>
@@ -460,205 +650,292 @@
<title>Guards</title>
<p>A guard sequence Gs is a sequence of guards <c>G_1; ...; G_k</c>, and
Rep(Gs) = <c>[Rep(G_1), ..., Rep(G_k)]</c>. If the guard sequence is
- empty, Rep(Gs) = <c>[]</c>.</p>
- <p>A guard G is a nonempty sequence of guard tests
+ empty, then Rep(Gs) = <c>[]</c>.</p>
+
+ <p>A guard G is a non-empty sequence of guard tests
<c>Gt_1, ..., Gt_k</c>, and Rep(G) =
<c>[Rep(Gt_1), ..., Rep(Gt_k)]</c>.</p>
- <p>A guard test <c>Gt</c> is one of the following alternatives:</p>
+
+ <p>A guard test Gt is one of the following:</p>
+
<list type="bulleted">
- <item>If Gt is an atomic literal <c>L</c>, then Rep(Gt) = Rep(L).</item>
- <item>If Gt is a bit string constructor
- <c>&lt;&lt;Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>></c>,
- where each <c>Size_i</c> is a guard test and each
- <c>TSL_i</c> is a type specificer list, then
- Rep(Gt) = <c>{bin,LINE,[{bin_element,LINE,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,Rep(Gt_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
- For Rep(TSL), see above.
- An omitted <c>Size_i</c> is represented by <c>default</c>.
- An omitted <c>TSL_i</c> is represented by <c>default</c>.</item>
- <item>If Gt is a cons skeleton <c>[Gt_h | Gt_t]</c>, then
- Rep(Gt) = <c>{cons,LINE,Rep(Gt_h),Rep(Gt_t)}</c>.</item>
- <item>If Gt is a function call <c>A(Gt_1, ..., Gt_k)</c>,
- where <c>A</c> is an atom, then Rep(Gt) =
- <c>{call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</item>
- <item>If Gt is a function call <c>A_m:A(Gt_1, ..., Gt_k)</c>,
- where <c>A_m</c> is the atom <c>erlang</c> and <c>A</c> is
- an atom or an operator, then Rep(Gt) =
- <c>{call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</item>
- <item>If Gt is a map creation <c>#{A_1, ..., A_k}</c>,
- where each <c>A_i</c> is an association <c>Gt_i_1 => Gt_i_2</c>
- or <c>Gt_i_1 := Gt_i_2</c>, then Rep(Gt) =
- <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>. For Rep(A), see
- above.</item>
- <item>If Gt is a map update <c>Gt_0#{A_1, ..., A_k}</c>, where each
- <c>A_i</c> is an association <c>Gt_i_1 => Gt_i_2</c>
- or <c>Gt_i_1 := Gt_i_2</c>, then Rep(Gt) =
- <c>{map,LINE,Rep(Gt_0),[Rep(A_1), ..., Rep(A_k)]}</c>.
- For Rep(A), see above.</item>
- <item>If Gt is nil, <c>[]</c>,
- then Rep(Gt) = <c>{nil,LINE}</c>.</item>
- <item>If Gt is an operator guard test <c>Gt_1 Op Gt_2</c>,
- where <c>Op</c> is a binary operator other than the match
- operator <c>=</c>, then
- Rep(Gt) = <c>{op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}</c>.</item>
- <item>If Gt is an operator guard test <c>Op Gt_0</c>,
- where <c>Op</c> is a unary operator, then
- Rep(Gt) = <c>{op,LINE,Op,Rep(Gt_0)}</c>.</item>
- <item>If Gt is a parenthesized guard test <c>( Gt_0 )</c>, then
- Rep(Gt) = <c>Rep(Gt_0)</c>, that is, parenthesized
- guard tests cannot be distinguished from their bodies.</item>
- <item>If Gt is a record creation
- <c>#Name{Field_1=Gt_1, ..., Field_k=Gt_k}</c>,
- where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(Gt) =
- <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}</c>.</item>
- <item>If Gt is a record field access <c>Gt_0#Name.Field</c>,
- where <c>Field</c> is an atom, then
- Rep(Gt) = <c>{record_field,LINE,Rep(Gt_0),Name,Rep(Field)}</c>.</item>
- <item>If Gt is a record field index <c>#Name.Field</c>,
- where <c>Field</c> is an atom, then
- Rep(Gt) = <c>{record_index,LINE,Name,Rep(Field)}</c>.</item>
- <item>If Gt is a tuple skeleton <c>{Gt_1, ..., Gt_k}</c>, then
- Rep(Gt) = <c>{tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</item>
- <item>If Gt is a variable pattern <c>V</c>, then
- Rep(Gt) = <c>{var,LINE,A}</c>, where A is an atom with
- a printname consisting of the same characters as <c>V</c>.</item>
+ <item>
+ <p>If Gt is an atomic literal <c>L</c>, then Rep(Gt) = Rep(L).</p>
+ </item>
+ <item>
+ <p>If Gt is a bitstring constructor
+ <c>&lt;&lt;Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>></c>,
+ where each <c>Size_i</c> is a guard test and each
+ <c>TSL_i</c> is a type specificer list, then Rep(Gt) =
+ <c>{bin,LINE,[{bin_element,LINE,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)},
+ ..., {bin_element,LINE,Rep(Gt_k),Rep(Size_k),Rep(TSL_k)}]}</c>.
+ For Rep(TSL), see above.
+ An omitted <c>Size_i</c> is represented by <c>default</c>.
+ An omitted <c>TSL_i</c> is represented by <c>default</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a cons skeleton <c>[Gt_h | Gt_t]</c>, then Rep(Gt) =
+ <c>{cons,LINE,Rep(Gt_h),Rep(Gt_t)}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a function call <c>A(Gt_1, ..., Gt_k)</c>,
+ where <c>A</c> is an atom, then Rep(Gt) =
+ <c>{call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a function call <c>A_m:A(Gt_1, ..., Gt_k)</c>,
+ where <c>A_m</c> is the atom <c>erlang</c> and <c>A</c> is
+ an atom or an operator, then Rep(Gt) =
+ <c>{call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ...,
+ Rep(Gt_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a map creation <c>#{A_1, ..., A_k}</c>,
+ where each <c>A_i</c> is an association <c>Gt_i_1 => Gt_i_2</c>
+ or <c>Gt_i_1 := Gt_i_2</c>, then Rep(Gt) =
+ <c>{map,LINE,[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see above.</p>
+ </item>
+ <item>
+ <p>If Gt is a map update <c>Gt_0#{A_1, ..., A_k}</c>,
+ where each <c>A_i</c> is an association <c>Gt_i_1 => Gt_i_2</c>
+ or <c>Gt_i_1 := Gt_i_2</c>, then Rep(Gt) =
+ <c>{map,LINE,Rep(Gt_0),[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see above.</p>
+ </item>
+ <item>
+ <p>If Gt is nil, <c>[]</c>, then Rep(Gt) = <c>{nil,LINE}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is an operator guard test <c>Gt_1 Op Gt_2</c>,
+ where <c>Op</c> is a binary operator other than match
+ operator <c>=</c>, then Rep(Gt) =
+ <c>{op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is an operator guard test <c>Op Gt_0</c>,
+ where <c>Op</c> is a unary operator, then Rep(Gt) =
+ <c>{op,LINE,Op,Rep(Gt_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a parenthesized guard test <c>( Gt_0 )</c>, then Rep(Gt) =
+ <c>Rep(Gt_0)</c>, that is, parenthesized
+ guard tests cannot be distinguished from their bodies.</p>
+ </item>
+ <item>
+ <p>If Gt is a record creation
+ <c>#Name{Field_1=Gt_1, ..., Field_k=Gt_k}</c>,
+ where each <c>Field_i</c> is an atom or <c>_</c>, then Rep(Gt) =
+ <c>{record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)},
+ ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a record field access <c>Gt_0#Name.Field</c>,
+ where <c>Field</c> is an atom, then Rep(Gt) =
+ <c>{record_field,LINE,Rep(Gt_0),Name,Rep(Field)}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a record field index <c>#Name.Field</c>,
+ where <c>Field</c> is an atom, then Rep(Gt) =
+ <c>{record_index,LINE,Name,Rep(Field)}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a tuple skeleton <c>{Gt_1, ..., Gt_k}</c>, then Rep(Gt) =
+ <c>{tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If Gt is a variable pattern <c>V</c>, then Rep(Gt) =
+ <c>{var,LINE,A}</c>, where A is an atom with
+ a printname consisting of the same characters as <c>V</c>.</p>
+ </item>
</list>
- <p>Note that every guard test has the same source form as some expression,
- and is represented the same way as the corresponding expression.</p>
+
+ <p>Notice that every guard test has the same source form as some expression,
+ and is represented in the same way as the corresponding expression.</p>
</section>
<section>
<title>Types</title>
<list type="bulleted">
- <item>If T is an annotated type <c>A :: T_0</c>,
- where <c>A</c> is a variable, then Rep(T) =
- <c>{ann_type,LINE,[Rep(A),Rep(T_0)]}</c>.</item>
- <item>If T is an atom or integer literal L, then Rep(T) = Rep(L).
- </item>
- <item>If T is a bit string type <c>&lt;&lt;_:M,_:_*N>></c>,
- where <c>M</c> and <c>N</c> are singleton integer types, then Rep(T) =
- <c>{type,LINE,binary,[Rep(M),Rep(N)]}</c>.</item>
- <item>If T is the empty list type <c>[]</c>, then Rep(T) =
- <c>{type,Line,nil,[]}</c>.</item>
- <item>If T is a fun type <c>fun()</c>, then Rep(T) =
- <c>{type,LINE,'fun',[]}</c>.</item>
- <item>If T is a fun type <c>fun((...) -> T_0)</c>, then
- Rep(T) = <c>{type,LINE,'fun',[{type,LINE,any},Rep(T_0)]}</c>.
- </item>
- <item>If T is a fun type <c>fun(Ft)</c>, where
- <c>Ft</c> is a function type,
- then Rep(T) = <c>Rep(Ft)</c>. For Rep(Ft), see below.</item>
- <item>If T is an integer range type <c>L .. H</c>,
- where <c>L</c> and <c>H</c> are singleton integer types, then
- Rep(T) = <c>{type,LINE,range,[Rep(L),Rep(H)]}</c>.</item>
- <item>If T is a map type <c>map()</c>, then Rep(T) =
- <c>{type,LINE,map,any}</c>.</item>
- <item>If T is a map type <c>#{A_1, ..., A_k}</c>, where each
- <c>A_i</c> is an association type, then Rep(T) =
- <c>{type,LINE,map,[Rep(A_1), ..., Rep(A_k)]}</c>.
- For Rep(A), see below.</item>
- <item>If T is an operator type <c>T_1 Op T_2</c>,
- where <c>Op</c> is a binary operator (this is an occurrence of
- an expression that can be evaluated to an integer at compile
- time), then
- Rep(T) = <c>{op,LINE,Op,Rep(T_1),Rep(T_2)}</c>.</item>
- <item>If T is an operator type <c>Op T_0</c>, where <c>Op</c> is a
- unary operator (this is an occurrence of
- an expression that can be evaluated to an integer at compile time),
- then Rep(T) = <c>{op,LINE,Op,Rep(T_0)}</c>.</item>
- <item>If T is <c>( T_0 )</c>, then Rep(T) = <c>Rep(T_0)</c>,
- that is, parenthesized types cannot be distinguished from their
- bodies.</item>
- <item>If T is a predefined (or built-in) type <c>N(T_1, ..., T_k)</c>,
- then Rep(T) =
- <c>{type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}</c>.</item>
- <item>If T is a record type <c>#Name{F_1, ..., F_k}</c>,
- where each <c>F_i</c> is a record field type, then Rep(T) =
- <c>{type,LINE,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}</c>.
- For Rep(F), see below.</item>
- <item>If T is a remote type <c>M:N(T_1, ..., T_k)</c>, then Rep(T) =
- <c>{remote_type,LINE,[Rep(M),Rep(N),[Rep(T_1), ..., Rep(T_k)]]}</c>.
- </item>
- <item>If T is a tuple type <c>tuple()</c>, then Rep(T) =
- <c>{type,LINE,tuple,any}</c>.</item>
- <item>If T is a tuple type <c>{T_1, ..., T_k}</c>, then Rep(T) =
- <c>{type,LINE,tuple,[Rep(T_1), ..., Rep(T_k)]}</c>.</item>
- <item>If T is a type union <c>T_1 | ... | T_k</c>, then Rep(T) =
- <c>{type,LINE,union,[Rep(T_1), ..., Rep(T_k)]}</c>.</item>
- <item>If T is a type variable <c>V</c>, then Rep(T) =
- <c>{var,LINE,A}</c>, where <c>A</c> is an atom with a printname
- consisting of the same characters as <c>V</c>. A type variable
- is any variable except underscore (<c>_</c>).</item>
- <item>If T is a user-defined type <c>N(T_1, ..., T_k)</c>,
- then Rep(T) =
- <c>{user_type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}</c>.</item>
+ <item>
+ <p>If T is an annotated type <c>A :: T_0</c>,
+ where <c>A</c> is a variable, then Rep(T) =
+ <c>{ann_type,LINE,[Rep(A),Rep(T_0)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is an atom or integer literal L, then Rep(T) = Rep(L).</p>
+ </item>
+ <item>
+ <p>If T is a bitstring type <c>&lt;&lt;_:M,_:_*N>></c>,
+ where <c>M</c> and <c>N</c> are singleton integer types, then Rep(T) =
+ <c>{type,LINE,binary,[Rep(M),Rep(N)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is the empty list type <c>[]</c>, then Rep(T) =
+ <c>{type,Line,nil,[]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a fun type <c>fun()</c>, then Rep(T) =
+ <c>{type,LINE,'fun',[]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a fun type <c>fun((...) -> T_0)</c>, then Rep(T) =
+ <c>{type,LINE,'fun',[{type,LINE,any},Rep(T_0)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a fun type <c>fun(Ft)</c>, where
+ <c>Ft</c> is a function type, then Rep(T) = <c>Rep(Ft)</c>.
+ For Rep(Ft), see below.</p>
+ </item>
+ <item>
+ <p>If T is an integer range type <c>L .. H</c>,
+ where <c>L</c> and <c>H</c> are singleton integer types, then Rep(T) =
+ <c>{type,LINE,range,[Rep(L),Rep(H)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a map type <c>map()</c>, then Rep(T) =
+ <c>{type,LINE,map,any}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a map type <c>#{A_1, ..., A_k}</c>, where each
+ <c>A_i</c> is an association type, then Rep(T) =
+ <c>{type,LINE,map,[Rep(A_1), ..., Rep(A_k)]}</c>.
+ For Rep(A), see below.</p>
+ </item>
+ <item>
+ <p>If T is an operator type <c>T_1 Op T_2</c>,
+ where <c>Op</c> is a binary operator (this is an occurrence of
+ an expression that can be evaluated to an integer at compile
+ time), then Rep(T) =
+ <c>{op,LINE,Op,Rep(T_1),Rep(T_2)}</c>.</p>
+ </item>
+ <item>
+ <p>If T is an operator type <c>Op T_0</c>, where <c>Op</c> is a
+ unary operator (this is an occurrence of an expression that can
+ be evaluated to an integer at compile time), then Rep(T) =
+ <c>{op,LINE,Op,Rep(T_0)}</c>.</p>
+ </item>
+ <item>
+ <p>If T is <c>( T_0 )</c>, then Rep(T) = <c>Rep(T_0)</c>, that is,
+ parenthesized types cannot be distinguished from their bodies.</p>
+ </item>
+ <item>
+ <p>If T is a predefined (or built-in) type <c>N(T_1, ..., T_k)</c>,
+ then Rep(T) = <c>{type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a record type <c>#Name{F_1, ..., F_k}</c>,
+ where each <c>F_i</c> is a record field type, then Rep(T) =
+ <c>{type,LINE,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}</c>.
+ For Rep(F), see below.</p>
+ </item>
+ <item>
+ <p>If T is a remote type <c>M:N(T_1, ..., T_k)</c>, then Rep(T) =
+ <c>{remote_type,LINE,[Rep(M),Rep(N),[Rep(T_1), ...,
+ Rep(T_k)]]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a tuple type <c>tuple()</c>, then Rep(T) =
+ <c>{type,LINE,tuple,any}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a tuple type <c>{T_1, ..., T_k}</c>, then Rep(T) =
+ <c>{type,LINE,tuple,[Rep(T_1), ..., Rep(T_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a type union <c>T_1 | ... | T_k</c>, then Rep(T) =
+ <c>{type,LINE,union,[Rep(T_1), ..., Rep(T_k)]}</c>.</p>
+ </item>
+ <item>
+ <p>If T is a type variable <c>V</c>, then Rep(T) =
+ <c>{var,LINE,A}</c>, where <c>A</c> is an atom with a printname
+ consisting of the same characters as <c>V</c>. A type variable
+ is any variable except underscore (<c>_</c>).</p>
+ </item>
+ <item>
+ <p>If T is a user-defined type <c>N(T_1, ..., T_k)</c>, then Rep(T) =
+ <c>{user_type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}</c>.</p>
+ </item>
</list>
<section>
<title>Function Types</title>
- <p>A function type Ft is one of the following alternatives:</p>
+ <p>A function type Ft is one of the following:</p>
+
<list type="bulleted">
- <item>If Ft is a constrained function type <c>Ft_1 when Fc</c>,
- where <c>Ft_1</c> is a function type and
- <c>Fc</c> is a function constraint, then Rep(T) =
- <c>{type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}</c>.
- For Rep(Fc), see below.</item>
- <item>If Ft is a function type <c>(T_1, ..., T_n) -> T_0</c>,
- where each <c>T_i</c> is a type, then
- Rep(Ft) = <c>{type,LINE,'fun',[{type,LINE,product,[Rep(T_1),
- ..., Rep(T_n)]},Rep(T_0)]}</c>.</item>
+ <item>
+ <p>If Ft is a constrained function type <c>Ft_1 when Fc</c>,
+ where <c>Ft_1</c> is a function type and
+ <c>Fc</c> is a function constraint, then Rep(T) =
+ <c>{type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}</c>.
+ For Rep(Fc), see below.</p>
+ </item>
+ <item>
+ <p>If Ft is a function type <c>(T_1, ..., T_n) -> T_0</c>,
+ where each <c>T_i</c> is a type, then Rep(Ft) =
+ <c>{type,LINE,'fun',[{type,LINE,product,[Rep(T_1), ...,
+ Rep(T_n)]},Rep(T_0)]}</c>.</p>
+ </item>
</list>
</section>
<section>
<title>Function Constraints</title>
- <p>A function constraint Fc is a nonempty sequence of constraints
- <c>C_1, ..., C_k</c>, and
- Rep(Fc) = <c>[Rep(C_1), ..., Rep(C_k)]</c>.</p>
+ <p>A function constraint Fc is a non-empty sequence of constraints
+ <c>C_1, ..., C_k</c>, and
+ Rep(Fc) = <c>[Rep(C_1), ..., Rep(C_k)]</c>.</p>
+
<list type="bulleted">
- <item>If C is a constraint <c>is_subtype(V, T)</c> or <c>V :: T</c>,
- where <c>V</c> is a type variable and <c>T</c> is a type, then
- Rep(C) = <c>{type,LINE,constraint,[{atom,LINE,is_subtype},[Rep(V),Rep(T)]]}</c>.
- </item>
+ <item>If C is a constraint <c>is_subtype(V, T)</c> or <c>V :: T</c>,
+ where <c>V</c> is a type variable
+ and <c>T</c> is a type, then Rep(C) =
+ <c>{type,LINE,constraint,[{atom,LINE,is_subtype},[Rep(V),Rep(T)]]}</c>.
+ </item>
</list>
</section>
<section>
<title>Association Types</title>
<list type="bulleted">
- <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>
+ <item>
+ <p>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>.</p>
+ </item>
+ <item>
+ <p>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>.</p>
+ </item>
</list>
</section>
<section>
<title>Record Field Types</title>
<list type="bulleted">
- <item>If F is a record field type <c>Name :: Type</c>,
- where <c>Type</c> is a type, then Rep(F) =
- <c>{type,LINE,field_type,[Rep(Name),Rep(Type)]}</c>.</item>
+ <item>If F is a record field type <c>Name :: Type</c>,
+ where <c>Type</c> is a type, then Rep(F) =
+ <c>{type,LINE,field_type,[Rep(Name),Rep(Type)]}</c>.
+ </item>
</list>
</section>
</section>
<section>
- <title>The Abstract Format After Preprocessing</title>
- <p>The compilation option <c>debug_info</c> can be given to the
+ <title>The Abstract Format after Preprocessing</title>
+ <p>The compilation option <c>debug_info</c> can be specified to the
compiler to have the abstract code stored in
- the <c>abstract_code</c> chunk in the BEAM file
+ the <c>abstract_code</c> chunk in the Beam file
(for debugging purposes).</p>
- <p>In OTP R9C and later, the <c>abstract_code</c> chunk will
- contain</p>
- <p><c>{raw_abstract_v1,AbstractCode}</c></p>
- <p>where <c>AbstractCode</c> is the abstract code as described
- in this document.</p>
- <p>In releases of OTP prior to R9C, the abstract code after some more
- processing was stored in the BEAM file. The first element of the
- tuple would be either <c>abstract_v1</c> (R7B) or <c>abstract_v2</c>
- (R8B).</p>
+
+ <p>As from Erlang/OTP R9C, the <c>abstract_code</c> chunk contains
+ <c>{raw_abstract_v1,AbstractCode}</c>, where <c>AbstractCode</c> is the
+ abstract code as described in this section.</p>
+
+ <p>In OTP releases before R9C, the abstract code after some more
+ processing was stored in the Beam file. The first element of the
+ tuple would be either <c>abstract_v1</c> (in OTP R7B) or
+ <c>abstract_v2</c> (in OTP R8B).</p>
</section>
</chapter>
diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml
index e283acc1b4..be969a8267 100644
--- a/erts/doc/src/alt_dist.xml
+++ b/erts/doc/src/alt_dist.xml
@@ -22,7 +22,8 @@
</legalnotice>
- <title>How to implement an alternative carrier for the Erlang distribution</title>
+ <title>How to Implement an Alternative Carrier for the Erlang Distribution
+ </title>
<prepared>Patrik Nyblom</prepared>
<responsible></responsible>
<docno></docno>
@@ -32,203 +33,270 @@
<rev>PA2</rev>
<file>alt_dist.xml</file>
</header>
- <p>This document describes how one can implement ones own carrier
+ <p>This section describes how to implement an alternative carrier
protocol for the Erlang distribution. The distribution is normally
- carried by the TCP/IP protocol. What's explained here is the method for
- replacing TCP/IP with another protocol. </p>
- <p>The document is a step by step explanation of the <c><![CDATA[uds_dist]]></c> example
- application (seated in the kernel applications <c><![CDATA[examples]]></c> directory).
- The <c><![CDATA[uds_dist]]></c> application implements distribution over Unix domain
- sockets and is written for the Sun Solaris 2 operating environment. The
- mechanisms are however general and applies to any operating system Erlang
- runs on. The reason the C code is not made portable, is simply readability.</p>
- <note><p>This document was written a long time ago. Most of it is still
- valid, but some things have changed since it was first written.
- Most notably the driver interface. There have been some updates
- to the documentation of the driver presented in this documentation,
- but more could be done and are planned for the future. The
- reader is encouraged to also read the
- <seealso marker="erl_driver">erl_driver</seealso>, and the
- <seealso marker="erl_driver">driver_entry</seealso> documentation.
- </p></note>
+ carried by TCP/IP. Here is explained a method for replacing TCP/IP
+ with another protocol.</p>
+
+ <p>The section is a step-by-step explanation of the
+ <c><![CDATA[uds_dist]]></c> example application (in the
+ Kernel application <c><![CDATA[examples]]></c> directory). The
+ <c><![CDATA[uds_dist]]></c> application implements distribution over Unix
+ domain sockets and is written for the Sun Solaris 2 operating environment.
+ The mechanisms are however general and apply to any operating system Erlang
+ runs on. The reason the C code is not made portable, is simply
+ readability.</p>
+
+ <note>
+ <p>This section was written a long time ago. Most of it is still
+ valid, but some things have changed since then.
+ Most notably is the driver interface. Some updates have been made
+ to the documentation of the driver presented here,
+ but more can be done and is planned for the future.
+ The reader is encouraged to read the
+ <seealso marker="erl_driver"><c>erl_driver</c></seealso> and
+ <seealso marker="driver_entry"><c>driver_entry</c></seealso>
+ documentation also.</p>
+ </note>
<section>
<title>Introduction</title>
- <p>To implement a new carrier for the Erlang distribution, one must first
- make the protocol available to the Erlang machine, which involves writing
- an Erlang driver. There is no way one can use a port program,
- there <em>has</em> to
- be an Erlang driver. Erlang drivers can either be statically
- linked
- to the emulator, which can be an alternative when using the open source
- distribution of Erlang, or dynamically loaded into the Erlang machines
- address space, which is the only alternative if a precompiled version of
- Erlang is to be used. </p>
- <p>Writing an Erlang driver is by no means easy. The driver is written
- as a couple of call-back functions called by the Erlang emulator when
- data is sent to the driver or the driver has any data available on a file
- descriptor. As the driver call-back routines execute in the main
- thread of the Erlang machine, the call-back functions can perform
- no blocking activity whatsoever. The call-backs should only set up
- file descriptors for waiting and/or read/write available data. All
- I/O has to be non blocking. Driver call-backs are however executed
- in sequence, why a global state can safely be updated within the
- routines. </p>
- <p>When the driver is implemented, one would preferably write an
- Erlang interface for the driver to be able to test the
- functionality of the driver separately. This interface can then
- be used by the distribution module which will cover the details of
- the protocol from the <c><![CDATA[net_kernel]]></c>. The easiest path is to
- mimic the <c><![CDATA[inet]]></c> and <c><![CDATA[inet_tcp]]></c> interfaces, but a lot of
- functionality in those modules need not be implemented. In the
- example application, only a few of the usual interfaces are
- implemented, and they are much simplified.</p>
- <p>When the protocol is available to Erlang through a driver and an
- Erlang interface module, a distribution module can be
- written. The distribution module is a module with well defined
- call-backs, much like a <c><![CDATA[gen_server]]></c> (there is no compiler support
- for checking the call-backs though). The details of finding other
- nodes (i.e. talking to epmd or something similar), creating a
- listen port (or similar), connecting to other nodes and performing
- the handshakes/cookie verification are all implemented by this
- module. There is however a utility module, <c><![CDATA[dist_util]]></c>, that
- will do most of the hard work of handling handshakes, cookies,
- timers and ticking. Using <c><![CDATA[dist_util]]></c> makes implementing a
- distribution module much easier and that's what we are doing in
- the example application.</p>
- <p>The last step is to create boot scripts to make the protocol
- implementation available at boot time. The implementation can be
- debugged by starting the distribution when all of the system is
- running, but in a real system the distribution should start very
- early, why a boot-script and some command line parameters are
- necessary. This last step also implies that the Erlang code in the
- interface and distribution modules is written in such a way that
- it can be run in the startup phase. Most notably there can be no
- calls to the <c><![CDATA[application]]></c> module or to any modules not
- loaded at boot-time (i.e. only <c><![CDATA[kernel]]></c>, <c><![CDATA[stdlib]]></c> and the
- application itself can be used).</p>
+ <p>To implement a new carrier for the Erlang distribution, the main
+ steps are as follows.</p>
+
+ <section>
+ <title>Writing an Erlang Driver</title>
+ <p>First, the protocol must be available to the Erlang machine, which
+ involves writing an Erlang driver. A port program cannot be used,
+ an Erlang driver is required. Erlang drivers can be:</p>
+
+ <list type="bulleted">
+ <item>
+ <p>Statically linked to the emulator, which can be an alternative
+ when using the open source distribution of Erlang, or</p>
+ </item>
+ <item>
+ <p>Dynamically loaded into the Erlang machines address space,
+ which is the only alternative if a precompiled version of
+ Erlang is to be used</p>
+ </item>
+ </list>
+
+ <p>Writing an Erlang driver is not easy. The driver is written
+ as some callback functions called by the Erlang emulator when
+ data is sent to the driver, or the driver has any data available on
+ a file descriptor. As the driver callback routines execute in the main
+ thread of the Erlang machine, the callback functions can perform
+ no blocking activity whatsoever. The callbacks are only to set up
+ file descriptors for waiting and/or read/write available data. All
+ I/O must be non-blocking. Driver callbacks are however executed
+ in sequence, why a global state can safely be updated within the
+ routines.</p>
+ </section>
+
+ <section>
+ <title>Writing an Erlang Interface for the Driver</title>
+ <p>When the driver is implemented, one would preferably write an
+ Erlang interface for the driver to be able to test the
+ functionality of the driver separately. This interface can then
+ be used by the distribution module, which will cover the details of
+ the protocol from the <c><![CDATA[net_kernel]]></c>.</p>
+
+ <p>The easiest path
+ is to mimic the <c><![CDATA[inet]]></c> and <c><![CDATA[inet_tcp]]></c>
+ interfaces, but not much
+ functionality in those modules needs to be implemented. In the
+ example application, only a few of the usual interfaces are
+ implemented, and they are much simplified.</p>
+ </section>
+
+ <section>
+ <title>Writing a Distribution Module</title>
+ <p>When the protocol is available to Erlang through a driver and an
+ Erlang interface module, a distribution module can be written.
+ The distribution module is a module with well-defined callbacks,
+ much like a <c><![CDATA[gen_server]]></c> (there is no compiler support
+ for checking the callbacks, though). This module implements:</p>
+
+ <list type="bulleted">
+ <item>The details of finding other nodes (that is, talking to
+ <c>epmd</c> or something similar)</item>
+ <item>Creating a listen port (or similar)</item>
+ <item>Connecting to other nodes</item>
+ <item>Performing the handshakes/cookie verification</item>
+ </list>
+
+ <p>There is however a utility module, <c><![CDATA[dist_util]]></c>, which
+ does most of the hard work of handling handshakes, cookies, timers,
+ and ticking. Using <c><![CDATA[dist_util]]></c> makes implementing a
+ distribution module much easier and that is done in
+ the example application.</p>
+ </section>
+
+ <section>
+ <title>Creating Boot Scripts</title>
+ <p>The last step is to create boot scripts to make the protocol
+ implementation available at boot time. The implementation can be
+ debugged by starting the distribution when all the system is
+ running, but in a real system the distribution is to start very
+ early, why a boot script and some command-line parameters are
+ necessary.</p>
+
+ <p>This step also implies that the Erlang code in the
+ interface and distribution modules is written in such a way that
+ it can be run in the startup phase. In particular, there can be no
+ calls to the <c><![CDATA[application]]></c> module or to any modules
+ not loaded at boot time. That is, only <c><![CDATA[Kernel]]></c>,
+ <c><![CDATA[STDLIB]]></c>, and the application itself can be used.</p>
+ </section>
</section>
<section>
- <title>The driver</title>
- <p>Although Erlang drivers in general may be beyond the scope of this
- document, a brief introduction seems to be in place.</p>
+ <title>The Driver</title>
+ <p>Although Erlang drivers in general can be beyond the scope of this
+ section, a brief introduction seems to be in place.</p>
<section>
- <title>Drivers in general</title>
+ <title>Drivers in General</title>
<p>An Erlang driver is a native code module written in C (or
- assembler) which serves as an interface for some special operating
+ assembler), which serves as an interface for some special operating
system service. This is a general mechanism that is used
throughout the Erlang emulator for all kinds of I/O. An Erlang
driver can be dynamically linked (or loaded) to the Erlang
emulator at runtime by using the <c><![CDATA[erl_ddll]]></c> Erlang
module. Some of the drivers in OTP are however statically linked
- to the runtime system, but that's more an optimization than a
+ to the runtime system, but that is more an optimization than a
necessity.</p>
- <p>The driver data-types and the functions available to the driver
- writer are defined in the header file <c><![CDATA[erl_driver.h]]></c> (there
- is also an deprecated version called <c><![CDATA[driver.h]]></c>, don't use
- that one.) seated in Erlang's include directory (and in
- $ERL_TOP/erts/emulator/beam in the source code
- distribution). Refer to that file for function prototypes etc.</p>
+
+ <p>The driver data types and the functions available to the driver
+ writer are defined in header file <c><![CDATA[erl_driver.h]]></c>
+ seated in Erlang's include directory. See the
+ <seealso marker="erts:erl_driver">erl_driver</seealso> documentation
+ for details of which functions are available.</p>
+
<p>When writing a driver to make a communications protocol available
to Erlang, one should know just about everything worth knowing
- about that particular protocol. All operation has to be non
- blocking and all possible situations should be accounted for in
- the driver. A non stable driver will affect and/or crash the
- whole Erlang runtime system, which is seldom what's wanted. </p>
+ about that particular protocol. All operation must be
+ non-blocking and all possible situations are to be accounted for in
+ the driver. A non-stable driver will affect and/or crash the
+ whole Erlang runtime system.</p>
+
<p>The emulator calls the driver in the following situations:</p>
+
<list type="bulleted">
- <item>When the driver is loaded. This call-back has to have a
- special name and will inform the emulator of what call-backs should
- be used by returning a pointer to a <c><![CDATA[ErlDrvEntry]]></c> struct,
- which should be properly filled in (see below).</item>
- <item>When a port to the driver is opened (by a <c><![CDATA[open_port]]></c>
- call from Erlang). This routine should set up internal data
- structures and return an opaque data entity of the type
- <c><![CDATA[ErlDrvData]]></c>, which is a data-type large enough to hold a
- pointer. The pointer returned by this function will be the first
- argument to all other call-backs concerning this particular
- port. It is usually called the port handle. The emulator only
- stores the handle and does never try to interpret it, why it can
- be virtually anything (well anything not larger than a pointer
- that is) and can point to anything if it is a pointer. Usually
- this pointer will refer to a structure holding information about
- the particular port, as i t does in our example.</item>
- <item>When an Erlang process sends data to the port. The data will
- arrive as a buffer of bytes, the interpretation is not defined,
- but is up to the implementor. This call-back returns nothing to the
- caller, answers are sent to the caller as messages (using a
- routine called <c><![CDATA[driver_output]]></c> available to all
- drivers). There is also a way to talk in a synchronous way to
- drivers, described below. There can be an additional call-back
- function for handling data that is fragmented (sent in a deep
- io-list). That interface will get the data in a form suitable for
- Unix <c><![CDATA[writev]]></c> rather than in a single buffer. There is no
- need for a distribution driver to implement such a call-back, so
- we wont.</item>
- <item>When a file descriptor is signaled for input. This call-back
- is called when the emulator detects input on a file descriptor
- which the driver has marked for monitoring by using the interface
- <c><![CDATA[driver_select]]></c>. The mechanism of driver select makes it
- possible to read non blocking from file descriptors by calling
- <c><![CDATA[driver_select]]></c> when reading is needed and then do the actual
- reading in this call-back (when reading is actually possible). The
- typical scenario is that <c><![CDATA[driver_select]]></c> is called when an
- Erlang process orders a read operation, and that this routine
- sends the answer when data is available on the file descriptor.</item>
- <item>When a file descriptor is signaled for output. This call-back
- is called in a similar way as the previous, but when writing to a
- file descriptor is possible. The usual scenario is that Erlang
- orders writing on a file descriptor and that the driver calls
- <c><![CDATA[driver_select]]></c>. When the descriptor is ready for output,
- this call-back is called an the driver can try to send the
- output. There may of course be queuing involved in such
- operations, and there are some convenient queue routines available
- to the driver writer to use in such situations.</item>
- <item>When a port is closed, either by an Erlang process or by the
- driver calling one of the <c><![CDATA[driver_failure_XXX]]></c> routines. This
- routine should clean up everything connected to one particular
- port. Note that when other call-backs call a
- <c><![CDATA[driver_failure_XXX]]></c> routine, this routine will be
- immediately called and the call-back routine issuing the error can
- make no more use of the data structures for the port, as this
- routine surely has freed all associated data and closed all file
- descriptors. If the queue utility available to driver writes is
- used, this routine will however <em>not</em> be called until the
- queue is empty.</item>
- <item>When an Erlang process calls <c>erlang:port_control/3</c>,
- which is a synchronous interface to drivers. The control interface
- is used to set driver options, change states of ports etc. We'll
- use this interface quite a lot in our example.</item>
- <item>When a timer expires. The driver can set timers with the
- function <c><![CDATA[driver_set_timer]]></c>. When such timers expire, a
- specific call-back function is called. We will not use timers in
- our example.</item>
- <item>When the whole driver is unloaded. Every resource allocated
- by the driver should be freed.</item>
+ <item>
+ <p>When the driver is loaded. This callback must have a special
+ name and inform the emulator of what callbacks are to be used
+ by returning a pointer to a <c><![CDATA[ErlDrvEntry]]></c> struct,
+ which is to be properly filled in (see below).</p>
+ </item>
+ <item>
+ <p>When a port to the driver is opened (by a
+ <c><![CDATA[open_port]]></c> call from Erlang). This routine is to
+ set up internal data structures and return an opaque data entity of
+ the type <c><![CDATA[ErlDrvData]]></c>, which is a data type large
+ enough to hold a pointer.
+ The pointer returned by this function is the first
+ argument to all other callbacks concerning this particular
+ port. It is usually called the port handle. The emulator only
+ stores the handle and does never try to interpret it, why it can
+ be virtually anything (anything not larger than a pointer
+ that is) and can point to anything if it is a pointer. Usually
+ this pointer refers to a structure holding information about
+ the particular port, as it does in the example.</p>
+ </item>
+ <item>
+ <p>When an Erlang process sends data to the port. The data
+ arrives as a buffer of bytes, the interpretation is not defined,
+ but is up to the implementor. This callback returns nothing to the
+ caller, answers are sent to the caller as messages (using a
+ routine called <c><![CDATA[driver_output]]></c> available to all
+ drivers). There is also a way to talk in a synchronous way to
+ drivers, described below. There can be an additional callback
+ function for handling data that is fragmented (sent in a deep
+ io-list). That interface gets the data in a form suitable for
+ Unix <c><![CDATA[writev]]></c> rather than in a single buffer.
+ There is no need for a distribution driver to implement such a
+ callback, so we will not.</p>
+ </item>
+ <item>
+ <p>When a file descriptor is signaled for input. This callback
+ is called when the emulator detects input on a file descriptor
+ that the driver has marked for monitoring by using the interface
+ <c><![CDATA[driver_select]]></c>. The mechanism of driver select
+ makes it possible to read non-blocking from file descriptors by
+ calling <c><![CDATA[driver_select]]></c> when reading is needed, and
+ then do the reading in this callback (when reading is possible).
+ The typical scenario is that <c><![CDATA[driver_select]]></c> is
+ called when an Erlang process orders a read operation, and that
+ this routine sends the answer when data is available on the file
+ descriptor.</p>
+ </item>
+ <item>
+ <p>When a file descriptor is signaled for output. This callback
+ is called in a similar way as the previous, but when writing to a
+ file descriptor is possible. The usual scenario is that Erlang
+ orders writing on a file descriptor and that the driver calls
+ <c><![CDATA[driver_select]]></c>. When the descriptor is ready for
+ output, this callback is called and the driver can try to send the
+ output. Queuing can be involved in such operations, and there are
+ convenient queue routines available to the driver writer to use.</p>
+ </item>
+ <item>
+ <p>When a port is closed, either by an Erlang process or by the
+ driver calling one of the <c><![CDATA[driver_failure_XXX]]></c>
+ routines. This routine is to clean up everything connected to one
+ particular port. When other callbacks call a
+ <c><![CDATA[driver_failure_XXX]]></c> routine, this routine is
+ immediately called. The callback routine issuing the error can
+ make no more use of the data structures for the port, as this
+ routine surely has freed all associated data and closed all file
+ descriptors. If the queue utility available to driver writer is
+ used, this routine is however <em>not</em> called until the
+ queue is empty.</p>
+ </item>
+ <item>
+ <p>When an Erlang process calls
+ <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso>,
+ which is a synchronous interface to drivers. The control interface
+ is used to set driver options, change states of ports, and so on.
+ This interface is used a lot in the example.</p>
+ </item>
+ <item>
+ <p>When a timer expires. The driver can set timers with the function
+ <c><![CDATA[driver_set_timer]]></c>. When such timers expire, a
+ specific callback function is called. No timers are used in
+ the example.</p>
+ </item>
+ <item>
+ <p>When the whole driver is unloaded. Every resource allocated
+ by the driver is to be freed.</p>
+ </item>
</list>
</section>
<section>
- <title>The distribution driver's data structures</title>
- <p>The driver used for Erlang distribution should implement a
- reliable, order maintaining, variable length packet oriented
- protocol. All error correction, re-sending and such need to be
+ <title>The Data Structures of the Distribution Driver</title>
+ <p>The driver used for Erlang distribution is to implement a
+ reliable, order maintaining, variable length packet-oriented
+ protocol. All error correction, resending and such need to be
implemented in the driver or by the underlying communications
- protocol. If the protocol is stream oriented (as is the case with
+ protocol. If the protocol is stream-oriented (as is the case with
both TCP/IP and our streamed Unix domain sockets), some mechanism
for packaging is needed. We will use the simple method of having a
- header of four bytes containing the length of the package in a big
- endian 32 bit integer (as Unix domain sockets only can be used
- between processes on the same machine, we actually don't need to
- code the integer in some special endianess, but I'll do it anyway
- because in most situation you do need to do it. Unix domain
- sockets are reliable and order maintaining, so we don't need to
- implement resends and such in our driver.</p>
- <p>Lets start writing our example Unix domain sockets driver by
- declaring prototypes and filling in a static ErlDrvEntry
- structure.</p>
+ header of four bytes containing the length of the package in a
+ big-endian 32-bit integer. As Unix domain sockets only can be used
+ between processes on the same machine, we do not need to
+ code the integer in some special endianess, but we will do it anyway
+ because in most situation you need to do it. Unix domain
+ sockets are reliable and order maintaining, so we do not need to
+ implement resends and such in the driver.</p>
+
+ <p>We start writing the example Unix domain sockets driver by
+ declaring prototypes and filling in a static <c>ErlDrvEntry</c>
+ structure:</p>
+
<code type="none"><![CDATA[
( 1) #include <stdio.h>
( 2) #include <stdlib.h>
@@ -286,94 +354,122 @@
(51) NULL, /* process_exit callback */
(52) NULL /* stop_select callback */
(53) };]]></code>
- <p>On line 1 to 10 we have included the OS headers needed for our
- driver. As this driver is written for Solaris, we know that the
- header <c><![CDATA[uio.h]]></c> exists, why we can define the preprocessor
- variable <c><![CDATA[HAVE_UIO_H]]></c> before we include <c><![CDATA[erl_driver.h]]></c>
- at line 12. The definition of <c><![CDATA[HAVE_UIO_H]]></c> will make the
+
+ <p>On line 1-10 the OS headers needed for the driver are included.
+ As this driver is written for Solaris, we know that the
+ header <c><![CDATA[uio.h]]></c> exists. So the preprocessor variable
+ <c><![CDATA[HAVE_UIO_H]]></c> can be defined before
+ <c><![CDATA[erl_driver.h]]></c> is included on line 12.
+ The definition of <c><![CDATA[HAVE_UIO_H]]></c> will make the
I/O vectors used in Erlang's driver queues to correspond to the
operating systems ditto, which is very convenient.</p>
- <p>The different call-back functions are declared ("forward
- declarations") on line 16 to 23.</p>
- <p>The driver structure is similar for statically linked in
- drivers and dynamically loaded. However some of the fields
- should be left empty (i.e. initialized to NULL) in the
+
+ <p>On line 16-23 the different callback functions are declared ("forward
+ declarations").</p>
+
+ <p>The driver structure is similar for statically linked-in
+ drivers and dynamically loaded. However, some of the fields
+ are to be left empty (that is, initialized to NULL) in the
different types of drivers. The first field (the <c><![CDATA[init]]></c>
function pointer) is always left blank in a dynamically loaded
- driver, which can be seen on line 26. The NULL on line 37
- should always be there, the field is no longer used and is
- retained for backward compatibility. We use no timers in this
- driver, why no call-back for timers is needed. The <c>outputv</c> field
+ driver, see line 26. <c>NULL</c> on line 37
+ is always to be there, the field is no longer used and is
+ retained for backward compatibility. No timers are used in this
+ driver, why no callback for timers is needed. The <c>outputv</c> field
(line 40) can be used to implement an interface similar to
Unix <c><![CDATA[writev]]></c> for output. The Erlang runtime
- system could previously not use <c>outputv</c> for the
- distribution, but since erts version 5.7.2 it can.
- Since this driver was written before erts version 5.7.2 it does
- not use the <c>outputv</c> callback. Using the <c>outputv</c>
- callback is preferred since it reduces copying of data. (We
- will however use scatter/gather I/O internally in the driver).</p>
- <p>As of erts version 5.5.3 the driver interface was extended with
- version control and the possibility to pass capability information.
- Capability flags are present at line 48. As of erts version 5.7.4
- the
- <seealso marker="driver_entry#driver_flags">ERL_DRV_FLAG_SOFT_BUSY</seealso>
- flag is required for drivers that are to be used by the distribution.
- The soft busy flag implies that the driver is capable of handling
- calls to the <c>output</c> and <c>outputv</c> callbacks even though
- it has marked itself as busy. This has always been a requirement
- on drivers used by the distribution, but there have previously not
- been any capability information available about this. For more
- information see
- <seealso marker="erl_driver#set_busy_port">set_busy_port()</seealso>).
-</p>
+ system could previously not use <c>outputv</c> for the
+ distribution, but it can as from ERTS 5.7.2.
+ As this driver was written before ERTS 5.7.2 it does
+ not use the <c>outputv</c> callback. Using the <c>outputv</c>
+ callback is preferred, as it reduces copying of data. (We
+ will however use scatter/gather I/O internally in the driver.)</p>
+
+ <p>As from ERTS 5.5.3 the driver interface was extended with
+ version control and the possibility to pass capability information.
+ Capability flags are present on line 48. As from ERTS 5.7.4 flag
+ <seealso marker="driver_entry#driver_flags">
+ <c>ERL_DRV_FLAG_SOFT_BUSY</c></seealso> is required for drivers that
+ are to be used by the distribution. The soft busy flag implies that the
+ driver can handle calls to the <c>output</c> and <c>outputv</c>
+ callbacks although it has marked itself as busy. This has always been a
+ requirement on drivers used by the distribution, but no capability
+ information has been available about this previously. For more
+ information. see <seealso marker="erl_driver#set_busy_port">
+ <c>erl_driver:set_busy_port()</c></seealso>).</p>
+
<p>This driver was written before the runtime system had SMP support.
- The driver will still function in the runtime system with SMP support,
- but performance will suffer from lock contention on the driver lock
- used for the driver. This can be alleviated by reviewing and perhaps
- rewriting the code so that each instance of the driver safely can
- execute in parallel. When instances safely can execute in parallel it
- is safe to enable instance specific locking on the driver. This is done
- by passing
- <seealso marker="driver_entry#driver_flags">ERL_DRV_FLAG_USE_PORT_LOCKING</seealso>
- as a driver flag. This is left as an exercise for the reader.</p>
- <p>Our defined call-backs thus are:</p>
- <list type="bulleted">
- <item>uds_start, which shall initiate data for a port. We wont
- create any actual sockets here, just initialize data structures.</item>
- <item>uds_stop, the function called when a port is closed.</item>
- <item>uds_command, which will handle messages from Erlang. The
- messages can either be plain data to be sent or more subtle
- instructions to the driver. We will use this function mostly for
- data pumping.</item>
- <item>uds_input, this is the call-back which is called when we have
- something to read from a socket.</item>
- <item>uds_output, this is the function called when we can write to a
- socket.</item>
- <item>uds_finish, which is called when the driver is unloaded. A
- distribution driver will actually (or hopefully) never be unloaded,
- but we include this for completeness. Being able to clean up after
- oneself is always a good thing.</item>
- <item>uds_control, the <c>erlang:port_control/2</c> call-back, which
- will be used a lot in this implementation.</item>
- </list>
- <p>The ports implemented by this driver will operate in two major
- modes, which i will call the <em>command</em> and <em>data</em>
- modes. In command mode, only passive reading and writing (like
- gen_tcp:recv/gen_tcp:send) can be
- done, and this is the mode the port will be in during the
- distribution handshake. When the connection is up, the port will
- be switched to data mode and all data will be immediately read and
- passed further to the Erlang emulator. In data mode, no data
- arriving to the uds_command will be interpreted, but just packaged
- and sent out on the socket. The uds_control call-back will do the
- switching between those two modes.</p>
- <p>While the <c><![CDATA[net_kernel]]></c> informs different subsystems that the
- connection is coming up, the port should accept data to send, but
- not receive any data, to avoid that data arrives from another node
- before every kernel subsystem is prepared to handle it. We have a
- third mode for this intermediate stage, lets call it the
- <em>intermediate</em> mode.</p>
- <p>Lets define an enum for the different types of ports we have:</p>
+ The driver will still function in the runtime system with SMP support,
+ but performance will suffer from lock contention on the driver lock
+ used for the driver. This can be alleviated by reviewing and perhaps
+ rewriting the code so that each instance of the driver safely can
+ execute in parallel. When instances safely can execute in parallel, it
+ is safe to enable instance-specific locking on the driver. This is done
+ by passing <seealso marker="driver_entry#driver_flags">
+ <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c></seealso> as a driver flag. This
+ is left as an exercise for the reader.</p>
+
+ <p>Thus, the defined callbacks are as follows:</p>
+
+ <taglist>
+ <tag><c>uds_start</c></tag>
+ <item>
+ <p>Must initiate data for a port. We do not create any sockets
+ here, only initialize data structures.</p>
+ </item>
+ <tag><c>uds_stop</c></tag>
+ <item>
+ <p>Called when a port is closed.</p>
+ </item>
+ <tag><c>uds_command</c></tag>
+ <item>
+ <p>Handles messages from Erlang. The
+ messages can either be plain data to be sent or more subtle
+ instructions to the driver. This function is here mostly for
+ data pumping.</p>
+ </item>
+ <tag><c>uds_input</c></tag>
+ <item>
+ <p>Called when there is something to read from a socket.</p>
+ </item>
+ <tag><c>uds_output</c></tag>
+ <item>
+ <p>Called when it is possible to write to a socket.</p>
+ </item>
+ <tag><c>uds_finish</c></tag>
+ <item>
+ <p>Called when the driver is unloaded. A distribution driver will
+ never be unloaded, but we include this for completeness. To be
+ able to clean up after oneself is always a good thing.</p>
+ </item>
+ <tag><c>uds_control</c></tag>
+ <item>
+ <p>The <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso> callback, which is
+ used a lot in this implementation.</p>
+ </item>
+ </taglist>
+
+ <p>The ports implemented by this driver operate in two major modes,
+ named <c>command</c> and <c>data</c>. In <c>command</c> mode,
+ only passive reading and writing (like
+ <c>gen_tcp:recv</c>/<c>gen_tcp:send</c>) can be done. The port is in
+ this mode during the distribution handshake. When the connection is up,
+ the port is switched to <c>data</c> mode and all data is immediately
+ read and passed further to the Erlang emulator. In <c>data</c>
+ mode, no data arriving to <c>uds_command</c> is interpreted, only
+ packaged and sent out on the socket. The <c>uds_control</c> callback
+ does the switching between those two modes.</p>
+
+ <p>While <c><![CDATA[net_kernel]]></c> informs different subsystems
+ that the connection is coming up, the port is to accept data to send.
+ However, the port should not receive any data, to avoid that data
+ arrives from another node before every kernel subsystem is prepared
+ to handle it. A third mode, named <c>intermediate</c>, is used for this
+ intermediate stage.</p>
+
+ <p>An enum is defined for the different types of ports:</p>
+
<code type="none"><![CDATA[
( 1) typedef enum {
( 2) portTypeUnknown, /* An uninitialized port */
@@ -384,40 +480,63 @@
( 7) portTypeCommand, /* A connected open port in command mode */
( 8) portTypeIntermediate, /* A connected open port in special
( 9) half active mode */
-(10) portTypeData /* A connectec open port in data mode */
+(10) portTypeData /* A connected open port in data mode */
(11) } PortType; ]]></code>
- <p>Lets look at the different types:</p>
- <list type="bulleted">
- <item>portTypeUnknown - The type a port has when it's opened, but
- not actually bound to any file descriptor.</item>
- <item>portTypeListener - A port that is connected to a listen
- socket. This port will not do especially much, there will be no data
- pumping done on this socket, but there will be read data available
- when one is trying to do an accept on the port.</item>
- <item>portTypeAcceptor - This is a port that is to represent the
- result of an accept operation. It is created when one wants to
- accept from a listen socket, and it will be converted to a
- portTypeCommand when the accept succeeds.</item>
- <item>portTypeConnector - Very similar to portTypeAcceptor, an
- intermediate stage between the request for a connect operation and
- that the socket is really connected to an accepting ditto in the
- other end. As soon as the sockets are connected, the port will
- switch type to portTypeCommand.</item>
- <item>portTypeCommand - A connected socket (or accepted socket if
- you want) that is in the command mode mentioned earlier.</item>
- <item>portTypeIntermediate - The intermediate stage for a connected
- socket. There should be no processing of input for this socket.</item>
- <item>portTypeData - The mode where data is pumped through the port
- and the uds_command routine will regard every call as a call where
- sending is wanted. In this mode all input available will be read and
- sent to Erlang as soon as it arrives on the socket, much like in the
- active mode of a <c><![CDATA[gen_tcp]]></c> socket.</item>
- </list>
- <p>Now lets look at the state we'll need for our ports. One can note
- that not all fields are used for all types of ports and that one
- could save some space by using unions, but that would clutter the
- code with multiple indirections, so i simply use one struct for
- all types of ports, for readability.</p>
+
+ <p>The different types are as follows:</p>
+
+ <taglist>
+ <tag><c>portTypeUnknown</c></tag>
+ <item>
+ <p>The type a port has when it is opened, but
+ not bound to any file descriptor.</p>
+ </item>
+ <tag><c>portTypeListener</c></tag>
+ <item>
+ <p>A port that is connected to a listen socket. This port does not
+ do much, no data pumping is done on this socket, but read data is
+ available when one is trying to do an accept on the port.</p>
+ </item>
+ <tag><c>portTypeAcceptor</c></tag>
+ <item>
+ <p>This port is to represent the result of an accept operation. It is
+ created when one wants to accept from a listen socket, and it is
+ converted to a <c>portTypeCommand</c> when the accept succeeds.</p>
+ </item>
+ <tag><c>portTypeConnector</c></tag>
+ <item>
+ <p>Very similar to <c>portTypeAcceptor</c>, an
+ intermediate stage between the request for a connect operation and
+ that the socket is connected to an accepting ditto in the
+ other end. When the sockets are connected, the port
+ switches type to <c>portTypeCommand</c>.</p>
+ </item>
+ <tag><c>portTypeCommand</c></tag>
+ <item>
+ <p>A connected socket (or accepted socket) in <c>command</c> mode
+ mentioned earlier.</p>
+ </item>
+ <tag><c>portTypeIntermediate</c></tag>
+ <item>
+ <p>The intermediate stage for a connected socket.
+ There is to be no processing of input for this socket.</p>
+ </item>
+ <tag><c>portTypeData</c></tag>
+ <item>
+ <p>The mode where data is pumped through the port and the
+ <c>uds_command</c> routine regards every call as a call where
+ sending is wanted. In this mode, all input available is read and
+ sent to Erlang when it arrives on the socket, much like in the
+ active mode of a <c><![CDATA[gen_tcp]]></c> socket.</p>
+ </item>
+ </taglist>
+
+ <p>We study the state that is needed for the ports. Notice
+ that not all fields are used for all types of ports. Some space
+ could be saved by using unions, but that would clutter the
+ code with multiple indirections, so here is used one struct for
+ all types of ports, for readability:</p>
+
<code type="none"><![CDATA[
( 1) typedef unsigned char Byte;
( 2) typedef unsigned int Word;
@@ -428,7 +547,7 @@
( 6) int lockfd; /* The file descriptor for a lock file in
( 7) case of listen sockets */
( 8) Byte creation; /* The creation serial derived from the
-( 9) lockfile */
+( 9) lock file */
(10) PortType type; /* Type of port */
(11) char *name; /* Short name of socket for unlink */
(12) Word sent; /* Bytes sent */
@@ -442,114 +561,154 @@
(20) input buffer */
(21) Byte *buffer; /* The actual input buffer */
(22) } UdsData; ]]></code>
+
<p>This structure is used for all types of ports although some
fields are useless for some types. The least memory consuming
solution would be to arrange this structure as a union of
- structures, but the multiple indirections in the code to
- access a field in such a structure will clutter the code to
+ structures. However, the multiple indirections in the code to
+ access a field in such a structure would clutter the code too
much for an example.</p>
- <p>Let's look at the fields in our structure:</p>
- <list type="bulleted">
- <item>fd - The file descriptor of the socket associated with the
- port.</item>
- <item>port - The port identifier for the port which this structure
- corresponds to. It is needed for most <c><![CDATA[driver_XXX]]></c>
- calls from the driver back to the emulator.</item>
+
+ <p>The fields in the structure are as follows:</p>
+
+ <taglist>
+ <tag><c>fd</c></tag>
<item>
- <p>lockfd - If the socket is a listen socket, we use a separate
+ <p>The file descriptor of the socket associated with the port.</p>
+ </item>
+ <tag><c>port</c></tag>
+ <item>
+ <p>The port identifier for the port that this structure
+ corresponds to. It is needed for most <c><![CDATA[driver_XXX]]></c>
+ calls from the driver back to the emulator.</p>
+ </item>
+ <tag><c>lockfd</c></tag>
+ <item>
+ <p>If the socket is a listen socket, we use a separate
(regular) file for two purposes:</p>
<list type="bulleted">
- <item>We want a locking mechanism that gives no race
- conditions, so that we can be sure of if another Erlang
- node uses the listen socket name we require or if the
- file is only left there from a previous (crashed)
- session.</item>
<item>
- <p>We store the <em>creation</em> serial number in the
- file. The <em>creation</em> is a number that should
+ <p>We want a locking mechanism that gives no race
+ conditions, to be sure if another Erlang
+ node uses the listen socket name we require or if the
+ file is only left there from a previous (crashed) session.</p>
+ </item>
+ <item>
+ <p>We store the <c>creation</c> serial number in the
+ file. The <c>creation</c> is a number that is to
change between different instances of different Erlang
emulators with the same name, so that process
- identifiers from one emulator won't be valid when sent
+ identifiers from one emulator do not become valid when sent
to a new emulator with the same distribution name. The
- creation can be between 0 and 3 (two bits) and is stored
- in every process identifier sent to another node. </p>
- <p>In a system with TCP based distribution, this data is
+ creation can be from 0 through 3 (two bits) and is stored
+ in every process identifier sent to another node.</p>
+ <p>In a system with TCP-based distribution, this data is
kept in the <em>Erlang port mapper daemon</em>
(<c><![CDATA[epmd]]></c>), which is contacted when a distributed
- node starts. The lock-file and a convention for the UDS
- listen socket's name will remove the need for
+ node starts. The lock file and a convention for the UDS
+ listen socket's name remove the need for
<c><![CDATA[epmd]]></c> when using this distribution module. UDS
is always restricted to one host, why avoiding a port
mapper is easy.</p>
</item>
</list>
</item>
- <item>creation - The creation number for a listen socket, which is
- calculated as (the value found in the lock-file + 1) rem
- 4. This creation value is also written back into the
- lock-file, so that the next invocation of the emulator will
- found our value in the file.</item>
- <item>type - The current type/state of the port, which can be one
- of the values declared above.</item>
- <item>name - The name of the socket file (the path prefix
- removed), which allows for deletion (<c><![CDATA[unlink]]></c>) when the
- socket is closed.</item>
- <item>sent - How many bytes that have been sent over the
- socket. This may wrap, but that's no problem for the
- distribution, as the only thing that interests the Erlang
- distribution is if this value has changed (the Erlang
- net_kernel <em>ticker</em> uses this value by calling the
- driver to fetch it, which is done through the
- <c>erlang:port_control</c> routine).</item>
- <item>received - How many bytes that are read (received) from the
- socket, used in similar ways as <c><![CDATA[sent]]></c>.</item>
- <item>partner - A pointer to another port structure, which is
- either the listen port from which this port is accepting a
- connection or the other way around. The "partner relation"
- is always bidirectional.</item>
- <item>next - Pointer to next structure in a linked list of all
- port structures. This list is used when accepting
- connections and when the driver is unloaded.</item>
- <item>buffer_size, buffer_pos, header_pos, buffer - data for input
- buffering. Refer to the source code (in the kernel/examples
- directory) for details about the input buffering. That
- certainly goes beyond the scope of this document.</item>
- </list>
+ <tag><c>creation</c></tag>
+ <item>
+ <p>The creation number for a listen socket, which is
+ calculated as (the value found in the lock-file + 1) rem 4.
+ This creation value is also written back into the
+ lock file, so that the next invocation of the emulator
+ finds our value in the file.</p>
+ </item>
+ <tag><c>type</c></tag>
+ <item>
+ <p>The current type/state of the port, which can be one
+ of the values declared above.</p>
+ </item>
+ <tag><c>name</c></tag>
+ <item>
+ <p>The name of the socket file (the path prefix removed),
+ which allows for deletion (<c><![CDATA[unlink]]></c>) when the
+ socket is closed.</p>
+ </item>
+ <tag><c>sent</c></tag>
+ <item>
+ <p>How many bytes that have been sent over the
+ socket. This can wrap, but that is no problem for the
+ distribution, as the Erlang distribution is only interested in
+ if this value has changed. (The Erlang
+ <c>net_kernel</c> <c>ticker</c> uses this value by calling the
+ driver to fetch it, which is done through the
+ <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso> routine.)</p>
+ </item>
+ <tag><c>received</c></tag>
+ <item>
+ <p>How many bytes that are read (received) from the
+ socket, used in similar ways as <c><![CDATA[sent]]></c>.</p>
+ </item>
+ <tag><c>partner</c></tag>
+ <item>
+ <p>A pointer to another port structure, which is
+ either the listen port from which this port is accepting a
+ connection or conversely. The "partner relation"
+ is always bidirectional.</p>
+ </item>
+ <tag><c>next</c></tag>
+ <item>
+ <p>Pointer to next structure in a linked list of all
+ port structures. This list is used when accepting
+ connections and when the driver is unloaded.</p>
+ </item>
+ <tag><c>buffer_size</c>, <c>buffer_pos</c>, <c>header_pos</c>,
+ <c>buffer</c></tag>
+ <item>
+ <p>Data for input buffering. For details about the input buffering,
+ see the source code in directory <c>kernel/examples</c>. That
+ certainly goes beyond the scope of this section.</p>
+ </item>
+ </taglist>
</section>
<section>
- <title>Selected parts of the distribution driver implementation</title>
- <p>The distribution drivers implementation is not completely
- covered in this text, details about buffering and other things
+ <title>Selected Parts of the Distribution Driver Implementation</title>
+ <p>The implemenation of the distribution driver is not completely
+ covered here, details about buffering and other things
unrelated to driver writing are not explained. Likewise are
some peculiarities of the UDS protocol not explained in
detail. The chosen protocol is not important.</p>
- <p>Prototypes for the driver call-back routines can be found in
+
+ <p>Prototypes for the driver callback routines can be found in
the <c><![CDATA[erl_driver.h]]></c> header file.</p>
+
<p>The driver initialization routine is (usually) declared with a
macro to make the driver easier to port between different
- operating systems (and flavours of systems). This is the only
- routine that has to have a well defined name. All other
- call-backs are reached through the driver structure. The macro
+ operating systems (and flavors of systems). This is the only
+ routine that must have a well-defined name. All other
+ callbacks are reached through the driver structure. The macro
to use is named <c><![CDATA[DRIVER_INIT]]></c> and takes the driver name
- as parameter.</p>
+ as parameter:</p>
+
<code type="none"><![CDATA[
(1) /* Beginning of linked list of ports */
(2) static UdsData *first_data;
-
(3) DRIVER_INIT(uds_drv)
(4) {
(5) first_data = NULL;
(6) return &uds_driver_entry;
(7) } ]]></code>
+
<p>The routine initializes the single global data structure and
- returns a pointer to the driver entry. The routine will be
- called when <c><![CDATA[erl_ddll:load_driver]]></c> is called from Erlang.</p>
- <p>The <c><![CDATA[uds_start]]></c> routine is called when a port is opened
- from Erlang. In our case, we only allocate a structure and
+ returns a pointer to the driver entry. The routine is called
+ when <c><![CDATA[erl_ddll:load_driver]]></c> is called from Erlang.</p>
+
+ <p>The <c><![CDATA[uds_start]]></c> routine is called when a port is
+ opened from Erlang. In this case, we only allocate a structure and
initialize it. Creating the actual socket is left to the
<c><![CDATA[uds_command]]></c> routine.</p>
+
<code type="none"><![CDATA[
( 1) static ErlDrvData uds_start(ErlDrvPort port, char *buff)
( 2) {
@@ -574,15 +733,18 @@
(21)
(22) return((ErlDrvData) ud);
(23) } ]]></code>
- <p>Every data item is initialized, so that no problems will arise
+
+ <p>Every data item is initialized, so that no problems arise
when a newly created port is closed (without there being any
corresponding socket). This routine is called when
- <c><![CDATA[open_port({spawn, "uds_drv"},[])]]></c> is called from Erlang.</p>
- <p>The <c><![CDATA[uds_command]]></c> routine is the routine called when an
- Erlang process sends data to the port. All asynchronous
- commands when the port is in <em>command mode</em> as well as
- the sending of all data when the port is in <em>data mode</em>
- is handled in this9s routine. Let's have a look at it:</p>
+ <c><![CDATA[open_port({spawn, "uds_drv"},[])]]></c> is called from
+ Erlang.</p>
+
+ <p>The <c><![CDATA[uds_command]]></c> routine is the routine called when
+ an Erlang process sends data to the port. This routine handles all
+ asynchronous commands when the port is in <c>command</c> mode and
+ the sending of all data when the port is in <c>data</c> mode:</p>
+
<code type="none"><![CDATA[
( 1) static void uds_command(ErlDrvData handle, char *buff, int bufflen)
( 2) {
@@ -636,57 +798,77 @@
(49) return;
(50) }
(51) } ]]></code>
- <p>The command routine takes three parameters; the handle
- returned for the port by <c><![CDATA[uds_start]]></c>, which is a pointer
- to the internal port structure, the data buffer and the length
+
+ <p>The command routine takes three parameters; the handle returned for
+ the port by <c><![CDATA[uds_start]]></c>, which is a pointer
+ to the internal port structure, the data buffer, and the length
of the data buffer. The buffer is the data sent from Erlang
- (a list of bytes) converted to an C array (of bytes). </p>
- <p>If Erlang sends i.e. the list <c><![CDATA[[$a,$b,$c]]]></c> to the port,
- the <c><![CDATA[bufflen]]></c> variable will be <c><![CDATA[3]]></c> ant the
- <c><![CDATA[buff]]></c> variable will contain <c><![CDATA[{'a','b','c'}]]></c> (no
- null termination). Usually the first byte is used as an
- opcode, which is the case in our driver to (at least when the
- port is in command mode). The opcodes are defined as:</p>
- <list type="bulleted">
- <item>'L'&lt;socketname&gt;: Create and listen on socket with the
- given name.</item>
- <item>'A'&lt;listennumber as 32 bit bigendian&gt;: Accept from the
- listen socket identified by the given identification
- number. The identification number is retrieved with the
- uds_control routine.</item>
- <item>'C'&lt;socketname&gt;: Connect to the socket named
- &lt;socketname&gt;.</item>
- <item>'S'&lt;data&gt;: Send the data &lt;data&gt; on the
- connected/accepted socket (in command mode). The sending is
- acked when the data has left this process.</item>
- <item>'R': Receive one packet of data.</item>
- </list>
- <p>One may wonder what is meant by "one packet of data" in the
- 'R' command. This driver always sends data packeted with a 4
- byte header containing a big endian 32 bit integer that
+ (a list of bytes) converted to an C array (of bytes).</p>
+
+ <p>If Erlang sends, for example, the list <c><![CDATA[[$a,$b,$c]]]></c>
+ to the port, the <c><![CDATA[bufflen]]></c> variable is
+ <c><![CDATA[3]]></c> and the <c><![CDATA[buff]]></c> variable contains
+ <c><![CDATA[{'a','b','c'}]]></c> (no
+ <c>NULL</c> termination). Usually the first byte is used as an
+ opcode, which is the case in this driver too (at least when the
+ port is in <c>command</c> mode). The opcodes are defined as follows:</p>
+
+ <taglist>
+ <tag><c>'L'&lt;socket name&gt;</c></tag>
+ <item>
+ <p>Creates and listens on socket with the specified name.</p>
+ </item>
+ <tag><c>'A'&lt;listen number as 32-bit big-endian&gt;</c></tag>
+ <item>
+ <p>Accepts from the listen socket identified by the specified
+ identification number. The identification number is retrieved with
+ the <c>uds_control</c> routine.</p>
+ </item>
+ <tag><c>'C'&lt;socket name&gt;</c></tag>
+ <item>
+ <p>Connects to the socket named &lt;socket name&gt;.</p>
+ </item>
+ <tag><c>'S'&lt;data&gt;</c></tag>
+ <item>
+ <p>Sends the data &lt;data&gt; on the
+ connected/accepted socket (in <c>command</c> mode). The sending is
+ acknowledged when the data has left this process.</p>
+ </item>
+ <tag><c>'R'</c></tag>
+ <item>
+ <p>Receives one packet of data.</p>
+ </item>
+ </taglist>
+
+ <p>"One packet of data" in command <c>'R'</c> can be explained
+ as follows. This driver always sends data packaged with a 4
+ byte header containing a big-endian 32-bit integer that
represents the length of the data in the packet. There is no
need for different packet sizes or some kind of streamed
- mode, as this driver is for the distribution only. One may
- wonder why the header word is coded explicitly in big endian
- when an UDS socket is local to the host. The answer simply is
- that I see it as a good practice when writing a distribution
- driver, as distribution in practice usually cross the host
- boundaries. </p>
- <p>On line 4-8 we handle the case where the port is in data or
- intermediate mode, the rest of the routine handles the
- different commands. We see (first on line 15) that the routine
- uses the <c><![CDATA[driver_failure_posix()]]></c> routine to report
- errors. One important thing to remember is that the failure
- routines make a call to our <c><![CDATA[uds_stop]]></c> routine, which
- will remove the internal port data. The handle (and the casted
- handle <c><![CDATA[ud]]></c>) is therefore <em>invalid pointers</em> after a
- <c><![CDATA[driver_failure]]></c> call and we should <em>immediately return</em>. The runtime system will send exit signals to all
+ mode, as this driver is for the distribution only.
+ Why is the header word coded explicitly in big-endian when a UDS
+ socket is local to the host? It is good practice when writing a
+ distribution driver, as distribution in practice usually crosses
+ the host boundaries.</p>
+
+ <p>On line 4-8 is handled the case where the port is in <c>data</c> mode
+ or <c>intermediate</c> mode and the remaining routine handles the
+ different commands. The routine uses the
+ <c><![CDATA[driver_failure_posix()]]></c> routine to report errors
+ (see, for example, line 15). Notice that the failure routines make
+ a call to the <c><![CDATA[uds_stop]]></c> routine, which will
+ remove the internal port data. The handle (and the casted handle
+ <c><![CDATA[ud]]></c>) is therefore <em>invalid pointers</em> after a
+ <c><![CDATA[driver_failure]]></c> call and we should <em>return
+ immediately</em>. The runtime system will send exit signals to all
linked processes.</p>
- <p>The uds_input routine gets called when data is available on a
- file descriptor previously passed to the <c><![CDATA[driver_select]]></c>
- routine. Typically this happens when a read command is issued
- and no data is available. Lets look at the <c><![CDATA[do_recv]]></c>
- routine:</p>
+
+ <p>The <c>uds_input</c> routine is called when data is available on a
+ file descriptor previously passed to the
+ <c><![CDATA[driver_select]]></c> routine. This occurs typically when
+ a read command is issued and no data is available. The
+ <c><![CDATA[do_recv]]></c> routine is as follows:</p>
+
<code type="none"><![CDATA[
( 1) static void do_recv(UdsData *ud)
( 2) {
@@ -716,29 +898,34 @@
(26) }
(27) }
(28) } ]]></code>
+
<p>The routine tries to read data until a packet is read or the
<c><![CDATA[buffered_read_package]]></c> routine returns a
- <c><![CDATA[NORMAL_READ_FAILURE]]></c> (an internally defined constant for
- the module that means that the read operation resulted in an
- <c><![CDATA[EWOULDBLOCK]]></c>). If the port is in command mode, the
- reading stops when one package is read, but if it is in data
- mode, the reading continues until the socket buffer is empty
- (read failure). If no more data can be read and more is wanted
- (always the case when socket is in data mode) driver_select is
- called to make the <c><![CDATA[uds_input]]></c> call-back be called when
- more data is available for reading.</p>
- <p>When the port is in data mode, all data is sent to Erlang in a
- format that suits the distribution, in fact the raw data will
+ <c><![CDATA[NORMAL_READ_FAILURE]]></c> (an internally defined constant
+ for the module, which means that the read operation resulted in an
+ <c><![CDATA[EWOULDBLOCK]]></c>). If the port is in <c>command</c> mode,
+ the reading stops when one package is read. If the port is in
+ <c>data</c> mode, the reading continues until the socket buffer is empty
+ (read failure). If no more data can be read and more is wanted (which
+ is always the case when the socket is in <c>data</c> mode),
+ <c>driver_select</c> is called to make the <c><![CDATA[uds_input]]></c>
+ callback be called when more data is available for reading.</p>
+
+ <p>When the port is in <c>data</c> mode, all data is sent to Erlang in a
+ format that suits the distribution. In fact, the raw data will
never reach any Erlang process, but will be
translated/interpreted by the emulator itself and then
delivered in the correct format to the correct processes. In
- the current emulator version, received data should be tagged
- with a single byte of 100. Thats what the macro
- <c><![CDATA[DIST_MAGIC_RECV_TAG]]></c> is defined to. The tagging of data
- in the distribution will possibly change in the future.</p>
- <p>The <c><![CDATA[uds_input]]></c> routine will handle other input events
- (like nonblocking <c><![CDATA[accept]]></c>), but most importantly handle
+ the current emulator version, received data is to be tagged
+ with a single byte of 100. That is what the macro
+ <c><![CDATA[DIST_MAGIC_RECV_TAG]]></c> is defined to. The tagging of
+ data in the distribution can be changed in the future.</p>
+
+ <p>The <c><![CDATA[uds_input]]></c> routine handles other input events
+ (like non-blocking <c><![CDATA[accept]]></c>), but most importantly
+ handle
data arriving at the socket by calling <c><![CDATA[do_recv]]></c>:</p>
+
<code type="none"><![CDATA[
( 1) static void uds_input(ErlDrvData handle, ErlDrvEvent event)
( 2) {
@@ -768,13 +955,16 @@
(24) }
(25) do_recv(ud);
(26) } ]]></code>
- <p>The important line here is the last line in the function, the
- <c><![CDATA[do_read]]></c> routine is called to handle new input. The rest
- of the function handles input on a listen socket, which means
- that there should be possible to do an accept on the
+
+ <p>The important line is the last line in the function: the
+ <c><![CDATA[do_read]]></c> routine is called to handle new input.
+ The remaining function handles input on a listen socket, which means
+ that it is to be possible to do an accept on the
socket, which is also recognized as a read event.</p>
- <p>The output mechanisms are similar to the input. Lets first
- look at the <c><![CDATA[do_send]]></c> routine:</p>
+
+ <p>The output mechanisms are similar to the input.
+ The <c><![CDATA[do_send]]></c> routine is as follows:</p>
+
<code type="none"><![CDATA[
( 1) static void do_send(UdsData *ud, char *buff, int bufflen)
( 2) {
@@ -816,33 +1006,36 @@
(37) driver_enqv(ud->port, &eio, written);
(38) send_out_queue(ud);
(39) } ]]></code>
+
<p>This driver uses the <c><![CDATA[writev]]></c> system call to send data
- onto the socket. A combination of writev and the driver output
- queues is very convenient. An <em>ErlIOVec</em> structure
- contains a <em>SysIOVec</em> (which is equivalent to the
- <c><![CDATA[struct iovec]]></c> structure defined in <c><![CDATA[uio.h]]></c>. The
- ErlIOVec also contains an array of <em>ErlDrvBinary</em>
+ onto the socket. A combination of <c>writev</c> and the driver output
+ queues is very convenient. An <c>ErlIOVec</c> structure
+ contains a <c>SysIOVec</c> (which is equivalent to the
+ <c><![CDATA[struct iovec]]></c> structure defined in
+ <c><![CDATA[uio.h]]></c>. The
+ <c>ErlIOVec</c> also contains an array of <c>ErlDrvBinary</c>
pointers, of the same length as the number of buffers in the
I/O vector itself. One can use this to allocate the binaries
- for the queue "manually" in the driver, but we'll just fill
- the binary array with NULL values (line 7) , which will make
- the runtime system allocate its own buffers when we call
- <c><![CDATA[driver_enqv]]></c> (line 37).</p>
- <p></p>
+ for the queue "manually" in the driver, but here
+ the binary array is filled with <c>NULL</c> values (line 7).
+ The runtime system then allocates its own buffers when
+ <c><![CDATA[driver_enqv]]></c> is called (line 37).</p>
+
<p>The routine builds an I/O vector containing the header bytes
and the buffer (the opcode has been removed and the buffer
length decreased by the output routine). If the queue is
- empty, we'll write the data directly to the socket (or at
+ empty, we write the data directly to the socket (or at
least try to). If any data is left, it is stored in the queue
- and then we try to send the queue (line 38). An ack is sent
- when the message is delivered completely (line 22). The
- <c><![CDATA[send_out_queue]]></c> will send acks if the sending is
- completed there. If the port is in command mode, the Erlang
+ and then we try to send the queue (line 38). An acknowledgement
+ is sent when the message is delivered completely (line 22). The
+ <c><![CDATA[send_out_queue]]></c> sends acknowledgements if the sending
+ is completed there. If the port is in <c>command</c> mode, the Erlang
code serializes the send operations so that only one packet
- can be waiting for delivery at a time. Therefore the ack can
- be sent simply whenever the queue is empty.</p>
- <p></p>
- <p>A short look at the <c><![CDATA[send_out_queue]]></c> routine:</p>
+ can be waiting for delivery at a time. Therefore the acknowledgement
+ can be sent whenever the queue is empty.</p>
+
+ <p>The <c><![CDATA[send_out_queue]]></c> routine is as follows:</p>
+
<code type="none"><![CDATA[
( 1) static int send_out_queue(UdsData *ud)
( 2) {
@@ -874,20 +1067,24 @@
(28) ud->sent += wrote;
(29) }
(30) } ]]></code>
- <p>What we do is simply to pick out an I/O vector from the queue
- (which is the whole queue as an <em>SysIOVec</em>). If the I/O
- vector is to long (IO_VECTOR_MAX is defined to 16), the vector
+
+ <p>We simply pick out an I/O vector from the queue
+ (which is the whole queue as a <c>SysIOVec</c>). If the I/O
+ vector is too long (<c>IO_VECTOR_MAX</c> is defined to 16), the vector
length is decreased (line 15), otherwise the <c><![CDATA[writev]]></c>
- (line 17) call will
- fail. Writing is tried and anything written is dequeued (line
- 27). If the write fails with <c><![CDATA[EWOULDBLOCK]]></c> (note that all
- sockets are in nonblocking mode), <c><![CDATA[driver_select]]></c> is
+ call (line 17) fails. Writing is tried and anything written is dequeued
+ (line 27).
+ If the write fails with <c><![CDATA[EWOULDBLOCK]]></c> (notice that all
+ sockets are in non-blocking mode), <c><![CDATA[driver_select]]></c> is
called to make the <c><![CDATA[uds_output]]></c> routine be called when
there is space to write again.</p>
- <p>We will continue trying to write until the queue is empty or
- the writing would block.</p>
- <p>The routine above are called from the <c><![CDATA[uds_output]]></c>
- routine, which looks like this:</p>
+
+ <p>We continue trying to write until the queue is empty or
+ the writing blocks.</p>
+
+ <p>The routine above is called from the <c><![CDATA[uds_output]]></c>
+ routine:</p>
+
<code type="none"><![CDATA[
( 1) static void uds_output(ErlDrvData handle, ErlDrvEvent event)
( 2) {
@@ -900,51 +1097,80 @@
( 9) }
(10) send_out_queue(ud);
(11) } ]]></code>
- <p>The routine is simple, it first handles the fact that the
+
+ <p>The routine is simple: it first handles the fact that the
output select will concern a socket in the business of
connecting (and the connecting blocked). If the socket is in
- a connected state it simply sends the output queue, this
- routine is called when there is possible to write to a socket
+ a connected state, it simply sends the output queue. This
+ routine is called when it is possible to write to a socket
where we have an output queue, so there is no question what to
do.</p>
+
<p>The driver implements a control interface, which is a
synchronous interface called when Erlang calls
- <c><![CDATA[erlang:port_control/3]]></c>. This is the only interface
- that can control the driver when it is in data mode and it may
+ <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso>. Only this interface
+ can control the driver when it is in <c>data</c> mode. It can
be called with the following opcodes:</p>
- <list type="bulleted">
- <item>'C': Set port in command mode.</item>
- <item>'I': Set port in intermediate mode.</item>
- <item>'D': Set port in data mode.</item>
- <item>'N': Get identification number for listen port, this
- identification number is used in an accept command to the
- driver, it is returned as a big endian 32 bit integer, which
- happens to be the file identifier for the listen socket.</item>
- <item>'S': Get statistics, which is the number of bytes received,
- the number of bytes sent and the number of bytes pending in
- the output queue. This data is used when the distribution
- checks that a connection is alive (ticking). The statistics
- is returned as 3 32 bit big endian integers.</item>
- <item>'T': Send a tick message, which is a packet of length
- 0. Ticking is done when the port is in data mode, so the
- command for sending data cannot be used (besides it ignores
- zero length packages in command mode). This is used by the
- ticker to send dummy data when no other traffic is present.
- <em>Note</em> that it is important that the interface for
- sending ticks is not blocking. This implementation uses
- <c>erlang:port_control/3</c> which does not block the caller.
- If <c>erlang:port_command</c> is used, use
- <c>erlang:port_command/3</c> and pass <c>[force]</c> as
- option list; otherwise, the caller can be blocked indefinitely
- on a busy port and prevent the system from taking down a
- connection that is not functioning.</item>
- <item>'R': Get creation number of listen socket, which is used to
- dig out the number stored in the lock file to differentiate
- between invocations of Erlang nodes with the same name.</item>
- </list>
+
+ <taglist>
+ <tag><c>'C'</c></tag>
+ <item>
+ <p>Sets port in <c>command</c> mode.</p>
+ </item>
+ <tag><c>'I'</c></tag>
+ <item>
+ <p>Sets port in <c>intermediate</c> mode.</p>
+ </item>
+ <tag><c>'D'</c></tag>
+ <item>
+ <p>Sets port in <c>data</c> mode.</p>
+ </item>
+ <tag><c>'N'</c></tag>
+ <item>
+ <p>Gets identification number for listen port. This
+ identification number is used in an accept command to the
+ driver. It is returned as a big-endian 32-bit integer, which
+ is the file identifier for the listen socket.</p>
+ </item>
+ <tag><c>'S'</c></tag>
+ <item>
+ <p>Gets statistics, which is the number of bytes received,
+ the number of bytes sent, and the number of bytes pending in
+ the output queue. This data is used when the distribution
+ checks that a connection is alive (ticking). The statistics
+ is returned as three 32-bit big-endian integers.</p>
+ </item>
+ <tag><c>'T'</c></tag>
+ <item>
+ <p>Sends a tick message, which is a packet of length 0.
+ Ticking is done when the port is in <c>data</c> mode, so the
+ command for sending data cannot be used (besides it ignores
+ zero length packages in <c>command</c> mode). This is used by the
+ ticker to send dummy data when no other traffic is present.</p>
+ <p><em>Note:</em> It is important that the interface for
+ sending ticks is not blocking. This implementation uses
+ <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso>, which does not block the
+ caller. If <c>erlang:port_command</c> is used, use
+ <seealso marker="erlang#port_command/3">
+ <c>erlang:port_command/3</c></seealso> and pass <c>[force]</c> as
+ option list; otherwise the caller can be blocked indefinitely
+ on a busy port and prevent the system from taking down a
+ connection that is not functioning.</p>
+ </item>
+ <tag><c>'R'</c></tag>
+ <item>
+ <p>Gets creation number of a listen socket, which is used to
+ dig out the number stored in the lock file to differentiate
+ between invocations of Erlang nodes with the same name.</p>
+ </item>
+ </taglist>
+
<p>The control interface gets a buffer to return its value in,
- but is free to allocate its own buffer is the provided one is
- to small. Here is the code for <c><![CDATA[uds_control]]></c>:</p>
+ but is free to allocate its own buffer if the provided one is
+ too small. The <c><![CDATA[uds_control]]></c> code is as follows:</p>
+
<code type="none"><![CDATA[
( 1) static int uds_control(ErlDrvData handle, unsigned int command,
( 2) char* buf, int count, char** res, int res_size)
@@ -1025,47 +1251,55 @@
(75) }
(76) #undef ENSURE
(77) } ]]></code>
- <p>The macro <c><![CDATA[ENSURE]]></c> (line 5 to 10) is used to ensure that
- the buffer is large enough for our answer. We switch on the
- command and take actions, there is not much to say about this
- routine. Worth noting is that we always has read select active
- on a port in data mode (achieved by calling <c><![CDATA[do_recv]]></c> on
- line 45), but turn off read selection in intermediate and
- command modes (line 27 and 36).</p>
- <p>The rest of the driver is more or less UDS specific and not of
+
+ <p>The macro <c><![CDATA[ENSURE]]></c> (line 5-10) is used to ensure that
+ the buffer is large enough for the answer. We switch on the command and
+ take actions. We always have read select active on a port in <c>data</c>
+ mode (achieved by calling <c><![CDATA[do_recv]]></c> on line 45), but
+ we turn off read selection in <c>intermediate</c> and <c>command</c>
+ modes (line 27 and 36).</p>
+
+ <p>The rest of the driver is more or less UDS-specific and not of
general interest.</p>
</section>
</section>
<section>
- <title>Putting it all together</title>
- <p>To test the distribution, one can use the
- <c><![CDATA[net_kernel:start/1]]></c> function, which is useful as it starts
- the distribution on a running system, where tracing/debugging
- can be performed. The <c><![CDATA[net_kernel:start/1]]></c> routine takes a
- list as its single argument. The lists first element should be
- the node name (without the "@hostname") as an atom, and the second (and
- last) element should be one of the atoms <c><![CDATA[shortnames]]></c> or
- <c><![CDATA[longnames]]></c>. In the example case <c><![CDATA[shortnames]]></c> is
- preferred. </p>
- <p>For net kernel to find out which distribution module to use, the
- command line argument <c><![CDATA[-proto_dist]]></c> is used. The argument
- is followed by one or more distribution module names, with the
- "_dist" suffix removed, i.e. uds_dist as a distribution module
+ <title>Putting It All Together</title>
+ <p>To test the distribution, the <c><![CDATA[net_kernel:start/1]]></c>
+ function can be used. It is useful, as it starts the distribution on a
+ running system, where tracing/debugging can be performed.
+ The <c><![CDATA[net_kernel:start/1]]></c> routine takes a
+ list as its single argument. The list first element in the list is to be
+ the node name (without the "@hostname") as an atom. The second (and
+ last) element is to be one of the atoms <c><![CDATA[shortnames]]></c> or
+ <c><![CDATA[longnames]]></c>. In the example case,
+ <c><![CDATA[shortnames]]></c> is preferred.</p>
+
+ <p>For <c>net_kernel</c> to find out which distribution module to use,
+ command-line argument <c><![CDATA[-proto_dist]]></c> is used. It
+ is followed by one or more distribution module names, with suffix
+ "_dist" removed, that is, <c>uds_dist</c> as a distribution module
is specified as <c><![CDATA[-proto_dist uds]]></c>.</p>
- <p>If no epmd (TCP port mapper daemon) is used, one should also
- specify the command line option <c><![CDATA[-no_epmd]]></c>, which will make
- Erlang skip the epmd startup, both as a OS process and as an
+
+ <p>If no <c>epmd</c> (TCP port mapper daemon) is used, also command-line
+ option <c><![CDATA[-no_epmd]]></c> is to be specified, which makes
+ Erlang skip the <c>epmd</c> startup, both as an OS process and as an
Erlang ditto.</p>
+
<p>The path to the directory where the distribution modules reside
- must be known at boot, which can either be achieved by
- specifying <c><![CDATA[-pa <path>]]></c> on the command line or by building
- a boot script containing the applications used for your
- distribution protocol (in the uds_dist protocol, it's only the
- uds_dist application that needs to be added to the script).</p>
- <p>The distribution will be started at boot if all the above is
+ must be known at boot. This can be achieved either by
+ specifying <c><![CDATA[-pa <path>]]></c> on the command line or by
+ building a boot script containing the applications used for your
+ distribution protocol. (In the <c>uds_dist</c> protocol, only the
+ <c>uds_dist</c> application needs to be added to the script.)</p>
+
+ <p>The distribution starts at boot if all the above is
specified and an <c><![CDATA[-sname <name>]]></c> flag is present at the
- command line, here follows two examples: </p>
+ command line.</p>
+
+ <p><em>Example 1:</em></p>
+
<pre>
$ <input>erl -pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin -proto_dist uds -no_epmd</input>
Erlang (BEAM) emulator version 5.0
@@ -1074,7 +1308,9 @@ Eshell V5.0 (abort with ^G)
1> <input>net_kernel:start([bing,shortnames]).</input>
{ok,&lt;0.30.0>}
(bing@hador)2></pre>
- <p>...</p>
+
+ <p><em>Example 2:</em></p>
+
<pre>
$ <input>erl -pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin -proto_dist uds \ </input>
<input> -no_epmd -sname bong</input>
@@ -1082,8 +1318,10 @@ Erlang (BEAM) emulator version 5.0
Eshell V5.0 (abort with ^G)
(bong@hador)1></pre>
- <p>One can utilize the ERL_FLAGS environment variable to store the
+
+ <p>The <c>ERL_FLAGS</c> environment variable can be used to store the
complicated parameters in:</p>
+
<pre>
$ <input>ERL_FLAGS=-pa $ERL_TOP/lib/kernel/examples/uds_dist/ebin \ </input>
<input> -proto_dist uds -no_epmd</input>
@@ -1093,8 +1331,8 @@ Erlang (BEAM) emulator version 5.0
Eshell V5.0 (abort with ^G)
(bang@hador)1></pre>
- <p>The <c><![CDATA[ERL_FLAGS]]></c> should preferably not include the name of
- the node.</p>
+
+ <p><c><![CDATA[ERL_FLAGS]]></c> should not include the node name.</p>
</section>
</chapter>
diff --git a/erts/doc/src/communication.xml b/erts/doc/src/communication.xml
index 1eb05310e9..7e18a73aa8 100644
--- a/erts/doc/src/communication.xml
+++ b/erts/doc/src/communication.xml
@@ -26,63 +26,72 @@
<prepared>Rickard Green</prepared>
<responsible></responsible>
<docno></docno>
- <approved></approved>
+ <approved>uy</approved>
<checked></checked>
<date>2012-12-03</date>
<rev>PA1</rev>
<file>communication.xml</file>
</header>
<p>Communication in Erlang is conceptually performed using
- asynchronous signaling. All different executing entities
- such as processes, and ports communicate via asynchronous
+ asynchronous signaling. All different executing entities,
+ such as processes and ports, communicate through asynchronous
signals. The most commonly used signal is a message. Other
- common signals are exit, link, unlink, monitor, demonitor
+ common signals are exit, link, unlink, monitor, and demonitor
signals.</p>
+
<section>
<title>Passing of Signals</title>
- <p>The amount of time that passes between a signal being sent
+ <p>The amount of time that passes between a signal is sent
and the arrival of the signal at the destination is unspecified
- but positive. If the receiver has terminated, the signal will
- not arrive, but it is possible that it triggers another signal.
- For example, a link signal sent to a non-existing process will
- trigger an exit signal which will be sent back to where the link
+ but positive. If the receiver has terminated, the signal does
+ not arrive, but it can trigger another signal.
+ For example, a link signal sent to a non-existing process
+ triggers an exit signal, which is sent back to where the link
signal originated from. When communicating over the distribution,
- signals may be lost if the distribution channel goes down.</p>
- <p>The only signal ordering guarantee given is the following. If
+ signals can be lost if the distribution channel goes down.</p>
+
+ <p>The only signal ordering guarantee given is the following: if
an entity sends multiple signals to the same destination entity,
- the order will be preserved. That is, if <c>A</c> sends
+ the order is preserved; that is, if <c>A</c> sends
a signal <c>S1</c> to <c>B</c>, and later sends
- the signal <c>S2</c> to <c>B</c>, <c>S1</c> is guaranteed not to
+ signal <c>S2</c> to <c>B</c>, <c>S1</c> is guaranteed not to
arrive after <c>S2</c>.</p>
</section>
+
<section>
<title>Synchronous Communication</title>
<p>Some communication is synchronous. If broken down into pieces,
- a synchronous communication operation, consists of two asynchronous
- signals. One request signal and one reply signal. An example of
- such a synchronous communication is a call to <c>process_info/2</c>
- when the first argument is not <c>self()</c>. The caller will send
- an asynchronous signal requesting information, and will then
- wait for the reply signal containing the requested information. When
- the request signal reaches its destination the destination process
+ a synchronous communication operation consists of two asynchronous
+ signals; one request signal and one reply signal. An example of
+ such a synchronous communication is a call to
+ <seealso marker="erlang:process_info/2">
+ <c>erlang:process_info/2</c></seealso>
+ when the first argument is not <c>self()</c>. The caller sends
+ an asynchronous signal requesting information, and then
+ waits for the reply signal containing the requested information. When
+ the request signal reaches its destination, the destination process
replies with the requested information.</p>
</section>
+
<section>
<title>Implementation</title>
- <p>The implementation of different asynchronous signals in the
- VM may vary over time, but the behaviour will always respect this
+ <p>The implementation of different asynchronous signals in the virtual
+ machine can vary over time, but the behavior always respects this
concept of asynchronous signals being passed between entities
as described above.</p>
- <p>By inspecting the implementation you might notice that some
- specific signal actually gives a stricter guarantee than described
+
+ <p>By inspecting the implementation, you might notice that some
+ specific signal gives a stricter guarantee than described
above. It is of vital importance that such knowledge about the
- implementation is <em>not</em> used by Erlang code, since the
- implementation might change at any time without prior notice.</p>
- <p>Some example of major implementation changes:</p>
+ implementation is <em>not</em> used by Erlang code, as the
+ implementation can change at any time without prior notice.</p>
+
+ <p>Examples of major implementation changes:</p>
+
<list type="bulleted">
- <item>As of ERTS version 5.5.2 exit signals to processes are truly
+ <item>As from ERTS 5.5.2 exit signals to processes are truly
asynchronously delivered.</item>
- <item>As of ERTS version 5.10 all signals from processes to ports
+ <item>As from ERTS 5.10 all signals from processes to ports
are truly asynchronously delivered.</item>
</list>
</section>
diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml
index 0b827ae583..a9aeb1888c 100644
--- a/erts/doc/src/crash_dump.xml
+++ b/erts/doc/src/crash_dump.xml
@@ -22,7 +22,7 @@
</legalnotice>
- <title>How to interpret the Erlang crash dumps</title>
+ <title>How to Interpret the Erlang Crash Dumps</title>
<prepared>Patrik Nyblom</prepared>
<responsible></responsible>
<docno></docno>
@@ -32,401 +32,529 @@
<rev>PA1</rev>
<file>crash_dump.xml</file>
</header>
- <p>This document describes the <c><![CDATA[erl_crash.dump]]></c> file generated
- upon abnormal exit of the Erlang runtime system.</p>
- <p><em>Important:</em> For OTP release R9C the Erlang crash dump has
- had a major facelift. This means that the information in this
- document will not be directly applicable for older dumps. However,
- if you use the Crashdump Viewer tool on older dumps, the crash
- dumps are translated into a format similar to this.</p>
- <p>The system will write the crash dump in the current directory of
+ <p>This section describes the <c><![CDATA[erl_crash.dump]]></c> file
+ generated upon abnormal exit of the Erlang runtime system.</p>
+
+ <note>
+ <p>The Erlang crash dump had a major facelift in Erlang/OTP R9C. The
+ information in this section is therefore not directly applicable for
+ older dumps. However, if you use <seealso marker="observer:crashdump_viewer">
+ <c>crashdump_viewer(3)</c></seealso> on older dumps,
+ the crash dumps are translated into a format similar to this.</p>
+ </note>
+
+ <p>The system writes the crash dump in the current directory of
the emulator or in the file pointed out by the environment variable
(whatever that means on the current operating system)
- ERL_CRASH_DUMP. For a crash dump to be written, there has to be a
- writable file system mounted.</p>
+ <c>ERL_CRASH_DUMP</c>. For a crash dump to be written, a
+ writable file system must be mounted.</p>
+
<p>Crash dumps are written mainly for one of two reasons: either the
- builtin function <c><![CDATA[erlang:halt/1]]></c> is called explicitly with a
- string argument from running Erlang code, or else the runtime
+ built-in function <c><![CDATA[erlang:halt/1]]></c> is called explicitly
+ with a string argument from running Erlang code, or the runtime
system has detected an error that cannot be handled. The most
- usual reason that the system can't handle the error is that the
+ usual reason that the system cannot handle the error is that the
cause is external limitations, such as running out of memory. A
- crash dump due to an internal error may be caused by the system
+ crash dump caused by an internal error can be caused by the system
reaching limits in the emulator itself (like the number of atoms
- in the system, or too many simultaneous ets tables). Usually the
+ in the system, or too many simultaneous ETS tables). Usually the
emulator or the operating system can be reconfigured to avoid the
crash, which is why interpreting the crash dump correctly is
important.</p>
+
<p>On systems that support OS signals, it is also possible to stop
- the runtime system and generate a crash dump by sending the SIGUSR1.</p>
- <p>The erlang crash dump is a readable text file, but it might not be
- very easy to read. Using the Crashdump Viewer tool in the
- <c><![CDATA[observer]]></c> application will simplify the task. This is an
- wx-widget based tool for browsing Erlang crash dumps.</p>
+ the runtime system and generate a crash dump by sending the <c>SIGUSR1</c>
+ signal.</p>
+
+ <p>The Erlang crash dump is a readable text file, but it can be difficult
+ to read. Using the Crashdump Viewer tool in the
+ <c><![CDATA[Observer]]></c> application simplifies the task. This is a
+ wx-widget-based tool for browsing Erlang crash dumps.</p>
<section>
<marker id="general_info"></marker>
- <title>General information</title>
- <p>The first part of the dump shows the creation time for the dump,
- a slogan indicating the reason for the dump, the system version,
- of the node from which the dump originates, the compile time of
- the emulator running the originating node, the number of
- atoms in the atom table and the runtime system thread that caused
- the crash dump to happen.
- </p>
+ <title>General Information</title>
+ <p>The first part of the crash dump shows the following:</p>
+
+ <list type="bulleted">
+ <item>The creation time for the dump</item>
+ <item>A slogan indicating the reason for the dump</item>
+ <item>The system version of the node from which the dump originates</item>
+ <item>The compile time of the emulator running the originating node</item>
+ <item>The number of atoms in the atom table</item>
+ <item>The runtime system thread that caused the crash dump</item>
+ </list>
<section>
- <title>Reasons for crash dumps (slogan)</title>
- <p>The reason for the dump is noted in the beginning of the file
- as <em>Slogan: &lt;reason&gt;</em> (the word "slogan" has historical
- roots). If the system is halted by the BIF
+ <title>Reasons for Crash Dumps (Slogan)</title>
+ <p>The reason for the dump is shown in the beginning of the file as:</p>
+
+ <pre>
+Slogan: &lt;reason&gt;</pre>
+
+ <p>If the system is halted by the BIF
<c><![CDATA[erlang:halt/1]]></c>, the slogan is the string parameter
passed to the BIF, otherwise it is a description generated by
the emulator or the (Erlang) kernel. Normally the message
- should be enough to understand the problem, but nevertheless
- some messages are described here. Note however that the
- suggested reasons for the crash are <em>only suggestions</em>. The exact reasons for the errors may vary
+ is enough to understand the problem, but
+ some messages are described here. Notice that the
+ suggested reasons for the crash are <em>only suggestions</em>.
+ The exact reasons for the errors can vary
depending on the local applications and the underlying
operating system.</p>
- <list type="bulleted">
- <item>"<em>&lt;A&gt;</em>: Cannot allocate <em>&lt;N&gt;</em>
- bytes of memory (of type "<em>&lt;T&gt;</em>")." - The system
- has run out of memory. &lt;A&gt; is the allocator that failed
- to allocate memory, &lt;N&gt; is the number of bytes that
- &lt;A&gt; tried to allocate, and &lt;T&gt; is the memory block
- type that the memory was needed for. The most common case is
- that a process stores huge amounts of data. In this case
- &lt;T&gt; is most often <c><![CDATA[heap]]></c>, <c><![CDATA[old_heap]]></c>,
- <c><![CDATA[heap_frag]]></c>, or <c><![CDATA[binary]]></c>. For more information on
- allocators see
- <seealso marker="erts_alloc">erts_alloc(3)</seealso>.</item>
- <item>"<em>&lt;A&gt;</em>: Cannot reallocate <em>&lt;N&gt;</em>
- bytes of memory (of type "<em>&lt;T&gt;</em>")." - Same as
- above with the exception that memory was being reallocated
- instead of being allocated when the system ran out of memory.</item>
- <item>"Unexpected op code <em>N</em>" - Error in compiled
- code, <c><![CDATA[beam]]></c> file damaged or error in the compiler.</item>
- <item>"Module <em>Name</em> undefined" <c><![CDATA[|]]></c> "Function
- <em>Name</em> undefined" <c><![CDATA[|]]></c> "No function
- <em>Name</em>:<em>Name</em>/1" <c><![CDATA[|]]></c> "No function
- <em>Name</em>:start/2" - The kernel/stdlib applications are
- damaged or the start script is damaged.</item>
- <item>"Driver_select called with too large file descriptor
- <c><![CDATA[N]]></c>" - The number of file descriptors for sockets
- exceed 1024 (Unix only). The limit on file-descriptors in
- some Unix flavors can be set to over 1024, but only 1024
- sockets/pipes can be used simultaneously by Erlang (due to
- limitations in the Unix <c><![CDATA[select]]></c> call). The number of
- open regular files is not affected by this.</item>
- <item>"Received SIGUSR1" - Sending the SIGUSR1 signal to a
- Erlang machine (Unix only) forces a crash dump. This slogan reflects
- that the Erlang machine crash-dumped due to receiving that signal.</item>
- <item>"Kernel pid terminated (<em>Who</em>)
- (<em>Exit-reason</em>)" - The kernel supervisor has detected
- a failure, usually that the <c><![CDATA[application_controller]]></c>
- has shut down (<c><![CDATA[Who]]></c> = <c><![CDATA[application_controller]]></c>,
- <c><![CDATA[Why]]></c> = <c><![CDATA[shutdown]]></c>). The application controller
- may have shut down for a number of reasons, the most usual
- being that the node name of the distributed Erlang node is
- already in use. A complete supervisor tree "crash" (i.e.,
- the top supervisors have exited) will give about the same
- result. This message comes from the Erlang code and not from
- the virtual machine itself. It is always due to some kind of
- failure in an application, either within OTP or a
- "user-written" one. Looking at the error log for your
- application is probably the first step to take.</item>
- <item>"Init terminating in do_boot ()" - The primitive Erlang boot
- sequence was terminated, most probably because the boot
- script has errors or cannot be read. This is usually a
- configuration error - the system may have been started with
- a faulty <c><![CDATA[-boot]]></c> parameter or with a boot script from
- the wrong version of OTP.</item>
- <item>"Could not start kernel pid (<em>Who</em>) ()" - One of the
- kernel processes could not start. This is probably due to
- faulty arguments (like errors in a <c><![CDATA[-config]]></c> argument)
- or faulty configuration files. Check that all files are in
- their correct location and that the configuration files (if
- any) are not damaged. Usually there are also messages
- written to the controlling terminal and/or the error log
- explaining what's wrong.</item>
- </list>
- <p>Other errors than the ones mentioned above may occur, as the
- <c><![CDATA[erlang:halt/1]]></c> BIF may generate any message. If the
+
+ <taglist>
+ <tag><em>&lt;A&gt;: Cannot allocate &lt;N&gt; bytes of memory (of type
+ "&lt;T&gt;")</em></tag>
+ <item>
+ <p>The system has run out of memory. &lt;A&gt; is the allocator that
+ failed to allocate memory, &lt;N&gt; is the number of bytes that
+ &lt;A&gt; tried to allocate, and &lt;T&gt; is the memory block
+ type that the memory was needed for. The most common case is
+ that a process stores huge amounts of data. In this case
+ &lt;T&gt; is most often <c><![CDATA[heap]]></c>,
+ <c><![CDATA[old_heap]]></c>, <c><![CDATA[heap_frag]]></c>, or
+ <c><![CDATA[binary]]></c>. For more information on allocators, see
+ <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>.</p>
+ </item>
+ <tag><em>&lt;A&gt;: Cannot reallocate &lt;N&gt; bytes of memory (of
+ type "&lt;T&gt;")</em></tag>
+ <item>
+ <p>Same as above except that memory was reallocated
+ instead of allocated when the system ran out of memory.</p>
+ </item>
+ <tag><em>Unexpected op code &lt;N&gt;</em></tag>
+ <item>
+ <p>Error in compiled code, <c><![CDATA[beam]]></c> file damaged, or
+ error in the compiler.</p>
+ </item>
+ <tag><em>Module &lt;Name&gt; undefined <c><![CDATA[|]]></c> Function
+ &lt;Name&gt; undefined <c><![CDATA[|]]></c> No function
+ &lt;Name&gt;:&lt;Name&gt;/1 <c><![CDATA[|]]></c> No function
+ &lt;Name&gt;:start/2</em></tag>
+ <item>
+ <p>The Kernel/STDLIB applications are
+ damaged or the start script is damaged.</p>
+ </item>
+ <tag><em>Driver_select called with too large file descriptor
+ <c><![CDATA[N]]></c></em></tag>
+ <item>
+ <p>The number of file descriptors for sockets
+ exceeds 1024 (Unix only). The limit on file descriptors in
+ some Unix flavors can be set to over 1024, but only 1024
+ sockets/pipes can be used simultaneously by Erlang (because of
+ limitations in the Unix <c><![CDATA[select]]></c> call). The number
+ of open regular files is not affected by this.</p>
+ </item>
+ <tag><em>Received SIGUSR1</em></tag>
+ <item>
+ <p>Sending the <c>SIGUSR1</c> signal to an Erlang machine (Unix only)
+ forces a crash dump. This slogan reflects that the Erlang machine
+ crash-dumped because of receiving that signal.</p>
+ </item>
+ <tag><em>Kernel pid terminated (&lt;Who&gt;) (&lt;Exit
+ reason&gt;)</em></tag>
+ <item>
+ <p>The kernel supervisor has detected a failure, usually that the
+ <c><![CDATA[application_controller]]></c> has shut down
+ (<c><![CDATA[Who]]></c> = <c><![CDATA[application_controller]]></c>,
+ <c><![CDATA[Why]]></c> = <c><![CDATA[shutdown]]></c>).
+ The application controller
+ can have shut down for many reasons, the most usual
+ is that the node name of the distributed Erlang node is
+ already in use. A complete supervisor tree "crash" (that is,
+ the top supervisors have exited) gives about the same
+ result. This message comes from the Erlang code and not from
+ the virtual machine itself. It is always because of some
+ failure in an application, either within OTP or a
+ "user-written" one. Looking at the error log for your
+ application is probably the first step to take.</p>
+ </item>
+ <tag><em>Init terminating in do_boot ()</em></tag>
+ <item>
+ <p>The primitive Erlang boot sequence was terminated, most probably
+ because the boot script has errors or cannot be read. This is
+ usually a configuration error; the system can have been started
+ with a faulty <c><![CDATA[-boot]]></c> parameter or with a boot
+ script from the wrong OTP version.</p>
+ </item>
+ <tag><em>Could not start kernel pid (&lt;Who&gt;) ()</em></tag>
+ <item>
+ <p>One of the kernel processes could not start. This is probably
+ because of faulty arguments (like errors in a
+ <c><![CDATA[-config]]></c> argument)
+ or faulty configuration files. Check that all files are in
+ their correct location and that the configuration files (if
+ any) are not damaged. Usually messages are also
+ written to the controlling terminal and/or the error log
+ explaining what is wrong.</p>
+ </item>
+ </taglist>
+
+ <p>Other errors than these can occur, as the
+ <c><![CDATA[erlang:halt/1]]></c> BIF can generate any message. If the
message is not generated by the BIF and does not occur in the
- list above, it may be due to an error in the emulator. There
- may however be unusual messages that I haven't mentioned, that
- still are connected to an application failure. There is a lot
- more information available, so more thorough reading of the
- crash dump may reveal the crash reason. The size of processes,
- the number of ets tables and the Erlang data on each process
- stack can be useful for tracking down the problem.</p>
+ list above, it can be because of an error in the emulator. There
+ can however be unusual messages, not mentioned here, which
+ are still connected to an application failure. There is much
+ more information available, so a thorough reading of the
+ crash dump can reveal the crash reason. The size of processes,
+ the number of ETS tables, and the Erlang data on each process
+ stack can be useful to find the problem.</p>
</section>
<section>
- <title>Number of atoms</title>
+ <title>Number of Atoms</title>
<p>The number of atoms in the system at the time of the crash is
shown as <em>Atoms: &lt;number&gt;</em>. Some ten thousands atoms is
- perfectly normal, but more could indicate that the BIF
- <c><![CDATA[erlang:list_to_atom/1]]></c> is used to dynamically generate a
- lot of <em>different</em> atoms, which is never a good idea.</p>
+ perfectly normal, but more can indicate that the BIF
+ <c><![CDATA[erlang:list_to_atom/1]]></c> is used to generate many
+ <em>different</em> atoms dynamically, which is never a good idea.</p>
</section>
</section>
<section>
<marker id="scheduler"></marker>
- <title>Scheduler information</title>
- <p>Under the tag <em>=scheduler</em> information about the current state
- and statistics of the schedulers in the runtime system is displayed.
- On OSs that do allow instant suspension of other threads, the data within
- this section will reflect what the runtime system looks like at the moment
- when the crash happens.</p>
+ <title>Scheduler Information</title>
+ <p>Under the tag <em>=scheduler</em> is shown information about the current
+ state and statistics of the schedulers in the runtime system. On
+ operating systems that allow suspension of other threads, the
+ data within this section reflects what the runtime system looks like
+ when a crash occurs.</p>
+
<p>The following fields can exist for a process:</p>
+
<taglist>
<tag><em>=scheduler:id</em></tag>
- <item>Header, states the scheduler identifier.</item>
+ <item>
+ <p>Heading. States the scheduler identifier.</p>
+ </item>
<tag><em>Scheduler Sleep Info Flags</em></tag>
- <item>If empty the scheduler was doing some work.
- If not empty the scheduler is either in some state of sleep,
- or suspended. This entry is only present in a SMP enabled emulator</item>
+ <item>
+ <p>If empty, the scheduler was doing some work.
+ If not empty, the scheduler is either in some state of sleep,
+ or suspended. This entry is only present in an SMP-enabled
+ emulator.</p>
+ </item>
<tag><em>Scheduler Sleep Info Aux Work</em></tag>
- <item>If not empty, a scheduler internal auxiliary work is scheduled
- to be done.</item>
+ <item>
+ <p>If not empty, a scheduler internal auxiliary work is scheduled
+ to be done.</p>
+ </item>
<tag><em>Current Port</em></tag>
- <item>The port identifier of the port that is currently being
- executed by the scheduler.</item>
+ <item>
+ <p>The port identifier of the port that is currently
+ executed by the scheduler.</p>
+ </item>
<tag><em>Current Process</em></tag>
- <item>The process identifier of the process that is currently being
- executed by the scheduler. If there is such a process this entry is
- followed by the <em>State</em>,<em>Internal State</em>,
- <em>Program Counter</em>, <em>CP</em> of that same process. See
- <seealso marker="#processes">Process Information</seealso> for a
- description what the different entries mean. Keep in mind that
- this is a snapshot of what the entries are exactly when the crash
- dump is starting to be generated. Therefore they will most likely
- be different (and more telling) then the entries for the same
- processes found in the <em>=proc</em> section. If there is no currently
- running process, only the <em>Current Process</em> entry will be printed.
+ <item>
+ <p>The process identifier of the process that is currently
+ executed by the scheduler. If there is such a process, this entry is
+ followed by the <em>State</em>, <em>Internal State</em>,
+ <em>Program Counter</em>, and <em>CP</em> of that same process.
+ The entries are described in section
+ <seealso marker="#processes">Process Information</seealso>.</p>
+ <p>Notice that this is a snapshot of what the entries are exactly when
+ the crash dump is starting to be generated. Therefore they are most
+ likely different (and more telling) than the entries for the same
+ processes found in the <em>=proc</em> section. If there is no
+ currently running process, only the <em>Current Process</em> entry is
+ shown.</p>
</item>
<tag><em>Current Process Limited Stack Trace</em></tag>
- <item>This entry only shows up if there is a current process. It is very
- similar to <seealso marker="#proc_data"><em>=proc_stack</em></seealso>,
- except that only the function frames are printed (i.e. the stack variables
- are omited). It is also limited to only print the top and bottom part
- of the stack. If the stack is small (less that 512 slots) then the
- entire stack will be printed. If not, an entry stating
- <code>skipping ## slots</code> will be printed where ## is
- replaced by the number of slots that has been skipped.</item>
+ <item>
+ <p>This entry is shown only if there is a current process. It is
+ similar to <seealso marker="#proc_data">
+ <em>=proc_stack</em></seealso>, except that only the function frames
+ are shown (that is, the stack variables are omitted).
+ Also, only the top and bottom part of the stack are shown. If the
+ stack is small (&lt; 512 slots), the entire stack is shown. Otherwise
+ the entry <em>skipping ## slots</em> is shown, where <c>##</c>
+ is replaced by the number of slots that has been skipped.</p>
+ </item>
<tag><em>Run Queue</em></tag>
- <item>Displays statistics about how many processes and ports
- of different priorities are scheduled on this scheduler.</item>
+ <item>
+ <p>Shows statistics about how many processes and ports
+ of different priorities are scheduled on this scheduler.</p>
+ </item>
<tag><em>** crashed **</em></tag>
- <item>This entry is normally not printed. It signifies that getting
- the rest of the information about this scheduler failed for some reason.
+ <item>
+ <p>This entry is normally not shown. It signifies that getting the rest
+ of the information about this scheduler failed for some reason.</p>
</item>
</taglist>
</section>
<section>
<marker id="memory"></marker>
- <title>Memory information</title>
- <p>Under the tag <em>=memory</em> you will find information similar
- to what you can obtain on a living node with
- <seealso marker="erts:erlang#erlang:memory/0">erlang:memory()</seealso>.</p>
+ <title>Memory Information</title>
+ <p>Under the tag <em>=memory</em> is shown information similar
+ to what can be obtainted on a living node with
+ <seealso marker="erts:erlang#erlang:memory/0">
+ <c>erlang:memory()</c></seealso>.</p>
</section>
<section>
<marker id="internal_tables"></marker>
- <title>Internal table information</title>
- <p>The tags <em>=hash_table:&lt;table_name&gt;</em> and
- <em>=index_table:&lt;table_name&gt;</em> presents internal
- tables. These are mostly of interest for runtime system
- developers.</p>
+ <title>Internal Table Information</title>
+ <p>Under the tags <em>=hash_table:&lt;table_name&gt;</em> and
+ <em>=index_table:&lt;table_name&gt;</em> is shown internal
+ tables. These are mostly of interest for runtime system developers.</p>
</section>
<section>
<marker id="allocated_areas"></marker>
- <title>Allocated areas</title>
- <p>Under the tag <em>=allocated_areas</em> you will find information
- similar to what you can obtain on a living node with
- <seealso marker="erts:erlang#system_info_allocated_areas">erlang:system_info(allocated_areas)</seealso>.</p>
+ <title>Allocated Areas</title>
+ <p>Under the tag <em>=allocated_areas</em> is shown information
+ similar to what can be obtained on a living node with
+ <seealso marker="erts:erlang#system_info_allocated_areas">
+ <c>erlang:system_info(allocated_areas)</c></seealso>.</p>
</section>
<section>
<marker id="allocator"></marker>
<title>Allocator</title>
- <p>Under the tag <em>=allocator:&lt;A&gt;</em> you will find
+ <p>Under the tag <em>=allocator:&lt;A&gt;</em> is shown
various information about allocator &lt;A&gt;. The information
- is similar to what you can obtain on a living node with
- <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, &lt;A&gt;})</seealso>.
- For more information see the documentation of
- <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, &lt;A&gt;})</seealso>,
- and the
- <seealso marker="erts_alloc">erts_alloc(3)</seealso>
- documentation.</p>
+ is similar to what can be obtained on a living node with
+ <seealso marker="erts:erlang#system_info_allocator_tuple">
+ <c>erlang:system_info({allocator, &lt;A&gt;})</c></seealso>.
+ For more information, see also
+ <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>.</p>
</section>
<section>
<marker id="processes"></marker>
- <title>Process information</title>
+ <title>Process Information</title>
<p>The Erlang crashdump contains a listing of each living Erlang
- process in the system. The process information for one process
- may look like this (line numbers have been added):
- </p>
- <p>The following fields can exist for a process:</p>
+ process in the system. The following fields can exist for a process:</p>
+
<taglist>
<tag><em>=proc:&lt;pid&gt;</em></tag>
- <item>Heading, states the process identifier</item>
+ <item>
+ <p>Heading. States the process identifier.</p>
+ </item>
<tag><em>State</em></tag>
<item>
<p>The state of the process. This can be one of the following:</p>
- <list type="bulleted">
- <item><em>Scheduled</em> - The process was scheduled to run
- but not currently running ("in the run queue").</item>
- <item><em>Waiting</em> - The process was waiting for
- something (in <c><![CDATA[receive]]></c>).</item>
- <item><em>Running</em> - The process was currently
- running. If the BIF <c><![CDATA[erlang:halt/1]]></c> was called, this was
- the process calling it.</item>
- <item><em>Exiting</em> - The process was on its way to
- exit.</item>
- <item><em>Garbing</em> - This is bad luck, the process was
- garbage collecting when the crash dump was written, the rest
- of the information for this process is limited.</item>
- <item><em>Suspended</em> - The process is suspended, either
- by the BIF <c><![CDATA[erlang:suspend_process/1]]></c> or because it is
- trying to write to a busy port.</item>
- </list>
+ <taglist>
+ <tag><em>Scheduled</em></tag>
+ <item>The process was scheduled to run
+ but is currently not running ("in the run queue").</item>
+ <tag><em>Waiting</em></tag>
+ <item>The process was waiting for
+ something (in <c><![CDATA[receive]]></c>).</item>
+ <tag><em>Running</em></tag>
+ <item>The process was currently running.
+ If the BIF <c><![CDATA[erlang:halt/1]]></c> was called, this was
+ the process calling it.</item>
+ <tag><em>Exiting</em></tag>
+ <item>The process was on its way to exit.</item>
+ <tag><em>Garbing</em></tag>
+ <item>This is bad luck, the process was
+ garbage collecting when the crash dump was written. The rest
+ of the information for this process is limited.</item>
+ <tag><em>Suspended</em></tag>
+ <item>The process is suspended, either
+ by the BIF <c><![CDATA[erlang:suspend_process/1]]></c> or because
+ it tries to write to a busy port.</item>
+ </taglist>
</item>
<tag><em>Registered name</em></tag>
- <item>The registered name of the process, if any.</item>
+ <item>
+ <p>The registered name of the process, if any.</p>
+ </item>
<tag><em>Spawned as</em></tag>
- <item>The entry point of the process, i.e., what function was
- referenced in the <c><![CDATA[spawn]]></c> or <c><![CDATA[spawn_link]]></c> call that
- started the process.</item>
+ <item>
+ <p>The entry point of the process, that is, what function was
+ referenced in the <c><![CDATA[spawn]]></c> or
+ <c><![CDATA[spawn_link]]></c> call that
+ started the process.</p>
+ </item>
<tag><em>Last scheduled in for | Current call</em></tag>
- <item>The current function of the process. These fields will not
- always exist.</item>
+ <item>
+ <p>The current function of the process. These fields do not
+ always exist.</p>
+ </item>
<tag><em>Spawned by</em></tag>
- <item>The parent of the process, i.e. the process which executed
- <c><![CDATA[spawn]]></c> or <c><![CDATA[spawn_link]]></c>.</item>
+ <item>
+ <p>The parent of the process, that is, the process that executed
+ <c><![CDATA[spawn]]></c> or <c><![CDATA[spawn_link]]></c>.</p>
+ </item>
<tag><em>Started</em></tag>
- <item>The date and time when the process was started.</item>
+ <item>
+ <p>The date and time when the process was started.</p>
+ </item>
<tag><em>Message queue length</em></tag>
- <item>The number of messages in the process' message queue.</item>
+ <item>
+ <p>The number of messages in the process' message queue.</p>
+ </item>
<tag><em>Number of heap fragments</em></tag>
- <item>The number of allocated heap fragments.</item>
+ <item>
+ <p>The number of allocated heap fragments.</p>
+ </item>
<tag><em>Heap fragment data</em></tag>
- <item>Size of fragmented heap data. This is data either created by
- messages being sent to the process or by the Erlang BIFs. This
- amount depends on so many things that this field is utterly
- uninteresting.</item>
+ <item>
+ <p>Size of fragmented heap data. This is data either created by
+ messages sent to the process or by the Erlang BIFs. This
+ amount depends on so many things that this field is utterly
+ uninteresting.</p>
+ </item>
<tag><em>Link list</em></tag>
- <item>Process id's of processes linked to this one. May also contain
- ports. If process monitoring is used, this field also tells in
- which direction the monitoring is in effect, i.e., a link
- being "to" a process tells you that the "current" process was
- monitoring the other and a link "from" a process tells you
- that the other process was monitoring the current one.</item>
+ <item>
+ <p>Process IDs of processes linked to this one. Can also contain
+ ports. If process monitoring is used, this field also tells in
+ which direction the monitoring is in effect. That is, a link
+ "to" a process tells you that the "current" process was
+ monitoring the other, and a link "from" a process tells you
+ that the other process was monitoring the current one.</p>
+ </item>
<tag><em>Reductions</em></tag>
- <item>The number of reductions consumed by the process.</item>
+ <item>
+ <p>The number of reductions consumed by the process.</p>
+ </item>
<tag><em>Stack+heap</em></tag>
- <item>The size of the stack and heap (they share memory segment)</item>
+ <item>
+ <p>The size of the stack and heap (they share memory segment).</p>
+ </item>
<tag><em>OldHeap</em></tag>
- <item>The size of the "old heap". The Erlang virtual machine uses
- generational garbage collection with two generations. There is
- one heap for new data items and one for the data that have
- survived two garbage collections. The assumption (which is
- almost always correct) is that data that survive two garbage
- collections can be "tenured" to a heap more seldom garbage
- collected, as they will live for a long period. This is a
- quite usual technique in virtual machines. The sum of the
- heaps and stack together constitute most of the process's
- allocated memory.</item>
+ <item>
+ <p>The size of the "old heap". The Erlang virtual machine uses
+ generational garbage collection with two generations. There is
+ one heap for new data items and one for the data that has
+ survived two garbage collections. The assumption (which is
+ almost always correct) is that data surviving two garbage
+ collections can be "tenured" to a heap more seldom garbage
+ collected, as they will live for a long period. This is a
+ usual technique in virtual machines. The sum of the
+ heaps and stack together constitute most of the
+ allocated memory of the process.</p>
+ </item>
<tag><em>Heap unused, OldHeap unused</em></tag>
- <item>The amount of unused memory on each heap. This information is
- usually useless.</item>
- <tag><em>Stack</em></tag>
- <item>If the system uses shared heap, the fields
- <em>Stack+heap</em>, <em>OldHeap</em>, <em>Heap unused</em>
- and <em>OldHeap unused</em> do not exist. Instead this field
- presents the size of the process' stack.</item>
+ <item>
+ <p>The amount of unused memory on each heap. This information is
+ usually useless.</p>
+ </item>
<tag><em>Memory</em></tag>
- <item>The total memory used by this process. This includes call stack,
- heap and internal structures. Same as <seealso marker="erlang#process_info-2">erlang:process_info(Pid,memory)</seealso>.
+ <item>
+ <p>The total memory used by this process. This includes call stack,
+ heap, and internal structures. Same as
+ <seealso marker="erlang#process_info-2">
+ <c>erlang:process_info(Pid,memory)</c></seealso>.</p>
</item>
<tag><em>Program counter</em></tag>
- <item>The current instruction pointer. This is only interesting for
- runtime system developers. The function into which the program
- counter points is the current function of the process.</item>
+ <item>
+ <p>The current instruction pointer. This is only of interest for
+ runtime system developers. The function into which the program
+ counter points is the current function of the process.</p>
+ </item>
<tag><em>CP</em></tag>
- <item>The continuation pointer, i.e. the return address for the
- current call. Usually useless for other than runtime system
- developers. This may be followed by the function into which
- the CP points, which is the function calling the current
- function.</item>
+ <item>
+ <p>The continuation pointer, that is, the return address for the
+ current call. Usually useless for other than runtime system
+ developers. This can be followed by the function into which
+ the CP points, which is the function calling the current
+ function.</p>
+ </item>
<tag><em>Arity</em></tag>
- <item>The number of live argument registers. The argument registers,
- if any are live, will follow. These may contain the arguments
- of the function if they are not yet moved to the stack.</item>
- <item><em>Internal State</em></item>
- <item>A more detailed internal represantation of the state of
- this process.</item>
+ <item>
+ <p>The number of live argument registers. The argument registers
+ if any are live will follow. These can contain the arguments
+ of the function if they are not yet moved to the stack.</p>
+ </item>
+ <tag><em>Internal State</em></tag>
+ <item>
+ <p>A more detailed internal representation of the state of
+ this process.</p>
+ </item>
</taglist>
- <p>See also the section about <seealso marker="#proc_data">process data</seealso>.</p>
+ <p>See also section <seealso marker="#proc_data">Process Data</seealso>.</p>
</section>
<section>
<marker id="ports"></marker>
- <title>Port information</title>
+ <title>Port Information</title>
<p>This section lists the open ports, their owners, any linked
- processed, and the name of their driver or external process.</p>
+ processes, and the name of their driver or external process.</p>
</section>
<section>
<marker id="ets_tables"></marker>
- <title>ETS tables</title>
+ <title>ETS Tables</title>
<p>This section contains information about all the ETS tables in
- the system. The following fields are interesting for each table:</p>
+ the system. The following fields are of interest for each table:</p>
+
<taglist>
<tag><em>=ets:&lt;owner&gt;</em></tag>
- <item>Heading, states the owner of the table (a process identifier)</item>
+ <item>
+ <p>Heading. States the table owner (a process identifier).</p>
+ </item>
<tag><em>Table</em></tag>
- <item>The identifier for the table. If the table is a
- <c><![CDATA[named_table]]></c>, this is the name.</item>
+ <item>
+ <p>The identifier for the table. If the table is a
+ <c><![CDATA[named_table]]></c>, this is the name.</p>
+ </item>
<tag><em>Name</em></tag>
- <item>The name of the table, regardless of whether it is a
- <c><![CDATA[named_table]]></c> or not.</item>
+ <item>
+ <p>The table name, regardless of if it is a
+ <c><![CDATA[named_table]]></c> or not.</p>
+ </item>
<tag><em>Hash table, Buckets</em></tag>
- <item>This occurs if the table is a hash table, i.e. if it is not an
- <c><![CDATA[ordered_set]]></c>.</item>
+ <item>
+ <p>If the table is a hash table, that is, if it is not an
+ <c><![CDATA[ordered_set]]></c>.</p>
+ </item>
<tag><em>Hash table, Chain Length</em></tag>
- <item>Only applicable for hash tables. Contains statistics about the
- hash table, such as the max, min and avg chain length. Having a max much
- larger than the avg, and a std dev much larger that
- the expected std dev is a sign that the hashing of the terms is
- behaving badly for some reason.</item>
+ <item>
+ <p>If the table is a hash table. Contains statistics about the
+ table, such as the maximum, minimum, and average chain length.
+ Having a maximum much larger than the average, and a standard
+ deviation much larger than the expected standard deviation is
+ a sign that the hashing of the terms
+ behaves badly for some reason.</p>
+ </item>
<tag><em>Ordered set (AVL tree), Elements</em></tag>
- <item>This occurs only if the table is an <c><![CDATA[ordered_set]]></c>. (The
- number of elements is the same as the number of objects in the
- table.)</item>
+ <item>
+ <p>If the table is an <c><![CDATA[ordered_set]]></c>. (The
+ number of elements is the same as the number of objects in the
+ table.)</p>
+ </item>
<tag><em>Fixed</em></tag>
- <item>If the table is fixed using ets:safe_fixtable or some internal
- mechanism.</item>
+ <item>
+ <p>If the table is fixed using
+ <seealso marker="stdlib:ets#safe_fixtable/2">
+ <c>ets:safe_fixtable/2</c></seealso> or some internal mechanism.</p>
+ </item>
<tag><em>Objects</em></tag>
- <item>The number of objects in the table</item>
+ <item>
+ <p>The number of objects in the table.</p>
+ </item>
<tag><em>Words</em></tag>
- <item>The number of words (usually 4 bytes/word) allocated to data
- in the table.</item>
+ <item>
+ <p>The number of words (usually 4 bytes/word) allocated to data
+ in the table.</p>
+ </item>
<tag><em>Type</em></tag>
- <item>The type of the table, i.e. <c>set</c>, <c>bag</c>,
- <c>dublicate_bag</c> or <c>ordered_set</c>.</item>
+ <item>
+ <p>The table type, that is, <c>set</c>, <c>bag</c>,
+ <c>dublicate_bag</c>, or <c>ordered_set</c>.</p>
+ </item>
<tag><em>Compressed</em></tag>
- <item>If this table was compressed.</item>
+ <item>
+ <p>If the table was compressed.</p>
+ </item>
<tag><em>Protection</em></tag>
- <item>The protection of this table.</item>
+ <item>
+ <p>The protection of the table.</p>
+ </item>
<tag><em>Write Concurrency</em></tag>
- <item>If write_concurrency was enabled for this table.</item>
+ <item>
+ <p>If <c>write_concurrency</c> was enabled for the table.</p>
+ </item>
<tag><em>Read Concurrency</em></tag>
- <item>If read_concurrency was enabled for this table.</item>
+ <item>
+ <p>If <c>read_concurrency</c> was enabled for the table.</p>
+ </item>
</taglist>
</section>
@@ -435,167 +563,252 @@
<title>Timers</title>
<p>This section contains information about all the timers started
with the BIFs <c><![CDATA[erlang:start_timer/3]]></c> and
- <c><![CDATA[erlang:send_after/3]]></c>. The following fields exists for each
- timer:</p>
+ <c><![CDATA[erlang:send_after/3]]></c>. The following fields exist
+ for each timer:</p>
+
<taglist>
<tag><em>=timer:&lt;owner&gt;</em></tag>
- <item>Heading, states the owner of the timer (a process identifier)
- i.e. the process to receive the message when the timer
- expires.</item>
+ <item>
+ <p>Heading. States the timer owner (a process identifier),
+ that is, the process to receive the message when the timer
+ expires.</p>
+ </item>
<tag><em>Message</em></tag>
- <item>The message to be sent.</item>
+ <item>
+ <p>The message to be sent.</p>
+ </item>
<tag><em>Time left</em></tag>
- <item>Number of milliseconds left until the message would have been
- sent.</item>
+ <item>
+ <p>Number of milliseconds left until the message would have been
+ sent.</p>
+ </item>
</taglist>
</section>
<section>
<marker id="distribution_info"></marker>
- <title>Distribution information</title>
- <p>If the Erlang node was alive, i.e., set up for communicating
+ <title>Distribution Information</title>
+ <p>If the Erlang node was alive, that is, set up for communicating
with other nodes, this section lists the connections that were
active. The following fields can exist:</p>
+
<taglist>
<tag><em>=node:&lt;node_name&gt;</em></tag>
- <item>The name of the node</item>
+ <item>
+ <p>The node name.</p>
+ </item>
<tag><em>no_distribution</em></tag>
- <item>This will only occur if the node was not distributed.</item>
+ <item>
+ <p>If the node was not distributed.</p>
+ </item>
<tag><em>=visible_node:&lt;channel&gt;</em></tag>
- <item>Heading for a visible nodes, i.e. an alive node with a
- connection to the node that crashed. States the channel number
- for the node.</item>
+ <item>
+ <p>Heading for a visible node, that is, an alive node with a
+ connection to the node that crashed. States the channel number
+ for the node.</p>
+ </item>
<tag><em>=hidden_node:&lt;channel&gt;</em></tag>
- <item>Heading for a hidden node. A hidden node is the same as a
- visible node, except that it is started with the "-hidden"
- flag. States the channel number for the node.</item>
+ <item>
+ <p>Heading for a hidden node. A hidden node is the same as a
+ visible node, except that it is started with the <c>"-hidden"</c>
+ flag. States the channel number for the node.</p>
+ </item>
<tag><em>=not_connected:&lt;channel&gt;</em></tag>
- <item>Heading for a node which is has been connected to the crashed
- node earlier. References (i.e. process or port identifiers)
- to the not connected node existed at the time of the crash.
- exist. States the channel number for the node.</item>
+ <item>
+ <p>Heading for a node that was connected to the crashed
+ node earlier. References (that is, process or port identifiers)
+ to the not connected node existed at the time of the crash.
+ States the channel number for the node.</p>
+ </item>
<tag><em>Name</em></tag>
- <item>The name of the remote node.</item>
+ <item>
+ <p>The name of the remote node.</p>
+ </item>
<tag><em>Controller</em></tag>
- <item>The port which controls the communication with the remote node.</item>
+ <item>
+ <p>The port controlling communication with the remote node.</p>
+ </item>
<tag><em>Creation</em></tag>
- <item>An integer (1-3) which together with the node name identifies
- a specific instance of the node.</item>
- <tag><em>Remote monitoring: &lt;local_proc&gt; &lt;remote_proc&gt;</em></tag>
- <item>The local process was monitoring the remote process at the
- time of the crash.</item>
- <tag><em>Remotely monitored by: &lt;local_proc&gt; &lt;remote_proc&gt;</em></tag>
- <item>The remote process was monitoring the local process at the
- time of the crash.</item>
+ <item>
+ <p>An integer (1-3) that together with the node name identifies
+ a specific instance of the node.</p>
+ </item>
+ <tag><em>Remote monitoring: &lt;local_proc&gt; &lt;remote_proc&gt;</em>
+ </tag>
+ <item>
+ <p>The local process was monitoring the remote process at the
+ time of the crash.</p>
+ </item>
+ <tag><em>Remotely monitored by: &lt;local_proc&gt;
+ &lt;remote_proc&gt;</em></tag>
+ <item>
+ <p>The remote process was monitoring the local process at the
+ time of the crash.</p>
+ </item>
<tag><em>Remote link: &lt;local_proc&gt; &lt;remote_proc&gt;</em></tag>
- <item>A link existed between the local process and the remote
- process at the time of the crash.</item>
+ <item>
+ <p>A link existed between the local process and the remote
+ process at the time of the crash.</p>
+ </item>
</taglist>
</section>
<section>
<marker id="loaded_modules"></marker>
- <title>Loaded module information</title>
- <p>This section contains information about all loaded modules.
- First, the memory usage by loaded code is summarized. There is
- one field for "Current code" which is code that is the current
- latest version of the modules. There is also a field for "Old
- code" which is code where there exists a newer version in the
- system, but the old version is not yet purged. The memory usage
- is in bytes.</p>
- <p>All loaded modules are then listed. The following fields exist:</p>
+ <title>Loaded Module Information</title>
+ <p>This section contains information about all loaded modules.</p>
+
+ <p>First, the memory use by the loaded code is summarized:</p>
+
+ <taglist>
+ <tag><em>Current code</em></tag>
+ <item>
+ <p>Code that is the current latest version of the modules.</p>
+ </item>
+ <tag><em>Old code</em></tag>
+ <item>
+ <p>Code where there exists a newer version in the
+ system, but the old version is not yet purged.</p>
+ </item>
+ </taglist>
+
+ <p>The memory use is in bytes.</p>
+
+ <p>Then, all loaded modules are listed. The following fields exist:</p>
+
<taglist>
<tag><em>=mod:&lt;module_name&gt;</em></tag>
- <item>Heading, and the name of the module.</item>
+ <item>
+ <p>Heading. States the module name.</p>
+ </item>
<tag><em>Current size</em></tag>
- <item>Memory usage for the loaded code in bytes</item>
+ <item>
+ <p>Memory use for the loaded code, in bytes.</p>
+ </item>
<tag><em>Old size</em></tag>
- <item>Memory usage for the old code, if any.</item>
+ <item>
+ <p>Memory use for the old code, if any.</p>
+ </item>
<tag><em>Current attributes</em></tag>
- <item>Module attributes for the current code. This field is decoded
- when looked at by the Crashdump Viewer tool.</item>
+ <item>
+ <p>Module attributes for the current code. This field is decoded
+ when looked at by the Crashdump Viewer tool.</p>
+ </item>
<tag><em>Old attributes</em></tag>
- <item>Module attributes for the old code, if any. This field is
- decoded when looked at by the Crashdump Viewer tool.</item>
+ <item>
+ <p>Module attributes for the old code, if any. This field is
+ decoded when looked at by the Crashdump Viewer tool.</p>
+ </item>
<tag><em>Current compilation info</em></tag>
- <item>Compilation information (options) for the current code. This
- field is decoded when looked at by the Crashdump Viewer tool.</item>
+ <item>
+ <p>Compilation information (options) for the current code. This
+ field is decoded when looked at by the Crashdump Viewer tool.</p>
+ </item>
<tag><em>Old compilation info</em></tag>
- <item>Compilation information (options) for the old code, if
- any. This field is decoded when looked at by the Crashdump
- Viewer tool.</item>
+ <item>
+ <p>Compilation information (options) for the old code, if
+ any. This field is decoded when looked at by the Crashdump
+ Viewer tool.</p>
+ </item>
</taglist>
</section>
<section>
<marker id="funs"></marker>
- <title>Fun information</title>
- <p>In this section, all funs are listed. The following fields exist
- for each fun:</p>
+ <title>Fun Information</title>
+ <p>This section lists all funs. The following fields exist for each fun:</p>
+
<taglist>
<tag><em>=fun</em></tag>
- <item>Heading</item>
+ <item>
+ <p>Heading.</p>
+ </item>
<tag><em>Module</em></tag>
- <item>The name of the module where the fun was defined.</item>
+ <item>
+ <p>The name of the module where the fun was defined.</p>
+ </item>
<tag><em>Uniq, Index</em></tag>
- <item>Identifiers</item>
+ <item>
+ <p>Identifiers.</p>
+ </item>
<tag><em>Address</em></tag>
- <item>The address of the fun's code.</item>
+ <item>
+ <p>The address of the fun's code.</p>
+ </item>
<tag><em>Native_address</em></tag>
- <item>The address of the fun's code when HiPE is enabled.</item>
+ <item>
+ <p>The address of the fun's code when HiPE is enabled.</p>
+ </item>
<tag><em>Refc</em></tag>
- <item>The number of references to the fun.</item>
+ <item>
+ <p>The number of references to the fun.</p>
+ </item>
</taglist>
</section>
<section>
<marker id="proc_data"></marker>
<title>Process Data</title>
- <p>For each process there will be at least one <em>=proc_stack</em>
- and one <em>=proc_heap</em> tag followed by the raw memory
+ <p>For each process there is at least one <em>=proc_stack</em>
+ and one <em>=proc_heap</em> tag, followed by the raw memory
information for the stack and heap of the process.</p>
- <p>For each process there will also be a <em>=proc_messages</em>
- tag if the process' message queue is non-empty and a
- <em>=proc_dictionary</em> tag if the process' dictionary (the
- <c><![CDATA[put/2]]></c> and <c><![CDATA[get/1]]></c> thing) is non-empty.</p>
+
+ <p>For each process there is also a <em>=proc_messages</em>
+ tag if the process message queue is non-empty, and a
+ <em>=proc_dictionary</em> tag if the process dictionary (the
+ <c><![CDATA[put/2]]></c> and <c><![CDATA[get/1]]></c> thing) is
+ non-empty.</p>
+
<p>The raw memory information can be decoded by the Crashdump
- Viewer tool. You will then be able to see the stack dump, the
- message queue (if any) and the dictionary (if any).</p>
+ Viewer tool. You can then see the stack dump, the
+ message queue (if any), and the dictionary (if any).</p>
+
<p>The stack dump is a dump of the Erlang process stack. Most of
- the live data (i.e., variables currently in use) are placed on
- the stack; thus this can be quite interesting. One has to
- "guess" what's what, but as the information is symbolic,
- thorough reading of this information can be very useful. As an
- example we can find the state variable of the Erlang primitive
- loader on line <c><![CDATA[(5)]]></c> in the example below:</p>
+ the live data (that is, variables currently in use) are placed on
+ the stack; thus this can be interesting. One has to
+ "guess" what is what, but as the information is symbolic,
+ thorough reading of this information can be useful. As an
+ example, we can find the state variable of the Erlang primitive
+ loader online <c><![CDATA[(5)]]></c> and <c><![CDATA[(6)]]></c>
+ in the following example:</p>
+
<code type="none"><![CDATA[
(1) 3cac44 Return addr 0x13BF58 (<terminate process normally>)
-(2) y(0) ["/view/siri_r10_dev/clearcase/otp/erts/lib/kernel/ebin","/view/siri_r10_dev/
-(3) clearcase/otp/erts/lib/stdlib/ebin"]
+(2) y(0) ["/view/siri_r10_dev/clearcase/otp/erts/lib/kernel/ebin",
+(3) "/view/siri_r10_dev/clearcase/otp/erts/lib/stdlib/ebin"]
(4) y(1) <0.1.0>
-(5) y(2) {state,[],none,#Fun<erl_prim_loader.6.7085890>,undefined,#Fun<erl_prim_loader.7.9000327>,#Fun<erl_prim_loader.8.116480692>,#Port<0.2>,infinity,#Fun<erl_prim_loader.9.10708760>}
-(6) y(3) infinity ]]></code>
+(5) y(2) {state,[],none,#Fun<erl_prim_loader.6.7085890>,undefined,#Fun<erl_prim_loader.7.9000327>,
+(6) #Fun<erl_prim_loader.8.116480692>,#Port<0.2>,infinity,#Fun<erl_prim_loader.9.10708760>}
+(7) y(3) infinity ]]></code>
+
<p>When interpreting the data for a process, it is helpful to know
- that anonymous function objects (funs) are given a name
- constructed from the name of the function in which they are
- created, and a number (starting with 0) indicating the number of
- that fun within that function.</p>
+ that anonymous function objects (funs) are given the following:</p>
+
+ <list type="bulleted">
+ <item>A name constructed from the name of the function in which they are
+ created
+ </item>
+ <item>A number (starting with 0) indicating the number of that fun within
+ that function
+ </item>
+ </list>
</section>
<section>
<marker id="atoms"></marker>
<title>Atoms</title>
- <p>Now all the atoms in the system are written. This is only
- interesting if one suspects that dynamic generation of atoms could
+ <p>This section presents all the atoms in the system. This is only
+ of interest if one suspects that dynamic generation of atoms can
be a problem, otherwise this section can be ignored.</p>
- <p>Note that the last created atom is printed first.</p>
+
+ <p>Notice that the last created atom is shown first.</p>
</section>
<section>
<title>Disclaimer</title>
- <p>The format of the crash dump evolves between releases of
- OTP. Some information here may not apply to your
- version. A description as this will never be complete; it is meant as
+ <p>The format of the crash dump evolves between OTP releases.
+ Some information described here may not apply to your
+ version. A description like this will never be complete; it is meant as
an explanation of the crash dump in general and as a help
when trying to find application errors, not as a complete
specification.</p>
diff --git a/erts/doc/src/driver.xml b/erts/doc/src/driver.xml
index 4bef5e1388..8f31df4cad 100644
--- a/erts/doc/src/driver.xml
+++ b/erts/doc/src/driver.xml
@@ -22,103 +22,115 @@
</legalnotice>
- <title>How to implement a driver</title>
+ <title>How to Implement a Driver</title>
<prepared>Jakob C</prepared>
<docno></docno>
<date>2000-11-28</date>
<rev>PA1</rev>
<file>driver.xml</file>
</header>
-
- <note><p>This document was written a long time ago. A lot of it is still
- interesting since it explains important concepts, but it was
- written for an older driver interface so the examples do not
- work anymore. The reader is encouraged to read
- <seealso marker="erl_driver">erl_driver</seealso> and the
- <seealso marker="driver_entry">driver_entry</seealso> documentation.
- </p></note>
+ <note>
+ <p>This section was written a long time ago. Most of it is still
+ valid, as it explains important concepts, but this was
+ written for an older driver interface so the examples do not
+ work anymore. The reader is encouraged to read the
+ <seealso marker="erl_driver"><c>erl_driver</c></seealso> and
+ <seealso marker="driver_entry"><c>driver_entry</c></seealso>
+ documentation also.</p>
+ </note>
<section>
<title>Introduction</title>
- <p>This chapter tells you how to build your own driver for erlang.</p>
- <p>A driver in Erlang is a library written in C, that is linked to
- the Erlang emulator and called from erlang. Drivers can be used
- when C is more suitable than Erlang, to speed things up, or to
- provide access to OS resources not directly accessible from
- Erlang.</p>
+ <p>This section describes how to build your own driver for Erlang.</p>
+
+ <p>A driver in Erlang is a library written in C, which is linked to
+ the Erlang emulator and called from Erlang. Drivers can be used
+ when C is more suitable than Erlang, to speed up things, or to
+ provide access to OS resources not directly accessible from Erlang.</p>
+
<p>A driver can be dynamically loaded, as a shared library (known as
- a DLL on windows), or statically loaded, linked with the emulator
+ a DLL on Windows), or statically loaded, linked with the emulator
when it is compiled and linked. Only dynamically loaded drivers
are described here, statically linked drivers are beyond the scope
- of this chapter.</p>
- <p>When a driver is loaded it is executed in the context of the
- emulator, shares the same memory and the same thread. This means
- that all operations in the driver must be non-blocking, and that
- any crash in the driver will bring the whole emulator down. In
- short: you have to be extremely careful!</p>
- <p></p>
+ of this section.</p>
+
+ <warning>
+ <p>When a driver is loaded it is executed in the context of the
+ emulator, shares the same memory and the same thread. This means
+ that all operations in the driver must be non-blocking, and that
+ any crash in the driver brings the whole emulator down. In short,
+ be careful.</p>
+ </warning>
</section>
<section>
- <title>Sample driver</title>
- <p>This is a simple driver for accessing a postgres
+ <title>Sample Driver</title>
+ <p>This section describes a simple driver for accessing a postgres
database using the libpq C client library. Postgres
- is used because it's free and open source. For information
- on postgres, refer to the website
- <url href="http://www.postgres.org">www.postgres.org</url>.</p>
+ is used because it is free and open source. For information on postgres,
+ see <url href="http://www.postgres.org">www.postgres.org</url>.</p>
+
<p>The driver is synchronous, it uses the synchronous calls of
- the client library. This is only for simplicity, and is
- generally not good, since it will
- halt the emulator while waiting for the database.
- This will be improved on below with an asynchronous
- sample driver.</p>
- <p>The code is quite straight-forward: all
+ the client library. This is only for simplicity, but not good, as it
+ halts the emulator while waiting for the database.
+ This is improved below with an asynchronous sample driver.</p>
+
+ <p>The code is straightforward: all
communication between Erlang and the driver
is done with <c><![CDATA[port_control/3]]></c>, and the
driver returns data back using the <c><![CDATA[rbuf]]></c>.</p>
+
<p>An Erlang driver only exports one function: the driver
entry function. This is defined with a macro,
- <c><![CDATA[DRIVER_INIT]]></c>, and returns a pointer to a
+ <c><![CDATA[DRIVER_INIT]]></c>, which returns a pointer to a
C <c><![CDATA[struct]]></c> containing the entry points that are
called from the emulator. The <c><![CDATA[struct]]></c> defines the
entries that the emulator calls to call the driver, with
a <c><![CDATA[NULL]]></c> pointer for entries that are not defined
and used by the driver.</p>
+
<p>The <c><![CDATA[start]]></c> entry is called when the driver
is opened as a port with <c><![CDATA[open_port/2]]></c>. Here
we allocate memory for a user data structure.
- This user data will be passed every time the emulator
- calls us. First we store the driver handle, because it
- is needed in subsequent calls. We allocate memory for
+ This user data is passed every time the emulator
+ calls us. First we store the driver handle, as it
+ is needed in later calls. We allocate memory for
the connection handle that is used by LibPQ. We also
set the port to return allocated driver binaries, by
- setting the flag <c><![CDATA[PORT_CONTROL_FLAG_BINARY]]></c>, calling
+ setting flag <c><![CDATA[PORT_CONTROL_FLAG_BINARY]]></c>, calling
<c><![CDATA[set_port_control_flags]]></c>. (This is because
- we don't know whether our data will fit in the
- result buffer of <c><![CDATA[control]]></c>, which has a default size
- set up by the emulator, currently 64 bytes.)</p>
- <p>There is an entry <c><![CDATA[init]]></c> which is called when
- the driver is loaded, but we don't use this, since
+ we do not know if our data will fit in the
+ result buffer of <c><![CDATA[control]]></c>, which has a default size,
+ 64 bytes, set up by the emulator.)</p>
+
+ <p>An entry <c><![CDATA[init]]></c> is called when
+ the driver is loaded. However, we do not use this, as
it is executed only once, and we want to have the
possibility of several instances of the driver.</p>
+
<p>The <c><![CDATA[stop]]></c> entry is called when the port
is closed.</p>
+
<p>The <c><![CDATA[control]]></c> entry is called from the emulator
when the Erlang code calls <c><![CDATA[port_control/3]]></c>,
to do the actual work. We have defined a simple set of
- commands: <c><![CDATA[connect]]></c> to login to the database, <c><![CDATA[disconnect]]></c>
- to log out and <c><![CDATA[select]]></c> to send a SQL-query and get the result.
+ commands: <c><![CDATA[connect]]></c> to log in to the database,
+ <c><![CDATA[disconnect]]></c> to log out, and <c><![CDATA[select]]></c>
+ to send a SQL-query and get the result.
All results are returned through <c><![CDATA[rbuf]]></c>.
- The library <c><![CDATA[ei]]></c> in <c><![CDATA[erl_interface]]></c> is used
- to encode data in binary term format. The result is returned
+ The library <c><![CDATA[ei]]></c> in <c><![CDATA[erl_interface]]></c> is
+ used to encode data in binary term format. The result is returned
to the emulator as binary terms, so <c><![CDATA[binary_to_term]]></c>
is called in Erlang to convert the result to term form.</p>
- <p>The code is available in <c><![CDATA[pg_sync.c]]></c> in the <c><![CDATA[sample]]></c>
- directory of <c><![CDATA[erts]]></c>.</p>
+
+ <p>The code is available in <c><![CDATA[pg_sync.c]]></c> in the
+ <c><![CDATA[sample]]></c> directory of <c><![CDATA[erts]]></c>.</p>
+
<p>The driver entry contains the functions that
- will be called by the emulator. In our simple
- example, we only provide <c><![CDATA[start]]></c>, <c><![CDATA[stop]]></c>
- and <c><![CDATA[control]]></c>.</p>
+ will be called by the emulator. In this example,
+ only <c><![CDATA[start]]></c>, <c><![CDATA[stop]]></c>,
+ and <c><![CDATA[control]]></c> are provided:</p>
+
<code type="none"><![CDATA[
/* Driver interface declarations */
static ErlDrvData start(ErlDrvPort port, char *command);
@@ -128,15 +140,15 @@ static int control(ErlDrvData drv_data, unsigned int command, char *buf,
static ErlDrvEntry pq_driver_entry = {
NULL, /* init */
- start,
- stop,
+ start,
+ stop,
NULL, /* output */
NULL, /* ready_input */
- NULL, /* ready_output */
+ NULL, /* ready_output */
"pg_sync", /* the name of the driver */
NULL, /* finish */
NULL, /* handle */
- control,
+ control,
NULL, /* timeout */
NULL, /* outputv */
NULL, /* ready_async */
@@ -145,14 +157,18 @@ static ErlDrvEntry pq_driver_entry = {
NULL /* event */
};
]]></code>
+
<p>We have a structure to store state needed by the driver,
- in this case we only need to keep the database connection.</p>
+ in this case we only need to keep the database connection:</p>
+
<code type="none"><![CDATA[
typedef struct our_data_s {
PGconn* conn;
} our_data_t;
]]></code>
- <p>These are control codes we have defined.</p>
+
+ <p>The control codes that we have defined are as follows:</p>
+
<code type="none"><![CDATA[
/* Keep the following definitions in alignment with the
* defines in erl_pq_sync.erl
@@ -162,10 +178,12 @@ typedef struct our_data_s {
#define DRV_DISCONNECT 'D'
#define DRV_SELECT 'S'
]]></code>
- <p>This just returns the driver structure. The macro
+
+ <p>This returns the driver structure. The macro
<c><![CDATA[DRIVER_INIT]]></c> defines the only exported function.
All the other functions are static, and will not be exported
from the library.</p>
+
<code type="none"><![CDATA[
/* INITIALIZATION AFTER LOADING */
@@ -180,9 +198,11 @@ DRIVER_INIT(pq_drv)
return &pq_driver_entry;
}
]]></code>
- <p>Here we do some initialization, <c><![CDATA[start]]></c> is called from
- <c><![CDATA[open_port]]></c>. The data will be passed to <c><![CDATA[control]]></c>
- and <c><![CDATA[stop]]></c>.</p>
+
+ <p>Here some initialization is done, <c><![CDATA[start]]></c> is called from
+ <c><![CDATA[open_port]]></c>. The data will be passed to
+ <c><![CDATA[control]]></c> and <c><![CDATA[stop]]></c>.</p>
+
<code type="none"><![CDATA[
/* DRIVER INTERFACE */
static ErlDrvData start(ErlDrvPort port, char *command)
@@ -195,8 +215,10 @@ static ErlDrvData start(ErlDrvPort port, char *command)
return (ErlDrvData)data;
}
]]></code>
+
<p>We call disconnect to log out from the database.
(This should have been done from Erlang, but just in case.)</p>
+
<code type="none"><![CDATA[
static int do_disconnect(our_data_t* data, ei_x_buff* x);
@@ -208,22 +230,27 @@ static void stop(ErlDrvData drv_data)
driver_free(data);
}
]]></code>
+
<p>We use the binary format only to return data to the emulator;
- input data is a string paramater for <c><![CDATA[connect]]></c> and
+ input data is a string parameter for <c><![CDATA[connect]]></c> and
<c><![CDATA[select]]></c>. The returned data consists of Erlang terms.</p>
- <p>The functions <c><![CDATA[get_s]]></c> and <c><![CDATA[ei_x_to_new_binary]]></c> are
- utilities that are used to make the code shorter. <c><![CDATA[get_s]]></c>
- duplicates the string and zero-terminates it, since the
+
+ <p>The functions <c><![CDATA[get_s]]></c> and
+ <c><![CDATA[ei_x_to_new_binary]]></c> are utilities that are used to
+ make the code shorter. <c><![CDATA[get_s]]></c>
+ duplicates the string and zero-terminates it, as the
postgres client library wants that. <c><![CDATA[ei_x_to_new_binary]]></c>
- takes an <c><![CDATA[ei_x_buff]]></c> buffer and allocates a binary and
- copies the data there. This binary is returned in <c><![CDATA[*rbuf]]></c>.
- (Note that this binary is freed by the emulator, not by us.)</p>
+ takes an <c><![CDATA[ei_x_buff]]></c> buffer, allocates a binary, and
+ copies the data there. This binary is returned in
+ <c><![CDATA[*rbuf]]></c>.
+ (Notice that this binary is freed by the emulator, not by us.)</p>
+
<code type="none"><![CDATA[
static char* get_s(const char* buf, int len);
static int do_connect(const char *s, our_data_t* data, ei_x_buff* x);
static int do_select(const char* s, our_data_t* data, ei_x_buff* x);
-/* Since we are operating in binary mode, the return value from control
+/* As we are operating in binary mode, the return value from control
* is irrelevant, as long as it is not negative.
*/
static int control(ErlDrvData drv_data, unsigned int command, char *buf,
@@ -246,10 +273,12 @@ static int control(ErlDrvData drv_data, unsigned int command, char *buf,
return r;
}
]]></code>
- <p><c><![CDATA[do_connect]]></c> is where we log in to the database. If the connection
- was successful we store the connection handle in our driver
- data, and return ok. Otherwise, we return the error message
- from postgres, and store <c><![CDATA[NULL]]></c> in the driver data.</p>
+
+ <p><c><![CDATA[do_connect]]></c> is where we log in to the database. If the
+ connection was successful, we store the connection handle in the driver
+ data, and return <c>'ok'</c>. Otherwise, we return the error message
+ from postgres and store <c><![CDATA[NULL]]></c> in the driver data.</p>
+
<code type="none"><![CDATA[
static int do_connect(const char *s, our_data_t* data, ei_x_buff* x)
{
@@ -265,10 +294,13 @@ static int do_connect(const char *s, our_data_t* data, ei_x_buff* x)
return 0;
}
]]></code>
- <p>If we are connected (if the connection handle is not <c><![CDATA[NULL]]></c>),
+
+ <p>If we are connected (and if the connection handle is not
+ <c><![CDATA[NULL]]></c>),
we log out from the database. We need to check if we should
- encode an ok, since we might get here from the <c><![CDATA[stop]]></c>
- function, which doesn't return data to the emulator.</p>
+ encode an <c>'ok'</c>, as we can get here from function
+ <c><![CDATA[stop]]></c>, which does not return data to the emulator:</p>
+
<code type="none"><![CDATA[
static int do_disconnect(our_data_t* data, ei_x_buff* x)
{
@@ -281,9 +313,11 @@ static int do_disconnect(our_data_t* data, ei_x_buff* x)
return 0;
}
]]></code>
- <p>We execute a query and encode the result. Encoding is done
- in another C module, <c><![CDATA[pg_encode.c]]></c> which is also provided
+
+ <p>We execute a query and encode the result. Encoding is done in
+ another C module, <c><![CDATA[pg_encode.c]]></c>, which is also provided
as sample code.</p>
+
<code type="none"><![CDATA[
static int do_select(const char* s, our_data_t* data, ei_x_buff* x)
{
@@ -293,12 +327,14 @@ static int do_select(const char* s, our_data_t* data, ei_x_buff* x)
return 0;
}
]]></code>
- <p>Here we simply check the result from postgres, and
- if it's data we encode it as lists of lists with
+
+ <p>Here we check the result from postgres.
+ If it is data, we encode it as lists of lists with
column data. Everything from postgres is C strings,
- so we just use <c><![CDATA[ei_x_encode_string]]></c> to send
+ so we use <c><![CDATA[ei_x_encode_string]]></c> to send
the result as strings to Erlang. (The head of the list
contains the column names.)</p>
+
<code type="none"><![CDATA[
void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn)
{
@@ -338,33 +374,36 @@ void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn)
</section>
<section>
- <title>Compiling and linking the sample driver</title>
- <p>The driver should be compiled and linked to a shared
- library (DLL on windows). With gcc this is done
- with the link flags <c><![CDATA[-shared]]></c> and <c><![CDATA[-fpic]]></c>.
- Since we use the <c><![CDATA[ei]]></c> library we should include
+ <title>Compiling and Linking the Sample Driver</title>
+ <p>The driver is to be compiled and linked to a shared
+ library (DLL on Windows). With gcc, this is done with
+ link flags <c><![CDATA[-shared]]></c> and <c><![CDATA[-fpic]]></c>.
+ As we use the <c><![CDATA[ei]]></c> library, we should include
it too. There are several versions of <c><![CDATA[ei]]></c>, compiled
for debug or non-debug and multi-threaded or single-threaded.
- In the makefile for the samples the <c><![CDATA[obj]]></c> directory
+ In the makefile for the samples, the <c><![CDATA[obj]]></c> directory
is used for the <c><![CDATA[ei]]></c> library, meaning that we use
the non-debug, single-threaded version.</p>
</section>
<section>
- <title>Calling a driver as a port in Erlang</title>
+ <title>Calling a Driver as a Port in Erlang</title>
<p>Before a driver can be called from Erlang, it must be
loaded and opened. Loading is done using the <c><![CDATA[erl_ddll]]></c>
module (the <c><![CDATA[erl_ddll]]></c> driver that loads dynamic
- driver, is actually a driver itself). If loading is ok
+ driver is actually a driver itself). If loading is successfull,
the port can be opened with <c><![CDATA[open_port/2]]></c>. The port
name must match the name of the shared library and
the name in the driver entry structure.</p>
+
<p>When the port has been opened, the driver can be called. In
- the <c><![CDATA[pg_sync]]></c> example, we don't have any data from
+ the <c><![CDATA[pg_sync]]></c> example, we do not have any data from
the port, only the return value from the
<c><![CDATA[port_control]]></c>.</p>
+
<p>The following code is the Erlang part of the synchronous
- postgres driver, <c><![CDATA[pg_sync.erl]]></c>.</p>
+ postgres driver, <c><![CDATA[pg_sync.erl]]></c>:</p>
+
<code type="none"><![CDATA[
-module(pg_sync).
@@ -394,20 +433,35 @@ disconnect(Port) ->
select(Port, Query) ->
binary_to_term(port_control(Port, ?DRV_SELECT, Query)).
]]></code>
- <p>The API is simple: <c><![CDATA[connect/1]]></c> loads the driver, opens it
- and logs on to the database, returning the Erlang port
- if successful, <c><![CDATA[select/2]]></c> sends a query to the driver,
- and returns the result, <c><![CDATA[disconnect/1]]></c> closes the
- database connection and the driver. (It does not unload it,
- however.) The connection string should be a connection
- string for postgres.</p>
- <p>The driver is loaded with <c><![CDATA[erl_ddll:load_driver/2]]></c>,
- and if this is successful, or if it's already loaded,
+
+ <p>The API is simple:</p>
+
+ <list type="bulleted">
+ <item>
+ <p><c><![CDATA[connect/1]]></c> loads the driver, opens it,
+ and logs on to the database, returning the Erlang port
+ if successful.</p>
+ </item>
+ <item>
+ <p><c><![CDATA[select/2]]></c> sends a query to the driver
+ and returns the result.</p>
+ </item>
+ <item>
+ <p><c><![CDATA[disconnect/1]]></c> closes the database
+ connection and the driver. (However, it does not unload it.)</p>
+ </item>
+ </list>
+
+ <p>The connection string is to be a connection string for postgres.</p>
+
+ <p>The driver is loaded with <c><![CDATA[erl_ddll:load_driver/2]]></c>.
+ If this is successful, or if it is already loaded,
it is opened. This will call the <c><![CDATA[start]]></c> function
in the driver.</p>
+
<p>We use the <c><![CDATA[port_control/3]]></c> function for all
- calls into the driver, the result from the driver is
- returned immediately, and converted to terms by calling
+ calls into the driver. The result from the driver is
+ returned immediately and converted to terms by calling
<c><![CDATA[binary_to_term/1]]></c>. (We trust that the terms returned
from the driver are well-formed, otherwise the
<c><![CDATA[binary_to_term]]></c> calls could be contained in a
@@ -415,16 +469,18 @@ select(Port, Query) ->
</section>
<section>
- <title>Sample asynchronous driver</title>
- <p>Sometimes database queries can take long time to
+ <title>Sample Asynchronous Driver</title>
+ <p>Sometimes database queries can take a long time to
complete, in our <c><![CDATA[pg_sync]]></c> driver, the emulator
halts while the driver is doing its job. This is
- often not acceptable, since no other Erlang process
+ often not acceptable, as no other Erlang process
gets a chance to do anything. To improve on our
- postgres driver, we reimplement it using the asynchronous
+ postgres driver, we re-implement it using the asynchronous
calls in LibPQ.</p>
- <p>The asynchronous version of the driver is in the
- sample files <c><![CDATA[pg_async.c]]></c> and <c><![CDATA[pg_asyng.erl]]></c>.</p>
+
+ <p>The asynchronous version of the driver is in the sample files
+ <c><![CDATA[pg_async.c]]></c> and <c><![CDATA[pg_asyng.erl]]></c>.</p>
+
<code type="none"><![CDATA[
/* Driver interface declarations */
static ErlDrvData start(ErlDrvPort port, char *command);
@@ -459,22 +515,26 @@ typedef struct our_data_t {
int connecting;
} our_data_t;
]]></code>
- <p>Here some things have changed from <c><![CDATA[pg_sync.c]]></c>: we use the
- entry <c><![CDATA[ready_io]]></c> for <c><![CDATA[ready_input]]></c> and
- <c><![CDATA[ready_output]]></c> which will be called from the emulator only
- when there is input to be read from the socket. (Actually, the
+
+ <p>Some things have changed from <c><![CDATA[pg_sync.c]]></c>: we use
+ the entry <c><![CDATA[ready_io]]></c> for <c><![CDATA[ready_input]]></c>
+ and <c><![CDATA[ready_output]]></c>, which is called from the emulator
+ only when there is input to be read from the socket. (Actually, the
socket is used in a <c><![CDATA[select]]></c> function inside
- the emulator, and when the socket is signalled,
- indicating there is data to read, the <c><![CDATA[ready_input]]></c> entry
- is called. More on this below.)</p>
+ the emulator, and when the socket is signaled,
+ indicating there is data to read, the <c><![CDATA[ready_input]]></c>
+ entry is called. More about this below.)</p>
+
<p>Our driver data is also extended, we keep track of the
socket used for communication with postgres, and also
the port, which is needed when we send data to the port with
- <c><![CDATA[driver_output]]></c>. We have a flag <c><![CDATA[connecting]]></c> to tell
+ <c><![CDATA[driver_output]]></c>. We have a flag
+ <c><![CDATA[connecting]]></c> to tell
whether the driver is waiting for a connection or waiting
- for the result of a query. (This is needed since the entry
- <c><![CDATA[ready_io]]></c> will be called both when connecting and
+ for the result of a query. (This is needed, as the entry
+ <c><![CDATA[ready_io]]></c> is called both when connecting and
when there is a query result.)</p>
+
<code type="none"><![CDATA[
static int do_connect(const char *s, our_data_t* data)
{
@@ -498,16 +558,20 @@ static int do_connect(const char *s, our_data_t* data)
return 0;
}
]]></code>
- <p>The <c><![CDATA[connect]]></c> function looks a bit different too. We connect
- using the asynchronous <c><![CDATA[PQconnectStart]]></c> function. After the
- connection is started, we retrieve the socket for the connection
+
+ <p>The <c><![CDATA[connect]]></c> function looks a bit different too. We
+ connect using the asynchronous <c><![CDATA[PQconnectStart]]></c> function.
+ After the connection is started, we retrieve the socket for the connection
with <c><![CDATA[PQsocket]]></c>. This socket is used with the
<c><![CDATA[driver_select]]></c> function to wait for connection. When
- the socket is ready for input or for output, the <c><![CDATA[ready_io]]></c>
- function will be called.</p>
- <p>Note that we only return data (with <c><![CDATA[driver_output]]></c>) if there
+ the socket is ready for input or for output, the
+ <c><![CDATA[ready_io]]></c> function is called.</p>
+
+ <p>Notice that we only return data (with <c><![CDATA[driver_output]]></c>)
+ if there
is an error here, otherwise we wait for the connection to be completed,
- in which case our <c><![CDATA[ready_io]]></c> function will be called.</p>
+ in which case our <c><![CDATA[ready_io]]></c> function is called.</p>
+
<code type="none"><![CDATA[
static int do_select(const char* s, our_data_t* data)
{
@@ -525,9 +589,11 @@ static int do_select(const char* s, our_data_t* data)
return 0;
}
]]></code>
+
<p>The <c><![CDATA[do_select]]></c> function initiates a select, and returns
- if there is no immediate error. The actual result will be returned
+ if there is no immediate error. The result is returned
when <c><![CDATA[ready_io]]></c> is called.</p>
+
<code type="none"><![CDATA[
static void ready_io(ErlDrvData drv_data, ErlDrvEvent event)
{
@@ -566,26 +632,31 @@ static void ready_io(ErlDrvData drv_data, ErlDrvEvent event)
ei_x_free(&x);
}
]]></code>
- <p>The <c><![CDATA[ready_io]]></c> function will be called when the socket
+
+ <p>The <c><![CDATA[ready_io]]></c> function is called when the socket
we got from postgres is ready for input or output. Here
we first check if we are connecting to the database. In that
- case we check connection status and return ok if the
- connection is successful, or error if it's not. If the
- connection is not yet established, we simply return; <c><![CDATA[ready_io]]></c>
- will be called again.</p>
+ case, we check connection status and return OK if the
+ connection is successful, or error if it is not. If the
+ connection is not yet established, we simply return;
+ <c><![CDATA[ready_io]]></c> is called again.</p>
+
<p>If we have a result from a connect, indicated by having data in
the <c><![CDATA[x]]></c> buffer, we no longer need to select on
output (<c><![CDATA[ready_output]]></c>), so we remove this by calling
<c><![CDATA[driver_select]]></c>.</p>
- <p>If we're not connecting, we're waiting for results from a
+
+ <p>If we are not connecting, we wait for results from a
<c><![CDATA[PQsendQuery]]></c>, so we get the result and return it. The
encoding is done with the same functions as in the earlier
example.</p>
- <p>We should add error handling here, for instance checking
- that the socket is still open, but this is just a simple
- example.</p>
+
+ <p>Error handling is to be added here, for example, checking
+ that the socket is still open, but this is only a simple example.</p>
+
<p>The Erlang part of the asynchronous driver consists of the
sample file <c><![CDATA[pg_async.erl]]></c>.</p>
+
<code type="none"><![CDATA[
-module(pg_async).
@@ -626,45 +697,50 @@ return_port_data(Port) ->
binary_to_term(Data)
end.
]]></code>
- <p>The Erlang code is slightly different, this is because we
- don't return the result synchronously from <c><![CDATA[port_control]]></c>,
+
+ <p>The Erlang code is slightly different, as we do not
+ return the result synchronously from <c><![CDATA[port_control]]></c>,
instead we get it from <c><![CDATA[driver_output]]></c> as data in the
message queue. The function <c><![CDATA[return_port_data]]></c> above
- receives data from the port. Since the data is in
+ receives data from the port. As the data is in
binary format, we use <c><![CDATA[binary_to_term/1]]></c> to convert
- it to an Erlang term. Note that the driver is opened in
- binary mode (<c><![CDATA[open_port/2]]></c> is called with the option
+ it to an Erlang term. Notice that the driver is opened in
+ binary mode (<c><![CDATA[open_port/2]]></c> is called with option
<c><![CDATA[[binary]]]></c>). This means that data sent from the driver
- to the emulator is sent as binaries. Without the <c><![CDATA[binary]]></c>
- option, they would have been lists of integers.</p>
+ to the emulator is sent as binaries. Without option
+ <c><![CDATA[binary]]></c>, they would have been lists of integers.</p>
</section>
<section>
- <title>An asynchronous driver using driver_async</title>
- <p>As a final example we demonstrate the use of <c><![CDATA[driver_async]]></c>.
+ <title>An Asynchronous Driver Using driver_async</title>
+ <p>As a final example we demonstrate the use of
+ <c><![CDATA[driver_async]]></c>.
We also use the driver term interface. The driver is written
- in C++. This enables us to use an algorithm from STL. We will
- use the <c><![CDATA[next_permutation]]></c> algorithm to get the next permutation
- of a list of integers. For large lists (more than 100000
- elements), this will take some time, so we will perform this
+ in C++. This enables us to use an algorithm from STL. We use
+ the <c><![CDATA[next_permutation]]></c> algorithm to get the next
+ permutation of a list of integers. For large lists (&gt; 100,000
+ elements), this takes some time, so we perform this
as an asynchronous task.</p>
- <p>The asynchronous API for drivers is quite complicated. First
- of all, the work must be prepared. In our example we do this
- in <c><![CDATA[output]]></c>. We could have used <c><![CDATA[control]]></c> just as well,
- but we want some variation in our examples. In our driver, we allocate
- a structure that contains anything that's needed for the asynchronous task
- to do the work. This is done in the main emulator thread.
+
+ <p>The asynchronous API for drivers is complicated. First,
+ the work must be prepared. In the example, this is done in
+ <c><![CDATA[output]]></c>. We could have used <c><![CDATA[control]]></c>,
+ but we want some variation in the examples. In our driver, we allocate
+ a structure that contains anything that is needed for the asynchronous
+ task to do the work. This is done in the main emulator thread.
Then the asynchronous function is called from a driver thread,
- separate from the main emulator thread. Note that the driver-functions
- are not reentrant, so they shouldn't be used.
+ separate from the main emulator thread. Notice that the driver functions
+ are not re-entrant, so they are not to be used.
Finally, after the function is completed, the driver callback
<c><![CDATA[ready_async]]></c> is called from the main emulator thread,
- this is where we return the result to Erlang. (We can't
- return the result from within the asynchronous function, since
- we can't call the driver-functions.)</p>
- <p>The code below is from the sample file <c><![CDATA[next_perm.cc]]></c>.</p>
- <p>The driver entry looks like before, but also contains the
- call-back <c><![CDATA[ready_async]]></c>.</p>
+ this is where we return the result to Erlang. (We cannot
+ return the result from within the asynchronous function, as
+ we cannot call the driver functions.)</p>
+
+ <p>The following code is from the sample file
+ <c><![CDATA[next_perm.cc]]></c>. The driver entry looks like before,
+ but also contains the callback <c><![CDATA[ready_async]]></c>.</p>
+
<code type="none"><![CDATA[
static ErlDrvEntry next_perm_driver_entry = {
NULL, /* init */
@@ -685,17 +761,21 @@ static ErlDrvEntry next_perm_driver_entry = {
NULL /* event */
};
]]></code>
- <p>The <c><![CDATA[output]]></c> function allocates the work-area of the
- asynchronous function. Since we use C++, we use a struct,
- and stuff the data in it. We have to copy the original data,
+
+ <p>The <c><![CDATA[output]]></c> function allocates the work area of the
+ asynchronous function. As we use C++, we use a struct,
+ and stuff the data in it. We must copy the original data,
it is not valid after we have returned from the <c><![CDATA[output]]></c>
- function, and the <c><![CDATA[do_perm]]></c> function will be called later,
- and from another thread. We return no data here, instead it will
- be sent later from the <c><![CDATA[ready_async]]></c> call-back.</p>
- <p>The <c><![CDATA[async_data]]></c> will be passed to the <c><![CDATA[do_perm]]></c> function.
- We do not use a <c><![CDATA[async_free]]></c> function (the last argument to
- <c><![CDATA[driver_async]]></c>), it's only used if the task is cancelled
+ function, and the <c><![CDATA[do_perm]]></c> function is called
+ later, and from another thread. We return no data here, instead it
+ is sent later from the <c><![CDATA[ready_async]]></c> callback.</p>
+
+ <p>The <c><![CDATA[async_data]]></c> is passed to the
+ <c><![CDATA[do_perm]]></c> function. We do not use a
+ <c><![CDATA[async_free]]></c> function (the last argument to
+ <c><![CDATA[driver_async]]></c>), it is only used if the task is cancelled
programmatically.</p>
+
<code type="none"><![CDATA[
struct our_async_data {
bool prev;
@@ -720,8 +800,10 @@ static void output(ErlDrvData drv_data, char *buf, int len)
driver_async(port, NULL, do_perm, async_data, do_free);
}
]]></code>
- <p>In the <c><![CDATA[do_perm]]></c> we simply do the work, operating
+
+ <p>In the <c><![CDATA[do_perm]]></c> we do the work, operating
on the structure that was allocated in <c><![CDATA[output]]></c>.</p>
+
<code type="none"><![CDATA[
static void do_perm(void* async_data)
{
@@ -732,13 +814,17 @@ static void do_perm(void* async_data)
next_permutation(d->data.begin(), d->data.end());
}
]]></code>
- <p>In the <c><![CDATA[ready_async]]></c> function, the output is sent back to the
+
+ <p>In the <c><![CDATA[ready_async]]></c> function the output is sent back
+ to the
emulator. We use the driver term format instead of <c><![CDATA[ei]]></c>.
- This is the only way to send Erlang terms directly to a driver,
- without having the Erlang code to call <c><![CDATA[binary_to_term/1]]></c>. In
- our simple example this works well, and we don't need to use
+ This is the only way to send Erlang terms directly to a driver, without
+ having the Erlang code to call <c><![CDATA[binary_to_term/1]]></c>. In
+ the simple example this works well, and we do not need to use
<c><![CDATA[ei]]></c> to handle the binary term format.</p>
- <p>When the data is returned we deallocate our data.</p>
+
+ <p>When the data is returned, we deallocate our data.</p>
+
<code type="none"><![CDATA[
static void ready_async(ErlDrvData drv_data, ErlDrvThreadData async_data)
{
@@ -759,12 +845,15 @@ static void ready_async(ErlDrvData drv_data, ErlDrvThreadData async_data)
delete d;
}
]]></code>
- <p>This driver is called like the others from Erlang, however, since
+
+ <p>This driver is called like the others from Erlang. However, as
we use <c><![CDATA[driver_output_term]]></c>, there is no need to call
- binary_to_term. The Erlang code is in the sample file
+ <c>binary_to_term</c>. The Erlang code is in the sample file
<c><![CDATA[next_perm.erl]]></c>.</p>
+
<p>The input is changed into a list of integers and sent to
the driver.</p>
+
<code type="none"><![CDATA[
-module(next_perm).
diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml
index ae7f264d0c..2421e0a8d9 100644
--- a/erts/doc/src/driver_entry.xml
+++ b/erts/doc/src/driver_entry.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2015</year>
+ <year>2001</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,55 +33,64 @@
<file>driver_entry.xml</file>
</header>
<lib>driver_entry</lib>
- <libsummary>The driver-entry structure used by erlang drivers.</libsummary>
+ <libsummary>The driver-entry structure used by Erlang drivers.</libsummary>
<description>
<marker id="WARNING"/>
- <warning><p><em>Use this functionality with extreme care!</em></p>
+ <warning>
+ <p><em>Use this functionality with extreme care.</em></p>
<p>A driver callback is executed as a direct extension of the
- native code of the VM. Execution is not made in a safe environment.
- The VM can <em>not</em> provide the same services as provided when
- executing Erlang code, such as preemptive scheduling or memory
- protection. If the driver callback function doesn't behave well,
- the whole VM will misbehave.</p>
- <list>
- <item><p>A driver callback that crash will crash the whole VM.</p></item>
- <item><p>An erroneously implemented driver callback might cause
- a VM internal state inconsistency which may cause a crash of the VM,
- or miscellaneous misbehaviors of the VM at any point after the call
- to the driver callback.</p></item>
- <item><p>A driver callback that do
- <seealso marker="erl_driver#lengthy_work">lengthy work</seealso>
- before returning will degrade responsiveness of the VM,
- and may cause miscellaneous strange behaviors. Such strange behaviors
- include, but are not limited to, extreme memory usage, and bad load
- balancing between schedulers. Strange behaviors that might occur due
- to lengthy work may also vary between OTP releases.</p></item>
+ native code of the VM. Execution is not made in a safe environment.
+ The VM <em>cannot</em> provide the same services as provided when
+ executing Erlang code, such as pre-emptive scheduling or memory
+ protection. If the driver callback function does not behave well,
+ the whole VM will misbehave.</p>
+ <list type="bulleted">
+ <item>
+ <p>A driver callback that crash will crash the whole VM.</p>
+ </item>
+ <item>
+ <p>An erroneously implemented driver callback can cause a VM
+ internal state inconsistency, which can cause a crash of the VM,
+ or miscellaneous misbehaviors of the VM at any point after the
+ call to the driver callback.</p>
+ </item>
+ <item>
+ <p>A driver callback doing
+ <seealso marker="erl_driver#lengthy_work">lengthy work</seealso>
+ before returning degrades responsiveness of the VM, and can cause
+ miscellaneous strange behaviors. Such strange behaviors
+ include, but are not limited to, extreme memory usage, and bad
+ load balancing between schedulers. Strange behaviors that can
+ occur because of lengthy work can also vary between Erlang/OTP
+ releases.</p>
+ </item>
</list>
- </warning>
- <p>
- As of erts version 5.9 (OTP release R15B) the driver interface
+ </warning>
+
+ <p>As from ERTS 5.9 (Erlang/OTP R15B) the driver interface
has been changed with larger types for the callbacks
- <seealso marker="#output">output</seealso>,
- <seealso marker="#control">control</seealso> and
- <seealso marker="#call">call</seealso>.
+ <seealso marker="#output"><c>output</c></seealso>,
+ <seealso marker="#control"><c>control</c></seealso>, and
+ <seealso marker="#call"><c>call</c></seealso>.
See driver <seealso marker="erl_driver#version_management">
version management</seealso> in
- <seealso marker="erl_driver">erl_driver</seealso>.
- </p>
+ <seealso marker="erl_driver"><c>erl_driver</c></seealso>.</p>
+
<note>
<p>Old drivers (compiled with an <c>erl_driver.h</c> from an
- earlier erts version than 5.9) have to be updated and have
- to use the extended interface (with
- <seealso marker="erl_driver#version_management">version management
- </seealso>).</p>
+ ERTS version earlier than 5.9) must be updated and have
+ to use the extended interface (with
+ <seealso marker="erl_driver#version_management">version management
+ </seealso>).</p>
</note>
- <p>The <c>driver_entry</c> structure is a C struct that all erlang
- drivers define. It contains entry points for the erlang driver
- that are called by the erlang emulator when erlang code accesses
+
+ <p>The <c>driver_entry</c> structure is a C struct that all Erlang
+ drivers define. It contains entry points for the Erlang driver,
+ which are called by the Erlang emulator when Erlang code accesses
the driver.</p>
- <p>
- <marker id="emulator"></marker>
- The <seealso marker="erl_driver">erl_driver</seealso> driver
+
+ <p><marker id="emulator"></marker>
+ The <seealso marker="erl_driver"><c>erl_driver</c></seealso> driver
API functions need a port handle
that identifies the driver instance (and the port in the
emulator). This is only passed to the <c>start</c> function, but
@@ -90,416 +99,466 @@
common practice is to have the <c>start</c> function allocate
some application-defined structure and stash the <c>port</c>
handle in it, to use it later with the driver API functions.</p>
- <p>The driver call-back functions are called synchronously from the
- erlang emulator. If they take too long before completing, they
- can cause timeouts in the emulator. Use the queue or
- asynchronous calls if necessary, since the emulator must be
+
+ <p>The driver callback functions are called synchronously from the
+ Erlang emulator. If they take too long before completing, they
+ can cause time-outs in the emulator. Use the queue or
+ asynchronous calls if necessary, as the emulator must be
responsive.</p>
- <p>The driver structure contains the name of the driver and some
- 15 function pointers. These pointers are called at different
+
+ <p>The driver structure contains the driver name and some
+ 15 function pointers, which are called at different
times by the emulator.</p>
+
<p>The only exported function from the driver is
<c>driver_init</c>. This function returns the <c>driver_entry</c>
structure that points to the other functions in the driver. The
- <c>driver_init</c> function is declared with a macro
- <c>DRIVER_INIT(drivername)</c>. (This is because different OS's
- have different names for it.)</p>
- <p>When writing a driver in C++, the driver entry should be of
- <c>"C"</c> linkage. One way to do this is to put this line
- somewhere before the driver entry:
- <c>extern "C" DRIVER_INIT(drivername);</c>.</p>
+ <c>driver_init</c> function is declared with a macro,
+ <c>DRIVER_INIT(drivername)</c>. (This is because different
+ operating systems have different names for it.)</p>
+
+ <p>When writing a driver in C++, the driver entry is to be of
+ <c>"C"</c> linkage. One way to do this is to put the
+ following line somewhere before the driver entry:</p>
+
+ <pre>
+extern "C" DRIVER_INIT(drivername);</pre>
+
<p>When the driver has passed the <c>driver_entry</c> over to
the emulator, the driver is <em>not</em> allowed to modify the
<c>driver_entry</c>.</p>
- <p>If compiling a driver for static inclusion via --enable-static-drivers you
- have to define STATIC_ERLANG_DRIVER before the DRIVER_INIT declaration.</p>
+
+ <p>If compiling a driver for static inclusion through
+ <c>--enable-static-drivers</c>, you must define
+ <c>STATIC_ERLANG_DRIVER</c> before the <c>DRIVER_INIT</c> declaration.</p>
+
<note>
- <p>Do <em>not</em> declare the <c>driver_entry</c> <c>const</c>. This since the emulator needs to
- modify the <c>handle</c>, and the <c>handle2</c>
- fields. A statically allocated, and <c>const</c>
- declared <c>driver_entry</c> may be located in
- read only memory which will cause the emulator
- to crash.</p>
+ <p>Do <em>not</em> declare the <c>driver_entry</c> <c>const</c>.
+ This because the emulator must
+ modify the <c>handle</c> and the <c>handle2</c>
+ fields. A statically allocated, and <c>const</c>-declared
+ <c>driver_entry</c> can be located in
+ read-only memory, which causes the emulator to crash.</p>
</note>
</description>
<section>
- <title>DATA TYPES</title>
- <taglist>
- <tag><em>ErlDrvEntry</em></tag>
- <item>
- <p/>
+ <title>Data Types</title>
+ <p><c>ErlDrvEntry</c></p>
<code type="none">
typedef struct erl_drv_entry {
- int (*init)(void); /* called at system start up for statically
+ int (*init)(void); /* Called at system startup for statically
linked drivers, and after loading for
- dynamically loaded drivers */
-
+ dynamically loaded drivers */
#ifndef ERL_SYS_DRV
ErlDrvData (*start)(ErlDrvPort port, char *command);
- /* called when open_port/2 is invoked.
- return value -1 means failure. */
+ /* Called when open_port/2 is invoked,
+ return value -1 means failure */
#else
ErlDrvData (*start)(ErlDrvPort port, char *command, SysDriverOpts* opts);
- /* special options, only for system driver */
+ /* Special options, only for system driver */
#endif
void (*stop)(ErlDrvData drv_data);
- /* called when port is closed, and when the
- emulator is halted. */
+ /* Called when port is closed, and when the
+ emulator is halted */
void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len);
- /* called when we have output from erlang to
+ /* Called when we have output from Erlang to
the port */
void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event);
- /* called when we have input from one of
+ /* Called when we have input from one of
the driver's handles */
void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event);
- /* called when output is possible to one of
+ /* Called when output is possible to one of
the driver's handles */
- char *driver_name; /* name supplied as command
- in open_port XXX ? */
- void (*finish)(void); /* called before unloading the driver -
- DYNAMIC DRIVERS ONLY */
- void *handle; /* Reserved -- Used by emulator internally */
+ char *driver_name; /* Name supplied as command in
+ erlang:open_port/2 */
+ void (*finish)(void); /* Called before unloading the driver -
+ dynamic drivers only */
+ void *handle; /* Reserved, used by emulator internally */
ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command,
char *buf, ErlDrvSizeT len,
char **rbuf, ErlDrvSizeT rlen);
- /* "ioctl" for drivers - invoked by
+ /* "ioctl" for drivers - invoked by
port_control/3 */
- void (*timeout)(ErlDrvData drv_data); /* Handling of timeout in driver */
+ void (*timeout)(ErlDrvData drv_data);
+ /* Handling of time-out in driver */
void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev);
- /* called when we have output from erlang
+ /* Called when we have output from Erlang
to the port */
void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data);
void (*flush)(ErlDrvData drv_data);
- /* called when the port is about to be
- closed, and there is data in the
- driver queue that needs to be flushed
+ /* Called when the port is about to be
+ closed, and there is data in the
+ driver queue that must be flushed
before 'stop' can be called */
ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command,
char *buf, ErlDrvSizeT len,
char **rbuf, ErlDrvSizeT rlen, unsigned int *flags);
/* Works mostly like 'control', a synchronous
- call into the driver. */
+ call into the driver */
void (*event)(ErlDrvData drv_data, ErlDrvEvent event,
ErlDrvEventData event_data);
- /* Called when an event selected by
+ /* Called when an event selected by
driver_event() has occurred */
int extended_marker; /* ERL_DRV_EXTENDED_MARKER */
int major_version; /* ERL_DRV_EXTENDED_MAJOR_VERSION */
int minor_version; /* ERL_DRV_EXTENDED_MINOR_VERSION */
int driver_flags; /* ERL_DRV_FLAGs */
- void *handle2; /* Reserved -- Used by emulator internally */
+ void *handle2; /* Reserved, used by emulator internally */
void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor);
/* Called when a process monitor fires */
void (*stop_select)(ErlDrvEvent event, void* reserved);
/* Called to close an event object */
- } ErlDrvEntry;
- </code>
- <p/>
+ } ErlDrvEntry;</code>
<taglist>
- <tag><marker id="init"/>int (*init)(void)</tag>
- <item>
- <p>This is called directly after the driver has been loaded by
- <c>erl_ddll:load_driver/2</c>. (Actually when the driver is
- added to the driver list.) The driver should return 0, or if
- the driver can't initialize, -1.</p>
+ <tag><marker id="init"/><c>int (*init)(void)</c></tag>
+ <item>
+ <p>Called directly after the driver has been loaded by
+ <seealso marker="kernel:erl_ddll#load_driver/2">
+ <c>erl_ddll:load_driver/2</c></seealso> (actually when the driver is
+ added to the driver list). The driver is to return <c>0</c>, or, if
+ the driver cannot initialize, <c>-1</c>.</p>
</item>
- <tag><marker id="start"/>ErlDrvData (*start)(ErlDrvPort port, char* command)</tag>
+ <tag><marker id="start"/>
+ <c>ErlDrvData (*start)(ErlDrvPort port, char* command)</c></tag>
<item>
- <p>This is called when the driver is instantiated, when
- <c>open_port/2</c> is called. The driver should return a
- number &gt;= 0 or a pointer, or if the driver can't be started,
- one of three error codes should be returned:</p>
- <p>ERL_DRV_ERROR_GENERAL - general error, no error code</p>
- <p>ERL_DRV_ERROR_ERRNO - error with error code in <c>errno</c></p>
- <p>ERL_DRV_ERROR_BADARG - error, badarg</p>
- <p>If an error code is returned, the port isn't started.</p>
+ <p>Called when the driver is instantiated, when
+ <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso> is called.
+ The driver is to return a number &gt;= 0 or a pointer, or, if the
+ driver cannot be started, one of three error codes:</p>
+ <taglist>
+ <tag><c>ERL_DRV_ERROR_GENERAL</c></tag>
+ <item>General error, no error code</item>
+ <tag><c>ERL_DRV_ERROR_ERRNO</c></tag>
+ <item>Error with error code in <c>errno</c></item>
+ <tag><c>ERL_DRV_ERROR_BADARG</c></tag>
+ <item>Error, <c>badarg</c></item>
+ </taglist>
+ <p>If an error code is returned, the port is not started.</p>
</item>
- <tag><marker id="stop"/>void (*stop)(ErlDrvData drv_data)</tag>
+ <tag><marker id="stop"/><c>void (*stop)(ErlDrvData drv_data)</c></tag>
<item>
- <p>This is called when the port is closed, with
- <c>port_close/1</c> or <c>Port ! {self(), close}</c>. Note
- that terminating the port owner process also closes the
+ <p>Called when the port is closed, with
+ <seealso marker="erlang#port_close/1">
+ <c>erlang:port_close/1</c></seealso> or <c>Port ! {self(), close}</c>.
+ Notice that terminating the port owner process also closes the
port. If <c>drv_data</c> is a pointer to memory allocated in
- <c>start</c>, then <c>stop</c> is the place to deallocate that
- memory.</p>
+ <c>start</c>, then <c>stop</c> is the place to deallocate that
+ memory.</p>
</item>
- <tag><marker id="output"/>void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)</tag>
+ <tag><marker id="output"/>
+ <c>void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)</c>
+ </tag>
<item>
- <p>This is called when an erlang process has sent data to the
- port. The data is pointed to by <c>buf</c>, and is
- <c>len</c> bytes. Data is sent to the port with <c>Port ! {self(), {command, Data}}</c>, or with
- <c>port_command/2</c>. Depending on how the port was opened,
- it should be either a list of integers 0...255 or a
- binary. See <c>open_port/3</c> and <c>port_command/2</c>.</p>
+ <p>Called when an Erlang process has sent data to the port. The data is
+ pointed to by <c>buf</c>, and is <c>len</c> bytes. Data is sent to
+ the port with <c>Port ! {self(), {command, Data}}</c> or with
+ <c>erlang:port_command/2</c>. Depending on how the port was
+ opened, it is to be either a list of integers <c>0...255</c> or a
+ binary. See <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso> and
+ <seealso marker="erlang#port_command/2">
+ <c>erlang:port_command/2</c></seealso>.</p>
</item>
-
- <tag><marker id="ready_input"/>void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event)</tag>
- <item/>
- <tag><marker id="ready_output"/>void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event)</tag>
+ <tag><marker id="ready_input"/>
+ <c>void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event)</c>
+ </tag>
+ <item></item>
+ <tag><marker id="ready_output"/>
+ <c>void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event)</c>
+ </tag>
<item>
- <p>This is called when a driver event (given in the
- <c>event</c> parameter) is signaled. This is used to help
- asynchronous drivers "wake up" when something happens.</p>
- <p>On unix the <c>event</c> is a pipe or socket handle (or
+ <p>Called when a driver event (specified in parameter
+ <c>event</c>) is signaled. This is used to help
+ asynchronous drivers "wake up" when something occurs.</p>
+ <p>On Unix the <c>event</c> is a pipe or socket handle (or
something that the <c>select</c> system call understands).</p>
- <p>On Windows the <c>event</c> is an Event or Semaphore (or
- something that the <c>WaitForMultipleObjects</c> API
+ <p>On Windows the <c>event</c> is an <c>Event</c> or <c>Semaphore</c>
+ (or something that the <c>WaitForMultipleObjects</c> API
function understands). (Some trickery in the emulator allows
more than the built-in limit of 64 <c>Events</c> to be used.)</p>
<p>To use this with threads and asynchronous routines, create a
- pipe on unix and an Event on Windows. When the routine
+ pipe on Unix and an <c>Event</c> on Windows. When the routine
completes, write to the pipe (use <c>SetEvent</c> on
- Windows), this will make the emulator call
+ Windows), this makes the emulator call
<c>ready_input</c> or <c>ready_output</c>.</p>
- <p>Spurious events may happen. That is, calls to <c>ready_input</c>
- or <c>ready_output</c> even though no real events are signaled. In
- reality it should be rare (and OS dependant), but a robust driver
+ <p>False events can occur. That is, calls to <c>ready_input</c>
+ or <c>ready_output</c> although no real events are signaled. In
+ reality, it is rare (and OS-dependant), but a robust driver
must nevertheless be able to handle such cases.</p>
</item>
- <tag><marker id="driver_name"/>char *driver_name</tag>
+ <tag><marker id="driver_name"/><c>char *driver_name</c></tag>
<item>
- <p>This is the name of the driver, it must correspond to the
- atom used in <c>open_port</c>, and the name of the driver
+ <p>The driver name. It must correspond to the atom used in
+ <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso>, and the name of the driver
library file (without the extension).</p>
</item>
- <tag><marker id="finish"/>void (*finish)(void)</tag>
+ <tag><marker id="finish"/><c>void (*finish)(void)</c></tag>
<item>
- <p>This function is called by the <c>erl_ddll</c> driver when the
+ <p>Called by the <c>erl_ddll</c> driver when the
driver is unloaded. (It is only called in dynamic drivers.)</p>
<p>The driver is only unloaded as a result of calling
- <c>unload_driver/1</c>, or when the emulator halts.</p>
+ <seealso marker="kernel:erl_ddll#unload_driver/1">
+ <c>erl_ddll:unload_driver/1</c></seealso>,
+ or when the emulator halts.</p>
</item>
- <tag>void *handle</tag>
+ <tag><c>void *handle</c></tag>
<item>
<p>This field is reserved for the emulator's internal use. The
- emulator will modify this field; therefore, it is important
- that the <c>driver_entry</c> isn't declared <c>const</c>.</p>
+ emulator will modify this field, so it is important
+ that the <c>driver_entry</c> is not declared <c>const</c>.</p>
</item>
- <tag><marker id="control"></marker>ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)</tag>
+ <tag><marker id="control"></marker>
+ <c>ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command,
+ char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)</c></tag>
<item>
- <p>This is a special routine invoked with the erlang function
- <c>port_control/3</c>. It works a little like an "ioctl" for
- erlang drivers. The data given to <c>port_control/3</c>
- arrives in <c>buf</c> and <c>len</c>. The driver may send
+ <p>A special routine invoked with
+ <seealso marker="erlang#port_control/3">
+ <c>erlang:port_control/3</c></seealso>.
+ It works a little like an "ioctl" for
+ Erlang drivers. The data specified to <c>port_control/3</c>
+ arrives in <c>buf</c> and <c>len</c>. The driver can send
data back, using <c>*rbuf</c> and <c>rlen</c>.</p>
<p>This is the fastest way of calling a driver and get a
- response. It won't make any context switch in the erlang
- emulator, and requires no message passing. It is suitable
- for calling C function to get faster execution, when erlang
+ response. It makes no context switch in the Erlang
+ emulator and requires no message passing. It is suitable
+ for calling C function to get faster execution, when Erlang
is too slow.</p>
- <p>If the driver wants to return data, it should return it in
+ <p>If the driver wants to return data, it is to return it in
<c>rbuf</c>. When <c>control</c> is called,
<c>*rbuf</c> points to a default buffer of <c>rlen</c> bytes, which
- can be used to return data. Data is returned different depending on
+ can be used to return data. Data is returned differently depending on
the port control flags (those that are set with
- <seealso marker="erl_driver#set_port_control_flags">set_port_control_flags</seealso>).
- </p>
+ <seealso marker="erl_driver#set_port_control_flags">
+ <c>erl_driver:set_port_control_flags</c></seealso>).</p>
<p>If the flag is set to <c>PORT_CONTROL_FLAG_BINARY</c>,
- a binary will be returned. Small binaries can be returned by writing
- the raw data into the default buffer. A binary can also be
- returned by setting <c>*rbuf</c> to point to a binary allocated with
- <seealso marker="erl_driver#driver_alloc_binary">driver_alloc_binary</seealso>.
- This binary will be freed automatically after <c>control</c> has returned.
+ a binary is returned. Small binaries can be returned by writing
+ the raw data into the default buffer. A binary can also be
+ returned by setting <c>*rbuf</c> to point to a binary allocated with
+ <seealso marker="erl_driver#driver_alloc_binary">
+ <c>erl_driver:driver_alloc_binary</c></seealso>.
+ This binary is freed automatically after <c>control</c> has returned.
The driver can retain the binary for <em>read only</em> access with
- <seealso marker="erl_driver#driver_binary_inc_refc">driver_binary_inc_refc</seealso> to be freed later with
- <seealso marker="erl_driver#driver_free_binary">driver_free_binary</seealso>.
- It is never allowed to alter the binary after <c>control</c> has returned.
- If <c>*rbuf</c> is set to NULL, an empty list will be returned.
- </p>
+ <seealso marker="erl_driver#driver_binary_inc_refc">
+ <c>erl_driver:driver_binary_inc_refc</c></seealso> to be freed later
+ with <seealso marker="erl_driver#driver_free_binary">
+ <c>erl_driver:driver_free_binary</c></seealso>.
+ It is never allowed to change the binary after <c>control</c> has
+ returned. If <c>*rbuf</c> is set to <c>NULL</c>, an empty list is
+ returned.</p>
<p>If the flag is set to <c>0</c>, data is returned as a
list of integers. Either use the default buffer or set
<c>*rbuf</c> to point to a larger buffer allocated with
- <seealso marker="erl_driver#driver_alloc">driver_alloc</seealso>.
- The buffer will be freed automatically after <c>control</c> has returned.</p>
+ <seealso marker="erl_driver#driver_alloc">
+ <c>erl_driver:driver_alloc</c></seealso>. The
+ buffer is freed automatically after <c>control</c> has returned.</p>
<p>Using binaries is faster if more than a few bytes are returned.</p>
- <p>The return value is the number of bytes returned in
- <c>*rbuf</c>.</p>
+ <p>The return value is the number of bytes returned in <c>*rbuf</c>.</p>
</item>
-
- <tag><marker id="timeout"/>void (*timeout)(ErlDrvData drv_data)</tag>
+ <tag><marker id="timeout"/><c>void (*timeout)(ErlDrvData drv_data)</c>
+ </tag>
<item>
- <p>This function is called any time after the driver's timer
- reaches 0. The timer is activated with
- <c>driver_set_timer</c>. There are no priorities or ordering
- among drivers, so if several drivers time out at the same
- time, any one of them is called first.</p>
+ <p>Called any time after the driver's timer reaches <c>0</c>.
+ The timer is activated with
+ <seealso marker="erl_driver#driver_set_timer">
+ <c>erl_driver:driver_set_timer</c></seealso>. No priorities or
+ ordering exist among drivers, so if several drivers time out at
+ the same time, anyone of them is called first.</p>
</item>
-
- <tag><marker id="outputv"/>void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev)</tag>
+ <tag><marker id="outputv"/>
+ <c>void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev)</c></tag>
<item>
- <p>This function is called whenever the port is written to. If
+ <p>Called whenever the port is written to. If
it is <c>NULL</c>, the <c>output</c> function is called
- instead. This function is faster than <c>output</c>, because
+ instead. This function is faster than <c>output</c>, as
it takes an <c>ErlIOVec</c> directly, which requires no
- copying of the data. The port should be in binary mode, see
- <c>open_port/2</c>.</p>
- <p>The <c>ErlIOVec</c> contains both a <c>SysIOVec</c>,
+ copying of the data. The port is to be in binary mode, see
+ <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso>.</p>
+ <p><c>ErlIOVec</c> contains both a <c>SysIOVec</c>,
suitable for <c>writev</c>, and one or more binaries. If
- these binaries should be retained, when the driver returns
- from <c>outputv</c>, they can be queued (using <seealso marker="erl_driver#driver_enq_bin">driver_enq_bin</seealso>
- for instance), or if they are kept in a static or global
+ these binaries are to be retained when the driver returns
+ from <c>outputv</c>, they can be queued (using, for example,
+ <seealso marker="erl_driver#driver_enq_bin">
+ <c>erl_driver:driver_enq_bin</c></seealso>)
+ or, if they are kept in a static or global
variable, the reference counter can be incremented.</p>
</item>
- <tag><marker id="ready_async"/>void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data)</tag>
+ <tag><marker id="ready_async"/>
+ <c>void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData
+ thread_data)</c></tag>
<item>
- <p>This function is called after an asynchronous call has
- completed. The asynchronous call is started with <seealso marker="erl_driver#driver_async">driver_async</seealso>.
- This function is called from the erlang emulator thread, as
+ <p>Called after an asynchronous call has completed.
+ The asynchronous call is started with
+ <seealso marker="erl_driver#driver_async">
+ <c>erl_driver:driver_async</c></seealso>.
+ This function is called from the Erlang emulator thread, as
opposed to the asynchronous function, which is called in
- some thread (if multithreading is enabled).</p>
+ some thread (if multi-threading is enabled).</p>
+ </item>
+ <tag><c>void (*flush)(ErlDrvData drv_data)</c></tag>
+ <item>
+ <p>Called when the port is about to be closed,
+ and there is data in the driver queue that must be flushed
+ before 'stop' can be called.</p>
</item>
- <tag><marker id="call"/>ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen, unsigned int *flags)</tag>
+ <tag><marker id="call"/><c>ErlDrvSSizeT (*call)(ErlDrvData drv_data,
+ unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf,
+ ErlDrvSizeT rlen, unsigned int *flags)</c></tag>
<item>
- <p>This function is called from <c>erlang:port_call/3</c>. It
- works a lot like the <c>control</c> call-back, but uses the
+ <p>Called from <seealso marker="erlang#port_call/3">
+ <c>erlang:port_call/3</c></seealso>.
+ It works a lot like the <c>control</c> callback, but uses the
external term format for input and output.</p>
<p><c>command</c> is an integer, obtained from the call from
- erlang (the second argument to <c>erlang:port_call/3</c>).</p>
+ Erlang (the second argument to <c>erlang:port_call/3</c>).</p>
<p><c>buf</c> and <c>len</c> provide the arguments to the call
(the third argument to <c>erlang:port_call/3</c>). They can
be decoded using <c>ei</c> functions.</p>
<p><c>rbuf</c> points to a return buffer, <c>rlen</c> bytes
- long. The return data should be a valid erlang term in the
- external (binary) format. This is converted to an erlang
+ long. The return data is to be a valid Erlang term in the
+ external (binary) format. This is converted to an Erlang
term and returned by <c>erlang:port_call/3</c> to the
- caller. If more space than <c>rlen</c> bytes is needed to
+ caller. If more space than <c>rlen</c> bytes is needed to
return data, <c>*rbuf</c> can be set to memory allocated with
- <c>driver_alloc</c>. This memory will be freed automatically
- after <c>call</c> has returned.</p>
+ <seealso marker="erl_driver#driver_alloc">
+ <c>erl_driver:driver_alloc</c></seealso>.
+ This memory is freed automatically after <c>call</c> has returned.</p>
<p>The return value is the number of bytes returned in
<c>*rbuf</c>. If <c>ERL_DRV_ERROR_GENERAL</c> is returned
- (or in fact, anything &lt; 0), <c>erlang:port_call/3</c> will
- throw a <c>BAD_ARG</c>.</p>
+ (or in fact, anything &lt; 0), <c>erlang:port_call/3</c>
+ throws a <c>BAD_ARG</c>.</p>
</item>
- <tag>void (*event)(ErlDrvData drv_data, ErlDrvEvent event, ErlDrvEventData event_data)</tag>
+ <tag><c>void (*event)(ErlDrvData drv_data, ErlDrvEvent event,
+ ErlDrvEventData event_data)</c></tag>
<item>
<p>Intentionally left undocumented.</p>
</item>
- <tag><marker id="extended_marker"/>int extended_marker</tag>
+ <tag><marker id="extended_marker"/><c>int extended_marker</c></tag>
<item>
- <p>
- This field should either be equal to <c>ERL_DRV_EXTENDED_MARKER</c>
+ <p>This field is either to be equal to <c>ERL_DRV_EXTENDED_MARKER</c>
or <c>0</c>. An old driver (not aware of the extended driver
- interface) should set this field to <c>0</c>. If this field is
- equal to <c>0</c>, all the fields following this field also
- <em>have</em> to be <c>0</c>, or <c>NULL</c> in case it is a
- pointer field.
- </p>
+ interface) is to set this field to <c>0</c>. If this field is
+ <c>0</c>, all the following fields <em>must</em> also be <c>0</c>,
+ or <c>NULL</c> if it is a pointer field.</p>
</item>
- <tag>int major_version</tag>
+ <tag><c>int major_version</c></tag>
<item>
- <p>This field should equal <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> if
- the <c>extended_marker</c> field equals
+ <p>This field is to equal <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> if
+ field <c>extended_marker</c> equals
<c>ERL_DRV_EXTENDED_MARKER</c>.</p>
</item>
- <tag>int minor_version</tag>
+ <tag><c>int minor_version</c></tag>
<item>
- <p>
- This field should equal <c>ERL_DRV_EXTENDED_MINOR_VERSION</c> if
- the <c>extended_marker</c> field equals
- <c>ERL_DRV_EXTENDED_MARKER</c>.
- </p>
+ <p>This field is to equal <c>ERL_DRV_EXTENDED_MINOR_VERSION</c> if
+ field <c>extended_marker</c> equals
+ <c>ERL_DRV_EXTENDED_MARKER</c>.</p>
</item>
-
- <tag><marker id="driver_flags"/>int driver_flags</tag>
+ <tag><marker id="driver_flags"/><c>int driver_flags</c></tag>
<item>
<p>This field is used to pass driver capability and other
- information to the runtime system. If the
- <c>extended_marker</c> field equals <c>ERL_DRV_EXTENDED_MARKER</c>,
- it should contain <c>0</c> or driver flags (<c>ERL_DRV_FLAG_*</c>)
- ored bitwise. Currently the following driver flags exist:
- </p>
+ information to the runtime system. If
+ field <c>extended_marker</c> equals <c>ERL_DRV_EXTENDED_MARKER</c>,
+ it is to contain <c>0</c> or driver flags (<c>ERL_DRV_FLAG_*</c>)
+ OR'ed bitwise. The following driver flags exist:</p>
<taglist>
<tag><c>ERL_DRV_FLAG_USE_PORT_LOCKING</c></tag>
<item>
- The runtime system will use port level locking on
- all ports executing this driver instead of driver
- level locking when the driver is run in a runtime
- system with SMP support. For more information see the
- <seealso marker="erl_driver#smp_support">erl_driver</seealso>
- documentation.
- </item>
+ <p>The runtime system uses port-level locking on
+ all ports executing this driver instead of driver-level
+ locking when the driver is run in a runtime
+ system with SMP support. For more information, see
+ <seealso marker="erl_driver#smp_support">
+ <c>erl_driver</c></seealso>.</p>
+ </item>
<tag><c>ERL_DRV_FLAG_SOFT_BUSY</c></tag>
<item>
- Marks that driver instances can handle being called
- in the <seealso marker="#output">output</seealso> and/or
- <seealso marker="#outputv">outputv</seealso> callbacks even
- though a driver instance has marked itself as busy (see
- <seealso marker="erl_driver#set_busy_port">set_busy_port()</seealso>).
- Since erts version 5.7.4 this flag is required for drivers used
- by the Erlang distribution (the behaviour has always been
- required by drivers used by the distribution).
+ <p>Marks that driver instances can handle being called
+ in the <seealso marker="#output"><c>output</c></seealso> and/or
+ <seealso marker="#outputv"><c>outputv</c></seealso> callbacks
+ although a driver instance has marked itself as busy (see
+ <seealso marker="erl_driver#set_busy_port">
+ <c>erl_driver:set_busy_port</c></seealso>).
+ As from ERTS 5.7.4 this flag is required for drivers used
+ by the Erlang distribution (the behavior has always been
+ required by drivers used by the distribution).</p>
</item>
<tag><c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c></tag>
- <item>Disable busy port message queue functionality. For
- more information, see the documentation of the
- <seealso marker="erl_driver#erl_drv_busy_msgq_limits">erl_drv_busy_msgq_limits()</seealso>
- function.
+ <item>
+ <p>Disables busy port message queue functionality. For
+ more information, see
+ <seealso marker="erl_driver#erl_drv_busy_msgq_limits">
+ <c>erl_driver:erl_drv_busy_msgq_limits</c></seealso>.</p>
</item>
<tag><c>ERL_DRV_FLAG_USE_INIT_ACK</c></tag>
- <item>When this flag is given the linked-in driver has to manually
- acknowledge that the port has been successfully started using
- <seealso marker="erl_driver#erl_drv_init_ack">erl_drv_init_ack()</seealso>.
- This allows the implementor to make the erlang:open_port exit with
- badarg after some initial asynchronous initialization has been done.
+ <item>
+ <p>When this flag is specified, the linked-in driver must manually
+ acknowledge that the port has been successfully started using
+ <seealso marker="erl_driver#erl_drv_init_ack">
+ <c>erl_driver:erl_drv_init_ack()</c></seealso>.
+ This allows the implementor to make the
+ <c>erlang:open_port</c> exit with <c>badarg</c> after some
+ initial asynchronous initialization has been done.</p>
</item>
</taglist>
</item>
- <tag>void *handle2</tag>
+ <tag><c>void *handle2</c></tag>
<item>
- <p>
- This field is reserved for the emulator's internal use. The
- emulator will modify this field; therefore, it is important
- that the <c>driver_entry</c> isn't declared <c>const</c>.
- </p>
+ <p>This field is reserved for the emulator's internal use. The
+ emulator modifies this field, so it is important
+ that the <c>driver_entry</c> is not declared <c>const</c>.</p>
</item>
- <tag><marker id="process_exit"/>void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor)</tag>
+ <tag><marker id="process_exit"/>
+ <c>void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor)</c>
+ </tag>
<item>
- <p>This callback is called when a monitored process exits. The
+ <p>Called when a monitored process exits. The
<c>drv_data</c> is the data associated with the port for which
- the process is monitored (using <seealso marker="erl_driver#driver_monitor_process">driver_monitor_process</seealso>)
- and the <c>monitor</c> corresponds to the <c>ErlDrvMonitor</c>
+ the process is monitored (using
+ <seealso marker="erl_driver#driver_monitor_process">
+ <c>erl_driver:driver_monitor_process</c></seealso>)
+ and the <c>monitor</c> corresponds to the <c>ErlDrvMonitor</c>
structure filled
in when creating the monitor. The driver interface function
- <seealso marker="erl_driver#driver_get_monitored_process">driver_get_monitored_process</seealso>
- can be used to retrieve the process id of the exiting process as
+ <seealso marker="erl_driver#driver_get_monitored_process">
+ <c>erl_driver:driver_get_monitored_process</c></seealso>
+ can be used to retrieve the process ID of the exiting process as
an <c>ErlDrvTermData</c>.</p>
</item>
- <tag><marker id="stop_select"/>void (*stop_select)(ErlDrvEvent event, void* reserved)</tag>
+ <tag><marker id="stop_select"/>
+ <c>void (*stop_select)(ErlDrvEvent event, void* reserved)</c></tag>
<item>
- <p>This function is called on behalf of
- <seealso marker="erl_driver#driver_select">driver_select</seealso>
- when it is safe to close an event object.</p>
+ <p>Called on behalf of
+ <seealso marker="erl_driver#driver_select">
+ <c>erl_driver:driver_select</c></seealso>
+ when it is safe to close an event object.</p>
<p>A typical implementation on Unix is to do
- <c>close((int)event)</c>.</p>
- <p>Argument <c>reserved</c> is intended for future use and should be ignored.</p>
- <p>In contrast to most of the other call-back functions,
- <c>stop_select</c> is called independent of any port. No
- <c>ErlDrvData</c> argument is passed to the function. No
- driver lock or port lock is guaranteed to be held. The port that
- called <c>driver_select</c> might even be closed at the
- time <c>stop_select</c> is called. But it could also be
- the case that <c>stop_select</c> is called directly by
- <c>driver_select</c>.</p>
+ <c>close((int)event)</c>.</p>
+ <p>Argument <c>reserved</c> is intended for future use and is to be
+ ignored.</p>
+ <p>In contrast to most of the other callback functions,
+ <c>stop_select</c> is called independent of any port. No
+ <c>ErlDrvData</c> argument is passed to the function. No
+ driver lock or port lock is guaranteed to be held. The port that
+ called <c>driver_select</c> can even be closed at the
+ time <c>stop_select</c> is called. But it can also be
+ the case that <c>stop_select</c> is called directly by
+ <c>erl_driver:driver_select</c>.</p>
<p>It is not allowed to call any functions in the
- <seealso marker="erl_driver">driver API</seealso> from
- <c>stop_select</c>. This strict limitation is due to the
- volatile context that <c>stop_select</c> may be called.</p>
+ <seealso marker="erl_driver">driver API</seealso> from
+ <c>stop_select</c>. This strict limitation is because the
+ volatile context that <c>stop_select</c> can be called.</p>
</item>
-
- </taglist>
- </item>
-
</taglist>
</section>
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="erl_driver">erl_driver(3)</seealso>,
- <seealso marker="kernel:erl_ddll">erl_ddll(3)</seealso>,
- <seealso marker="erlang">erlang(3)</seealso>,
- kernel(3)</p>
+ <title>See Also</title>
+ <p><seealso marker="erl_driver"><c>erl_driver(3)</c></seealso>,
+ <seealso marker="erlang"><c>erlang(3)</c></seealso>,
+ <seealso marker="kernel:erl_ddll"><c>erl_ddll(3)</c></seealso></p>
</section>
</cref>
diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml
index d9f580d081..311483022d 100644
--- a/erts/doc/src/epmd.xml
+++ b/erts/doc/src/epmd.xml
@@ -28,242 +28,259 @@
<docno>1</docno>
<approved></approved>
<checked></checked>
- <date>98-01-05</date>
+ <date>1998-01-05</date>
<rev>A</rev>
<file>epmd.xml</file>
</header>
<com>epmd</com>
- <comsummary>
- <p>Erlang Port Mapper Daemon</p>
- <taglist>
- <tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-address Addresses] [-port No] [-daemon] [-relaxed_command_check]]]></c></tag>
- <item>
- <p>Starts the port mapper daemon</p>
- </item>
- <tag><c><![CDATA[epmd [-d|-debug] [-port No] [-names|-kill|-stop Name]]]></c></tag>
- <item>
- <p>Communicates with a running port mapper daemon</p>
- </item>
- </taglist>
- </comsummary>
+ <comsummary>Erlang Port Mapper Daemon</comsummary>
+
<description>
- <p>This daemon acts as a name server on all hosts involved in
- distributed Erlang computations. When an Erlang node
- starts, the node has a name and it obtains an address from the host
- OS kernel.
- The name and the address are sent to the
+ <taglist>
+ <tag><c><![CDATA[epmd [-d|-debug] [DbgExtra...] [-address Addresses]
+ [-port No] [-daemon] [-relaxed_command_check]]]></c></tag>
+ <item>
+ <p>Starts the port mapper daemon.</p>
+ </item>
+ <tag><c><![CDATA[epmd [-d|-debug] [-port No]
+ [-names|-kill|-stop Name]]]></c></tag>
+ <item>
+ <p>Communicates with a running port mapper daemon.</p>
+ </item>
+ </taglist>
+
+ <p>This daemon acts as a name server on all hosts involved in
+ distributed Erlang computations. When an Erlang node starts,
+ the node has a name and it obtains an address from the host
+ OS kernel. The name and address are sent to the
<c><![CDATA[epmd]]></c> daemon running on the local host.
In a TCP/IP environment, the address consists
- of the IP address and a port number. The name of the node is
+ of the IP address and a port number. The node name is
an atom on the form of <c><![CDATA[Name@Node]]></c>.
The job of the <c><![CDATA[epmd]]></c> daemon is to keep track of which
node name listens on which address. Hence, <c><![CDATA[epmd]]></c> maps
symbolic node names to machine addresses.</p>
- <p>The TCP/IP <c>epmd</c> daemon actually only keeps track of
+ <p>The TCP/IP <c>epmd</c> daemon only keeps track of
the <c>Name</c> (first) part of an Erlang node name. The <c>Host</c>
part (whatever is after the <c><![CDATA[@]]></c>) is implicit in the
- node name where the <c>epmd</c> daemon was actually contacted,
+ node name where the <c>epmd</c> daemon was contacted,
as is the IP address where the Erlang node can be
reached. Consistent and correct TCP naming services are
therefore required for an Erlang network to function
correctly.</p>
<taglist>
- <tag>Starting the port mapper daemon</tag>
- <item>
-
- <p>The daemon is started automatically by the <c>erl</c>
- command if the node is to be distributed and there is no
- running instance present. If automatically launched,
- environment variables have to be used to alter the behavior of
- the daemon. See the <seealso
- marker="#environment_variables">Environment
- variables</seealso> section below.</p>
-
- <p>If the -daemon argument is not given,
- <c><![CDATA[epmd]]></c> runs as a normal program with the
- controlling terminal of the shell in which it is
- started. Normally, it should run as a daemon.</p>
-
- <p>Regular start-up options are described in the
- <seealso marker="#daemon_flags">Regular options</seealso>
- section below.</p>
-
- <p>The <c>DbgExtra</c> options are described in the
- <seealso marker="#debug_flags">DbgExtra options</seealso>
- section below.</p>
-
- </item>
- <tag>Communicating with a running port mapper daemon</tag>
- <item>
-
- <p>Communicating with the running epmd daemon by means of the
- <c>epmd</c> program is done primarily for debugging
- purposes.</p>
-
- <p>The different queries are described in the <seealso
- marker="#interactive_flags">Interactive options</seealso>
- section below.</p>
-
- </item>
- </taglist>
+ <tag>Starting the port mapper daemon</tag>
+ <item>
+ <p>The daemon is started automatically by command
+ <seealso marker="erl"><c>erl(1)</c></seealso>
+ if the node is to be distributed and no running
+ instance is present. If automatically launched
+ environment variables must be used to change the behavior
+ of the daemon; see section
+ <seealso marker="#environment_variables">Environment
+ Variables</seealso>.</p>
+ <p>If argument <c>-daemon</c> is not specified,
+ <c><![CDATA[epmd]]></c> runs as a normal program with the
+ controlling terminal of the shell in which it is
+ started. Normally, it is to be run as a daemon.</p>
+ <p>Regular startup options are described in section
+ <seealso marker="#daemon_flags">Regular Options</seealso>.</p>
+ <p>The <c>DbgExtra</c> options are described in section
+ <seealso marker="#debug_flags">DbgExtra Options</seealso>.</p>
+ </item>
+ <tag>Communicating with a running port mapper daemon</tag>
+ <item>
+ <p>Communicating with the running <c>epmd</c> daemon by the
+ <c>epmd</c> program is done primarily for debugging purposes.</p>
+ <p>The different queries are described in section <seealso
+ marker="#interactive_flags">Interactive options</seealso>.</p>
+ </item>
+ </taglist>
</description>
+
<section>
<marker id="daemon_flags"></marker>
- <title>Regular options</title>
+ <title>Regular Options</title>
+ <p>These options are available when starting the name server. The name
+ server is normally started automatically by command
+ <seealso marker="erl"><c>erl(1)</c></seealso> (if not already available),
+ but it can also be started at system startup.</p>
- <p>These options are available when starting the actual name server. The name server is normally started automatically by the <c>erl</c> command (if not already available), but it can also be started at i.e. system start-up.</p>
<taglist>
- <tag><c><![CDATA[-address List]]></c></tag>
- <item>
- <p>Let this instance of <c>epmd</c> listen only on the
- comma-separated list of IP addresses and on the loopback address
- (which is implicitly added to the list if it has not been
- specified). This can also be set using the
- <c><![CDATA[ERL_EPMD_ADDRESS]]></c> environment variable. See the
- section <seealso marker="#environment_variables">Environment
- variables</seealso> below.</p>
- </item>
- <tag><c><![CDATA[-port No]]></c></tag>
- <item>
- <p>Let this instance of epmd listen to another TCP port than
- default 4369. This can also be set using the
- <c><![CDATA[ERL_EPMD_PORT]]></c> environment variable. See the
- section <seealso marker="#environment_variables">Environment
- variables</seealso> below</p>
- </item>
- <tag><c><![CDATA[-d | -debug]]></c></tag>
- <item>
-
- <p>Enable debug output. The more <c>-d</c> flags given, the more
- debug output you will get (to a certain limit). This option is
- most useful when the epmd daemon is not started as a daemon.</p>
- </item>
- <tag><c><![CDATA[-daemon]]></c></tag>
- <item>
- <p>Start epmd detached from the controlling terminal. Logging will end up in syslog when available and correctly configured. If the epmd daemon is started at boot, this option should definitely be used. It is also used when the <c>erl</c> command automatically starts <c>epmd</c>.</p>
- </item>
- <tag><c><![CDATA[-relaxed_command_check]]></c></tag>
- <item>
- <p>Start the epmd program with relaxed command checking (mostly for backward compatibility). This affects the following:</p>
- <list type="bulleted">
- <item>
- <p>With relaxed command checking, the <c>epmd</c> daemon can be killed from the localhost with i.e. <c>epmd -kill</c> even if there are active nodes registered. Normally only daemons with an empty node database can be killed with the <c>epmd -kill</c> command.</p>
+ <tag><c><![CDATA[-address List]]></c></tag>
+ <item>
+ <p>Lets this instance of <c>epmd</c> listen only on the
+ comma-separated list of IP addresses and on the loopback address
+ (which is implicitly added to the list if it has not been
+ specified). This can also be set using environment variable
+ <c><![CDATA[ERL_EPMD_ADDRESS]]></c>; see section <seealso
+ marker="#environment_variables">Environment Variables</seealso>.</p>
+ </item>
+ <tag><c><![CDATA[-port No]]></c></tag>
+ <item>
+ <p>Lets this instance of <c>epmd</c> listen to another TCP port than
+ default 4369. This can also be set using environment variable
+ <c><![CDATA[ERL_EPMD_PORT]]></c>; see section <seealso
+ marker="#environment_variables">Environment Variables</seealso>.</p>
+ </item>
+ <tag><c><![CDATA[-d | -debug]]></c></tag>
+ <item>
+ <p>Enables debug output. The more <c>-d</c> flags specified, the more
+ debug output you will get (to a certain limit). This option is most
+ useful when the <c>epmd</c> daemon is not started as a daemon.</p>
+ </item>
+ <tag><c><![CDATA[-daemon]]></c></tag>
+ <item>
+ <p>Starts <c>epmd</c> detached from the controlling terminal. Logging
+ ends up in syslog when available and correctly configured. If the
+ <c>epmd</c> daemon is started at boot, this option is definitely
+ to be used. It is also used when command <c>erl</c> automatically
+ starts <c>epmd</c>.</p>
+ </item>
+ <tag><c><![CDATA[-relaxed_command_check]]></c></tag>
+ <item>
+ <p>Starts the <c>epmd</c> program with relaxed command checking
+ (mostly for backward compatibility). This affects the following:</p>
+ <list type="bulleted">
+ <item>
+ <p>With relaxed command checking, the <c>epmd</c> daemon can be
+ killed from the local host with, for example, command
+ <c>epmd -kill</c> even if active nodes are registered. Normally
+ only daemons with an empty node database can be killed with
+ <c>epmd -kill</c>.</p>
</item>
<item>
- <p>The <c>epmd -stop</c> command (and the corresponding messages to epmd, as can be given using <c>erl_interface/ei</c>) is normally always ignored, as it opens up the possibility of a strange situation where two nodes of the same name can be alive at the same time. A node unregisters itself by just closing the connection to epmd, which is why the <c>stop</c> command was only intended for use in debugging situations.</p>
- <p>With relaxed command checking enabled, you can forcibly unregister live nodes.</p>
+ <p>Command <c>epmd -stop</c> (and the corresponding messages to
+ <c>epmd</c>, as can be specified using <seealso
+ marker="erl_interface:ei"><c>erl_interface:ei(3)</c></seealso>) is
+ normally always ignored. This because it can cause a strange
+ situation where two nodes of the same name can be alive at the
+ same time. A node unregisters itself by only closing the
+ connection to <c>epmd</c>, which is why command <c>stop</c>
+ was only intended for use in debugging situations.</p>
+ <p>With relaxed command checking enabled, you can forcibly
+ unregister live nodes.</p>
</item>
- </list>
- <p>Relaxed command checking can also be enabled by setting the environment variable <c>ERL_EPMD_RELAXED_COMMAND_CHECK</c> prior to starting <c>epmd</c>.</p>
- <p>Only use relaxed command checking on systems with very limited interactive usage.</p>
- </item>
- </taglist>
+ </list>
+ <p>Relaxed command checking can also be enabled by setting environment
+ variable <c>ERL_EPMD_RELAXED_COMMAND_CHECK</c> before starting
+ <c>epmd</c>.</p>
+ <p>Use relaxed command checking only on systems with very limited
+ interactive usage.</p>
+ </item>
+ </taglist>
</section>
<section>
<marker id="debug_flags"></marker>
- <title>DbgExtra options</title>
- <p>These options are purely for debugging and testing epmd clients. They should not be used in normal operation.</p>
+ <title>DbgExtra Options</title>
+ <note>
+ <p>These options are only for debugging and testing <c>epmd</c> clients.
+ They are not to be used in normal operation.</p>
+ </note>
<taglist>
- <tag><c><![CDATA[-packet_timeout Seconds]]></c></tag>
- <item>
- <p>Set the number of seconds a connection can be
- inactive before epmd times out and closes the
- connection (default 60).</p>
- </item>
- <tag><c><![CDATA[-delay_accept Seconds]]></c></tag>
- <item>
- <p>To simulate a busy server you can insert a delay between when epmd
- gets notified that a new connection is requested and
- when the connection gets accepted.</p>
- </item>
- <tag><c><![CDATA[-delay_write Seconds]]></c></tag>
- <item>
- <p>Also a simulation of a busy server. Inserts
- a delay before a reply is sent.</p>
- </item>
+ <tag><c><![CDATA[-packet_timeout Seconds]]></c></tag>
+ <item>
+ <p>Sets the number of seconds a connection can be
+ inactive before <c>epmd</c> times out and closes the
+ connection. Defaults to 60.</p>
+ </item>
+ <tag><c><![CDATA[-delay_accept Seconds]]></c></tag>
+ <item>
+ <p>To simulate a busy server, you can insert a delay between when
+ <c>epmd</c> gets notified that a new connection is requested and
+ when the connection gets accepted.</p>
+ </item>
+ <tag><c><![CDATA[-delay_write Seconds]]></c></tag>
+ <item>
+ <p>Also a simulation of a busy server. Inserts
+ a delay before a reply is sent.</p>
+ </item>
</taglist>
</section>
+
<section>
<marker id="interactive_flags"></marker>
- <title>Interactive options</title>
- <p>These options make <c>epmd</c> run as an interactive command, displaying the results of sending queries to an already running instance of <c>epmd</c>. The epmd contacted is always on the local node, but the <c>-port</c> option can be used to select between instances if several are running using different ports on the host.</p>
- <taglist>
- <tag><c><![CDATA[-port No]]></c></tag>
- <item>
- <p>Contacts the <c>epmd</c> listening on the given TCP port number
- (default 4369). This can also be set using the
- <c><![CDATA[ERL_EPMD_PORT]]></c> environment variable. See the
- section <seealso marker="#environment_variables">Environment
- variables</seealso> below.</p>
- </item>
- <tag><c><![CDATA[-names]]></c></tag>
- <item>
- <p>List names registered with the currently running epmd</p>
- </item>
- <tag><c><![CDATA[-kill]]></c></tag>
- <item>
- <p>Kill the currently running <c>epmd</c>.</p>
+ <title>Interactive Options</title>
+ <p>These options make <c>epmd</c> run as an interactive command,
+ displaying the results of sending queries to an already running
+ instance of <c>epmd</c>. The <c>epmd</c> contacted is always on the
+ local node, but option <c>-port</c> can be used to select between
+ instances if several are running using different ports on the host.</p>
- <p>Killing the running <c>epmd</c> is only allowed if <c>epmd
- -names</c> shows an empty database or
- <c>-relaxed_command_check</c> was given when the running
- instance of <c>epmd</c> was started. Note that
- <c>-relaxed_command_check</c> is given when starting the
- daemon that is to accept killing when it has live nodes
- registered. When running epmd interactively,
- <c>-relaxed_command_check</c> has no effect. A daemon that is
- started without relaxed command checking has to be killed
- using i.e. signals or some other OS specific method if it has
- active clients registered.</p>
- </item>
- <tag><c><![CDATA[-stop Name]]></c></tag>
- <item>
- <p>Forcibly unregister a live node from <c>epmd</c>'s database</p>
-
- <p>This command can only be used when contacting <c>epmd</c>
- instances started with the <c>-relaxed_command_check</c>
- flag. Note that relaxed command checking has to be enabled for
- the <c>epmd</c> daemon contacted. When running epmd
- interactively,
- <c>-relaxed_command_check</c> has no effect.</p>
- </item>
- </taglist>
+ <taglist>
+ <tag><c><![CDATA[-port No]]></c></tag>
+ <item>
+ <p>Contacts the <c>epmd</c> listening on the specified TCP port
+ number (default 4369). This can also be set using environment
+ variable <c><![CDATA[ERL_EPMD_PORT]]></c>; see section <seealso
+ marker="#environment_variables">Environment Variables</seealso>.</p>
+ </item>
+ <tag><c><![CDATA[-names]]></c></tag>
+ <item>
+ <p>Lists names registered with the currently running <c>epmd</c>.</p>
+ </item>
+ <tag><c><![CDATA[-kill]]></c></tag>
+ <item>
+ <p>Kills the currently running <c>epmd</c>.</p>
+ <p>Killing the running <c>epmd</c> is only allowed if
+ <c>epmd -names</c> shows an empty database or if
+ <c>-relaxed_command_check</c> was specified when the running
+ instance of <c>epmd</c> was started.</p>
+ <p>Notice that <c>-relaxed_command_check</c> is specified when
+ starting the daemon that is to accept killing when it has live
+ nodes registered. When running <c>epmd</c> interactively,
+ <c>-relaxed_command_check</c> has no effect. A daemon that is
+ started without relaxed command checking must be killed using,
+ for example, signals or some other OS-specific method if it has
+ active clients registered.</p>
+ </item>
+ <tag><c><![CDATA[-stop Name]]></c></tag>
+ <item>
+ <p>Forcibly unregisters a live node from the <c>epmd</c> database.</p>
+ <p>This command can only be used when contacting <c>epmd</c>
+ instances started with flag <c>-relaxed_command_check</c>.</p>
+ <p>Notice that relaxed command checking must enabled for the
+ <c>epmd</c> daemon contacted. When running <c>epmd</c>
+ interactively, <c>-relaxed_command_check</c> has no effect.</p>
+ </item>
+ </taglist>
</section>
+
<section>
<marker id="environment_variables"></marker>
- <title>Environment variables</title>
+ <title>Environment Variables</title>
<taglist>
<tag><c><![CDATA[ERL_EPMD_ADDRESS]]></c></tag>
<item>
- <p>This environment variable may be set to a comma-separated
- list of IP addresses, in which case the <c>epmd</c> daemon
- will listen only on the specified address(es) and on the
- loopback address (which is implicitly added to the list if it
- has not been specified). The default behaviour is to listen on
- all available IP addresses.</p>
+ <p>Can be set to a comma-separated
+ list of IP addresses, in which case the <c>epmd</c> daemon
+ will listen only on the specified address(es) and on the
+ loopback address (which is implicitly added to the list if it
+ has not been specified). The default behavior is to listen on
+ all available IP addresses.</p>
</item>
<tag><c><![CDATA[ERL_EPMD_PORT]]></c></tag>
<item>
- <p>This environment variable can contain the port number epmd will use.
- The default port will work fine in most cases. A different port can
- be specified to allow several instances of epmd, representing
- independent clusters of nodes, to co-exist on the same host.
- All nodes in a cluster must use the same epmd port number.</p>
+ <p>Can contain the port number <c>epmd</c> will use.
+ The default port will work fine in most cases. A different port can
+ be specified to allow several instances of <c>epmd</c>, representing
+ independent clusters of nodes, to co-exist on the same host.
+ All nodes in a cluster must use the same <c>epmd</c> port number.</p>
</item>
<tag><c><![CDATA[ERL_EPMD_RELAXED_COMMAND_CHECK]]></c></tag>
<item>
- <p>If set prior to start, the <c>epmd</c> daemon will behave
- as if the <c>-relaxed_command_check</c> option was given at
- start-up. Consequently, if this option is set before starting
- the Erlang virtual machine, the automatically started
- <c>epmd</c> will accept the <c>-kill</c> and <c>-stop</c>
- commands without restrictions.</p>
+ <p>If set before start, the <c>epmd</c> daemon behaves
+ as if option <c>-relaxed_command_check</c> was specified at
+ startup. Consequently, if this option is set before starting
+ the Erlang virtual machine, the automatically started
+ <c>epmd</c> accepts the <c>-kill</c> and <c>-stop</c>
+ commands without restrictions.</p>
</item>
</taglist>
</section>
@@ -271,41 +288,45 @@
<section>
<title>Logging</title>
<p>On some operating systems <em>syslog</em> will be used for
- error reporting when epmd runs as an daemon. To enable
- the error logging you have to edit <path unix="" windows="">/etc/syslog.conf</path>
- file and add an entry</p>
+ error reporting when <c>epmd</c> runs as a daemon. To enable
+ the error logging, you must edit the
+ <path unix="" windows="">/etc/syslog.conf</path> file and add an
+ entry:</p>
+
<code type="none"><![CDATA[
- !epmd
- *.*<TABs>/var/log/epmd.log
- ]]></code>
- <p>where &lt;TABs&gt; are at least one real tab character. Spaces will
- silently be ignored.
- </p>
+ !epmd
+ *.*<TABs>/var/log/epmd.log
+]]></code>
+
+ <p>where <c>&lt;TABs&gt;</c> are at least one real tab character.
+ Spaces are silently ignored.</p>
</section>
+
<section>
- <title>Access restrictions</title>
- <p>The <c>epmd</c> daemon accepts messages from both localhost and
- remote hosts. However, only the query commands are answered (and
- acted upon) if the query comes from a remote host. It is always an
- error to try to register a nodename if the client is not a process
- located on the same host as the <c>epmd</c> instance is running on-
- such requests are considered hostile and the connection is
- immediately closed.</p>
+ <title>Access Restrictions</title>
+ <p>The <c>epmd</c> daemon accepts messages from both the local host and
+ remote hosts. However, only the query commands are answered (and
+ acted upon) if the query comes from a remote host. It is always an
+ error to try to register a node name if the client is not a process
+ on the same host as the <c>epmd</c> instance is running on. Such
+ requests are considered hostile and the connection is closed
+ immediately.</p>
- <p>The queries accepted from remote nodes are:</p>
- <list type="bulleted">
- <item>
- <p>Port queries - i.e. on which port does the node with a given
- name listen</p>
- </item>
- <item>
- <p>Name listing - i.e. give a list of all names registered on
+ <p>The following queries are accepted from remote nodes:</p>
+
+ <list type="bulleted">
+ <item>
+ <p>Port queries, that is, on which port the node with a specified
+ name listens</p>
+ </item>
+ <item>
+ <p>Name listing, that is, gives a list of all names registered on
the host</p>
- </item>
- </list>
- <p>To restrict access further, firewall software has to be used.</p>
- </section>
+ </item>
+ </list>
+ <p>To restrict access further, firewall software must be used.</p>
+ </section>
</comref>
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 37387f2c59..8da832ac37 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -30,86 +30,92 @@
<file>erl.xml</file>
</header>
<com>erl</com>
- <comsummary>The Erlang Emulator</comsummary>
+ <comsummary>The Erlang emulator.</comsummary>
<description>
<p>The <c><![CDATA[erl]]></c> program starts an Erlang runtime system.
- The exact details (for example, whether <c><![CDATA[erl]]></c> is a script or
- a program and which other programs it calls) are system-dependent.</p>
- <p>Windows users probably wants to use the <c><![CDATA[werl]]></c> program
+ The exact details (for example, whether <c><![CDATA[erl]]></c> is a
+ script or a program and which other programs it calls) are
+ system-dependent.</p>
+
+ <p>Windows users probably want to use the <c><![CDATA[werl]]></c> program
instead, which runs in its own window with scrollbars and supports
- command-line editing. The <c><![CDATA[erl]]></c> program on Windows provides
- no line editing in its shell, and on Windows 95 there is no way
- to scroll back to text which has scrolled off the screen.
- The <c><![CDATA[erl]]></c> program must be used, however, in pipelines or if
+ command-line editing. The <c><![CDATA[erl]]></c> program on Windows
+ provides no line editing in its shell, and on Windows 95 there is no way
+ to scroll back to text that has scrolled off the screen. The
+ <c><![CDATA[erl]]></c> program must be used, however, in pipelines or if
you want to redirect standard input or output.</p>
- <note><p>As of ERTS version 5.9 (OTP-R15B) the runtime system will by
- default <em>not</em> bind schedulers to logical processors.
- For more information see documentation of the
- <seealso marker="#+sbt">+sbt</seealso> system flag.
- </p>
- </note>
+
+ <note>
+ <p>As from ERTS 5.9 (Erlang/OTP R15B) the runtime system does by
+ default <em>not</em> bind schedulers to logical processors.
+ For more information, see system flag
+ <seealso marker="#+sbt"><c>+sbt</c></seealso>.</p>
+ </note>
</description>
+
<funcs>
<func>
<name>erl &lt;arguments></name>
- <fsummary>Start an Erlang runtime system</fsummary>
+ <fsummary>Start an Erlang runtime system.</fsummary>
<desc>
- <p>Starts an Erlang runtime system.</p>
- <p>The arguments can be divided into <em>emulator flags</em>,
- <em>flags</em> and <em>plain arguments</em>:</p>
- <list type="bulleted">
- <item>
- <p>Any argument starting with the character <c><![CDATA[+]]></c> is
- interpreted as an <seealso marker="#emu_flags">emulator flag</seealso>.</p>
- <p>As indicated by the name, emulator flags controls
- the behavior of the emulator.</p>
- </item>
- <item>
- <p>Any argument starting with the character <c><![CDATA[-]]></c>
- (hyphen) is interpreted as a
- <seealso marker="#init_flags">flag</seealso> which should
- be passed to the Erlang part of the runtime system, more
- specifically to the <c><![CDATA[init]]></c> system process, see
- <seealso marker="init">init(3)</seealso>.</p>
- <p>The <c><![CDATA[init]]></c> process itself interprets some of these
- flags, the <em>init flags</em>. It also stores any
- remaining flags, the <em>user flags</em>. The latter can
- be retrieved by calling <c><![CDATA[init:get_argument/1]]></c>.</p>
- <p>It can be noted that there are a small number of "-"
- flags which now actually are emulator flags, see
- the description below.</p>
- </item>
- <item>
- <p>Plain arguments are not interpreted in any way. They are
- also stored by the <c><![CDATA[init]]></c> process and can be
- retrieved by calling <c><![CDATA[init:get_plain_arguments/0]]></c>.
- Plain arguments can occur before the first flag, or after
- a <c><![CDATA[--]]></c> flag. Additionally, the flag <c><![CDATA[-extra]]></c>
- causes everything that follows to become plain arguments.</p>
- </item>
- </list>
- <p>Example:</p>
- <pre>
+ <p>Starts an Erlang runtime system.</p>
+ <p>The arguments can be divided into <em>emulator flags</em>,
+ <em>flags</em>, and <em>plain arguments</em>:</p>
+ <list type="bulleted">
+ <item>
+ <p>Any argument starting with character <c><![CDATA[+]]></c> is
+ interpreted as an
+ <seealso marker="#emu_flags">emulator flag</seealso>.</p>
+ <p>As indicated by the name, emulator flags control
+ the behavior of the emulator.</p>
+ </item>
+ <item>
+ <p>Any argument starting with character <c><![CDATA[-]]></c>
+ (hyphen) is interpreted as a
+ <seealso marker="#init_flags">flag</seealso>, which is to
+ be passed to the Erlang part of the runtime system, more
+ specifically to the <c><![CDATA[init]]></c> system process, see
+ <seealso marker="init"><c>init(3)</c></seealso>.</p>
+ <p>The <c><![CDATA[init]]></c> process itself interprets some of
+ these flags, the <em>init flags</em>. It also stores any
+ remaining flags, the <em>user flags</em>. The latter can be
+ retrieved by calling <c><![CDATA[init:get_argument/1]]></c>.</p>
+ <p>A small number of "-" flags exist, which now actually are
+ emulator flags, see the description below.</p>
+ </item>
+ <item>
+ <p>Plain arguments are not interpreted in any way. They are also
+ stored by the <c><![CDATA[init]]></c> process and can be retrieved
+ by calling <c><![CDATA[init:get_plain_arguments/0]]></c>.
+ Plain arguments can occur before the first flag, or after a
+ <c><![CDATA[--]]></c> flag. Also, the <c><![CDATA[-extra]]></c>
+ flag causes everything that follows to become plain arguments.</p>
+ </item>
+ </list>
+ <p><em>Examples:</em></p>
+ <pre>
% <input>erl +W w -sname arnie +R 9 -s my_init -extra +bertie</input>
(arnie@host)1> <input>init:get_argument(sname).</input>
{ok,[["arnie"]]}
(arnie@host)2> <input>init:get_plain_arguments().</input>
["+bertie"]</pre>
- <p>Here <c><![CDATA[+W w]]></c> and <c><![CDATA[+R 9]]></c> are emulator flags.
- <c><![CDATA[-s my_init]]></c> is an init flag, interpreted by <c><![CDATA[init]]></c>.
- <c><![CDATA[-sname arnie]]></c> is a user flag, stored by <c><![CDATA[init]]></c>.
- It is read by Kernel and will cause the Erlang runtime system
- to become distributed. Finally, everything after <c><![CDATA[-extra]]></c>
- (that is, <c><![CDATA[+bertie]]></c>) is considered as plain arguments.</p>
- <pre>
+ <p>Here <c><![CDATA[+W w]]></c> and <c><![CDATA[+R 9]]></c> are
+ emulator flags. <c><![CDATA[-s my_init]]></c> is an init flag,
+ interpreted by <c><![CDATA[init]]></c>.
+ <c><![CDATA[-sname arnie]]></c> is a user flag, stored by
+ <c><![CDATA[init]]></c>. It is read by Kernel and causes the
+ Erlang runtime system to become distributed. Finally, everything after
+ <c><![CDATA[-extra]]></c> (that is, <c><![CDATA[+bertie]]></c>) is
+ considered as plain arguments.</p>
+ <pre>
% <input>erl -myflag 1</input>
1> <input>init:get_argument(myflag).</input>
{ok,[["1"]]}
2> <input>init:get_plain_arguments().</input>
[]</pre>
- <p>Here the user flag <c><![CDATA[-myflag 1]]></c> is passed to and stored
- by the <c><![CDATA[init]]></c> process. It is a user defined flag,
- presumably used by some user defined application.</p>
+ <p>Here the user flag <c><![CDATA[-myflag 1]]></c> is passed to and
+ stored by the <c><![CDATA[init]]></c> process. It is a user-defined
+ flag, presumably used by some user-defined application.</p>
</desc>
</func>
</funcs>
@@ -117,50 +123,54 @@
<section>
<marker id="init_flags"></marker>
<title>Flags</title>
- <p>In the following list, init flags are marked (init flag).
+ <p>In the following list, init flags are marked "(init flag)".
Unless otherwise specified, all other flags are user flags, for
which the values can be retrieved by calling
- <c><![CDATA[init:get_argument/1]]></c>. Note that the list of user flags is
- not exhaustive, there may be additional, application specific
- flags which instead are documented in the corresponding
+ <c><![CDATA[init:get_argument/1]]></c>. Notice that the list of user
+ flags is not exhaustive, there can be more application-specific
+ flags that instead are described in the corresponding
application documentation.</p>
<taglist>
- <tag><c><![CDATA[--]]></c>(init flag)</tag>
+ <tag><c><![CDATA[--]]></c> (init flag)</tag>
<item>
<p>Everything following <c><![CDATA[--]]></c> up to the next flag
- (<c><![CDATA[-flag]]></c> or <c><![CDATA[+flag]]></c>) is considered plain arguments
- and can be retrieved using <c><![CDATA[init:get_plain_arguments/0]]></c>.</p>
+ (<c><![CDATA[-flag]]></c> or <c><![CDATA[+flag]]></c>) is considered
+ plain arguments and can be retrieved using
+ <c><![CDATA[init:get_plain_arguments/0]]></c>.</p>
</item>
<tag><c><![CDATA[-Application Par Val]]></c></tag>
<item>
- <p>Sets the application configuration parameter <c><![CDATA[Par]]></c> to
- the value <c><![CDATA[Val]]></c> for the application <c><![CDATA[Application]]></c>,
- see <seealso marker="kernel:app">app(4)</seealso> and
- <seealso marker="kernel:application">application(3)</seealso>.</p>
+ <p>Sets the application configuration parameter <c><![CDATA[Par]]></c>
+ to the value <c><![CDATA[Val]]></c> for the application
+ <c><![CDATA[Application]]></c>; see
+ <seealso marker="kernel:app"><c>app(4)</c></seealso> and
+ <seealso marker="kernel:application">
+ <c>application(3)</c></seealso>.</p>
</item>
<tag><marker id="args_file"/><c><![CDATA[-args_file FileName]]></c></tag>
<item>
- <p>Command line arguments are read from the file <c><![CDATA[FileName]]></c>.
- The arguments read from the file replace the
- '<c><![CDATA[-args_file FileName]]></c>' flag on the resulting command line.</p>
- <p>The file <c><![CDATA[FileName]]></c> should be a plain text file and may
- contain comments and command line arguments. A comment begins
- with a # character and continues until next end of line character.
- Backslash (\\) is used as quoting character. All command line
- arguments accepted by <c><![CDATA[erl]]></c> are allowed, also the
- <c><![CDATA[-args_file FileName]]></c> flag. Be careful not to cause circular
- dependencies between files containing the <c><![CDATA[-args_file]]></c> flag,
- though.</p>
- <p>The <c><![CDATA[-extra]]></c> flag is treated specially. Its scope ends
- at the end of the file. Arguments following an <c><![CDATA[-extra]]></c>
- flag are moved on the command line into the <c><![CDATA[-extra]]></c> section,
- i.e. the end of the command line following after an <c><![CDATA[-extra]]></c>
- flag.</p>
+ <p>Command-line arguments are read from the file
+ <c><![CDATA[FileName]]></c>. The arguments read from the file replace
+ flag '<c><![CDATA[-args_file FileName]]></c>' on the resulting
+ command line.</p>
+ <p>The file <c><![CDATA[FileName]]></c> is to be a plain text file and
+ can contain comments and command-line arguments. A comment begins
+ with a <c>#</c> character and continues until the next end of line
+ character. Backslash (\\) is used as quoting character. All
+ command-line arguments accepted by <c><![CDATA[erl]]></c> are allowed,
+ also flag <c><![CDATA[-args_file FileName]]></c>. Be careful not to
+ cause circular dependencies between files containing flag
+ <c><![CDATA[-args_file]]></c>, though.</p>
+ <p>The flag <c><![CDATA[-extra]]></c> is treated in special way. Its
+ scope ends at the end of the file. Arguments following an
+ <c><![CDATA[-extra]]></c> flag are moved on the command line into the
+ <c><![CDATA[-extra]]></c> section, that is, the end of the command
+ line following after an <c><![CDATA[-extra]]></c> flag.</p>
</item>
<tag><c><![CDATA[-async_shell_start]]></c></tag>
<item>
<p>The initial Erlang shell does not read user input until
- the system boot procedure has been completed (Erlang 5.4 and
+ the system boot procedure has been completed (Erlang/OTP 5.4 and
later). This flag disables the start synchronization feature
and lets the shell start in parallel with the rest of
the system.</p>
@@ -168,52 +178,56 @@
<tag><c><![CDATA[-boot File]]></c></tag>
<item>
<p>Specifies the name of the boot file, <c><![CDATA[File.boot]]></c>,
- which is used to start the system. See
- <seealso marker="init">init(3)</seealso>. Unless
+ which is used to start the system; see
+ <seealso marker="init"><c>init(3)</c></seealso>. Unless
<c><![CDATA[File]]></c> contains an absolute path, the system searches
- for <c><![CDATA[File.boot]]></c> in the current and <c><![CDATA[$ROOT/bin]]></c>
- directories.</p>
+ for <c><![CDATA[File.boot]]></c> in the current and
+ <c><![CDATA[$ROOT/bin]]></c> directories.</p>
<p>Defaults to <c><![CDATA[$ROOT/bin/start.boot]]></c>.</p>
</item>
<tag><c><![CDATA[-boot_var Var Dir]]></c></tag>
<item>
- <p>If the boot script contains a path variable <c><![CDATA[Var]]></c> other
- than <c><![CDATA[$ROOT]]></c>, this variable is expanded to <c><![CDATA[Dir]]></c>.
- Used when applications are installed in another directory
- than <c><![CDATA[$ROOT/lib]]></c>, see
- <seealso marker="sasl:systools#make_script/1">systools:make_script/1,2</seealso>.</p>
+ <p>If the boot script contains a path variable <c><![CDATA[Var]]></c>
+ other than <c><![CDATA[$ROOT]]></c>, this variable is expanded to
+ <c><![CDATA[Dir]]></c>. Used when applications are installed in
+ another directory than <c><![CDATA[$ROOT/lib]]></c>; see
+ <seealso marker="sasl:systools#make_script/1">
+ <c>systools:make_script/1,2</c></seealso> in SASL.</p>
</item>
<tag><c><![CDATA[-code_path_cache]]></c></tag>
<item>
- <p>Enables the code path cache of the code server, see
- <seealso marker="kernel:code">code(3)</seealso>.</p>
+ <p>Enables the code path cache of the code server; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-compile Mod1 Mod2 ...]]></c></tag>
<item>
<p>Compiles the specified modules and then terminates (with
non-zero exit code if the compilation of some file did not
- succeed). Implies <c><![CDATA[-noinput]]></c>. Not recommended - use
- <seealso marker="erlc">erlc</seealso> instead.</p>
+ succeed). Implies <c><![CDATA[-noinput]]></c>.</p>
+ <p>Not recommended; use <seealso marker="erlc"><c>erlc</c></seealso>
+ instead.</p>
</item>
<tag><c><![CDATA[-config Config]]></c></tag>
<item>
<p>Specifies the name of a configuration file,
<c><![CDATA[Config.config]]></c>, which is used to configure
- applications. See
- <seealso marker="kernel:app">app(4)</seealso> and
- <seealso marker="kernel:application">application(3)</seealso>.</p>
+ applications; see
+ <seealso marker="kernel:app"><c>app(4)</c></seealso> and
+ <seealso marker="kernel:application">
+ <c>application(3)</c></seealso>.</p>
</item>
<tag><marker id="connect_all"/><c><![CDATA[-connect_all false]]></c></tag>
<item>
- <p>If this flag is present, <c><![CDATA[global]]></c> will not maintain a
- fully connected network of distributed Erlang nodes, and then
- global name registration cannot be used. See
- <seealso marker="kernel:global">global(3)</seealso>.</p>
+ <p>If this flag is present, <c><![CDATA[global]]></c> does not maintain
+ a fully connected network of distributed Erlang nodes, and then
+ global name registration cannot be used; see
+ <seealso marker="kernel:global"><c>global(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-cookie Cookie]]></c></tag>
<item>
<p>Obsolete flag without any effect and common misspelling for
- <c><![CDATA[-setcookie]]></c>. Use <c><![CDATA[-setcookie]]></c> instead.</p>
+ <c><![CDATA[-setcookie]]></c>. Use <c><![CDATA[-setcookie]]></c>
+ instead.</p>
</item>
<tag><c><![CDATA[-detached]]></c></tag>
<item>
@@ -223,8 +237,7 @@
</item>
<tag><c><![CDATA[-emu_args]]></c></tag>
<item>
- <p>Useful for debugging. Prints out the actual arguments
- sent to the emulator.</p>
+ <p>Useful for debugging. Prints the arguments sent to the emulator.</p>
</item>
<tag><c><![CDATA[-env Variable Value]]></c></tag>
<item>
@@ -234,14 +247,21 @@
<pre>
% <input>erl -env DISPLAY gin:0</input></pre>
<p>In this example, an Erlang runtime system is started with
- the <c><![CDATA[DISPLAY]]></c> environment variable set to <c><![CDATA[gin:0]]></c>.</p>
+ environment variable <c><![CDATA[DISPLAY]]></c> set to
+ <c><![CDATA[gin:0]]></c>.</p>
</item>
- <tag><c><![CDATA[-eval Expr]]></c>(init flag)</tag>
+ <tag><c><![CDATA[-epmd_module Module]]></c> (init flag)</tag>
<item>
- <p>Makes <c><![CDATA[init]]></c> evaluate the expression <c><![CDATA[Expr]]></c>, see
- <seealso marker="init">init(3)</seealso>.</p>
+ <p>Configures the module responsible to communicate to
+ <seealso marker="epmd">epmd</seealso>. Defaults to <c>erl_epmd</c>.</p>
</item>
- <tag><c><![CDATA[-extra]]></c>(init flag)</tag>
+ <tag><c><![CDATA[-eval Expr]]></c> (init flag)</tag>
+ <item>
+ <p>Makes <c><![CDATA[init]]></c> evaluate the expression
+ <c><![CDATA[Expr]]></c>; see
+ <seealso marker="init"><c>init(3)</c></seealso>.</p>
+ </item>
+ <tag><c><![CDATA[-extra]]></c> (init flag)</tag>
<item>
<p>Everything following <c><![CDATA[-extra]]></c> is considered plain
arguments and can be retrieved using
@@ -249,8 +269,9 @@
</item>
<tag><c><![CDATA[-heart]]></c></tag>
<item>
- <p>Starts heart beat monitoring of the Erlang runtime system.
- See <seealso marker="kernel:heart">heart(3)</seealso>.</p>
+ <p>Starts heartbeat monitoring of the Erlang runtime system;
+ see <seealso marker="kernel:heart">
+ <c>heart(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-hidden]]></c></tag>
<item>
@@ -258,91 +279,99 @@
run as a distributed node. Hidden nodes always establish
hidden connections to all other nodes except for nodes in the
same global group. Hidden connections are not published on
- either of the connected nodes, i.e. neither of the connected
- nodes are part of the result from <c><![CDATA[nodes/0]]></c> on the other
- node. See also hidden global groups,
- <seealso marker="kernel:global_group">global_group(3)</seealso>.</p>
+ any of the connected nodes, that is, none of the connected
+ nodes are part of the result from <c><![CDATA[nodes/0]]></c> on the
+ other node. See also hidden global groups;
+ <seealso marker="kernel:global_group">
+ <c>global_group(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-hosts Hosts]]></c></tag>
<item>
- <p>Specifies the IP addresses for the hosts on which Erlang
- boot servers are running, see
- <seealso marker="kernel:erl_boot_server">erl_boot_server(3)</seealso>.
- This flag is mandatory if the <c><![CDATA[-loader inet]]></c> flag is
- present.</p>
- <p>The IP addresses must be given in the standard form (four
- decimal numbers separated by periods, for example
- <c><![CDATA["150.236.20.74"]]></c>. Hosts names are not acceptable, but
- a broadcast address (preferably limited to the local network)
+ <p>Specifies the IP addresses for the hosts on which Erlang boot servers
+ are running, see <seealso marker="kernel:erl_boot_server">
+ <c>erl_boot_server(3)</c></seealso>. This flag
+ is mandatory if flag <c><![CDATA[-loader inet]]></c> is present.</p>
+ <p>The IP addresses must be specified in the standard form (four
+ decimal numbers separated by periods, for example,
+ <c><![CDATA["150.236.20.74"]]></c>. Hosts names are not acceptable,
+ but a broadcast address (preferably limited to the local network)
is.</p>
</item>
<tag><c><![CDATA[-id Id]]></c></tag>
<item>
<p>Specifies the identity of the Erlang runtime system. If it is
run as a distributed node, <c><![CDATA[Id]]></c> must be identical to
- the name supplied together with the <c><![CDATA[-sname]]></c> or
- <c><![CDATA[-name]]></c> flag.</p>
+ the name supplied together with flag <c><![CDATA[-sname]]></c> or
+ <c><![CDATA[-name]]></c>.</p>
</item>
<tag><c><![CDATA[-init_debug]]></c></tag>
<item>
<p>Makes <c><![CDATA[init]]></c> write some debug information while
interpreting the boot script.</p>
</item>
- <tag><marker id="instr"/><c><![CDATA[-instr]]></c>(emulator flag)</tag>
+ <tag><marker id="instr"/><c><![CDATA[-instr]]></c> (emulator flag)</tag>
<item>
<p>Selects an instrumented Erlang runtime system (virtual
machine) to run, instead of the ordinary one. When running an
instrumented runtime system, some resource usage data can be
- obtained and analysed using the module <c><![CDATA[instrument]]></c>.
+ obtained and analyzed using the <c><![CDATA[instrument]]></c> module.
Functionally, it behaves exactly like an ordinary Erlang
runtime system.</p>
</item>
<tag><c><![CDATA[-loader Loader]]></c></tag>
<item>
- <p>Specifies the method used by <c><![CDATA[erl_prim_loader]]></c> to load
- Erlang modules into the system. See
- <seealso marker="erl_prim_loader">erl_prim_loader(3)</seealso>.
- Two <c><![CDATA[Loader]]></c> methods are supported, <c><![CDATA[efile]]></c> and
- <c><![CDATA[inet]]></c>. <c><![CDATA[efile]]></c> means use the local file system,
- this is the default. <c><![CDATA[inet]]></c> means use a boot server on
- another machine, and the <c><![CDATA[-id]]></c>, <c><![CDATA[-hosts]]></c> and
- <c><![CDATA[-setcookie]]></c> flags must be specified as well. If
- <c><![CDATA[Loader]]></c> is something else, the user supplied
+ <p>Specifies the method used by <c><![CDATA[erl_prim_loader]]></c> to
+ load Erlang modules into the system; see
+ <seealso marker="erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>.
+ Two <c><![CDATA[Loader]]></c> methods are supported:</p>
+ <list type="bulleted">
+ <item>
+ <p><c><![CDATA[efile]]></c>, which means use the local file system,
+ this is the default.</p>
+ </item>
+ <item>
+ <p><c><![CDATA[inet]]></c>, which means use a boot server on
+ another machine. The flags <c><![CDATA[-id]]></c>,
+ <c><![CDATA[-hosts]]></c> and <c><![CDATA[-setcookie]]></c> must
+ also be specified.</p>
+ </item>
+ </list>
+ <p>If <c><![CDATA[Loader]]></c> is something else, the user-supplied
<c><![CDATA[Loader]]></c> port program is started.</p>
</item>
<tag><c><![CDATA[-make]]></c></tag>
<item>
- <p>Makes the Erlang runtime system invoke <c><![CDATA[make:all()]]></c> in
- the current working directory and then terminate. See
- <seealso marker="tools:make">make(3)</seealso>. Implies
+ <p>Makes the Erlang runtime system invoke <c><![CDATA[make:all()]]></c>
+ in the current working directory and then terminate; see
+ <seealso marker="tools:make"><c>make(3)</c></seealso>. Implies
<c><![CDATA[-noinput]]></c>.</p>
</item>
<tag><c><![CDATA[-man Module]]></c></tag>
<item>
- <p>Displays the manual page for the Erlang module <c><![CDATA[Module]]></c>.
- Only supported on Unix.</p>
+ <p>Displays the manual page for the Erlang module
+ <c><![CDATA[Module]]></c>. Only supported on Unix.</p>
</item>
<tag><c><![CDATA[-mode interactive | embedded]]></c></tag>
<item>
- <p>Indicates if the system should load code dynamically
- (<c><![CDATA[interactive]]></c>), or if all code should be loaded
- during system initialization (<c><![CDATA[embedded]]></c>), see
- <seealso marker="kernel:code">code(3)</seealso>. Defaults to
- <c><![CDATA[interactive]]></c>.</p>
+ <p>Indicates if the system is to load code dynamically
+ (<c><![CDATA[interactive]]></c>), or if all code is to be loaded
+ during system initialization (<c><![CDATA[embedded]]></c>); see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.
+ Defaults to <c><![CDATA[interactive]]></c>.</p>
</item>
<tag><c><![CDATA[-name Name]]></c></tag>
<item>
<p>Makes the Erlang runtime system into a distributed node.
This flag invokes all network servers necessary for a node to
- become distributed. See
- <seealso marker="kernel:net_kernel">net_kernel(3)</seealso>.
- It is also ensured that <c><![CDATA[epmd]]></c> runs on the current host
- before Erlang is started. See
- <seealso marker="epmd">epmd(1)</seealso> and the
+ become distributed; see <seealso marker="kernel:net_kernel">
+ <c>net_kernel(3)</c></seealso>. It is also ensured that
+ <c><![CDATA[epmd]]></c> runs on the current host before Erlang is
+ started; see <seealso marker="epmd"><c>epmd(1)</c></seealso>.and the
<seealso marker="#start_epmd"><c>-start_epmd</c></seealso> option.</p>
- <p>The name of the node will be <c><![CDATA[Name@Host]]></c>, where
- <c><![CDATA[Host]]></c> is the fully qualified host name of the current
- host. For short names, use the <c><![CDATA[-sname]]></c> flag instead.</p>
+ <p>The node name will be <c><![CDATA[Name@Host]]></c>, where
+ <c><![CDATA[Host]]></c> is the fully qualified host name of the
+ current host. For short names, use flag <c><![CDATA[-sname]]></c>
+ instead.</p>
</item>
<tag><c><![CDATA[-noinput]]></c></tag>
<item>
@@ -353,115 +382,113 @@
<item>
<p>Starts an Erlang runtime system with no shell. This flag
makes it possible to have the Erlang runtime system as a
- component in a series of UNIX pipes.</p>
+ component in a series of Unix pipes.</p>
</item>
<tag><c><![CDATA[-nostick]]></c></tag>
<item>
<p>Disables the sticky directory facility of the Erlang code
- server, see
- <seealso marker="kernel:code">code(3)</seealso>.</p>
+ server; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-oldshell]]></c></tag>
<item>
- <p>Invokes the old Erlang shell from Erlang 3.3. The old shell
+ <p>Invokes the old Erlang shell from Erlang/OTP 3.3. The old shell
can still be used.</p>
</item>
<tag><c><![CDATA[-pa Dir1 Dir2 ...]]></c></tag>
<item>
<p>Adds the specified directories to the beginning of the code
- path, similar to <c><![CDATA[code:add_pathsa/1]]></c>. See
- <seealso marker="kernel:code">code(3)</seealso>.
- As an alternative to <c>-pa</c>, if several directories are
+ path, similar to <seealso marker="kernel:code#add_pathsa/1">
+ <c><![CDATA[code:add_pathsa/1]]></c></seealso>. Note that the
+ order of the given directories will be reversed in the
+ resulting path.</p>
+ <p>As an alternative to <c>-pa</c>, if several directories are
to be prepended to the code path and the directories have a
- common parent directory, that parent directory could be
- specified in the <c>ERL_LIBS</c> environment variable.
- See <seealso marker="kernel:code">code(3)</seealso>.</p>
+ common parent directory, that parent directory can be
+ specified in environment variable <c>ERL_LIBS</c>; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-pz Dir1 Dir2 ...]]></c></tag>
<item>
<p>Adds the specified directories to the end of the code path,
- similar to <c><![CDATA[code:add_pathsz/1]]></c>. See
- <seealso marker="kernel:code">code(3)</seealso>.</p>
+ similar to <c><![CDATA[code:add_pathsz/1]]></c>; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-path Dir1 Dir2 ...]]></c></tag>
<item>
- <p>Replaces the path specified in the boot script. See
- <seealso marker="sasl:script">script(4)</seealso>.</p>
+ <p>Replaces the path specified in the boot script; see
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-proto_dist Proto]]></c></tag>
<item>
- <p>Specify a protocol for Erlang distribution.</p>
- <taglist>
- <tag><c>inet_tcp</c></tag>
- <item>
- <p>TCP over IPv4 (the default)</p>
- </item>
- <tag><c>inet_tls</c></tag>
- <item>
- <p>distribution over TLS/SSL</p>
- </item>
- <tag><c>inet6_tcp</c></tag>
- <item>
- <p>TCP over IPv6</p>
- </item>
+ <p>Specifies a protocol for Erlang distribution:</p>
+ <taglist>
+ <tag><c>inet_tcp</c></tag>
+ <item>TCP over IPv4 (the default)</item>
+ <tag><c>inet_tls</c></tag>
+ <item>Distribution over TLS/SSL</item>
+ <tag><c>inet6_tcp</c></tag>
+ <item>TCP over IPv6</item>
</taglist>
<p>For example, to start up IPv6 distributed nodes:</p>
<pre>
-% <input>erl -name [email protected] -proto_dist inet6_tcp</input>
-</pre>
+% <input>erl -name [email protected] -proto_dist inet6_tcp</input></pre>
</item>
<tag><c><![CDATA[-remsh Node]]></c></tag>
<item>
- <p>Starts Erlang with a remote shell connected to <c><![CDATA[Node]]></c>.</p>
+ <p>Starts Erlang with a remote shell connected to
+ <c><![CDATA[Node]]></c>.</p>
</item>
<tag><c><![CDATA[-rsh Program]]></c></tag>
<item>
- <p>Specifies an alternative to <c><![CDATA[rsh]]></c> for starting a slave
- node on a remote host. See
- <seealso marker="stdlib:slave">slave(3)</seealso>.</p>
+ <p>Specifies an alternative to <c><![CDATA[rsh]]></c> for starting a
+ slave node on a remote host; see
+ <seealso marker="stdlib:slave"><c>slave(3)</c></seealso>.</p>
</item>
- <tag><c><![CDATA[-run Mod [Func [Arg1, Arg2, ...]]]]></c>(init flag)</tag>
+ <tag><c><![CDATA[-run Mod [Func [Arg1, Arg2, ...]]]]></c> (init
+ flag)</tag>
<item>
- <p>Makes <c><![CDATA[init]]></c> call the specified function. <c><![CDATA[Func]]></c>
- defaults to <c><![CDATA[start]]></c>. If no arguments are provided,
- the function is assumed to be of arity 0. Otherwise it is
- assumed to be of arity 1, taking the list
- <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are passed
- as strings. See
- <seealso marker="init">init(3)</seealso>.</p>
+ <p>Makes <c><![CDATA[init]]></c> call the specified function.
+ <c><![CDATA[Func]]></c> defaults to <c><![CDATA[start]]></c>.
+ If no arguments are provided, the function is assumed to be of
+ arity 0. Otherwise it is assumed to be of arity 1, taking the list
+ <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are
+ passed as strings. See <seealso marker="init">
+ <c>init(3)</c></seealso>.</p>
</item>
- <tag><c><![CDATA[-s Mod [Func [Arg1, Arg2, ...]]]]></c>(init flag)</tag>
+ <tag><c><![CDATA[-s Mod [Func [Arg1, Arg2, ...]]]]></c> (init flag)</tag>
<item>
- <p>Makes <c><![CDATA[init]]></c> call the specified function. <c><![CDATA[Func]]></c>
- defaults to <c><![CDATA[start]]></c>. If no arguments are provided,
- the function is assumed to be of arity 0. Otherwise it is
- assumed to be of arity 1, taking the list
- <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are passed
- as atoms. See
- <seealso marker="init">init(3)</seealso>.</p>
+ <p>Makes <c><![CDATA[init]]></c> call the specified function.
+ <c><![CDATA[Func]]></c> defaults to <c><![CDATA[start]]></c>.
+ If no arguments are provided, the function is assumed to be of
+ arity 0. Otherwise it is assumed to be of arity 1, taking the list
+ <c><![CDATA[[Arg1,Arg2,...]]]></c> as argument. All arguments are
+ passed as atoms. See <seealso marker="init">
+ <c>init(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[-setcookie Cookie]]></c></tag>
<item>
- <p>Sets the magic cookie of the node to <c><![CDATA[Cookie]]></c>, see
- <seealso marker="erlang#set_cookie/2">erlang:set_cookie/2</seealso>.</p>
+ <p>Sets the magic cookie of the node to <c><![CDATA[Cookie]]></c>; see
+ <seealso marker="erlang#set_cookie/2">
+ <c>erlang:set_cookie/2</c></seealso>.</p>
</item>
<tag><c><![CDATA[-shutdown_time Time]]></c></tag>
<item>
<p>Specifies how long time (in milliseconds) the <c><![CDATA[init]]></c>
process is allowed to spend shutting down the system. If
- <c><![CDATA[Time]]></c> ms have elapsed, all processes still existing are
- killed. Defaults to <c><![CDATA[infinity]]></c>.</p>
+ <c><![CDATA[Time]]></c> milliseconds have elapsed, all processes still
+ existing are killed. Defaults to <c><![CDATA[infinity]]></c>.</p>
</item>
<tag><c><![CDATA[-sname Name]]></c></tag>
<item>
- <p>Makes the Erlang runtime system into a distributed node,
- similar to <c><![CDATA[-name]]></c>, but the host name portion of the node
+ <p>Makes the Erlang runtime system into a distributed node, similar to
+ <c><![CDATA[-name]]></c>, but the host name portion of the node
name <c><![CDATA[Name@Host]]></c> will be the short name, not fully
qualified.</p>
<p>This is sometimes the only way to run distributed Erlang if
- the DNS (Domain Name System) is not running. There can be no
- communication between nodes running with the <c><![CDATA[-sname]]></c>
- flag and those running with the <c><![CDATA[-name]]></c> flag, as node
+ the Domain Name System (DNS) is not running. No communication can
+ exist between nodes running with flag <c><![CDATA[-sname]]></c>
+ and those running with flag <c><![CDATA[-name]]></c>, as node
names must be unique in distributed Erlang systems.</p>
</item>
<tag><marker id="start_epmd"/><c>-start_epmd true | false</c></tag>
@@ -481,19 +508,21 @@
</item>
<tag><marker id="smp"/><c><![CDATA[-smp [enable|auto|disable]]]></c></tag>
<item>
- <p><c>-smp enable</c> and <c>-smp</c> starts the Erlang runtime
- system with SMP support enabled. This may fail if no runtime
+ <p><c>-smp enable</c> and <c>-smp</c> start the Erlang runtime
+ system with SMP support enabled. This can fail if no runtime
system with SMP support is available. <c>-smp auto</c> starts
the Erlang runtime system with SMP support enabled if it is
- available and more than one logical processor are detected.
+ available and more than one logical processor is detected.
<c>-smp disable</c> starts a runtime system without SMP support.</p>
- <p><em>NOTE</em>: The runtime system with SMP support will not
- be available on all supported platforms. See also the
- <seealso marker="#+S">+S</seealso> flag.</p>
+ <note>
+ <p>The runtime system with SMP support is not available on all
+ supported platforms. See also flag
+ <seealso marker="#+S"><c>+S</c></seealso>.</p>
+ </note>
</item>
- <tag><c><![CDATA[-version]]></c>(emulator flag)</tag>
+ <tag><c><![CDATA[-version]]></c> (emulator flag)</tag>
<item>
- <p>Makes the emulator print out its version number. The same
+ <p>Makes the emulator print its version number. The same
as <c><![CDATA[erl +V]]></c>.</p>
</item>
</taglist>
@@ -505,133 +534,169 @@
<p><c><![CDATA[erl]]></c> invokes the code for the Erlang emulator (virtual
machine), which supports the following flags:</p>
<taglist>
- <tag><marker id="async_thread_stack_size"/><c><![CDATA[+a size]]></c></tag>
+ <tag><marker id="async_thread_stack_size"/>
+ <c><![CDATA[+a size]]></c></tag>
<item>
<p>Suggested stack size, in kilowords, for threads in the
- async-thread pool. Valid range is 16-8192 kilowords. The
- default suggested stack size is 16 kilowords, i.e, 64
+ async thread pool. Valid range is 16-8192 kilowords. The
+ default suggested stack size is 16 kilowords, that is, 64
kilobyte on 32-bit architectures. This small default size
- has been chosen since the amount of async-threads might
- be quite large. The default size is enough for drivers
- delivered with Erlang/OTP, but might not be sufficiently
- large for other dynamically linked in drivers that use the
- <seealso marker="erl_driver#driver_async">driver_async()</seealso>
- functionality. Note that the value passed is only a
- suggestion, and it might even be ignored on some
- platforms.</p>
+ has been chosen because the number of async threads can
+ be large. The default size is enough for drivers
+ delivered with Erlang/OTP, but might not be large
+ enough for other dynamically linked-in drivers that use the
+ <seealso marker="erl_driver#driver_async">
+ <c>driver_async()</c></seealso> functionality.
+ Notice that the value passed is only a suggestion,
+ and it can even be ignored on some platforms.</p>
</item>
<tag><marker id="async_thread_pool_size"/><c><![CDATA[+A size]]></c></tag>
<item>
- <p>Sets the number of threads in async thread pool, valid range
- is 0-1024. If thread support is available, the default is 10.</p>
+ <p>Sets the number of threads in async thread pool. Valid range
+ is 0-1024. Defaults to 10 if thread support is available.</p>
</item>
<tag><c><![CDATA[+B [c | d | i]]]></c></tag>
<item>
- <p>The <c><![CDATA[c]]></c> option makes <c><![CDATA[Ctrl-C]]></c> interrupt the current
- shell instead of invoking the emulator break handler.
- The <c><![CDATA[d]]></c> option (same as specifying <c><![CDATA[+B]]></c> without an
- extra option) disables the break handler. The <c><![CDATA[i]]></c> option
- makes the emulator ignore any break signal.</p>
- <p>If the <c><![CDATA[c]]></c> option is used with <c><![CDATA[oldshell]]></c> on Unix,
- <c><![CDATA[Ctrl-C]]></c> will restart the shell process rather than
- interrupt it.</p>
- <p>Note that on Windows, this flag is only applicable for
- <c><![CDATA[werl]]></c>, not <c><![CDATA[erl]]></c> (<c><![CDATA[oldshell]]></c>). Note also that
- <c><![CDATA[Ctrl-Break]]></c> is used instead of <c><![CDATA[Ctrl-C]]></c> on Windows.</p>
+ <p>Option <c><![CDATA[c]]></c> makes <c><![CDATA[Ctrl-C]]></c>
+ interrupt the current shell instead of invoking the emulator break
+ handler. Option <c><![CDATA[d]]></c> (same as specifying
+ <c><![CDATA[+B]]></c> without an extra option) disables the break
+ handler. Option <c><![CDATA[i]]></c> makes the emulator ignore any
+ break signal.</p>
+ <p>If option <c><![CDATA[c]]></c> is used with
+ <c><![CDATA[oldshell]]></c> on Unix, <c><![CDATA[Ctrl-C]]></c> will
+ restart the shell process rather than interrupt it.</p>
+ <p>Notice that on Windows, this flag is only applicable for
+ <c><![CDATA[werl]]></c>, not <c><![CDATA[erl]]></c>
+ (<c><![CDATA[oldshell]]></c>). Notice also that
+ <c><![CDATA[Ctrl-Break]]></c> is used instead of
+ <c><![CDATA[Ctrl-C]]></c> on Windows.</p>
</item>
<tag><marker id="+c"/><c><![CDATA[+c true | false]]></c></tag>
<item>
- <p>Enable or disable
- <seealso marker="time_correction#Time_Correction">time correction</seealso>:</p>
+ <p>Enables or disables
+ <seealso marker="time_correction#Time_Correction">time
+ correction</seealso>:</p>
<taglist>
<tag><c>true</c></tag>
- <item><p>Enable time correction. This is the default if
- time correction is supported on the specific platform.</p></item>
-
- <tag><c>false</c></tag>
- <item><p>Disable time correction.</p></item>
- </taglist>
- <p>For backwards compatibility, the boolean value can be omitted.
- This is interpreted as <c>+c false</c>.
- </p>
- </item>
- <tag><marker id="+C_"/><c><![CDATA[+C no_time_warp | single_time_warp | multi_time_warp]]></c></tag>
- <item>
- <p>Set
- <seealso marker="time_correction#Time_Warp_Modes">time warp mode</seealso>:
- </p>
- <taglist>
- <tag><c>no_time_warp</c></tag>
- <item><p><seealso marker="time_correction#No_Time_Warp_Mode">No Time Warp Mode</seealso> (the default)</p></item>
- <tag><c>single_time_warp</c></tag>
- <item><p><seealso marker="time_correction#Single_Time_Warp_Mode">Single Time Warp Mode</seealso></p></item>
- <tag><c>multi_time_warp</c></tag>
- <item><p><seealso marker="time_correction#Multi_Time_Warp_Mode">Multi Time Warp Mode</seealso></p></item>
- </taglist>
+ <item>Enables time correction. This is the default if
+ time correction is supported on the specific platform.</item>
+ <tag><c>false</c></tag>
+ <item>Disables time correction.</item>
+ </taglist>
+ <p>For backward compatibility, the boolean value can be omitted.
+ This is interpreted as <c>+c false</c>.</p>
+ </item>
+ <tag><marker id="+C_"/><c><![CDATA[+C no_time_warp | single_time_warp |
+ multi_time_warp]]></c></tag>
+ <item>
+ <p>Sets <seealso marker="time_correction#Time_Warp_Modes">time warp
+ mode</seealso>:</p>
+ <taglist>
+ <tag><c>no_time_warp</c></tag>
+ <item><seealso marker="time_correction#No_Time_Warp_Mode">
+ No time warp mode</seealso> (the default)</item>
+ <tag><c>single_time_warp</c></tag>
+ <item><seealso marker="time_correction#Single_Time_Warp_Mode">
+ Single time warp mode</seealso></item>
+ <tag><c>multi_time_warp</c></tag>
+ <item><seealso marker="time_correction#Multi_Time_Warp_Mode">
+ Multi-time warp mode</seealso></item>
+ </taglist>
</item>
<tag><c><![CDATA[+d]]></c></tag>
<item>
<p>If the emulator detects an internal error (or runs out of memory),
- it will by default generate both a crash dump and a core dump.
- The core dump will, however, not be very useful since the content
- of process heaps is destroyed by the crash dump generation.</p>
-
- <p>The <c>+d</c> option instructs the emulator to only produce a
- core dump and no crash dump if an internal error is detected.</p>
-
- <p>Calling <c>erlang:halt/1</c> with a string argument will still
- produce a crash dump. On Unix systems, sending an emulator process
- a SIGUSR1 signal will also force a crash dump.</p>
+ it, by default, generates both a crash dump and a core dump.
+ The core dump is, however, not very useful as the content
+ of process heaps is destroyed by the crash dump generation.</p>
+ <p>Option <c>+d</c> instructs the emulator to produce only a
+ core dump and no crash dump if an internal error is detected.</p>
+ <p>Calling <seealso marker="erlang:halt/1">
+ <c>erlang:halt/1</c></seealso> with a string argument still
+ produces a crash dump. On Unix systems, sending an emulator process
+ a <c>SIGUSR1</c> signal also forces a crash dump.</p>
</item>
<tag><marker id="+e"/><c><![CDATA[+e Number]]></c></tag>
<item>
- <p>Set max number of ETS tables.</p>
+ <p>Sets the maximum number of ETS tables.</p>
</item>
<tag><c><![CDATA[+ec]]></c></tag>
<item>
- <p>Force the <c>compressed</c> option on all ETS tables.
- Only intended for test and evaluation.</p>
+ <p>Forces option <c>compressed</c> on all ETS tables.
+ Only intended for test and evaluation.</p>
</item>
- <tag><marker id="file_name_encoding"></marker><c><![CDATA[+fnl]]></c></tag>
+ <tag><marker id="file_name_encoding"></marker>
+ <c><![CDATA[+fnl]]></c></tag>
<item>
- <p>The VM works with file names as if they are encoded using the ISO-latin-1 encoding, disallowing Unicode characters with codepoints beyond 255.</p>
- <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see <seealso marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">STDLIB User's Guide</seealso>).</p>
+ <p>The virtual machine works with filenames as if they are encoded
+ using the ISO Latin-1 encoding, disallowing Unicode characters with
+ code points &gt; 255.</p>
+ <p>For more information about Unicode filenames, see section
+ <seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode
+ Filenames</seealso> in the STDLIB User's Guide. Notice that
+ this value also applies to command-line parameters and environment
+ variables (see section <seealso
+ marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">
+ Unicode in Enviroment and Parameters</seealso> in the STDLIB
+ User's Guide).</p>
</item>
<tag><c><![CDATA[+fnu[{w|i|e}]]]></c></tag>
<item>
- <p>The VM works with file names as if they are encoded using
- UTF-8 (or some other system specific Unicode encoding). This
- is the default on operating systems that enforce Unicode
- encoding, i.e. Windows and MacOS X.</p>
- <p>The <c>+fnu</c> switch can be followed by <c>w</c>,
- <c>i</c>, or <c>e</c> to control the way wrongly encoded file
- names are to be reported. <c>w</c> means that a warning is
- sent to the <c>error_logger</c> whenever a wrongly encoded
- file name is "skipped" in directory listings, <c>i</c> means
- that those wrongly encoded file names are silently ignored and
- <c>e</c> means that the API function will return an error
- whenever a wrongly encoded file (or directory) name is
- encountered. <c>w</c> is the default. Note that
- <c>file:read_link/1</c> will always return an error if the
- link points to an invalid file name.</p>
- <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see <seealso marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">STDLIB User's Guide</seealso>).</p>
+ <p>The virtual machine works with filenames as if they are encoded
+ using UTF-8 (or some other system-specific Unicode encoding). This is
+ the default on operating systems that enforce Unicode encoding, that
+ is, Windows and MacOS X.</p>
+ <p>The <c>+fnu</c> switch can be followed by <c>w</c>, <c>i</c>, or
+ <c>e</c> to control how wrongly encoded filenames are to be
+ reported:</p>
+ <list type="bulleted">
+ <item>
+ <p><c>w</c> means that a warning is sent to the <c>error_logger</c>
+ whenever a wrongly encoded filename is "skipped" in directory
+ listings. This is the default.</p>
+ </item>
+ <item>
+ <p><c>i</c> means that those wrongly encoded filenames are silently
+ ignored.</p>
+ </item>
+ <item>
+ <p><c>e</c> means that the API function returns an error whenever a
+ wrongly encoded filename (or directory name) is encountered.</p>
+ </item>
+ </list>
+ <p>Notice that <seealso marker="kernel:file#read_link/1">
+ <c>file:read_link/1</c></seealso> always returns an error if the link
+ points to an invalid filename.</p>
+ <p>For more information about Unicode filenames, see section
+ <seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode
+ Filenames</seealso> in the STDLIB User's Guide. Notice that
+ this value also applies to command-line parameters and environment
+ variables (see section <seealso
+ marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">
+ Unicode in Enviroment and Parameters</seealso> in the STDLIB
+ User's Guide).</p>
</item>
<tag><c><![CDATA[+fna[{w|i|e}]]]></c></tag>
<item>
<p>Selection between <c>+fnl</c> and <c>+fnu</c> is done based
- on the current locale settings in the OS, meaning that if you
- have set your terminal for UTF-8 encoding, the filesystem is
- expected to use the same encoding for file names. This is
- default on all operating systems except MacOS X and
- Windows.</p>
- <p>The <c>+fna</c> switch can be followed by <c>w</c>,
- <c>i</c>, or <c>e</c>. This will have effect if the locale
- settings cause the behavior of <c>+fnu</c> to be selected.
- See the description of <c>+fnu</c> above. If the locale
- settings cause the behavior of <c>+fnl</c> to be selected,
- then <c>w</c>, <c>i</c>, or <c>e</c> will not have any
- effect.</p>
- <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names. Note that this value also applies to command-line parameters and environment variables (see <seealso marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">STDLIB User's Guide</seealso>).</p>
+ on the current locale settings in the OS. This means that if you
+ have set your terminal for UTF-8 encoding, the filesystem is
+ expected to use the same encoding for filenames. This is
+ default on all operating systems, except MacOS X and Windows.</p>
+ <p>The <c>+fna</c> switch can be followed by <c>w</c>, <c>i</c>, or
+ <c>e</c>. This has effect if the locale settings cause the behavior
+ of <c>+fnu</c> to be selected; see the description of <c>+fnu</c>
+ above. If the locale settings cause the behavior of <c>+fnl</c> to be
+ selected, then <c>w</c>, <c>i</c>, or <c>e</c> have no effect.</p>
+ <p>For more information about Unicode filenames, see section
+ <seealso marker="stdlib:unicode_usage#unicode_file_names">Unicode
+ Filenames</seealso> in the STDLIB User's Guide. Notice that
+ this value also applies to command-line parameters and environment
+ variables (see section <seealso
+ marker="stdlib:unicode_usage#unicode_in_environment_and_parameters">
+ Unicode in Enviroment and Parameters</seealso> in the STDLIB
+ User's Guide).</p>
</item>
<tag><c><![CDATA[+hms Size]]></c></tag>
<item>
@@ -646,26 +711,24 @@
<tag><marker id="+hmax"/><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
+ <c><![CDATA[Size]]></c>. Defaults to <c>0</c>, which means that no
+ maximum heap size is used. For more information, see
<seealso marker="erlang#process_flag_max_heap_size">
- <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
+ <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
</item>
<tag><marker id="+hmaxel"/><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">
+ <p>Sets whether to send an error logger message or not for processes
+ reaching the maximum heap size. Defaults to <c>true</c>.
+ For more information, see
+ <seealso marker="erlang#process_flag_max_heap_size">
<c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
</item>
<tag><marker id="+hmaxk"/><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">
+ <p>Sets whether to kill processes reaching the maximum heap size or not.
+ Default to <c>true</c>. For more information, see
+ <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>
@@ -674,95 +737,62 @@
<c><![CDATA[Size]]></c>.</p>
</item>
<tag><marker id="+hmqd"/><c>+hmqd off_heap|on_heap</c></tag>
- <item><p>
- Sets the default value for the process flag
- <c>message_queue_data</c>. If <c>+hmqd</c> is not
- passed, <c>on_heap</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>
+ <item>
+ <p>Sets the default value for process flag <c>message_queue_data</c>.
+ Defaults to <c>on_heap</c>. If <c>+hmqd</c> is not
+ passed, <c>on_heap</c> will be the default. For more information, see
+ <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
- the emulator supports it. Default is <c><![CDATA[false]]></c> (disabled).
- If the emulator does not support kernel poll, and
- the <c><![CDATA[+K]]></c> flag is passed to the emulator, a warning is
+ <p>Enables or disables the kernel poll functionality if supported by
+ the emulator. Defaults to <c><![CDATA[false]]></c> (disabled).
+ If the emulator does not support kernel poll, and flag
+ <c><![CDATA[+K]]></c> is passed to the emulator, a warning is
issued at startup.</p>
</item>
<tag><c><![CDATA[+l]]></c></tag>
<item>
- <p>Enables auto load tracing, displaying info while loading
+ <p>Enables autoload tracing, displaying information while loading
code.</p>
</item>
<tag><c><![CDATA[+L]]></c></tag>
<item>
- <p>Don't load information about source file names and line numbers.
- This will save some memory, but exceptions will not contain
- information about the file names and line numbers.
- </p>
+ <p>Prevents loading information about source filenames and line
+ numbers. This saves some memory, but exceptions do not contain
+ information about the filenames and line numbers.</p>
</item>
<tag><marker id="erts_alloc"/><c><![CDATA[+MFlag Value]]></c></tag>
<item>
- <p>Memory allocator specific flags, see
- <seealso marker="erts_alloc">erts_alloc(3)</seealso> for
- further information.</p>
- </item>
- <tag><marker id="+n"/><c><![CDATA[+n Behavior]]></c></tag>
- <item>
- <p>Control behavior of signals to ports.</p>
- <p>As of OTP-R16 signals to ports are truly asynchronously
- delivered. Note that signals always have been documented as
- asynchronous. The underlying implementation has, however,
- previously delivered these signals synchronously. Correctly
- written Erlang programs should be able to handle this without
- any issues. Bugs in existing Erlang programs that make false
- assumptions about signals to ports may, however, be tricky to
- find. This switch has been introduced in order to at least
- make it easier to compare behaviors during a transition period.
- Note that <em>this flag is deprecated</em> as of its
- introduction, and is scheduled for removal in OTP-R17.
- <c>Behavior</c> should be one of the following characters:</p>
- <taglist>
- <tag><c>d</c></tag>
- <item>The default. Asynchronous signals. A process that sends
- a signal to a port may continue execution before the signal
- has been delivered to the port.</item>
- <tag><c>s</c></tag>
- <item>Synchronous signals. A processes that sends a signal
- to a port will not continue execution until the signal has
- been delivered. Should <em>only</em> be used for testing and
- debugging.</item>
- <tag><c>a</c></tag>
- <item>Asynchronous signals. As the default, but a processes
- that sends a signal will even more frequently continue
- execution before the signal has been delivered to the
- port. Should <em>only</em> be used for testing and
- debugging.</item>
- </taglist>
- </item>
- <tag><marker id="+pc"/><marker id="printable_character_range"/><c><![CDATA[+pc Range]]></c></tag>
- <item>
- <p>Sets the range of characters that the system will consider printable in heuristic detection of strings. This typically affects the shell, debugger and io:format functions (when ~tp is used in the format string).</p>
- <p>Currently two values for the <c>Range</c> are supported:</p>
- <taglist>
- <tag><c>latin1</c></tag> <item>The default. Only characters
- in the ISO-latin-1 range can be considered printable, which means
- that a character with a code point &gt; 255 will never be
- considered printable and that lists containing such
- characters will be displayed as lists of integers rather
- than text strings by tools.</item>
- <tag><c>unicode</c></tag>
- <item>All printable Unicode characters are considered when
- determining if a list of integers is to be displayed in
- string syntax. This may give unexpected results if for
- example your font does not cover all Unicode
- characters.</item>
- </taglist>
- <p>Se also <seealso marker="stdlib:io#printable_range/0">
- io:printable_range/0</seealso>.</p>
- </item>
- <tag><marker id="+P"/><marker id="max_processes"/><c><![CDATA[+P Number|legacy]]></c></tag>
+ <p>Memory allocator-specific flags. For more information, see
+ <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>.</p>
+ </item>
+ <tag><marker id="+pc"/><marker id="printable_character_range"/>
+ <c><![CDATA[+pc Range]]></c></tag>
+ <item>
+ <p>Sets the range of characters that the system considers printable in
+ heuristic detection of strings. This typically affects the shell,
+ debugger, and <c>io:format</c> functions (when <c>~tp</c> is used in
+ the format string).</p>
+ <p>Two values are supported for <c>Range</c>:</p>
+ <taglist>
+ <tag><c>latin1</c></tag>
+ <item>The default. Only characters in the ISO Latin-1 range can be
+ considered printable. This means that a character with a code point
+ &gt; 255 is never considered printable and that lists containing
+ such characters are displayed as lists of integers rather than text
+ strings by tools.</item>
+ <tag><c>unicode</c></tag>
+ <item>All printable Unicode characters are considered when
+ determining if a list of integers is to be displayed in
+ string syntax. This can give unexpected results if, for
+ example, your font does not cover all Unicode characters.</item>
+ </taglist>
+ <p>See also <seealso marker="stdlib:io#printable_range/0">
+ <c>io:printable_range/0</c></seealso> in STDLIB.</p>
+ </item>
+ <tag><marker id="+P"/><marker id="max_processes"/><c><![CDATA[+P Number]]></c></tag>
<item>
<p>Sets the maximum number of simultaneously existing processes for this
system if a <c>Number</c> is passed as value. Valid range for
@@ -774,15 +804,8 @@
checked by calling
<seealso marker="erlang#system_info_process_limit">erlang:system_info(process_limit)</seealso>.</p>
<p>The default value is <c>262144</c></p>
- <p>If <c>legacy</c> is passed as value, the legacy algorithm for
- allocation of process identifiers will be used. Using the legacy
- algorithm, identifiers will be allocated in a strictly increasing
- fashion until largest possible identifier has been reached. Note that
- this algorithm suffers from performance issues and can under certain
- circumstances be extremely expensive. The legacy algoritm is deprecated,
- and the <c>legacy</c> option is scheduled for removal in OTP-R18.</p>
</item>
- <tag><marker id="+Q"/><marker id="max_ports"/><c><![CDATA[+Q Number|legacy]]></c></tag>
+ <tag><marker id="+Q"/><marker id="max_ports"/><c><![CDATA[+Q Number]]></c></tag>
<item>
<p>Sets the maximum number of simultaneously existing ports for this
system if a Number is passed as value. Valid range for <c>Number</c>
@@ -799,179 +822,179 @@
than <c>65536</c>, the chosen value will increased to a value
larger or equal to the maximum amount of file descriptors that
can be opened.</p>
- <p>On Windows the default value is set to <c>8196</c> because the
+ <p>On Windows the default value is set to <c>8196</c> because the
normal OS limitations are set higher than most machines can handle.</p>
- <p>Previously the environment variable <c>ERL_MAX_PORTS</c> was used
- for setting the maximum number of simultaneously existing ports. This
- environment variable is deprecated, and scheduled for removal in
- OTP-R17, but can still be used.</p>
- <p>If <c>legacy</c> is passed as value, the legacy algorithm for
- allocation of port identifiers will be used. Using the legacy
- algorithm, identifiers will be allocated in a strictly increasing
- fashion until largest possible identifier has been reached. Note that
- this algorithm suffers from performance issues and can under certain
- circumstances be extremely expensive. The legacy algoritm is deprecated,
- and the <c>legacy</c> option is scheduled for removal in OTP-R18.</p>
</item>
<tag><marker id="compat_rel"/><c><![CDATA[+R ReleaseNumber]]></c></tag>
<item>
<p>Sets the compatibility mode.</p>
- <p>The distribution mechanism is not backwards compatible by
- default. This flags sets the emulator in compatibility mode
+ <p>The distribution mechanism is not backward compatible by
+ default. This flag sets the emulator in compatibility mode
with an earlier Erlang/OTP release <c><![CDATA[ReleaseNumber]]></c>.
The release number must be in the range
<c><![CDATA[<current release>-2..<current release>]]></c>. This
- limits the emulator, making it possible for it to communicate
- with Erlang nodes (as well as C- and Java nodes) running that
- earlier release.</p>
- <p>Note: Make sure all nodes (Erlang-, C-, and Java nodes) of
- a distributed Erlang system is of the same Erlang/OTP release,
- or from two different Erlang/OTP releases X and Y, where
- <em>all</em> Y nodes have compatibility mode X.</p>
+ limits the emulator, making it possible for it to communicate
+ with Erlang nodes (as well as C- and Java nodes) running that
+ earlier release.</p>
+ <note>
+ <p>Ensure that all nodes (Erlang-, C-, and Java nodes) of
+ a distributed Erlang system is of the same Erlang/OTP release,
+ or from two different Erlang/OTP releases X and Y, where
+ <em>all</em> Y nodes have compatibility mode X.</p>
+ </note>
</item>
<tag><c><![CDATA[+r]]></c></tag>
<item>
- <p>Force ets memory block to be moved on realloc.</p>
+ <p>Forces ETS memory block to be moved on realloc.</p>
</item>
<tag><marker id="+rg"/><c><![CDATA[+rg ReaderGroupsLimit]]></c></tag>
<item>
- <p>Limits the amount of reader groups used by read/write locks
- optimized for read operations in the Erlang runtime system. By
- default the reader groups limit equals 64.</p>
- <p>When the amount of schedulers is less than or equal to the reader
- groups limit, each scheduler has its own reader group. When the
- amount of schedulers is larger than the reader groups limit,
- schedulers share reader groups. Shared reader groups degrades
- read lock and read unlock performance while a large amount of
- reader groups degrades write lock performance, so the limit is a
- tradeoff between performance for read operations and performance
- for write operations. Each reader group currently consumes 64 byte
- in each read/write lock. Also note that a runtime system using
- shared reader groups benefits from <seealso marker="#+sbt">binding
- schedulers to logical processors</seealso>, since the reader groups
- are distributed better between schedulers.</p>
- </item>
- <tag><marker id="+S"/><c><![CDATA[+S Schedulers:SchedulerOnline]]></c></tag>
- <item>
- <p>Sets the number of scheduler threads to create and scheduler
- threads to set online when SMP support has been enabled. The maximum for
- both values is 1024. If the Erlang runtime system is able to determine the
- amount of logical processors configured and logical processors available,
- <c>Schedulers</c> will default to logical processors configured, and
- <c>SchedulersOnline</c> will default to logical processors available;
- otherwise, the default values will be 1. <c>Schedulers</c> may be omitted
- if <c>:SchedulerOnline</c> is not and vice versa. The number of schedulers
- online can be changed at run time via
- <seealso marker="erlang#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.
- </p>
- <p>If <c>Schedulers</c> or <c>SchedulersOnline</c> is specified as a
- negative number, the value is subtracted from the default number of
- logical processors configured or logical processors available, respectively.
- </p>
- <p>Specifying the value 0 for <c>Schedulers</c> or <c>SchedulersOnline</c>
- resets the number of scheduler threads or scheduler threads online respectively
- to its default value.
- </p>
- <p>This option is ignored if the emulator doesn't have
- SMP support enabled (see the <seealso marker="#smp">-smp</seealso>
- flag).</p>
- </item>
- <tag><marker id="+SP"/><c><![CDATA[+SP SchedulersPercentage:SchedulersOnlinePercentage]]></c></tag>
- <item>
- <p>Similar to <seealso marker="#+S">+S</seealso> but uses percentages to set the
- number of scheduler threads to create, based on logical processors configured,
- and scheduler threads to set online, based on logical processors available, when
- SMP support has been enabled. Specified values must be greater than 0. For example,
- <c>+SP 50:25</c> sets the number of scheduler threads to 50% of the logical processors
- configured and the number of scheduler threads online to 25% of the logical processors available.
- <c>SchedulersPercentage</c> may be omitted if <c>:SchedulersOnlinePercentage</c> is
- not and vice versa. The number of schedulers online can be changed at run time via
- <seealso marker="erlang#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.
- </p>
- <p>This option interacts with <seealso marker="#+S">+S</seealso> settings.
- For example, on a system with 8 logical cores configured and 8 logical cores
- available, the combination of the options <c>+S 4:4 +SP 50:25</c> (in either order)
- results in 2 scheduler threads (50% of 4) and 1 scheduler thread online (25% of 4).
- </p>
- <p>This option is ignored if the emulator doesn't have
- SMP support enabled (see the <seealso marker="#smp">-smp</seealso>
- flag).</p>
- </item>
- <tag><marker id="+SDcpu"/><c><![CDATA[+SDcpu DirtyCPUSchedulers:DirtyCPUSchedulersOnline]]></c></tag>
- <item>
- <p>Sets the number of dirty CPU scheduler threads to create and dirty
- CPU scheduler threads to set online when threading support has been
- enabled. The maximum for both values is 1024, and each value is further
- limited by the settings for normal schedulers: the number of dirty CPU
- scheduler threads created cannot exceed the number of normal scheduler
- threads created, and the number of dirty CPU scheduler threads online
- cannot exceed the number of normal scheduler threads online (see the
- <seealso marker="#+S">+S</seealso> and <seealso marker="#+SP">+SP</seealso>
- flags for more details). By default, the number of dirty CPU scheduler
- threads created equals the number of normal scheduler threads created,
- and the number of dirty CPU scheduler threads online equals the number
- of normal scheduler threads online. <c>DirtyCPUSchedulers</c> may be
- omitted if <c>:DirtyCPUSchedulersOnline</c> is not and vice versa. The
- number of dirty CPU schedulers online can be changed at run time via
- <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.
- </p>
- <p>
- The amount of dirty CPU schedulers is limited by the amount of
- normal schedulers in order to limit the effect on processes
- executing on ordinary schedulers. If the amount of dirty CPU
- schedulers was allowed to be unlimited, dirty CPU bound jobs would
- potentially starve normal jobs.
- </p>
- <p>This option is ignored if the emulator doesn't have threading support
- enabled. Currently, <em>this option is experimental</em> and is supported only
- if the emulator was configured and built with support for dirty schedulers
- enabled (it's disabled by default).
- </p>
- </item>
- <tag><marker id="+SDPcpu"/><c><![CDATA[+SDPcpu DirtyCPUSchedulersPercentage:DirtyCPUSchedulersOnlinePercentage]]></c></tag>
- <item>
- <p>Similar to <seealso marker="#+SDcpu">+SDcpu</seealso> but uses percentages to set the
- number of dirty CPU scheduler threads to create and number of dirty CPU scheduler threads
- to set online when threading support has been enabled. Specified values must be greater
- than 0. For example, <c>+SDPcpu 50:25</c> sets the number of dirty CPU scheduler threads
- to 50% of the logical processors configured and the number of dirty CPU scheduler threads
- online to 25% of the logical processors available. <c>DirtyCPUSchedulersPercentage</c> may
- be omitted if <c>:DirtyCPUSchedulersOnlinePercentage</c> is not and vice versa. The
- number of dirty CPU schedulers online can be changed at run time via
- <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.
- </p>
- <p>This option interacts with <seealso marker="#+SDcpu">+SDcpu</seealso> settings.
- For example, on a system with 8 logical cores configured and 8 logical cores available,
- the combination of the options <c>+SDcpu 4:4 +SDPcpu 50:25</c> (in either order) results
- in 2 dirty CPU scheduler threads (50% of 4) and 1 dirty CPU scheduler thread online (25% of 4).
- </p>
- <p>This option is ignored if the emulator doesn't have threading support
- enabled. Currently, <em>this option is experimental</em> and is supported only
- if the emulator was configured and built with support for dirty schedulers
- enabled (it's disabled by default).
- </p>
+ <p>Limits the number of reader groups used by read/write locks
+ optimized for read operations in the Erlang runtime system. By
+ default the reader groups limit is 64.</p>
+ <p>When the number of schedulers is less than or equal to the reader
+ groups limit, each scheduler has its own reader group. When the
+ number of schedulers is larger than the reader groups limit,
+ schedulers share reader groups. Shared reader groups degrade
+ read lock and read unlock performance while many
+ reader groups degrade write lock performance. So, the limit is a
+ tradeoff between performance for read operations and performance
+ for write operations. Each reader group consumes 64 byte
+ in each read/write lock.</p>
+ <p>Notice that a runtime system using shared reader groups benefits from
+ <seealso marker="#+sbt">binding schedulers to logical
+ processors</seealso>, as the reader groups are distributed better
+ between schedulers.</p>
+ </item>
+ <tag><marker id="+S"/>
+ <c><![CDATA[+S Schedulers:SchedulerOnline]]></c></tag>
+ <item>
+ <p>Sets the number of scheduler threads to create and scheduler threads
+ to set online when SMP support has been enabled. The maximum for both
+ values is 1024. If the Erlang runtime system is able to determine the
+ number of logical processors configured and logical processors
+ available, <c>Schedulers</c> defaults to logical processors
+ configured, and <c>SchedulersOnline</c> defaults to logical processors
+ available; otherwise the default values are 1. <c>Schedulers</c> can
+ be omitted if <c>:SchedulerOnline</c> is not and conversely. The
+ number of schedulers online can be changed at runtime through
+ <seealso marker="erlang#system_flag_schedulers_online">
+ <c>erlang:system_flag(schedulers_online,
+ SchedulersOnline)</c></seealso>.</p>
+ <p>If <c>Schedulers</c> or <c>SchedulersOnline</c> is specified as a
+ negative number, the value is subtracted from the default number of
+ logical processors configured or logical processors available,
+ respectively.</p>
+ <p>Specifying value <c>0</c> for <c>Schedulers</c> or
+ <c>SchedulersOnline</c> resets the number of scheduler threads or
+ scheduler threads online, respectively, to its default value.</p>
+ <p>This option is ignored if the emulator does not have SMP support
+ enabled (see flag <seealso marker="#smp"><c>-smp</c></seealso>).</p>
+ </item>
+ <tag><marker id="+SP"/><c><![CDATA[+SP
+ SchedulersPercentage:SchedulersOnlinePercentage]]></c></tag>
+ <item>
+ <p>Similar to <seealso marker="#+S"><c>+S</c></seealso> but uses
+ percentages to set the number of scheduler threads to create, based
+ on logical processors configured, and scheduler threads to set online,
+ based on logical processors available, when SMP support has been
+ enabled. Specified values must be &gt; 0. For example,
+ <c>+SP 50:25</c> sets the number of scheduler threads to 50% of the
+ logical processors configured, and the number of scheduler threads
+ online to 25% of the logical processors available.
+ <c>SchedulersPercentage</c> can be omitted if
+ <c>:SchedulersOnlinePercentage</c> is not and conversely. The number
+ of schedulers online can be changed at runtime through
+ <seealso marker="erlang#system_flag_schedulers_online">
+ <c>erlang:system_flag(schedulers_online,
+ SchedulersOnline)</c></seealso>.</p>
+ <p>This option interacts with <seealso marker="#+S"><c>+S</c></seealso>
+ settings. For example, on a system with 8 logical cores configured
+ and 8 logical cores available, the combination of the options
+ <c>+S 4:4 +SP 50:25</c> (in either order) results in 2 scheduler
+ threads (50% of 4) and 1 scheduler thread online (25% of 4).</p>
+ <p>This option is ignored if the emulator does not have SMP support
+ enabled (see flag <seealso marker="#smp"><c>-smp</c></seealso>).</p>
+ </item>
+ <tag><marker id="+SDcpu"/><c><![CDATA[+SDcpu
+ DirtyCPUSchedulers:DirtyCPUSchedulersOnline]]></c></tag>
+ <item>
+ <p>Sets the number of dirty CPU scheduler threads to create and dirty
+ CPU scheduler threads to set online when threading support has been
+ enabled. The maximum for both values is 1024, and each value is
+ further limited by the settings for normal schedulers:</p>
+ <list type="bulleted">
+ <item>The number of dirty CPU scheduler threads created cannot exceed
+ the number of normal scheduler threads created.</item>
+ <item>The number of dirty CPU scheduler threads online cannot exceed
+ the number of normal scheduler threads online.</item>
+ </list>
+ <p>For details, see the <seealso marker="#+S"><c>+S</c></seealso> and
+ <seealso marker="#+SP"><c>+SP</c></seealso>. By default, the number
+ of dirty CPU scheduler threads created equals the number of normal
+ scheduler threads created, and the number of dirty CPU scheduler
+ threads online equals the number of normal scheduler threads online.
+ <c>DirtyCPUSchedulers</c> can be omitted if
+ <c>:DirtyCPUSchedulersOnline</c> is not and conversely. The number of
+ dirty CPU schedulers online can be changed at runtime through
+ <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">
+ <c>erlang:system_flag(dirty_cpu_schedulers_online,
+ DirtyCPUSchedulersOnline)</c></seealso>.</p>
+ <p>The amount of dirty CPU schedulers is limited by the amount of
+ normal schedulers in order to limit the effect on processes
+ executing on ordinary schedulers. If the amount of dirty CPU
+ schedulers was allowed to be unlimited, dirty CPU bound jobs would
+ potentially starve normal jobs.</p>
+ <p>This option is ignored if the emulator does not have threading
+ support enabled. <em>This option is experimental</em> and
+ is supported only if the emulator was configured and built with
+ support for dirty schedulers enabled (it is disabled by default).</p>
+ </item>
+ <tag><marker id="+SDPcpu"/><c><![CDATA[+SDPcpu
+ DirtyCPUSchedulersPercentage:DirtyCPUSchedulersOnlinePercentage]]></c></tag>
+ <item>
+ <p>Similar to <seealso marker="#+SDcpu"><c>+SDcpu</c></seealso> but
+ uses percentages to set the number of dirty CPU scheduler threads to
+ create and the number of dirty CPU scheduler threads to set online
+ when threading support has been enabled. Specified values must be
+ &gt; 0. For example, <c>+SDPcpu 50:25</c> sets the number of dirty
+ CPU scheduler threads to 50% of the logical processors configured
+ and the number of dirty CPU scheduler threads online to 25% of the
+ logical processors available. <c>DirtyCPUSchedulersPercentage</c> can
+ be omitted if <c>:DirtyCPUSchedulersOnlinePercentage</c> is not and
+ conversely. The number of dirty CPU schedulers online can be changed
+ at runtime through
+ <seealso marker="erlang#system_flag_dirty_cpu_schedulers_online">
+ <c>erlang:system_flag(dirty_cpu_schedulers_online,
+ DirtyCPUSchedulersOnline)</c></seealso>.</p>
+ <p>This option interacts with <seealso
+ marker="#+SDcpu"><c>+SDcpu</c></seealso> settings. For example, on a
+ system with 8 logical cores configured and 8 logical cores available,
+ the combination of the options <c>+SDcpu 4:4 +SDPcpu 50:25</c> (in
+ either order) results in 2 dirty CPU scheduler threads (50% of 4) and
+ 1 dirty CPU scheduler thread online (25% of 4).</p>
+ <p>This option is ignored if the emulator does not have threading
+ support enabled. <em>This option is experimental</em> and
+ is supported only if the emulator was configured and built with
+ support for dirty schedulers enabled (it is disabled by default).</p>
</item>
<tag><marker id="+SDio"/><c><![CDATA[+SDio DirtyIOSchedulers]]></c></tag>
<item>
- <p>Sets the number of dirty I/O scheduler threads to create when threading
- support has been enabled. The valid range is 0-1024. By default, the number
- of dirty I/O scheduler threads created is 10, same as the default number of
- threads in the <seealso marker="#async_thread_pool_size">async thread pool
- </seealso>.
- </p>
- <p>
- The amount of dirty IO schedulers is not limited by the amount of
- normal schedulers <seealso marker="#+SDcpu">like the amount of
- dirty CPU schedulers</seealso>. This since only I/O bound work is
- expected to execute on dirty I/O schedulers. If the user should schedule CPU
- bound jobs on dirty I/O schedulers, these jobs might starve ordinary
- jobs executing on ordinary schedulers.
- </p>
- <p>This option is ignored if the emulator doesn't have threading support
- enabled. Currently, <em>this option is experimental</em> and is supported only
- if the emulator was configured and built with support for dirty schedulers
- enabled (it's disabled by default).
- </p>
+ <p>Sets the number of dirty I/O scheduler threads to create when
+ threading support has been enabled. Valid range is 0-1024. By
+ default, the number of dirty I/O scheduler threads created is 10,
+ same as the default number of threads in the <seealso
+ marker="#async_thread_pool_size">async thread pool</seealso>.</p>
+ <p>The amount of dirty IO schedulers is not limited by the amount of
+ normal schedulers <seealso marker="#+SDcpu">like the amount of
+ dirty CPU schedulers</seealso>. This since only I/O bound work is
+ expected to execute on dirty I/O schedulers. If the user should schedule CPU
+ bound jobs on dirty I/O schedulers, these jobs might starve ordinary
+ jobs executing on ordinary schedulers.</p>
+ <p>This option is ignored if the emulator does not have threading
+ support enabled. <em>This option is experimental</em> and
+ is supported only if the emulator was configured and built with
+ support for dirty schedulers enabled (it is disabled by default).</p>
</item>
<tag><c><![CDATA[+sFlag Value]]></c></tag>
<item>
@@ -979,238 +1002,237 @@
<taglist>
<tag><marker id="+sbt"/><c>+sbt BindType</c></tag>
<item>
- <p>Set scheduler bind type.</p>
- <p>Schedulers can also be bound using the
- <seealso marker="#+stbt">+stbt</seealso> flag. The only difference
- between these two flags is how the following errors are handled:</p>
- <list>
- <item>Binding of schedulers is not supported on the specific
- platform.</item>
- <item>No available CPU topology. That is the runtime system
- was not able to automatically detected the CPU topology, and
- no <seealso marker="#+sct">user defined CPU topology</seealso>
- was set.</item>
- </list>
- <p>If any of these errors occur when <c>+sbt</c> has been passed,
- the runtime system will print an error message, and refuse to
- start. If any of these errors occur when <c>+stbt</c> has been
- passed, the runtime system will silently ignore the error, and
- start up using unbound schedulers.</p>
- <p>Currently valid <c>BindType</c>s:
- </p>
- <taglist>
- <tag><c>u</c></tag>
- <item>
- <p><c>unbound</c> - Schedulers will not be bound to logical
- processors, i.e., the operating system decides where the
- scheduler threads execute, and when to migrate them. This is
- the default.</p>
+ <p>Sets scheduler bind type.</p>
+ <p>Schedulers can also be bound using flag
+ <seealso marker="#+stbt"><c>+stbt</c></seealso>. The only
+ difference between these two flags is how the following errors
+ are handled:</p>
+ <list type="bulleted">
+ <item>Binding of schedulers is not supported on the specific
+ platform.</item>
+ <item>No available CPU topology. That is, the runtime system was
+ not able to detect the CPU topology automatically, and no
+ <seealso marker="#+sct">user-defined CPU topology</seealso>
+ was set.</item>
+ </list>
+ <p>If any of these errors occur when <c>+sbt</c> has been passed,
+ the runtime system prints an error message, and refuses to
+ start. If any of these errors occur when <c>+stbt</c> has been
+ passed, the runtime system silently ignores the error, and
+ start up using unbound schedulers.</p>
+ <p>Valid <c>BindType</c>s:</p>
+ <taglist>
+ <tag><c>u</c></tag>
+ <item><c>unbound</c> - Schedulers are not bound to logical
+ processors, that is, the operating system decides where the
+ scheduler threads execute, and when to migrate them. This is
+ the default.
</item>
- <tag><c>ns</c></tag>
- <item>
- <p><c>no_spread</c> - Schedulers with close scheduler
- identifiers will be bound as close as possible in hardware.</p>
+ <tag><c>ns</c></tag>
+ <item><c>no_spread</c> - Schedulers with close scheduler
+ identifiers are bound as close as possible in hardware.
</item>
- <tag><c>ts</c></tag>
- <item>
- <p><c>thread_spread</c> - Thread refers to hardware threads
- (e.g. Intel's hyper-threads). Schedulers with low scheduler
- identifiers, will be bound to the first hardware thread of
- each core, then schedulers with higher scheduler identifiers
- will be bound to the second hardware thread of each core,
- etc.</p>
+ <tag><c>ts</c></tag>
+ <item><c>thread_spread</c> - Thread refers to hardware threads
+ (such as Intel's hyper-threads). Schedulers with low scheduler
+ identifiers, are bound to the first hardware thread of
+ each core, then schedulers with higher scheduler identifiers
+ are bound to the second hardware thread of each core,and so on.
</item>
- <tag><c>ps</c></tag>
- <item>
- <p><c>processor_spread</c> - Schedulers will be spread like
- <c>thread_spread</c>, but also over physical processor chips.</p>
+ <tag><c>ps</c></tag>
+ <item><c>processor_spread</c> - Schedulers are spread like
+ <c>thread_spread</c>, but also over physical processor chips.
</item>
- <tag><c>s</c></tag>
- <item>
- <p><c>spread</c> - Schedulers will be spread as much as
- possible.</p>
+ <tag><c>s</c></tag>
+ <item><c>spread</c> - Schedulers are spread as much as possible.
</item>
- <tag><c>nnts</c></tag>
- <item>
- <p><c>no_node_thread_spread</c> - Like <c>thread_spread</c>,
- but if multiple NUMA (Non-Uniform Memory Access) nodes exists,
- schedulers will be spread over one NUMA node at a time,
- i.e., all logical processors of one NUMA node will be bound
- to schedulers in sequence.</p>
+ <tag><c>nnts</c></tag>
+ <item><c>no_node_thread_spread</c> - Like <c>thread_spread</c>,
+ but if multiple Non-Uniform Memory Access (NUMA) nodes exist,
+ schedulers are spread over one NUMA node at a time,
+ that is, all logical processors of one NUMA node are bound
+ to schedulers in sequence.
</item>
- <tag><c>nnps</c></tag>
- <item>
- <p><c>no_node_processor_spread</c> - Like
- <c>processor_spread</c>, but if multiple NUMA nodes exists,
- schedulers will be spread over one NUMA node at a time, i.e.,
- all logical processors of one NUMA node will be bound to
- schedulers in sequence.</p>
+ <tag><c>nnps</c></tag>
+ <item><c>no_node_processor_spread</c> - Like
+ <c>processor_spread</c>, but if multiple NUMA nodes exist,
+ schedulers are spread over one NUMA node at a time, that is,
+ all logical processors of one NUMA node are bound to
+ schedulers in sequence.
</item>
- <tag><c>tnnps</c></tag>
- <item>
- <p><c>thread_no_node_processor_spread</c> - A combination of
- <c>thread_spread</c>, and <c>no_node_processor_spread</c>.
- Schedulers will be spread over hardware threads across NUMA
- nodes, but schedulers will only be spread over processors
- internally in one NUMA node at a time.</p>
+ <tag><c>tnnps</c></tag>
+ <item><c>thread_no_node_processor_spread</c> - A combination of
+ <c>thread_spread</c>, and <c>no_node_processor_spread</c>.
+ Schedulers are spread over hardware threads across NUMA
+ nodes, but schedulers are only spread over processors
+ internally in one NUMA node at a time.
</item>
- <tag><c>db</c></tag>
- <item>
- <p><c>default_bind</c> - Binds schedulers the default way.
- Currently the default is <c>thread_no_node_processor_spread</c>
- (which might change in the future).</p>
+ <tag><c>db</c></tag>
+ <item><c>default_bind</c> - Binds schedulers the default way.
+ Defaults to <c>thread_no_node_processor_spread</c>
+ (which can change in the future).
</item>
- </taglist>
- <p>Binding of schedulers is currently only supported on newer
- Linux, Solaris, FreeBSD, and Windows systems.</p>
- <p>If no CPU topology is available when the <c>+sbt</c> flag
- is processed and <c>BindType</c> is any other type than
- <c>u</c>, the runtime system will fail to start. CPU
- topology can be defined using the
- <seealso marker="#+sct">+sct</seealso> flag. Note
- that the <c>+sct</c> flag may have to be passed before the
- <c>+sbt</c> flag on the command line (in case no CPU topology
- has been automatically detected).</p>
- <p>The runtime system will by default <em>not</em> bind schedulers
- to logical processors.
- </p>
- <p><em>NOTE:</em> If the Erlang runtime system is the only operating system
- process that binds threads to logical processors, this
- improves the performance of the runtime system. However,
- if other operating system processes (as for example
- another Erlang runtime system) also bind threads to
- logical processors, there might be a performance penalty
- instead. In some cases this performance penalty might be
- severe. If this is the case, you are advised to not
- bind the schedulers.</p>
+ </taglist>
+ <p>Binding of schedulers is only supported on newer
+ Linux, Solaris, FreeBSD, and Windows systems.</p>
+ <p>If no CPU topology is available when flag <c>+sbt</c>
+ is processed and <c>BindType</c> is any other type than
+ <c>u</c>, the runtime system fails to start. CPU
+ topology can be defined using flag
+ <seealso marker="#+sct"><c>+sct</c></seealso>. Notice
+ that flag <c>+sct</c> can have to be passed before flag
+ <c>+sbt</c> on the command line (if no CPU topology
+ has been automatically detected).</p>
+ <p>The runtime system does by default <em>not</em> bind schedulers
+ to logical processors.</p>
+ <note>
+ <p>If the Erlang runtime system is the only operating system
+ process that binds threads to logical processors, this
+ improves the performance of the runtime system. However,
+ if other operating system processes (for example
+ another Erlang runtime system) also bind threads to
+ logical processors, there can be a performance penalty
+ instead. This performance penalty can sometimes be
+ severe. If so, you are advised not to
+ bind the schedulers.</p>
+ </note>
<p>How schedulers are bound matters. For example, in
- situations when there are fewer running processes than
- schedulers online, the runtime system tries to migrate
- processes to schedulers with low scheduler identifiers.
- The more the schedulers are spread over the hardware,
- the more resources will be available to the runtime
- system in such situations.
- </p>
- <p>
- <em>NOTE:</em> If a scheduler fails to bind, this
- will often be silently ignored. This since it isn't always
- possible to verify valid logical processor identifiers. If
- an error is reported, it will be reported to the
- <c>error_logger</c>. If you want to verify that the
- schedulers actually have bound as requested, call
- <seealso marker="erlang#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.
- </p>
+ situations when there are fewer running processes than
+ schedulers online, the runtime system tries to migrate
+ processes to schedulers with low scheduler identifiers.
+ The more the schedulers are spread over the hardware,
+ the more resources are available to the runtime
+ system in such situations.</p>
+ <note>
+ <p>If a scheduler fails to bind, this is
+ often silently ignored, as it is not always
+ possible to verify valid logical processor identifiers. If
+ an error is reported, it is reported to the
+ <c>error_logger</c>. If you want to verify that the
+ schedulers have bound as requested, call
+ <seealso marker="erlang#system_info_scheduler_bindings">
+ <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p>
+ </note>
</item>
- <tag><marker id="+sbwt"/><c>+sbwt none|very_short|short|medium|long|very_long</c></tag>
- <item>
- <p>Set scheduler busy wait threshold. Default is <c>medium</c>.
- The threshold determines how long schedulers should busy
- wait when running out of work before going to sleep.
- </p>
- <p><em>NOTE:</em> This flag may be removed or changed at any time
- without prior notice.
- </p>
- </item>
- <tag><marker id="+scl"/><c>+scl true|false</c></tag>
+ <tag><marker id="+sbwt"/>
+ <c>+sbwt none|very_short|short|medium|long|very_long</c></tag>
<item>
- <p>Enable or disable scheduler compaction of load. By default
- scheduler compaction of load is enabled. When enabled, load
- balancing will strive for a load distribution which causes
- as many scheduler threads as possible to be fully loaded (i.e.,
- not run out of work). This is accomplished by migrating load
- (e.g. runnable processes) into a smaller set of schedulers
- when schedulers frequently run out of work. When disabled,
- the frequency with which schedulers run out of work will
- not be taken into account by the load balancing logic.
- <br/>&nbsp;&nbsp;<c>+scl false</c> is similar to
- <seealso marker="#+sub">+sub true</seealso> with the difference
- that <c>+sub true</c> also will balance scheduler utilization
- between schedulers.
- </p>
+ <p>Sets scheduler busy wait threshold. Defaults to <c>medium</c>.
+ The threshold determines how long schedulers are to busy
+ wait when running out of work before going to sleep.</p>
+ <note>
+ <p>This flag can be removed or changed at any time
+ without prior notice.</p>
+ </note>
+ </item>
+<tag><marker id="+scl"/><c>+scl true|false</c></tag>
+ <item>
+ <p>Enables or disables scheduler compaction of load. By default
+ scheduler compaction of load is enabled. When enabled, load
+ balancing strives for a load distribution, which causes
+ as many scheduler threads as possible to be fully loaded (that is,
+ not run out of work). This is accomplished by migrating load
+ (for example, runnable processes) into a smaller set of schedulers
+ when schedulers frequently run out of work. When disabled,
+ the frequency with which schedulers run out of work is
+ not taken into account by the load balancing logic.</p>
+ <p><c>+scl false</c> is similar to
+ <seealso marker="#+sub"><c>+sub true</c></seealso>, but
+ <c>+sub true</c> also balances scheduler utilization
+ between schedulers.</p>
</item>
<tag><marker id="+sct"/><c>+sct CpuTopology</c></tag>
<item>
<list type="bulleted">
- <item><c><![CDATA[<Id> = integer(); when 0 =< <Id> =< 65535]]></c></item>
+ <item><c><![CDATA[<Id> = integer(); when 0 =< <Id> =< 65535]]></c>
+ </item>
<item><c><![CDATA[<IdRange> = <Id>-<Id>]]></c></item>
<item><c><![CDATA[<IdOrIdRange> = <Id> | <IdRange>]]></c></item>
- <item><c><![CDATA[<IdList> = <IdOrIdRange>,<IdOrIdRange> | <IdOrIdRange>]]></c></item>
+ <item><c><![CDATA[<IdList> = <IdOrIdRange>,<IdOrIdRange> |
+ <IdOrIdRange>]]></c></item>
<item><c><![CDATA[<LogicalIds> = L<IdList>]]></c></item>
- <item><c><![CDATA[<ThreadIds> = T<IdList> | t<IdList>]]></c></item>
+ <item><c><![CDATA[<ThreadIds> = T<IdList> | t<IdList>]]></c>
+ </item>
<item><c><![CDATA[<CoreIds> = C<IdList> | c<IdList>]]></c></item>
- <item><c><![CDATA[<ProcessorIds> = P<IdList> | p<IdList>]]></c></item>
+ <item><c><![CDATA[<ProcessorIds> = P<IdList> | p<IdList>]]></c>
+ </item>
<item><c><![CDATA[<NodeIds> = N<IdList> | n<IdList>]]></c></item>
- <item><c><![CDATA[<IdDefs> = <LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds> | <LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c></item>
- <item><c><![CDATA[CpuTopology = <IdDefs>:<IdDefs> | <IdDefs>]]></c></item>
+ <item><c><![CDATA[<IdDefs> =
+ <LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds> |
+ <LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c>
+ </item>
+ <item><c><![CDATA[CpuTopology = <IdDefs>:<IdDefs> |
+ <IdDefs>]]></c></item>
+ </list>
+ <p>Sets a user-defined CPU topology. The user-defined
+ CPU topology overrides any automatically detected
+ CPU topology. The CPU topology is used when
+ <seealso marker="#+sbt">binding schedulers to logical
+ processors</seealso>.</p>
+ <p>Uppercase letters signify real identifiers and lowercase
+ letters signify fake identifiers only used for description
+ of the topology. Identifiers passed as real identifiers can
+ be used by the runtime system when trying to access specific
+ hardware; if they are incorrect the behavior is
+ undefined. Faked logical CPU identifiers are not accepted,
+ as there is no point in defining the CPU topology without
+ real logical CPU identifiers. Thread, core, processor, and
+ node identifiers can be omitted. If omitted, the thread ID
+ defaults to <c>t0</c>, the core ID defaults to <c>c0</c>,
+ the processor ID defaults to <c>p0</c>, and the node ID is
+ left undefined. Either each logical processor must
+ belong to only one NUMA node, or no logical
+ processors must belong to any NUMA nodes.</p>
+ <p>Both increasing and decreasing <c><![CDATA[<IdRange>]]></c>s
+ are allowed.</p>
+ <p>NUMA node identifiers are system wide. That is, each NUMA
+ node on the system must have a unique identifier. Processor
+ identifiers are also system wide. Core identifiers are
+ processor wide. Thread identifiers are core wide.</p>
+ <p>The order of the identifier types implies the hierarchy of the
+ CPU topology. The valid orders are as follows:</p>
+ <list type="bulleted">
+ <item>
+ <p><c><![CDATA[<LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds>]]></c>,
+ that is, thread is part of a core that is part of a processor,
+ which is part of a NUMA node.</p>
+ </item>
+ <item>
+ <p><c><![CDATA[<LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c>,
+ that is, thread is part of a core that is part of a NUMA node,
+ which is part of a processor.</p>
+ </item>
+ </list>
+ <p>A CPU topology can consist of both processor external, and
+ processor internal NUMA nodes as long as each logical processor
+ belongs to only one NUMA node. If
+ <c><![CDATA[<ProcessorIds>]]></c> is omitted, its default position
+ is before <c><![CDATA[<NodeIds>]]></c>. That is, the default is
+ processor external NUMA nodes.</p>
+ <p>If a list of identifiers is used in an
+ <c><![CDATA[<IdDefs>]]></c>:</p>
+ <list type="bulleted">
+ <item><c><![CDATA[<LogicalIds>]]></c> must be a list
+ of identifiers.</item>
+ <item>At least one other identifier type besides
+ <c><![CDATA[<LogicalIds>]]></c> must also have a
+ list of identifiers.</item>
+ <item>All lists of identifiers must produce the
+ same number of identifiers.</item>
</list>
- <p>Set a user defined CPU topology. The user defined
- CPU topology will override any automatically detected
- CPU topology. The CPU topology is used when
- <seealso marker="#+sbt">binding schedulers to logical
- processors</seealso>.
- </p>
- <p>Upper-case letters signify real identifiers and lower-case
- letters signify fake identifiers only used for description
- of the topology. Identifiers passed as real identifiers may
- be used by the runtime system when trying to access specific
- hardware and if they are not correct the behavior is
- undefined. Faked logical CPU identifiers are not accepted
- since there is no point in defining the CPU topology without
- real logical CPU identifiers. Thread, core, processor, and
- node identifiers may be left out. If left out, thread id
- defaults to <c>t0</c>, core id defaults to <c>c0</c>,
- processor id defaults to <c>p0</c>, and node id will
- be left undefined. Either each logical processor must
- belong to one and only one NUMA node, or no logical
- processors must belong to any NUMA nodes.
- </p>
- <p>Both increasing and decreasing <c><![CDATA[<IdRange>]]></c>s
- are allowed.</p>
- <p>NUMA node identifiers are system wide. That is, each NUMA
- node on the system have to have a unique identifier. Processor
- identifiers are also system wide. Core identifiers are
- processor wide. Thread identifiers are core wide.</p>
- <p>The order of the identifier types imply the hierarchy of the
- CPU topology. Valid orders are either
- <c><![CDATA[<LogicalIds><ThreadIds><CoreIds><ProcessorIds><NodeIds>]]></c>,
- or
- <c><![CDATA[<LogicalIds><ThreadIds><CoreIds><NodeIds><ProcessorIds>]]></c>.
- That is, thread is part of a core which is part of a processor
- which is part of a NUMA node, or thread is part of a core which
- is part of a NUMA node which is part of a processor. A cpu
- topology can consist of both processor external, and processor
- internal NUMA nodes as long as each logical processor belongs
- to one and only one NUMA node. If <c><![CDATA[<ProcessorIds>]]></c>
- is left out, its default position will be before
- <c><![CDATA[<NodeIds>]]></c>. That is, the default is
- processor external NUMA nodes.
- </p>
- <p>If a list of identifiers is used in an
- <c><![CDATA[<IdDefs>]]></c>:</p>
- <list type="bulleted">
- <item><c><![CDATA[<LogicalIds>]]></c> have to be a list
- of identifiers.</item>
- <item>At least one other identifier type apart from
- <c><![CDATA[<LogicalIds>]]></c> also have to have a
- list of identifiers.</item>
- <item>All lists of identifiers have to produce the
- same amount of identifiers.</item>
- </list>
- <p>A simple example. A single quad core processor may be
- described this way:</p>
+ <p>A simple example. A single quad core processor can be
+ described as follows:</p>
<pre>
% <input>erl +sct L0-3c0-3</input>
1> <input>erlang:system_info(cpu_topology).</input>
[{processor,[{core,{logical,0}},
{core,{logical,1}},
{core,{logical,2}},
- {core,{logical,3}}]}]
-</pre>
- <p>A little more complicated example. Two quad core
- processors. Each processor in its own NUMA node.
- The ordering of logical processors is a little weird.
- This in order to give a better example of identifier
- lists:</p>
+ {core,{logical,3}}]}]</pre>
+ <p>A more complicated example with two quad core
+ processors, each processor in its own NUMA node.
+ The ordering of logical processors is a bit weird.
+ This to give a better example of identifier lists:</p>
<pre>
% <input>erl +sct L0-1,3-2c0-3p0N0:L7,4,6-5c0-3p1N1</input>
1> <input>erlang:system_info(cpu_topology).</input>
@@ -1221,239 +1243,247 @@
{node,[{processor,[{core,{logical,7}},
{core,{logical,4}},
{core,{logical,6}},
- {core,{logical,5}}]}]}]
-</pre>
- <p>As long as real identifiers are correct it is okay
- to pass a CPU topology that is not a correct
- description of the CPU topology. When used with
- care this can actually be very useful. This in
- order to trick the emulator to bind its schedulers
- as you want. For example, if you want to run multiple
- Erlang runtime systems on the same machine, you
- want to reduce the amount of schedulers used and
- manipulate the CPU topology so that they bind to
- different logical CPUs. An example, with two Erlang
- runtime systems on a quad core machine:</p>
+ {core,{logical,5}}]}]}]</pre>
+ <p>As long as real identifiers are correct, it is OK
+ to pass a CPU topology that is not a correct
+ description of the CPU topology. When used with
+ care this can be very useful. This
+ to trick the emulator to bind its schedulers
+ as you want. For example, if you want to run multiple
+ Erlang runtime systems on the same machine, you
+ want to reduce the number of schedulers used and
+ manipulate the CPU topology so that they bind to
+ different logical CPUs. An example, with two Erlang
+ runtime systems on a quad core machine:</p>
<pre>
% <input>erl +sct L0-3c0-3 +sbt db +S3:2 -detached -noinput -noshell -sname one</input>
-% <input>erl +sct L3-0c0-3 +sbt db +S3:2 -detached -noinput -noshell -sname two</input>
-</pre>
- <p>In this example each runtime system have two
- schedulers each online, and all schedulers online
- will run on different cores. If we change to one
- scheduler online on one runtime system, and three
- schedulers online on the other, all schedulers
- online will still run on different cores.</p>
- <p>Note that a faked CPU topology that does not reflect
- how the real CPU topology looks like is likely to
- decrease the performance of the runtime system.</p>
- <p>For more information, see
- <seealso marker="erlang#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>.</p>
+% <input>erl +sct L3-0c0-3 +sbt db +S3:2 -detached -noinput -noshell -sname two</input></pre>
+ <p>In this example, each runtime system have two
+ schedulers each online, and all schedulers online
+ will run on different cores. If we change to one
+ scheduler online on one runtime system, and three
+ schedulers online on the other, all schedulers
+ online will still run on different cores.</p>
+ <p>Notice that a faked CPU topology that does not reflect
+ how the real CPU topology looks like is likely to
+ decrease the performance of the runtime system.</p>
+ <p>For more information, see
+ <seealso marker="erlang#system_info_cpu_topology">
+ <c>erlang:system_info(cpu_topology)</c></seealso>.</p>
</item>
<tag><marker id="+secio"/><c>+secio true|false</c></tag>
<item>
- <p>Enable or disable eager check I/O scheduling. The default
- is currently <c>true</c>. The default was changed from <c>false</c>
- to <c>true</c> as of erts version 7.0. The behaviour before this
- flag was introduced corresponds to <c>+secio false</c>.</p>
- <p>The flag effects when schedulers will check for I/O
- operations possible to execute, and when such I/O operations
- will execute. As the name of the parameter implies,
- schedulers will be more eager to check for I/O when
- <c>true</c> is passed. This however also implies that
- execution of outstanding I/O operation will not be
- prioritized to the same extent as when <c>false</c> is
- passed.</p>
- <p><seealso marker="erlang#system_info_eager_check_io"><c>erlang:system_info(eager_check_io)</c></seealso>
- returns the value of this parameter used when starting the VM.</p>
+ <p>Enables or disables eager check I/O scheduling. Defaults
+ to <c>true</c>. The default was changed from <c>false</c>
+ as from ERTS 7.0. The behavior before this
+ flag was introduced corresponds to <c>+secio false</c>.</p>
+ <p>The flag effects when schedulers will check for I/O
+ operations possible to execute, and when such I/O operations
+ will execute. As the parameter name implies,
+ schedulers are more eager to check for I/O when
+ <c>true</c> is passed. This, however, also implies that
+ execution of outstanding I/O operation is not
+ prioritized to the same extent as when <c>false</c> is
+ passed.</p>
+ <p><seealso marker="erlang#system_info_eager_check_io">
+ <c>erlang:system_info(eager_check_io)</c></seealso>
+ returns the value of this parameter used when starting
+ the virtual machine.</p>
</item>
<tag><marker id="+sfwi"/><c>+sfwi Interval</c></tag>
<item>
- <p>Set scheduler forced wakeup interval. All run queues will
- be scanned each <c>Interval</c> milliseconds. While there are
- sleeping schedulers in the system, one scheduler will be woken
- for each non-empty run queue found. An <c>Interval</c> of zero
- disables this feature, which also is the default.
- </p>
- <p>This feature has been introduced as a temporary workaround
- for long-executing native code, and native code that does not
- bump reductions properly in OTP. When these bugs have be fixed
- the <c>+sfwi</c> flag will be removed.
- </p>
- </item>
+ <p>Sets scheduler-forced wakeup interval. All run queues are
+ scanned each <c>Interval</c> milliseconds. While there are
+ sleeping schedulers in the system, one scheduler is woken
+ for each non-empty run queue found. <c>Interval</c> default
+ to <c>0</c>, meaning this feature is disabled.</p>
+ <note>
+ <p>This feature has been introduced as a temporary workaround
+ for long-executing native code, and native code that does not
+ bump reductions properly in OTP. When these bugs have be fixed,
+ this flag will be removed.</p>
+ </note>
+ </item>
+ <tag><marker id="+spp"/><c>+spp Bool</c></tag>
+ <item>
+ <p>Sets default scheduler hint for port parallelism. If set to
+ <c>true</c>, the virtual machine schedules port tasks when it
+ improves parallelism in the system. If set to <c>false</c>, the
+ virtual machine tries to perform port tasks immediately,
+ improving latency at the expense of parallelism. Default to
+ <c>false</c>. The default used can be inspected in runtime by
+ calling <seealso marker="erlang#system_info_port_parallelism">
+ <c>erlang:system_info(port_parallelism)</c></seealso>.
+ The default can be overridden on port creation by passing option
+ <seealso marker="erlang#open_port_parallelism">
+ <c>parallelism</c></seealso> to
+ <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso></p>.
+ </item>
+ <tag><marker id="sched_thread_stack_size"/>
+ <c><![CDATA[+sss size]]></c></tag>
+ <item>
+ <p>Suggested stack size, in kilowords, for scheduler threads.
+ Valid range is 4-8192 kilowords. The default stack size is
+ OS-dependent.</p>
+ </item>
<tag><marker id="+stbt"/><c>+stbt BindType</c></tag>
<item>
- <p>Try to set scheduler bind type. The same as the
- <seealso marker="#+sbt">+sbt</seealso> flag with the exception of
- how some errors are handled. For more information, see the
- documentation of the <seealso marker="#+sbt">+sbt</seealso> flag.
- </p>
- </item>
+ <p>Tries to set the scheduler bind type. The same as flag
+ <seealso marker="#+sbt"><c>+sbt</c></seealso> except
+ how some errors are handled. For more information, see
+ <seealso marker="#+sbt"><c>+sbt</c></seealso>.</p>
+ </item>
<tag><marker id="+sub"/><c>+sub true|false</c></tag>
<item>
- <p>Enable or disable
- <seealso marker="erts:erlang#statistics_scheduler_wall_time">scheduler
- utilization</seealso> balancing of load. By default scheduler
- utilization balancing is disabled and instead scheduler
- compaction of load is enabled which will strive for a load
- distribution which causes as many scheduler threads as possible
- to be fully loaded (i.e., not run out of work). When scheduler
- utilization balancing is enabled the system will instead try to
- balance scheduler utilization between schedulers. That is,
- strive for equal scheduler utilization on all schedulers.
- <br/>&nbsp;&nbsp;&nbsp;<c>+sub true</c> is only supported on
- systems where the runtime system detects and uses a monotonically
- increasing high resolution clock. On other systems, the runtime
- system will fail to start.
- <br/>&nbsp;&nbsp;&nbsp;<c>+sub true</c> implies
- <seealso marker="#+scl">+scl false</seealso>. The difference
- between <c>+sub true</c> and <c>+scl false</c> is that
- <c>+scl false</c> will not try to balance the scheduler
- utilization.
- </p>
+ <p>Enables or disables
+ <seealso marker="erts:erlang#statistics_scheduler_wall_time">
+ scheduler utilization</seealso> balancing of load. By default
+ scheduler utilization balancing is disabled and instead scheduler
+ compaction of load is enabled, which strives for a load
+ distribution that causes as many scheduler threads as possible
+ to be fully loaded (that is, not run out of work). When scheduler
+ utilization balancing is enabled, the system instead tries to
+ balance scheduler utilization between schedulers. That is,
+ strive for equal scheduler utilization on all schedulers.</p>
+ <p><c>+sub true</c> is only supported on systems where the runtime
+ system detects and uses a monotonically increasing high-resolution
+ clock. On other systems, the runtime system fails to start.</p>
+ <p><c>+sub true</c> implies <seealso marker="#+scl">
+ <c>+scl false</c></seealso>. The difference between
+ <c>+sub true</c> and <c>+scl false</c> is that <c>+scl false</c>
+ does not try to balance the scheduler utilization.</p>
</item>
- <tag><marker id="+swct"/><c>+swct very_eager|eager|medium|lazy|very_lazy</c></tag>
- <item>
- <p>
- Set scheduler wake cleanup threshold. Default is <c>medium</c>.
- This flag controls how eager schedulers should be requesting
- wake up due to certain cleanup operations. When a lazy setting
- is used, more outstanding cleanup operations can be left undone
- while a scheduler is idling. When an eager setting is used,
- schedulers will more frequently be woken, potentially increasing
- CPU-utilization.
- </p>
- <p><em>NOTE:</em> This flag may be removed or changed at any time without prior notice.
- </p>
- </item>
- <tag><marker id="+sws"/><c>+sws default|legacy</c></tag>
- <item>
- <p>
- Set scheduler wakeup strategy. Default strategy changed in erts-5.10/OTP-R16A. This strategy was previously known as <c>proposal</c> in OTP-R15. The <c>legacy</c> strategy was used as default from R13 up to and including R15.
- </p>
- <p><em>NOTE:</em> This flag may be removed or changed at any time without prior notice.
- </p>
- </item>
- <tag><marker id="+swt"/><c>+swt very_low|low|medium|high|very_high</c></tag>
- <item>
- <p>Set scheduler wakeup threshold. Default is <c>medium</c>.
- The threshold determines when to wake up sleeping schedulers
- when more work than can be handled by currently awake schedulers
- exist. A low threshold will cause earlier wakeups, and a high
- threshold will cause later wakeups. Early wakeups will
- distribute work over multiple schedulers faster, but work will
- more easily bounce between schedulers.
- </p>
- <p><em>NOTE:</em> This flag may be removed or changed at any time
- without prior notice.
- </p>
- </item>
- <tag><marker id="+spp"/><c>+spp Bool</c></tag>
+ <tag><marker id="+swct"/>
+ <c>+swct very_eager|eager|medium|lazy|very_lazy</c></tag>
<item>
- <p>Set default scheduler hint for port parallelism. If set to
- <c>true</c>, the VM will schedule port tasks when doing so will
- improve parallelism in the system. If set to <c>false</c>, the VM
- will try to perform port tasks immediately, improving latency at the
- expense of parallelism. If this flag has not been passed, the
- default scheduler hint for port parallelism is currently
- <c>false</c>. The default used can be inspected in runtime by
- calling <seealso
- marker="erlang#system_info_port_parallelism">erlang:system_info(port_parallelism)</seealso>.
- The default can be overriden on port creation by passing the
- <seealso marker="erlang#open_port_parallelism">parallelism</seealso>
- option to <seealso
- marker="erlang#open_port/2">open_port/2</seealso></p>.
- </item>
- <tag><marker id="sched_thread_stack_size"/><c><![CDATA[+sss size]]></c></tag>
- <item>
- <p>Suggested stack size, in kilowords, for scheduler threads.
- Valid range is 4-8192 kilowords. The default stack size
- is OS dependent.</p>
- </item>
+ <p>Sets scheduler wake cleanup threshold. Defaults to <c>medium</c>.
+ Controls how eager schedulers are to be requesting
+ wakeup because of certain cleanup operations. When a lazy setting
+ is used, more outstanding cleanup operations can be left undone
+ while a scheduler is idling. When an eager setting is used,
+ schedulers are more frequently woken, potentially increasing
+ CPU-utilization.</p>
+ <note>
+ <p>This flag can be removed or changed at any time without prior
+ notice.</p>
+ </note>
+ </item>
+ <tag><marker id="+sws"/><c>+sws default|legacy</c></tag>
+ <item>
+ <p>Sets scheduler wakeup strategy. Default strategy changed in
+ ERTS 5.10 (Erlang/OTP R16A). This strategy was known as
+ <c>proposal</c> in Erlang/OTP R15. The <c>legacy</c> strategy
+ was used as default from R13 up to and including R15.</p>
+ <note>
+ <p>This flag can be removed or changed at any time without prior
+ notice.</p>
+ </note>
+ </item>
+ <tag><marker id="+swt"/>
+ <c>+swt very_low|low|medium|high|very_high</c></tag>
+ <item>
+ <p>Sets scheduler wakeup threshold. Defaults to <c>medium</c>.
+ The threshold determines when to wake up sleeping schedulers
+ when more work than can be handled by currently awake schedulers
+ exists. A low threshold causes earlier wakeups, and a high
+ threshold causes later wakeups. Early wakeups distribute work
+ over multiple schedulers faster, but work does more easily bounce
+ between schedulers.</p>
+ <note>
+ <p>This flag can be removed or changed at any time without prior
+ notice.</p>
+ </note>
+ </item>
</taglist>
</item>
<tag><marker id="+t"/><c><![CDATA[+t size]]></c></tag>
<item>
- <p>Set the maximum number of atoms the VM can handle. Default is 1048576.</p>
+ <p>Sets the maximum number of atoms the virtual machine can handle.
+ Defaults to 1,048,576.</p>
</item>
<tag><marker id="+T"/><c><![CDATA[+T Level]]></c></tag>
<item>
- <p>Enables modified timing and sets the modified timing level.
- Currently valid range is 0-9. The timing of the runtime system
- will change. A high level usually means a greater change than
- a low level. Changing the timing can be very useful for finding
- timing related bugs.</p>
- <p>Currently, modified timing affects the following:</p>
+ <p>Enables modified timing and sets the modified timing level. Valid
+ range is 0-9. The timing of the runtime system is changed. A high
+ level usually means a greater change than a low level. Changing the
+ timing can be very useful for finding timing-related bugs.</p>
+ <p>Modified timing affects the following:</p>
<taglist>
<tag>Process spawning</tag>
- <item>
- <p>A process calling <c><![CDATA[spawn]]></c>, <c><![CDATA[spawn_link]]></c>,
- <c><![CDATA[spawn_monitor]]></c>, or <c><![CDATA[spawn_opt]]></c> will be scheduled
- out immediately after completing the call. When higher modified
- timing levels are used, the caller will also sleep for a while
- after being scheduled out.</p>
+ <item>A process calling <c><![CDATA[spawn]]></c>,
+ <c><![CDATA[spawn_link]]></c>, <c><![CDATA[spawn_monitor]]></c>,
+ or <c><![CDATA[spawn_opt]]></c> is scheduled out immediately
+ after completing the call. When higher modified timing levels are
+ used, the caller also sleeps for a while after it is scheduled out.
</item>
<tag>Context reductions</tag>
- <item>The amount of reductions a process is a allowed to
- use before being scheduled out is increased or reduced.</item>
+ <item>The number of reductions a process is allowed to use before it
+ is scheduled out is increased or reduced.
+ </item>
<tag>Input reductions</tag>
- <item>The amount of reductions performed before checking I/O
- is increased or reduced.</item>
+ <item>The number of reductions performed before checking I/O is
+ increased or reduced.
+ </item>
</taglist>
- <p><em>NOTE:</em> Performance will suffer when modified timing
- is enabled. This flag is <em>only</em> intended for testing and
- debugging. Also note that <c><![CDATA[return_to]]></c> and <c><![CDATA[return_from]]></c>
- trace messages will be lost when tracing on the spawn BIFs. This
- flag may be removed or changed at any time without prior notice.</p>
- </item>
- <tag><c><![CDATA[+V]]></c></tag>
- <item>
- <p>Makes the emulator print out its version number.</p>
+ <note>
+ <p>Performance suffers when modified timing is enabled. This flag is
+ <em>only</em> intended for testing and debugging.</p>
+ <p><c><![CDATA[return_to]]></c> and <c><![CDATA[return_from]]></c>
+ trace messages are lost when tracing on the spawn BIFs.</p>
+ <p>This flag can be removed or changed at any time without prior
+ notice.</p>
+ </note>
</item>
<tag><c><![CDATA[+v]]></c></tag>
<item>
<p>Verbose.</p>
</item>
+ <tag><c><![CDATA[+V]]></c></tag>
+ <item>
+ <p>Makes the emulator print its version number.</p>
+ </item>
<tag><c><![CDATA[+W w | i | e]]></c></tag>
<item>
- <p>Sets the mapping of warning messages for <c><![CDATA[error_logger]]></c>.
- Messages sent to the error logger using one of the warning
- routines can be mapped either to errors (<c><![CDATA[+W e]]></c>),
- warnings (<c><![CDATA[+W w]]></c>), or info reports
- (<c><![CDATA[+W i]]></c>). The default is warnings.
+ <p>Sets the mapping of warning messages for
+ <c><![CDATA[error_logger]]></c>. Messages sent to the error logger
+ using one of the warning routines can be mapped to errors
+ (<c><![CDATA[+W e]]></c>), warnings (<c><![CDATA[+W w]]></c>), or
+ information reports (<c><![CDATA[+W i]]></c>). Defaults to warnings.
The current mapping can be retrieved using
- <c><![CDATA[error_logger:warning_map/0]]></c>. See
- <seealso marker="kernel:error_logger#warning_map/0">error_logger(3)</seealso>
- for further information.</p>
+ <c><![CDATA[error_logger:warning_map/0]]></c>. For more information,
+ see <seealso marker="kernel:error_logger#warning_map/0">
+ <c>error_logger:warning_map/0</c></seealso> in Kernel.</p>
</item>
<tag><c><![CDATA[+zFlag Value]]></c></tag>
<item>
- <p>Miscellaneous flags.</p>
+ <p>Miscellaneous flags:</p>
<taglist>
<tag><marker id="+zdbbl"/><c>+zdbbl size</c></tag>
<item>
- <p>Set the distribution buffer busy limit
- (<seealso marker="erlang#system_info_dist_buf_busy_limit">dist_buf_busy_limit</seealso>)
- in kilobytes. Valid range is 1-2097151. Default is 1024.</p>
- <p>A larger buffer limit will allow processes to buffer
- more outgoing messages over the distribution. When the
- buffer limit has been reached, sending processes will be
- suspended until the buffer size has shrunk. The buffer
- limit is per distribution channel. A higher limit will
- give lower latency and higher throughput at the expense
- of higher memory usage.</p>
+ <p>Sets the distribution buffer busy limit
+ (<seealso marker="erlang#system_info_dist_buf_busy_limit">
+ <c>dist_buf_busy_limit</c></seealso>)
+ in kilobytes. Valid range is 1-2097151. Defaults to 1024.</p>
+ <p>A larger buffer limit allows processes to buffer
+ more outgoing messages over the distribution. When the
+ buffer limit has been reached, sending processes will be
+ suspended until the buffer size has shrunk. The buffer
+ limit is per distribution channel. A higher limit
+ gives lower latency and higher throughput at the expense
+ of higher memory use.</p>
</item>
<tag><marker id="+zdntgc"/><c>+zdntgc time</c></tag>
<item>
- <p>Set the delayed node table garbage collection time
- (<seealso marker="erlang#system_info_delayed_node_table_gc">delayed_node_table_gc</seealso>)
- in seconds. Valid values are either <c>infinity</c> or
- an integer in the range [0-100000000]. Default is 60.</p>
- <p>Node table entries that are not referred will linger
- in the table for at least the amount of time that this
- parameter determines. The lingering prevents repeated
- deletions and insertions in the tables from occurring.
- </p>
+ <p>Sets the delayed node table garbage collection time
+ (<seealso marker="erlang#system_info_delayed_node_table_gc">
+ <c>delayed_node_table_gc</c></seealso>)
+ in seconds. Valid values are either <c>infinity</c> or
+ an integer in the range 0-100000000. Defaults to 60.</p>
+ <p>Node table entries that are not referred linger
+ in the table for at least the amount of time that this
+ parameter determines. The lingering prevents repeated
+ deletions and insertions in the tables from occurring.</p>
</item>
</taglist>
</item>
@@ -1462,164 +1492,188 @@
<section>
<marker id="environment_variables"></marker>
- <title>Environment variables</title>
+ <title>Environment Variables</title>
<taglist>
<tag><c><![CDATA[ERL_CRASH_DUMP]]></c></tag>
<item>
<p>If the emulator needs to write a crash dump, the value of this
- variable will be the file name of the crash dump file.
- If the variable is not set, the name of the crash dump file will
- be <c><![CDATA[erl_crash.dump]]></c> in the current directory.</p>
+ variable is the filename of the crash dump file.
+ If the variable is not set, the name of the crash dump file is
+ <c><![CDATA[erl_crash.dump]]></c> in the current directory.</p>
</item>
<tag><c><![CDATA[ERL_CRASH_DUMP_NICE]]></c></tag>
<item>
- <p><em>Unix systems</em>: If the emulator needs to write a crash dump,
- it will use the value of this variable to set the nice value
- for the process, thus lowering its priority. The allowable range is
- 1 through 39 (higher values will be replaced with 39). The highest
- value, 39, will give the process the lowest priority.</p>
+ <p><em>Unix systems</em>: If the emulator needs to write a crash dump,
+ it uses the value of this variable to set the nice value
+ for the process, thus lowering its priority. Valid range is
+ 1-39 (higher values are replaced with 39). The highest
+ value, 39, gives the process the lowest priority.</p>
</item>
<tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS]]></c></tag>
<item>
- <p><em>Unix systems</em>: This variable gives the number of seconds that
- the emulator will be allowed to spend writing a crash dump. When
- the given number of seconds have elapsed, the emulator will be
- terminated by a SIGALRM signal.</p>
-
- <p> If the environment variable is <em>not</em> set or it is set to zero seconds, <c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c>,
- the runtime system will not even attempt to write the crash dump file. It will just terminate.
- </p>
- <p> If the environment variable is set to negative valie, e.g. <c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c>,
- the runtime system will wait indefinitely for the crash dump file to be written.
- </p>
- <p> This environment variable is used in conjuction with
- <seealso marker="kernel:heart"><c>heart</c></seealso> if <c>heart</c> is running:
- </p>
- <taglist>
- <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag>
- <item><p>
- Suppresses the writing a crash dump file entirely,
- thus rebooting the runtime system immediately.
- This is the same as not setting the environment variable.
- </p>
- </item>
- <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c></tag>
- <item><p>Setting the environment variable to a negative value will cause the
- termination of the runtime system to wait until the crash dump file
- has been completly written.
- </p>
- </item>
- <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=S]]></c></tag>
- <item><p>
- Will wait for <c>S</c> seconds to complete the crash dump file and
- then terminate the runtime system.
- </p>
- </item>
- </taglist>
+ <p><em>Unix systems</em>: This variable gives the number of seconds
+ that the emulator is allowed to spend writing a crash dump. When the
+ given number of seconds have elapsed, the emulator is terminated by a
+ <c>SIGALRM</c> signal.</p>
+ <p>If the variable is <em>not</em> set or set to <c>0</c> seconds
+ (<c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c>), the runtime system does
+ not even attempt to write the crash dump file. It only terminates.</p>
+ <p>If the variable is set to negative value, such as
+ <c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c>, the runtime system
+ waits indefinitely for the crash dump file to be written.</p>
+ <p>This variable is used with <seealso marker="kernel:heart">
+ <c>heart(3)</c></seealso> if <c>heart</c> is running:</p>
+ <taglist>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag>
+ <item>Suppresses the writing a crash dump file entirely, thus
+ rebooting the runtime system immediately. This is the same as not
+ setting the environment variable.
+ </item>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c></tag>
+ <item>Setting the environment variable to a negative value causes the
+ termination of the runtime system to wait until the crash dump file
+ has been completly written.
+ </item>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=S]]></c></tag>
+ <item>Waits for <c>S</c> seconds to complete the crash dump file and
+ then terminates the runtime system.
+ </item>
+ </taglist>
</item>
- <tag><marker id="ERL_AFLAGS"/><c><![CDATA[ERL_AFLAGS]]></c></tag>
+ <tag><c><![CDATA[ERL_CRASH_DUMP_BYTES]]></c></tag>
<item>
- <p>The content of this environment variable will be added to the
- beginning of the command line for <c><![CDATA[erl]]></c>.</p>
- <p>The <c><![CDATA[-extra]]></c> flag is treated specially. Its scope ends
- at the end of the environment variable content. Arguments
- following an <c><![CDATA[-extra]]></c> flag are moved on the command line into
- the <c><![CDATA[-extra]]></c> section, i.e. the end of the command line
- following after an <c><![CDATA[-extra]]></c> flag.</p>
+ <p>This variable sets the maximum size of a crash dump file in bytes.
+ The crash dump will be truncated if this limit is exceeded. If the
+ variable is not set, no size limit is enforced by default. If the
+ variable is set to <c>0</c>, the runtime system does not even attempt
+ to write a crash dump file.</p>
+ <p>Introduced in ERTS 8.1.2 (Erlang/OTP 19.2).</p>
</item>
- <tag><marker id="ERL_ZFLAGS"/><c><![CDATA[ERL_ZFLAGS]]></c> and <marker id="ERL_FLAGS"/><c><![CDATA[ERL_FLAGS]]></c></tag>
+ <tag><marker id="ERL_AFLAGS"/><c><![CDATA[ERL_AFLAGS]]></c></tag>
<item>
- <p>The content of these environment variables will be added to the
- end of the command line for <c><![CDATA[erl]]></c>.</p>
- <p>The <c><![CDATA[-extra]]></c> flag is treated specially. Its scope ends
- at the end of the environment variable content. Arguments
- following an <c><![CDATA[-extra]]></c> flag are moved on the command line into
- the <c><![CDATA[-extra]]></c> section, i.e. the end of the command line
- following after an <c><![CDATA[-extra]]></c> flag.</p>
+ <p>The content of this variable is added to the beginning of the
+ command line for <c><![CDATA[erl]]></c>.</p>
+ <p>Flag <c><![CDATA[-extra]]></c> is treated in a special way. Its
+ scope ends at the end of the environment variable content. Arguments
+ following an <c><![CDATA[-extra]]></c> flag are moved on the command
+ line into section <c><![CDATA[-extra]]></c>, that is, the end of the
+ command line following an <c><![CDATA[-extra]]></c> flag.</p>
+ </item>
+ <tag><marker id="ERL_ZFLAGS"/><c><![CDATA[ERL_ZFLAGS]]></c> and
+ <marker id="ERL_FLAGS"/><c><![CDATA[ERL_FLAGS]]></c></tag>
+ <item>
+ <p>The content of these variables are added to the end of the command
+ line for <c><![CDATA[erl]]></c>.</p>
+ <p>Flag <c><![CDATA[-extra]]></c> is treated in a special way. Its
+ scope ends at the end of the environment variable content. Arguments
+ following an <c><![CDATA[-extra]]></c> flag are moved on the command
+ line into section <c><![CDATA[-extra]]></c>, that is, the end of the
+ command line following an <c><![CDATA[-extra]]></c> flag.</p>
</item>
<tag><c><![CDATA[ERL_LIBS]]></c></tag>
<item>
- <p>This environment variable contains a list of additional library
- directories that the code server will search for applications and
- add to the code path.
- See <seealso marker="kernel:code">code(3)</seealso>.</p>
+ <p>Contains a list of additional library directories that the code
+ server searches for applications and adds to the code path; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c><![CDATA[ERL_EPMD_ADDRESS]]></c></tag>
<item>
- <p>This environment variable may be set to a comma-separated
- list of IP addresses, in which case the
- <seealso marker="epmd">epmd</seealso> daemon
- will listen only on the specified address(es) and on the
- loopback address (which is implicitly added to the list if it
- has not been specified).</p>
+ <p>Can be set to a comma-separated list of IP addresses, in which case
+ the <seealso marker="epmd"><c>epmd</c></seealso> daemon listens only
+ on the specified address(es) and on the loopback address (which is
+ implicitly added to the list if it has not been specified).</p>
</item>
<tag><c><![CDATA[ERL_EPMD_PORT]]></c></tag>
<item>
- <p>This environment variable can contain the port number to use when
- communicating with <seealso marker="epmd">epmd</seealso>. The default
- port will work fine in most cases. A different port can be specified
+ <p>Can contain the port number to use when communicating with
+ <seealso marker="epmd"><c>epmd</c></seealso>. The default port works
+ fine in most cases. A different port can be specified
to allow nodes of independent clusters to co-exist on the same host.
- All nodes in a cluster must use the same epmd port number.</p>
+ All nodes in a cluster must use the same <c>epmd</c> port number.</p>
</item>
</taglist>
</section>
<section>
+ <marker id="signals"></marker>
+ <title>Signals</title>
+ <p>On Unix systems, the Erlang runtime will interpret two types of signals.</p>
+ <taglist>
+ <tag><c>SIGUSR1</c></tag>
+ <item>
+ <p>A <c>SIGUSR1</c> signal forces a crash dump.</p>
+ </item>
+ <tag><c>SIGTERM</c></tag>
+ <item>
+ <p>A <c>SIGTERM</c> will produce a <c>stop</c> message to the <c>init</c> process.
+ This is equivalent to a <c>init:stop/0</c> call.</p>
+ <p>Introduced in ERTS 8.3 (Erlang/OTP 19.3)</p>
+ </item>
+ </taglist>
+ <p>The signal <c>SIGUSR2</c> is reserved for internal usage. No other signals are handled.</p>
+ </section>
+
+ <section>
<marker id="configuration"></marker>
<title>Configuration</title>
- <p>The standard Erlang/OTP system can be re-configured to change the default
- behavior on start-up.</p>
+ <p>The standard Erlang/OTP system can be reconfigured to change the default
+ behavior on startup.</p>
<taglist>
- <tag>The .erlang Start-up File</tag>
- <item>
- <p>When Erlang/OTP is started, the system searches for a file named .erlang
- in the directory where Erlang/OTP is started. If not found, the user's home
- directory is searched for an .erlang file.</p>
- <p>If an .erlang file is found, it is assumed to contain valid Erlang expressions.
- These expressions are evaluated as if they were input to the shell.</p>
- <p>A typical .erlang file contains a set of search paths, for example:</p>
- <code type="none"><![CDATA[
- io:format("executing user profile in HOME/.erlang\n",[]).
- code:add_path("/home/calvin/test/ebin").
- code:add_path("/home/hobbes/bigappl-1.2/ebin").
- io:format(".erlang rc finished\n",[]).
- ]]></code>
- </item>
- <tag>user_default and shell_default</tag>
- <item>
- <p>Functions in the shell which are not prefixed by a module name are assumed
- to be functional objects (Funs), built-in functions (BIFs), or belong to the
- module user_default or shell_default.</p>
- <p>To include private shell commands, define them in a module user_default and
- add the following argument as the first line in the .erlang file.</p>
+ <tag>The <c>.erlang</c> startup file</tag>
+ <item>
+ <p>When Erlang/OTP is started, the system searches for a file named
+ <c>.erlang</c> in the directory where Erlang/OTP is started. If not
+ found, the user's home directory is searched for an <c>.erlang</c>
+ file.</p>
+ <p>If an <c>.erlang</c> file is found, it is assumed to contain valid
+ Erlang expressions. These expressions are evaluated as if they were
+ input to the shell.</p>
+ <p>A typical <c>.erlang</c> file contains a set of search paths, for
+ example:</p>
<code type="none"><![CDATA[
- code:load_abs("..../user_default").
- ]]></code>
- </item>
- <tag>erl</tag>
- <item>
- <p>If the contents of .erlang are changed and a private version of
- user_default is defined, it is possible to customize the Erlang/OTP environment.
- More powerful changes can be made by supplying command line arguments in the
- start-up script erl. Refer to erl(1) and <seealso marker="init">init(3)</seealso>
- for further information.</p>
- </item>
+io:format("executing user profile in HOME/.erlang\n",[]).
+code:add_path("/home/calvin/test/ebin").
+code:add_path("/home/hobbes/bigappl-1.2/ebin").
+io:format(".erlang rc finished\n",[]). ]]></code>
+ </item>
+ <tag>user_default and shell_default</tag>
+ <item>
+ <p>Functions in the shell that are not prefixed by a module name are
+ assumed to be functional objects (funs), built-in functions (BIFs),
+ or belong to the module <c>user_default</c> or
+ <c>shell_default</c>.</p>
+ <p>To include private shell commands, define them in a module
+ <c>user_default</c> and add the following argument as the first line
+ in the <c>.erlang</c> file:</p>
+ <code type="none"><![CDATA[
+code:load_abs("..../user_default"). ]]></code>
+ </item>
+ <tag>erl</tag>
+ <item>
+ <p>If the contents of <c>.erlang</c> are changed and a private version
+ of <c>user_default</c> is defined, the Erlang/OTP environment can be
+ customized. More powerful changes can be made by supplying
+ command-line arguments in the startup script <c>erl</c>. For more
+ information, see <seealso marker="init"><c>init(3)</c></seealso>.</p>
+ </item>
</taglist>
</section>
- <section>
- <title>SEE ALSO</title>
- <p><seealso marker="init">init(3)</seealso>,
- <seealso marker="erl_prim_loader">erl_prim_loader(3)</seealso>,
- <seealso marker="kernel:erl_boot_server">erl_boot_server(3)</seealso>,
- <seealso marker="kernel:code">code(3)</seealso>,
- <seealso marker="kernel:application">application(3)</seealso>,
- <seealso marker="kernel:heart">heart(3)</seealso>,
- <seealso marker="kernel:net_kernel">net_kernel(3)</seealso>,
- <seealso marker="kernel:auth">auth(3)</seealso>,
- <seealso marker="tools:make">make(3)</seealso>,
- <seealso marker="epmd">epmd(1)</seealso>,
- <seealso marker="erts_alloc">erts_alloc(3)</seealso></p>
+ <section>
+ <title>See Also</title>
+ <p><seealso marker="epmd"><c>epmd(1)</c></seealso>,
+ <seealso marker="erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>,
+ <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>,
+ <seealso marker="init"><c>init(3)</c></seealso>,
+ <seealso marker="kernel:application">
+ <c>application(3)</c></seealso>,
+ <seealso marker="kernel:auth"><c>auth(3)</c></seealso>,
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>,
+ <seealso marker="kernel:erl_boot_server">
+ <c>erl_boot_server(3)</c></seealso>,
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso>,
+ <seealso marker="kernel:net_kernel"><c>net_kernel(3)</c></seealso>,
+ <seealso marker="tools:make"><c>make(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml
index 25f601a235..ee74983730 100644
--- a/erts/doc/src/erl_dist_protocol.xml
+++ b/erts/doc/src/erl_dist_protocol.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2015</year>
+ <year>2016</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,1061 +32,989 @@
<file>erl_dist_protocol.xml</file>
</header>
-<p>
-The description here is far from complete and will therefore be further
-refined in upcoming releases.
-
-The protocols both from Erlang nodes towards
-EPMD (Erlang Port Mapper Daemon) and between Erlang nodes, however, are
-stable since many years.
-</p>
-
-<p>The distribution protocol can be divided into four (4) parts:</p>
-<list>
- <item>
- <p>
- 1. Low level socket connection.
- </p>
- </item>
- <item>
- 2. Handshake, interchange node name and authenticate.
- </item>
- <item>
- 3. Authentication (done by net_kernel).
- </item>
- <item>
- 4. Connected.
- </item>
-</list>
-<p>
- A node fetches the Port number of another node through the EPMD (at the
- other host) in order to initiate a connection request.
-</p>
-<p>
-For each host where a distributed Erlang node is running there should also
-be an EPMD running. The EPMD can be started explicitly or automatically
-as a result of the Erlang node startup.
-</p>
-<p>
-By default EPMD listens on port 4369.
-</p>
-<p>
- 3 and 4 are performed at the same level but the net_kernel disconnects the
- other node if it communicates using an invalid cookie (after one (1) second).
-</p>
-
-<p>The integers in all multi-byte fields are in big-endian order.</p>
+ <p>This description is far from complete. It will be updated if the
+ protocol is updated. However, the protocols, both from Erlang
+ nodes to the Erlang Port Mapper Daemon (EPMD) and between Erlang nodes
+ are stable since many years.</p>
+
+ <p>The distribution protocol can be divided into four parts:</p>
+
+ <list type="bulleted">
+ <item>
+ <p>Low-level socket connection (1)</p>
+ </item>
+ <item>
+ <p>Handshake, interchange node name, and authenticate (2)</p>
+ </item>
+ <item>
+ <p>Authentication (done by <seealso marker="kernel:net_kernel">
+ <c>net_kernel(3)</c></seealso>) (3)</p>
+ </item>
+ <item>
+ <p>Connected (4)</p>
+ </item>
+ </list>
+
+ <p>A node fetches the port number of another node through the EPMD (at the
+ other host) to initiate a connection request.</p>
+
+ <p>For each host, where a distributed Erlang node is running, also an EPMD
+ is to be running. The EPMD can be started explicitly or automatically
+ as a result of the Erlang node startup.</p>
+
+ <p>By default the EPMD listens on port 4369.</p>
+
+ <p>(3) and (4) above are performed at the same level but the <c>net_kernel</c>
+ disconnects the other node if it communicates using an invalid cookie (after
+ 1 second).</p>
+
+ <p>The integers in all multibyte fields are in big-endian order.</p>
<section>
<title>EPMD Protocol</title>
- <p>
- The requests served by the EPMD (Erlang Port Mapper Daemon) are
- summarized in the figure below.
- </p>
-
- <image file="erl_ext_fig.gif">
- <icaption>
- Summary of EPMD requests.
- </icaption>
- </image>
- <p>
- Each request <c>*_REQ</c> is preceded by a two-byte length field.
- Thus, the overall request format is:
- </p>
+ <p>The requests served by the EPMD are summarized in the following
+ figure.</p>
+
+ <image file="erl_ext_fig.gif">
+ <icaption>Summary of EPMD Requests</icaption>
+ </image>
+
+ <p>Each request <c>*_REQ</c> is preceded by a 2 byte length field.
+ Thus, the overall request format is as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">2</cell>
+ <cell align="center">n</cell>
+ </row>
+ <row>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>Request</c></cell>
+ </row>
+ <tcaption>Request Format</tcaption>
+ </table>
+
+ <section>
+ <title>Register a Node in EPMD</title>
+ <p>When a distributed node is started it registers itself in the EPMD.
+ The message <c>ALIVE2_REQ</c> described below is sent from the node to
+ the EPMD. The response from the EPMD is <c>ALIVE2_RESP</c>.</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">2</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Nlen</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Elen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>120</c></cell>
+ <cell align="center"><c>PortNo</c></cell>
+ <cell align="center"><c>NodeType</c></cell>
+ <cell align="center"><c>Protocol</c></cell>
+ <cell align="center"><c>HighestVersion</c></cell>
+ <cell align="center"><c>LowestVersion</c></cell>
+ <cell align="center"><c>Nlen</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ <cell align="center"><c>Elen</c></cell>
+ <cell align="center"><c>Extra</c></cell>
+ </row>
+ <tcaption>ALIVE2_REQ (120)</tcaption>
+ </table>
+
+ <taglist>
+ <tag><c>PortNo</c></tag>
+ <item>
+ <p>The port number on which the node accept connection requests.</p>
+ </item>
+ <tag><c>NodeType</c></tag>
+ <item>
+ <p>77 = normal Erlang node, 72 = hidden node (C-node), ...</p>
+ </item>
+ <tag><c>Protocol</c></tag>
+ <item>
+ <p>0 = TCP/IPv4, ...</p>
+ </item>
+ <tag><c>HighestVersion</c></tag>
+ <item>
+ <p>The highest distribution version that this node can handle.
+ The value in Erlang/OTP R6B and later is 5.</p>
+ </item>
+ <tag><c>LowestVersion</c></tag>
+ <item>
+ <p>The lowest distribution version that this node can handle.
+ The value in Erlang/OTP R6B and later is 5.</p>
+ </item>
+ <tag><c>Nlen</c></tag>
+ <item>
+ <p>The length (in bytes) of field <c>NodeName</c>.</p>
+ </item>
+ <tag><c>NodeName</c></tag>
+ <item>
+ <p>The node name as an UTF-8 encoded string of <c>Nlen</c> bytes.</p>
+ </item>
+ <tag><c>Elen</c></tag>
+ <item>
+ <p>The length of field <c>Extra</c>.</p>
+ </item>
+ <tag><c>Extra</c></tag>
+ <item>
+ <p>Extra field of <c>Elen</c> bytes.</p>
+ </item>
+ </taglist>
+
+ <p>The connection created to the EPMD must be kept as long as the
+ node is a distributed node. When the connection is closed,
+ the node is automatically unregistered from the EPMD.</p>
+
+ <p>The response message <c>ALIVE2_RESP</c> is as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ </row>
+ <row>
+ <cell align="center"><c>121</c></cell>
+ <cell align="center"><c>Result</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>ALIVE2_RESP (121)</tcaption>
+ </table>
+
+ <p>Result = 0 -> ok, result &gt; 0 -> error.</p>
+ </section>
+
+ <section>
+ <title>Unregister a Node from EPMD</title>
+ <p>A node unregisters itself from the EPMD by closing the TCP
+ connection to EPMD established when the node was registered.</p>
+ </section>
+
+ <section>
+ <title>Get the Distribution Port of Another Node</title>
+ <p>When one node wants to connect to another node it starts with
+ a <c>PORT_PLEASE2_REQ</c> request to the EPMD on the host where the
+ node resides to get the distribution port that the node listens to.</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">N</cell>
+ </row>
+ <row>
+ <cell align="center"><c>122</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ </row>
+ <tcaption>PORT_PLEASE2_REQ (122)</tcaption>
+ </table>
+
+ <p>where N = <c>Length</c> - 1.</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>119</c></cell>
+ <cell align="center"><c>Result</c></cell>
+ </row>
+ <tcaption>PORT2_RESP (119) Response Indicating Error, Result &gt; 0
+ </tcaption>
+ </table>
+
+ <p>or</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">2</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Nlen</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Elen</cell>
+ </row>
+ <row>
+ <cell align="center"><c>119</c></cell>
+ <cell align="center"><c>Result</c></cell>
+ <cell align="center"><c>PortNo</c></cell>
+ <cell align="center"><c>NodeType</c></cell>
+ <cell align="center"><c>Protocol</c></cell>
+ <cell align="center"><c>HighestVersion</c></cell>
+ <cell align="center"><c>LowestVersion</c></cell>
+ <cell align="center"><c>Nlen</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ <cell align="center"><c>Elen</c></cell>
+ <cell align="center">><c>Extra</c></cell>
+ </row>
+ <tcaption>PORT2_RESP, Result = 0</tcaption>
+ </table>
+
+ <p>If <c>Result</c> &gt; 0, the packet only consists of
+ <c>[119, Result]</c>.</p>
+
+ <p>The EPMD closes the socket when it has sent the information.</p>
+ </section>
+
+ <section>
+ <title>Get All Registered Names from EPMD</title>
+ <p>This request is used through the Erlang function
+ <seealso marker="kernel:net_adm#names/1,2">
+ <c>net_adm:names/1,2</c></seealso>. A TCP connection is opened
+ to the EPMD and this request is sent.</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>110</c></cell>
+ </row>
+ <tcaption>NAMES_REQ (110)</tcaption>
+ </table>
+
+ <p>The response for a <c>NAMES_REQ</c> is as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">4</cell>
+ <cell align="center">&nbsp;</cell>
+ </row>
+ <row>
+ <cell align="center"><c>EPMDPortNo</c></cell>
+ <cell align="center"><c>NodeInfo*</c></cell>
+ </row>
+ <tcaption>NAMES_RESP</tcaption>
+ </table>
+
+ <p><c>NodeInfo</c> is a string written for each active node.
+ When all <c>NodeInfo</c> has been written the connection is
+ closed by the EPMD.</p>
+
+ <p><c>NodeInfo</c> is, as expressed in Erlang:</p>
+
+ <code>
+io:format("name ~ts at port ~p~n", [NodeName, Port]).</code>
+ </section>
+
+ <section>
+ <title>Dump All Data from EPMD</title>
+ <p>This request is not really used, it is to be regarded as a debug
+ feature.</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>100</c></cell>
+ </row>
+ <tcaption>DUMP_REQ</tcaption>
+ </table>
+
+ <p>The response for a <c>DUMP_REQ</c> is as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">4</cell>
+ <cell align="center">&nbsp;</cell>
+ </row>
+ <row>
+ <cell align="center"><c>EPMDPortNo</c></cell>
+ <cell align="center"><c>NodeInfo*</c></cell>
+ </row>
+ <tcaption>DUMP_RESP</tcaption>
+ </table>
+
+ <p><c>NodeInfo</c> is a string written for each node kept in the EPMD.
+ When all <c>NodeInfo</c> has been written the connection is
+ closed by the EPMD.</p>
+
+ <p><c>NodeInfo</c> is, as expressed in Erlang:</p>
+
+ <code>
+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",
+ [NodeName, Port, Fd]).</code>
+ </section>
+
+ <section>
+ <title>Kill EPMD</title>
+ <p>This request kills the running EPMD. It is almost never used.</p>
<table align="left">
- <row>
- <cell align="center">2</cell>
- <cell align="center">n</cell>
- </row>
- <row>
- <cell align="center">Length</cell>
- <cell align="center">Request</cell>
- </row>
- <tcaption></tcaption></table>
-
- <section>
- <title>Register a node in the EPMD</title>
- <p>
- When a distributed node is started it registers itself in EPMD.
- The message ALIVE2_REQ described below is sent from the node towards
- EPMD. The response from EPMD is ALIVE2_RESP.
- </p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">2</cell>
- <cell align="center">2</cell>
- <cell align="center">Nlen</cell>
- <cell align="center">2</cell>
- <cell align="center">Elen</cell>
- </row>
- <row>
- <cell align="center">120</cell>
- <cell align="center">PortNo</cell>
- <cell align="center">NodeType</cell>
- <cell align="center">Protocol</cell>
- <cell align="center">HighestVersion</cell>
- <cell align="center">LowestVersion</cell>
- <cell align="center">Nlen</cell>
- <cell align="center">NodeName</cell>
- <cell align="center">Elen</cell>
- <cell align="center">Extra</cell>
- </row>
- <tcaption>ALIVE2_REQ (120)</tcaption></table>
- <taglist>
- <tag><c>PortNo</c></tag>
- <item>
- The port number on which the node accept connection requests.
- </item>
- <tag><c>NodeType</c></tag>
- <item>
- 77 = normal Erlang node, 72 = hidden node (C-node),...
- </item>
- <tag><c>Protocol</c></tag>
- <item>
- 0 = tcp/ip-v4, ...
- </item>
- <tag><c>HighestVersion</c></tag>
- <item>
- The highest distribution version that this node can handle.
- The value in R6B and later is 5.
- </item>
- <tag><c>LowestVersion</c></tag>
- <item>
- The lowest distribution version that this node can handle.
- The value in R6B and later is 5.
- </item>
- <tag><c>Nlen</c></tag>
- <item>
- The length (in bytes) of the <c>NodeName</c> field.
- </item>
- <tag><c>NodeName</c></tag>
- <item>
- The NodeName as an UTF-8 encoded string of
- <c>Nlen</c> bytes.
- </item>
- <tag><c>Elen</c></tag>
- <item>
- The length of the <c>Extra</c> field.
- </item>
- <tag><c>Extra</c></tag>
- <item>
- Extra field of <c>Elen</c> bytes.
- </item>
- </taglist>
- <p>
- The connection created to the EPMD must be kept as long as the
- node is a distributed node. When the connection is closed
- the node is automatically unregistered from the EPMD.
- </p>
- <p>
- The response message ALIVE2_RESP is described below.
- </p>
-
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- </row>
- <row>
- <cell align="center">121</cell>
- <cell align="center">Result</cell>
- <cell align="center">Creation</cell>
- </row>
- <tcaption>ALIVE2_RESP (121)</tcaption></table>
- <p>
- Result = 0 -> ok, Result > 0 -> error
- </p>
- </section>
-
- <section>
- <title>Unregister a node from the EPMD</title>
- <p>
- A node unregisters itself from the EPMD by simply closing the
- TCP connection towards EPMD established when the node was registered.
- </p>
- </section>
-
- <section>
- <title>Get the distribution port of another node</title>
- <p>
- When one node wants to connect to another node it starts with
- a PORT_PLEASE2_REQ request towards EPMD on the host where the
- node resides in order to get the distribution port that the node
- listens to.
- </p>
-
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">N</cell>
- </row>
- <row>
- <cell align="center">122</cell>
- <cell align="center">NodeName</cell>
- </row>
- <tcaption>PORT_PLEASE2_REQ (122)</tcaption></table>
- <p>
- where N = Length - 1
- </p>
-
- <p>
- </p>
+ <row>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>107</c></cell>
+ </row>
+ <tcaption>KILL_REQ</tcaption>
+ </table>
+
+ <p>The response for a <c>KILL_REQ</c> is as follows:</p>
+
<table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center">119</cell>
- <cell align="center">Result</cell>
- </row>
- <tcaption>
- PORT2_RESP (119) response indicating error, Result > 0.
- </tcaption>
+ <row>
+ <cell align="center">2</cell>
+ </row>
+ <row>
+ <cell align="center"><c>OKString</c></cell>
+ </row>
+ <tcaption>KILL_RESP</tcaption>
</table>
- <p>Or</p>
+
+ <p>where <c>OKString</c> is "OK".</p>
+ </section>
+
+ <section>
+ <title>STOP_REQ (Not Used)</title>
<table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">2</cell>
- <cell align="center">2</cell>
- <cell align="center">Nlen</cell>
- <cell align="center">2</cell>
- <cell align="center">Elen</cell>
- </row>
- <row>
- <cell align="center">119</cell>
- <cell align="center">Result</cell>
- <cell align="center">PortNo</cell>
- <cell align="center">NodeType</cell>
- <cell align="center">Protocol</cell>
- <cell align="center">HighestVersion</cell>
- <cell align="center">LowestVersion</cell>
- <cell align="center">Nlen</cell>
- <cell align="center">NodeName</cell>
- <cell align="center">Elen</cell>
- <cell align="center">Extra</cell>
- </row>
- <tcaption>PORT2_RESP when Result = 0.</tcaption></table>
-<p>
-If Result > 0, the packet only consists of [119, Result].
-</p>
-
- <p>EPMD will close the socket as soon as it has sent the information.</p>
- </section>
-
- <section>
- <title>Get all registered names from EPMD</title>
- <p>
- This request is used via the Erlang function
- <c>net_adm:names/1,2</c>. A TCP connection is opened
- towards EPMD and this request is sent.
- </p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center">110</cell>
- </row>
- <tcaption>NAMES_REQ (110)</tcaption></table>
-
-
- <p>The response for a <c>NAMES_REQ</c> looks like this:</p>
- <table align="left">
- <row>
- <cell align="center">4</cell>
- <cell align="center">&nbsp;</cell>
- </row>
- <row>
- <cell align="center">EPMDPortNo</cell>
- <cell align="center">NodeInfo*</cell>
- </row>
- <tcaption>NAMES_RESP</tcaption></table>
- <p>
- NodeInfo is a string written for each active node.
- When all NodeInfo has been written the connection is
- closed by EPMD.
- </p>
- <p>
- NodeInfo is, as expressed in Erlang:
- </p>
- <code>
- io:format("name ~ts at port ~p~n", [NodeName, Port]).
- </code>
- </section>
-
-
- <section>
- <title>Dump all data from EPMD</title>
- <p>
- This request is not really used, it should be regarded as a debug
- feature.
- </p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center">100</cell>
- </row>
- <tcaption>DUMP_REQ</tcaption></table>
-
- <p>The response for a <c>DUMP_REQ</c> looks like this:</p>
- <table align="left">
- <row>
- <cell align="center">4</cell>
- <cell align="center">&nbsp;</cell>
- </row>
- <row>
- <cell align="center">EPMDPortNo</cell>
- <cell align="center">NodeInfo*</cell>
- </row>
- <tcaption>DUMP_RESP</tcaption></table>
- <p>
- NodeInfo is a string written for each node kept in EPMD.
- When all NodeInfo has been written the connection is
- closed by EPMD.
- </p>
- <p>
- NodeInfo is, as expressed in Erlang:
- </p>
- <code>
- 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",
- [NodeName, Port, Fd]).
- </code>
-
- </section>
-
- <section>
- <title>Kill the EPMD</title>
- <p>
- This request will kill the running EPMD. It is almost never used.
- </p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center">107</cell>
- </row>
- <tcaption>KILL_REQ</tcaption></table>
-
- <p>The response fo a <c>KILL_REQ</c> looks like this:</p>
- <table align="left">
- <row>
- <cell align="center">2</cell>
- </row>
- <row>
- <cell align="center">OKString</cell>
- </row>
- <tcaption>KILL_RESP</tcaption></table>
- <p>
- where <c>OKString</c> is "OK".
- </p>
- </section>
-
- <section>
- <title>STOP_REQ (Not Used)</title>
- <p></p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">n</cell>
- </row>
- <row>
- <cell align="center">115</cell>
- <cell align="center">NodeName</cell>
- </row>
- <tcaption>STOP_REQ</tcaption></table>
- <p>
- where n = Length - 1
- </p>
- <p>
- The current implementation of Erlang does not care if the connection
- to the EPMD is broken.
- </p>
- <p>The response for a <c>STOP_REQ</c> looks like this.</p>
- <table align="left">
- <row>
- <cell align="center">7</cell>
- </row>
- <row>
- <cell align="center">OKString</cell>
- </row>
- <tcaption>STOP_RESP</tcaption></table>
- <p>
- where OKString is "STOPPED".
- </p>
- <p>A negative response can look like this.</p>
- <table align="left">
- <row>
- <cell align="center">7</cell>
- </row>
- <row>
- <cell align="center">NOKString</cell>
- </row>
- <tcaption>STOP_NOTOK_RESP</tcaption></table>
- <p>
- where NOKString is "NOEXIST".
- </p>
- </section>
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">n</cell>
+ </row>
+ <row>
+ <cell align="center"><c>115</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ </row>
+ <tcaption>STOP_REQ</tcaption>
+ </table>
+
+ <p>where n = <c>Length</c> - 1.</p>
+
+ <p>The current implementation of Erlang does not care if the connection
+ to the EPMD is broken.</p>
+
+ <p>The response for a <c>STOP_REQ</c> is as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">7</cell>
+ </row>
+ <row>
+ <cell align="center"><c>OKString</c></cell>
+ </row>
+ <tcaption>STOP_RESP</tcaption>
+ </table>
+
+ <p>where <c>OKString</c> is "STOPPED".</p>
+
+ <p>A negative response can look as follows:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">7</cell>
+ </row>
+ <row>
+ <cell align="center"><c>NOKString</c></cell>
+ </row>
+ <tcaption>STOP_NOTOK_RESP</tcaption>
+ </table>
+
+ <p>where <c>NOKString</c> is "NOEXIST".</p>
+ </section>
<!--
- <section>
- <title>ALIVE_REQ (97)</title>
- <p></p>
-
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">n</cell>
- </row>
- <row>
- <cell align="center">97</cell>
- <cell align="center">PortNo</cell>
- <cell align="center">NodeName</cell>
- </row>
- <tcaption></tcaption></table>
-
- <p>
- where n = Length - 3
- </p>
- <p>
- The connection created to the EPMD must be kept until the node is
- not a distributed node any longer.
- </p>
- </section>
-
- <section>
- <title>ALIVE_OK_RESP (89)</title>
- <p></p>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- </row>
- <row>
- <cell align="center">89</cell>
- <cell align="center">Creation</cell>
- </row>
- <tcaption></tcaption></table>
- </section>
-
-
- <section>
- <title>ALIVE_NOTOK_RESP ()</title>
- <p>
- EPMD closed the connection.
- </p>
- </section>
-
- <section>
- <title>PORT_PLEASE_REQ (112)</title>
- <p></p>
+ <section>
+ <title>ALIVE_REQ (97)</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">n</cell>
+ </row>
+ <row>
+ <cell align="center"><c>97</c></cell>
+ <cell align="center"><c>PortNo</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ </row>
+ <tcaption></tcaption>
+ </table>
+
+ <p>where n = <c>Length</c> - 3.</p>
+
+ <p>The connection created to the EPMD must be kept until the node is
+ not a distributed node any longer.</p>
+ </section>
+
+ <section>
+ <title>ALIVE_OK_RESP (89)</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ </row>
+ <row>
+ <cell align="center"><c>89</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption></tcaption>
+ </table>
+ </section>
+
+ <section>
+ <title>ALIVE_NOTOK_RESP ()</title>
+ <p>The EPMD closed the connection.</p>
+ </section>
+
+ <section>
+ <title>PORT_PLEASE_REQ (112)</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">n</cell>
+ </row>
+ <row>
+ <cell align="center"><c>112</c></cell>
+ <cell align="center"><c>NodeName</c></cell>
+ </row>
+ <tcaption></tcaption>
+ </table>
+
+ <p>where n = <c>Length</c> - 1.</p>
+ </section>
+
+ <section>
+ <title>PORT_OK_RESP ()</title>
<table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">n</cell>
- </row>
- <row>
- <cell align="center">112</cell>
- <cell align="center">NodeName</cell>
- </row>
- <tcaption></tcaption></table>
- <p>
- where n = Length - 1
- </p>
- </section>
-
- <section>
- <title>PORT_OK_RESP ()</title>
- <p></p>
- <table align="left">
- <row>
- <cell align="center">2</cell>
- </row>
- <row>
- <cell align="center">PortNo</cell>
- </row>
- <tcaption></tcaption></table>
-
- </section>
-
- <section>
- <title>PORT_NOTOK_RESP ()</title>
- <p>
- EPMD closed the connection.
- </p>
- </section>
-
-
- <section>
- <title>PORT_NOTOK_RESP ()</title>
- <p>
- EPMD closed the connection.
- </p>
- </section>
+ <row>
+ <cell align="center">2</cell>
+ </row>
+ <row>
+ <cell align="center"><c>PortNo</c></cell>
+ </row>
+ <tcaption></tcaption>
+ </table>
+ </section>
+
+ <section>
+ <title>PORT_NOTOK_RESP ()</title>
+ <p>The EPMD closed the connection.</p>
+ </section>
+
+ <section>
+ <title>PORT_NOTOK_RESP ()</title>
+ <p>The EPMD closed the connection.</p>
+ </section>
-->
+ </section>
+
+ <section>
+ <marker id="distribution_handshake"/>
+ <title>Distribution Handshake</title>
+ <p>This section describes the distribution handshake protocol introduced
+ in Erlang/OTP R6. This description was previously located in
+ <c>$ERL_TOP/lib/kernel/internal_doc/distribution_handshake.txt</c> and
+ has more or less been copied and "formatted" here. It has been almost
+ unchanged since 1999, but the handshake has not changed much since then
+ either.</p>
+
+ <section>
+ <title>General</title>
+ <p>The TCP/IP distribution uses a handshake that expects a
+ connection-based protocol, that is, the protocol does not include any
+ authentication after the handshake procedure.</p>
+
+ <p>This is not entirely safe, as it is vulnerable against takeover
+ attacks, but it is a tradeoff between fair safety and performance.</p>
+
+ <p>The cookies are never sent in cleartext and the handshake procedure
+ expects the client (called <c>A</c>) to be the first one to prove that
+ it can generate a sufficient digest. The digest is generated with the
+ MD5 message digest algorithm and the challenges are expected to be
+ random numbers.</p>
+ </section>
+ <section>
+ <title>Definitions</title>
+ <p>A challenge is a 32-bit integer in big-endian order. Below the function
+ <c>gen_challenge()</c> returns a random 32-bit integer used as a
+ challenge.</p>
+
+ <p>A digest is a (16 bytes) MD5 hash of the challenge (as text)
+ concatenated with the cookie (as text). Below, the function
+ <c>gen_digest(Challenge, Cookie)</c> generates a digest as described
+ above.</p>
+
+ <p>An <c>out_cookie</c> is the cookie used in outgoing communication to a
+ certain node, so that <c>A</c>'s <c>out_cookie</c> for <c>B</c> is to
+ correspond with <c>B</c>'s <c>in_cookie</c> for <c>A</c> and conversely.
+ <c>A</c>'s <c>out_cookie</c> for <c>B</c> and <c>A</c>'s
+ <c>in_cookie</c> for <c>B</c> need <em>not</em> be the same. Below the
+ function <c>out_cookie(Node)</c> returns the current node's
+ <c>out_cookie</c> for <c>Node</c>.</p>
+
+ <p>An <c>in_cookie</c> is the cookie expected to be used by another node
+ when communicating with us, so that <c>A</c>'s <c>in_cookie</c> for
+ <c>B</c> corresponds with <c>B</c>'s <c>out_cookie</c> for <c>A</c>.
+ Below the function <c>in_cookie(Node)</c> returns the current node's
+ <c>in_cookie</c> for <c>Node</c>.</p>
+
+ <p>The cookies are text strings that can be viewed as passwords.</p>
+
+ <p>Every message in the handshake starts with a 16-bit big-endian integer,
+ which contains the message length (not counting the two initial bytes).
+ In Erlang this corresponds to option <c>{packet, 2}</c> in
+ <seealso marker="kernel:gen_tcp"><c>gen_tcp(3)</c></seealso>.
+ Notice that after the handshake, the distribution switches to 4 byte
+ packet headers.</p>
</section>
- <section>
- <title>Distribution Handshake</title>
- <p>
- <marker id="distribution_handshake"/>
- This section describes the distribution handshake protocol
- introduced in the OTP-R6 release of Erlang/OTP. This
- description was previously located in
- <c>$ERL_TOP/lib/kernel/internal_doc/distribution_handshake.txt</c>,
- and has more or less been copied and "formatted" here. It has been
- more or less unchanged since the year 1999, but the handshake
- should not have changed much since then either.
- </p>
- <section>
- <title>General</title>
- <p>
- The TCP/IP distribution uses a handshake which expects a
- connection based protocol, i.e. the protocol does not include
- any authentication after the handshake procedure.
- </p>
- <p>
- This is not entirely safe, as it is vulnerable against takeover
- attacks, but it is a tradeoff between fair safety and performance.
- </p>
- <p>
- The cookies are never sent in cleartext and the handshake procedure
- expects the client (called A) to be the first one to prove that it can
- generate a sufficient digest. The digest is generated with the
- MD5 message digest algorithm and the challenges are expected to be very
- random numbers.
- </p>
- </section>
- <section>
- <title>Definitions</title>
- <p>
- A challenge is a 32 bit integer number in big endian order. Below the function
- <c>gen_challenge()</c> returns a random 32 bit integer used as a challenge.
- </p>
- <p>
- A digest is a (16 bytes) MD5 hash of the Challenge (as text) concatenated
- with the cookie (as text). Below, the function <c>gen_digest(Challenge, Cookie)</c>
- generates a digest as described above.
- </p>
- <p>
- An out_cookie is the cookie used in outgoing communication to a certain node,
- so that A's out_cookie for B should correspond with B's in_cookie for A and
- the other way around. A's out_cookie for B and A's in_cookie for B need <em>NOT</em>
- be the same. Below the function <c>out_cookie(Node)</c> returns the current
- node's out_cookie for <c>Node</c>.
- </p>
- <p>
- An in_cookie is the cookie expected to be used by another node when
- communicating with us, so that A's in_cookie for B corresponds with B's
- out_cookie for A. Below the function <c>in_cookie(Node)</c> returns the current
- node's <c>in_cookie</c> for <c>Node</c>.
- </p>
- <p>
- The cookies are text strings that can be viewed as passwords.
- </p>
- <p>
- Every message in the handshake starts with a 16 bit big endian integer
- which contains the length of the message (not counting the two initial bytes).
- In erlang this corresponds to the <c>gen_tcp</c> option <c>{packet, 2}</c>. Note that after
- the handshake, the distribution switches to 4 byte packet headers.
- </p>
-
- </section>
- <section>
- <title>The Handshake in Detail</title>
- <p>
- Imagine two nodes, node A, which initiates the handshake and node B, which
- accepts the connection.
- </p>
- <taglist>
- <tag>1) connect/accept</tag>
- <item><p>A connects to B via TCP/IP and B accepts the connection.</p></item>
- <tag>2) send_name/receive_name</tag>
- <item><p>A sends an initial identification to B. B receives the message.
- The message looks like this (every "square" being one byte and the packet
- header removed):
- </p>
-<pre>
+
+ <section>
+ <title>The Handshake in Detail</title>
+ <p>Imagine two nodes, <c>A</c> that initiates the handshake and <c>B</c>
+ that accepts the connection.</p>
+
+ <taglist>
+ <tag>1) connect/accept</tag>
+ <item>
+ <p><c>A</c> connects to <c>B</c> through TCP/IP and <c>B</c> accepts
+ the connection.</p>
+ </item>
+ <tag>2) <c>send_name</c>/<c>receive_name</c></tag>
+ <item>
+ <p><c>A</c> sends an initial identification to <c>B</c>, which
+ receives the message. The message looks as follows (every "square"
+ is one byte and the packet header is removed):</p>
+ <pre>
+---+--------+--------+-----+-----+-----+-----+-----+-----+-...-+-----+
|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Name0|Name1| ... |NameN|
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-... +-----+
-</pre>
- <p>
- The 'n' is just a message tag.
- Version0 and Version1 is the distribution version selected by node A,
- based on information from EPMD. (16 bit big endian)
- Flag0 ... Flag3 are capability flags, the capabilities defined in
- <c>$ERL_TOP/lib/kernel/include/dist.hrl</c>.
- (32 bit big endian)
- Name0 ... NameN is the full nodename of A, as a string of bytes (the
- packet length denotes how long it is).
- </p></item>
- <tag>3) recv_status/send_status</tag>
- <item><p>B sends a status message to A, which indicates
- if the connection is allowed. The following status codes are defined:</p>
- <taglist>
- <tag><c>ok</c></tag>
- <item>The handshake will continue.</item>
- <tag><c>ok_simultaneous</c></tag>
- <item>The handshake will continue, but A is informed that B
- has another ongoing connection attempt that will be
- shut down (simultaneous connect where A's name is
- greater than B's name, compared literally).</item>
- <tag><c>nok</c></tag>
- <item>The handshake will not continue, as B already has an ongoing handshake
- which it itself has initiated. (simultaneous connect where B's name is
- greater than A's).</item>
- <tag><c>not_allowed</c></tag>
- <item>The connection is disallowed for some (unspecified) security
- reason.</item>
- <tag><c>alive</c></tag>
- <item>A connection to the node is already active, which either means
- that node A is confused or that the TCP connection breakdown
- of a previous node with this name has not yet reached node B.
- See 3B below.</item>
- </taglist>
- <p>This is the format of the status message:</p>
-<pre>
++---+--------+--------+-----+-----+-----+-----+-----+-----+-... +-----+</pre>
+ <p>'n' is the message tag. 'Version0' and 'Version1' is the
+ distribution version selected by <c>A</c>, based on information
+ from the EPMD. (16-bit big-endian) 'Flag0' ... 'Flag3' are
+ capability flags, the capabilities are defined in
+ <c>$ERL_TOP/lib/kernel/include/dist.hrl</c>. (32-bit big-endian)
+ 'Name0' ... 'NameN' is the full node name of <c>A</c>, as a string
+ of bytes (the packet length denotes how long it is).</p>
+ </item>
+ <tag>3) <c>recv_status</c>/<c>send_status</c></tag>
+ <item>
+ <p><c>B</c> sends a status message to <c>A</c>, which indicates if the
+ connection is allowed. The following status codes are defined:</p>
+ <taglist>
+ <tag><c>ok</c></tag>
+ <item>
+ <p>The handshake will continue.</p>
+ </item>
+ <tag><c>ok_simultaneous</c></tag>
+ <item>
+ <p>The handshake will continue, but <c>A</c> is informed that
+ <c>B</c> has another ongoing connection attempt that will be
+ shut down (simultaneous connect where <c>A</c>'s name is
+ greater than <c>B</c>'s name, compared literally).</p>
+ </item>
+ <tag><c>nok</c></tag>
+ <item>
+ <p>The handshake will not continue, as <c>B</c> already has an
+ ongoing handshake, which it itself has initiated (simultaneous
+ connect where <c>B</c>'s name is greater than <c>A</c>'s).</p>
+ </item>
+ <tag><c>not_allowed</c></tag>
+ <item>
+ <p>The connection is disallowed for some (unspecified) security
+ reason.</p>
+ </item>
+ <tag><c>alive</c></tag>
+ <item>
+ <p>A connection to the node is already active, which either means
+ that node <c>A</c> is confused or that the TCP connection
+ breakdown of a previous node with this name has not yet reached
+ node <c>B</c>. See step 3B below.</p>
+ </item>
+ </taglist>
+ <p>The format of the status message is as follows:</p>
+ <pre>
+---+-------+-------+-...-+-------+
|'s'|Status0|Status1| ... |StatusN|
-+---+-------+-------+-...-+-------+
-</pre>
- <p>
- 's' is the message tag Status0 ... StatusN is the status as a string (not terminated)
- </p>
- </item>
- <tag>3B) send_status/recv_status</tag>
- <item><p>If status was 'alive', node A will answer with
- another status message containing either 'true' which means that the
- connection should continue (The old connection from this node is broken), or
- <c>'false'</c>, which simply means that the connection should be closed, the
- connection attempt was a mistake.</p></item>
- <tag>4) recv_challenge/send_challenge</tag>
- <item><p>If the status was <c>ok</c> or <c>ok_simultaneous</c>,
- The handshake continues with B sending A another message, the challenge.
- The challenge contains the same type of information as the "name" message
- initially sent from A to B, with the addition of a 32 bit challenge:</p>
-<pre>
++---+-------+-------+-...-+-------+</pre>
+ <p>'s' is the message tag. 'Status0' ... 'StatusN' is the status as a
+ string (not terminated).</p>
+ </item>
+ <tag>3B) <c>send_status</c>/<c>recv_status</c></tag>
+ <item>
+ <p>If status was <c>alive</c>, node <c>A</c> answers with another
+ status message containing either <c>true</c>, which means that the
+ connection is to continue (the old connection from this node is
+ broken), or <c>false</c>, which means that the connection is to be
+ closed (the connection attempt was a mistake.</p>
+ </item>
+ <tag>4) <c>recv_challenge</c>/<c>send_challenge</c></tag>
+ <item>
+ <p>If the status was <c>ok</c> or <c>ok_simultaneous</c>, the
+ handshake continues with <c>B</c> sending <c>A</c> another message,
+ the challenge. The challenge contains the same type of information
+ as the "name" message initially sent from <c>A</c> to <c>B</c>, plus
+ a 32-bit challenge:</p>
+ <pre>
+---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-...-+-----+
|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Chal0|Chal1|Chal2|Chal3|Name0|Name1| ... |NameN|
-+---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-... +-----+
-</pre>
- <p>
- Where Chal0 ... Chal3 is the challenge as a 32 bit big endian integer
- and the other fields are B's version, flags and full nodename.
- </p></item>
- <tag>5) send_challenge_reply/recv_challenge_reply</tag>
- <item><p>Now A has generated a digest and its own challenge. Those are
- sent together in a package to B:</p>
-<pre>
++---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-... +-----+</pre>
+ <p>'Chal0' ... 'Chal3' is the challenge as a 32-bit big-endian integer
+ and the other fields are <c>B</c>'s version, flags, and full node
+ name.</p>
+ </item>
+ <tag>5) <c>send_challenge_reply</c>/<c>recv_challenge_reply</c></tag>
+ <item>
+ <p>Now <c>A</c> has generated a digest and its own challenge. Those
+ are sent together in a package to <c>B</c>:</p>
+ <pre>
+---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
|'r'|Chal0|Chal1|Chal2|Chal3|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
-+---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
-</pre>
- <p>
- Where 'r' is the tag, Chal0 ... Chal3 is A's challenge for B to handle and
- Dige0 ... Dige15 is the digest that A constructed from the challenge B sent
- in the previous step.
- </p></item>
- <tag>6) recv_challenge_ack/send_challenge_ack</tag>
- <item><p>B checks that the digest received from A is correct and generates a
- digest from the challenge received from A. The digest is then sent to A. The
- message looks like this:</p>
-<pre>
++---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+</pre>
+ <p>'r' is the tag. 'Chal0' ... 'Chal3' is <c>A</c>'s challenge for
+ <c>B</c> to handle. 'Dige0' ... 'Dige15' is the digest that <c>A</c>
+ constructed from the challenge <c>B</c> sent in the previous
+ step.</p>
+ </item>
+ <tag>6) <c>recv_challenge_ack</c>/<c>send_challenge_ack</c></tag>
+ <item>
+ <p><c>B</c> checks that the digest received from <c>A</c> is correct
+ and generates a digest from the challenge received from <c>A</c>.
+ The digest is then sent to <c>A</c>. The message is as follows:</p>
+ <pre>
+---+-----+-----+-----+-----+-...-+------+
|'a'|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
-+---+-----+-----+-----+-----+-...-+------+
-</pre>
- <p>
- Where 'a' is the tag and Dige0 ... Dige15 is the digest calculated by B
- for A's challenge.</p></item>
- <tag>7)</tag>
- <item><p>A checks the digest from B and the connection is up.</p></item>
- </taglist>
- </section>
- <section>
- <title>Semigraphic View</title>
-<pre>
-A (initiator) B (acceptor)
-
-TCP connect -----------------------------------------&gt;
- TCP accept
-
-send_name -----------------------------------------&gt;
- recv_name
-
- &lt;---------------------------------------- send_status
++---+-----+-----+-----+-----+-...-+------+</pre>
+ <p>'a' is the tag. 'Dige0' ... 'Dige15' is the digest calculated by
+ <c>B</c> for <c>A</c>'s challenge.</p>
+ </item>
+ <tag>7) check</tag>
+ <item>
+ <p><c>A</c> checks the digest from <c>B</c> and the connection is
+ up.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Semigraphic View</title>
+ <pre>
+A (initiator) B (acceptor)
+
+TCP connect ------------------------------------&gt;
+ TCP accept
+
+send_name --------------------------------------&gt;
+ recv_name
+
+ &lt;---------------------------------------------- send_status
recv_status
(if status was 'alive'
- send_status - - - - - - - - - - - - - - - - - - - -&gt;
- recv_status)
- ChB = gen_challenge()
- (ChB)
- &lt;---------------------------------------- send_challenge
+ send_status - - - - - - - - - - - - - - - - - -&gt;
+ recv_status)
+ ChB = gen_challenge()
+ (ChB)
+ &lt;---------------------------------------------- send_challenge
recv_challenge
-
ChA = gen_challenge(),
OCA = out_cookie(B),
-DiA = gen_digest(ChB,OCA)
- (ChA, DiA)
-send_challenge_reply --------------------------------&gt;
- recv_challenge_reply
- ICB = in_cookie(A),
- check:
- DiA == gen_digest
- (ChB, ICB) ?
- - if OK:
- OCB = out_cookie(A),
- DiB = gen_digest
- (DiB) (ChA, OCB)
- &lt;----------------------------------------- send_challenge_ack
-recv_challenge_ack DONE
-ICA = in_cookie(B), - else
-check: CLOSE
-DiB == gen_digest(ChA,ICA) ?
-- if OK
+DiA = gen_digest(ChB, OCA)
+ (ChA, DiA)
+send_challenge_reply ---------------------------&gt;
+ recv_challenge_reply
+ ICB = in_cookie(A),
+ check:
+ DiA == gen_digest (ChB, ICB)?
+ - if OK:
+ OCB = out_cookie(A),
+ DiB = gen_digest (ChA, OCB)
+ (DiB)
+ &lt;----------------------------------------------- send_challenge_ack
+recv_challenge_ack DONE
+ICA = in_cookie(B), - else:
+check: CLOSE
+DiB == gen_digest(ChA, ICA)?
+- if OK:
DONE
-- else
- CLOSE
-</pre>
- </section>
- <marker id="dflags"/>
- <section>
- <title>The Currently Defined Distribution Flags</title>
- <p>
- Currently (OTP-R16) the following capability flags are defined:
- </p>
-<pre>
-%% The node should be published and part of the global namespace
--define(DFLAG_PUBLISHED,1).
-
-%% The node implements an atom cache (obsolete)
--define(DFLAG_ATOM_CACHE,2).
-
-%% The node implements extended (3 * 32 bits) references. This is
-%% required today. If not present connection will be refused.
--define(DFLAG_EXTENDED_REFERENCES,4).
-
-%% The node implements distributed process monitoring.
--define(DFLAG_DIST_MONITOR,8).
-
-%% The node uses separate tag for fun's (lambdas) in the distribution protocol.
--define(DFLAG_FUN_TAGS,16#10).
-
-%% The node implements distributed named process monitoring.
--define(DFLAG_DIST_MONITOR_NAME,16#20).
-
-%% The (hidden) node implements atom cache (obsolete)
--define(DFLAG_HIDDEN_ATOM_CACHE,16#40).
-
-%% The node understand new fun-tags
--define(DFLAG_NEW_FUN_TAGS,16#80).
-
-%% The node is capable of handling extended pids and ports. This is
-%% required today. If not present connection will be refused.
--define(DFLAG_EXTENDED_PIDS_PORTS,16#100).
-
-%%
--define(DFLAG_EXPORT_PTR_TAG,16#200).
-
-%%
--define(DFLAG_BIT_BINARIES,16#400).
-
-%% The node understands new float format
--define(DFLAG_NEW_FLOATS,16#800).
-
-%%
--define(DFLAG_UNICODE_IO,16#1000).
-
-%% The node implements atom cache in distribution header.
--define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000).
-
-%% The node understand the SMALL_ATOM_EXT tag
--define(DFLAG_SMALL_ATOM_TAGS, 16#4000).
-
-%% The node understand UTF-8 encoded atoms
--define(DFLAG_UTF8_ATOMS, 16#10000).
-
-</pre>
- </section>
- </section>
-
- <section>
- <marker id="connected_nodes"/>
- <title>Protocol between connected nodes</title>
- <p>
- As of erts version 5.7.2 the runtime system passes a distribution
- flag in the handshake stage that enables the use of a
- <seealso marker="erl_ext_dist#distribution_header">distribution
- header</seealso> on all messages passed. Messages passed between
- nodes are in this case on the following format:
- </p>
- <table align="left">
- <row>
- <cell align="center">4</cell>
- <cell align="center">d</cell>
- <cell align="center">n</cell>
- <cell align="center">m</cell>
- </row>
- <row>
- <cell align="center"><c>Length</c></cell>
- <cell align="center"><c>DistributionHeader</c></cell>
- <cell align="center"><c>ControlMessage</c></cell>
- <cell align="center"><c>Message</c></cell>
- </row>
- <tcaption></tcaption></table>
- <p>
- where:
- </p>
- <p>
- <c>Length</c> is equal to d + n + m
- </p>
- <p>
- <c>ControlMessage</c> is a tuple passed using the external format of
- Erlang.
- </p>
- <p>
- <c>Message</c> is the message sent to another node using the '!'
- (in external format). Note that <c>Message</c> is only passed in
- combination with a <c>ControlMessage</c> encoding a send ('!').
- </p>
- <p>
- Also note that <seealso marker="erl_ext_dist#overall_format">the
- version number is omitted from the terms that follow a
- distribution header</seealso>.
- </p>
- <p>
- Nodes with an erts version less than 5.7.2 does not pass the
- distribution flag that enables the distribution header. Messages
- passed between nodes are in this case on the following format:
- </p>
- <table align="left">
- <row>
- <cell align="center">4</cell>
- <cell align="center">1</cell>
- <cell align="center">n</cell>
- <cell align="center">m</cell>
- </row>
- <row>
- <cell align="center"><c>Length</c></cell>
- <cell align="center"><c>Type</c></cell>
- <cell align="center"><c>ControlMessage</c></cell>
- <cell align="center"><c>Message</c></cell>
- </row>
- <tcaption></tcaption></table>
- <p>
- where:
- </p>
- <p>
- <c>Length</c> is equal to 1 + n + m
- </p>
- <p>
- Type is: 112 (pass through)
- </p>
- <p>
- <c>ControlMessage</c> is a tuple passed using the external format of
- Erlang.
- </p>
- <p>
- <c>Message</c> is the message sent to another node using the '!'
- (in external format). Note that <c>Message</c> is only passed in
- combination with a <c>ControlMessage</c> encoding a send ('!').
- </p>
- <p>
- The <c>ControlMessage</c> is a tuple, where the first element
- indicates which distributed operation it encodes.
- </p>
- <taglist>
- <tag><c>LINK</c></tag>
- <item>
- <p>
- <c>{1, FromPid, ToPid}</c>
- </p>
- </item>
-
- <tag><c>SEND</c></tag>
- <item>
- <p>
- <c>{2, Unused, ToPid}</c>
- </p>
- <p>
- <em>Note</em> followed by <c>Message</c>
- </p>
- <p>
- <c>Unused</c> is kept for backward compatibility
- </p>
- </item>
-
- <tag><c>EXIT</c></tag>
- <item>
- <p>
- <c>{3, FromPid, ToPid, Reason}</c>
- </p>
- </item>
-
- <tag><c>UNLINK</c></tag>
- <item>
- <p>
- <c>{4, FromPid, ToPid}</c>
- </p>
- </item>
-
- <tag><c>NODE_LINK</c></tag>
- <item>
- <p>
- <c>{5}</c>
- </p>
- </item>
-
- <tag><c>REG_SEND</c></tag>
- <item>
- <p>
- <c>{6, FromPid, Unused, ToName}</c>
- </p>
- <p>
- <em>Note</em> followed by <c>Message</c>
- </p>
- <p>
- <c>Unused</c> is kept for backward compatibility
- </p>
- </item>
-
- <tag><c>GROUP_LEADER</c></tag>
- <item>
- <p>
- <c>{7, FromPid, ToPid}</c>
- </p>
- </item>
-
- <tag><c>EXIT2</c></tag>
- <item>
- <p>
- <c>{8, FromPid, ToPid, Reason}</c>
- </p>
- </item>
- </taglist>
- </section>
-
-
- <section>
- <title>New Ctrlmessages for distrvsn = 1 (OTP R4)</title>
- <taglist>
- <tag><c>SEND_TT</c></tag>
- <item>
- <p>
- <c>{12, Unused, ToPid, TraceToken}</c>
- </p>
- <p>
- <em>Note</em> followed by <c>Message</c>
- </p>
- <p>
- <c>Unused</c> is kept for backward compatibility
- </p>
- </item>
-
- <tag><c>EXIT_TT</c></tag>
- <item>
- <p>
- <c>{13, FromPid, ToPid, TraceToken, Reason}</c>
- </p>
- </item>
-
- <tag><c>REG_SEND_TT</c></tag>
- <item>
- <p>
- <c>{16, FromPid, Unused, ToName, TraceToken}</c>
- </p>
- <p>
- <em>Note</em> followed by <c>Message</c>
- </p>
- <p>
- <c>Unused</c> is kept for backward compatibility
- </p>
- </item>
-
- <tag><c>EXIT2_TT</c></tag>
- <item>
- <p>
- <c>{18, FromPid, ToPid, TraceToken, Reason}</c>
- </p>
- </item>
- </taglist>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 2</title>
- <p>
- distrvsn 2 was never used.
- </p>
- </section>
-
- <section>
- <title>New Ctrlmessages for distrvsn = 3 (OTP R5C)</title>
- <p>
- None, but the version number was increased anyway.
- </p>
+- else:
+ CLOSE</pre>
</section>
<section>
- <title>New Ctrlmessages for distrvsn = 4 (OTP R6)</title>
- <p>
- These are only recognized by Erlang nodes, not by hidden nodes.
- </p>
+ <marker id="dflags"/>
+ <title>Distribution Flags</title>
+ <p>The following capability flags are defined:</p>
<taglist>
- <tag><c>MONITOR_P</c></tag>
- <item>
- <p>
- <c>{19, FromPid, ToProc, Ref}</c>
-
- <c>FromPid</c> = monitoring process
- <c>ToProc</c> = monitored process pid or name (atom)
- </p>
- </item>
-
- <tag><c>DEMONITOR_P</c></tag>
- <item>
- <p>
- <c>{20, FromPid, ToProc, Ref}</c>
- We include the FromPid just in case we want to trace this.
-
- <c>FromPid</c> = monitoring process
- <c>ToProc</c> = monitored process pid or name (atom)
- </p>
- </item>
-
- <tag><c>MONITOR_P_EXIT</c></tag>
- <item>
- <p>
- <c>{21, FromProc, ToPid, Ref, Reason}</c>
-
- <c>FromProc</c> = monitored process pid or name (atom)
- <c>ToPid</c> = monitoring process
- <c>Reason</c> = exit reason for the monitored process
- </p>
- </item>
+ <tag><c>-define(DFLAG_PUBLISHED,16#1).</c></tag>
+ <item>
+ <p>The node is to be published and part of the global namespace.</p>
+ </item>
+ <tag><c>-define(DFLAG_ATOM_CACHE,16#2).</c></tag>
+ <item>
+ <p>The node implements an atom cache (obsolete).</p>
+ </item>
+ <tag><c>-define(DFLAG_EXTENDED_REFERENCES,16#4).</c></tag>
+ <item>
+ <p>The node implements extended (3 &times; 32 bits) references. This
+ is required today. If not present, the connection is refused.</p>
+ </item>
+ <tag><c>-define(DFLAG_DIST_MONITOR,16#8).</c></tag>
+ <item>
+ <p>The node implements distributed process monitoring.</p>
+ </item>
+ <tag><c>-define(DFLAG_FUN_TAGS,16#10).</c></tag>
+ <item>
+ <p>The node uses separate tag for funs (lambdas) in the distribution
+ protocol.</p>
+ </item>
+ <tag><c>-define(DFLAG_DIST_MONITOR_NAME,16#20).</c></tag>
+ <item>
+ <p>The node implements distributed named process monitoring.</p>
+ </item>
+ <tag><c>-define(DFLAG_HIDDEN_ATOM_CACHE,16#40).</c></tag>
+ <item>
+ <p>The (hidden) node implements atom cache (obsolete).</p>
+ </item>
+ <tag><c>-define(DFLAG_NEW_FUN_TAGS,16#80).</c></tag>
+ <item>
+ <p>The node understand new fun tags.</p>
+ </item>
+ <tag><c>-define(DFLAG_EXTENDED_PIDS_PORTS,16#100).</c></tag>
+ <item>
+ <p>The node can handle extended pids and ports. This is required
+ today. If not present, the connection is refused.</p>
+ </item>
+ <tag><c>-define(DFLAG_EXPORT_PTR_TAG,16#200).</c></tag>
+ <item>
+ </item>
+ <tag><c>-define(DFLAG_BIT_BINARIES,16#400).</c></tag>
+ <item>
+ </item>
+ <tag><c>-define(DFLAG_NEW_FLOATS,16#800).</c></tag>
+ <item>
+ <p>The node understands new float format.</p>
+ </item>
+ <tag><c>-define(DFLAG_UNICODE_IO,16#1000).</c></tag>
+ <item>
+ </item>
+ <tag><c>-define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000).</c></tag>
+ <item>
+ <p>The node implements atom cache in distribution header.</p>
+ </item>
+ <tag><c>-define(DFLAG_SMALL_ATOM_TAGS, 16#4000).</c></tag>
+ <item>
+ <p>The node understand the <c>SMALL_ATOM_EXT</c> tag.</p>
+ </item>
+ <tag><c>-define(DFLAG_UTF8_ATOMS, 16#10000).</c></tag>
+ <item>
+ <p>The node understand UTF-8 encoded atoms.</p>
+ </item>
</taglist>
</section>
- </chapter>
+ </section>
+
+ <section>
+ <marker id="connected_nodes"/>
+ <title>Protocol between Connected Nodes</title>
+ <p>As from ERTS 5.7.2 the runtime system passes a distribution flag
+ in the handshake stage that enables the use of a
+ <seealso marker="erl_ext_dist#distribution_header">distribution header
+ </seealso> on all messages passed. Messages passed between nodes have in
+ this case the following format:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">4</cell>
+ <cell align="center">d</cell>
+ <cell align="center">n</cell>
+ <cell align="center">m</cell>
+ </row>
+ <row>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>DistributionHeader</c></cell>
+ <cell align="center"><c>ControlMessage</c></cell>
+ <cell align="center"><c>Message</c></cell>
+ </row>
+ <tcaption>Format of Messages Passed between Nodes (as from ERTS 5.7.2)
+ </tcaption>
+ </table>
+
+ <taglist>
+ <tag><c>Length</c></tag>
+ <item>
+ <p>Equal to d + n + m.</p>
+ </item>
+ <tag><c>ControlMessage</c></tag>
+ <item>
+ <p>A tuple passed using the external format of Erlang.</p>
+ </item>
+ <tag><c>Message</c></tag>
+ <item>
+ <p>The message sent to another node using the '!' (in external format).
+ Notice that <c>Message</c> is only passed in combination with a
+ <c>ControlMessage</c> encoding a send ('!').</p>
+ </item>
+ </taglist>
+
+ <p>Notice that <seealso marker="erl_ext_dist#overall_format">the version
+ number is omitted from the terms that follow a distribution header
+ </seealso>.</p>
+
+ <p>Nodes with an ERTS version earlier than 5.7.2 does not pass the
+ distribution flag that enables the distribution header. Messages passed
+ between nodes have in this case the following format:</p>
+
+ <table align="left">
+ <row>
+ <cell align="center">4</cell>
+ <cell align="center">1</cell>
+ <cell align="center">n</cell>
+ <cell align="center">m</cell>
+ </row>
+ <row>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>Type</c></cell>
+ <cell align="center"><c>ControlMessage</c></cell>
+ <cell align="center"><c>Message</c></cell>
+ </row>
+ <tcaption>Format of Messages Passed between Nodes (before ERTS 5.7.2)
+ </tcaption>
+ </table>
+
+ <taglist>
+ <tag><c>Length</c></tag>
+ <item>
+ <p>Equal to 1 + n + m.</p>
+ </item>
+ <tag><c>Type</c></tag>
+ <item>
+ <p>Equal to <c>112</c> (pass through).</p>
+ </item>
+ <tag><c>ControlMessage</c></tag>
+ <item>
+ <p>A tuple passed using the external format of Erlang.</p>
+ </item>
+ <tag><c>Message</c></tag>
+ <item>
+ <p>The message sent to another node using the '!' (in external format).
+ Notice that <c>Message</c> is only passed in combination with a
+ <c>ControlMessage</c> encoding a send ('!').</p>
+ </item>
+ </taglist>
+
+ <p>The <c>ControlMessage</c> is a tuple, where the first element indicates
+ which distributed operation it encodes:</p>
+
+ <taglist>
+ <tag><c>LINK</c></tag>
+ <item>
+ <p><c>{1, FromPid, ToPid}</c></p>
+ </item>
+ <tag><c>SEND</c></tag>
+ <item>
+ <p><c>{2, Unused, ToPid}</c></p>
+ <p>Followed by <c>Message</c>.</p>
+ <p><c>Unused</c> is kept for backward compatibility.</p>
+ </item>
+ <tag><c>EXIT</c></tag>
+ <item>
+ <p><c>{3, FromPid, ToPid, Reason}</c></p>
+ </item>
+ <tag><c>UNLINK</c></tag>
+ <item>
+ <p><c>{4, FromPid, ToPid}</c></p>
+ </item>
+ <tag><c>NODE_LINK</c></tag>
+ <item>
+ <p><c>{5}</c></p>
+ </item>
+ <tag><c>REG_SEND</c></tag>
+ <item>
+ <p><c>{6, FromPid, Unused, ToName}</c></p>
+ <p>Followed by <c>Message</c>.</p>
+ <p><c>Unused</c> is kept for backward compatibility.</p>
+ </item>
+ <tag><c>GROUP_LEADER</c></tag>
+ <item>
+ <p><c>{7, FromPid, ToPid}</c></p>
+ </item>
+ <tag><c>EXIT2</c></tag>
+ <item>
+ <p><c>{8, FromPid, ToPid, Reason}</c></p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>New Ctrlmessages for distrvsn = 1 (Erlang/OTP R4)</title>
+ <taglist>
+ <tag><c>SEND_TT</c></tag>
+ <item>
+ <p><c>{12, Unused, ToPid, TraceToken}</c></p>
+ <p>Followed by <c>Message</c>.</p>
+ <p><c>Unused</c> is kept for backward compatibility.</p>
+ </item>
+ <tag><c>EXIT_TT</c></tag>
+ <item>
+ <p><c>{13, FromPid, ToPid, TraceToken, Reason}</c></p>
+ </item>
+ <tag><c>REG_SEND_TT</c></tag>
+ <item>
+ <p><c>{16, FromPid, Unused, ToName, TraceToken}</c></p>
+ <p>Followed by <c>Message</c>.</p>
+ <p><c>Unused</c> is kept for backward compatibility.</p>
+ </item>
+ <tag><c>EXIT2_TT</c></tag>
+ <item>
+ <p><c>{18, FromPid, ToPid, TraceToken, Reason}</c></p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>New Ctrlmessages for distrvsn = 2</title>
+ <p><c>distrvsn</c> 2 was never used.</p>
+ </section>
+
+ <section>
+ <title>New Ctrlmessages for distrvsn = 3 (Erlang/OTP R5C)</title>
+ <p>None, but the version number was increased anyway.</p>
+ </section>
+
+ <section>
+ <title>New Ctrlmessages for distrvsn = 4 (Erlang/OTP R6)</title>
+ <p>These are only recognized by Erlang nodes, not by hidden nodes.</p>
+
+ <taglist>
+ <tag><c>MONITOR_P</c></tag>
+ <item>
+ <p><c>{19, FromPid, ToProc, Ref}</c>, where
+ <c>FromPid</c> = monitoring process and
+ <c>ToProc</c> = monitored process pid or name (atom)</p>
+ </item>
+ <tag><c>DEMONITOR_P</c></tag>
+ <item>
+ <p><c>{20, FromPid, ToProc, Ref}</c>, where
+ <c>FromPid</c> = monitoring process and
+ <c>ToProc</c> = monitored process pid or name (atom)</p>
+ <p>We include <c>FromPid</c> just in case we want to trace this.</p>
+ </item>
+ <tag><c>MONITOR_P_EXIT</c></tag>
+ <item>
+ <p><c>{21, FromProc, ToPid, Ref, Reason}</c>, where
+ <c>FromProc</c> = monitored process pid or name (atom),
+ <c>ToPid</c> = monitoring process, and
+ <c>Reason</c> = exit reason for the monitored process</p>
+ </item>
+ </taglist>
+ </section>
+</chapter>
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index 82215ead46..5705100ab2 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2016</year>
+ <year>2001</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -21,7 +21,6 @@
limitations under the License.
</legalnotice>
-
<title>erl_driver</title>
<prepared>Jakob Cederlund</prepared>
<responsible>Jakob Cederlund</responsible>
@@ -33,519 +32,545 @@
<file>erl_driver.xml</file>
</header>
<lib>erl_driver</lib>
- <libsummary>API functions for an Erlang driver</libsummary>
+ <libsummary>API functions for an Erlang driver.</libsummary>
<description>
<p>An Erlang driver is a library containing a set of native driver
- callback functions that the Erlang VM calls when certain
- events occur. There may be multiple instances of a driver, each
+ callback functions that the Erlang Virtual Machine calls when certain
+ events occur. There can be multiple instances of a driver, each
instance is associated with an Erlang port.</p>
+
<marker id="WARNING"/>
- <warning><p><em>Use this functionality with extreme care!</em></p>
+ <warning>
+ <p><em>Use this functionality with extreme care.</em></p>
<p>A driver callback is executed as a direct extension of the
- native code of the VM. Execution is not made in a safe environment.
- The VM can <em>not</em> provide the same services as provided when
- executing Erlang code, such as preemptive scheduling or memory
- protection. If the driver callback function doesn't behave well,
- the whole VM will misbehave.</p>
- <list>
- <item><p>A driver callback that crash will crash the whole VM.</p></item>
- <item><p>An erroneously implemented driver callback might cause
- a VM internal state inconsistency which may cause a crash of the VM,
- or miscellaneous misbehaviors of the VM at any point after the call
- to the driver callback.</p></item>
- <item><p>A driver callback that do <seealso marker="#lengthy_work">lengthy
- work</seealso> before returning will degrade responsiveness of the VM,
- and may cause miscellaneous strange behaviors. Such strange behaviors
- include, but are not limited to, extreme memory usage, and bad load
- balancing between schedulers. Strange behaviors that might occur due
- to lengthy work may also vary between OTP releases.</p></item>
+ native code of the VM. Execution is not made in a safe environment.
+ The VM <em>cannot</em> provide the same services as provided when
+ executing Erlang code, such as pre-emptive scheduling or memory
+ protection. If the driver callback function does not behave well,
+ the whole VM will misbehave.</p>
+ <list type="bulleted">
+ <item>
+ <p>A driver callback that crash will crash the whole VM.</p>
+ </item>
+ <item>
+ <p>An erroneously implemented driver callback can cause a VM
+ internal state inconsistency, which can cause a crash of the VM,
+ or miscellaneous misbehaviors of the VM at any point after the
+ call to the driver callback.</p>
+ </item>
+ <item>
+ <p>A driver callback doing
+ <seealso marker="#lengthy_work">lengthy work</seealso> before
+ returning degrades responsiveness of the VM and can cause
+ miscellaneous strange behaviors. Such strange behaviors
+ include, but are not limited to, extreme memory usage and bad
+ load balancing between schedulers. Strange behaviors that can
+ occur because of lengthy work can also vary between Erlang/OTP
+ releases.</p>
+ </item>
</list>
- </warning>
- <p>As of erts version 5.5.3 the driver interface has been extended
- (see <seealso marker="driver_entry#extended_marker">extended marker</seealso>).
- The extended interface introduce
+ </warning>
+
+ <p>As from ERTS 5.5.3 the driver interface has been extended
+ (see <seealso marker="driver_entry#extended_marker">
+ <c>extended marker</c></seealso>). The extended interface introduces
<seealso marker="#version_management">version management</seealso>,
- the possibility to pass capability flags
- (see <seealso marker="driver_entry#driver_flags">driver flags</seealso>)
- to the runtime system at driver initialization, and some new
- driver API functions. </p>
+ the possibility to pass capability flags (see
+ <seealso marker="driver_entry#driver_flags">
+ <c>driver_flags</c></seealso>) to the runtime system at driver
+ initialization, and some new driver API functions.</p>
+
<note>
- <p>As of erts version 5.9 old drivers have to be recompiled
- and have to use the extended interface. They also have to be
- adjusted to the
- <seealso marker="#rewrites_for_64_bits">64-bit capable driver interface.
- </seealso>
- </p>
+ <p>As from ERTS 5.9 old drivers must be recompiled
+ and use the extended interface. They must also be adjusted to the
+ <seealso marker="#rewrites_for_64_bits">
+ 64-bit capable driver interface</seealso>.</p>
</note>
+
<p>The driver calls back to the emulator, using the API
functions declared in <c>erl_driver.h</c>. They are used for
- outputting data from the driver, using timers, etc.</p>
+ outputting data from the driver, using timers, and so on.</p>
+
<p>Each driver instance is associated with a port. Every port
has a port owner process. Communication with the port is normally
done through the port owner process. Most of the functions take
the <c>port</c> handle as an argument. This identifies the driver
- instance. Note that this port handle must be stored by the driver,
+ instance. Notice that this port handle must be stored by the driver,
it is not given when the driver is called from the emulator (see
- <seealso marker="driver_entry#emulator">driver_entry</seealso>).</p>
+ <seealso marker="driver_entry#emulator">
+ <c>driver_entry</c></seealso>).</p>
+
<p>Some of the functions take a parameter of type
- <c>ErlDrvBinary</c>, a driver binary. It should be both
+ <c>ErlDrvBinary</c>, a driver binary. It is to be both
allocated and freed by the caller. Using a binary directly avoids
one extra copying of data.</p>
+
<p>Many of the output functions have a "header buffer", with
<c>hbuf</c> and <c>hlen</c> parameters. This buffer is sent as a
list before the binary (or list, depending on port mode) that is
sent. This is convenient when matching on messages received from
- the port. (Although in the latest versions of Erlang, there is
- the binary syntax, that enables you to match on the beginning of
- a binary.)
- <marker id="smp_support"></marker>
-</p>
- <p>In the runtime system with SMP support, drivers are locked either
- on driver level or port level (driver instance level). By default
- driver level locking will be used, i.e., only one emulator thread
+ the port. (Although in the latest Erlang versions there is
+ the binary syntax, which enables you to match on the beginning of
+ a binary.)</p>
+ <p><marker id="smp_support"></marker>In the runtime system with
+ SMP support, drivers are locked either on driver level
+ or port level (driver instance level). By default
+ driver level locking will be used, that is, only one emulator thread
will execute code in the driver at a time. If port level locking
- is used, multiple emulator threads may execute code in the driver
- at the same time. There will only be one thread at a time calling
- driver call-backs corresponding to the same port, though. In order
- to enable port level locking set the <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c>
+ is used, multiple emulator threads can execute code in the driver
+ at the same time. Only one thread at a time will call
+ driver callbacks corresponding to the same port, though.
+ To enable port level locking, set the <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c>
<seealso marker="driver_entry#driver_flags">driver flag</seealso> in
- the <seealso marker="driver_entry">driver_entry</seealso>
- used by the driver. When port level locking is used it is the
- responsibility of the driver writer to synchronize all accesses
+ the <seealso marker="driver_entry"><c>driver_entry</c></seealso>
+ used by the driver. When port level locking is used,
+ the driver writer is responsible for synchronizing all accesses
to data shared by the ports (driver instances).</p>
+
<p>Most drivers written before the runtime system with SMP
- support existed will be able to run in the runtime system
- with SMP support without being rewritten if driver
+ support existed can run in the runtime system
+ with SMP support, without being rewritten, if driver
level locking is used.</p>
+
<note>
<p>It is assumed that drivers do not access other drivers. If
- drivers should access each other they have to provide their own
- mechanism for thread safe synchronization. Such "inter driver
+ drivers access each other, they must provide their own
+ mechanism for thread-safe synchronization. Such "inter-driver
communication" is strongly discouraged.</p>
</note>
+
<p>Previously, in the runtime system without SMP support,
- specific driver call-backs were always called from the same
+ specific driver callbacks were always called from the same
thread. This is <em>not</em> the case in the runtime system
with SMP support. Regardless of locking scheme used, calls
- to driver call-backs may be made from different threads, e.g.,
- two consecutive calls to exactly the same call-back for exactly
- the same port may be made from two different threads. This
- will for <em>most</em> drivers not be a problem, but it might.
- Drivers that depend on all call-backs being called in the
- same thread, <em>have</em> to be rewritten before being used
+ to driver callbacks can be made from different threads. For example,
+ two consecutive calls to exactly the same callback for exactly
+ the same port can be made from two different threads. This
+ is for <em>most</em> drivers not a problem, but it can be.
+ Drivers that depend on all callbacks that are called in the
+ same thread, <em>must</em> be rewritten before they are used
in the runtime system with SMP support.</p>
+
<note>
<p>Regardless of locking scheme used, calls to driver
- call-backs may be made from different threads.</p>
+ callbacks can be made from different threads.</p>
</note>
- <p>Most functions in this API are <em>not</em> thread-safe, i.e.,
- they may <em>not</em> be called from an arbitrary thread. Functions
- that are not documented as thread-safe may only be called from
- driver call-backs or function calls descending from a driver
- call-back call. Note that driver call-backs may be called from
+
+ <p>Most functions in this API are <em>not</em> thread-safe, that is,
+ they <em>cannot</em> be called from any thread. Functions
+ that are not documented as thread-safe can only be called from
+ driver callbacks or function calls descending from a driver
+ callback call. Notice that driver callbacks can be called from
different threads. This, however, is not a problem for any
- function in this API, since the emulator has control over
+ function in this API, as the emulator has control over
these threads.</p>
+
<warning>
- <p>Functions not explicitly documented as thread safe are
- <em>not</em> thread safe. Also note that some functions
- are <em>only</em> thread safe when used in a runtime
+ <p>Functions not explicitly documented as thread-safe are
+ <em>not</em> thread safe. Also notice that some functions
+ are <em>only</em> thread-safe when used in a runtime
system with SMP support.</p>
- <p>A function not explicitly documented as thread safe may at
- some point in time have a thread safe implementation in the
- runtime system. Such an implementation may however change to
- a thread <em>unsafe</em> implementation at any time <em>without
- any notice</em> at all.
- </p>
- <p><em>Only use functions explicitly documented as thread safe
- from arbitrary threads.</em></p>
+ <p>A function not explicitly documented as thread-safe can, at
+ some point in time, have a thread-safe implementation in the
+ runtime system. Such an implementation can however change to
+ a thread <em>unsafe</em> implementation at any time <em>without
+ any notice</em>.</p>
+ <p><em>Only use functions explicitly documented as thread-safe
+ from arbitrary threads.</em></p>
</warning>
- <p><marker id="lengthy_work"/>
- As mentioned in the <seealso marker="#WARNING">warning</seealso> text at
- the beginning of this document it is of vital importance that a driver callback
- does return relatively fast. It is hard to give an exact maximum amount
- of time that a driver callback is allowed to work, but as a rule of thumb
- a well behaving driver callback should return before a millisecond has
- passed. This can be achieved using different approaches.
- If you have full control over the code that are to execute in the driver
- callback, the best approach is to divide the work into multiple chunks of
- work and trigger multiple calls to the
- <seealso marker="driver_entry#timeout">timeout callback</seealso> using
- zero timeouts. The
- <seealso marker="#erl_drv_consume_timeslice"><c>erl_drv_consume_timeslice()</c></seealso>
- function can be useful in order to determine when to trigger such
- timeout callback calls. It might, however, not always be possible to
- implement it this way, e.g. when calling third party libraries. In this
- case you typically want to dispatch the work to another thread.
- Information about thread primitives can be found below.</p>
+
+ <p><marker id="lengthy_work"/>
+ As mentioned in the <seealso marker="#WARNING">warning</seealso> text at
+ the beginning of this section, it is of vital importance that a driver
+ callback returns relatively fast. It is difficult to give an exact
+ maximum amount of time that a driver callback is allowed to work, but
+ usually a well-behaving driver callback is to return within 1 millisecond.
+ This can be achieved using different approaches.
+ If you have full control over the code to execute in the driver
+ callback, the best approach is to divide the work into multiple chunks of
+ work, and trigger multiple calls to the
+ <seealso marker="driver_entry#timeout">time-out callback</seealso> using
+ zero time-outs. Function <seealso marker="#erl_drv_consume_timeslice">
+ <c>erl_drv_consume_timeslice</c></seealso> can be useful to
+ determine when to trigger such time-out callback calls. However, sometimes
+ it cannot be implemented this way, for example when calling
+ third-party libraries. In this case, you typically want to dispatch the
+ work to another thread. Information about thread primitives is provided
+ below.</p>
</description>
<section>
- <title>FUNCTIONALITY</title>
+ <title>Functionality</title>
<p>All functions that a driver needs to do with Erlang are
- performed through driver API functions. There are functions
+ performed through driver API functions. Functions exist
for the following functionality:</p>
+
<taglist>
<tag>Timer functions</tag>
- <item>Timer functions are used to control the timer that a driver
- may use. The timer will have the emulator call the
- <seealso marker="driver_entry#timeout">timeout</seealso> entry
- function after a specified time. Only one timer is available
- for each driver instance.</item>
+ <item>
+ <p>Control the timer that a driver can use. The timer has the
+ emulator call the <seealso marker="driver_entry#timeout">
+ <c>timeout</c></seealso> entry function after a specified time.
+ Only one timer is available for each driver instance.</p>
+ </item>
<tag>Queue handling</tag>
<item>
<p>Every driver instance has an associated queue. This queue is a
- <c>SysIOVec</c> that works as a buffer. It's mostly used for
- the driver to buffer data that should be written to a device,
+ <c>SysIOVec</c>, which works as a buffer. It is mostly used for
+ the driver to buffer data that is to be written to a device,
it is a byte stream. If the port owner process closes the
- driver, and the queue is not empty, the driver will not be
+ driver, and the queue is not empty, the driver is not
closed. This enables the driver to flush its buffers before
closing.</p>
- <p>The queue can be manipulated from arbitrary threads if
- a port data lock is used. See documentation of the
- <seealso marker="#ErlDrvPDL">ErlDrvPDL</seealso> type for
- more information.</p>
+ <p>The queue can be manipulated from any threads if
+ a port data lock is used. For more information, see
+ <seealso marker="#ErlDrvPDL"><c>ErlDrvPDL</c></seealso>.</p>
</item>
<tag>Output functions</tag>
- <item>With the output functions, the driver sends data back to
- the emulator. They will be received as messages by the port owner
- process, see <c>open_port/2</c>. The vector function and the
- function taking a driver binary are faster, because they avoid
- copying the data buffer. There is also a fast way of sending
- terms from the driver, without going through the binary term
- format.</item>
+ <item>
+ <p>With these functions, the driver sends data back to the emulator.
+ The data is received as messages by the port owner process, see
+ <seealso marker="erlang:open_port/2">
+ <c>erlang:open_port/2</c></seealso>. The vector function and the
+ function taking a driver binary are faster, as they avoid
+ copying the data buffer. There is also a fast way of sending
+ terms from the driver, without going through the binary term
+ format.</p></item>
<tag>Failure</tag>
- <item>The driver can exit and signal errors up to Erlang. This is
- only for severe errors, when the driver can't possibly keep
- open.</item>
+ <item>
+ <p>The driver can exit and signal errors up to Erlang. This is
+ only for severe errors, when the driver cannot possibly keep
+ open.</p>
+ </item>
<tag>Asynchronous calls</tag>
- <item>The latest Erlang versions (R7B and later) has provision for
- asynchronous function calls, using a thread pool provided by
- Erlang. There is also a select call, that can be used for
- asynchronous drivers.</item>
+ <item>
+ <p>Erlang/OTP R7B and later versions have provision for
+ asynchronous function calls, using a thread pool provided by
+ Erlang. There is also a select call, which can be used for
+ asynchronous drivers.</p>
+ </item>
<tag><marker id="multi_threading"/>Multi-threading</tag>
<item>
- <p>A POSIX thread like API for multi-threading is provided. The
- Erlang driver thread API only provide a subset of the functionality
- provided by the POSIX thread API. The subset provided is
- more or less the basic functionality needed for multi-threaded
- programming:
- </p>
- <list>
- <item><seealso marker="#ErlDrvTid">Threads</seealso></item>
- <item><seealso marker="#ErlDrvMutex">Mutexes</seealso></item>
- <item><seealso marker="#ErlDrvCond">Condition variables</seealso></item>
- <item><seealso marker="#ErlDrvRWLock">Read/Write locks</seealso></item>
- <item><seealso marker="#ErlDrvTSDKey">Thread specific data</seealso></item>
- </list>
- <p>The Erlang driver thread API can be used in conjunction with
- the POSIX thread API on UN-ices and with the Windows native thread
- API on Windows. The Erlang driver thread API has the advantage of
- being portable, but there might exist situations where you want to
- use functionality from the POSIX thread API or the Windows
- native thread API.
- </p>
- <p>The Erlang driver thread API only returns error codes when it is
- reasonable to recover from an error condition. If it isn't reasonable
- to recover from an error condition, the whole runtime system is
- terminated. For example, if a create mutex operation fails, an error
- code is returned, but if a lock operation on a mutex fails, the
- whole runtime system is terminated.
- </p>
- <p>Note that there exists no "condition variable wait with timeout" in
- the Erlang driver thread API. This is due to issues with
- <c>pthread_cond_timedwait()</c>. When the system clock suddenly
- is changed, it isn't always guaranteed that you will wake up from
- the call as expected. An Erlang runtime system has to be able to
- cope with sudden changes of the system clock. Therefore, we have
- omitted it from the Erlang driver thread API. In the Erlang driver
- case, timeouts can and should be handled with the timer functionality
- of the Erlang driver API.
- </p>
- <p>In order for the Erlang driver thread API to function, thread
- support has to be enabled in the runtime system. An Erlang driver
- can check if thread support is enabled by use of
- <seealso marker="#driver_system_info">driver_system_info()</seealso>.
- Note that some functions in the Erlang driver API are thread-safe
- only when the runtime system has SMP support, also this
- information can be retrieved via
- <seealso marker="#driver_system_info">driver_system_info()</seealso>.
- Also note that a lot of functions in the Erlang driver API are
- <em>not</em> thread-safe regardless of whether SMP support is
- enabled or not. If a function isn't documented as thread-safe it
- is <em>not</em> thread-safe.
- </p>
- <p><em>NOTE</em>: When executing in an emulator thread, it is
- <em>very important</em> that you unlock <em>all</em> locks you
- have locked before letting the thread out of your control;
- otherwise, you are <em>very likely</em> to deadlock the whole
- emulator. If you need to use thread specific data in an emulator
- thread, only have the thread specific data set while the thread is
- under your control, and clear the thread specific data before
- you let the thread out of your control.
- </p>
- <p>In the future there will probably be debug functionality
- integrated with the Erlang driver thread API. All functions
- that create entities take a <c>name</c> argument. Currently
- the <c>name</c> argument is unused, but it will be used when
- the debug functionality has been implemented. If you name all
- entities created well, the debug functionality will be able
- to give you better error reports.
- </p>
+ <p>A POSIX thread like API for multi-threading is provided. The
+ Erlang driver thread API only provides a subset of the functionality
+ provided by the POSIX thread API. The subset provided is
+ more or less the basic functionality needed for multi-threaded
+ programming:</p>
+ <list>
+ <item><seealso marker="#ErlDrvTid">Threads</seealso></item>
+ <item><seealso marker="#ErlDrvMutex">Mutexes</seealso></item>
+ <item><seealso marker="#ErlDrvCond">
+ Condition variables</seealso></item>
+ <item><seealso marker="#ErlDrvRWLock">
+ Read/write locks</seealso></item>
+ <item><seealso marker="#ErlDrvTSDKey">
+ Thread-specific data</seealso></item>
+ </list>
+ <p>The Erlang driver thread API can be used in conjunction with
+ the POSIX thread API on UN-ices and with the Windows native thread
+ API on Windows. The Erlang driver thread API has the advantage of
+ being portable, but there can exist situations where you want to
+ use functionality from the POSIX thread API or the Windows
+ native thread API.</p>
+ <p>The Erlang driver thread API only returns error codes when it is
+ reasonable to recover from an error condition. If it is not reasonable
+ to recover from an error condition, the whole runtime system is
+ terminated. For example, if a create mutex operation fails, an error
+ code is returned, but if a lock operation on a mutex fails, the
+ whole runtime system is terminated.</p>
+ <p>Notice that there is no "condition variable wait with time-out" in
+ the Erlang driver thread API. This because of issues with
+ <c>pthread_cond_timedwait</c>. When the system clock suddenly
+ is changed, it is not always guaranteed that you will wake up from
+ the call as expected. An Erlang runtime system must be able to
+ cope with sudden changes of the system clock. Therefore, we have
+ omitted it from the Erlang driver thread API. In the Erlang driver
+ case, time-outs can and are to be handled with the timer functionality
+ of the Erlang driver API.</p>
+ <p>In order for the Erlang driver thread API to function, thread
+ support must be enabled in the runtime system. An Erlang driver
+ can check if thread support is enabled by use of
+ <seealso marker="#driver_system_info">
+ <c>driver_system_info</c></seealso>.
+ Notice that some functions in the Erlang driver API are thread-safe
+ only when the runtime system has SMP support, also this
+ information can be retrieved through
+ <seealso marker="#driver_system_info">
+ <c>driver_system_info</c></seealso>.
+ Also notice that many functions in the Erlang driver API are
+ <em>not</em> thread-safe, regardless of whether SMP support is
+ enabled or not. If a function is not documented as thread-safe, it
+ is <em>not</em> thread-safe.</p>
+ <note>
+ <p>When executing in an emulator thread, it is
+ <em>very important</em> that you unlock <em>all</em> locks you
+ have locked before letting the thread out of your control;
+ otherwise you are <em>very likely</em> to deadlock the whole
+ emulator.</p>
+ <p>If you need to use thread-specific data in an emulator
+ thread, only have the thread-specific data set while the thread is
+ under your control, and clear the thread-specific data before
+ you let the thread out of your control.</p>
+ </note>
+ <p>In the future, debug functionality will probably be
+ integrated with the Erlang driver thread API. All functions
+ that create entities take a <c>name</c> argument. Currently
+ the <c>name</c> argument is unused, but it will be used when
+ the debug functionality is implemented. If you name all
+ entities created well, the debug functionality will be able
+ to give you better error reports.</p>
+ </item>
+ <tag>Adding/removing drivers</tag>
+ <item>
+ <p>A driver can add and later remove drivers.</p>
</item>
- <tag>Adding / removing drivers</tag>
- <item><p>A driver can add and later remove drivers.</p></item>
<tag>Monitoring processes</tag>
- <item><p>A driver can monitor a process that does not own a port.</p></item>
+ <item>
+ <p>A driver can monitor a process that does not own a port.</p>
+ </item>
<tag><marker id="version_management"/>Version management</tag>
<item>
<p>Version management is enabled for drivers that have set the
- <seealso marker="driver_entry#extended_marker">extended_marker</seealso>
- field of their
- <seealso marker="driver_entry">driver_entry</seealso>
- to <c>ERL_DRV_EXTENDED_MARKER</c>. <c>erl_driver.h</c> defines
- <c>ERL_DRV_EXTENDED_MARKER</c>,
- <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c>, and
- <c>ERL_DRV_EXTENDED_MINOR_VERSION</c>.
- <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> will be incremented when
- driver incompatible changes are made to the Erlang runtime
- system. Normally it will suffice to recompile drivers when the
- <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> has changed, but it
- could, under rare circumstances, mean that drivers have to
- be slightly modified. If so, this will of course be documented.
- <c>ERL_DRV_EXTENDED_MINOR_VERSION</c> will be incremented when
- new features are added. The runtime system uses the minor version
- of the driver to determine what features to use.
- The runtime system will normally refuse to load a driver if the major
+ <seealso marker="driver_entry#extended_marker">
+ <c>extended_marker</c></seealso> field of their
+ <seealso marker="driver_entry"><c>driver_entry</c></seealso>
+ to <c>ERL_DRV_EXTENDED_MARKER</c>. <c>erl_driver.h</c> defines:</p>
+ <list type="bulleted">
+ <item>
+ <p><c>ERL_DRV_EXTENDED_MARKER</c></p>
+ </item>
+ <item>
+ <p><c>ERL_DRV_EXTENDED_MAJOR_VERSION</c>, which is incremented when
+ driver incompatible changes are made to the Erlang runtime
+ system. Normally it suffices to recompile drivers when
+ <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> has changed, but it
+ can, under rare circumstances, mean that drivers must
+ be slightly modified. If so, this will of course be
+ documented.</p>
+ </item>
+ <item>
+ <p><c>ERL_DRV_EXTENDED_MINOR_VERSION</c>, which is incremented when
+ new features are added. The runtime system uses the minor version
+ of the driver to determine what features to use.</p>
+ </item>
+ </list>
+ <p>The runtime system normally refuses to load a driver if the major
versions differ, or if the major versions are equal and the
minor version used by the driver is greater than the one used
by the runtime system. Old drivers with lower major versions
- will however be allowed after a bump of the major version during
- a transition period of two major releases. Such old drivers might
- however fail if deprecated features are used.</p>
- <p>The emulator will refuse to load a driver that does not use
- the extended driver interface,
- to allow for 64-bit capable drivers,
- since incompatible type changes for the callbacks
- <seealso marker="driver_entry#output">output</seealso>,
- <seealso marker="driver_entry#control">control</seealso> and
- <seealso marker="driver_entry#call">call</seealso>
- were introduced in release R15B. A driver written
- with the old types would compile with warnings and when
- called return garbage sizes to the emulator causing it
- to read random memory and create huge incorrect result blobs.</p>
- <p>Therefore it is not enough to just recompile drivers written with
- version management for pre-R15B types; the types have to be changed
- in the driver suggesting other rewrites especially regarding
- size variables. Investigate all warnings when recompiling!</p>
- <p>Also, the API driver functions <c>driver_output*</c>,
- <c>driver_vec_to_buf</c>, <c>driver_alloc/realloc*</c>
- and the <c>driver_*</c> queue functions were changed to have
- larger length arguments and return values. This is a
- lesser problem since code that passes smaller types
- will get them auto converted in the calls and as long as
- the driver does not handle sizes that overflow an <c>int</c>
- all will work as before.</p>
+ are however allowed after a bump of the major version during
+ a transition period of two major releases. Such old drivers can,
+ however, fail if deprecated features are used.</p>
+ <p>The emulator refuses to load a driver that does not use
+ the extended driver interface, to allow for 64-bit capable drivers,
+ as incompatible type changes for the callbacks
+ <seealso marker="driver_entry#output"><c>output</c></seealso>,
+ <seealso marker="driver_entry#control"><c>control</c></seealso>, and
+ <seealso marker="driver_entry#call"><c>call</c></seealso>
+ were introduced in Erlang/OTP R15B. A driver written
+ with the old types would compile with warnings and when
+ called return garbage sizes to the emulator, causing it
+ to read random memory and create huge incorrect result blobs.</p>
+ <p>Therefore it is not enough to only recompile drivers written with
+ version management for pre R15B types; the types must be changed
+ in the driver suggesting other rewrites, especially regarding size
+ variables. <em>Investigate all warnings when recompiling.</em></p>
+ <p>Also, the API driver functions <c>driver_output*</c> and
+ <c>driver_vec_to_buf</c>, <c>driver_alloc/realloc*</c>, and the
+ <c>driver_*</c> queue functions were changed to have
+ larger length arguments and return values. This is a
+ lesser problem, as code that passes smaller types
+ gets them auto-converted in the calls, and as long as
+ the driver does not handle sizes that overflow an <c>int</c>,
+ all will work as before.</p>
</item>
- <tag><marker id="time_measurement"/>Time Measurement</tag>
- <item><p>Support for time measurement in drivers:</p>
- <list>
- <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item>
- <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item>
- <item><seealso marker="#erl_drv_monotonic_time"><c>erl_drv_monotonic_time()</c></seealso></item>
- <item><seealso marker="#erl_drv_time_offset"><c>erl_drv_time_offset()</c></seealso></item>
- <item><seealso marker="#erl_drv_convert_time_unit"><c>erl_drv_convert_time_unit()</c></seealso></item>
- </list>
+ <tag><marker id="time_measurement"/>Time measurement</tag>
+ <item>
+ <p>Support for time measurement in drivers:</p>
+ <list type="bulleted">
+ <item><seealso marker="#ErlDrvTime">
+ <c>ErlDrvTime</c></seealso></item>
+ <item><seealso marker="#ErlDrvTimeUnit">
+ <c>ErlDrvTimeUnit</c></seealso></item>
+ <item><seealso marker="#erl_drv_monotonic_time">
+ <c>erl_drv_monotonic_time</c></seealso></item>
+ <item><seealso marker="#erl_drv_time_offset">
+ <c>erl_drv_time_offset</c></seealso></item>
+ <item><seealso marker="#erl_drv_convert_time_unit">
+ <c>erl_drv_convert_time_unit</c></seealso></item>
+ </list>
</item>
</taglist>
</section>
<section>
<marker id="rewrites_for_64_bits"/>
- <title>
- REWRITES FOR 64-BIT DRIVER INTERFACE
- </title>
- <p>
- For erts-5.9 two new integer types
- <seealso marker="#ErlDrvSizeT">ErlDrvSizeT</seealso> and
- <seealso marker="#ErlDrvSSizeT">ErlDrvSSizeT</seealso>
- were introduced that can hold 64-bit sizes if necessary.
- </p>
- <p>
- To not update a driver and just recompile it probably works
+ <title>Rewrites for 64-Bit Driver Interface</title>
+ <p>ERTS 5.9 introduced two new integer types,
+ <seealso marker="#ErlDrvSizeT"><c>ErlDrvSizeT</c></seealso> and
+ <seealso marker="#ErlDrvSSizeT"><c>ErlDrvSSizeT</c></seealso>,
+ which can hold 64-bit sizes if necessary.</p>
+
+ <p>To not update a driver and only recompile, it probably works
when building for a 32-bit machine creating a false sense of security.
Hopefully that will generate many important warnings.
- But when recompiling the same driver later on for a 64-bit machine
+ But when recompiling the same driver later on for a 64-bit machine,
there <em>will</em> be warnings and almost certainly crashes.
- So it is a BAD idea to postpone updating the driver and
- not fixing the warnings!
- </p>
- <p>
- When recompiling with <c>gcc</c> use the <c>-Wstrict-prototypes</c>
- flag to get better warnings. Try to find a similar flag if you
- are using some other compiler.
- </p>
- <p>
- Here follows a checklist for rewriting a pre erts-5.9 driver,
- most important first.
- </p>
+ So it is a <em>bad</em> idea to postpone updating the driver and
+ not fixing the warnings.</p>
+
+ <p>When recompiling with <c>gcc</c>, use flag <c>-Wstrict-prototypes</c>
+ to get better warnings. Try to find a similar flag if you use
+ another compiler.</p>
+
+ <p>The following is a checklist for rewriting a pre ERTS 5.9 driver,
+ most important first:</p>
+
<taglist>
<tag>Return types for driver callbacks</tag>
<item>
- <p>
- Rewrite driver callback
+ <p>Rrewrite driver callback
<seealso marker="driver_entry#control"><c>control</c></seealso>
- to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.
- </p>
- <p>
- Rewrite driver callback
+ to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.</p>
+ <p>Rewrite driver callback
<seealso marker="driver_entry#call"><c>call</c></seealso>
- to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.
- </p>
+ to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.</p>
<note>
- <p>
- These changes are essential to not crash the emulator
+ <p>These changes are essential not to crash the emulator
or worse cause malfunction.
- Without them a driver may return garbage in the high 32 bits
- to the emulator causing it to build a huge result from random
- bytes either crashing on memory allocation or succeeding with
- a random result from the driver call.
- </p>
+ Without them a driver can return garbage in the high 32 bits
+ to the emulator, causing it to build a huge result from random
+ bytes, either crashing on memory allocation or succeeding with
+ a random result from the driver call.</p>
</note>
</item>
<tag>Arguments to driver callbacks</tag>
<item>
- <p>
- Driver callback
+ <p>Driver callback
<seealso marker="driver_entry#output"><c>output</c></seealso>
now gets <c>ErlDrvSizeT</c> as 3rd argument instead
- of previously <c>int</c>.
- </p>
- <p>
- Driver callback
+ of previously <c>int</c>.</p>
+ <p>Driver callback
<seealso marker="driver_entry#control"><c>control</c></seealso>
now gets <c>ErlDrvSizeT</c> as 4th and 6th arguments instead
- of previously <c>int</c>.
- </p>
- <p>
- Driver callback
+ of previously <c>int</c>.</p>
+ <p>Driver callback
<seealso marker="driver_entry#call"><c>call</c></seealso>
now gets <c>ErlDrvSizeT</c> as 4th and 6th arguments instead
- of previously <c>int</c>.
- </p>
- <p>
- Sane compiler's calling conventions probably make these changes
+ of previously <c>int</c>.</p>
+ <p>Sane compiler's calling conventions probably make these changes
necessary only for a driver to handle data chunks that require
- 64-bit size fields (mostly larger than 2 GB since that is what
+ 64-bit size fields (mostly larger than 2 GB, as that is what
an <c>int</c> of 32 bits can hold). But it is possible to think
of non-sane calling conventions that would make the driver
- callbacks mix up the arguments causing malfunction.
- </p>
+ callbacks mix up the arguments causing malfunction.</p>
<note>
- <p>
- The argument type change is from signed to unsigned which
- may cause problems for e.g. loop termination conditions or
- error conditions if you just change the types all over the place.
- </p>
+ <p>The argument type change is from signed to unsigned. This
+ can cause problems for, for example, loop termination conditions or
+ error conditions if you only change the types all over the place.
+ </p>
</note>
</item>
<tag>Larger <c>size</c> field in <c>ErlIOVec</c></tag>
<item>
- <p>
- The <c>size</c> field in
+ <p>The <c>size</c> field in
<seealso marker="#ErlIOVec"><c>ErlIOVec</c></seealso>
has been changed to <c>ErlDrvSizeT</c> from <c>int</c>.
- Check all code that use that field.
- </p>
- <p>
- Automatic type casting probably makes these changes necessary only
- for a driver that encounters sizes larger than 32 bits.
- </p>
+ Check all code that use that field.</p>
+ <p>Automatic type-casting probably makes these changes necessary only
+ for a driver that encounters sizes &gt; 32 bits.</p>
<note>
- <p>
- The <c>size</c> field changed from signed to unsigned which
- may cause problems for e.g. loop termination conditions or
- error conditions if you just change the types all over the place.
- </p>
+ <p>The <c>size</c> field changed from signed to unsigned. This
+ can cause problems for, for example, loop termination conditions or
+ error conditions if you only change the types all over the place.
+ </p>
</note>
</item>
<tag>Arguments and return values in the driver API</tag>
<item>
- <p>
- Many driver API functions have changed argument type
+ <p>Many driver API functions have changed argument type
and/or return value to <c>ErlDrvSizeT</c> from mostly <c>int</c>.
- Automatic type casting probably makes these changes necessary only
- for a driver that encounters sizes larger than 32 bits.
- </p>
+ Automatic type-casting probably makes these changes necessary only
+ for a driver that encounters sizes &gt; 32 bits.</p>
<taglist>
- <tag><seealso marker="#driver_output">driver_output</seealso></tag>
+ <tag><seealso marker="#driver_output">
+ <c>driver_output</c></seealso></tag>
<item>3rd argument</item>
- <tag><seealso marker="#driver_output2">driver_output2</seealso></tag>
+ <tag><seealso marker="#driver_output2">
+ <c>driver_output2</c></seealso></tag>
<item>3rd and 5th arguments</item>
- <tag>
- <seealso marker="#driver_output_binary">driver_output_binary</seealso>
- </tag>
- <item>3rd 5th and 6th arguments</item>
- <tag><seealso marker="#driver_outputv">driver_outputv</seealso></tag>
+ <tag><seealso marker="#driver_output_binary">
+ <c>driver_output_binary</c></seealso></tag>
+ <item>3rd, 5th, and 6th arguments</item>
+ <tag><seealso marker="#driver_outputv">
+ <c>driver_outputv</c></seealso></tag>
<item>3rd and 5th arguments</item>
- <tag>
- <seealso marker="#driver_vec_to_buf">driver_vec_to_buf</seealso>
- </tag>
+ <tag><seealso marker="#driver_vec_to_buf">
+ <c>driver_vec_to_buf</c></seealso></tag>
<item>3rd argument and return value</item>
- <tag><seealso marker="#driver_alloc">driver_alloc</seealso></tag>
+ <tag><seealso marker="#driver_alloc">
+ <c>driver_alloc</c></seealso></tag>
<item>1st argument</item>
- <tag><seealso marker="#driver_realloc">driver_realloc</seealso></tag>
+ <tag><seealso marker="#driver_realloc">
+ <c>driver_realloc</c></seealso></tag>
<item>2nd argument</item>
- <tag>
- <seealso marker="#driver_alloc_binary">driver_alloc_binary</seealso>
- </tag>
+ <tag><seealso marker="#driver_alloc_binary">
+ <c>driver_alloc_binary</c></seealso></tag>
<item>1st argument</item>
- <tag>
- <seealso marker="#driver_realloc_binary">driver_realloc_binary</seealso>
- </tag>
+ <tag><seealso marker="#driver_realloc_binary">
+ <c>driver_realloc_binary</c></seealso></tag>
<item>2nd argument</item>
- <tag><seealso marker="#driver_enq">driver_enq</seealso></tag>
+ <tag><seealso marker="#driver_enq">
+ <c>driver_enq</c></seealso></tag>
<item>3rd argument</item>
- <tag><seealso marker="#driver_pushq">driver_pushq</seealso></tag>
+ <tag><seealso marker="#driver_pushq">
+ <c>driver_pushq</c></seealso></tag>
<item>3rd argument</item>
- <tag><seealso marker="#driver_deq">driver_deq</seealso></tag>
+ <tag><seealso marker="#driver_deq">
+ <c>driver_deq</c></seealso></tag>
<item>2nd argument and return value</item>
- <tag><seealso marker="#driver_sizeq">driver_sizeq</seealso></tag>
- <item>return value</item>
- <tag><seealso marker="#driver_enq_bin">driver_enq_bin</seealso></tag>
- <item>3rd and 4th argument</item>
- <tag><seealso marker="#driver_pushq_bin">driver_pushq_bin</seealso></tag>
- <item>3rd and 4th argument</item>
- <tag><seealso marker="#driver_enqv">driver_enqv</seealso></tag>
+ <tag><seealso marker="#driver_sizeq">
+ <c>driver_sizeq</c></seealso></tag>
+ <item>Return value</item>
+ <tag><seealso marker="#driver_enq_bin">
+ <c>driver_enq_bin</c></seealso></tag>
+ <item>3rd and 4th arguments</item>
+ <tag><seealso marker="#driver_pushq_bin">
+ <c>driver_pushq_bin</c></seealso></tag>
+ <item>3rd and 4th arguments</item>
+ <tag><seealso marker="#driver_enqv">
+ <c>driver_enqv</c></seealso></tag>
<item>3rd argument</item>
- <tag><seealso marker="#driver_pushqv">driver_pushqv</seealso></tag>
+ <tag><seealso marker="#driver_pushqv">
+ <c>driver_pushqv</c></seealso></tag>
<item>3rd argument</item>
- <tag><seealso marker="#driver_peekqv">driver_peekqv</seealso></tag>
- <item>return value</item>
+ <tag><seealso marker="#driver_peekqv">
+ <c>driver_peekqv</c></seealso></tag>
+ <item>Return value</item>
</taglist>
<note>
- <p>
- This is a change from signed to unsigned which
- may cause problems for e.g. loop termination conditions and
- error conditions if you just change the types all over the place.
- </p>
+ <p>This is a change from signed to unsigned. This can cause
+ problems for, for example, loop termination conditions and error
+ conditions if you only change the types all over the place.</p>
</note>
</item>
</taglist>
</section>
<section>
- <title>DATA TYPES</title>
-
+ <title>Data Types</title>
<taglist>
- <tag><marker id="ErlDrvSizeT"/>ErlDrvSizeT</tag>
- <item><p>An unsigned integer type to be used as <c>size_t</c></p></item>
- <tag><marker id="ErlDrvSSizeT"/>ErlDrvSSizeT</tag>
- <item><p>A signed integer type the size of <c>ErlDrvSizeT</c></p></item>
- <tag><marker id="ErlDrvSysInfo"/>ErlDrvSysInfo</tag>
+ <tag><marker id="ErlDrvSizeT"/><c>ErlDrvSizeT</c></tag>
<item>
- <p/>
- <code type="none">
+ <p>An unsigned integer type to be used as <c>size_t</c>.</p>
+ </item>
+ <tag><marker id="ErlDrvSSizeT"/><c>ErlDrvSSizeT</c></tag>
+ <item>
+ <p>A signed integer type, the size of <c>ErlDrvSizeT</c>.</p>
+ </item>
+ <tag><marker id="ErlDrvSysInfo"/><c>ErlDrvSysInfo</c></tag>
+ <item>
+ <code type="none">
typedef struct ErlDrvSysInfo {
int driver_major_version;
int driver_minor_version;
@@ -558,2610 +583,2633 @@ typedef struct ErlDrvSysInfo {
int nif_major_version;
int nif_minor_version;
int dirty_scheduler_support;
-} ErlDrvSysInfo;
- </code>
-
- <p>
- The <c>ErlDrvSysInfo</c> structure is used for storage of
- information about the Erlang runtime system.
- <seealso marker="#driver_system_info">driver_system_info()</seealso>
- will write the system information when passed a reference to
- a <c>ErlDrvSysInfo</c> structure. A description of the
- fields in the structure follows:
- </p>
- <taglist>
- <tag><c>driver_major_version</c></tag>
- <item>The value of
- <seealso marker="#version_management">ERL_DRV_EXTENDED_MAJOR_VERSION</seealso>
- when the runtime system was compiled. This value is the same
- as the value of
- <seealso marker="#version_management">ERL_DRV_EXTENDED_MAJOR_VERSION</seealso>
- used when compiling the driver; otherwise, the runtime system
- would have refused to load the driver.
- </item>
- <tag><c>driver_minor_version</c></tag>
- <item>The value of
- <seealso marker="#version_management">ERL_DRV_EXTENDED_MINOR_VERSION</seealso>
- when the runtime system was compiled. This value might differ
- from the value of
- <seealso marker="#version_management">ERL_DRV_EXTENDED_MINOR_VERSION</seealso>
- used when compiling the driver.
- </item>
- <tag><c>erts_version</c></tag>
- <item>A string containing the version number of the runtime system
- (the same as returned by
- <seealso marker="erlang#system_info_version">erlang:system_info(version)</seealso>).
- </item>
- <tag><c>otp_release</c></tag>
- <item>A string containing the OTP release number
- (the same as returned by
- <seealso marker="erlang#system_info_otp_release">erlang:system_info(otp_release)</seealso>).
- </item>
- <tag><c>thread_support</c></tag>
- <item>A value <c>!= 0</c> if the runtime system has thread support;
- otherwise, <c>0</c>.
- </item>
- <tag><c>smp_support</c></tag>
- <item>A value <c>!= 0</c> if the runtime system has SMP support;
- otherwise, <c>0</c>.
- </item>
- <tag><c>async_threads</c></tag>
- <item>The number of async threads in the async thread pool used
- by <seealso marker="#driver_async">driver_async()</seealso>
- (the same as returned by
- <seealso marker="erlang#system_info_thread_pool_size">erlang:system_info(thread_pool_size)</seealso>).
- </item>
- <tag><c>scheduler_threads</c></tag>
- <item>The number of scheduler threads used by the runtime system
- (the same as returned by
- <seealso marker="erlang#system_info_schedulers">erlang:system_info(schedulers)</seealso>).
- </item>
- <tag><c>nif_major_version</c></tag>
- <item>The value of <c>ERL_NIF_MAJOR_VERSION</c> when the runtime system was compiled.
- </item>
- <tag><c>nif_minor_version</c></tag>
- <item>The value of <c>ERL_NIF_MINOR_VERSION</c> when the runtime system was compiled.
- </item>
- <tag><c>dirty_scheduler_support</c></tag>
- <item>A value <c>!= 0</c> if the runtime system has support for dirty scheduler threads;
- otherwise <c>0</c>.
- </item>
+} ErlDrvSysInfo;</code>
+ <p>The <c>ErlDrvSysInfo</c> structure is used for storage of
+ information about the Erlang runtime system.
+ <seealso marker="#driver_system_info">
+ <c>driver_system_info</c></seealso>
+ writes the system information when passed a reference to
+ a <c>ErlDrvSysInfo</c> structure. The fields in the structure
+ are as follows:</p>
+ <taglist>
+ <tag><c>driver_major_version</c></tag>
+ <item>
+ <p>The value of <seealso marker="#version_management">
+ <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c></seealso>
+ when the runtime system was compiled. This value is the same
+ as the value of <seealso marker="#version_management">
+ <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c></seealso>
+ used when compiling the driver; otherwise the runtime system
+ would have refused to load the driver.</p>
+ </item>
+ <tag><c>driver_minor_version</c></tag>
+ <item>
+ <p>The value of <seealso marker="#version_management">
+ <c>ERL_DRV_EXTENDED_MINOR_VERSION</c></seealso>
+ when the runtime system was compiled. This value can differ
+ from the value of <seealso marker="#version_management">
+ <c>ERL_DRV_EXTENDED_MINOR_VERSION</c></seealso>
+ used when compiling the driver.</p>
+ </item>
+ <tag><c>erts_version</c></tag>
+ <item>
+ <p>A string containing the version number of the runtime system
+ (the same as returned by
+ <seealso marker="erlang#system_info_version">
+ <c>erlang:system_info(version)</c></seealso>).</p>
+ </item>
+ <tag><c>otp_release</c></tag>
+ <item>
+ <p>A string containing the OTP release number
+ (the same as returned by
+ <seealso marker="erlang#system_info_otp_release">
+ <c>erlang:system_info(otp_release)</c></seealso>).</p>
+ </item>
+ <tag><c>thread_support</c></tag>
+ <item>
+ <p>A value <c>!= 0</c> if the runtime system has thread support;
+ otherwise <c>0</c>.</p>
+ </item>
+ <tag><c>smp_support</c></tag>
+ <item>
+ <p>A value <c>!= 0</c> if the runtime system has SMP support;
+ otherwise <c>0</c>.</p>
+ </item>
+ <tag><c>async_threads</c></tag>
+ <item>
+ <p>The number of async threads in the async thread pool used by
+ <seealso marker="#driver_async"><c>driver_async</c></seealso>
+ (the same as returned by
+ <seealso marker="erlang#system_info_thread_pool_size">
+ <c>erlang:system_info(thread_pool_size)</c></seealso>).</p>
+ </item>
+ <tag><c>scheduler_threads</c></tag>
+ <item>
+ <p>The number of scheduler threads used by the runtime system
+ (the same as returned by
+ <seealso marker="erlang#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>).</p>
+ </item>
+ <tag><c>nif_major_version</c></tag>
+ <item>
+ <p>The value of <c>ERL_NIF_MAJOR_VERSION</c> when the runtime
+ system was compiled.</p>
+ </item>
+ <tag><c>nif_minor_version</c></tag>
+ <item>
+ <p>The value of <c>ERL_NIF_MINOR_VERSION</c> when the runtime
+ system was compiled.</p>
+ </item>
+ <tag><c>dirty_scheduler_support</c></tag>
+ <item>
+ <p>A value <c>!= 0</c> if the runtime system has support for dirty
+ scheduler threads; otherwise <c>0</c>.</p>
+ </item>
</taglist>
</item>
- <tag><marker id="ErlDrvBinary"/>ErlDrvBinary</tag>
+ <tag><marker id="ErlDrvBinary"/><c>ErlDrvBinary</c></tag>
<item>
- <p/>
- <code type="none">
+ <code type="none">
typedef struct ErlDrvBinary {
ErlDrvSint orig_size;
char orig_bytes[];
-} ErlDrvBinary;
-</code>
+} ErlDrvBinary;</code>
<p>The <c>ErlDrvBinary</c> structure is a binary, as sent
between the emulator and the driver. All binaries are
reference counted; when <c>driver_binary_free</c> is called,
the reference count is decremented, when it reaches zero,
- the binary is deallocated. The <c>orig_size</c> is the size
- of the binary, and <c>orig_bytes</c> is the buffer. The
- <c>ErlDrvBinary</c> does not have a fixed size, its size is
+ the binary is deallocated. <c>orig_size</c> is the binary size
+ and <c>orig_bytes</c> is the buffer.
+ <c>ErlDrvBinary</c> has not a fixed size, its size is
<c>orig_size + 2 * sizeof(int)</c>.</p>
<note>
<p>The <c>refc</c> field has been removed. The reference count of
an <c>ErlDrvBinary</c> is now stored elsewhere. The
- reference count of an <c>ErlDrvBinary</c> can be accessed via
- <seealso marker="#driver_binary_get_refc">driver_binary_get_refc()</seealso>,
- <seealso marker="#driver_binary_inc_refc">driver_binary_inc_refc()</seealso>,
- and
- <seealso marker="#driver_binary_dec_refc">driver_binary_dec_refc()</seealso>.</p>
+ reference count of an <c>ErlDrvBinary</c> can be accessed through
+ <seealso marker="#driver_binary_get_refc">
+ <c>driver_binary_get_refc</c></seealso>,
+ <seealso marker="#driver_binary_inc_refc">
+ <c>driver_binary_inc_refc</c></seealso>, and
+ <seealso marker="#driver_binary_dec_refc">
+ <c>driver_binary_dec_refc</c></seealso>.</p>
</note>
<p>Some driver calls, such as <c>driver_enq_binary</c>,
increment the driver reference count, and others, such as
<c>driver_deq</c> decrement it.</p>
- <p>Using a driver binary instead of a normal buffer, is often
- faster, since the emulator doesn't need to copy the data,
+ <p>Using a driver binary instead of a normal buffer is often
+ faster, as the emulator needs not to copy the data,
only the pointer is used.</p>
<p>A driver binary allocated in the driver, with
- <c>driver_alloc_binary</c>, should be freed in the driver (unless otherwise stated),
- with <c>driver_free_binary</c>. (Note that this doesn't
+ <c>driver_alloc_binary</c>, is to be freed in the driver
+ (unless otherwise stated)
+ with <c>driver_free_binary</c>. (Notice that this does not
necessarily deallocate it, if the driver is still referred
in the emulator, the ref-count will not go to zero.)</p>
<p>Driver binaries are used in the <c>driver_output2</c> and
<c>driver_outputv</c> calls, and in the queue. Also the
- driver call-back <seealso marker="driver_entry#outputv">outputv</seealso> uses driver
- binaries.</p>
- <p>If the driver for some reason or another, wants to keep a
- driver binary around, in a static variable for instance, the
- reference count should be incremented,
- and the binary can later be freed in the <seealso marker="driver_entry#stop">stop</seealso> call-back, with
- <c>driver_free_binary</c>.</p>
- <p>Note that since a driver binary is shared by the driver and
- the emulator, a binary received from the emulator or sent to
- the emulator, must not be changed by the driver.</p>
- <p>Since erts version 5.5 (OTP release R11B), orig_bytes is
+ driver callback <seealso marker="driver_entry#outputv">
+ <c>outputv</c></seealso> uses driver binaries.</p>
+ <p>If the driver for some reason wants to keep a
+ driver binary around, for example in a static variable, the
+ reference count is to be incremented, and the binary can later
+ be freed in the <seealso marker="driver_entry#stop">
+ <c>stop</c></seealso> callback, with <c>driver_free_binary</c>.</p>
+ <p>Notice that as a driver binary is shared by the driver and
+ the emulator. A binary received from the emulator or sent to
+ the emulator must not be changed by the driver.</p>
+ <p>Since ERTS 5.5 (Erlang/OTP R11B), <c>orig_bytes</c> is
guaranteed to be properly aligned for storage of an array of
doubles (usually 8-byte aligned).</p>
</item>
- <tag>ErlDrvData</tag>
+ <tag><c>ErlDrvData</c></tag>
<item>
- <p>The <c>ErlDrvData</c> is a handle to driver-specific data,
- passed to the driver call-backs. It is a pointer, and is
+ <p>A handle to driver-specific data,
+ passed to the driver callbacks. It is a pointer, and is
most often type cast to a specific pointer in the driver.</p>
</item>
- <tag>SysIOVec</tag>
+ <tag><c>SysIOVec</c></tag>
<item>
- <p>This is a system I/O vector, as used by <c>writev</c> on
- unix and <c>WSASend</c> on Win32. It is used in
+ <p>A system I/O vector, as used by <c>writev</c> on
+ Unix and <c>WSASend</c> on Win32. It is used in
<c>ErlIOVec</c>.</p>
</item>
- <tag><marker id="ErlIOVec"/>ErlIOVec</tag>
+ <tag><marker id="ErlIOVec"/><c>ErlIOVec</c></tag>
<item>
- <p/>
- <code type="none">
+ <code type="none">
typedef struct ErlIOVec {
int vsize;
ErlDrvSizeT size;
SysIOVec* iov;
ErlDrvBinary** binv;
-} ErlIOVec;
-</code>
- <p>The I/O vector used by the emulator and drivers, is a list
+} ErlIOVec;</code>
+ <p>The I/O vector used by the emulator and drivers is a list
of binaries, with a <c>SysIOVec</c> pointing to the buffers
of the binaries. It is used in <c>driver_outputv</c> and the
- <seealso marker="driver_entry#outputv">outputv</seealso>
- driver call-back. Also, the driver queue is an
+ <seealso marker="driver_entry#outputv"><c>outputv</c></seealso>
+ driver callback. Also, the driver queue is an
<c>ErlIOVec</c>.</p>
</item>
-
- <tag>ErlDrvMonitor</tag>
+ <tag><c>ErlDrvMonitor</c></tag>
<item>
<p>When a driver creates a monitor for a process, a
<c>ErlDrvMonitor</c> is filled in. This is an opaque
- data-type which can be assigned to but not compared without
- using the supplied compare function (i.e. it behaves like a struct).</p>
- <p>The driver writer should provide the memory for storing the
- monitor when calling <seealso marker="#driver_monitor_process">driver_monitor_process</seealso>. The
+ data type that can be assigned to, but not compared without
+ using the supplied compare function (that is, it behaves like
+ a struct).</p>
+ <p>The driver writer is to provide the memory for storing the
+ monitor when calling <seealso marker="#driver_monitor_process">
+ <c>driver_monitor_process</c></seealso>. The
address of the data is not stored outside of the driver, so
- the <c>ErlDrvMonitor</c> can be used as any other datum, it
- can be copied, moved in memory, forgotten etc.</p>
+ <c>ErlDrvMonitor</c> can be used as any other data, it
+ can be copied, moved in memory, forgotten, and so on.</p>
</item>
- <tag><marker id="ErlDrvNowData"/>ErlDrvNowData</tag>
+ <tag><marker id="ErlDrvNowData"/><c>ErlDrvNowData</c></tag>
<item>
- <p>The <c>ErlDrvNowData</c> structure holds a timestamp
+ <p>The <c>ErlDrvNowData</c> structure holds a time stamp
consisting of three values measured from some arbitrary
point in the past. The three structure members are:</p>
<taglist>
- <tag>megasecs</tag>
+ <tag><c>megasecs</c></tag>
<item>The number of whole megaseconds elapsed since the arbitrary
- point in time</item>
- <tag>secs</tag>
+ point in time</item>
+ <tag><c>secs</c></tag>
<item>The number of whole seconds elapsed since the arbitrary
- point in time</item>
- <tag>microsecs</tag>
+ point in time</item>
+ <tag><c>microsecs</c></tag>
<item>The number of whole microseconds elapsed since the arbitrary
- point in time</item>
+ point in time</item>
</taglist>
</item>
- <tag><marker id="ErlDrvPDL"/>ErlDrvPDL</tag>
+ <tag><marker id="ErlDrvPDL"/><c>ErlDrvPDL</c></tag>
<item>
- <p>If certain port specific data have to be accessed from other
- threads than those calling the driver call-backs, a port data lock
- can be used in order to synchronize the operations on the data.
- Currently, the only port specific data that the emulator
+ <p>If certain port-specific data must be accessed from other
+ threads than those calling the driver callbacks, a port data lock
+ can be used to synchronize the operations on the data.
+ Currently, the only port-specific data that the emulator
associates with the port data lock is the driver queue.</p>
- <p>Normally a driver instance does not have a port data lock. If
- the driver instance wants to use a port data lock, it has to
+ <p>Normally a driver instance has no port data lock. If
+ the driver instance wants to use a port data lock, it must
create the port data lock by calling
- <seealso marker="#driver_pdl_create">driver_pdl_create()</seealso>.
- <em>NOTE</em>: Once the port data lock has been created, every
- access to data associated with the port data lock has to be done
- while having the port data lock locked. The port data lock is
- locked, and unlocked, respectively, by use of
- <seealso marker="#driver_pdl_lock">driver_pdl_lock()</seealso>, and
- <seealso marker="#driver_pdl_unlock">driver_pdl_unlock()</seealso>.</p>
+ <seealso marker="#driver_pdl_create">
+ <c>driver_pdl_create</c></seealso>.</p>
+ <note>
+ <p>Once the port data lock has been created, every
+ access to data associated with the port data lock must be done
+ while the port data lock is locked. The port data lock is
+ locked and unlocked by
+ <seealso marker="#driver_pdl_lock">
+ <c>driver_pdl_lock</c></seealso>, and
+ <seealso marker="#driver_pdl_unlock">
+ <c>driver_pdl_unlock</c></seealso>, respectively.</p>
+ </note>
<p>A port data lock is reference counted, and when the reference
- count reaches zero, it will be destroyed. The emulator will at
- least increment the reference count once when the lock is
- created and decrement it once when the port associated with
- the lock terminates. The emulator will also increment the
- reference count when an async job is enqueued and decrement
- it after an async job has been invoked. Besides
- this, it is the responsibility of the driver to ensure that
+ count reaches zero, it is destroyed. The emulator at
+ least increments the reference count once when the lock is
+ created and decrements it once the port associated with
+ the lock terminates. The emulator also increments the
+ reference count when an async job is enqueued and decrements
+ it when an async job has been invoked.
+ Also, the driver is responsible for ensuring that
the reference count does not reach zero before the last use
of the lock by the driver has been made. The reference count
- can be read, incremented, and decremented, respectively, by
- use of
- <seealso marker="#driver_pdl_get_refc">driver_pdl_get_refc()</seealso>,
- <seealso marker="#driver_pdl_inc_refc">driver_pdl_inc_refc()</seealso>, and
- <seealso marker="#driver_pdl_dec_refc">driver_pdl_dec_refc()</seealso>.</p>
+ can be read, incremented, and decremented by
+ <seealso marker="#driver_pdl_get_refc">
+ <c>driver_pdl_get_refc</c></seealso>,
+ <seealso marker="#driver_pdl_inc_refc">
+ <c>driver_pdl_inc_refc</c></seealso>, and
+ <seealso marker="#driver_pdl_dec_refc">
+ <c>driver_pdl_dec_refc</c></seealso>, respectively.</p>
</item>
-
- <tag><marker id="ErlDrvTid"/>ErlDrvTid</tag>
+ <tag><marker id="ErlDrvTid"/><c>ErlDrvTid</c></tag>
<item>
<p>Thread identifier.</p>
- <p>See also:
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>,
- <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>,
- <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>,
- <seealso marker="#erl_drv_thread_self">erl_drv_thread_self()</seealso>,
- and
- <seealso marker="#erl_drv_equal_tids">erl_drv_equal_tids()</seealso>.
- </p>
+ <p>See also <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>,
+ <seealso marker="#erl_drv_thread_exit">
+ <c>erl_drv_thread_exit</c></seealso>,
+ <seealso marker="#erl_drv_thread_join">
+ <c>erl_drv_thread_join</c></seealso>,
+ <seealso marker="#erl_drv_thread_self">
+ <c>erl_drv_thread_self</c></seealso>, and
+ <seealso marker="#erl_drv_equal_tids">
+ <c>erl_drv_equal_tids</c></seealso>.</p>
</item>
- <tag><marker id="ErlDrvThreadOpts"/>ErlDrvThreadOpts</tag>
+ <tag><marker id="ErlDrvThreadOpts"/><c>ErlDrvThreadOpts</c></tag>
<item>
- <p/>
- <code type="none">
- int suggested_stack_size;
- </code>
+ <code type="none">
+int suggested_stack_size;</code>
<p>Thread options structure passed to
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
- Currently the following fields exist:
- </p>
+ <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>.
+ The following fields exists:</p>
<taglist>
- <tag>suggested_stack_size</tag>
- <item>A suggestion, in kilo-words, on how large a stack to use. A value less
- than zero means default size.
+ <tag><c>suggested_stack_size</c></tag>
+ <item>A suggestion, in kilowords, on how large a stack to use.
+ A value &lt; 0 means default size.
</item>
</taglist>
- <p>See also:
- <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>,
- <seealso marker="#erl_drv_thread_opts_destroy">erl_drv_thread_opts_destroy()</seealso>,
- and
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
- </p>
+ <p>See also <seealso marker="#erl_drv_thread_opts_create">
+ <c>erl_drv_thread_opts_create</c></seealso>,
+ <seealso marker="#erl_drv_thread_opts_destroy">
+ <c>erl_drv_thread_opts_destroy</c></seealso>, and
+ <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>.</p>
</item>
-
- <tag><marker id="ErlDrvMutex"/>ErlDrvMutex</tag>
+ <tag><marker id="ErlDrvMutex"/><c>ErlDrvMutex</c></tag>
<item>
<p>Mutual exclusion lock. Used for synchronizing access to shared data.
- Only one thread at a time can lock a mutex.
- </p>
- <p>See also:
- <seealso marker="#erl_drv_mutex_create">erl_drv_mutex_create()</seealso>,
- <seealso marker="#erl_drv_mutex_destroy">erl_drv_mutex_destroy()</seealso>,
- <seealso marker="#erl_drv_mutex_lock">erl_drv_mutex_lock()</seealso>,
- <seealso marker="#erl_drv_mutex_trylock">erl_drv_mutex_trylock()</seealso>,
- and
- <seealso marker="#erl_drv_mutex_unlock">erl_drv_mutex_unlock()</seealso>.
- </p>
+ Only one thread at a time can lock a mutex.</p>
+ <p>See also <seealso marker="#erl_drv_mutex_create">
+ <c>erl_drv_mutex_create</c></seealso>,
+ <seealso marker="#erl_drv_mutex_destroy">
+ <c>erl_drv_mutex_destroy</c></seealso>,
+ <seealso marker="#erl_drv_mutex_lock">
+ <c>erl_drv_mutex_lock</c></seealso>,
+ <seealso marker="#erl_drv_mutex_trylock">
+ <c>erl_drv_mutex_trylock</c></seealso>, and
+ <seealso marker="#erl_drv_mutex_unlock">
+ <c>erl_drv_mutex_unlock</c></seealso>.</p>
</item>
- <tag><marker id="ErlDrvCond"/>ErlDrvCond</tag>
+ <tag><marker id="ErlDrvCond"/><c>ErlDrvCond</c></tag>
<item>
- <p>Condition variable. Used when threads need to wait for a specific
- condition to appear before continuing execution. Condition variables
- need to be used with associated mutexes.
- </p>
- <p>See also:
- <seealso marker="#erl_drv_cond_create">erl_drv_cond_create()</seealso>,
- <seealso marker="#erl_drv_cond_destroy">erl_drv_cond_destroy()</seealso>,
- <seealso marker="#erl_drv_cond_signal">erl_drv_cond_signal()</seealso>,
- <seealso marker="#erl_drv_cond_broadcast">erl_drv_cond_broadcast()</seealso>,
- and
- <seealso marker="#erl_drv_cond_wait">erl_drv_cond_wait()</seealso>.
- </p>
+ <p>Condition variable. Used when threads must wait for a specific
+ condition to appear before continuing execution. Condition variables
+ must be used with associated mutexes.</p>
+ <p>See also <seealso marker="#erl_drv_cond_create">
+ <c>erl_drv_cond_create</c></seealso>,
+ <seealso marker="#erl_drv_cond_destroy">
+ <c>erl_drv_cond_destroy</c></seealso>,
+ <seealso marker="#erl_drv_cond_signal">
+ <c>erl_drv_cond_signal</c></seealso>,
+ <seealso marker="#erl_drv_cond_broadcast">
+ <c>erl_drv_cond_broadcast</c></seealso>, and
+ <seealso marker="#erl_drv_cond_wait">
+ <c>erl_drv_cond_wait</c></seealso>.</p>
</item>
- <tag><marker id="ErlDrvRWLock"/>ErlDrvRWLock</tag>
+ <tag><marker id="ErlDrvRWLock"/><c>ErlDrvRWLock</c></tag>
<item>
<p>Read/write lock. Used to allow multiple threads to read shared data
- while only allowing one thread to write the same data. Multiple threads
- can read lock an rwlock at the same time, while only one thread can
- read/write lock an rwlock at a time.
- </p>
- <p>See also:
- <seealso marker="#erl_drv_rwlock_create">erl_drv_rwlock_create()</seealso>,
- <seealso marker="#erl_drv_rwlock_destroy">erl_drv_rwlock_destroy()</seealso>,
- <seealso marker="#erl_drv_rwlock_rlock">erl_drv_rwlock_rlock()</seealso>,
- <seealso marker="#erl_drv_rwlock_tryrlock">erl_drv_rwlock_tryrlock()</seealso>,
- <seealso marker="#erl_drv_rwlock_runlock">erl_drv_rwlock_runlock()</seealso>,
- <seealso marker="#erl_drv_rwlock_rwlock">erl_drv_rwlock_rwlock()</seealso>,
- <seealso marker="#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock()</seealso>,
- and
- <seealso marker="#erl_drv_rwlock_rwunlock">erl_drv_rwlock_rwunlock()</seealso>.
- </p>
+ while only allowing one thread to write the same data. Multiple
+ threads can read lock an rwlock at the same time, while only
+ one thread can read/write lock an rwlock at a time.</p>
+ <p>See also <seealso marker="#erl_drv_rwlock_create">
+ <c>erl_drv_rwlock_create</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_destroy">
+ <c>erl_drv_rwlock_destroy</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_rlock">
+ <c>erl_drv_rwlock_rlock</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_tryrlock">
+ <c>erl_drv_rwlock_tryrlock</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_runlock">
+ <c>erl_drv_rwlock_runlock</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_rwlock">
+ <c>erl_drv_rwlock_rwlock</c></seealso>,
+ <seealso marker="#erl_drv_rwlock_tryrwlock">
+ <c>erl_drv_rwlock_tryrwlock</c></seealso>, and
+ <seealso marker="#erl_drv_rwlock_rwunlock">
+ <c>erl_drv_rwlock_rwunlock</c></seealso>.</p>
</item>
- <tag><marker id="ErlDrvTSDKey"/>ErlDrvTSDKey</tag>
+ <tag><marker id="ErlDrvTSDKey"/><c>ErlDrvTSDKey</c></tag>
<item>
- <p>Key which thread specific data can be associated with.</p>
- <p>See also:
- <seealso marker="#erl_drv_tsd_key_create">erl_drv_tsd_key_create()</seealso>,
- <seealso marker="#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy()</seealso>,
- <seealso marker="#erl_drv_tsd_set">erl_drv_tsd_set()</seealso>,
- and
- <seealso marker="#erl_drv_tsd_get">erl_drv_tsd_get()</seealso>.
- </p>
+ <p>Key that thread-specific data can be associated with.</p>
+ <p>See also <seealso marker="#erl_drv_tsd_key_create">
+ <c>erl_drv_tsd_key_create</c></seealso>,
+ <seealso marker="#erl_drv_tsd_key_destroy">
+ <c>erl_drv_tsd_key_destroy</c></seealso>,
+ <seealso marker="#erl_drv_tsd_set">
+ <c>erl_drv_tsd_set</c></seealso>, and
+ <seealso marker="#erl_drv_tsd_get">
+ <c>erl_drv_tsd_get</c></seealso>.</p>
</item>
- <tag><marker id="ErlDrvTime"/>ErlDrvTime</tag>
+ <tag><marker id="ErlDrvTime"/><c>ErlDrvTime</c></tag>
<item>
- <p>A signed 64-bit integer type for representation of time.</p>
+ <p>A signed 64-bit integer type for time representation.</p>
</item>
- <tag><marker id="ErlDrvTimeUnit"/>ErlDrvTimeUnit</tag>
+ <tag><marker id="ErlDrvTimeUnit"/><c>ErlDrvTimeUnit</c></tag>
<item>
<p>An enumeration of time units supported by the driver API:</p>
- <taglist>
+ <taglist>
<tag><c>ERL_DRV_SEC</c></tag>
- <item><p>Seconds</p></item>
+ <item>Seconds</item>
<tag><c>ERL_DRV_MSEC</c></tag>
- <item><p>Milliseconds</p></item>
+ <item>Milliseconds</item>
<tag><c>ERL_DRV_USEC</c></tag>
- <item><p>Microseconds</p></item>
+ <item>Microseconds</item>
<tag><c>ERL_DRV_NSEC</c></tag>
- <item><p>Nanoseconds</p></item>
- </taglist>
+ <item>Nanoseconds</item>
+ </taglist>
</item>
- </taglist>
- </section>
+ </taglist>
+ </section>
<funcs>
<func>
- <name><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo *sys_info_ptr, size_t size)</nametext></name>
- <fsummary>Get information about the Erlang runtime system</fsummary>
+ <name><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry
+ *de)</nametext></name>
+ <fsummary>Add a driver entry.</fsummary>
<desc>
- <marker id="driver_system_info"></marker>
- <p>This function will write information about the Erlang runtime
- system into the
- <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
- structure referred to by the first argument. The second
- argument should be the size of the
- <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
- structure, i.e., <c>sizeof(ErlDrvSysInfo)</c>.</p>
- <p>See the documentation of the
- <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
- structure for information about specific fields.</p>
+ <marker id="add_driver_entry"></marker>
+ <p>Adds a driver entry to the list of drivers known by Erlang.
+ The <seealso marker="driver_entry#init"><c>init</c></seealso>
+ function of parameter <c>de</c> is called.</p>
+ <note>
+ <p>To use this function for adding drivers residing in
+ dynamically loaded code is dangerous. If the driver code
+ for the added driver resides in the same dynamically
+ loaded module (that is, <c>.so</c> file) as a normal
+ dynamically loaded driver (loaded with the <c>erl_ddll</c>
+ interface), the caller is to call
+ <seealso marker="#driver_lock_driver">
+ <c>driver_lock_driver</c></seealso> before
+ adding driver entries.</p>
+ <p><em>Use of this function is generally deprecated.</em></p>
+ </note>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf, ErlDrvSizeT len)</nametext></name>
- <fsummary>Send data from driver to port owner</fsummary>
+ <name><ret>void *</ret>
+ <nametext>driver_alloc(ErlDrvSizeT size)</nametext></name>
+ <fsummary>Allocate memory.</fsummary>
<desc>
- <marker id="driver_output"></marker>
- <p>The <c>driver_output</c> function is used to send data from
- the driver up to the emulator. The data will be received as
- terms or binary data, depending on how the driver port was
- opened.</p>
- <p>The data is queued in the port owner process' message
- queue. Note that this does not yield to the emulator. (Since
- the driver and the emulator run in the same thread.)</p>
- <p>The parameter <c>buf</c> points to the data to send, and
- <c>len</c> is the number of bytes.</p>
- <p>The return value for all output functions is 0. (Unless the
- driver is used for distribution, in which case it can fail
- and return -1. For normal use, the output function always
- returns 0.)</p>
+ <marker id="driver_alloc"></marker>
+ <p>Allocates a memory block of the size specified
+ in <c>size</c>, and returns it. This fails only on out of
+ memory, in which case <c>NULL</c> is returned. (This is most
+ often a wrapper for <c>malloc</c>).</p>
+ <p>Memory allocated must be explicitly freed with a corresponding
+ call to <seealso marker="#driver_free"><c>driver_free</c></seealso>
+ (unless otherwise stated).</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)</nametext></name>
- <fsummary>Send data and binary data to port owner</fsummary>
+ <name><ret>ErlDrvBinary *</ret>
+ <nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name>
+ <fsummary>Allocate a driver binary.</fsummary>
<desc>
- <marker id="driver_output2"></marker>
- <p>The <c>driver_output2</c> function first sends <c>hbuf</c>
- (length in <c>hlen</c>) data as a list, regardless of port
- settings. Then <c>buf</c> is sent as a binary or list.
- E.g. if <c>hlen</c> is 3 then the port owner process will
- receive <c>[H1, H2, H3 | T]</c>.</p>
- <p>The point of sending data as a list header, is to facilitate
- matching on the data received.</p>
- <p>The return value is 0 for normal use.</p>
+ <marker id="driver_alloc_binary"></marker>
+ <p>Allocates a driver binary with a memory block
+ of at least <c>size</c> bytes, and returns a pointer to it,
+ or <c>NULL</c> on failure (out of memory). When a driver binary has
+ been sent to the emulator, it must not be changed. Every
+ allocated binary is to be freed by a corresponding call to
+ <seealso marker="#driver_free_binary">
+ <c>driver_free_binary</c></seealso> (unless otherwise stated).</p>
+ <p>Notice that a driver binary has an internal reference counter.
+ This means that calling <c>driver_free_binary</c>, it may not
+ actually dispose of it. If it is sent to the emulator, it can
+ be referenced there.</p>
+ <p>The driver binary has a field, <c>orig_bytes</c>, which
+ marks the start of the data in the binary.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name>
- <fsummary>Send data from a driver binary to port owner</fsummary>
+ <name><ret>long</ret><nametext>driver_async(ErlDrvPort port, unsigned
+ int* key, void (*async_invoke)(void*), void* async_data, void
+ (*async_free)(void*))</nametext></name>
+ <fsummary>Perform an asynchronous call within a driver.</fsummary>
<desc>
- <marker id="driver_output_binary"></marker>
- <p>This function sends data to port owner process from a
- driver binary, it has a header buffer (<c>hbuf</c>
- and <c>hlen</c>) just like <c>driver_output2</c>. The
- <c>hbuf</c> parameter can be <c>NULL</c>.</p>
- <p>The parameter <c>offset</c> is an offset into the binary and
- <c>len</c> is the number of bytes to send.</p>
- <p>Driver binaries are created with <c>driver_alloc_binary</c>.</p>
- <p>The data in the header is sent as a list and the binary as
- an Erlang binary in the tail of the list.</p>
- <p>E.g. if <c>hlen</c> is 2, then the port owner process will
- receive <c><![CDATA[[H1, H2 | <<T>>]]]></c>.</p>
- <p>The return value is 0 for normal use.</p>
- <p>Note that, using the binary syntax in Erlang, the driver
- application can match the header directly from the binary,
- so the header can be put in the binary, and hlen can be set
- to 0.</p>
+ <marker id="driver_async"></marker>
+ <p>Performs an asynchronous call. The function
+ <c>async_invoke</c> is invoked in a thread separate from the
+ emulator thread. This enables the driver to perform
+ time-consuming, blocking operations without blocking the
+ emulator.</p>
+ <p>The async thread pool size can be set with command-line argument
+ <seealso marker="erl#async_thread_pool_size"><c>+A</c></seealso>
+ in <seealso marker="erl"><c>erl(1)</c></seealso>.
+ If an async thread pool is unavailable, the call is made
+ synchronously in the thread calling <c>driver_async</c>. The
+ current number of async threads in the async thread pool can be
+ retrieved through <seealso marker="#driver_system_info">
+ <c>driver_system_info</c></seealso>.</p>
+ <p>If a thread pool is available, a thread is used.
+ If argument <c>key</c> is <c>NULL</c>, the threads from the
+ pool are used in a round-robin way, each call to
+ <c>driver_async</c> uses the next thread in the pool. With
+ argument <c>key</c> set, this behavior is changed. The two
+ same values of <c>*key</c> always get the same thread.</p>
+ <p>To ensure that a driver instance always uses the same
+ thread, the following call can be used:</p>
+ <code type="none"><![CDATA[
+unsigned int myKey = driver_async_port_key(myPort);
+
+r = driver_async(myPort, &myKey, myData, myFunc); ]]></code>
+ <p>It is enough to initialize <c>myKey</c> once for each
+ driver instance.</p>
+ <p>If a thread is already working, the calls are
+ queued up and executed in order. Using the same thread for
+ each driver instance ensures that the calls are made in sequence.</p>
+ <p>The <c>async_data</c> is the argument to the functions
+ <c>async_invoke</c> and <c>async_free</c>. It is typically a
+ pointer to a structure containing a pipe or event that
+ can be used to signal that the async operation completed.
+ The data is to be freed in <c>async_free</c>.</p>
+ <p>When the async operation is done,
+ <seealso marker="driver_entry#ready_async">
+ <c>ready_async</c></seealso> driver
+ entry function is called. If <c>ready_async</c> is <c>NULL</c> in
+ the driver entry, the <c>async_free</c> function is called
+ instead.</p>
+ <p>The return value is <c>-1</c> if the <c>driver_async</c> call
+ fails.</p>
+ <note>
+ <p>As from ERTS 5.5.4.3 the default stack size for
+ threads in the async-thread pool is 16 kilowords,
+ that is, 64 kilobyte on 32-bit architectures.
+ This small default size has been chosen because the
+ amount of async-threads can be quite large. The
+ default stack size is enough for drivers delivered
+ with Erlang/OTP, but is possibly not sufficiently large
+ for other dynamically linked-in drivers that use the
+ <c>driver_async</c> functionality. A suggested stack size
+ for threads in the async-thread pool can be configured
+ through command-line argument
+ <seealso marker="erl#async_thread_stack_size"><c>+a</c></seealso>
+ in <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
+ </note>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf, ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name>
- <fsummary>Send vectorized data to port owner</fsummary>
+ <name><ret>unsigned int</ret><nametext>driver_async_port_key(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Calculate an async key from an ErlDrvPort.</fsummary>
<desc>
- <marker id="driver_outputv"></marker>
- <p>This function sends data from an IO vector, <c>ev</c>, to
- the port owner process. It has a header buffer (<c>hbuf</c>
- and <c>hlen</c>), just like <c>driver_output2</c>.</p>
- <p>The <c>skip</c> parameter is a number of bytes to skip of
- the <c>ev</c> vector from the head.</p>
- <p>You get vectors of <c>ErlIOVec</c> type from the driver
- queue (see below), and the <seealso marker="driver_entry#outputv">outputv</seealso> driver entry
- function. You can also make them yourself, if you want to
- send several <c>ErlDrvBinary</c> buffers at once. Often
- it is faster to use <c>driver_output</c> or
- <c>driver_output_binary</c>.</p>
- <p>E.g. if <c>hlen</c> is 2 and <c>ev</c> points to an array of
- three binaries, the port owner process will receive <c><![CDATA[[H1, H2, <<B1>>, <<B2>> | <<B3>>]]]></c>.</p>
- <p>The return value is 0 for normal use.</p>
- <p>The comment for <c>driver_output_binary</c> applies for
- <c>driver_outputv</c> too.</p>
+ <marker id="driver_async_port_key"></marker>
+ <p>Calculates a key for later use in <seealso
+ marker="#driver_async"><c>driver_async</c></seealso>. The keys are
+ evenly distributed so that a fair mapping between port IDs
+ and async thread IDs is achieved.</p>
+ <note>
+ <p>Before Erlang/OTP R16, the port ID could be used as a key
+ with proper casting, but after the rewrite of the port
+ subsystem, this is no longer the case. With this function, you
+ can achieve the same distribution based on port IDs as before
+ Erlang/OTP R16.</p>
+ </note>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev, char *buf, ErlDrvSizeT len)</nametext></name>
- <fsummary>Collect data segments into a buffer</fsummary>
+ <name><ret>long</ret>
+ <nametext>driver_binary_dec_refc(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Decrement the reference count of a driver binary.</fsummary>
<desc>
- <marker id="driver_vec_to_buf"></marker>
- <p>This function collects several segments of data, referenced
- by <c>ev</c>, by copying them in order to the buffer
- <c>buf</c>, of the size <c>len</c>.</p>
- <p>If the data is to be sent from the driver to the port owner
- process, it is faster to use <c>driver_outputv</c>.</p>
- <p>The return value is the space left in the buffer, i.e. if
- the <c>ev</c> contains less than <c>len</c> bytes it's the
- difference, and if <c>ev</c> contains <c>len</c> bytes or
- more, it's 0. This is faster if there is more than one header byte,
- since the binary syntax can construct integers directly from
- the binary.</p>
+ <marker id="driver_binary_dec_refc"></marker>
+ <p>Decrements the reference count on <c>bin</c> and returns
+ the reference count reached after the decrement.</p>
+ <p>This function is thread-safe.</p>
+ <note>
+ <p>The reference count of driver binary is normally to be decremented
+ by calling <seealso marker="#driver_free_binary">
+ <c>driver_free_binary</c></seealso>.</p>
+ <p><c>driver_binary_dec_refc</c> does <em>not</em> free
+ the binary if the reference count reaches zero. <em>Only</em>
+ use <c>driver_binary_dec_refc</c> when you are sure
+ <em>not</em> to reach a reference count of zero.</p>
+ </note>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned long time)</nametext></name>
- <fsummary>Set a timer to call the driver</fsummary>
+ <name><ret>long</ret>
+ <nametext>driver_binary_get_refc(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Get the reference count of a driver binary.</fsummary>
<desc>
- <marker id="driver_set_timer"></marker>
- <p>This function sets a timer on the driver, which will count
- down and call the driver when it is timed out. The
- <c>time</c> parameter is the time in milliseconds before the
- timer expires.</p>
- <p>When the timer reaches 0 and expires, the driver entry
- function <seealso marker="driver_entry#timeout">timeout</seealso> is called.</p>
- <p>Note that there is only one timer on each driver instance;
- setting a new timer will replace an older one.</p>
- <p>Return value is 0 (-1 only when the <c>timeout</c> driver
- function is <c>NULL</c>).</p>
+ <marker id="driver_binary_get_refc"></marker>
+ <p>Returns the current reference count on <c>bin</c>.</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>long</ret>
+ <nametext>driver_binary_inc_refc(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Increment the reference count of a driver binary.</fsummary>
+ <desc>
+ <marker id="driver_binary_inc_refc"></marker>
+ <p>Increments the reference count on <c>bin</c> and returns
+ the reference count reached after the increment.</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Return the process making the driver call.</fsummary>
+ <desc>
+ <marker id="driver_caller"></marker>
+ <p>Returns the process ID of the process that
+ made the current call to the driver. The process ID can be used with
+ <seealso marker="#driver_send_term"><c>driver_send_term</c></seealso>
+ to send back data to the caller.
+ <c>driver_caller</c> only returns valid data
+ when currently executing in one of the following driver callbacks:</p>
+ <taglist>
+ <tag><seealso marker="driver_entry#start">
+ <c>start</c></seealso></tag>
+ <item>Called from <seealso marker="erlang:open_port/2">
+ <c>erlang:open_port/2</c></seealso>.</item>
+ <tag><seealso marker="driver_entry#output">
+ <c>output</c></seealso></tag>
+ <item>Called from <seealso marker="erlang:send/2">
+ <c>erlang:send/2</c></seealso> and
+ <seealso marker="erlang:port_command/2">
+ <c>erlang:port_command/2</c></seealso>.</item>
+ <tag><seealso marker="driver_entry#outputv">
+ <c>outputv</c></seealso></tag>
+ <item>Called from <seealso marker="erlang:send/2">
+ <c>erlang:send/2</c></seealso> and
+ <seealso marker="erlang:port_command/2">
+ <c>erlang:port_command/2</c></seealso>.</item>
+ <tag><seealso marker="driver_entry#control">
+ <c>control</c></seealso></tag>
+ <item>Called from <seealso marker="erlang:port_control/3">
+ <c>erlang:port_control/3</c></seealso>.</item>
+ <tag><seealso marker="driver_entry#call">
+ <c>call</c></seealso></tag>
+ <item>Called from <seealso marker="erlang:port_call/3">
+ <c>erlang:port_call/3</c></seealso>.</item>
+ </taglist>
+ <p>Notice that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_cancel_timer(ErlDrvPort port)</nametext></name>
- <fsummary>Cancel a previously set timer</fsummary>
+ <name><ret>int</ret>
+ <nametext>driver_cancel_timer(ErlDrvPort port)</nametext></name>
+ <fsummary>Cancel a previously set timer.</fsummary>
<desc>
<marker id="driver_cancel_timer"></marker>
- <p>This function cancels a timer set with
- <c>driver_set_timer</c>.</p>
- <p>The return value is 0.</p>
+ <p>Cancels a timer set with
+ <seealso marker="#driver_set_timer">
+ <c>driver_set_timer</c></seealso>.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned long *time_left)</nametext></name>
- <fsummary>Read the time left before timeout</fsummary>
+ <name><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor
+ *monitor1, const ErlDrvMonitor *monitor2)</nametext></name>
+ <fsummary>Compare two monitors.</fsummary>
<desc>
- <marker id="driver_read_timer"></marker>
- <p>This function reads the current time of a timer, and places
- the result in <c>time_left</c>. This is the time in
- milliseconds, before the timeout will occur.</p>
- <p>The return value is 0.</p>
+ <marker id="driver_compare_monitors"></marker>
+ <p>Compares two <c>ErlDrvMonitor</c>s.
+ Can also be used to imply some artificial order on monitors,
+ for whatever reason.</p>
+ <p>Returns <c>0</c> if <c>monitor1</c> and <c>monitor2</c> are equal,
+ &lt; <c>0</c> if <c>monitor1</c> &lt; <c>monitor2</c>, and
+ &gt; <c>0</c> if <c>monitor1</c> &gt; <c>monitor2</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_get_now(ErlDrvNowData *now)</nametext></name>
- <fsummary>Read a system timestamp</fsummary>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Return the port owner process.</fsummary>
<desc>
- <marker id="driver_get_now"></marker>
- <warning><p><em>This function is deprecated! Do not use it!</em>
- Use <seealso marker="#erl_drv_monotonic_time"><c>erl_drv_monotonic_time()</c></seealso>
- (perhaps in combination with
- <seealso marker="#erl_drv_time_offset"><c>erl_drv_time_offset()</c></seealso>)
- instead.</p></warning>
- <p>This function reads a timestamp into the memory pointed to by
- the parameter <c>now</c>. See the description of <seealso marker="#ErlDrvNowData">ErlDrvNowData</seealso> for
- specification of its fields. </p>
- <p>The return value is 0 unless the <c>now</c> pointer is not
- valid, in which case it is &lt; 0. </p>
+ <marker id="driver_connected"></marker>
+ <p>Returns the port owner process.</p>
+ <p>Notice that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent event, int mode, int on)</nametext></name>
- <fsummary>Provide an event for having the emulator call the driver</fsummary>
+ <name><ret>ErlDrvPort</ret><nametext>driver_create_port(ErlDrvPort port,
+ ErlDrvTermData owner_pid, char* name,
+ ErlDrvData drv_data)</nametext></name>
+ <fsummary>Create a new port (driver instance).</fsummary>
<desc>
- <marker id="driver_select"></marker>
- <p>This function is used by drivers to provide the emulator with
- events to check for. This enables the emulator to call the driver
- when something has happened asynchronously.</p>
- <p>The <c>event</c> argument identifies an OS-specific event object.
- On Unix systems, the functions <c>select</c>/<c>poll</c> are used. The
- event object must be a socket or pipe (or other object that
- <c>select</c>/<c>poll</c> can use).
- On windows, the Win32 API function <c>WaitForMultipleObjects</c>
- is used. This places other restrictions on the event object.
- Refer to the Win32 SDK documentation.</p>
- <p>The <c>on</c> parameter should be <c>1</c> for setting events
- and <c>0</c> for clearing them.</p>
- <p>The <c>mode</c> argument is a bitwise-or combination of
- <c>ERL_DRV_READ</c>, <c>ERL_DRV_WRITE</c> and <c>ERL_DRV_USE</c>.
- The first two specify whether to wait for read events and/or write
- events. A fired read event will call
- <seealso marker="driver_entry#ready_input">ready_input</seealso>
- while a fired write event will call
- <seealso marker="driver_entry#ready_output">ready_output</seealso>.
- </p>
- <note>
- <p>Some OS (Windows) do not differentiate between read and write events.
- The call-back for a fired event then only depends on the value of <c>mode</c>.</p>
- </note>
- <p><c>ERL_DRV_USE</c> specifies if we are using the event object or if we want to close it.
- On an emulator with SMP support, it is not safe to clear all events
- and then close the event object after <c>driver_select</c> has
- returned. Another thread may still be using the event object
- internally. To safely close an event object call
- <c>driver_select</c> with <c>ERL_DRV_USE</c> and <c>on==0</c>. That
- will clear all events and then call
- <seealso marker="driver_entry#stop_select">stop_select</seealso>
- when it is safe to close the event object.
- <c>ERL_DRV_USE</c> should be set together with the first event
- for an event object. It is harmless to set <c>ERL_DRV_USE</c>
- even though it already has been done. Clearing all events but keeping
- <c>ERL_DRV_USE</c> set will indicate that we are using the event
- object and probably will set events for it again.</p>
- <note>
- <p>ERL_DRV_USE was added in OTP release R13. Old drivers will still work
- as before. But it is recommended to update them to use <c>ERL_DRV_USE</c> and
- <c>stop_select</c> to make sure that event objects are closed in a safe way.</p>
- </note>
- <p>The return value is 0 (failure, -1, only if the
- <c>ready_input</c>/<c>ready_output</c> is
- <c>NULL</c>).</p>
+ <p>Creates a new port executing the same driver
+ code as the port creating the new port.</p>
+ <taglist>
+ <tag><c>port</c></tag>
+ <item>The port handle of the port (driver instance) creating
+ the new port.</item>
+ <tag><c>owner_pid</c></tag>
+ <item>The process ID of the Erlang process to become
+ owner of the new port. This process will be linked
+ to the new port. You usually want to use
+ <c>driver_caller(port)</c> as <c>owner_pid</c>.</item>
+ <tag><c>name</c></tag>
+ <item>The port name of the new port. You usually want to
+ use the same port name as the driver name
+ (<seealso marker="driver_entry#driver_name">
+ <c>driver_name</c></seealso> field of the
+ <seealso marker="driver_entry"><c>driver_entry</c></seealso>).
+ </item>
+ <tag><c>drv_data</c></tag>
+ <item>The driver-defined handle that is passed in later
+ calls to driver callbacks. Notice that the
+ <seealso marker="driver_entry#start">driver start
+ callback</seealso> is not called for this new driver instance.
+ The driver-defined handle is normally created in the
+ <seealso marker="driver_entry#start">driver start callback</seealso>
+ when a port is created through
+ <seealso marker="erlang#open_port/2">
+ <c>erlang:open_port/2</c></seealso>.
+ </item>
+ </taglist>
+ <p>The caller of <c>driver_create_port</c> is allowed to
+ manipulate the newly created port when <c>driver_create_port</c>
+ has returned. When
+ <seealso marker="#smp_support">port level locking</seealso>
+ is used, the creating port is only allowed to
+ manipulate the newly created port until the current driver
+ callback, which was called by the emulator, returns.</p>
</desc>
</func>
+
<func>
- <name><ret>void *</ret><nametext>driver_alloc(ErlDrvSizeT size)</nametext></name>
- <fsummary>Allocate memory</fsummary>
+ <name><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port,
+ const ErlDrvMonitor *monitor)</nametext></name>
+ <fsummary>Stop monitoring a process from a driver.</fsummary>
<desc>
- <marker id="driver_alloc"></marker>
- <p>This function allocates a memory block of the size specified
- in <c>size</c>, and returns it. This only fails on out of
- memory, in that case <c>NULL</c> is returned. (This is most
- often a wrapper for <c>malloc</c>).</p>
- <p>Memory allocated must be explicitly freed with a corresponding
- call to <c>driver_free</c> (unless otherwise stated).</p>
- <p>This function is thread-safe.</p>
+ <marker id="driver_demonitor_process"></marker>
+ <p>Cancels a monitor created earlier.</p>
+ <p>Returns <c>0</c> if a monitor was removed and &gt; 0 if the monitor
+ no longer exists.</p>
</desc>
</func>
+
<func>
- <name><ret>void *</ret><nametext>driver_realloc(void *ptr, ErlDrvSizeT size)</nametext></name>
- <fsummary>Resize an allocated memory block</fsummary>
+ <name><ret>ErlDrvSizeT</ret><nametext>driver_deq(ErlDrvPort port,
+ ErlDrvSizeT size)</nametext></name>
+ <fsummary>Dequeue data from the head of the driver queue.</fsummary>
<desc>
- <marker id="driver_realloc"></marker>
- <p>This function resizes a memory block, either in place, or by
- allocating a new block, copying the data and freeing the old
- block. A pointer is returned to the reallocated memory. On
- failure (out of memory), <c>NULL</c> is returned. (This is
- most often a wrapper for <c>realloc</c>.)</p>
- <p>This function is thread-safe.</p>
+ <marker id="driver_deq"></marker>
+ <p>Dequeues data by moving the head pointer
+ forward in the driver queue by <c>size</c> bytes. The data
+ in the queue is deallocated.</p>
+ <p>Returns the number of bytes remaining in the queue on success,
+ otherwise <c>-1</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name>
- <fsummary>Free an allocated memory block</fsummary>
+ <name><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf,
+ ErlDrvSizeT len)</nametext></name>
+ <fsummary>Enqueue data in the driver queue.</fsummary>
<desc>
- <marker id="driver_free"></marker>
- <p>This function frees the memory pointed to by <c>ptr</c>. The
- memory should have been allocated with
- <c>driver_alloc</c>. All allocated memory should be
- deallocated, just once. There is no garbage collection in
- drivers.</p>
- <p>This function is thread-safe.</p>
+ <marker id="driver_enq"></marker>
+ <p>Enqueues data in the driver queue. The data in
+ <c>buf</c> is copied (<c>len</c> bytes) and placed at the
+ end of the driver queue. The driver queue is normally used
+ in a FIFO way.</p>
+ <p>The driver queue is available to queue output from the
+ emulator to the driver (data from the driver to the emulator
+ is queued by the emulator in normal Erlang message
+ queues). This can be useful if the driver must wait for
+ slow devices, and so on, and wants to yield back to the
+ emulator. The driver queue is implemented as an <c>ErlIOVec</c>.</p>
+ <p>When the queue contains data, the driver does not close until
+ the queue is empty.</p>
+ <p>The return value is <c>0</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvBinary *</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name>
- <fsummary>Allocate a driver binary</fsummary>
+ <name><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port,
+ ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext>
+ </name>
+ <fsummary>Enqueue binary in the driver queue.</fsummary>
<desc>
- <marker id="driver_alloc_binary"></marker>
- <p>This function allocates a driver binary with a memory block
- of at least <c>size</c> bytes, and returns a pointer to it,
- or NULL on failure (out of memory). When a driver binary has
- been sent to the emulator, it must not be altered. Every
- allocated binary should be freed by a corresponding call to
- <c>driver_free_binary</c> (unless otherwise stated).</p>
- <p>Note that a driver binary has an internal reference counter,
- this means that calling <c>driver_free_binary</c> it may not
- actually dispose of it. If it's sent to the emulator, it may
- be referenced there.</p>
- <p>The driver binary has a field, <c>orig_bytes</c>, which
- marks the start of the data in the binary.</p>
- <p>This function is thread-safe.</p>
+ <marker id="driver_enq_bin"></marker>
+ <p>Enqueues a driver binary in the driver
+ queue. The data in <c>bin</c> at <c>offset</c> with length
+ <c>len</c> is placed at the end of the queue. This function
+ is most often faster than
+ <seealso marker="#driver_enq"><c>driver_enq</c></seealso>,
+ because no data must be copied.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvBinary *</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name>
- <fsummary>Resize a driver binary</fsummary>
+ <name><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev,
+ ErlDrvSizeT skip)</nametext></name>
+ <fsummary>Enqueue vector in the driver queue.</fsummary>
<desc>
- <marker id="driver_realloc_binary"></marker>
- <p>This function resizes a driver binary, while keeping the
- data. The resized driver binary is returned. On failure (out
- of memory), <c>NULL</c> is returned.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="driver_enqv"></marker>
+ <p>Enqueues the data in <c>ev</c>, skipping the
+ first <c>skip</c> bytes of it, at the end of the driver
+ queue. It is faster than
+ <seealso marker="#driver_enq"><c>driver_enq</c></seealso>,
+ because no data must be copied.</p>
+ <p>The return value is <c>0</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>driver_free_binary(ErlDrvBinary *bin)</nametext></name>
- <fsummary>Free a driver binary</fsummary>
+ <name><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int
+ error)</nametext></name>
+ <name><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char
+ *string)</nametext></name>
+ <name><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int
+ error)</nametext></name>
+ <fsummary>Fail with error.</fsummary>
<desc>
- <marker id="driver_free_binary"></marker>
- <p>This function frees a driver binary <c>bin</c>, allocated
- previously with <c>driver_alloc_binary</c>. Since binaries
- in Erlang are reference counted, the binary may still be
- around.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="driver_failure_atom"></marker>
+ <marker id="driver_failure_posix"></marker>
+ <marker id="driver_failure"></marker>
+ <p>Signals to Erlang that the driver has
+ encountered an error and is to be closed. The port is
+ closed and the tuple <c>{'EXIT', error, Err}</c> is sent to
+ the port owner process, where error is an error atom
+ (<c>driver_failure_atom</c> and
+ <c>driver_failure_posix</c>) or an integer
+ (<c>driver_failure</c>).</p>
+ <p>The driver is to fail only when in severe error situations,
+ when the driver cannot possibly keep open, for example,
+ buffer allocation gets out of memory. For normal errors
+ it is more appropriate to send error codes with
+ <seealso marker="#driver_output"><c>driver_output</c></seealso>.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_binary_get_refc(ErlDrvBinary *bin)</nametext></name>
- <fsummary>Get the reference count of a driver binary</fsummary>
+ <name><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Fail with EOF.</fsummary>
<desc>
- <marker id="driver_binary_get_refc"></marker>
- <p>Returns current reference count on <c>bin</c>.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="driver_failure_eof"></marker>
+ <p>Signals to Erlang that the driver has
+ encountered an EOF and is to be closed, unless the port was
+ opened with option <c>eof</c>, in which case <c>eof</c> is sent
+ to the port. Otherwise the port is closed and an
+ <c>'EXIT'</c> message is sent to the port owner process.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_binary_inc_refc(ErlDrvBinary *bin)</nametext></name>
- <fsummary>Increment the reference count of a driver binary</fsummary>
+ <name><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name>
+ <fsummary>Free an allocated memory block.</fsummary>
<desc>
- <marker id="driver_binary_inc_refc"></marker>
- <p>Increments the reference count on <c>bin</c> and returns
- the reference count reached after the increment.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="driver_free"></marker>
+ <p>Frees the memory pointed to by <c>ptr</c>. The
+ memory is to have been allocated with
+ <c>driver_alloc</c>. All allocated memory is to be
+ deallocated, only once. There is no garbage collection in
+ drivers.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_binary_dec_refc(ErlDrvBinary *bin)</nametext></name>
- <fsummary>Decrement the reference count of a driver binary</fsummary>
+ <name><ret>void</ret>
+ <nametext>driver_free_binary(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Free a driver binary.</fsummary>
<desc>
- <marker id="driver_binary_dec_refc"></marker>
- <p>Decrements the reference count on <c>bin</c> and returns
- the reference count reached after the decrement.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
- <note>
- <p>You should normally decrement the reference count of a
- driver binary by calling
- <seealso marker="#driver_free_binary">driver_free_binary()</seealso>.
- <c>driver_binary_dec_refc()</c> does <em>not</em> free
- the binary if the reference count reaches zero. <em>Only</em>
- use <c>driver_binary_dec_refc()</c> when you are sure
- <em>not</em> to reach a reference count of zero.</p>
- </note>
+ <marker id="driver_free_binary"></marker>
+ <p>Frees a driver binary <c>bin</c>, allocated previously with
+ <seealso marker="#driver_alloc_binary">
+ <c>driver_alloc_binary</c></seealso>. As binaries
+ in Erlang are reference counted, the binary can still be around.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf, ErlDrvSizeT len)</nametext></name>
- <fsummary>Enqueue data in the driver queue</fsummary>
+ <name><ret>ErlDrvTermData</ret>
+ <nametext>driver_get_monitored_process(ErlDrvPort port, const
+ ErlDrvMonitor *monitor)</nametext></name>
+ <fsummary>Retrieve the process ID from a monitor.</fsummary>
<desc>
- <marker id="driver_enq"></marker>
- <p>This function enqueues data in the driver queue. The data in
- <c>buf</c> is copied (<c>len</c> bytes) and placed at the
- end of the driver queue. The driver queue is normally used
- in a FIFO way.</p>
- <p>The driver queue is available to queue output from the
- emulator to the driver (data from the driver to the emulator
- is queued by the emulator in normal erlang message
- queues). This can be useful if the driver has to wait for
- slow devices etc, and wants to yield back to the
- emulator. The driver queue is implemented as an ErlIOVec.</p>
- <p>When the queue contains data, the driver won't close, until
- the queue is empty.</p>
- <p>The return value is 0.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_get_monitored_process"></marker>
+ <p>Returns the process ID associated with a living
+ monitor. It can be used in the
+ <seealso marker="driver_entry#process_exit">
+ <c>process_exit</c></seealso> callback to
+ get the process identification for the exiting process.</p>
+ <p>Returns <c>driver_term_nil</c> if the monitor no longer exists.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf, ErlDrvSizeT len)</nametext></name>
- <fsummary>Push data at the head of the driver queue</fsummary>
+ <name><ret>int</ret>
+ <nametext>driver_get_now(ErlDrvNowData *now)</nametext></name>
+ <fsummary>Read a system time stamp.</fsummary>
<desc>
- <marker id="driver_pushq"></marker>
- <p>This function puts data at the head of the driver queue. The
- data in <c>buf</c> is copied (<c>len</c> bytes) and placed
- at the beginning of the queue.</p>
- <p>The return value is 0.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_get_now"></marker>
+ <warning>
+ <p><em>This function is deprecated. Do not use it.</em> Use
+ <seealso marker="#erl_drv_monotonic_time">
+ <c>erl_drv_monotonic_time</c></seealso> (perhaps in combination with
+ <seealso marker="#erl_drv_time_offset">
+ <c>erl_drv_time_offset</c></seealso>) instead.</p>
+ </warning>
+ <p>Reads a time stamp into the memory pointed to by
+ parameter <c>now</c>. For information about specific fields, see
+ <seealso marker="#ErlDrvNowData"><c>ErlDrvNowData</c></seealso>.</p>
+ <p>The return value is <c>0</c>, unless the <c>now</c> pointer is
+ invalid, in which case it is &lt; <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvSizeT</ret><nametext>driver_deq(ErlDrvPort port, ErlDrvSizeT size)</nametext></name>
- <fsummary>Dequeue data from the head of the driver queue</fsummary>
+ <name><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Ensure the driver is never unloaded.</fsummary>
<desc>
- <marker id="driver_deq"></marker>
- <p>This function dequeues data by moving the head pointer
- forward in the driver queue by <c>size</c> bytes. The data
- in the queue will be deallocated.</p>
- <p>The return value is the number of bytes remaining in the queue
- or -1 on failure.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_lock_driver"></marker>
+ <p>Locks the driver used by the port <c>port</c>
+ in memory for the rest of the emulator process'
+ lifetime. After this call, the driver behaves as one of Erlang's
+ statically linked-in drivers.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvSizeT</ret><nametext>driver_sizeq(ErlDrvPort port)</nametext></name>
- <fsummary>Return the size of the driver queue</fsummary>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char*
+ string)</nametext></name>
+ <fsummary>Make an atom from a name.</fsummary>
<desc>
- <marker id="driver_sizeq"></marker>
- <p>This function returns the number of bytes currently in the
- driver queue.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_mk_atom"></marker>
+ <p>Returns an atom given a name
+ <c>string</c>. The atom is created and does not change, so the
+ return value can be saved and reused, which is faster than
+ looking up the atom several times.</p>
+ <p>Notice that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name>
- <fsummary>Enqueue binary in the driver queue</fsummary>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort
+ port)</nametext></name>
+ <fsummary>Make an Erlang term port from a port.</fsummary>
<desc>
- <marker id="driver_enq_bin"></marker>
- <p>This function enqueues a driver binary in the driver
- queue. The data in <c>bin</c> at <c>offset</c> with length
- <c>len</c> is placed at the end of the queue. This function
- is most often faster than <c>driver_enq</c>, because the
- data doesn't have to be copied.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
- <p>The return value is 0.</p>
+ <marker id="driver_mk_port"></marker>
+ <p>Converts a port handle to the Erlang term format, usable in
+ <seealso marker="#erl_drv_output_term">
+ <c>erl_drv_output_term</c></seealso> and
+ <seealso marker="#erl_drv_send_term">
+ <c>erl_drv_send_term</c></seealso>.</p>
+ <p>Notice that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name>
- <fsummary>Push binary at the head of the driver queue</fsummary>
+ <name><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port,
+ ErlDrvTermData process, ErlDrvMonitor *monitor)</nametext></name>
+ <fsummary>Monitor a process from a driver.</fsummary>
<desc>
- <marker id="driver_pushq_bin"></marker>
- <p>This function puts data in the binary <c>bin</c>, at
- <c>offset</c> with length <c>len</c> at the head of the
- driver queue. It is most often faster than
- <c>driver_pushq</c>, because the data doesn't have to be
- copied.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
- <p>The return value is 0.</p>
+ <marker id="driver_monitor_process"></marker>
+ <p>Starts monitoring a process from a driver. When a process is
+ monitored, a process exit results in a call to the provided
+ <seealso marker="driver_entry#process_exit">
+ <c>process_exit</c></seealso> callback
+ in the <seealso marker="driver_entry"><c>ErlDrvEntry</c></seealso>
+ structure. The <c>ErlDrvMonitor</c> structure is filled in, for later
+ removal or compare.</p>
+ <p>Parameter <c>process</c> is to be the return value of an
+ earlier call to <seealso marker="#driver_caller">
+ <c>driver_caller</c></seealso> or
+ <seealso marker="#driver_connected"><c>driver_connected</c></seealso>
+ call.</p>
+ <p>Returns <c>0</c> on success, &lt; 0 if no callback is
+ provided, and &gt; 0 if the process is no longer alive.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvSizeT</ret><nametext>driver_peekqv(ErlDrvPort port, ErlIOVec *ev)</nametext></name>
- <fsummary>Get the driver queue as an IO vector</fsummary>
+ <name><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf,
+ ErlDrvSizeT len)</nametext></name>
+ <fsummary>Send data from driver to port owner.</fsummary>
<desc>
- <marker id="driver_peekqv"></marker>
- <p>
- This function retrieves the driver queue into a supplied
- <c>ErlIOVec</c> <c>ev</c>. It also returns the queue size.
- This is one of two ways to get data out of the queue.
- </p>
- <p>
- If <c>ev</c> is <c>NULL</c> all ones i.e. <c>-1</c> type cast to
- <c>ErlDrvSizeT</c> is returned.
- </p>
- <p>Nothing is removed from the queue by this function, that must be done
- with <c>driver_deq</c>.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_output"></marker>
+ <p>Sends data from the driver up to the emulator. The data is received
+ as terms or binary data, depending on how the driver port was
+ opened.</p>
+ <p>The data is queued in the port owner process' message
+ queue. Notice that this does not yield to the emulator (as
+ the driver and the emulator run in the same thread).</p>
+ <p>Parameter <c>buf</c> points to the data to send, and
+ <c>len</c> is the number of bytes.</p>
+ <p>The return value for all output functions is <c>0</c> for normal use.
+ If the driver is used for distribution, it can fail and return
+ <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name>
- <fsummary>Get the driver queue as a vector</fsummary>
+ <name><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char
+ *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset,
+ ErlDrvSizeT len)</nametext></name>
+ <fsummary>Send data from a driver binary to port owner.</fsummary>
<desc>
- <marker id="driver_peekq"></marker>
- <p>This function retrieves the driver queue as a pointer to an
- array of <c>SysIOVec</c>s. It also returns the number of
- elements in <c>vlen</c>. This is one of two ways to get data
- out of the queue.</p>
- <p>Nothing is removed from the queue by this function, that must be done
- with <c>driver_deq</c>.</p>
- <p>The returned array is suitable to use with the Unix system
- call <c>writev</c>.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_output_binary"></marker>
+ <p>Sends data to a port owner process from a
+ driver binary. It has a header buffer (<c>hbuf</c>
+ and <c>hlen</c>) just like
+ <seealso marker="#driver_output2"><c>driver_output2</c></seealso>.
+ Parameter <c>hbuf</c> can be <c>NULL</c>.</p>
+ <p>Parameter <c>offset</c> is an offset into the binary and
+ <c>len</c> is the number of bytes to send.</p>
+ <p>Driver binaries are created with
+ <seealso marker="#driver_alloc_binary">
+ <c>driver_alloc_binary</c></seealso>.</p>
+ <p>The data in the header is sent as a list and the binary as
+ an Erlang binary in the tail of the list.</p>
+ <p>For example, if <c>hlen</c> is <c>2</c>, the port owner process
+ receives <c><![CDATA[[H1, H2 | <<T>>]]]></c>.</p>
+ <p>The return value is <c>0</c> for normal use.</p>
+ <p>Notice that, using the binary syntax in Erlang, the driver
+ application can match the header directly from the binary,
+ so the header can be put in the binary, and <c>hlen</c> can be set
+ to <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name>
- <fsummary>Enqueue vector in the driver queue</fsummary>
+ <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port,
+ ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data from driver to port owner.</fsummary>
<desc>
- <marker id="driver_enqv"></marker>
- <p>This function enqueues the data in <c>ev</c>, skipping the
- first <c>skip</c> bytes of it, at the end of the driver
- queue. It is faster than <c>driver_enq</c>, because the data
- doesn't have to be copied.</p>
- <p>The return value is 0.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_output_term"></marker>
+ <warning>
+ <p><em>This function is deprecated.</em>
+ Use <seealso marker="#erl_drv_send_term">
+ <c>erl_drv_output_term</c></seealso>instead.</p>
+ </warning>
+ <p>Parameters <c>term</c> and <c>n</c> work as in
+ <seealso marker="#erl_drv_output_term">
+ <c>erl_drv_output_term</c></seealso>.</p>
+ <p>Notice that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name>
- <fsummary>Push vector at the head of the driver queue</fsummary>
+ <name><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf,
+ ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)</nametext></name>
+ <fsummary>Send data and binary data to port owner.</fsummary>
<desc>
- <marker id="driver_pushqv"></marker>
- <p>This function puts the data in <c>ev</c>, skipping the first
- <c>skip</c> bytes of it, at the head of the driver queue.
- It is faster than <c>driver_pushq</c>, because the data
- doesn't have to be copied.</p>
- <p>The return value is 0.</p>
- <p>This function can be called from an arbitrary thread if a
- <seealso marker="#ErlDrvPDL">port data lock</seealso>
- associated with the <c>port</c> is locked by the calling
- thread during the call.</p>
+ <marker id="driver_output2"></marker>
+ <p>First sends <c>hbuf</c>
+ (length in <c>hlen</c>) data as a list, regardless of port
+ settings. Then sends <c>buf</c> as a binary or list.
+ For example, if <c>hlen</c> is <c>3</c>, the port owner process
+ receives <c>[H1, H2, H3 | T]</c>.</p>
+ <p>The point of sending data as a list header, is to facilitate
+ matching on the data received.</p>
+ <p>The return value is <c>0</c> for normal use.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvPDL</ret><nametext>driver_pdl_create(ErlDrvPort port)</nametext></name>
- <fsummary>Create a port data lock</fsummary>
+ <name><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf,
+ ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name>
+ <fsummary>Send vectorized data to port owner.</fsummary>
<desc>
- <marker id="driver_pdl_create"></marker>
- <p>This function creates a port data lock associated with
- the <c>port</c>. <em>NOTE</em>: Once a port data lock has
- been created, it has to be locked during all operations
- on the driver queue of the <c>port</c>.</p>
- <p>On success a newly created port data lock is returned. On
- failure <c>NULL</c> is returned. <c>driver_pdl_create()</c> will
- fail if <c>port</c> is invalid or if a port data lock already has
- been associated with the <c>port</c>.</p>
+ <marker id="driver_outputv"></marker>
+ <p>Sends data from an I/O vector, <c>ev</c>, to
+ the port owner process. It has a header buffer (<c>hbuf</c>
+ and <c>hlen</c>), just like <seealso marker="#driver_output2">
+ <c>driver_output2</c></seealso>.</p>
+ <p>Parameter <c>skip</c> is a number of bytes to skip of
+ the <c>ev</c> vector from the head.</p>
+ <p>You get vectors of <c>ErlIOVec</c> type from the driver
+ queue (see below), and the
+ <seealso marker="driver_entry#outputv"><c>outputv</c></seealso>
+ driver entry function. You can also make them yourself, if you want to
+ send several <c>ErlDrvBinary</c> buffers at once. Often
+ it is faster to use
+ <seealso marker="#driver_output"><c>driver_output</c></seealso> or
+ <seealso marker="#driver_output_binary"></seealso>.</p>
+ <p>For example, if <c>hlen</c> is <c>2</c> and <c>ev</c> points to an
+ array of three binaries, the port owner process receives
+ <c><![CDATA[[H1, H2, <<B1>>, <<B2>> | <<B3>>]]]></c>.</p>
+ <p>The return value is <c>0</c> for normal use.</p>
+ <p>The comment for <c>driver_output_binary</c> also applies for
+ <c>driver_outputv</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>driver_pdl_lock(ErlDrvPDL pdl)</nametext></name>
- <fsummary>Lock port data lock</fsummary>
+ <name><ret>ErlDrvPDL</ret>
+ <nametext>driver_pdl_create(ErlDrvPort port)</nametext></name>
+ <fsummary>Create a port data lock.</fsummary>
<desc>
- <marker id="driver_pdl_lock"></marker>
- <p>This function locks the port data lock passed as argument
- (<c>pdl</c>).</p>
- <p>This function is thread-safe.</p>
+ <marker id="driver_pdl_create"></marker>
+ <p>Creates a port data lock associated with the <c>port</c>.</p>
+ <note>
+ <p>Once a port data lock has been created, it must be locked during
+ all operations on the driver queue of the <c>port</c>.</p>
+ </note>
+ <p>Returns a newly created port data lock on success,
+ otherwise <c>NULL</c>. The function fails
+ if <c>port</c> is invalid or if a port data lock already has
+ been associated with the <c>port</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>driver_pdl_unlock(ErlDrvPDL pdl)</nametext></name>
- <fsummary>Unlock port data lock</fsummary>
+ <name><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL
+ pdl)</nametext></name>
+ <fsummary></fsummary>
<desc>
- <marker id="driver_pdl_unlock"></marker>
- <p>This function unlocks the port data lock passed as argument
- (<c>pdl</c>).</p>
+ <marker id="driver_pdl_dec_refc"></marker>
+ <p>Decrements the reference count of
+ the port data lock passed as argument (<c>pdl</c>).</p>
+ <p>The current reference count after the decrement has
+ been performed is returned.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_pdl_get_refc(ErlDrvPDL pdl)</nametext></name>
+ <name><ret>long</ret>
+ <nametext>driver_pdl_get_refc(ErlDrvPDL pdl)</nametext></name>
<fsummary></fsummary>
<desc>
<marker id="driver_pdl_get_refc"></marker>
- <p>This function returns the current reference count of
+ <p>Returns the current reference count of
the port data lock passed as argument (<c>pdl</c>).</p>
<p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_pdl_inc_refc(ErlDrvPDL pdl)</nametext></name>
+ <name><ret>long</ret>
+ <nametext>driver_pdl_inc_refc(ErlDrvPDL pdl)</nametext></name>
<fsummary></fsummary>
<desc>
<marker id="driver_pdl_inc_refc"></marker>
- <p>This function increments the reference count of
+ <p>Increments the reference count of
the port data lock passed as argument (<c>pdl</c>).</p>
<p>The current reference count after the increment has
been performed is returned.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL pdl)</nametext></name>
- <fsummary></fsummary>
+ <name><ret>void</ret>
+ <nametext>driver_pdl_lock(ErlDrvPDL pdl)</nametext></name>
+ <fsummary>Lock port data lock.</fsummary>
<desc>
- <marker id="driver_pdl_dec_refc"></marker>
- <p>This function decrements the reference count of
- the port data lock passed as argument (<c>pdl</c>).</p>
- <p>The current reference count after the decrement has
- been performed is returned.</p>
+ <marker id="driver_pdl_lock"></marker>
+ <p>Locks the port data lock passed as argument (<c>pdl</c>).</p>
<p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port, ErlDrvTermData process, ErlDrvMonitor *monitor)</nametext></name>
- <fsummary>Monitor a process from a driver</fsummary>
+ <name><ret>void</ret>
+ <nametext>driver_pdl_unlock(ErlDrvPDL pdl)</nametext></name>
+ <fsummary>Unlock port data lock.</fsummary>
<desc>
- <marker id="driver_monitor_process"></marker>
- <p>Start monitoring a process from a driver. When a process is
- monitored, a process exit will result in a call to the
- provided <seealso marker="driver_entry#process_exit">process_exit</seealso> call-back
- in the <seealso marker="driver_entry">ErlDrvEntry</seealso>
- structure. The <c>ErlDrvMonitor</c> structure is filled in, for later
- removal or compare.</p>
- <p>The <c>process</c> parameter should be the return value of an
- earlier call to <seealso marker="#driver_caller">driver_caller</seealso> or <seealso marker="#driver_connected">driver_connected</seealso> call.</p>
- <p>The function returns 0 on success, &lt; 0 if no call-back is
- provided and &gt; 0 if the process is no longer alive.</p>
+ <marker id="driver_pdl_unlock"></marker>
+ <p>Unlocks the port data lock passed as argument (<c>pdl</c>).</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name>
- <fsummary>Stop monitoring a process from a driver</fsummary>
+ <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int
+ *vlen)</nametext></name>
+ <fsummary>Get the driver queue as a vector.</fsummary>
<desc>
- <marker id="driver_demonitor_process"></marker>
- <p>This function cancels a monitor created earlier. </p>
- <p>The function returns 0 if a monitor was removed and &gt; 0
- if the monitor did no longer exist.</p>
+ <marker id="driver_peekq"></marker>
+ <p>Retrieves the driver queue as a pointer to an
+ array of <c>SysIOVec</c>s. It also returns the number of
+ elements in <c>vlen</c>. This is one of two ways to get data
+ out of the queue.</p>
+ <p>Nothing is removed from the queue by this function, that must be done
+ with <seealso marker="#driver_deq"><c>driver_deq</c></seealso>.</p>
+ <p>The returned array is suitable to use with the Unix system
+ call <c>writev</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvTermData</ret><nametext>driver_get_monitored_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name>
- <fsummary>Retrieve the process id from a monitor</fsummary>
+ <name><ret>ErlDrvSizeT</ret><nametext>driver_peekqv(ErlDrvPort port,
+ ErlIOVec *ev)</nametext></name>
+ <fsummary>Get the driver queue as an I/O vector.</fsummary>
<desc>
- <marker id="driver_get_monitored_process"></marker>
- <p>The function returns the process id associated with a living
- monitor. It can be used in the <c>process_exit</c> call-back to
- get the process identification for the exiting process.</p>
- <p>The function returns <c>driver_term_nil</c> if the monitor
- no longer exists.</p>
+ <marker id="driver_peekqv"></marker>
+ <p>Retrieves the driver queue into a supplied
+ <c>ErlIOVec</c> <c>ev</c>. It also returns the queue size.
+ This is one of two ways to get data out of the queue.</p>
+ <p>If <c>ev</c> is <c>NULL</c>, all ones that is <c>-1</c> type cast to
+ <c>ErlDrvSizeT</c> are returned.</p>
+ <p>Nothing is removed from the queue by this function, that must be done
+ with <seealso marker="#driver_deq"><c>driver_deq</c></seealso>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor *monitor1, const ErlDrvMonitor *monitor2)</nametext></name>
- <fsummary>Compare two monitors</fsummary>
+ <name><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf,
+ ErlDrvSizeT len)</nametext></name>
+ <fsummary>Push data at the head of the driver queue.</fsummary>
<desc>
- <marker id="driver_compare_monitors"></marker>
- <p>This function is used to compare two <c>ErlDrvMonitor</c>s. It
- can also be used to imply some artificial order on monitors,
- for whatever reason.</p>
- <p>The function returns 0 if <c>monitor1</c> and
- <c>monitor2</c> are equal, &lt; 0 if <c>monitor1</c> is less
- than <c>monitor2</c> and &gt; 0 if <c>monitor1</c> is greater
- than <c>monitor2</c>.</p>
+ <marker id="driver_pushq"></marker>
+ <p>Puts data at the head of the driver queue. The
+ data in <c>buf</c> is copied (<c>len</c> bytes) and placed
+ at the beginning of the queue.</p>
+ <p>The return value is <c>0</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry *de)</nametext></name>
- <fsummary>Add a driver entry</fsummary>
+ <name><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port,
+ ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext>
+ </name>
+ <fsummary>Push binary at the head of the driver queue.</fsummary>
<desc>
- <marker id="add_driver_entry"></marker>
- <p>This function adds a driver entry to the list of drivers
- known by Erlang. The <seealso marker="driver_entry#init">init</seealso> function of the <c>de</c>
- parameter is called.</p>
- <note>
- <p>To use this function for adding drivers residing in
- dynamically loaded code is dangerous. If the driver code
- for the added driver resides in the same dynamically
- loaded module (i.e. <c>.so</c> file) as a normal
- dynamically loaded driver (loaded with the <c>erl_ddll</c>
- interface), the caller should call <seealso marker="#driver_lock_driver">driver_lock_driver</seealso> before
- adding driver entries.</p>
- <p>Use of this function is generally deprecated.</p>
- </note>
+ <marker id="driver_pushq_bin"></marker>
+ <p>Puts data in the binary <c>bin</c>, at
+ <c>offset</c> with length <c>len</c> at the head of the
+ driver queue. It is most often faster than
+ <seealso marker="#driver_pushq"><c>driver_pushq</c></seealso>,
+ because no data must be copied.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry *de)</nametext></name>
- <fsummary>Remove a driver entry</fsummary>
+ <name><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec
+ *ev, ErlDrvSizeT skip)</nametext></name>
+ <fsummary>Push vector at the head of the driver queue.</fsummary>
<desc>
- <marker id="remove_driver_entry"></marker>
- <p>This function removes a driver entry <c>de</c> previously
- added with <c>add_driver_entry</c>.</p>
- <p>Driver entries added by the <c>erl_ddll</c> erlang interface can
- not be removed by using this interface.</p>
+ <marker id="driver_pushqv"></marker>
+ <p>Puts the data in <c>ev</c>, skipping the first
+ <c>skip</c> bytes of it, at the head of the driver queue.
+ It is faster than
+ <seealso marker="#driver_pushq"><c>driver_pushq</c></seealso>,
+ because no data must be copied.</p>
+ <p>The return value is <c>0</c>.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name>
- <fsummary>Get erlang error atom name from error number</fsummary>
+ <name><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned
+ long *time_left)</nametext></name>
+ <fsummary>Read the time left before time-out.</fsummary>
<desc>
- <marker id="erl_errno_id"></marker>
- <p>This function returns the atom name of the erlang error,
- given the error number in <c>error</c>. Error atoms are:
- <c>einval</c>, <c>enoent</c>, etc. It can be used to make
- error terms from the driver.</p>
+ <marker id="driver_read_timer"></marker>
+ <p>Reads the current time of a timer, and places
+ the result in <c>time_left</c>. This is the time in
+ milliseconds, before the time-out occurs.</p>
+ <p>The return value is <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port, ErlDrvSizeT *low, ErlDrvSizeT *high)</nametext></name>
- <fsummary>Set and get limits for busy port message queue</fsummary>
- <desc>
- <marker id="erl_drv_busy_msgq_limits"></marker>
- <p>Sets and gets limits that will be used for controling the
- busy state of the port message queue.</p>
- <p>The port message queue will be set into a busy
- state when the amount of command data queued on the
- message queue reaches the <c>high</c> limit. The port
- message queue will be set into a not busy state when the
- amount of command data queued on the message queue falls
- below the <c>low</c> limit. Command data is in this
- context data passed to the port using either
- <c>Port ! {Owner, {command, Data}}</c>, or
- <c>port_command/[2,3]</c>. Note that these limits
- only concerns command data that have not yet reached the
- port. The <seealso marker="#set_busy_port">busy port</seealso>
- feature can be used for data that has reached the port.</p>
-
- <p>Valid limits are values in the range
- <c>[ERL_DRV_BUSY_MSGQ_LIM_MIN, ERL_DRV_BUSY_MSGQ_LIM_MAX]</c>.
- Limits will be automatically adjusted to be sane. That is,
- the system will adjust values so that the low limit used is
- lower than or equal to the high limit used. By default the high
- limit will be 8 kB and the low limit will be 4 kB.</p>
-
- <p>By passing a pointer to an integer variable containing
- the value <c>ERL_DRV_BUSY_MSGQ_READ_ONLY</c>, currently used
- limit will be read and written back to the integer variable.
- A new limit can be set by passing a pointer to an integer
- variable containing a valid limit. The passed value will be
- written to the internal limit. The internal limit will then
- be adjusted. After this the adjusted limit will be written
- back to the integer variable from which the new value was
- read. Values are in bytes.</p>
-
- <p>The busy message queue feature can be disabled either
- by setting the <c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c>
- <seealso marker="driver_entry#driver_flags">driver flag</seealso>
- in the <seealso marker="driver_entry">driver_entry</seealso>
- used by the driver, or by calling this function with
- <c>ERL_DRV_BUSY_MSGQ_DISABLED</c> as a limit (either low or
- high). When this feature has been disabled it cannot be
- enabled again. When reading the limits both of them
- will be <c>ERL_DRV_BUSY_MSGQ_DISABLED</c>, if this
- feature has been disabled.</p>
-
- <p>Processes sending command data to the port will be suspended
- if either the port is busy or if the port message queue is
- busy. Suspended processes will be resumed when neither the
- port is busy, nor the port message queue is busy.</p>
-
- <p>For information about busy port functionality
- see the documentation of the
- <seealso marker="#set_busy_port">set_busy_port()</seealso>
- function.</p>
- </desc>
- </func>
- <func>
- <name><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int on)</nametext></name>
- <fsummary>Signal or unsignal port as busy</fsummary>
- <desc>
- <marker id="set_busy_port"></marker>
- <p>This function set and unset the busy state of the port. If
- <c>on</c> is non-zero, the port is set to busy, if it's zero the port
- is set to not busy. You typically want to combine
- this feature with the <seealso marker="#erl_drv_busy_msgq_limits">busy
- port message queue</seealso> functionality.</p>
- <p>Processes sending command data to the port will be suspended
- if either the port is busy or if the port message queue
- is busy. Suspended processes will be resumed when neither the
- port is busy, nor the port message queue is busy. Command data
- is in this context data passed to the port using either
- <c>Port ! {Owner, {command, Data}}</c>, or
- <c>port_command/[2,3]</c>.</p>
- <p>If the
- <seealso marker="driver_entry#driver_flags"><![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso>
- has been set in the
- <seealso marker="driver_entry">driver_entry</seealso>,
- data can be forced into the driver via
- <seealso marker="erlang#port_command/3">port_command(Port, Data, [force])</seealso>
- even though the driver has signaled that it is busy.
- </p>
- <p>For information about busy port message queue functionality
- see the documentation of the
- <seealso marker="#erl_drv_busy_msgq_limits">erl_drv_busy_msgq_limits()</seealso>
- function.</p>
- </desc>
- </func>
- <func>
- <name><ret>void</ret><nametext>set_port_control_flags(ErlDrvPort port, int flags)</nametext></name>
- <fsummary>Set flags on how to handle control entry function</fsummary>
+ <name><ret>void *</ret>
+ <nametext>driver_realloc(void *ptr, ErlDrvSizeT size)</nametext></name>
+ <fsummary>Resize an allocated memory block.</fsummary>
<desc>
- <marker id="set_port_control_flags"></marker>
- <p>This function sets flags for how the <seealso marker="driver_entry#control">control</seealso> driver entry
- function will return data to the port owner process. (The
- <c>control</c> function is called from <c>port_control/3</c>
- in erlang.)</p>
- <p>Currently there are only two meaningful values for
- <c>flags</c>: 0 means that data is returned in a list, and
- <c>PORT_CONTROL_FLAG_BINARY</c> means data is returned as
- a binary from <c>control</c>.</p>
+ <marker id="driver_realloc"></marker>
+ <p>Resizes a memory block, either in place, or by
+ allocating a new block, copying the data, and freeing the old
+ block. A pointer is returned to the reallocated memory. On
+ failure (out of memory), <c>NULL</c> is returned. (This is
+ most often a wrapper for <c>realloc</c>.)</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort port)</nametext></name>
- <fsummary>Fail with EOF</fsummary>
+ <name><ret>ErlDrvBinary *</ret>
+ <nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)
+ </nametext></name>
+ <fsummary>Resize a driver binary.</fsummary>
<desc>
- <marker id="driver_failure_eof"></marker>
- <p>This function signals to erlang that the driver has
- encountered an EOF and should be closed, unless the port was
- opened with the <c>eof</c> option, in that case eof is sent
- to the port. Otherwise, the port is closed and an
- <c>'EXIT'</c> message is sent to the port owner process.</p>
- <p>The return value is 0.</p>
+ <marker id="driver_realloc_binary"></marker>
+ <p>Resizes a driver binary, while keeping the data.</p>
+ <p>Returns the resized driver binary on success. Returns <c>NULL</c>
+ on failure (out of memory).</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char *string)</nametext></name>
- <name><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int error)</nametext></name>
- <name><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int error)</nametext></name>
- <fsummary>Fail with error</fsummary>
+ <name><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent
+ event, int mode, int on)</nametext></name>
+ <fsummary>Provides an event for having the emulator call the driver.
+ </fsummary>
<desc>
- <marker id="driver_failure_atom"></marker>
- <marker id="driver_failure_posix"></marker>
- <marker id="driver_failure"></marker>
- <p>These functions signal to Erlang that the driver has
- encountered an error and should be closed. The port is
- closed and the tuple <c>{'EXIT', error, Err}</c>, is sent to
- the port owner process, where error is an error atom
- (<c>driver_failure_atom</c> and
- <c>driver_failure_posix</c>), or an integer
- (<c>driver_failure</c>).</p>
- <p>The driver should fail only when in severe error situations,
- when the driver cannot possibly keep open, for instance
- buffer allocation gets out of memory. For normal errors
- it is more appropriate to send error codes with
- <c>driver_output</c>.</p>
- <p>The return value is 0.</p>
+ <marker id="driver_select"></marker>
+ <p>This function is used by drivers to provide the emulator with
+ events to check for. This enables the emulator to call the driver
+ when something has occurred asynchronously.</p>
+ <p>Parameter <c>event</c> identifies an OS-specific event object.
+ On Unix systems, the functions <c>select</c>/<c>poll</c> are used.
+ The event object must be a socket or pipe (or other object that
+ <c>select</c>/<c>poll</c> can use).
+ On Windows, the Win32 API function <c>WaitForMultipleObjects</c>
+ is used. This places other restrictions on the event object;
+ see the Win32 SDK documentation.</p>
+ <p>Parameter <c>on</c> is to be <c>1</c> for setting events
+ and <c>0</c> for clearing them.</p>
+ <p>Parameter <c>mode</c> is a bitwise OR combination of
+ <c>ERL_DRV_READ</c>, <c>ERL_DRV_WRITE</c>, and <c>ERL_DRV_USE</c>.
+ The first two specify whether to wait for read events and/or write
+ events. A fired read event calls
+ <seealso marker="driver_entry#ready_input">
+ <c>ready_input</c></seealso> and a fired write event calls
+ <seealso marker="driver_entry#ready_output">
+ <c>ready_output</c></seealso>.</p>
+ <note>
+ <p>Some OS (Windows) do not differentiate between read and write
+ events. The callback for a fired event then only depends on the
+ value of <c>mode</c>.</p>
+ </note>
+ <p><c>ERL_DRV_USE</c> specifies if we are using the event object or
+ if we want to close it.
+ On an emulator with SMP support, it is not safe to clear all events
+ and then close the event object after <c>driver_select</c> has
+ returned. Another thread can still be using the event object
+ internally. To safely close an event object, call
+ <c>driver_select</c> with <c>ERL_DRV_USE</c> and <c>on==0</c>, which
+ clears all events and then either calls
+ <seealso marker="driver_entry#stop_select"><c>stop_select</c></seealso>
+ or schedules it to be called when it is safe to close the event
+ object. <c>ERL_DRV_USE</c> is to be set together with the first event
+ for an event object. It is harmless to set <c>ERL_DRV_USE</c>
+ even if it already has been done. Clearing all events but keeping
+ <c>ERL_DRV_USE</c> set indicates that we are using the event
+ object and probably will set events for it again.</p>
+ <note>
+ <p><c>ERL_DRV_USE</c> was added in Erlang/OTP R13. Old drivers still
+ work as before, but it is recommended to update them to use
+ <c>ERL_DRV_USE</c> and <c>stop_select</c> to ensure that event
+ objects are closed in a safe way.</p>
+ </note>
+ <p>The return value is <c>0</c>, unless
+ <c>ready_input</c>/<c>ready_output</c> is <c>NULL</c>, in which case
+ it is <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort port)</nametext></name>
- <fsummary>Return the port owner process</fsummary>
+ <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port,
+ ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data to other process than port owner process.
+ </fsummary>
<desc>
- <marker id="driver_connected"></marker>
- <p>This function returns the port owner process.</p>
- <p>Note that this function is <em>not</em> thread-safe, not
- even when the emulator with SMP support is used.</p>
+ <marker id="driver_send_term"></marker>
+ <warning>
+ <p><em>This function is deprecated.</em>
+ Use <seealso marker="#erl_drv_send_term">
+ <c>erl_drv_send_term</c></seealso> instead.</p>
+ </warning>
+ <note>
+ <p>The parameters of this function
+ cannot be properly checked by the runtime system when
+ executed by arbitrary threads. This can cause the
+ function not to fail when it should.</p>
+ </note>
+ <p>Parameters <c>term</c> and <c>n</c> work as in
+ <seealso marker="#erl_drv_output_term">
+ <c>erl_drv_output_term</c></seealso>.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort port)</nametext></name>
- <fsummary>Return the process making the driver call</fsummary>
+ <name><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned
+ long time)</nametext></name>
+ <fsummary>Set a timer to call the driver.</fsummary>
<desc>
- <marker id="driver_caller"></marker>
- <p>This function returns the process id of the process that
- made the current call to the driver. The process id can be
- used with <c>driver_send_term</c> to send back data to the
- caller. <c>driver_caller()</c> only returns valid data
- when currently executing in one of the following driver
- callbacks:</p>
- <taglist>
- <tag><seealso marker="driver_entry#start">start</seealso></tag>
- <item>Called from <c>open_port/2</c>.</item>
- <tag><seealso marker="driver_entry#output">output</seealso></tag>
- <item>Called from <c>erlang:send/2</c>, and
- <c>erlang:port_command/2</c></item>
- <tag><seealso marker="driver_entry#outputv">outputv</seealso></tag>
- <item>Called from <c>erlang:send/2</c>, and
- <c>erlang:port_command/2</c></item>
- <tag><seealso marker="driver_entry#control">control</seealso></tag>
- <item>Called from <c>erlang:port_control/3</c></item>
- <tag><seealso marker="driver_entry#call">call</seealso></tag>
- <item>Called from <c>erlang:port_call/3</c></item>
- </taglist>
- <p>Note that this function is <em>not</em> thread-safe, not
- even when the emulator with SMP support is used.</p>
+ <marker id="driver_set_timer"></marker>
+ <p>Sets a timer on the driver, which will count
+ down and call the driver when it is timed out. Parameter
+ <c>time</c> is the time in milliseconds before the timer expires.</p>
+ <p>When the timer reaches <c>0</c> and expires, the driver entry
+ function <seealso marker="driver_entry#timeout">
+ <c>timeout</c></seealso> is called.</p>
+ <p>Notice that only one timer exists on each driver instance;
+ setting a new timer replaces an older one.</p>
+ <p>Return value is <c>0</c>, unless the <c>timeout</c>
+ driver function is <c>NULL</c>, in which case it is <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port, ErlDrvTermData* term, int n)</nametext></name>
- <fsummary>Send term data from driver to port owner</fsummary>
+ <name><ret>ErlDrvSizeT</ret>
+ <nametext>driver_sizeq(ErlDrvPort port)</nametext></name>
+ <fsummary>Return the size of the driver queue.</fsummary>
<desc>
- <marker id="erl_drv_output_term"></marker>
- <p>This functions sends data in the special driver term
- format to the port owner process. This is a fast way to
- deliver term data from a driver. It also needs no binary
- conversion, so the port owner process receives data as
- normal Erlang terms. The
- <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso>
- functions can be used for sending to any arbitrary process
- on the local node.</p>
- <note><p>Note that the <c>port</c> parameter is <em>not</em>
- an ordinary port handle, but a port handle converted using
- <c>driver_mk_port()</c>.</p></note>
- <p>The <c>term</c> parameter points to an array of
- <c>ErlDrvTermData</c>, with <c>n</c> elements. This array
- contains terms described in the driver term format. Every
- term consists of one to four elements in the array. The
- term first has a term type, and then arguments. The
- <c>port</c> parameter specifies the sending port.</p>
- <p>Tuples, maps and lists (with the exception of strings, see below),
- are built in reverse polish notation, so that to build a
- tuple, the elements are given first, and then the tuple
- term, with a count. Likewise for lists and maps.</p>
- <p>A tuple must be specified with the number of elements. (The
- elements precede the <c>ERL_DRV_TUPLE</c> term.)</p>
- <p>A list must be specified with the number of elements,
- including the tail, which is the last term preceding
- <c>ERL_DRV_LIST</c>.</p>
- <p>A map must be specified with the number of key-value pairs <c>N</c>.
- The key-value pairs must precede the <c>ERL_DRV_MAP</c> in this order:
- <c>key1,value1,key2,value2,...,keyN,valueN</c>.
- Duplicate keys are not allowed.</p>
- <p>The special term <c>ERL_DRV_STRING_CONS</c> is used to
- "splice" in a string in a list, a string given this way is
- not a list per se, but the elements are elements of the
- surrounding list.</p>
- <pre>
-Term type Argument(s)
-===========================================
-ERL_DRV_NIL
-ERL_DRV_ATOM ErlDrvTermData atom (from driver_mk_atom(char *string))
-ERL_DRV_INT ErlDrvSInt integer
-ERL_DRV_UINT ErlDrvUInt integer
-ERL_DRV_INT64 ErlDrvSInt64 *integer_ptr
-ERL_DRV_UINT64 ErlDrvUInt64 *integer_ptr
-ERL_DRV_PORT ErlDrvTermData port (from driver_mk_port(ErlDrvPort port))
-ERL_DRV_BINARY ErlDrvBinary *bin, ErlDrvUInt len, ErlDrvUInt offset
-ERL_DRV_BUF2BINARY char *buf, ErlDrvUInt len
-ERL_DRV_STRING char *str, int len
-ERL_DRV_TUPLE int sz
-ERL_DRV_LIST int sz
-ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port) or driver_caller(ErlDrvPort port))
-ERL_DRV_STRING_CONS char *str, int len
-ERL_DRV_FLOAT double *dbl
-ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
-ERL_DRV_MAP int sz
- </pre>
- <p>The unsigned integer data type <c>ErlDrvUInt</c> and the
- signed integer data type <c>ErlDrvSInt</c> are 64 bits wide
- on a 64 bit runtime system and 32 bits wide on a 32 bit
- runtime system. They were introduced in erts version 5.6,
- and replaced some of the <c>int</c> arguments in the list above.
- </p>
- <p>The unsigned integer data type <c>ErlDrvUInt64</c> and the
- signed integer data type <c>ErlDrvSInt64</c> are always 64 bits
- wide. They were introduced in erts version 5.7.4.
- </p>
-
- <p>To build the tuple <c>{tcp, Port, [100 | Binary]}</c>, the
- following call could be made.</p>
- <code type="none"><![CDATA[
- ErlDrvBinary* bin = ...
- ErlDrvPort port = ...
- ErlDrvTermData spec[] = {
- ERL_DRV_ATOM, driver_mk_atom("tcp"),
- ERL_DRV_PORT, driver_mk_port(drvport),
- ERL_DRV_INT, 100,
- ERL_DRV_BINARY, bin, 50, 0,
- ERL_DRV_LIST, 2,
- ERL_DRV_TUPLE, 3,
- };
- erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
- ]]>
- </code>
- <p>Where <c>bin</c> is a driver binary of length at least 50
- and <c>drvport</c> is a port handle. Note that the <c>ERL_DRV_LIST</c>
- comes after the elements of the list, likewise the
- <c>ERL_DRV_TUPLE</c>.</p>
- <p>The term <c>ERL_DRV_STRING_CONS</c> is a way to construct
- strings. It works differently from how <c>ERL_DRV_STRING</c>
- works. <c>ERL_DRV_STRING_CONS</c> builds a string list in
- reverse order, (as opposed to how <c>ERL_DRV_LIST</c>
- works), concatenating the strings added to a list. The tail
- must be given before <c>ERL_DRV_STRING_CONS</c>.</p>
- <p>The <c>ERL_DRV_STRING</c> constructs a string, and ends
- it. (So it's the same as <c>ERL_DRV_NIL</c> followed by
- <c>ERL_DRV_STRING_CONS</c>.)</p>
- <code type="none"><![CDATA[
- /* to send [x, "abc", y] to the port: */
- ErlDrvTermData spec[] = {
- ERL_DRV_ATOM, driver_mk_atom("x"),
- ERL_DRV_STRING, (ErlDrvTermData)"abc", 3,
- ERL_DRV_ATOM, driver_mk_atom("y"),
- ERL_DRV_NIL,
- ERL_DRV_LIST, 4
- };
- erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
- ]]></code>
- <p></p>
- <code type="none"><![CDATA[
- /* to send "abc123" to the port: */
- ErlDrvTermData spec[] = {
- ERL_DRV_NIL, /* with STRING_CONS, the tail comes first */
- ERL_DRV_STRING_CONS, (ErlDrvTermData)"123", 3,
- ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3,
- };
- erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
- ]]></code>
- <p>The <c>ERL_DRV_EXT2TERM</c> term type is used for passing a
- term encoded with the
- <seealso marker="erl_ext_dist">external format</seealso>,
- i.e., a term that has been encoded by
- <seealso marker="erlang#term_to_binary/2">erlang:term_to_binary</seealso>,
- <seealso marker="erl_interface:ei">erl_interface</seealso>, etc.
- For example, if <c>binp</c> is a pointer to an <c>ErlDrvBinary</c>
- that contains the term <c>{17, 4711}</c> encoded with the
- <seealso marker="erl_ext_dist">external format</seealso>
- and you want to wrap it in a two tuple with the tag <c>my_tag</c>,
- i.e., <c>{my_tag, {17, 4711}}</c>, you can do as follows:
- </p>
- <code type="none"><![CDATA[
- ErlDrvTermData spec[] = {
- ERL_DRV_ATOM, driver_mk_atom("my_tag"),
- ERL_DRV_EXT2TERM, (ErlDrvTermData) binp->orig_bytes, binp->orig_size
- ERL_DRV_TUPLE, 2,
- };
- erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
- ]]></code>
-
- <p>To build the map <c>#{key1 => 100, key2 => {200, 300}}</c>, the
- following call could be made.</p>
- <code type="none"><![CDATA[
- ErlDrvPort port = ...
- ErlDrvTermData spec[] = {
- ERL_DRV_ATOM, driver_mk_atom("key1"),
- ERL_DRV_INT, 100,
- ERL_DRV_ATOM, driver_mk_atom("key2"),
- ERL_DRV_INT, 200,
- ERL_DRV_INT, 300,
- ERL_DRV_TUPLE, 2,
- ERL_DRV_MAP, 2
- };
- erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
- ]]>
- </code>
-
- <p>If you want to pass a binary and don't already have the content
- of the binary in an <c>ErlDrvBinary</c>, you can benefit from using
- <c>ERL_DRV_BUF2BINARY</c> instead of creating an <c>ErlDrvBinary</c>
- via <c>driver_alloc_binary()</c> and then pass the binary via
- <c>ERL_DRV_BINARY</c>. The runtime system will often allocate
- binaries smarter if <c>ERL_DRV_BUF2BINARY</c> is used.
- However, if the content of the binary to pass already resides in
- an <c>ErlDrvBinary</c>, it is normally better to pass the binary
- using <c>ERL_DRV_BINARY</c> and the <c>ErlDrvBinary</c> in question.
- </p>
- <p>The <c>ERL_DRV_UINT</c>, <c>ERL_DRV_BUF2BINARY</c>, and
- <c>ERL_DRV_EXT2TERM</c> term types were introduced in the 5.6
- version of erts.
- </p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="driver_sizeq"></marker>
+ <p>Returns the number of bytes currently in the driver queue.</p>
+ <p>This function can be called from any thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port, ErlDrvTermData* term, int n)</nametext></name>
- <fsummary>Send term data from driver to port owner</fsummary>
+ <name><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo
+ *sys_info_ptr, size_t size)</nametext></name>
+ <fsummary>Get information about the Erlang runtime system.</fsummary>
<desc>
- <marker id="driver_output_term"></marker>
- <warning><p><c>driver_output_term()</c> is deprecated and will
- be removed in the OTP-R17 release. Use
- <seealso marker="#erl_drv_send_term">erl_drv_output_term()</seealso>
- instead.</p>
- </warning>
- <p>The parameters <c>term</c> and <c>n</c> do the same thing
- as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p>
- <p>Note that this function is <em>not</em> thread-safe, not
- even when the emulator with SMP support is used.</p>
+ <marker id="driver_system_info"></marker>
+ <p>Writes information about the Erlang runtime system into the
+ <seealso marker="#ErlDrvSysInfo"><c>ErlDrvSysInfo</c></seealso>
+ structure referred to by the first argument. The second
+ argument is to be the size of the
+ <seealso marker="#ErlDrvSysInfo"><c>ErlDrvSysInfo</c></seealso>
+ structure, that is, <c>sizeof(ErlDrvSysInfo)</c>.</p>
+ <p>For information about specific fields, see
+ <seealso marker="#ErlDrvSysInfo"><c>ErlDrvSysInfo</c></seealso>.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char* string)</nametext></name>
- <fsummary>Make an atom from a name</fsummary>
+ <name><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev,
+ char *buf, ErlDrvSizeT len)</nametext></name>
+ <fsummary>Collect data segments into a buffer.</fsummary>
<desc>
- <marker id="driver_mk_atom"></marker>
- <p>This function returns an atom given a name
- <c>string</c>. The atom is created and won't change, so the
- return value may be saved and reused, which is faster than
- looking up the atom several times.</p>
- <p>Note that this function is <em>not</em> thread-safe, not
- even when the emulator with SMP support is used.</p>
+ <marker id="driver_vec_to_buf"></marker>
+ <p>Collects several segments of data, referenced
+ by <c>ev</c>, by copying them in order to the buffer
+ <c>buf</c>, of the size <c>len</c>.</p>
+ <p>If the data is to be sent from the driver to the port owner
+ process, it is faster to use
+ <seealso marker="#driver_outputv"><c>driver_outputv</c></seealso>.</p>
+ <p>The return value is the space left in the buffer, that is, if
+ <c>ev</c> contains less than <c>len</c> bytes it is the
+ difference, and if <c>ev</c> contains <c>len</c> bytes or more,
+ it is <c>0</c>. This is faster if there is more than one header byte,
+ as the binary syntax can construct integers directly from
+ the binary.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort port)</nametext></name>
- <fsummary>Make a erlang term port from a port</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port,
+ ErlDrvSizeT *low, ErlDrvSizeT *high)</nametext></name>
+ <fsummary>Set and get limits for busy port message queue.</fsummary>
<desc>
- <marker id="driver_mk_port"></marker>
- <p>This function converts a port handle to the erlang term
- format, usable in the <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>, and <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso> functions.</p>
- <p>Note that this function is <em>not</em> thread-safe, not
- even when the emulator with SMP support is used.</p>
+ <marker id="erl_drv_busy_msgq_limits"></marker>
+ <p>Sets and gets limits that will be used for controlling the
+ busy state of the port message queue.</p>
+ <p>The port message queue is set into a busy
+ state when the amount of command data queued on the
+ message queue reaches the <c>high</c> limit. The port
+ message queue is set into a not busy state when the
+ amount of command data queued on the message queue falls
+ below the <c>low</c> limit. Command data is in this
+ context data passed to the port using either
+ <c>Port ! {Owner, {command, Data}}</c> or
+ <c>port_command/[2,3]</c>. Notice that these limits
+ only concerns command data that have not yet reached the
+ port. The <seealso marker="#set_busy_port">busy port</seealso>
+ feature can be used for data that has reached the port.</p>
+ <p>Valid limits are values in the range
+ <c>[ERL_DRV_BUSY_MSGQ_LIM_MIN, ERL_DRV_BUSY_MSGQ_LIM_MAX]</c>.
+ Limits are automatically adjusted to be sane. That is,
+ the system adjusts values so that the low limit used is
+ lower than or equal to the high limit used. By default the high
+ limit is 8 kB and the low limit is 4 kB.</p>
+ <p>By passing a pointer to an integer variable containing
+ the value <c>ERL_DRV_BUSY_MSGQ_READ_ONLY</c>, the currently used
+ limit is read and written back to the integer variable.
+ A new limit can be set by passing a pointer to an integer
+ variable containing a valid limit. The passed value is
+ written to the internal limit. The internal limit is then
+ adjusted. After this the adjusted limit is written
+ back to the integer variable from which the new value was
+ read. Values are in bytes.</p>
+ <p>The busy message queue feature can be disabled either
+ by setting the <c>ERL_DRV_FLAG_NO_BUSY_MSGQ</c>
+ <seealso marker="driver_entry#driver_flags">driver flag</seealso>
+ in the <seealso marker="driver_entry"><c>driver_entry</c></seealso>
+ used by the driver, or by calling this function with
+ <c>ERL_DRV_BUSY_MSGQ_DISABLED</c> as a limit (either low or
+ high). When this feature has been disabled, it cannot be
+ enabled again. When reading the limits, both are
+ <c>ERL_DRV_BUSY_MSGQ_DISABLED</c> if this
+ feature has been disabled.</p>
+ <p>Processes sending command data to the port are suspended
+ if either the port is busy or if the port message queue is
+ busy. Suspended processes are resumed when neither the
+ port or the port message queue is busy.</p>
+ <p>For information about busy port functionality, see
+ <seealso marker="#set_busy_port"><c>set_busy_port</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond
+ *cnd)</nametext></name>
+ <fsummary>Broadcast on a condition variable.</fsummary>
+ <desc>
+ <marker id="erl_drv_cond_broadcast"></marker>
+ <p>Broadcasts on a condition variable. That is, if
+ other threads are waiting on the condition variable being
+ broadcast on, <em>all</em> of them are woken.</p>
+ <p><c>cnd</c> is a pointer to a condition variable to broadcast on.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
- <fsummary>Send term data to other process than port owner process</fsummary>
+ <name><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char
+ *name)</nametext></name>
+ <fsummary>Create a condition variable.</fsummary>
<desc>
- <marker id="erl_drv_send_term"></marker>
- <p>This function is the only way for a driver to send data to
- <em>other</em> processes than the port owner process. The
- <c>receiver</c> parameter specifies the process to receive
- the data.</p>
- <note><p>Note that the <c>port</c> parameter is <em>not</em>
- an ordinary port handle, but a port handle converted using
- <c>driver_mk_port()</c>.</p></note>
- <p>The parameters <c>port</c>, <c>term</c> and <c>n</c> do the same thing
- as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="erl_drv_cond_create"></marker>
+ <p>Creates a condition variable and returns a pointer to it.</p>
+ <p><c>name</c> is a string identifying the created condition variable.
+ It is used to identify the condition variable in planned
+ future debug functionality.</p>
+ <p>Returns <c>NULL</c> on failure. The driver
+ creating the condition variable is responsible for
+ destroying it before the driver is unloaded.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
- <fsummary>Send term data to other process than port owner process</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond
+ *cnd)</nametext></name>
+ <fsummary>Destroy a condition variable.</fsummary>
<desc>
- <marker id="driver_send_term"></marker>
- <warning><p><c>driver_send_term()</c> is deprecated and will
- be removed in the OTP-R17 release. Use
- <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso>
- instead.</p>
- <p>Also note that parameters of <c>driver_send_term()</c>
- cannot be properly checked by the runtime system when
- executed by arbitrary threads. This may cause the
- <c>driver_send_term()</c> function not to fail when
- it should.</p>
- </warning>
- <p>The parameters <c>term</c> and <c>n</c> do the same thing
- as in <seealso marker="#erl_drv_output_term">erl_drv_output_term()</seealso>.</p>
- <p>This function is only thread-safe when the emulator with SMP
- support is used.</p>
+ <marker id="erl_drv_cond_destroy"></marker>
+ <p>Destroys a condition variable previously created by
+ <seealso marker="#erl_drv_cond_create">
+ <c>erl_drv_cond_create</c></seealso>.</p>
+ <p><c>cnd</c> is a pointer to a condition variable to destroy.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>long</ret><nametext>driver_async (ErlDrvPort port, unsigned int* key, void (*async_invoke)(void*), void* async_data, void (*async_free)(void*))</nametext></name>
- <fsummary>Perform an asynchronous call within a driver</fsummary>
+ <name><ret>char *</ret><nametext>erl_drv_cond_name(ErlDrvCond
+ *cnd)</nametext></name>
+ <fsummary>Get name of driver mutex.</fsummary>
<desc>
- <marker id="driver_async"></marker>
- <p>This function performs an asynchronous call. The function
- <c>async_invoke</c> is invoked in a thread separate from the
- emulator thread. This enables the driver to perform
- time-consuming, blocking operations without blocking the
- emulator.</p>
- <p>The async thread pool size can be set with the
- <seealso marker="erl#async_thread_pool_size">+A</seealso>
- command line argument of <seealso marker="erl">erl(1)</seealso>.
- If no async thread pool is available, the call is made
- synchronously in the thread calling <c>driver_async()</c>. The
- current number of async threads in the async thread pool can be
- retrieved via
- <seealso marker="#driver_system_info">driver_system_info()</seealso>.</p>
- <p>If there is a thread pool available, a thread will be
- used. If the <c>key</c> argument is null, the threads from the
- pool are used in a round-robin way, each call to
- <c>driver_async</c> uses the next thread in the pool. With the
- <c>key</c> argument set, this behaviour is changed. The two
- same values of <c>*key</c> always get the same thread.</p>
- <p>To make sure that a driver instance always uses the same
- thread, the following call can be used:</p>
- <p></p>
- <code type="none"><![CDATA[
- unsigned int myKey = driver_async_port_key(myPort);
-
- r = driver_async(myPort, &myKey, myData, myFunc);
- ]]></code>
- <p>It is enough to initialize <c>myKey</c> once for each
- driver instance.</p>
- <p>If a thread is already working, the calls will be
- queued up and executed in order. Using the same thread for
- each driver instance ensures that the calls will be made in
- sequence.</p>
- <p>The <c>async_data</c> is the argument to the functions
- <c>async_invoke</c> and <c>async_free</c>. It's typically a
- pointer to a structure that contains a pipe or event that
- can be used to signal that the async operation completed.
- The data should be freed in <c>async_free</c>.</p>
- <p>When the async operation is done, <seealso marker="driver_entry#ready_async">ready_async</seealso> driver
- entry function is called. If <c>ready_async</c> is null in
- the driver entry, the <c>async_free</c> function is called
- instead.</p>
- <p>The return value is -1 if the <c>driver_async</c> call
- fails.</p>
+ <marker id="erl_drv_cnd_name"></marker>
+ <p>Returns a pointer to the name of the condition.</p>
+ <p><c>cnd</c> is a pointer to an initialized condition.</p>
<note>
- <p>As of erts version 5.5.4.3 the default stack size for
- threads in the async-thread pool is 16 kilowords,
- i.e., 64 kilobyte on 32-bit architectures.
- This small default size has been chosen since the
- amount of async-threads might be quite large. The
- default stack size is enough for drivers delivered
- with Erlang/OTP, but might not be sufficiently large
- for other dynamically linked in drivers that use the
- driver_async() functionality. A suggested stack size
- for threads in the async-thread pool can be configured
- via the
- <seealso marker="erl#async_thread_stack_size">+a</seealso>
- command line argument of
- <seealso marker="erl">erl(1)</seealso>.</p>
+ <p>This function is intended for debugging purposes only.</p>
</note>
</desc>
</func>
+
<func>
- <name><ret>unsigned int</ret><nametext>driver_async_port_key (ErlDrvPort port)</nametext></name>
- <fsummary>Calculate an async key from an ErlDrvPort</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond
+ *cnd)</nametext></name>
+ <fsummary>Signal on a condition variable.</fsummary>
<desc>
- <marker id="driver_async_port_key"></marker>
- <p>This function calculates a key for later use in <seealso
- marker="#driver_async">driver_async()</seealso>. The keys are
- evenly distributed so that a fair mapping between port id's
- and async thread id's is achieved.</p>
- <note>
- <p>Before OTP-R16, the actual port id could be used as a key
- with proper casting, but after the rewrite of the port
- subsystem, this is no longer the case. With this function, you
- can achieve the same distribution based on port id's as before
- OTP-R16.</p>
- </note>
+ <marker id="erl_drv_cond_signal"></marker>
+ <p>Signals on a condition variable. That is, if
+ other threads are waiting on the condition variable being
+ signaled, <em>one</em> of them is woken.</p>
+ <p><c>cnd</c> is a pointer to a condition variable to signal on.</p>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort port)</nametext></name>
- <fsummary>Make sure the driver is never unloaded</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd,
+ ErlDrvMutex *mtx)</nametext></name>
+ <fsummary>Wait on a condition variable.</fsummary>
<desc>
- <marker id="driver_lock_driver"></marker>
- <p>This function locks the driver used by the port <c>port</c>
- in memory for the rest of the emulator process'
- lifetime. After this call, the driver behaves as one of Erlang's
- statically linked in drivers.</p>
+ <marker id="erl_drv_cond_wait"></marker>
+ <p>Waits on a condition variable. The calling
+ thread is blocked until another thread wakes it by signaling
+ or broadcasting on the condition variable. Before the calling
+ thread is blocked, it unlocks the mutex passed as argument.
+ When the calling thread is woken, it locks the same mutex before
+ returning. That is, the mutex currently must be locked by
+ the calling thread when calling this function.</p>
+ <p><c>cnd</c> is a pointer to a condition variable to wait on.
+ <c>mtx</c> is a pointer to a mutex to unlock while waiting.</p>
+ <note>
+ <p><c>erl_drv_cond_wait</c> can return even if
+ no one has signaled or broadcast on the condition
+ variable. Code calling <c>erl_drv_cond_wait</c> is
+ always to be prepared for <c>erl_drv_cond_wait</c>
+ returning even if the condition that the thread was
+ waiting for has not occurred. That is, when returning from
+ <c>erl_drv_cond_wait</c>, always check if the condition
+ has occurred, and if not call <c>erl_drv_cond_wait</c> again.</p>
+ </note>
+ <p>This function is thread-safe.</p>
</desc>
</func>
+
<func>
- <name><ret>ErlDrvPort</ret><nametext>driver_create_port(ErlDrvPort port, ErlDrvTermData owner_pid, char* name, ErlDrvData drv_data)</nametext></name>
- <fsummary>Create a new port (driver instance)</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port,
+ int percent)</nametext></name>
+ <fsummary>Give the runtime system a hint about how much CPU time the
+ current driver callback call has consumed.</fsummary>
<desc>
- <p>This function creates a new port executing the same driver
- code as the port creating the new port.
- A short description of the arguments:</p>
+ <marker id="erl_drv_consume_timeslice"></marker>
+ <p>Gives the runtime system a hint about how much CPU time the current
+ driver callback call has consumed since the last hint, or since the
+ the start of the callback if no previous hint has been given.</p>
<taglist>
<tag><c>port</c></tag>
- <item>The port handle of the port (driver instance) creating
- the new port.</item>
- <tag><c>owner_pid</c></tag>
- <item>The process id of the Erlang process which will be
- owner of the new port. This process will be linked
- to the new port. You usually want to use
- <c>driver_caller(port)</c> as <c>owner_pid</c>.</item>
- <tag><c>name</c></tag>
- <item>The port name of the new port. You usually want to
- use the same port name as the driver name
- (<seealso marker="driver_entry#driver_name">driver_name</seealso>
- field of the
- <seealso marker="driver_entry">driver_entry</seealso>).</item>
- <tag><c>drv_data</c></tag>
- <item>The driver defined handle that will be passed in subsequent
- calls to driver call-backs. Note, that the
- <seealso marker="driver_entry#start">driver start call-back</seealso>
- will not be called for this new driver instance.
- The driver defined handle is normally created in the
- <seealso marker="driver_entry#start">driver start call-back</seealso>
- when a port is created via
- <seealso marker="erlang#open_port/2">erlang:open_port/2</seealso>. </item>
+ <item>Port handle of the executing port.</item>
+ <tag><c>percent</c></tag>
+ <item>Approximate consumed fraction of a full
+ time-slice in percent.</item>
</taglist>
- <p>The caller of <c>driver_create_port()</c> is allowed to
- manipulate the newly created port when <c>driver_create_port()</c>
- has returned. When
- <seealso marker="#smp_support">port level locking</seealso>
- is used, the creating port is, however, only allowed to
- manipulate the newly created port until the current driver
- call-back that was called by the emulator returns.</p>
+ <p>The time is specified as a fraction, in percent, of a full time-slice
+ that a port is allowed to execute before it is to surrender the
+ CPU to other runnable ports or processes. Valid range is
+ <c>[1, 100]</c>. The scheduling time-slice is not an exact entity,
+ but can usually be approximated to about 1 millisecond.</p>
+ <p>Notice that it is up to the runtime system to determine if and
+ how to use this information. Implementations on some platforms
+ can use other means to determine the consumed fraction
+ of the time-slice. Lengthy driver callbacks should, regardless of
+ this, frequently call this function to determine if it is allowed
+ to continue execution or not.</p>
+ <p>This function returns a non-zero value
+ if the time-slice has been exhausted, and zero if the callback is
+ allowed to continue execution. If a non-zero value is
+ returned, the driver callback is to return as soon as possible in
+ order for the port to be able to yield.</p>
+ <p>This function is provided to better support co-operative scheduling,
+ improve system responsiveness, and to make it easier to prevent
+ misbehaviors of the VM because of a port monopolizing a scheduler
+ thread. It can be used when dividing lengthy work into some repeated
+ driver callback calls, without the need to use threads.</p>
+ <p>See also the important <seealso marker="#WARNING">warning</seealso>
+ text at the beginning of this manual page.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlDrvTime</ret><nametext>erl_drv_convert_time_unit(ErlDrvTime
+ val, ErlDrvTimeUnit from, ErlDrvTimeUnit to)</nametext></name>
+ <fsummary>Convert time unit of a time value.</fsummary>
+ <desc>
+ <marker id="erl_drv_convert_time_unit"></marker>
+ <p>Converts the <c>val</c> value of time unit <c>from</c> to
+ the corresponding value of time unit <c>to</c>. The result is
+ rounded using the floor function.</p>
+ <taglist>
+ <tag><c>val</c></tag>
+ <item>Value to convert time unit for.</item>
+ <tag><c>from</c></tag>
+ <item>Time unit of <c>val</c>.</item>
+ <tag><c>to</c></tag>
+ <item>Time unit of returned value.</item>
+ </taglist>
+ <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
+ time unit argument.</p>
+ <p>See also <seealso marker="#ErlDrvTime">
+ <c>ErlDrvTime</c></seealso> and
+ <seealso marker="#ErlDrvTimeUnit">
+ <c>ErlDrvTimeUnit</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1,
+ ErlDrvTid tid2)</nametext></name>
+ <fsummary>Compare thread identifiers for equality.</fsummary>
+ <desc>
+ <marker id="erl_drv_equal_tids"></marker>
+ <p>Compares two thread identifiers, <c>tid1</c> and <c>tid2</c>,
+ for equality.</p>
+ <p>Returns <c>0</c> it they are not equal, and a value not equal to
+ <c>0</c> if they are equal.</p>
<note>
- <p>When
- <seealso marker="#smp_support">port level locking</seealso>
- is used, the creating port is only allowed to manipulate
- the newly created port until the current driver call-back
- returns.</p>
+ <p>A thread identifier can be reused very quickly after
+ a thread has terminated. Therefore, if a thread
+ corresponding to one of the involved thread identifiers
+ has terminated since the thread identifier was saved,
+ the result of <c>erl_drv_equal_tids</c> does possibly not give
+ the expected result.</p>
</note>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_getenv(const char *key, char
+ *value, size_t *value_size)</nametext></name>
+ <fsummary>Get the value of an environment variable.</fsummary>
+ <desc>
+ <marker id="erl_drv_getenv"></marker>
+ <p>Retrieves the value of an environment variable.</p>
+ <taglist>
+ <tag><c>key</c></tag>
+ <item>A <c>NULL</c>-terminated string containing the
+ name of the environment variable.</item>
+ <tag><c>value</c></tag>
+ <item>A pointer to an output buffer.</item>
+ <tag><c>value_size</c></tag>
+ <item>A pointer to an integer. The integer is used both for
+ passing input and output sizes (see below).</item>
+ </taglist>
+ <p>When this function is called, <c>*value_size</c> is to contain the
+ size of the <c>value</c> buffer.</p>
+ <p>On success, <c>0</c> is returned,
+ the value of the environment variable has been written to
+ the <c>value</c> buffer, and <c>*value_size</c> contains the
+ string length (excluding the terminating <c>NULL</c> character) of
+ the value written to the <c>value</c> buffer.</p>
+ <p>On failure, that is, no such environment variable was found,
+ a value &lt; <c>0</c> is returned. When the size of the <c>value</c>
+ buffer is too small, a value &gt; <c>0</c> is returned and
+ <c>*value_size</c> has been set to the buffer size needed.</p>
+ <warning>
+ <p>Do <em>not</em> use libc's <c>getenv</c> or similar C library
+ interfaces from a driver.</p>
+ </warning>
+ <p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_init_ack(ErlDrvPort port, ErlDrvData res)</nametext></name>
- <fsummary>Acknowledge the start of the port</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_init_ack(ErlDrvPort port,
+ ErlDrvData res)</nametext></name>
+ <fsummary>Acknowledge the start of the port.</fsummary>
<desc>
<marker id="erl_drv_init_ack"></marker>
- <p>Arguments:</p>
+ <p>Acknowledges the start of the port.</p>
<taglist>
<tag><c>port</c></tag>
- <item>The port handle of the port (driver instance) creating
- doing the acknowledgment.
+ <item>The port handle of the port (driver instance)
+ doing the acknowledgment.
</item>
<tag><c>res</c></tag>
- <item>The result of the port initialization. This can be the same values
- as the return value of <seealso marker="driver_entry#start">start</seealso>,
- i.e any of the error codes or the ErlDrvData that is to be used for this
- port.
+ <item>The result of the port initialization. Can be the same
+ values as the return value of <seealso marker="driver_entry#start">
+ <c>start</c></seealso>, that is, any of the error codes or the
+ <c>ErlDrvData</c> that is to be used for this port.
</item>
</taglist>
- <p>
- When this function is called the initiating erlang:open_port call is
- returned as if the <seealso marker="driver_entry#start">start</seealso>
- function had just been called. It can only be used when the
- <seealso marker="driver_entry#driver_flags">ERL_DRV_FLAG_USE_INIT_ACK</seealso>
- flag has been set on the linked-in driver.
- </p>
+ <p>When this function is called the initiating <c>erlang:open_port</c>
+ call is returned as if the <seealso marker="driver_entry#start">
+ <c>start</c></seealso> function had just been called. It can only be
+ used when flag <seealso marker="driver_entry#driver_flags">
+ <c>ERL_DRV_FLAG_USE_INIT_ACK</c></seealso>
+ has been set on the linked-in driver.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_set_os_pid(ErlDrvPort port, ErlDrvSInt pid)</nametext></name>
- <fsummary>Set the os_pid for the port</fsummary>
+ <name><ret>ErlDrvTime</ret>
+ <nametext>erl_drv_monotonic_time(ErlDrvTimeUnit time_unit)</nametext>
+ </name>
+ <fsummary>Get Erlang monotonic time.</fsummary>
<desc>
- <marker id="erl_drv_set_os_pid"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>port</c></tag>
- <item>The port handle of the port (driver instance) to set the pid on.
- </item>
- <tag><c>pid</c></tag>
- <item>The pid to set.</item>
- </taglist>
- <p>
- Set the os_pid seen when doing erlang:port_info/2 on this port.
- </p>
+ <marker id="erl_drv_monotonic_time"></marker>
+ <p>Returns <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso>. Notice that negative values are
+ not uncommon.</p>
+ <p><c>time_unit</c> is time unit of returned value.</p>
+ <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
+ time unit argument, or if called from a thread that is not a
+ scheduler thread.</p>
+ <p>See also <seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso>
+ and <seealso marker="#ErlDrvTimeUnit">
+ <c>ErlDrvTimeUnit</c></seealso>.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_thread_create(char *name,
- ErlDrvTid *tid,
- void * (*func)(void *),
- void *arg,
- ErlDrvThreadOpts *opts)</nametext></name>
- <fsummary>Create a thread</fsummary>
+ <name><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char
+ *name)</nametext></name>
+ <fsummary>Create a mutex.</fsummary>
<desc>
- <marker id="erl_drv_thread_create"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>name</c></tag>
- <item>A string identifying the created thread. It will be used
- to identify the thread in planned future debug
- functionality.
- </item>
- <tag><c>tid</c></tag>
- <item>A pointer to a thread identifier variable.</item>
- <tag><c>func</c></tag>
- <item>A pointer to a function to execute in the created thread.</item>
- <tag><c>arg</c></tag>
- <item>A pointer to argument to the <c>func</c> function.</item>
- <tag><c>opts</c></tag>
- <item>A pointer to thread options to use or <c>NULL</c>.</item>
- </taglist>
- <p>This function creates a new thread. On success <c>0</c> is returned;
- otherwise, an <c>errno</c> value is returned to indicate the error.
- The newly created thread will begin executing in the function pointed
- to by <c>func</c>, and <c>func</c> will be passed <c>arg</c> as
- argument. When <c>erl_drv_thread_create()</c> returns the thread
- identifier of the newly created thread will be available in
- <c>*tid</c>. <c>opts</c> can be either a <c>NULL</c> pointer, or a
- pointer to an
- <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso>
- structure. If <c>opts</c> is a <c>NULL</c> pointer, default options
- will be used; otherwise, the passed options will be used.
- </p>
- <warning><p>You are not allowed to allocate the
- <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso>
- structure by yourself. It has to be allocated and
- initialized by
- <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>.
- </p></warning>
- <p>The created thread will terminate either when <c>func</c> returns
- or if
- <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>
- is called by the thread. The exit value of the thread is either
- returned from <c>func</c> or passed as argument to
- <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>.
- The driver creating the thread has the responsibility of joining the
- thread, via
- <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>,
- before the driver is unloaded. It is not possible to create
- "detached" threads, i.e., threads that don't need to be joined.
- </p>
- <warning><p>All created threads need to be joined by the driver before
- it is unloaded. If the driver fails to join all threads
- created before it is unloaded, the runtime system will
- most likely crash when the code of the driver is unloaded.
- </p></warning>
+ <marker id="erl_drv_mutex_create"></marker>
+ <p>Creates a mutex and returns a pointer to it.</p>
+ <p><c>name</c> is a string identifying the created mutex. It is used
+ to identify the mutex in planned future debug functionality.</p>
+ <p>Returns <c>NULL</c> on failure. The driver creating the mutex is
+ responsible for destroying it before the driver is unloaded.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>ErlDrvThreadOpts *</ret><nametext>erl_drv_thread_opts_create(char *name)</nametext></name>
- <fsummary>Create thread options</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex
+ *mtx)</nametext></name>
+ <fsummary>Destroy a mutex.</fsummary>
<desc>
- <marker id="erl_drv_thread_opts_create"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>name</c></tag>
- <item>A string identifying the created thread options. It will be used
- to identify the thread options in planned future debug
- functionality.
- </item>
- </taglist>
- <p>This function allocates and initialize a thread option
- structure. On failure <c>NULL</c> is returned. A thread option
- structure is used for passing options to
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
- If the structure isn't modified before it is passed to
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>,
- the default values will be used.
- </p>
- <warning><p>You are not allowed to allocate the
- <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso>
- structure by yourself. It has to be allocated and
- initialized by <c>erl_drv_thread_opts_create()</c>.
- </p></warning>
+ <marker id="erl_drv_mutex_destroy"></marker>
+ <p>Destroys a mutex previously created by
+ <seealso marker="#erl_drv_mutex_create">
+ <c>erl_drv_mutex_create</c></seealso>.
+ The mutex must be in an unlocked state before it is destroyed.</p>
+ <p><c>mtx</c> is a pointer to a mutex to destroy.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts)</nametext></name>
- <fsummary>Destroy thread options</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex
+ *mtx)</nametext></name>
+ <fsummary>Lock a mutex.</fsummary>
<desc>
- <marker id="erl_drv_thread_opts_destroy"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>opts</c></tag>
- <item>A pointer to thread options to destroy.</item>
- </taglist>
- <p>This function destroys thread options previously created by
- <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>.
- </p>
+ <marker id="erl_drv_mutex_lock"></marker>
+ <p>Locks a mutex. The calling thread is blocked until the mutex has
+ been locked. A thread that has currently locked the mutex
+ <em>cannot</em> lock the same mutex again.</p>
+ <p><c>mtx</c> is a pointer to a mutex to lock.</p>
+ <warning>
+ <p>If you leave a mutex locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_thread_exit(void *exit_value)</nametext></name>
- <fsummary>Terminate calling thread</fsummary>
+ <name><ret>char *</ret><nametext>erl_drv_mutex_name(ErlDrvMutex
+ *mtx)</nametext></name>
+ <fsummary>Get name of driver mutex.</fsummary>
<desc>
- <marker id="erl_drv_thread_exit"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>exit_value</c></tag>
- <item>A pointer to an exit value or <c>NULL</c>.</item>
- </taglist>
- <p>This function terminates the calling thread with the exit
- value passed as argument. You are only allowed to terminate
- threads created with
- <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
- The exit value can later be retrieved by another thread via
- <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>.
- </p>
- <p>This function is thread-safe.</p>
+ <marker id="erl_drv_mutex_name"></marker>
+ <p>Returns a pointer to the mutex name.</p>
+ <p><c>mtx</c> is a pointer to an initialized mutex.</p>
+ <note>
+ <p>This function is intended for debugging purposes only.</p>
+ </note>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void **exit_value)</nametext></name>
- <fsummary>Join with another thread</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex
+ *mtx)</nametext></name>
+ <fsummary>Try lock a mutex.</fsummary>
<desc>
- <marker id="erl_drv_thread_join"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>tid</c></tag>
- <item>The thread identifier of the thread to join.</item>
- <tag><c>exit_value</c></tag>
- <item>A pointer to a pointer to an exit value, or <c>NULL</c>.</item>
- </taglist>
- <p>This function joins the calling thread with another thread, i.e.,
- the calling thread is blocked until the thread identified by
- <c>tid</c> has terminated. On success <c>0</c> is returned;
- otherwise, an <c>errno</c> value is returned to indicate the error.
- A thread can only be joined once. The behavior of joining
- more than once is undefined, an emulator crash is likely. If
- <c>exit_value == NULL</c>, the exit value of the terminated thread
- will be ignored; otherwise, the exit value of the terminated thread
- will be stored at <c>*exit_value</c>.
- </p>
+ <marker id="erl_drv_mutex_trylock"></marker>
+ <p>Tries to lock a mutex. A thread that has currently locked the mutex
+ <em>cannot</em> try to lock the same mutex again.</p>
+ <p><c>mtx</c> is a pointer to a mutex to try to lock.</p>
+ <p>Returns <c>0</c> on success, otherwise <c>EBUSY</c>.</p>
+ <warning>
+ <p>If you leave a mutex locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>ErlDrvTid</ret><nametext>erl_drv_thread_self(void)</nametext></name>
- <fsummary>Get the thread identifier of the current thread</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex
+ *mtx)</nametext></name>
+ <fsummary>Unlock a mutex.</fsummary>
<desc>
- <marker id="erl_drv_thread_self"></marker>
- <p>This function returns the thread identifier of the
- calling thread.
- </p>
+ <marker id="erl_drv_mutex_unlock"></marker>
+ <p>Unlocks a mutex. The mutex currently must be
+ locked by the calling thread.</p>
+ <p><c>mtx</c> is a pointer to a mutex to unlock.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1, ErlDrvTid tid2)</nametext></name>
- <fsummary>Compare thread identifiers for equality</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port,
+ ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data from driver to port owner.</fsummary>
<desc>
- <marker id="erl_drv_equal_tids"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>tid1</c></tag>
- <item>A thread identifier.</item>
- <tag><c>tid2</c></tag>
- <item>A thread identifier.</item>
- </taglist>
- <p>This function compares two thread identifiers for equality,
- and returns <c>0</c> it they aren't equal, and
- a value not equal to <c>0</c> if they are equal.</p>
- <note><p>A Thread identifier may be reused very quickly after
- a thread has terminated. Therefore, if a thread
- corresponding to one of the involved thread identifiers
- has terminated since the thread identifier was saved,
- the result of <c>erl_drv_equal_tids()</c> might not give
- the expected result.
- </p></note>
- <p>This function is thread-safe.</p>
+ <marker id="erl_drv_output_term"></marker>
+ <p>Sends data in the special driver term
+ format to the port owner process. This is a fast way to
+ deliver term data from a driver. It needs no binary
+ conversion, so the port owner process receives data as
+ normal Erlang terms. The <seealso marker="#erl_drv_send_term">
+ <c>erl_drv_send_term</c></seealso>
+ functions can be used for sending to any process
+ on the local node.</p>
+ <note>
+ <p>Parameter <c>port</c> is <em>not</em>
+ an ordinary port handle, but a port handle converted using
+ <seealso marker="#driver_mk_port">
+ <c>driver_mk_port</c></seealso>.</p>
+ </note>
+ <p>Parameter <c>term</c> points to an array of
+ <c>ErlDrvTermData</c> with <c>n</c> elements. This array
+ contains terms described in the driver term format. Every
+ term consists of 1-4 elements in the array. The
+ first term has a term type and then arguments.
+ Parameter <c>port</c> specifies the sending port.</p>
+ <p>Tuples, maps, and lists (except strings, see below)
+ are built in reverse polish notation, so that to build a
+ tuple, the elements are specified first, and then the tuple
+ term, with a count. Likewise for lists and maps.</p>
+ <list type="bulleted">
+ <item>
+ <p>A tuple must be specified with the number of elements. (The
+ elements precede the <c>ERL_DRV_TUPLE</c> term.)</p>
+ </item>
+ <item>
+ <p>A map must be specified with the number of key-value pairs
+ <c>N</c>. The key-value pairs must precede the <c>ERL_DRV_MAP</c>
+ in this order: <c>key1,value1,key2,value2,...,keyN,valueN</c>.
+ Duplicate keys are not allowed.</p>
+ </item>
+ <item>
+ <p>A list must be specified with the number of elements,
+ including the tail, which is the last term preceding
+ <c>ERL_DRV_LIST</c>.</p>
+ </item>
+ </list>
+ <p>The special term <c>ERL_DRV_STRING_CONS</c> is used to
+ "splice" in a string in a list, a string specified this way is
+ not a list in itself, but the elements are elements of the
+ surrounding list.</p>
+ <pre>
+Term type Arguments
+--------- ---------
+ERL_DRV_NIL
+ERL_DRV_ATOM ErlDrvTermData atom (from driver_mk_atom(char *string))
+ERL_DRV_INT ErlDrvSInt integer
+ERL_DRV_UINT ErlDrvUInt integer
+ERL_DRV_INT64 ErlDrvSInt64 *integer_ptr
+ERL_DRV_UINT64 ErlDrvUInt64 *integer_ptr
+ERL_DRV_PORT ErlDrvTermData port (from driver_mk_port(ErlDrvPort port))
+ERL_DRV_BINARY ErlDrvBinary *bin, ErlDrvUInt len, ErlDrvUInt offset
+ERL_DRV_BUF2BINARY char *buf, ErlDrvUInt len
+ERL_DRV_STRING char *str, int len
+ERL_DRV_TUPLE int sz
+ERL_DRV_LIST int sz
+ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port)
+ or driver_caller(ErlDrvPort port))
+ERL_DRV_STRING_CONS char *str, int len
+ERL_DRV_FLOAT double *dbl
+ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
+ERL_DRV_MAP int sz</pre>
+ <p>The unsigned integer data type <c>ErlDrvUInt</c> and the
+ signed integer data type <c>ErlDrvSInt</c> are 64 bits wide
+ on a 64-bit runtime system and 32 bits wide on a 32-bit
+ runtime system. They were introduced in ERTS 5.6
+ and replaced some of the <c>int</c> arguments in the list above.</p>
+ <p>The unsigned integer data type <c>ErlDrvUInt64</c> and the
+ signed integer data type <c>ErlDrvSInt64</c> are always 64 bits
+ wide. They were introduced in ERTS 5.7.4.</p>
+ <p>To build the tuple <c>{tcp, Port, [100 | Binary]}</c>, the
+ following call can be made.</p>
+ <code type="none"><![CDATA[
+ErlDrvBinary* bin = ...
+ErlDrvPort port = ...
+ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("tcp"),
+ ERL_DRV_PORT, driver_mk_port(drvport),
+ ERL_DRV_INT, 100,
+ ERL_DRV_BINARY, bin, 50, 0,
+ ERL_DRV_LIST, 2,
+ ERL_DRV_TUPLE, 3,
+};
+erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
+ <p>Here <c>bin</c> is a driver binary of length at least 50 and
+ <c>drvport</c> is a port handle. Notice that <c>ERL_DRV_LIST</c>
+ comes after the elements of the list, likewise
+ <c>ERL_DRV_TUPLE</c>.</p>
+ <p>The <c>ERL_DRV_STRING_CONS</c> term is a way to construct
+ strings. It works differently from how <c>ERL_DRV_STRING</c>
+ works. <c>ERL_DRV_STRING_CONS</c> builds a string list in
+ reverse order (as opposed to how <c>ERL_DRV_LIST</c>
+ works), concatenating the strings added to a list. The tail
+ must be specified before <c>ERL_DRV_STRING_CONS</c>.</p>
+ <p><c>ERL_DRV_STRING</c> constructs a string, and ends
+ it. (So it is the same as <c>ERL_DRV_NIL</c> followed by
+ <c>ERL_DRV_STRING_CONS</c>.)</p>
+ <code type="none"><![CDATA[
+/* to send [x, "abc", y] to the port: */
+ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("x"),
+ ERL_DRV_STRING, (ErlDrvTermData)"abc", 3,
+ ERL_DRV_ATOM, driver_mk_atom("y"),
+ ERL_DRV_NIL,
+ ERL_DRV_LIST, 4
+};
+erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
+ <code type="none"><![CDATA[
+/* to send "abc123" to the port: */
+ErlDrvTermData spec[] = {
+ ERL_DRV_NIL, /* with STRING_CONS, the tail comes first */
+ ERL_DRV_STRING_CONS, (ErlDrvTermData)"123", 3,
+ ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3,
+};
+erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
+ <p>The <c>ERL_DRV_EXT2TERM</c> term type is used for passing a
+ term encoded with the
+ <seealso marker="erl_ext_dist">external format</seealso>,
+ that is, a term that has been encoded by
+ <seealso marker="erlang#term_to_binary/2">
+ <c>erlang:term_to_binary</c></seealso>,
+ <seealso marker="erl_interface:ei"><c>erl_interface:ei(3)</c></seealso>,
+ and so on.
+ For example, if <c>binp</c> is a pointer to an <c>ErlDrvBinary</c>
+ that contains term <c>{17, 4711}</c> encoded with the
+ <seealso marker="erl_ext_dist">external format</seealso>,
+ and you want to wrap it in a two-tuple with the tag <c>my_tag</c>,
+ that is, <c>{my_tag, {17, 4711}}</c>, you can do as follows:</p>
+ <code type="none"><![CDATA[
+ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("my_tag"),
+ ERL_DRV_EXT2TERM, (ErlDrvTermData) binp->orig_bytes, binp->orig_size
+ ERL_DRV_TUPLE, 2,
+};
+erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
+ <p>To build the map <c>#{key1 => 100, key2 => {200, 300}}</c>, the
+ following call can be made.</p>
+ <code type="none"><![CDATA[
+ErlDrvPort port = ...
+ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("key1"),
+ ERL_DRV_INT, 100,
+ ERL_DRV_ATOM, driver_mk_atom("key2"),
+ ERL_DRV_INT, 200,
+ ERL_DRV_INT, 300,
+ ERL_DRV_TUPLE, 2,
+ ERL_DRV_MAP, 2
+};
+erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
+ <p>If you want to pass a binary and do not already have the content
+ of the binary in an <c>ErlDrvBinary</c>, you can benefit from using
+ <c>ERL_DRV_BUF2BINARY</c> instead of creating an <c>ErlDrvBinary</c>
+ through <seealso marker="#driver_alloc_binary">
+ <c>driver_alloc_binary</c></seealso> and then pass the binary through
+ <c>ERL_DRV_BINARY</c>. The runtime system often allocates
+ binaries smarter if <c>ERL_DRV_BUF2BINARY</c> is used.
+ However, if the content of the binary to pass already resides in
+ an <c>ErlDrvBinary</c>, it is normally better to pass the binary using
+ <c>ERL_DRV_BINARY</c> and the <c>ErlDrvBinary</c> in question.</p>
+ <p>The <c>ERL_DRV_UINT</c>, <c>ERL_DRV_BUF2BINARY</c>, and
+ <c>ERL_DRV_EXT2TERM</c> term types were introduced in
+ ERTS 5.6.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
</desc>
</func>
<func>
- <name><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char *name)</nametext></name>
- <fsummary>Create a mutex</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_putenv(const char *key, char
+ *value)</nametext></name>
+ <fsummary>Set the value of an environment variable.</fsummary>
<desc>
- <marker id="erl_drv_mutex_create"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>name</c></tag>
- <item>A string identifying the created mutex. It will be used
- to identify the mutex in planned future debug functionality.
- </item>
- </taglist>
- <p>This function creates a mutex and returns a pointer to it. On
- failure <c>NULL</c> is returned. The driver creating the mutex
- has the responsibility of destroying it before the driver is
- unloaded.
- </p>
+ <marker id="erl_drv_putenv"></marker>
+ <p>Sets the value of an environment variable.</p>
+ <p><c>key</c> is a <c>NULL</c>-terminated string containing the
+ name of the environment variable.</p>
+ <p><c>value</c> is a <c>NULL</c>-terminated string containing the
+ new value of the environment variable.</p>
+ <p>Returns <c>0</c> on success, otherwise a value <c>!= 0</c>.</p>
+ <note>
+ <p>The result of passing the empty string (<c>""</c>) as a value
+ is platform-dependent. On some platforms the variable value
+ is set to the empty string, on others the
+ environment variable is removed.</p>
+ </note>
+ <warning>
+ <p>Do <em>not</em> use libc's <c>putenv</c> or similar C library
+ interfaces from a driver.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Destroy a mutex</fsummary>
+ <name><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char
+ *name)</nametext></name>
+ <fsummary>Create an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_mutex_destroy"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>mtx</c></tag>
- <item>A pointer to a mutex to destroy.</item>
- </taglist>
- <p>This function destroys a mutex previously created by
- <seealso marker="#erl_drv_mutex_create">erl_drv_mutex_create()</seealso>.
- The mutex has to be in an unlocked state before being
- destroyed.
- </p>
+ <marker id="erl_drv_rwlock_create"></marker>
+ <p>Creates an rwlock and returns a pointer to it.</p>
+ <p><c>name</c> is a string identifying the created rwlock.
+ It is used to identify the rwlock in planned future
+ debug functionality.</p>
+ <p>Returns <c>NULL</c> on failure. The driver creating the rwlock
+ is responsible for destroying it before the driver is unloaded.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Lock a mutex</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Destroy an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_mutex_lock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>mtx</c></tag>
- <item>A pointer to a mutex to lock.</item>
- </taglist>
- <p>This function locks a mutex. The calling thread will be
- blocked until the mutex has been locked. A thread
- which currently has locked the mutex may <em>not</em> lock
- the same mutex again.
- </p>
- <warning><p>If you leave a mutex locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
+ <marker id="erl_drv_rwlock_destroy"></marker>
+ <p>Destroys an rwlock previously created by
+ <seealso marker="#erl_drv_rwlock_create">
+ <c>erl_drv_rwlock_create</c></seealso>.
+ The rwlock must be in an unlocked state before it is destroyed.</p>
+ <p><c>rwlck</c> is a pointer to an rwlock to destroy.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Try lock a mutex</fsummary>
+ <name><ret>char *</ret><nametext>erl_drv_rwlock_name(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Get name of driver mutex.</fsummary>
<desc>
- <marker id="erl_drv_mutex_trylock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>mtx</c></tag>
- <item>A pointer to a mutex to try to lock.</item>
- </taglist>
- <p>This function tries to lock a mutex. If successful <c>0</c>,
- is returned; otherwise, <c>EBUSY</c> is returned. A thread
- which currently has locked the mutex may <em>not</em> try to
- lock the same mutex again.
- </p>
- <warning><p>If you leave a mutex locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
- <p>This function is thread-safe.</p>
+ <marker id="erl_drv_rwlock_name"></marker>
+ <p>Returns a pointer to the name of the rwlock.</p>
+ <p><c>rwlck</c> is a pointer to an initialized rwlock.</p>
+ <note>
+ <p>This function is intended for debugging purposes only.</p>
+ </note>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Unlock a mutex</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Read lock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_mutex_unlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>mtx</c></tag>
- <item>A pointer to a mutex to unlock.</item>
- </taglist>
- <p>This function unlocks a mutex. The mutex currently has to be
- locked by the calling thread.
- </p>
+ <marker id="erl_drv_rwlock_rlock"></marker>
+ <p>Read locks an rwlock. The calling thread is
+ blocked until the rwlock has been read locked. A thread
+ that currently has read or read/write locked the rwlock
+ <em>cannot</em> lock the same rwlock again.</p>
+ <p><c>rwlck</c> is a pointer to the rwlock to read lock.</p>
+ <warning>
+ <p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char *name)</nametext></name>
- <fsummary>Create a condition variable</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Read unlock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_cond_create"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>name</c></tag>
- <item>A string identifying the created condition variable. It
- will be used to identify the condition variable in planned
- future debug functionality.
- </item>
- </taglist>
- <p>This function creates a condition variable and returns a
- pointer to it. On failure <c>NULL</c> is returned. The driver
- creating the condition variable has the responsibility of
- destroying it before the driver is unloaded.</p>
+ <marker id="erl_drv_rwlock_runlock"></marker>
+ <p>Read unlocks an rwlock. The rwlock currently must
+ be read locked by the calling thread.</p>
+ <p><c>rwlck</c> is a pointer to an rwlock to read unlock.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond *cnd)</nametext></name>
- <fsummary>Destroy a condition variable</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Read/write lock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_cond_destroy"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>cnd</c></tag>
- <item>A pointer to a condition variable to destroy.</item>
- </taglist>
- <p>This function destroys a condition variable previously
- created by
- <seealso marker="#erl_drv_cond_create">erl_drv_cond_create()</seealso>.
- </p>
- <p>This function is thread-safe.</p>
+ <marker id="erl_drv_rwlock_rwlock"></marker>
+ <p>Read/write locks an rwlock. The calling thread
+ is blocked until the rwlock has been read/write locked.
+ A thread that currently has read or read/write locked the
+ rwlock <em>cannot</em> lock the same rwlock again.</p>
+ <p><c>rwlck</c> is a pointer to an rwlock to read/write lock.</p>
+ <warning>
+ <p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
+ <p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond *cnd)</nametext></name>
- <fsummary>Signal on a condition variable</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Read/write unlock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_cond_signal"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>cnd</c></tag>
- <item>A pointer to a condition variable to signal on.</item>
- </taglist>
- <p>This function signals on a condition variable. That is, if
- other threads are waiting on the condition variable being
- signaled, <em>one</em> of them will be woken.
- </p>
+ <marker id="erl_drv_rwlock_rwunlock"></marker>
+ <p>Read/write unlocks an rwlock. The rwlock currently must be
+ read/write locked by the calling thread.</p>
+ <p><c>rwlck</c> is a pointer to an rwlock to read/write unlock.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond *cnd)</nametext></name>
- <fsummary>Broadcast on a condition variable</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Try to read lock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_cond_broadcast"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>cnd</c></tag>
- <item>A pointer to a condition variable to broadcast on.</item>
- </taglist>
- <p>This function broadcasts on a condition variable. That is, if
- other threads are waiting on the condition variable being
- broadcast on, <em>all</em> of them will be woken.
- </p>
+ <marker id="erl_drv_rwlock_tryrlock"></marker>
+ <p>Tries to read lock an rwlock.</p>
+ <p><c>rwlck</c> is a pointer to an rwlock to try to read lock.</p>
+ <p>Returns <c>0</c> on success, otherwise <c>EBUSY</c>.
+ A thread that currently has read or read/write locked the
+ rwlock <em>cannot</em> try to lock the same rwlock again.</p>
+ <warning>
+ <p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd, ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Wait on a condition variable</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock
+ *rwlck)</nametext></name>
+ <fsummary>Try to read/write lock an rwlock.</fsummary>
<desc>
- <marker id="erl_drv_cond_wait"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>cnd</c></tag>
- <item>A pointer to a condition variable to wait on.</item>
- <tag><c>mtx</c></tag>
- <item>A pointer to a mutex to unlock while waiting.</item>
- <tag><c></c></tag>
- <item></item>
- </taglist>
- <p>This function waits on a condition variable. The calling
- thread is blocked until another thread wakes it by signaling
- or broadcasting on the condition variable. Before the calling
- thread is blocked it unlocks the mutex passed as argument, and
- when the calling thread is woken it locks the same mutex before
- returning. That is, the mutex currently has to be locked by
- the calling thread when calling this function.
- </p>
- <note><p><c>erl_drv_cond_wait()</c> might return even though
- no-one has signaled or broadcast on the condition
- variable. Code calling <c>erl_drv_cond_wait()</c> should
- always be prepared for <c>erl_drv_cond_wait()</c>
- returning even though the condition that the thread was
- waiting for hasn't occurred. That is, when returning from
- <c>erl_drv_cond_wait()</c> always check if the condition
- has occurred, and if not call <c>erl_drv_cond_wait()</c>
- again.
- </p></note>
+ <marker id="erl_drv_rwlock_tryrwlock"></marker>
+ <p>Tries to read/write lock an rwlock.
+ A thread that currently has read or read/write locked the
+ rwlock <em>cannot</em> try to lock the same rwlock again.</p>
+ <p><c>rwlck</c>is pointer to an rwlock to try to read/write lock.</p>
+ <p>Returns <c>0</c> on success, otherwise <c>EBUSY</c>.</p>
+ <warning>
+ <p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char *name)</nametext></name>
- <fsummary>Create an rwlock</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port,
+ ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data to other process than port owner process.
+ </fsummary>
<desc>
- <marker id="erl_drv_rwlock_create"></marker>
- <p>Arguments:</p>
+ <marker id="erl_drv_send_term"></marker>
+ <p>This function is the only way for a driver to send data to
+ <em>other</em> processes than the port owner process. Parameter
+ <c>receiver</c> specifies the process to receive the data.</p>
+ <note>
+ <p>Parameter <c>port</c> is <em>not</em> an ordinary port handle, but
+ a port handle converted using
+ <seealso marker="#driver_mk_port">
+ <c>driver_mk_port</c></seealso>.</p>
+ </note>
+ <p>Parameters <c>port</c>, <c>term</c>, and <c>n</c> work as in
+ <seealso marker="#erl_drv_output_term">
+ <c>erl_drv_output_term</c></seealso>.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_set_os_pid(ErlDrvPort port,
+ ErlDrvSInt pid)</nametext></name>
+ <fsummary>Set the os_pid for the port.</fsummary>
+ <desc>
+ <marker id="erl_drv_set_os_pid"></marker>
+ <p>Sets the <c>os_pid</c> seen when doing
+ <seealso marker="erlang:port_info/2">
+ <c>erlang:port_info/2</c></seealso> on this port.</p>
+ <p><c>port</c> is the port handle of the port (driver instance) to set
+ the pid on. <c>pid</c>is the pid to set.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_thread_create(char *name, ErlDrvTid
+ *tid, void * (*func)(void *), void *arg, ErlDrvThreadOpts
+ *opts)</nametext></name>
+ <fsummary>Create a thread.</fsummary>
+ <desc>
+ <marker id="erl_drv_thread_create"></marker>
+ <p>Creates a new thread.</p>
<taglist>
<tag><c>name</c></tag>
- <item>A string identifying the created rwlock. It will be used to
- identify the rwlock in planned future debug functionality.
- </item>
+ <item>A string identifying the created thread. It is used to
+ identify the thread in planned future debug functionality.
+ </item>
+ <tag><c>tid</c></tag>
+ <item>A pointer to a thread identifier variable.</item>
+ <tag><c>func</c></tag>
+ <item>A pointer to a function to execute in the created thread.</item>
+ <tag><c>arg</c></tag>
+ <item>A pointer to argument to the <c>func</c> function.</item>
+ <tag><c>opts</c></tag>
+ <item>A pointer to thread options to use or <c>NULL</c>.</item>
</taglist>
- <p>This function creates an rwlock and returns a pointer to it. On
- failure <c>NULL</c> is returned. The driver creating the rwlock
- has the responsibility of destroying it before the driver is
- unloaded.
- </p>
+ <p>Returns <c>0</c> on success,
+ otherwise an <c>errno</c> value is returned to indicate the error.
+ The newly created thread begins executing in the function pointed
+ to by <c>func</c>, and <c>func</c> is passed <c>arg</c> as
+ argument. When <c>erl_drv_thread_create</c> returns, the thread
+ identifier of the newly created thread is available in
+ <c>*tid</c>. <c>opts</c> can be either a <c>NULL</c> pointer, or a
+ pointer to an
+ <seealso marker="#ErlDrvThreadOpts"><c>ErlDrvThreadOpts</c></seealso>
+ structure. If <c>opts</c> is a <c>NULL</c> pointer, default options
+ are used, otherwise the passed options are used.</p>
+ <warning>
+ <p>You are not allowed to allocate the
+ <seealso marker="#ErlDrvThreadOpts">
+ <c>ErlDrvThreadOpts</c></seealso> structure by yourself.
+ It must be allocated and initialized by
+ <seealso marker="#erl_drv_thread_opts_create">
+ <c>erl_drv_thread_opts_create</c></seealso>.</p>
+ </warning>
+ <p>The created thread terminates either when <c>func</c> returns or if
+ <seealso marker="#erl_drv_thread_exit">
+ <c>erl_drv_thread_exit</c></seealso>
+ is called by the thread. The exit value of the thread is either
+ returned from <c>func</c> or passed as argument to
+ <seealso marker="#erl_drv_thread_exit">
+ <c>erl_drv_thread_exit</c></seealso>.
+ The driver creating the thread is responsible for joining the
+ thread, through <seealso marker="#erl_drv_thread_join">
+ <c>erl_drv_thread_join</c></seealso>,
+ before the driver is unloaded. "Detached" threads cannot be created,
+ that is, threads that do not need to be joined.</p>
+ <warning>
+ <p>All created threads must be joined by the driver before
+ it is unloaded. If the driver fails to join all threads
+ created before it is unloaded, the runtime system
+ most likely crashes when the driver code is unloaded.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Destroy an rwlock</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_thread_exit(void
+ *exit_value)</nametext></name>
+ <fsummary>Terminate calling thread.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_destroy"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to destroy.</item>
- </taglist>
- <p>This function destroys an rwlock previously created by
- <seealso marker="#erl_drv_rwlock_create">erl_drv_rwlock_create()</seealso>.
- The rwlock has to be in an unlocked state before being destroyed.
- </p>
+ <marker id="erl_drv_thread_exit"></marker>
+ <p>Terminates the calling thread with the exit value passed as
+ argument. <c>exit_value</c> is a pointer to an exit value or
+ <c>NULL</c>.</p>
+ <p>You are only allowed to terminate threads created with
+ <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>.</p>
+ <p>The exit value can later be retrieved by another thread through
+ <seealso marker="#erl_drv_thread_join">
+ <c>erl_drv_thread_join</c></seealso>.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Read lock an rwlock</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void
+ **exit_value)</nametext></name>
+ <fsummary>Join with another thread.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_rlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to read lock.</item>
- </taglist>
- <p>This function read locks an rwlock. The calling thread will be
- blocked until the rwlock has been read locked. A thread
- which currently has read or read/write locked the rwlock may
- <em>not</em> lock the same rwlock again.
- </p>
- <warning><p>If you leave an rwlock locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
+ <marker id="erl_drv_thread_join"></marker>
+ <p>Joins the calling thread with another thread, that is,
+ the calling thread is blocked until the thread identified by
+ <c>tid</c> has terminated.</p>
+ <p><c>tid</c> is the thread identifier of the thread to join.
+ <c>exit_value</c> is a pointer to a pointer to an exit value,
+ or <c>NULL</c>.</p>
+ <p>Returns <c>0</c> on success, otherwise an <c>errno</c>
+ value is returned to indicate the error.</p>
+ <p>A thread can only be joined once. The behavior of joining
+ more than once is undefined, an emulator crash is likely. If
+ <c>exit_value == NULL</c>, the exit value of the terminated thread
+ is ignored, otherwise the exit value of the terminated thread
+ is stored at <c>*exit_value</c>.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Try to read lock an rwlock</fsummary>
+ <name><ret>char *</ret><nametext>erl_drv_thread_name(ErlDrvTid
+ tid)</nametext></name>
+ <fsummary>Get name of driver mutex.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_tryrlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to try to read lock.</item>
- </taglist>
- <p>This function tries to read lock an rwlock. If successful
- <c>0</c>, is returned; otherwise, <c>EBUSY</c> is returned.
- A thread which currently has read or read/write locked the
- rwlock may <em>not</em> try to lock the same rwlock again.
- </p>
- <warning><p>If you leave an rwlock locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
- <p>This function is thread-safe.</p>
+ <marker id="erl_drv_rwlock_name"></marker>
+ <p>Returns a pointer to the name of the thread.</p>
+ <p><c>tid</c> is a thread identifier.</p>
+ <note>
+ <p>This function is intended for debugging purposes only.</p>
+ </note>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Read unlock an rwlock</fsummary>
+ <name><ret>ErlDrvThreadOpts *</ret>
+ <nametext>erl_drv_thread_opts_create(char *name)</nametext></name>
+ <fsummary>Create thread options.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_runlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to read unlock.</item>
- </taglist>
- <p>This function read unlocks an rwlock. The rwlock currently
- has to be read locked by the calling thread.
- </p>
+ <marker id="erl_drv_thread_opts_create"></marker>
+ <p>Allocates and initializes a thread option structure.</p>
+ <p><c>name</c> is a string identifying the created thread options.
+ It is used to identify the thread options in planned future debug
+ functionality.</p>
+ <p>Returns <c>NULL</c> on failure. A thread option
+ structure is used for passing options to
+ <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>.
+ If the structure is not modified before it is passed to
+ <seealso marker="#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>,
+ the default values are used.</p>
+ <warning>
+ <p>You are not allowed to allocate the
+ <seealso marker="#ErlDrvThreadOpts">
+ <c>ErlDrvThreadOpts</c></seealso>
+ structure by yourself. It must be allocated and initialized by
+ <c>erl_drv_thread_opts_create</c>.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Read/Write lock an rwlock</fsummary>
+ <name><ret>void</ret>
+ <nametext>erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts)</nametext>
+ </name>
+ <fsummary>Destroy thread options.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_rwlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to read/write lock.</item>
- </taglist>
- <p>This function read/write locks an rwlock. The calling thread
- will be blocked until the rwlock has been read/write locked.
- A thread which currently has read or read/write locked the
- rwlock may <em>not</em> lock the same rwlock again.
- </p>
- <warning><p>If you leave an rwlock locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
+ <marker id="erl_drv_thread_opts_destroy"></marker>
+ <p>Destroys thread options previously created by
+ <seealso marker="#erl_drv_thread_opts_create">
+ <c>erl_drv_thread_opts_create</c></seealso>.</p>
+ <p><c>opts</c> is a pointer to thread options to destroy.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Try to read/write lock an rwlock</fsummary>
+ <name><ret>ErlDrvTid</ret>
+ <nametext>erl_drv_thread_self(void)</nametext></name>
+ <fsummary>Get the thread identifier of the current thread.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_tryrwlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to try to read/write lock.</item>
- </taglist>
- <p>This function tries to read/write lock an rwlock. If successful
- <c>0</c>, is returned; otherwise, <c>EBUSY</c> is returned.
- A thread which currently has read or read/write locked the
- rwlock may <em>not</em> try to lock the same rwlock again.
- </p>
- <warning><p>If you leave an rwlock locked in an emulator thread
- when you let the thread out of your control, you will
- <em>very likely</em> deadlock the whole emulator.
- </p></warning>
+ <marker id="erl_drv_thread_self"></marker>
+ <p>Returns the thread identifier of the calling thread.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Read/Write unlock an rwlock</fsummary>
+ <name><ret>ErlDrvTime</ret><nametext>erl_drv_time_offset(ErlDrvTimeUnit
+ time_unit)</nametext></name>
+ <fsummary>Get current time offset.</fsummary>
<desc>
- <marker id="erl_drv_rwlock_rwunlock"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an rwlock to read/write unlock.</item>
- </taglist>
- <p>This function read/write unlocks an rwlock. The rwlock
- currently has to be read/write locked by the calling thread.
- </p>
+ <marker id="erl_drv_time_offset"></marker>
+ <p>Returns the current time offset between
+ <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso> and
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso>
+ converted into the <c>time_unit</c> passed as argument.</p>
+ <p><c>time_unit</c> is time unit of returned value.</p>
+ <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
+ time unit argument, or if called from a thread that is not a
+ scheduler thread.</p>
+ <p>See also <seealso marker="#ErlDrvTime">
+ <c>ErlDrvTime</c></seealso> and
+ <seealso marker="#ErlDrvTimeUnit">
+ <c>ErlDrvTimeUnit</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey
+ key)</nametext></name>
+ <fsummary>Get thread-specific data.</fsummary>
+ <desc>
+ <marker id="erl_drv_tsd_get"></marker>
+ <p>Returns the thread-specific data
+ associated with <c>key</c> for the calling thread.</p>
+ <p><c>key</c> is a thread-specific data key.</p>
+ <p>Returns <c>NULL</c> if no data has been associated
+ with <c>key</c> for the calling thread.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name, ErlDrvTSDKey *key)</nametext></name>
- <fsummary>Create a thread specific data key</fsummary>
+ <name><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name,
+ ErlDrvTSDKey *key)</nametext></name>
+ <fsummary>Create a thread-specific data key.</fsummary>
<desc>
<marker id="erl_drv_tsd_key_create"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>name</c></tag>
- <item>A string identifying the created key. It will be used
- to identify the key in planned future debug
- functionality.
- </item>
- <tag><c>key</c></tag>
- <item>A pointer to a thread specific data key variable.</item>
- </taglist>
- <p>This function creates a thread specific data key. On success
- <c>0</c> is returned; otherwise, an <c>errno</c> value is returned
- to indicate the error. The driver creating the key has the
- responsibility of destroying it before the driver is unloaded.
- </p>
+ <p>Creates a thread-specific data key.</p>
+ <p><c>name</c> is a string identifying the created key. It is used
+ to identify the key in planned future debug functionality.</p>
+ <p><c>key</c> is a pointer to a thread-specific data key variable.</p>
+ <p>Returns <c>0</c> on success, otherwise an <c>errno</c> value is
+ returned to indicate the error. The driver creating the key is
+ responsible for destroying it before the driver is unloaded.</p>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey key)</nametext></name>
- <fsummary>Destroy a thread specific data key</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey
+ key)</nametext></name>
+ <fsummary>Destroy a thread-specific data key.</fsummary>
<desc>
<marker id="erl_drv_tsd_key_destroy"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>key</c></tag>
- <item>A thread specific data key to destroy.</item>
- </taglist>
- <p>This function destroys a thread specific data key
- previously created by
- <seealso marker="#erl_drv_tsd_key_create">erl_drv_tsd_key_create()</seealso>.
- All thread specific data using this key in all threads
- have to be cleared (see
- <seealso marker="#erl_drv_tsd_set">erl_drv_tsd_set()</seealso>)
- prior to the call to <c>erl_drv_tsd_key_destroy()</c>.
- </p>
- <warning><p>A destroyed key is very likely to be reused soon.
- Therefore, if you fail to clear the thread specific
- data using this key in a thread prior to destroying
- the key, you will <em>very likely</em> get unexpected
- errors in other parts of the system.
- </p></warning>
+ <p>Destroys a thread-specific data key previously created by
+ <seealso marker="#erl_drv_tsd_key_create">
+ <c>erl_drv_tsd_key_create</c></seealso>.
+ All thread-specific data using this key in all threads
+ must be cleared (see <seealso marker="#erl_drv_tsd_set">
+ <c>erl_drv_tsd_set</c></seealso>)
+ before the call to <c>erl_drv_tsd_key_destroy</c>.</p>
+ <p><c>key</c> is a thread-specific data key to destroy.</p>
+ <warning>
+ <p>A destroyed key is very likely to be reused soon.
+ Therefore, if you fail to clear the thread-specific
+ data using this key in a thread before destroying
+ the key, you will <em>very likely</em> get unexpected
+ errors in other parts of the system.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void *data)</nametext></name>
- <fsummary>Set thread specific data</fsummary>
+ <name><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void
+ *data)</nametext></name>
+ <fsummary>Set thread-specific data.</fsummary>
<desc>
<marker id="erl_drv_tsd_set"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>key</c></tag>
- <item>A thread specific data key.</item>
- <tag><c>data</c></tag>
- <item>A pointer to data to associate with <c>key</c>
- in calling thread.
- </item>
- </taglist>
- <p>This function sets thread specific data associated with
- <c>key</c> for the calling thread. You are only allowed to set
- thread specific data for threads while they are fully under your
- control. For example, if you set thread specific data in a thread
- calling a driver call-back function, it has to be cleared, i.e.
- set to <c>NULL</c>, before returning from the driver call-back
- function.
- </p>
- <warning><p>If you fail to clear thread specific data in an
- emulator thread before letting it out of your control,
- you might not ever be able to clear this data with
- later unexpected errors in other parts of the system as
- a result.
- </p></warning>
+ <p>Sets thread-specific data associated with
+ <c>key</c> for the calling thread. You are only allowed to set
+ thread-specific data for threads while they are fully under your
+ control. For example, if you set thread-specific data in a thread
+ calling a driver callback function, it must be cleared, that is,
+ set to <c>NULL</c>, before returning from the driver callback
+ function.</p>
+ <p><c>key</c> is a thread-specific data key.</p>
+ <p><c>data</c> is a pointer to data to associate with <c>key</c>
+ in the calling thread.</p>
+ <warning>
+ <p>If you fail to clear thread-specific data in an
+ emulator thread before letting it out of your control,
+ you might never be able to clear this data with
+ later unexpected errors in other parts of the system as
+ a result.</p>
+ </warning>
<p>This function is thread-safe.</p>
</desc>
</func>
<func>
- <name><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey key)</nametext></name>
- <fsummary>Get thread specific data</fsummary>
+ <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name>
+ <fsummary>Get Erlang error atom name from error number.</fsummary>
<desc>
- <marker id="erl_drv_tsd_get"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>key</c></tag>
- <item>A thread specific data key.</item>
- </taglist>
- <p>This function returns the thread specific data
- associated with <c>key</c> for the calling thread.
- If no data has been associated with <c>key</c> for
- the calling thread, <c>NULL</c> is returned.
- </p>
- <p>This function is thread-safe.</p>
+ <marker id="erl_errno_id"></marker>
+ <p>Returns the atom name of the Erlang error,
+ given the error number in <c>error</c>. The error atoms are
+ <c>einval</c>, <c>enoent</c>, and so on. It can be used to make
+ error terms from the driver.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>erl_drv_putenv(const char *key, char *value)</nametext></name>
- <fsummary>Set the value of an environment variable</fsummary>
+ <name><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry
+ *de)</nametext></name>
+ <fsummary>Remove a driver entry.</fsummary>
<desc>
- <marker id="erl_drv_putenv"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>key</c></tag>
- <item>A null terminated string containing the
- name of the environment variable.</item>
- <tag><c>value</c></tag>
- <item>A null terminated string containing the
- new value of the environment variable.</item>
- </taglist>
- <p>This function sets the value of an environment variable.
- It returns <c>0</c> on success, and a value <c>!= 0</c> on
- failure.
- </p>
- <note><p>The result of passing the empty string ("") as a value
- is platform dependent. On some platforms the value of the
- variable is set to the empty string, on others, the
- environment variable is removed.</p>
- </note>
- <warning><p>Do <em>not</em> use libc's <c>putenv</c> or similar
- C library interfaces from a driver.
- </p></warning>
- <p>This function is thread-safe.</p>
+ <marker id="remove_driver_entry"></marker>
+ <p>Removes a driver entry <c>de</c> previously added with
+ <seealso marker="#add_driver_entry">
+ <c>add_driver_entry</c></seealso>.</p>
+ <p>Driver entries added by the <c>erl_ddll</c> Erlang interface
+ cannot be removed by using this interface.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>erl_drv_getenv(const char *key, char *value, size_t *value_size)</nametext></name>
- <fsummary>Get the value of an environment variable</fsummary>
+ <name><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int
+ on)</nametext></name>
+ <fsummary>Signal or unsignal port as busy.</fsummary>
<desc>
- <marker id="erl_drv_getenv"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>key</c></tag>
- <item>A null terminated string containing the
- name of the environment variable.</item>
- <tag><c>value</c></tag>
- <item>A pointer to an output buffer.</item>
- <tag><c>value_size</c></tag>
- <item>A pointer to an integer. The integer is both used for
- passing input and output sizes (see below).
- </item>
- </taglist>
- <p>This function retrieves the value of an environment variable.
- When called, <c>*value_size</c> should contain the size of
- the <c>value</c> buffer. On success <c>0</c> is returned,
- the value of the environment variable has been written to
- the <c>value</c> buffer, and <c>*value_size</c> contains the
- string length (excluding the terminating null character) of
- the value written to the <c>value</c> buffer. On failure,
- i.e., no such environment variable was found, a value less than
- <c>0</c> is returned. When the size of the <c>value</c>
- buffer is too small, a value greater than <c>0</c> is returned
- and <c>*value_size</c> has been set to the buffer size needed.
- </p>
- <warning><p>Do <em>not</em> use libc's <c>getenv</c> or similar
- C library interfaces from a driver.
- </p></warning>
- <p>This function is thread-safe.</p>
+ <marker id="set_busy_port"></marker>
+ <p>Sets and unsets the busy state of the port. If
+ <c>on</c> is non-zero, the port is set to busy. If it is zero,
+ the port is set to not busy. You typically want to combine
+ this feature with the <seealso marker="#erl_drv_busy_msgq_limits">
+ busy port message queue</seealso> functionality.</p>
+ <p>Processes sending command data to the port are suspended
+ if either the port or the port message queue
+ is busy. Suspended processes are resumed when neither the
+ port or the port message queue is busy. Command data
+ is in this context data passed to the port using either
+ <c>Port ! {Owner, {command, Data}}</c> or
+ <c>port_command/[2,3]</c>.</p>
+ <p>If the <seealso marker="driver_entry#driver_flags">
+ <![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso> has been set in the
+ <seealso marker="driver_entry"><c>driver_entry</c></seealso>,
+ data can be forced into the driver through
+ <seealso marker="erlang#port_command/3">
+ <c>erlang:port_command(Port, Data, [force])</c></seealso>
+ even if the driver has signaled that it is busy.</p>
+ <p>For information about busy port message queue functionality, see
+ <seealso marker="#erl_drv_busy_msgq_limits">
+ <c>erl_drv_busy_msgq_limits</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>set_port_control_flags(ErlDrvPort port,
+ int flags)</nametext></name>
+ <fsummary>Set flags on how to handle control entry function.</fsummary>
+ <desc>
+ <marker id="set_port_control_flags"></marker>
+ <p>Sets flags for how the <seealso marker="driver_entry#control">
+ <c>control</c></seealso> driver entry
+ function will return data to the port owner process.
+ (The <c>control</c> function is called from
+ <seealso marker="erlang:port_control/3">
+ <c>erlang:port_control/3</c></seealso>.)</p>
+ <p>Currently there are only two meaningful values for
+ <c>flags</c>: <c>0</c> means that data is returned in a list,
+ and <c>PORT_CONTROL_FLAG_BINARY</c> means data is returned as
+ a binary from <c>control</c>.</p>
</desc>
</func>
- <func>
- <name><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port, int percent)</nametext></name>
- <fsummary>Give the runtime system a hint about how much CPU time the
- current driver callback call has consumed</fsummary>
- <desc>
- <marker id="erl_drv_consume_timeslice"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>port</c></tag>
- <item>Port handle of the executing port.</item>
- <tag><c>percent</c></tag>
- <item>Approximate consumed fraction of a full
- time-slice in percent.</item>
- </taglist>
- <p>Give the runtime system a hint about how much CPU time the
- current driver callback call has consumed since last hint, or
- since the start of the callback if no previous hint has been given.
- The time is given as a fraction, in percent, of a full time-slice
- that a port is allowed to execute before it should surrender the
- CPU to other runnable ports or processes. Valid range is
- <c>[1, 100]</c>. The scheduling time-slice is not an exact entity,
- but can usually be approximated to about 1 millisecond.</p>
-
- <p>Note that it is up to the runtime system to determine if and
- how to use this information. Implementations on some platforms
- may use other means in order to determine the consumed fraction
- of the time-slice. Lengthy driver callbacks should regardless of
- this frequently call the <c>erl_drv_consume_timeslice()</c>
- function in order to determine if it is allowed to continue
- execution or not.</p>
-
- <p><c>erl_drv_consume_timeslice()</c> returns a non-zero value
- if the time-slice has been exhausted, and zero if the callback is
- allowed to continue execution. If a non-zero value is
- returned the driver callback should return as soon as possible in
- order for the port to be able to yield.</p>
-
- <p>This function is provided to better support co-operative scheduling,
- improve system responsiveness, and to make it easier to prevent
- misbehaviors of the VM due to a port monopolizing a scheduler thread.
- It can be used when dividing length work into a number of repeated
- driver callback calls without the need to use threads. Also see the
- important <seealso marker="#WARNING">warning</seealso> text at the
- beginning of this document.</p>
- </desc>
- </func>
-
- <func>
- <name><ret>char *</ret><nametext>erl_drv_cond_name(ErlDrvCond *cnd)</nametext></name>
- <fsummary>Get name of driver mutex.</fsummary>
- <desc>
- <marker id="erl_drv_cnd_name"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>cnd</c></tag>
- <item>A pointer to an initialized condition.</item>
- </taglist>
- <p>
- Returns a pointer to the name of the condition.
- </p>
- <note>
- <p>This function is intended for debugging purposes only.</p>
- </note>
- </desc>
- </func>
-
- <func>
- <name><ret>char *</ret><nametext>erl_drv_mutex_name(ErlDrvMutex *mtx)</nametext></name>
- <fsummary>Get name of driver mutex.</fsummary>
- <desc>
- <marker id="erl_drv_mutex_name"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>mtx</c></tag>
- <item>A pointer to an initialized mutex.</item>
- </taglist>
- <p>
- Returns a pointer to the name of the mutex.
- </p>
- <note>
- <p>This function is intended for debugging purposes only.</p>
- </note>
- </desc>
- </func>
-
- <func>
- <name><ret>char *</ret><nametext>erl_drv_rwlock_name(ErlDrvRWLock *rwlck)</nametext></name>
- <fsummary>Get name of driver mutex.</fsummary>
- <desc>
- <marker id="erl_drv_rwlock_name"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>rwlck</c></tag>
- <item>A pointer to an initialized r/w-lock.</item>
- </taglist>
- <p>
- Returns a pointer to the name of the r/w-lock.
- </p>
- <note>
- <p>This function is intended for debugging purposes only.</p>
- </note>
- </desc>
- </func>
-
- <func>
- <name><ret>char *</ret><nametext>erl_drv_thread_name(ErlDrvTid tid)</nametext></name>
- <fsummary>Get name of driver mutex.</fsummary>
- <desc>
- <marker id="erl_drv_rwlock_name"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>tid</c></tag>
- <item>A thread identifier.</item>
- </taglist>
- <p>
- Returns a pointer to the name of the thread.
- </p>
- <note>
- <p>This function is intended for debugging purposes only.</p>
- </note>
- </desc>
- </func>
-
- <func>
- <name><ret>ErlDrvTime</ret><nametext>erl_drv_monotonic_time(ErlDrvTimeUnit time_unit)</nametext></name>
- <fsummary>Get Erlang Monotonic Time</fsummary>
- <desc>
- <marker id="erl_drv_monotonic_time"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>time_unit</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>
- Returns
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso>. Note that it is not uncommon with
- negative values.
- </p>
- <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
- time unit argument, or if called from a thread that is not a
- scheduler thread.</p>
- <p>See also:</p>
- <list>
- <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item>
- <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item>
- </list>
- </desc>
- </func>
-
- <func>
- <name><ret>ErlDrvTime</ret><nametext>erl_drv_time_offset(ErlDrvTimeUnit time_unit)</nametext></name>
- <fsummary>Get current Time Offset</fsummary>
- <desc>
- <marker id="erl_drv_time_offset"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>time_unit</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>Returns the current time offset between
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso>
- and
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- converted into the <c>time_unit</c> passed as argument.</p>
- <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
- time unit argument, or if called from a thread that is not a
- scheduler thread.</p>
- <p>See also:</p>
- <list>
- <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item>
- <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item>
- </list>
- </desc>
- </func>
-
- <func>
- <name><ret>ErlDrvTime</ret><nametext>erl_drv_convert_time_unit(ErlDrvTime val, ErlDrvTimeUnit from, ErlDrvTimeUnit to)</nametext></name>
- <fsummary>Convert time unit of a time value</fsummary>
- <desc>
- <marker id="erl_drv_convert_time_unit"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>val</c></tag>
- <item>Value to convert time unit for.</item>
- <tag><c>from</c></tag>
- <item>Time unit of <c>val</c>.</item>
- <tag><c>to</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>Converts the <c>val</c> value of time unit <c>from</c> to
- the corresponding value of time unit <c>to</c>. The result is
- rounded using the floor function.</p>
- <p>Returns <c>ERL_DRV_TIME_ERROR</c> if called with an invalid
- time unit argument.</p>
- <p>See also:</p>
- <list>
- <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item>
- <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item>
- </list>
- </desc>
- </func>
-
</funcs>
+
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="driver_entry">driver_entry(3)</seealso>,
- <seealso marker="kernel:erl_ddll">erl_ddll(3)</seealso>,
- <seealso marker="erlang">erlang(3)</seealso></p>
- <p>An Alternative Distribution Driver (ERTS User's
- Guide Ch. 3)</p>
+ <title>See Also</title>
+ <p><seealso marker="driver_entry"><c>driver_entry(3)</c></seealso>,
+ <seealso marker="erlang"><c>erlang(3)</c></seealso>,
+ <seealso marker="kernel:erl_ddll"><c>erl_ddll(3)</c></seealso>,
+ section <seealso marker="alt_dist">How to Implement an Alternative
+ Carrier for the Erlang Distribution></seealso> in the User's Guide</p>
</section>
</cref>
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index 2ac974f497..4f799f8f34 100644
--- a/erts/doc/src/erl_ext_dist.xml
+++ b/erts/doc/src/erl_ext_dist.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2015</year>
+ <year>2016</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -35,135 +35,130 @@
<section>
<title>Introduction</title>
<p>
- The external term format is mainly used in the distribution
+ The external term format is mainly used in the distribution
mechanism of Erlang.
</p>
<p>
- Since Erlang has a fixed number of types, there is no need for a
- programmer to define a specification for the external format used
+ As Erlang has a fixed number of types, there is no need for a
+ programmer to define a specification for the external format used
within some application.
- All Erlang terms has an external representation and the interpretation
- of the different terms are application specific.
+ All Erlang terms have an external representation and the interpretation
+ of the different terms is application-specific.
</p>
<p>
- In Erlang the BIF <seealso marker="erts:erlang#term_to_binary/1">term_to_binary/1,2</seealso> is used to convert a
- term into the external format.
- To convert binary data encoding a term the BIF
+ In Erlang the BIF <seealso marker="erts:erlang#term_to_binary/1">
+ <c>erlang:term_to_binary/1,2</c></seealso> is used to convert a
+ term into the external format.
+ To convert binary data encoding to a term, the BIF
<seealso marker="erts:erlang#binary_to_term/1">
- binary_to_term/1
- </seealso>
- is used.
+ <c>erlang:binary_to_term/1</c>c></seealso> is used.
</p>
<p>
- The distribution does this implicitly when sending messages across
+ The distribution does this implicitly when sending messages across
node boundaries.
</p>
<marker id="overall_format"/>
<p>
- The overall format of the term format is:
+ The overall format of the term format is as follows:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">N</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">N</cell>
</row>
- <row>
- <cell align="center"><c>131</c></cell>
- <cell align="center"><c>Tag</c></cell>
- <cell align="center"><c>Data</c></cell>
- </row>
- <tcaption></tcaption></table>
+ <row>
+ <cell align="center"><c>131</c></cell>
+ <cell align="center"><c>Tag</c></cell>
+ <cell align="center"><c>Data</c></cell>
+ </row>
+ <tcaption>Term Format</tcaption></table>
<note>
- <p>
- When messages are
- <seealso marker="erl_dist_protocol#connected_nodes">passed between
- connected nodes</seealso> and a
- <seealso marker="#distribution_header">distribution
- header</seealso> is used, the first byte containing the version
- number (131) is omitted from the terms that follow the distribution
- header. This since
- the version number is implied by the version number in the
- distribution header.
- </p>
+ <p>
+ When messages are
+ <seealso marker="erl_dist_protocol#connected_nodes">passed between
+ connected nodes</seealso> and a
+ <seealso marker="#distribution_header">distribution
+ header</seealso> is used, the first byte containing the version
+ number (131) is omitted from the terms that follow the distribution
+ header. This is because the version number is implied by the version
+ number in the distribution header.
+ </p>
</note>
<p>
- A compressed term looks like this:
+ The compressed term format is as follows:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">4</cell>
- <cell align="center">N</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
+ <cell align="center">N</cell>
</row>
<row>
- <cell align="center">131</cell>
- <cell align="center">80</cell>
- <cell align="center">UncompressedSize</cell>
- <cell align="center">Zlib-compressedData</cell>
+ <cell align="center"><c>131</c></cell>
+ <cell align="center"><c>80</c></cell>
+ <cell align="center"><c>UncompressedSize</c></cell>
+ <cell align="center"><c>Zlib-compressedData</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>Compressed Term Format</tcaption></table>
<p>
- Uncompressed Size (unsigned 32 bit integer in big-endian byte order)
+ Uncompressed size (unsigned 32-bit integer in big-endian byte order)
is the size of the data before it was compressed.
- The compressed data has the following format when it has been
- expanded:
+ The compressed data has the following format when it has been expanded:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">Uncompressed Size</cell>
+ <cell align="center">1</cell>
+ <cell align="center">Uncompressed Size</cell>
</row>
<row>
- <cell align="center">Tag</cell>
- <cell align="center">Data</cell>
+ <cell align="center"><c>Tag</c></cell>
+ <cell align="center"><c>Data</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>Compressed Data Format when Expanded</tcaption></table>
<marker id="utf8_atoms"/>
<note>
- <p>As of ERTS version 5.10 (OTP-R16) support
- for UTF-8 encoded atoms has been introduced in the external format.
- However, only characters that can be encoded using Latin1 (ISO-8859-1)
- are currently supported in atoms. The support for UTF-8 encoded atoms
- in the external format has been implemented in order to be able to support
- all Unicode characters in atoms in <em>some future release</em>.
- Until full Unicode support for
- atoms has been introduced, it is an <em>error</em> to pass atoms containing
- characters that cannot be encoded in Latin1, and <em>the behavior is
- undefined</em>.</p>
- <p>When the
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_UTF8_ATOMS</c></seealso>
- distribution flag has been exchanged between both nodes in the
- <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>,
- all atoms in the distribution header will be encoded in UTF-8; otherwise,
- all atoms in the distribution header will be encoded in Latin1. The two
- new tags <seealso marker="#ATOM_UTF8_EXT">ATOM_UTF8_EXT</seealso>, and
- <seealso marker="#SMALL_ATOM_UTF8_EXT">SMALL_ATOM_UTF8_EXT</seealso>
- will only be used if the <c>DFLAG_UTF8_ATOMS</c> distribution flag has
- been exchanged between nodes, or if an atom containing characters
- that cannot be encoded in Latin1 is encountered.
- </p>
- <p>The maximum number of allowed characters in an atom is 255. In the
- UTF-8 case each character may need 4 bytes to be encoded.
- </p>
+ <p>As from ERTS 5.10 (OTP R16) support
+ for UTF-8 encoded atoms has been introduced in the external format.
+ However, only characters that can be encoded using Latin-1 (ISO-8859-1)
+ are currently supported in atoms. The support for UTF-8 encoded atoms
+ in the external format has been implemented to be able to support
+ all Unicode characters in atoms in <em>some future release</em>.
+ Until full Unicode support for atoms has been introduced,
+ it is an <em>error</em> to pass atoms containing
+ characters that cannot be encoded in Latin-1, and <em>the behavior is
+ undefined</em>.</p>
+ <p>When distribution flag <seealso marker="erl_dist_protocol#dflags">
+ <c>DFLAG_UTF8_ATOMS</c></seealso> has been exchanged between both nodes
+ in the <seealso marker="erl_dist_protocol#distribution_handshake">
+ distribution handshake</seealso>, all atoms in the distribution header
+ are encoded in UTF-8, otherwise in Latin-1. The two
+ new tags <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso>
+ and <seealso marker="#SMALL_ATOM_UTF8_EXT">
+ <c>SMALL_ATOM_UTF8_EXT</c></seealso>
+ are only used if the distribution flag <c>DFLAG_UTF8_ATOMS</c> has
+ been exchanged between nodes, or if an atom containing characters
+ that cannot be encoded in Latin-1 is encountered.</p>
+ <p>The maximum number of allowed characters in an atom is 255. In the
+ UTF-8 case, each character can need 4 bytes to be encoded.</p>
</note>
</section>
<section>
- <title>Distribution header</title>
+ <title>Distribution Header</title>
<p>
<marker id="distribution_header"/>
- As of erts version 5.7.2 the old atom cache protocol was
- dropped and a new one was introduced. This atom cache protocol
- introduced the distribution header. Nodes with erts versions
+ As from ERTS 5.7.2 the old atom cache protocol was
+ dropped and a new one was introduced. This protocol
+ introduced the distribution header. Nodes with an ERTS version
earlier than 5.7.2 can still communicate with new nodes,
- but no distribution header and no atom cache will be used.</p>
+ but no distribution header and no atom cache are used.</p>
<p>
- The distribution header currently only contains an atom cache
- reference section, but could in the future contain more
+ The distribution header only contains an atom cache
+ reference section, but can in the future contain more
information. The distribution header precedes one or more Erlang
- terms on the external format. For more information see the
+ terms on the external format. For more information, see the
documentation of the
<seealso marker="erl_dist_protocol#connected_nodes">protocol between
connected nodes</seealso> in the
@@ -173,37 +168,37 @@
<p>
<seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>
entries with corresponding <c>AtomCacheReferenceIndex</c> in terms
- encoded on the external format following a distribution header refers
+ encoded on the external format following a distribution header refer
to the atom cache references made in the distribution header. The range
- is 0 &lt;= <c>AtomCacheReferenceIndex</c> &lt; 255, i.e., at most 255
+ is 0 &lt;= <c>AtomCacheReferenceIndex</c> &lt; 255, that is, at most 255
different atom cache references from the following terms can be made.
</p>
<p>
- The distribution header format is:
+ The distribution header format is as follows:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
- <cell align="center">NumberOfAtomCacheRefs/2+1 | 0</cell>
- <cell align="center">N | 0</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">NumberOfAtomCacheRefs/2+1 | 0</cell>
+ <cell align="center">N | 0</cell>
</row>
<row>
- <cell align="center"><c>131</c></cell>
- <cell align="center"><c>68</c></cell>
- <cell align="center"><c>NumberOfAtomCacheRefs</c></cell>
- <cell align="center"><c>Flags</c></cell>
- <cell align="center"><c>AtomCacheRefs</c></cell>
+ <cell align="center"><c>131</c></cell>
+ <cell align="center"><c>68</c></cell>
+ <cell align="center"><c>NumberOfAtomCacheRefs</c></cell>
+ <cell align="center"><c>Flags</c></cell>
+ <cell align="center"><c>AtomCacheRefs</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>Distribution Header Format</tcaption></table>
<p>
- <c>Flags</c> consists of <c>NumberOfAtomCacheRefs/2+1</c> bytes,
+ <c>Flags</c> consist of <c>NumberOfAtomCacheRefs/2+1</c> bytes,
unless <c>NumberOfAtomCacheRefs</c> is <c>0</c>. If
<c>NumberOfAtomCacheRefs</c> is <c>0</c>, <c>Flags</c> and
- <c>AtomCacheRefs</c> are omitted. Each atom cache reference have
+ <c>AtomCacheRefs</c> are omitted. Each atom cache reference has
a half byte flag field. Flags corresponding to a specific
- <c>AtomCacheReferenceIndex</c>, are located in flag byte number
+ <c>AtomCacheReferenceIndex</c> are located in flag byte number
<c>AtomCacheReferenceIndex/2</c>. Flag byte 0 is the first byte
after the <c>NumberOfAtomCacheRefs</c> byte. Flags for an even
<c>AtomCacheReferenceIndex</c> are located in the least significant
@@ -216,95 +211,97 @@
</p>
<table align="left">
<row>
- <cell align="center">1 bit</cell>
- <cell align="center">3 bits</cell>
+ <cell align="center">1 bit</cell>
+ <cell align="center">3 bits</cell>
</row>
<row>
- <cell align="center"><c>NewCacheEntryFlag</c></cell>
- <cell align="center"><c>SegmentIndex</c></cell>
+ <cell align="center"><c>NewCacheEntryFlag</c></cell>
+ <cell align="center"><c>SegmentIndex</c></cell>
</row>
<tcaption></tcaption></table>
<p>
The most significant bit is the <c>NewCacheEntryFlag</c>. If set,
the corresponding cache reference is new. The three least
significant bits are the <c>SegmentIndex</c> of the corresponding
- atom cache entry. An atom cache consists of 8 segments each of size
- 256, i.e., an atom cache can contain 2048 entries.
+ atom cache entry. An atom cache consists of 8 segments, each of size
+ 256, that is, an atom cache can contain 2048 entries.
</p>
<p>
After flag fields for atom cache references, another half byte flag
- field is located which has the following format:
+ field is located with the following format:
</p>
<table align="left">
<row>
- <cell align="center">3 bits</cell>
- <cell align="center">1 bit</cell>
+ <cell align="center">3 bits</cell>
+ <cell align="center">1 bit</cell>
</row>
<row>
- <cell align="center"><c>CurrentlyUnused</c></cell>
- <cell align="center"><c>LongAtoms</c></cell>
+ <cell align="center"><c>CurrentlyUnused</c></cell>
+ <cell align="center"><c>LongAtoms</c></cell>
</row>
<tcaption></tcaption></table>
<p>
- The least significant bit in that half byte is the <c>LongAtoms</c>
- flag. If it is set, 2 bytes are used for atom lengths instead of
+ The least significant bit in that half byte is flag <c>LongAtoms</c>.
+ If it is set, 2 bytes are used for atom lengths instead of
1 byte in the distribution header.
</p>
<p>
After the <c>Flags</c> field follow the <c>AtomCacheRefs</c>. The
first <c>AtomCacheRef</c> is the one corresponding to
- <c>AtomCacheReferenceIndex</c> 0. Higher indices follows
+ <c>AtomCacheReferenceIndex</c> 0. Higher indices follow
in sequence up to index <c>NumberOfAtomCacheRefs - 1</c>.
</p>
<p>
If the <c>NewCacheEntryFlag</c> for the next <c>AtomCacheRef</c> has
- been set, a <c>NewAtomCacheRef</c> on the following format will follow:
+ been set, a <c>NewAtomCacheRef</c> on the following format follows:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">1 | 2</cell>
- <cell align="center">Length</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1 | 2</cell>
+ <cell align="center">Length</cell>
</row>
<row>
- <cell align="center"><c>InternalSegmentIndex</c></cell>
- <cell align="center"><c>Length</c></cell>
- <cell align="center"><c>AtomText</c></cell>
+ <cell align="center"><c>InternalSegmentIndex</c></cell>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>AtomText</c></cell>
</row>
<tcaption></tcaption></table>
<p>
<c>InternalSegmentIndex</c> together with the <c>SegmentIndex</c>
completely identify the location of an atom cache entry in the
- atom cache. <c>Length</c> is number of bytes that <c>AtomText</c>
- consists of. Length is a two byte big endian integer
- if the <c>LongAtoms</c> flag has been set, otherwise a one byte
- integer. When the
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_UTF8_ATOMS</c></seealso>
- distribution flag has been exchanged between both nodes in the
- <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>,
- characters in <c>AtomText</c> is encoded in UTF-8; otherwise,
- encoded in Latin1. Subsequent <c>CachedAtomRef</c>s with the same
+ atom cache. <c>Length</c> is the number of bytes that <c>AtomText</c>
+ consists of. Length is a 2 byte big-endian integer
+ if flag <c>LongAtoms</c> has been set, otherwise a 1 byte
+ integer. When distribution flag
+ <seealso marker="erl_dist_protocol#dflags">
+ <c>DFLAG_UTF8_ATOMS</c></seealso>
+ has been exchanged between both nodes in the
+ <seealso marker="erl_dist_protocol#distribution_handshake">
+ distribution handshake</seealso>,
+ characters in <c>AtomText</c> are encoded in UTF-8, otherwise
+ in Latin-1. The following <c>CachedAtomRef</c>s with the same
<c>SegmentIndex</c> and <c>InternalSegmentIndex</c> as this
- <c>NewAtomCacheRef</c> will refer to this atom until a new
+ <c>NewAtomCacheRef</c> refer to this atom until a new
<c>NewAtomCacheRef</c> with the same <c>SegmentIndex</c>
and <c>InternalSegmentIndex</c> appear.
</p>
<p>
- For more information on encoding of atoms, see
+ For more information on encoding of atoms, see the
<seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso>
- in the beginning of this document.
+ in the beginning of this section.
</p>
<p>
If the <c>NewCacheEntryFlag</c> for the next <c>AtomCacheRef</c>
has not been set, a <c>CachedAtomRef</c> on the following format
- will follow:
+ follows:
</p>
<table align="left">
<row>
- <cell align="center">1</cell>
+ <cell align="center">1</cell>
</row>
<row>
- <cell align="center"><c>InternalSegmentIndex</c></cell>
+ <cell align="center"><c>InternalSegmentIndex</c></cell>
</row>
<tcaption></tcaption></table>
<p>
@@ -319,157 +316,147 @@
<section>
<marker id="ATOM_CACHE_REF"/>
<title>ATOM_CACHE_REF</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
- <cell align="center">1</cell>
+ <cell align="center">1</cell>
</row>
<row>
- <cell align="center"><c>82</c></cell>
- <cell align="center"><c>AtomCacheReferenceIndex</c></cell>
+ <cell align="center"><c>82</c></cell>
+ <cell align="center"><c>AtomCacheReferenceIndex</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>ATOM_CACHE_REF</tcaption></table>
<p>
Refers to the atom with <c>AtomCacheReferenceIndex</c> in the
- <seealso marker="#distribution_header">distribution header</seealso>.
+ <seealso marker="#distribution_header">distribution header</seealso>.
</p>
</section>
<section>
<marker id="SMALL_INTEGER_EXT"/>
<title>SMALL_INTEGER_EXT</title>
-
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">1</cell>
+ <cell align="center">1</cell>
+ <cell align="center">1</cell>
</row>
<row>
- <cell align="center">97</cell>
- <cell align="center">Int</cell>
+ <cell align="center"><c>97</c></cell>
+ <cell align="center"><c>Int</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>SMALL_INTEGER_EXT</tcaption></table>
<p>
- Unsigned 8 bit integer.
+ Unsigned 8-bit integer.
</p>
</section>
<section>
<marker id="INTEGER_EXT"/>
<title>INTEGER_EXT</title>
-
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">4</cell>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
</row>
<row>
- <cell align="center">98</cell>
- <cell align="center">Int</cell>
+ <cell align="center"><c>98</c></cell>
+ <cell align="center"><c>Int</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>INTEGER_EXT</tcaption></table>
<p>
- Signed 32 bit integer in big-endian format (i.e. MSB first)
+ Signed 32-bit integer in big-endian format.
</p>
</section>
<section>
<marker id="FLOAT_EXT"/>
<title>FLOAT_EXT</title>
-
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">31</cell>
+ <cell align="center">1</cell>
+ <cell align="center">31</cell>
</row>
<row>
- <cell align="center">99</cell>
- <cell align="center">Float String</cell>
+ <cell align="center"><c>99</c></cell>
+ <cell align="center"><c>Float string</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>FLOAT_EXT</tcaption></table>
<p>
- A float is stored in string format. the format used in sprintf to
- format the float is "%.20e"
+ A float is stored in string format. The format used in sprintf to
+ format the float is "%.20e"
(there are more bytes allocated than necessary).
- To unpack the float use sscanf with format "%lf".
+ To unpack the float, use sscanf with format "%lf".
</p>
<p>
- This term is used in minor version 0 of the external format;
- it has been superseded by
- <seealso marker="#NEW_FLOAT_EXT">
- NEW_FLOAT_EXT
- </seealso>.
+ This term is used in minor version 0 of the external format;
+ it has been superseded by
+ <seealso marker="#NEW_FLOAT_EXT"><c>NEW_FLOAT_EXT</c></seealso>.
</p>
</section>
<section>
<marker id="ATOM_EXT"/>
<title>ATOM_EXT</title>
-
<table align="left">
<row>
- <cell align="center">1</cell>
- <cell align="center">2</cell>
- <cell align="center">Len</cell>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">Len</cell>
</row>
<row>
- <cell align="center"><c>100</c></cell>
- <cell align="center"><c>Len</c></cell>
- <cell align="center"><c>AtomName</c></cell>
+ <cell align="center"><c>100</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>AtomName</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>ATOM_EXT</tcaption></table>
<p>
- An atom is stored with a 2 byte unsigned length in big-endian order,
- followed by <c>Len</c> numbers of 8 bit Latin1 characters that forms
- the <c>AtomName</c>.
- <em>Note</em>: The maximum allowed value for <c>Len</c> is 255.
+ An atom is stored with a 2 byte unsigned length in big-endian order,
+ followed by <c>Len</c> numbers of 8-bit Latin-1 characters that forms
+ the <c>AtomName</c>. The maximum allowed value for <c>Len</c> is 255.
</p>
</section>
<section>
<marker id="REFERENCE_EXT"/>
<title>REFERENCE_EXT</title>
-
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">N</cell>
- <cell align="center">4</cell>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center"><c>101</c></cell>
- <cell align="center"><c>Node</c></cell>
- <cell align="center"><c>ID</c></cell>
- <cell align="center"><c>Creation</c></cell>
- </row>
- <tcaption></tcaption></table>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">N</cell>
+ <cell align="center">4</cell>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>101</c></cell>
+ <cell align="center"><c>Node</c></cell>
+ <cell align="center"><c>ID</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>REFERENCE_EXT</tcaption></table>
<p>
- Encode a reference object (an object generated with <c>make_ref/0</c>).
- The <c>Node</c> term is an encoded atom, i.e.
- <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>,
- <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso> or
- <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>.
- The <c>ID</c> field contains a big-endian
- unsigned integer,
- but <em>should be regarded as uninterpreted data</em>
- since this field is node specific.
- <c>Creation</c> is a byte containing a node serial number that
- makes it possible to separate old (crashed) nodes from a new one.
+ Encodes a reference object (an object generated with
+ <seealso marker="erlang:make_ref/0">erlang:make_ref/0</seealso>).
+ The <c>Node</c> term is an encoded atom, that is,
+ <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>, or
+ <seealso marker="#ATOM_CACHE_REF"><c>ATOM_CACHE_REF</c></seealso>.
+ The <c>ID</c> field contains a big-endian unsigned integer,
+ but <em>is to be regarded as uninterpreted data</em>,
+ as this field is node-specific.
+ <c>Creation</c> is a byte containing a node serial number, which
+ makes it possible to separate old (crashed) nodes from a new one.
</p>
<p>
- In <c>ID</c>, only 18 bits are significant; the rest should be 0.
- In <c>Creation</c>, only 2 bits are significant; the rest should be 0.
-
- See <seealso marker="#NEW_REFERENCE_EXT">NEW_REFERENCE_EXT</seealso>.
+ In <c>ID</c>, only 18 bits are significant; the rest are to be 0.
+ In <c>Creation</c>, only two bits are significant; the rest are to be 0.
+ See <seealso marker="#NEW_REFERENCE_EXT">
+ <c>NEW_REFERENCE_EXT</c></seealso>.
</p>
</section>
<section>
<marker id="PORT_EXT"/>
<title>PORT_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -483,20 +470,21 @@
<cell align="center"><c>ID</c></cell>
<cell align="center"><c>Creation</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>PORT_EXT</tcaption></table>
<p>
- Encode a port object (obtained form <c>open_port/2</c>).
- The <c>ID</c> is a node specific identifier for a local port.
+ Encodes a port object (obtained from
+ <seealso marker="erlang:open_port/2">
+ <c>erlang:open_port/2</c></seealso>).
+ The <c>ID</c> is a node-specific identifier for a local port.
Port operations are not allowed across node boundaries.
The <c>Creation</c> works just like in
- <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>.
+ <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>.
</p>
</section>
<section>
<marker id="PID_EXT"/>
<title>PID_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -512,23 +500,20 @@
<cell align="center"><c>Serial</c></cell>
<cell align="center"><c>Creation</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>PID_EXT</tcaption></table>
<p>
- Encode a process identifier object (obtained from <c>spawn/3</c> or
- friends).
- The <c>ID</c> and <c>Creation</c> fields works just like in
- <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>, while
- the <c>Serial</c> field is used to improve safety.
-
- In <c>ID</c>, only 15 bits are significant; the rest should be 0.
+ Encodes a process identifier object (obtained from
+ <seealso marker="erlang:spawn/3"><c>erlang:spawn/3</c></seealso> or
+ friends). The <c>ID</c> and <c>Creation</c> fields works just like in
+ <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>, while
+ the <c>Serial</c> field is used to improve safety.
+ In <c>ID</c>, only 15 bits are significant; the rest are to be 0.
</p>
-
</section>
<section>
<marker id="SMALL_TUPLE_EXT"/>
<title>SMALL_TUPLE_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -536,22 +521,21 @@
<cell align="center">N</cell>
</row>
<row>
- <cell align="center">104</cell>
- <cell align="center">Arity</cell>
- <cell align="center">Elements</cell>
+ <cell align="center"><c>104</c></cell>
+ <cell align="center"><c>Arity</c></cell>
+ <cell align="center"><c>Elements</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>SMALL_TUPLE_EXT</tcaption></table>
<p>
- <c>SMALL_TUPLE_EXT</c> encodes a tuple. The <c>Arity</c>
- field is an unsigned byte that determines how many element
- that follows in the <c>Elements</c> section.
+ Encodes a tuple. The <c>Arity</c>
+ field is an unsigned byte that determines how many elements
+ that follows in section <c>Elements</c>.
</p>
</section>
<section>
<marker id="LARGE_TUPLE_EXT"/>
<title>LARGE_TUPLE_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -559,23 +543,22 @@
<cell align="center">N</cell>
</row>
<row>
- <cell align="center">105</cell>
- <cell align="center">Arity</cell>
- <cell align="center">Elements</cell>
+ <cell align="center"><c>105</c></cell>
+ <cell align="center"><c>Arity</c></cell>
+ <cell align="center"><c>Elements</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>LARGE_TUPLE_EXT</tcaption></table>
<p>
- Same as
- <seealso marker="#SMALL_TUPLE_EXT">SMALL_TUPLE_EXT</seealso>
- with the exception that <c>Arity</c> is an
- unsigned 4 byte integer in big endian format.
+ Same as
+ <seealso marker="#SMALL_TUPLE_EXT"><c>SMALL_TUPLE_EXT</c></seealso>
+ except that <c>Arity</c> is an
+ unsigned 4 byte integer in big-endian format.
</p>
</section>
<section>
<marker id="MAP_EXT"/>
<title>MAP_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -583,43 +566,42 @@
<cell align="center">N</cell>
</row>
<row>
- <cell align="center">116</cell>
- <cell align="center">Arity</cell>
- <cell align="center">Pairs</cell>
+ <cell align="center"><c>116</c></cell>
+ <cell align="center"><c>Arity</c></cell>
+ <cell align="center"><c>Pairs</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>MAP_EXT</tcaption></table>
<p>
- <c>MAP_EXT</c> encodes a map. The <c>Arity</c> field is an unsigned
- 4 byte integer in big endian format that determines the number of
+ Encodes a map. The <c>Arity</c> field is an unsigned
+ 4 byte integer in big-endian format that determines the number of
key-value pairs in the map. Key and value pairs (<c>Ki => Vi</c>)
- are encoded in the <c>Pairs</c> section in the following order:
+ are encoded in section <c>Pairs</c> in the following order:
<c>K1, V1, K2, V2,..., Kn, Vn</c>.
Duplicate keys are <em>not allowed</em> within the same map.
</p>
- <p><em>Since: </em>OTP 17.0</p>
+ <p><em>As from </em>Erlang/OTP 17.0</p>
</section>
<section>
<marker id="NIL_EXT"/>
<title>NIL_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
</row>
<row>
- <cell align="center">106</cell>
+ <cell align="center"><c>106</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>NIL_EXT</tcaption></table>
<p>
- The representation for an empty list, i.e. the Erlang syntax <c>[]</c>.
+ The representation for an empty list, that is, the Erlang syntax
+ <c>[]</c>.
</p>
</section>
<section>
<marker id="STRING_EXT"/>
<title>STRING_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -627,27 +609,25 @@
<cell align="center">Len</cell>
</row>
<row>
- <cell align="center">107</cell>
- <cell align="center">Length</cell>
- <cell align="center">Characters</cell>
+ <cell align="center"><c>107</c></cell>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>Characters</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>STRING_EXT</tcaption></table>
<p>
- String does NOT have a corresponding Erlang representation,
+ String does <em>not</em> have a corresponding Erlang representation,
but is an optimization for sending lists of bytes (integer in
the range 0-255) more efficiently over the distribution.
- Since the <c>Length</c> field is an unsigned 2 byte integer
- (big endian), implementations must make sure that lists longer than
- 65535 elements are encoded as
- <seealso marker="#LIST_EXT">LIST_EXT</seealso>.
+ As field <c>Length</c> is an unsigned 2 byte integer
+ (big-endian), implementations must ensure that lists longer than
+ 65535 elements are encoded as
+ <seealso marker="#LIST_EXT"><c>LIST_EXT</c></seealso>.
</p>
-
</section>
<section>
<marker id="LIST_EXT"/>
<title>LIST_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -656,27 +636,24 @@
<cell align="center">&nbsp;</cell>
</row>
<row>
- <cell align="center">108</cell>
- <cell align="center">Length</cell>
- <cell align="center">Elements</cell>
- <cell align="center">Tail</cell>
+ <cell align="center"><c>108</c></cell>
+ <cell align="center"><c>Length</c></cell>
+ <cell align="center"><c>Elements</c></cell>
+ <cell align="center"><c>Tail</c></cell>
</row>
- <tcaption></tcaption></table>
-
+ <tcaption>LIST_EXT</tcaption></table>
<p>
- <c>Length</c> is the number of elements that follows in the
- <c>Elements</c> section. <c>Tail</c> is the final tail of
- the list; it is
- <seealso marker="#NIL_EXT">NIL_EXT</seealso>
- for a proper list, but may be anything type if the list is
- improper (for instance <c>[a|b]</c>).
+ <c>Length</c> is the number of elements that follows in section
+ <c>Elements</c>. <c>Tail</c> is the final tail of the list; it is
+ <seealso marker="#NIL_EXT"><c>NIL_EXT</c></seealso>
+ for a proper list, but can be any type if the list is
+ improper (for example, <c>[a|b]</c>).
</p>
</section>
<section>
<marker id="BINARY_EXT"/>
<title>BINARY_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -684,25 +661,26 @@
<cell align="center">Len</cell>
</row>
<row>
- <cell align="center">109</cell>
- <cell align="center">Len</cell>
- <cell align="center">Data</cell>
+ <cell align="center"><c>109</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>Data</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>BINARY_EXT</tcaption></table>
<p>
Binaries are generated with bit syntax expression or with
- <seealso marker="erts:erlang#list_to_binary/1">list_to_binary/1</seealso>,
- <seealso marker="erts:erlang#term_to_binary/1">term_to_binary/1</seealso>,
+ <seealso marker="erts:erlang#list_to_binary/1">
+ <c>erlang:list_to_binary/1</c></seealso>,
+ <seealso marker="erts:erlang#term_to_binary/1">
+ <c>erlang:term_to_binary/1</c></seealso>,
or as input from binary ports.
- The <c>Len</c> length field is an unsigned 4 byte integer
- (big endian).
+ The <c>Len</c> length field is an unsigned 4 byte integer
+ (big-endian).
</p>
</section>
<section>
<marker id="SMALL_BIG_EXT"/>
<title>SMALL_BIG_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -711,27 +689,26 @@
<cell align="center">n</cell>
</row>
<row>
- <cell align="center">110</cell>
- <cell align="center">n</cell>
- <cell align="center">Sign</cell>
- <cell align="center">d(0) ... d(n-1)</cell>
+ <cell align="center"><c>110</c></cell>
+ <cell align="center"><c>n</c></cell>
+ <cell align="center"><c>Sign</c></cell>
+ <cell align="center"><c>d(0)</c> ... <c>d(n-1)</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>SMALL_BIG_EXT</tcaption></table>
<p>
- Bignums are stored in unary form with a <c>Sign</c> byte
- that is 0 if the binum is positive and 1 if is negative. The
- digits are stored with the LSB byte stored first. To
- calculate the integer the following formula can be used:<br/>
-
- B = 256<br/>
- (d0*B^0 + d1*B^1 + d2*B^2 + ... d(N-1)*B^(n-1))
+ Bignums are stored in unary form with a <c>Sign</c> byte,
+ that is, 0 if the binum is positive and 1 if it is negative. The
+ digits are stored with the least significant byte stored first. To
+ calculate the integer, the following formula can be used:
+ </p>
+ <p><c>B</c> = 256<br/>
+ <c>(d0*B^0 + d1*B^1 + d2*B^2 + ... d(N-1)*B^(n-1))</c>
</p>
</section>
<section>
<marker id="LARGE_BIG_EXT"/>
<title>LARGE_BIG_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -740,24 +717,22 @@
<cell align="center">n</cell>
</row>
<row>
- <cell align="center">111</cell>
- <cell align="center">n</cell>
- <cell align="center">Sign</cell>
- <cell align="center">d(0) ... d(n-1)</cell>
+ <cell align="center"><c>111</c></cell>
+ <cell align="center"><c>n</c></cell>
+ <cell align="center"><c>Sign</c></cell>
+ <cell align="center"><c>d(0)</c> ... <c>d(n-1)</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>LARGE_BIG_EXT</tcaption></table>
<p>
- Same as <seealso marker="#SMALL_BIG_EXT">SMALL_BIG_EXT</seealso>
- with the difference that the length field
- is an unsigned 4 byte integer.
+ Same as <seealso marker="#SMALL_BIG_EXT">
+ <c>SMALL_BIG_EXT</c></seealso>
+ except that the length field is an unsigned 4 byte integer.
</p>
-
</section>
<section>
<marker id="NEW_REFERENCE_EXT"/>
<title>NEW_REFERENCE_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -767,44 +742,43 @@
<cell align="center">N'</cell>
</row>
<row>
- <cell align="center">114</cell>
- <cell align="center">Len</cell>
- <cell align="center">Node</cell>
- <cell align="center">Creation</cell>
- <cell align="center">ID ...</cell>
+ <cell align="center"><c>114</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>Node</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ <cell align="center"><c>ID ...</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>NEW_REFERENCE_EXT</tcaption></table>
<p>
- Node and Creation are as in
- <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>.
+ <c>Node</c> and <c>Creation</c> are as in
+ <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>.
</p>
<p>
- <c>ID</c> contains a sequence of big-endian unsigned integers
- (4 bytes each, so <c>N'</c> is a multiple of 4),
- but should be regarded as uninterpreted data.
+ <c>ID</c> contains a sequence of big-endian unsigned integers
+ (4 bytes each, so <c>N'</c> is a multiple of 4),
+ but is to be regarded as uninterpreted data.
</p>
<p>
<c>N'</c> = 4 * <c>Len</c>.
</p>
<p>
- In the first word (four bytes) of <c>ID</c>, only 18 bits are
- significant, the rest should be 0.
- In <c>Creation</c>, only 2 bits are significant,
- the rest should be 0.
+ In the first word (4 bytes) of <c>ID</c>, only 18 bits are
+ significant, the rest are to be 0.
+ In <c>Creation</c>, only two bits are significant,
+ the rest are to be 0.
</p>
<p>
- NEW_REFERENCE_EXT was introduced with distribution version 4.
- In version 4, <c>N'</c> should be at most 12.
+ <c>NEW_REFERENCE_EXT</c> was introduced with distribution version 4.
+ In version 4, <c>N'</c> is to be at most 12.
</p>
<p>
- See <seealso marker="#REFERENCE_EXT">REFERENCE_EXT</seealso>).
+ See <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>.
</p>
</section>
<section>
<marker id="SMALL_ATOM_EXT"/>
<title>SMALL_ATOM_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -816,24 +790,28 @@
<cell align="center"><c>Len</c></cell>
<cell align="center"><c>AtomName</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>SMALL_ATOM_EXT</tcaption></table>
<p>
- An atom is stored with a 1 byte unsigned length,
- followed by <c>Len</c> numbers of 8 bit Latin1 characters that
+ An atom is stored with a 1 byte unsigned length,
+ followed by <c>Len</c> numbers of 8-bit Latin-1 characters that
forms the <c>AtomName</c>. Longer atoms can be represented
- by <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>. <em>Note</em>
- the <c>SMALL_ATOM_EXT</c> was introduced in erts version 5.7.2 and
- require an exchange of the
- <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_SMALL_ATOM_TAGS</c></seealso>
- distribution flag in the
- <seealso marker="erl_dist_protocol#distribution_handshake">distribution handshake</seealso>.
+ by <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>.
</p>
+ <note>
+ <p>
+ <c>SMALL_ATOM_EXT</c> was introduced in ERTS 5.7.2 and
+ require an exchange of distribution flag
+ <seealso marker="erl_dist_protocol#dflags">
+ <c>DFLAG_SMALL_ATOM_TAGS</c></seealso> in the
+ <seealso marker="erl_dist_protocol#distribution_handshake">
+ distribution handshake</seealso>.
+ </p>
+ </note>
</section>
<section>
<marker id="FUN_EXT"/>
<title>FUN_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -845,48 +823,56 @@
<cell align="center">N5</cell>
</row>
<row>
- <cell align="center">117</cell>
- <cell align="center">NumFree</cell>
- <cell align="center">Pid</cell>
- <cell align="center">Module</cell>
- <cell align="center">Index</cell>
- <cell align="center">Uniq</cell>
- <cell align="center">Free vars ...</cell>
+ <cell align="center"><c>117</c></cell>
+ <cell align="center"><c>NumFree</c></cell>
+ <cell align="center"><c>Pid</c></cell>
+ <cell align="center"><c>Module</c></cell>
+ <cell align="center"><c>Index</c></cell>
+ <cell align="center"><c>Uniq</c></cell>
+ <cell align="center"><c>Free vars ...</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>FUN_EXT</tcaption></table>
<taglist>
- <tag><c>Pid</c></tag>
+ <tag><c>Pid</c></tag>
<item>
- is a process identifier as in
- <seealso marker="#PID_EXT">PID_EXT</seealso>.
- It represents the process in which the fun was created.
+ <p>A process identifier as in
+ <seealso marker="#PID_EXT"><c>PID_EXT</c></seealso>.
+ Represents the process in which the fun was created.
+ </p>
</item>
<tag><c>Module</c></tag>
<item>
- is an encoded as an atom, using
- <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>,
- <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso>
- or <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>.
- This is the module that the fun is implemented in.
+ <p>Encoded as an atom, using
+ <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>,
+ or <seealso marker="#ATOM_CACHE_REF">
+ <c>ATOM_CACHE_REF</c></seealso>.
+ This is the module that the fun is implemented in.
+ </p>
</item>
<tag><c>Index</c></tag>
<item>
- is an integer encoded using
- <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso>
- or <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>.
- It is typically a small index into the module's fun table.
+ <p>An integer encoded using
+ <seealso marker="#SMALL_INTEGER_EXT">
+ <c>SMALL_INTEGER_EXT</c></seealso>
+ or <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>.
+ It is typically a small index into the module's fun table.
+ </p>
</item>
<tag><c>Uniq</c></tag>
<item>
- is an integer encoded using
- <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso> or
- <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>.
- <c>Uniq</c> is the hash value of the parse for the fun.
+ <p>An integer encoded using
+ <seealso marker="#SMALL_INTEGER_EXT">
+ <c>SMALL_INTEGER_EXT</c></seealso> or
+ <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>.
+ <c>Uniq</c> is the hash value of the parse for the fun.
+ </p>
</item>
<tag><c>Free vars</c></tag>
<item>
- is <c>NumFree</c> number of terms, each one encoded according
- to its type.
+ <p><c>NumFree</c> number of terms, each one encoded according
+ to its type.
+ </p>
</item>
</taglist>
</section>
@@ -894,7 +880,6 @@
<section>
<marker id="NEW_FUN_EXT"/>
<title>NEW_FUN_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -910,19 +895,19 @@
<cell align="center">N5</cell>
</row>
<row>
- <cell align="center">112</cell>
- <cell align="center">Size</cell>
- <cell align="center">Arity</cell>
- <cell align="center">Uniq</cell>
- <cell align="center">Index</cell>
- <cell align="center">NumFree</cell>
- <cell align="center">Module</cell>
- <cell align="center">OldIndex</cell>
- <cell align="center">OldUniq</cell>
- <cell align="center">Pid</cell>
- <cell align="center">Free Vars</cell>
+ <cell align="center"><c>112</c></cell>
+ <cell align="center"><c>Size</c></cell>
+ <cell align="center"><c>Arity</c></cell>
+ <cell align="center"><c>Uniq</c></cell>
+ <cell align="center"><c>Index</c></cell>
+ <cell align="center"><c>NumFree</c></cell>
+ <cell align="center"><c>Module</c></cell>
+ <cell align="center"><c>OldIndex</c></cell>
+ <cell align="center"><c>OldUniq</c></cell>
+ <cell align="center"><c>Pid</c></cell>
+ <cell align="center"><c>Free Vars</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>NEW_FUN_EXT</tcaption></table>
<p>
This is the new encoding of internal funs: <c>fun F/A</c> and
<c>fun(Arg1,..) -> ... end</c>.
@@ -930,68 +915,73 @@
<taglist>
<tag><c>Size</c></tag>
<item>
- is the total number of bytes, including the <c>Size</c> field.
+ <p>The total number of bytes, including field <c>Size</c>.</p>
</item>
<tag><c>Arity</c></tag>
<item>
- is the arity of the function implementing the fun.
+ <p>The arity of the function implementing the fun.</p>
</item>
<tag><c>Uniq</c></tag>
<item>
- is the 16 bytes MD5 of the significant parts of the Beam file.
+ <p>The 16 bytes MD5 of the significant parts of the Beam file.</p>
</item>
<tag><c>Index</c></tag>
<item>
- is an index number. Each fun within a module has an unique
- index. <c>Index</c> is stored in big-endian byte order.
+ <p>An index number. Each fun within a module has an unique
+ index. <c>Index</c> is stored in big-endian byte order.
+ </p>
</item>
<tag><c>NumFree</c></tag>
<item>
- is the number of free variables.
+ <p>The number of free variables.</p>
</item>
<tag><c>Module</c></tag>
<item>
- is an encoded as an atom, using
- <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>,
- <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso> or
- <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>.
- This is the module that the fun is implemented in.
+ <p>Encoded as an atom, using
+ <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>,
+ or <seealso marker="#ATOM_CACHE_REF">
+ <c>ATOM_CACHE_REF</c></seealso>.
+ Is the module that the fun is implemented in.
+ </p>
</item>
<tag><c>OldIndex</c></tag>
<item>
- is an integer encoded using
- <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso>
- or <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>.
- It is typically a small index into the module's fun table.
+ <p>An integer encoded using
+ <seealso marker="#SMALL_INTEGER_EXT">
+ <c>SMALL_INTEGER_EXT</c></seealso> or
+ <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>.
+ Is typically a small index into the module's fun table.
+ </p>
</item>
<tag><c>OldUniq</c></tag>
<item>
- is an integer encoded using
- <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso>
- or
- <seealso marker="#INTEGER_EXT">INTEGER_EXT</seealso>.
- <c>Uniq</c> is the hash value of the parse tree for the fun.
+ <p>An integer encoded using
+ <seealso marker="#SMALL_INTEGER_EXT">
+ <c>SMALL_INTEGER_EXT</c></seealso> or
+ <seealso marker="#INTEGER_EXT"><c>INTEGER_EXT</c></seealso>.
+ <c>Uniq</c> is the hash value of the parse tree for the fun.
+ </p>
</item>
<tag><c>Pid</c></tag>
<item>
- is a process identifier as in
- <seealso marker="#PID_EXT">PID_EXT</seealso>.
- It represents the process in which
- the fun was created.
+ <p>A process identifier as in
+ <seealso marker="#PID_EXT"><c>PID_EXT</c></seealso>.
+ Represents the process in which the fun was created.
+ </p>
</item>
-
<tag><c>Free vars</c></tag>
<item>
- is <c>NumFree</c> number of terms, each one encoded according
- to its type.
+ <p><c>NumFree</c> number of terms, each one encoded according
+ to its type.
+ </p>
</item>
</taglist>
</section>
<section>
<marker id="EXPORT_EXT"/>
- <title>EXPORT_EXT</title>
-
+ <title>EXPORT_EXT</title>
<table align="left">
<row>
<cell align="center">1</cell>
@@ -1000,32 +990,31 @@
<cell align="center">N3</cell>
</row>
<row>
- <cell align="center">113</cell>
- <cell align="center">Module</cell>
- <cell align="center">Function</cell>
- <cell align="center">Arity</cell>
+ <cell align="center"><c>113</c></cell>
+ <cell align="center"><c>Module</c></cell>
+ <cell align="center"><c>Function</c></cell>
+ <cell align="center"><c>Arity</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>EXPORT_EXT</tcaption></table>
<p>
This term is the encoding for external funs: <c>fun M:F/A</c>.
</p>
<p>
- <c>Module</c> and <c>Function</c> are atoms
- (encoded using <seealso marker="#ATOM_EXT">ATOM_EXT</seealso>,
- <seealso marker="#SMALL_ATOM_EXT">SMALL_ATOM_EXT</seealso> or
- <seealso marker="#ATOM_CACHE_REF">ATOM_CACHE_REF</seealso>).
+ <c>Module</c> and <c>Function</c> are atoms
+ (encoded using <seealso marker="#ATOM_EXT"><c>ATOM_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_EXT"><c>SMALL_ATOM_EXT</c></seealso>, or
+ <seealso marker="#ATOM_CACHE_REF"><c>ATOM_CACHE_REF</c></seealso>).
</p>
<p>
- <c>Arity</c> is an integer encoded using
- <seealso marker="#SMALL_INTEGER_EXT">SMALL_INTEGER_EXT</seealso>.
+ <c>Arity</c> is an integer encoded using
+ <seealso marker="#SMALL_INTEGER_EXT">
+ <c>SMALL_INTEGER_EXT</c></seealso>.
</p>
-
</section>
<section>
<marker id="BIT_BINARY_EXT"/>
<title>BIT_BINARY_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -1034,39 +1023,35 @@
<cell align="center">Len</cell>
</row>
<row>
- <cell align="center">77</cell>
- <cell align="center">Len</cell>
- <cell align="center">Bits</cell>
- <cell align="center">Data</cell>
+ <cell align="center"><c>77</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>Bits</c></cell>
+ <cell align="center"><c>Data</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>BIT_BINARY_EXT</tcaption></table>
<p>
This term represents a bitstring whose length in bits does
not have to be a multiple of 8.
- The <c>Len</c> field is an unsigned 4 byte integer (big endian).
+ The <c>Len</c> field is an unsigned 4 byte integer (big-endian).
The <c>Bits</c> field is the number of bits (1-8) that are used
- in the last byte in the data field,
- counting from the most significant bit towards the least
- significant.
+ in the last byte in the data field,
+ counting from the most significant bit to the least significant.
</p>
-
-
</section>
<section>
<marker id="NEW_FLOAT_EXT"/>
<title>NEW_FLOAT_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
<cell align="center">8</cell>
</row>
<row>
- <cell align="center">70</cell>
- <cell align="center">IEEE float</cell>
+ <cell align="center"><c>70</c></cell>
+ <cell align="center"><c>IEEE float</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>NEW_FLOAT_EXT</tcaption></table>
<p>
A float is stored as 8 bytes in big-endian IEEE format.
</p>
@@ -1074,10 +1059,10 @@
This term is used in minor version 1 of the external format.
</p>
</section>
+
<section>
<marker id="ATOM_UTF8_EXT"/>
<title>ATOM_UTF8_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -1089,23 +1074,22 @@
<cell align="center"><c>Len</c></cell>
<cell align="center"><c>AtomName</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>ATOM_UTF8_EXT</tcaption></table>
<p>
An atom is stored with a 2 byte unsigned length in big-endian order,
followed by <c>Len</c> bytes containing the <c>AtomName</c> encoded
in UTF-8.
</p>
<p>
- For more information on encoding of atoms, see
+ For more information on encoding of atoms, see the
<seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso>
- in the beginning of this document.
+ in the beginning of this section.
</p>
</section>
<section>
<marker id="SMALL_ATOM_UTF8_EXT"/>
<title>SMALL_ATOM_UTF8_EXT</title>
-
<table align="left">
<row>
<cell align="center">1</cell>
@@ -1117,20 +1101,19 @@
<cell align="center"><c>Len</c></cell>
<cell align="center"><c>AtomName</c></cell>
</row>
- <tcaption></tcaption></table>
+ <tcaption>SMALL_ATOM_UTF8_EXT</tcaption></table>
<p>
- An atom is stored with a 1 byte unsigned length,
+ An atom is stored with a 1 byte unsigned length,
followed by <c>Len</c> bytes containing the <c>AtomName</c> encoded
in UTF-8. Longer atoms encoded in UTF-8 can be represented using
- <seealso marker="#ATOM_UTF8_EXT">ATOM_UTF8_EXT</seealso>.
+ <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso>.
</p>
<p>
- For more information on encoding of atoms, see
+ For more information on encoding of atoms, see the
<seealso marker="#utf8_atoms">note on UTF-8 encoded atoms</seealso>
- in the beginning of this document.
+ in the beginning of this section.
</p>
</section>
-
</chapter>
-
+
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index f3921f1922..b5dc9037c4 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -33,43 +33,52 @@
<file>erl_nif.xml</file>
</header>
<lib>erl_nif</lib>
- <libsummary>API functions for an Erlang NIF library</libsummary>
+ <libsummary>API functions for an Erlang NIF library.</libsummary>
<description>
<p>A NIF library contains native implementation of some functions
- of an Erlang module. The native implemented functions (NIFs) are
- called like any other functions without any difference to the
- caller. Each NIF must also have an implementation in Erlang that
- will be invoked if the function is called before the NIF library
- has been successfully loaded. A typical such stub implementation
- is to throw an exception. But it can also be used as a fallback
- implementation if the NIF library is not implemented for some
- architecture.</p>
- <marker id="WARNING"/>
- <warning><p><em>Use this functionality with extreme care!</em></p>
+ of an Erlang module. The native implemented functions (NIFs) are
+ called like any other functions without any difference to the
+ caller. Each NIF must have an implementation in Erlang that
+ is invoked if the function is called before the NIF library
+ is successfully loaded. A typical such stub implementation
+ is to throw an exception. But it can also be used as a fallback
+ implementation if the NIF library is not implemented for some
+ architecture.</p>
+
+ <warning>
+ <marker id="WARNING"/>
+ <p><em>Use this functionality with extreme care.</em></p>
<p>A native function is executed as a direct extension of the
- native code of the VM. Execution is not made in a safe environment.
- The VM can <em>not</em> provide the same services as provided when
- executing Erlang code, such as preemptive scheduling or memory
- protection. If the native function doesn't behave well, the whole
- VM will misbehave.</p>
- <list>
- <item><p>A native function that crash will crash the whole VM.</p></item>
- <item><p>An erroneously implemented native function might cause
- a VM internal state inconsistency which may cause a crash of the VM,
- or miscellaneous misbehaviors of the VM at any point after the call
- to the native function.</p></item>
- <item><p>A native function that do <seealso marker="#lengthy_work">lengthy
- work</seealso> before returning will degrade responsiveness of the VM,
- and may cause miscellaneous strange behaviors. Such strange behaviors
- include, but are not limited to, extreme memory usage, and bad load
- balancing between schedulers. Strange behaviors that might occur due
- to lengthy work may also vary between OTP releases.</p></item>
+ native code of the VM. Execution is not made in a safe environment.
+ The VM <em>cannot</em> provide the same services as provided when
+ executing Erlang code, such as pre-emptive scheduling or memory
+ protection. If the native function does not behave well, the whole
+ VM will misbehave.</p>
+ <list type="bulleted">
+ <item>
+ <p>A native function that crash will crash the whole VM.</p>
+ </item>
+ <item>
+ <p>An erroneously implemented native function can cause a VM
+ internal state inconsistency, which can cause a crash of the VM,
+ or miscellaneous misbehaviors of the VM at any point after the
+ call to the native function.</p>
+ </item>
+ <item>
+ <p>A native function doing <seealso marker="#lengthy_work">lengthy
+ work</seealso> before returning degrades responsiveness of the VM,
+ and can cause miscellaneous strange behaviors. Such strange
+ behaviors include, but are not limited to, extreme memory usage,
+ and bad load balancing between schedulers. Strange behaviors that
+ can occur because of lengthy work can also vary between Erlang/OTP
+ releases.</p>
+ </item>
</list>
- </warning>
+ </warning>
- <p>A minimal example of a NIF library can look like this:</p>
- <p/>
- <code type="none">
+ <p>A minimal example of a NIF library can look as follows:</p>
+
+ <code type="none">
/* niftest.c */
#include "erl_nif.h"
@@ -83,13 +92,11 @@ static ErlNifFunc nif_funcs[] =
{"hello", 0, hello}
};
-ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)
-</code>
+ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)</code>
+
+ <p>The Erlang module can look as follows:</p>
- <p>and the Erlang module would have to look something like
- this:</p>
- <p/>
- <code type="none">
+ <code type="none">
-module(niftest).
-export([init/0, hello/0]).
@@ -98,11 +105,11 @@ init() ->
erlang:load_nif("./niftest", 0).
hello() ->
- "NIF library not loaded".
-</code>
- <p>and compile and test something like this (on Linux):</p>
- <p/>
- <code type="none">
+ "NIF library not loaded".</code>
+
+ <p>Compile and test can look as follows (on Linux):</p>
+
+ <code type="none">
$> gcc -fPIC -shared -o niftest.so niftest.c -I $ERL_ROOT/usr/include/
$> erl
@@ -113,1353 +120,1936 @@ $> erl
3> niftest:init().
ok
4> niftest:hello().
-"Hello world!"
-</code>
-
- <p>A better solution for a real module is to take advantage of
- the new directive <seealso
- marker="doc/reference_manual:code_loading#on_load">on_load</seealso> to automatically
- load the NIF library when the module is loaded.</p>
- <note><p>A NIF does not have to be exported, it can be local to the module.
- Note however that unused local stub functions will be optimized
- away by the compiler causing loading of the NIF library to fail.</p>
+"Hello world!"</code>
+
+ <p>A better solution for a real module is to take advantage of the new
+ directive <c>on load</c> (see section
+ <seealso marker="doc/reference_manual:code_loading#on_load">Running a
+ Function When a Module is Loaded</seealso> in the Erlang Reference
+ Manual) to load the NIF library automatically when the module is
+ loaded.</p>
+
+ <note>
+ <p>A NIF does not have to be exported, it can be local to the module.
+ However, unused local stub functions will be optimized
+ away by the compiler, causing loading of the NIF library to fail.</p>
</note>
- <p>A loaded NIF library is tied to the Erlang module code version
- that loaded it. If the module is upgraded with a new version, the
- new Erlang code will have to load its own NIF library (or maybe choose not
- to). The new code version can however choose to load the exact
- same NIF library as the old code if it wants to. Sharing the same
- dynamic library will mean that static data defined by the library
- will be shared as well. To avoid unintentionally shared static
- data, each Erlang module code can keep its own private data. This
- private data can be set when the NIF library is loaded and
- then retrieved by calling <seealso marker="#enif_priv_data">enif_priv_data</seealso>.</p>
- <p>There is no way to explicitly unload a NIF library. A library will be
- automatically unloaded when the module code that it belongs to is purged
- by the code server.</p>
+ <p>A loaded NIF library is tied to the Erlang module code version
+ that loaded it. If the module is upgraded with a new version, the
+ new Erlang code need to load its own NIF library (or maybe choose not
+ to). The new code version can, however, choose to load the
+ same NIF library as the old code if it wants to. Sharing the
+ dynamic library means that static data defined by the library
+ is shared as well. To avoid unintentionally shared static
+ data, each Erlang module code can keep its own private data. This
+ private data can be set when the NIF library is loaded and
+ then retrieved by calling <seealso marker="#enif_priv_data">
+ <c>enif_priv_data</c></seealso>.</p>
+
+ <p>A NIF library cannot be loaded explicitly. A library is
+ automatically unloaded when the module code that it belongs to is purged
+ by the code server.</p>
</description>
+
<section>
- <title>FUNCTIONALITY</title>
- <p>All functions that a NIF library needs to do with Erlang are
- performed through the NIF API functions. There are functions
+ <title>Functionality</title>
+ <p>All functions that a NIF library needs to do with Erlang are
+ performed through the NIF API functions. Functions exist
for the following functionality:</p>
+
<taglist>
<tag>Read and write Erlang terms</tag>
- <item><p>Any Erlang terms can be passed to a NIF as function arguments and
- be returned as function return values. The terms are of C-type
- <seealso marker="#ERL_NIF_TERM">ERL_NIF_TERM</seealso>
- and can only be read or written using API functions. Most functions to read
- the content of a term are prefixed <c>enif_get_</c> and usually return
- true (or false) if the term was of the expected type (or not).
- The functions to write terms are all prefixed <c>enif_make_</c> and usually
- return the created <c>ERL_NIF_TERM</c>. There are also some functions
- to query terms, like <c>enif_is_atom</c>, <c>enif_is_identical</c> and
- <c>enif_compare</c>.</p>
- <p>All terms of type <c>ERL_NIF_TERM</c> belong to an environment of type
- <seealso marker="#ErlNifEnv">ErlNifEnv</seealso>. The lifetime of a term is
- controlled by the lifetime of its environment object. All API functions that read
- or write terms has the environment, that the term belongs to, as the first
- function argument.</p></item>
+ <item>
+ <p>Any Erlang terms can be passed to a NIF as function arguments and
+ be returned as function return values. The terms are of C-type
+ <seealso marker="#ERL_NIF_TERM"><c>ERL_NIF_TERM</c></seealso> and can
+ only be read or written using API functions. Most functions to read
+ the content of a term are prefixed <c>enif_get_</c> and usually return
+ <c>true</c> (or <c>false</c>) if the term is of the expected type (or
+ not). The functions to write terms are all prefixed <c>enif_make_</c>
+ and usually
+ return the created <c>ERL_NIF_TERM</c>. There are also some functions
+ to query terms, like <c>enif_is_atom</c>, <c>enif_is_identical</c>,
+ and <c>enif_compare</c>.</p>
+ <p>All terms of type <c>ERL_NIF_TERM</c> belong to an environment of
+ type <seealso marker="#ErlNifEnv"><c>ErlNifEnv</c></seealso>. The
+ lifetime of a term is controlled by the lifetime of its environment
+ object. All API functions that read or write terms has the
+ environment that the term belongs to as the first function
+ argument.</p>
+ </item>
<tag>Binaries</tag>
- <item><p>Terms of type binary are accessed with the help of the struct type
- <seealso marker="#ErlNifBinary">ErlNifBinary</seealso>
- that contains a pointer (<c>data</c>) to the raw binary data and the length
- (<c>size</c>) of the data in bytes. Both <c>data</c> and <c>size</c> are
- read-only and should only be written using calls to API functions.
- Instances of <c>ErlNifBinary</c> are however always allocated by the user
- (usually as local variables).</p>
- <p>The raw data pointed to by <c>data</c> is only mutable after a call to
- <seealso marker="#enif_alloc_binary">enif_alloc_binary</seealso> or
- <seealso marker="#enif_realloc_binary">enif_realloc_binary</seealso>.
- All other functions that operates on a binary will leave the data as read-only.
- A mutable binary must in the end either be freed with
- <seealso marker="#enif_release_binary">enif_release_binary</seealso>
- or made read-only by transferring it to an Erlang term with
- <seealso marker="#enif_make_binary">enif_make_binary</seealso>.
- But it does not have to happen in the same NIF call. Read-only binaries
- do not have to be released.</p>
- <p><seealso marker="#enif_make_new_binary">enif_make_new_binary</seealso>
- can be used as a shortcut to allocate and return a binary in the same NIF call.</p>
- <p>Binaries are sequences of whole bytes. Bitstrings with an arbitrary
- bit length have no support yet.</p>
- </item>
+ <item>
+ <p>Terms of type binary are accessed with the help of struct type
+ <seealso marker="#ErlNifBinary"><c>ErlNifBinary</c></seealso>,
+ which contains a pointer (<c>data</c>) to the raw binary data and the
+ length (<c>size</c>) of the data in bytes. Both <c>data</c> and
+ <c>size</c> are read-only and are only to be written using calls to
+ API functions. Instances of <c>ErlNifBinary</c> are, however, always
+ allocated by the user (usually as local variables).</p>
+ <p>The raw data pointed to by <c>data</c> is only mutable after a call
+ to <seealso marker="#enif_alloc_binary">
+ <c>enif_alloc_binary</c></seealso> or
+ <seealso marker="#enif_realloc_binary">
+ <c>enif_realloc_binary</c></seealso>. All other functions that
+ operate on a binary leave the data as read-only.
+ A mutable binary must in the end either be freed with
+ <seealso marker="#enif_release_binary">
+ <c>enif_release_binary</c></seealso>
+ or made read-only by transferring it to an Erlang term with
+ <seealso marker="#enif_make_binary"><c>enif_make_binary</c></seealso>.
+ However, it does not have to occur in the same NIF call. Read-only
+ binaries do not have to be released.</p>
+ <p><seealso marker="#enif_make_new_binary">
+ <c>enif_make_new_binary</c></seealso> can be used as a shortcut to
+ allocate and return a binary in the same NIF call.</p>
+ <p>Binaries are sequences of whole bytes. Bitstrings with an arbitrary
+ bit length have no support yet.</p>
+ </item>
<tag>Resource objects</tag>
- <item><p>The use of resource objects is a safe way to return pointers to
- native data structures from a NIF. A resource object is
- just a block of memory allocated with
- <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
- A handle ("safe pointer") to this memory block can then be returned to Erlang by the use of
- <seealso marker="#enif_make_resource">enif_make_resource</seealso>.
- The term returned by <c>enif_make_resource</c>
- is totally opaque in nature. It can be stored and passed between processes
- on the same node, but the only real end usage is to pass it back as an argument to a NIF.
- The NIF can then call <seealso marker="#enif_get_resource">enif_get_resource</seealso>
- and get back a pointer to the memory block that is guaranteed to still be
- valid. A resource object will not be deallocated until the last handle term
- has been garbage collected by the VM and the resource has been
- released with <seealso marker="#enif_release_resource">enif_release_resource</seealso>
- (not necessarily in that order).</p>
- <p>All resource objects are created as instances of some <em>resource type</em>.
- This makes resources from different modules to be distinguishable.
- A resource type is created by calling
- <seealso marker="#enif_open_resource_type">enif_open_resource_type</seealso>
- when a library is loaded. Objects of that resource type can then later be allocated
- and <c>enif_get_resource</c> verifies that the resource is of the expected type.
- A resource type can have a user supplied destructor function that is
- automatically called when resources of that type are released (by either
- the garbage collector or <c>enif_release_resource</c>). Resource types
- are uniquely identified by a supplied name string and the name of the
- implementing module.</p>
- <marker id="enif_resource_example"/><p>Here is a template example of how to create and return a resource object.</p>
- <p/>
- <code type="none">
- ERL_NIF_TERM term;
- MyStruct* obj = enif_alloc_resource(my_resource_type, sizeof(MyStruct));
-
- /* initialize struct ... */
-
- term = enif_make_resource(env, obj);
-
- if (keep_a_reference_of_our_own) {
- /* store 'obj' in static variable, private data or other resource object */
- }
- else {
- enif_release_resource(obj);
- /* resource now only owned by "Erlang" */
- }
- return term;
- </code>
- <p>Note that once <c>enif_make_resource</c> creates the term to
- return to Erlang, the code can choose to either keep its own
- native pointer to the allocated struct and release it later, or
- release it immediately and rely solely on the garbage collector
- to eventually deallocate the resource object when it collects
- the term.</p>
- <p>Another usage of resource objects is to create binary terms with
- user defined memory management.
- <seealso marker="#enif_make_resource_binary">enif_make_resource_binary</seealso>
- will create a binary term that is connected to a resource object. The
- destructor of the resource will be called when the binary is garbage
- collected, at which time the binary data can be released. An example of
- this can be a binary term consisting of data from a <c>mmap</c>'ed file.
- The destructor can then do <c>munmap</c> to release the memory
- region.</p>
- <p>Resource types support upgrade in runtime by allowing a loaded NIF
- library to takeover an already existing resource type and thereby
- "inherit" all existing objects of that type. The destructor of the new
- library will thereafter be called for the inherited objects and the
- library with the old destructor function can be safely unloaded. Existing
- resource objects, of a module that is upgraded, must either be deleted
- or taken over by the new NIF library. The unloading of a library will be
- postponed as long as there exist resource objects with a destructor
- function in the library.
- </p>
+ <item>
+ <p>The use of resource objects is a safe way to return pointers to
+ native data structures from a NIF. A resource object is
+ only a block of memory allocated with
+ <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>.
+ A handle ("safe pointer") to this memory block can then be returned
+ to Erlang by the use of
+ <seealso marker="#enif_make_resource">
+ <c>enif_make_resource</c></seealso>.
+ The term returned by <c>enif_make_resource</c> is opaque in nature.
+ It can be stored and passed between processes on the same node, but
+ the only real end usage is to pass it back as an argument to a NIF.
+ The NIF can then call <seealso marker="#enif_get_resource">
+ <c>enif_get_resource</c></seealso> and get back a pointer to the
+ memory block, which is guaranteed to still be valid. A resource
+ object is not deallocated until the last handle term
+ is garbage collected by the VM and the resource is released with
+ <seealso marker="#enif_release_resource">
+ <c>enif_release_resource</c></seealso>
+ (not necessarily in that order).</p>
+ <p>All resource objects are created as instances of some <em>resource
+ type</em>. This makes resources from different modules to be
+ distinguishable. A resource type is created by calling
+ <seealso marker="#enif_open_resource_type">
+ <c>enif_open_resource_type</c></seealso> when a library is loaded.
+ Objects of that resource type can then later be allocated and
+ <c>enif_get_resource</c> verifies that the resource is of the
+ expected type. A resource type can have a user-supplied destructor
+ function, which is automatically called when resources of that type
+ are released (by either the garbage collector or
+ <c>enif_release_resource</c>). Resource types are uniquely identified
+ by a supplied name string and the name of the implementing module.</p>
+ <marker id="enif_resource_example"/>
+ <p>The following is a template example of how to create and return a
+ resource object.</p>
+ <code type="none">
+ERL_NIF_TERM term;
+MyStruct* obj = enif_alloc_resource(my_resource_type, sizeof(MyStruct));
+
+/* initialize struct ... */
+
+term = enif_make_resource(env, obj);
+
+if (keep_a_reference_of_our_own) {
+ /* store 'obj' in static variable, private data or other resource object */
+}
+else {
+ enif_release_resource(obj);
+ /* resource now only owned by "Erlang" */
+}
+return term;</code>
+ <p>Notice that once <c>enif_make_resource</c> creates the term to
+ return to Erlang, the code can choose to either keep its own
+ native pointer to the allocated struct and release it later, or
+ release it immediately and rely only on the garbage collector
+ to deallocate the resource object eventually when it collects
+ the term.</p>
+ <p>Another use of resource objects is to create binary terms with
+ user-defined memory management.
+ <seealso marker="#enif_make_resource_binary">
+ <c>enif_make_resource_binary</c></seealso>
+ creates a binary term that is connected to a resource object. The
+ destructor of the resource is called when the binary is garbage
+ collected, at which time the binary data can be released. An example
+ of this can be a binary term consisting of data from a <c>mmap</c>'ed
+ file. The destructor can then do <c>munmap</c> to release the memory
+ region.</p>
+ <p>Resource types support upgrade in runtime by allowing a loaded NIF
+ library to take over an already existing resource type and by that
+ "inherit" all existing objects of that type. The destructor of the
+ new library is thereafter called for the inherited objects and the
+ library with the old destructor function can be safely unloaded.
+ Existing resource objects, of a module that is upgraded, must either
+ be deleted or taken over by the new NIF library. The unloading of a
+ library is postponed as long as there exist resource objects with a
+ destructor function in the library.</p>
</item>
<tag>Threads and concurrency</tag>
- <item><p>A NIF is thread-safe without any explicit synchronization as
- long as it acts as a pure function and only reads the supplied
- arguments. As soon as you write towards a shared state either through
- static variables or <seealso marker="#enif_priv_data">enif_priv_data</seealso>
- you need to supply your own explicit synchronization. This includes terms
- in process independent environments that are shared between threads.
- Resource objects will also require synchronization if you treat them as
- mutable.</p>
- <p>The library initialization callbacks <c>load</c>, <c>reload</c> and
- <c>upgrade</c> are all thread-safe even for shared state data.</p>
+ <item>
+ <p>A NIF is thread-safe without any explicit synchronization as
+ long as it acts as a pure function and only reads the supplied
+ arguments. When you write to a shared state either through
+ static variables or <seealso marker="#enif_priv_data">
+ <c>enif_priv_data</c></seealso>, you need to supply your own explicit
+ synchronization. This includes terms in process-independent
+ environments that are shared between threads. Resource objects also
+ require synchronization if you treat them as mutable.</p>
+ <p>The library initialization callbacks <c>load</c>, <c>reload</c>, and
+ <c>upgrade</c> are all thread-safe even for shared state data.</p>
</item>
-
<tag><marker id="version_management"/>Version Management</tag>
- <item><p>
- When a NIF library is built, information about NIF API version
- is compiled into the library. When a NIF library is loaded the
- runtime system verifies that the library is of a compatible version.
- <c>erl_nif.h</c> defines <c>ERL_NIF_MAJOR_VERSION</c>, and
- <c>ERL_NIF_MINOR_VERSION</c>. <c>ERL_NIF_MAJOR_VERSION</c> will be
- incremented when NIF library incompatible changes are made to the
- Erlang runtime system. Normally it will suffice to recompile the NIF
- library when the <c>ERL_NIF_MAJOR_VERSION</c> has changed, but it
- could, under rare circumstances, mean that NIF libraries have to
- be slightly modified. If so, this will of course be documented.
- <c>ERL_NIF_MINOR_VERSION</c> will be incremented when
- new features are added. The runtime system uses the minor version
- to determine what features to use.
- </p><p>
- The runtime system will normally refuse to load a NIF library if
- the major versions differ, or if the major versions are equal and
- the minor version used by the NIF library is greater than the one
- used by the runtime system. Old NIF libraries with lower major
- versions will however be allowed after a bump of the major version
- during a transition period of two major releases. Such old NIF
- libraries might however fail if deprecated features are used.
- </p></item>
-
+ <item>
+ <p>When a NIF library is built, information about the NIF API version
+ is compiled into the library. When a NIF library is loaded, the
+ runtime system verifies that the library is of a compatible version.
+ <c>erl_nif.h</c> defines the following:</p>
+ <taglist>
+ <tag><c>ERL_NIF_MAJOR_VERSION</c></tag>
+ <item>
+ <p>Incremented when NIF library incompatible changes are made to the
+ Erlang runtime system. Normally it suffices to recompile the NIF
+ library when the <c>ERL_NIF_MAJOR_VERSION</c> has changed, but it
+ can, under rare circumstances, mean that NIF libraries must be
+ slightly modified. If so, this will of course be documented.</p>
+ </item>
+ <tag><c>ERL_NIF_MINOR_VERSION</c></tag>
+ <item>
+ <p>Incremented when new features are added. The runtime system uses
+ the minor version to determine what features to use.</p>
+ </item>
+ </taglist>
+ <p>The runtime system normally refuses to load a NIF library if
+ the major versions differ, or if the major versions are equal and
+ the minor version used by the NIF library is greater than the one
+ used by the runtime system. Old NIF libraries with lower major
+ versions are, however, allowed after a bump of the major version
+ during a transition period of two major releases. Such old NIF
+ libraries can however fail if deprecated features are used.</p>
+ </item>
<tag><marker id="time_measurement"/>Time Measurement</tag>
- <item><p>Support for time measurement in NIF libraries:</p>
- <list>
- <item><seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso></item>
- <item><seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso></item>
- <item><seealso marker="#enif_monotonic_time"><c>enif_monotonic_time()</c></seealso></item>
- <item><seealso marker="#enif_time_offset"><c>enif_time_offset()</c></seealso></item>
- <item><seealso marker="#enif_convert_time_unit"><c>enif_convert_time_unit()</c></seealso></item>
+ <item>
+ <p>Support for time measurement in NIF libraries:</p>
+ <list type="bulleted">
+ <item><seealso marker="#ErlNifTime">
+ <c>ErlNifTime</c></seealso></item>
+ <item><seealso marker="#ErlNifTimeUnit">
+ <c>ErlNifTimeUnit</c></seealso></item>
+ <item><seealso marker="#enif_monotonic_time">
+ <c>enif_monotonic_time()</c></seealso></item>
+ <item><seealso marker="#enif_time_offset">
+ <c>enif_time_offset()</c></seealso></item>
+ <item><seealso marker="#enif_convert_time_unit">
+ <c>enif_convert_time_unit()</c></seealso></item>
</list>
</item>
-
<tag><marker id="lengthy_work"/>Long-running NIFs</tag>
-
- <item><p>
- As mentioned in the <seealso marker="#WARNING">warning</seealso> text at
- the beginning of this document it is of <em>vital importance</em> that a
- native function return relatively quickly. It is hard to give an exact
- maximum amount of time that a native function is allowed to work, but as a
- rule of thumb a well-behaving native function should return to its caller
- before a millisecond has passed. This can be achieved using different
- approaches. If you have full control over the code to execute in the
- native function, the best approach is to divide the work into multiple
- chunks of work and call the native function multiple times. In some
- cases this might however not always be possible, e.g. when calling
- third-party libraries.</p>
-
- <p>The
- <seealso marker="#enif_consume_timeslice">enif_consume_timeslice()</seealso>
- function can be used to inform the runtime system about the length of the
- NIF call. It should typically always be used unless the NIF executes very
- quickly.</p>
-
- <p>If the NIF call is too lengthy one needs to handle this in one of the
- following ways in order to avoid degraded responsiveness, scheduler load
- balancing problems, and other strange behaviors:</p>
-
- <taglist>
- <tag>Yielding NIF</tag>
- <item>
- <p>
- If the functionality of a long-running NIF can be split so that
- its work can be achieved through a series of shorter NIF calls,
- the application can either make that series of NIF calls from the
- Erlang level, or it can call a NIF that first performs a chunk of
- the work, then invokes the
- <seealso marker="#enif_schedule_nif">enif_schedule_nif</seealso>
- function to schedule another NIF call to perform the next chunk.
- The final call scheduled in this manner can then return the
- overall result. Breaking up a long-running function in
- this manner enables the VM to regain control between calls to the
- NIFs.
- </p>
- <p>
- This approach is always preferred over the other alternatives
- described below. This both from a performance perspective and
- a system characteristics perspective.
- </p>
- </item>
-
- <tag>Threaded NIF</tag>
- <item>
- <p>
- This is accomplished by dispatching the work to another thread
- managed by the NIF library, return from the NIF, and wait for the
- result. The thread can send the result back to the Erlang
- process using <seealso marker="#enif_send">enif_send</seealso>.
- Information about thread primitives can be found below.
- </p>
- </item>
-
- <tag><marker id="dirty_nifs"/>Dirty NIF</tag>
- <item>
-
- <note>
- <p>
- <em>The dirty NIF functionality described here
- is experimental</em>. Dirty NIF support is available only when
- the emulator is configured with dirty schedulers enabled. This
- feature is currently disabled by default. The Erlang runtime
- without SMP support do not support dirty schedulers even when
- the dirty scheduler support has been enabled. To check at
- runtime for the presence of dirty scheduler threads, code can
- use the
- <seealso marker="#enif_system_info"><c>enif_system_info()</c></seealso>
- API function.
- </p>
- </note>
-
- <p>
- A NIF that cannot be split and cannot execute in a millisecond or
- less is called a "dirty NIF" because it performs work that the
- ordinary schedulers of the Erlang runtime system cannot handle cleanly.
- Applications that make use of such functions must indicate to the
- runtime that the functions are dirty so they can be handled
- specially. This is handled by executing dirty jobs on a separate
+ <item>
+ <p>As mentioned in the <seealso marker="#WARNING">warning</seealso> text
+ at the beginning of this manual page, it is of <em>vital
+ importance</em> that a native function returns relatively fast. It is
+ difficult to give an exact maximum amount of time that a native
+ function is allowed to work, but usually a well-behaving native
+ function is to return to its caller within 1 millisecond. This can be
+ achieved using different approaches. If you have full control over the
+ code to execute in the native function, the best approach is to
+ divide the work into multiple chunks of work and call the native
+ function multiple times. This is, however, not always possible, for
+ example when calling third-party libraries.</p>
+ <p>The <seealso marker="#enif_consume_timeslice">
+ <c>enif_consume_timeslice()</c></seealso> function can be used to
+ inform the runtime system about the length of the NIF call.
+ It is typically always to be used unless the NIF executes very
+ fast.</p>
+ <p>If the NIF call is too lengthy, this must be handled in one of
+ the following ways to avoid degraded responsiveness, scheduler load
+ balancing problems, and other strange behaviors:</p>
+ <taglist>
+ <tag>Yielding NIF</tag>
+ <item>
+ <p>If the functionality of a long-running NIF can be split so that
+ its work can be achieved through a series of shorter NIF calls,
+ the application has two options:</p>
+ <list type="bulleted">
+ <item>
+ <p>Make that series of NIF calls from the Erlang level.</p>
+ </item>
+ <item>
+ <p>Call a NIF that first performs a chunk of the work, then
+ invokes the <seealso marker="#enif_schedule_nif">
+ <c>enif_schedule_nif</c></seealso> function to schedule
+ another NIF call to perform the next chunk. The final call
+ scheduled in this manner can then return the overall
+ result.</p>
+ </item>
+ </list>
+ <p>Breaking up a long-running function in this manner enables the
+ VM to regain control between calls to the NIFs.</p>
+ <p>This approach is always preferred over the other alternatives
+ described below. This both from a performance perspective and
+ a system characteristics perspective.</p>
+ </item>
+ <tag>Threaded NIF</tag>
+ <item>
+ <p>This is accomplished by dispatching the work to another thread
+ managed by the NIF library, return from the NIF, and wait for
+ the result. The thread can send the result back to the Erlang
+ process using <seealso marker="#enif_send">
+ <c>enif_send</c></seealso>.
+ Information about thread primitives is provided below.</p>
+ </item>
+ <tag><marker id="dirty_nifs"/>Dirty NIF</tag>
+ <item>
+ <note>
+ <p><em>The dirty NIF functionality described here
+ is experimental</em>. Dirty NIF support is available only when
+ the emulator is configured with dirty schedulers enabled. This
+ feature is disabled by default. The Erlang runtime
+ without SMP support does not support dirty schedulers even when
+ the dirty scheduler support is enabled. To check at runtime for
+ the presence of dirty scheduler threads, code can use the
+ <seealso marker="#enif_system_info">
+ <c>enif_system_info()</c></seealso> API function.</p>
+ </note>
+ <p>A NIF that cannot be split and cannot execute in a millisecond
+ or less is called a "dirty NIF", as it performs work that the
+ ordinary schedulers of the Erlang runtime system cannot handle cleanly.
+ Applications that make use of such functions must indicate to the
+ runtime that the functions are dirty so they can be handled
+ specially. This is handled by executing dirty jobs on a separate
set of schedulers called dirty schedulers. A dirty NIF executing
on a dirty scheduler does not have the same duration restriction
as a normal NIF.
- </p>
+ </p>
- <p>
- It is important to classify the dirty job correct. An I/O bound
+ <p>
+ It is important to classify the dirty job correct. An I/O bound
job should be classified as such, and a CPU bound job should be
classified as such. If you should classify CPU bound jobs
as I/O bound jobs, dirty I/O schedulers might starve ordinary
schedulers. I/O bound jobs are expected to either block waiting
for I/O, and/or spend a limited amount of time moving data.
- </p>
-
- <p>
- To schedule a dirty NIF for execution, the appropriate
- <c>flags</c> value can be set for the NIF in its
- <seealso marker="#ErlNifFunc"><c>ErlNifFunc</c></seealso>
- entry, or the application can call
- <seealso marker="#enif_schedule_nif"><c>enif_schedule_nif</c></seealso>,
- passing to it a pointer to the dirty NIF to be executed and
- indicating with the <c>flags</c> argument whether it expects the
- operation to be CPU-bound or I/O-bound. A job that alternates
- between I/O bound and CPU bound can be reclassified and
- rescheduled using <c>enif_schedule_nif</c> so that it executes on
- the correct type of dirty scheduler at all times. For more
- information see the documentation of the <c>erl</c> command line
- arguments <seealso marker="erl#+SDcpu"><c>+SDcpu</c></seealso>,
- and <seealso marker="erl#+SDio"><c>+SDio</c></seealso>.
- </p>
-
- <p>
- While a process is executing a dirty NIF some operations that
- communicate with it may take a very long time to complete.
- Suspend, or garbage collection of a process executing a dirty
- NIF cannot be done until the dirty NIF has returned, so other
- processes waiting for such operations to complete might have to
- wait for a very long time. Blocking multi scheduling, i.e.,
- calling
- <seealso marker="erlang#system_flag_multi_scheduling"><c>erlang:system_flag(multi_scheduling,
- block)</c></seealso>, might also take a very long time to
- complete. This since all ongoing dirty operations on all
- dirty schedulers need to complete before the block
- operation can complete.
- </p>
-
- <p>
- A lot of operations communicating with a process executing a
- dirty NIF can, however, complete while it is executing the
- dirty NIF. For example, retrieving information about it via
- <c>process_info()</c>, setting its group leader,
- register/unregister its name, etc.
- </p>
-
- <p>
- Termination of a process executing a dirty NIF can only be
- completed up to a certain point while it is executing the
- dirty NIF. All Erlang resources such as its registered name,
- its ETS tables, etc will be released. All links and monitors
- will be triggered. The actual execution of the NIF will
- however <em>not</em> be stopped. The NIF can safely continue
- execution, allocate heap memory, etc, but it is of course better
- to stop executing as soon as possible. The NIF can check
- whether current process is alive or not using
- <seealso marker="#enif_is_current_process_alive"><c>enif_is_current_process_alive</c></seealso>.
- Communication using
- <seealso marker="#enif_send"><c>enif_send</c></seealso>,
- and <seealso marker="#enif_port_command"><c>enif_port_command</c></seealso>
- will also be dropped when the sending process is not alive.
- Deallocation of certain internal resources such as process
- heap, and process control block will be delayed until the
- dirty NIF has completed.
- </p>
-
- <p>Currently known issues that are planned to be fixed:</p>
- <list>
- <item>
- <p>
- Since purging of a module currently might need to garbage
- collect a process in order to determine if it has
- references to the module, a process executing a dirty
- NIF might delay purging for a very long time. Delaying
- a purge operation implies delaying <em>all</em> code
- loading operations which might cause severe problems for
- the system as a whole.
- </p>
- </item>
- </list>
-
- </item>
- </taglist>
+ </p>
+ <p>
+ To schedule a dirty NIF for execution, the application has two options:</p>
+ <list type="bulleted">
+ <item>
+ <p>Set the appropriate flags value for the dirty NIF in its
+ <seealso marker="#ErlNifFunc"> <c>ErlNifFunc</c></seealso>
+ entry.</p>
+ </item>
+ <item>
+ <p>Call <seealso marker="#enif_schedule_nif">
+ <c>enif_schedule_nif</c></seealso>, pass to it a pointer
+ to the dirty NIF to be executed, and indicate with argument
+ <c>flags</c> whether it expects the operation to be CPU-bound
+ or I/O-bound.</p>
+ </item>
+ </list>
+ <p>A job that alternates between I/O bound and CPU bound can be
+ reclassified and rescheduled using <c>enif_schedule_nif</c> so
+ that it executes on the correct type of dirty scheduler at all
+ times. For more information see the documentation of the
+ <c>erl(1)</c> command line arguments
+ <seealso marker="erl#+SDcpu"><c>+SDcpu</c></seealso>,
+ and <seealso marker="erl#+SDio"><c>+SDio</c></seealso>.</p>
+ <p>While a process executes a dirty NIF, some operations that
+ communicate with it can take a very long time to complete.
+ Suspend or garbage collection of a process executing a dirty
+ NIF cannot be done until the dirty NIF has returned. Thus, other
+ processes waiting for such operations to complete might
+ have to wait for a very long time. Blocking multi-scheduling, that
+ is, calling <seealso marker="erlang#system_flag_multi_scheduling">
+ <c>erlang:system_flag(multi_scheduling, block)</c></seealso>, can
+ also take a very long time to complete. This becaue all ongoing
+ dirty operations on all dirty schedulers must complete before
+ the block operation can complete.</p>
+ <p>Many operations communicating with a process executing a
+ dirty NIF can, however, complete while it executes the
+ dirty NIF. For example, retrieving information about it through
+ <seealso marker="erlang:process_info/1">
+ <c>erlang:process_info</c></seealso>, setting its group leader,
+ register/unregister its name, and so on.</p>
+ <p>Termination of a process executing a dirty NIF can only be
+ completed up to a certain point while it executes the dirty NIF.
+ All Erlang resources, such as its registered name and its ETS
+ tables, are released. All links and monitors are triggered. The
+ execution of the NIF is, however, <em>not</em> stopped. The NIF
+ can safely continue execution, allocate heap memory, and so on,
+ but it is of course better to stop executing as soon as possible.
+ The NIF can check whether a current process is alive using
+ <seealso marker="#enif_is_current_process_alive">
+ <c>enif_is_current_process_alive</c></seealso>. Communication
+ using <seealso marker="#enif_send"><c>enif_send</c></seealso> and
+ <seealso marker="#enif_port_command">
+ <c>enif_port_command</c></seealso> is also dropped when the
+ sending process is not alive. Deallocation of certain internal
+ resources, such as process heap and process control block, is
+ delayed until the dirty NIF has completed.</p>
+ </item>
+ </taglist>
</item>
</taglist>
</section>
+
<section>
- <title>INITIALIZATION</title>
+ <title>Initialization</title>
<taglist>
- <tag><marker id="ERL_NIF_INIT"/>ERL_NIF_INIT(MODULE, ErlNifFunc funcs[], load, reload, upgrade, unload)</tag>
- <item><p>This is the magic macro to initialize a NIF library. It
- should be evaluated in global file scope.</p>
- <p><c>MODULE</c> is the name of the Erlang module as an
- identifier without string quotations. It will be stringified by
- the macro.</p>
- <p><c>funcs</c> is a static array of function descriptors for
- all the implemented NIFs in this library.</p>
- <p><c>load</c>, <c>reload</c>, <c>upgrade</c> and <c>unload</c>
- are pointers to functions. One of <c>load</c>, <c>reload</c> or
- <c>upgrade</c> will be called to initialize the library.
- <c>unload</c> is called to release the library. They are all
- described individually below.</p>
- <p>If compiling a nif for static inclusion via --enable-static-nifs you
- have to define STATIC_ERLANG_NIF before the ERL_NIF_INIT declaration.</p>
+ <tag><marker id="ERL_NIF_INIT"/><c>ERL_NIF_INIT(MODULE,
+ ErlNifFunc funcs[], load, reload, upgrade, unload)</c></tag>
+ <item>
+ <p>This is the magic macro to initialize a NIF library. It
+ is to be evaluated in global file scope.</p>
+ <p><c>MODULE</c> is the name of the Erlang module as an
+ identifier without string quotations. It is stringified by
+ the macro.</p>
+ <p><c>funcs</c> is a static array of function descriptors for
+ all the implemented NIFs in this library.</p>
+ <p><c>load</c>, <c>reload</c>, <c>upgrade</c> and <c>unload</c>
+ are pointers to functions. One of <c>load</c>, <c>reload</c>, or
+ <c>upgrade</c> is called to initialize the library.
+ <c>unload</c> is called to release the library. All are
+ described individually below.</p>
+ <p>If compiling a NIF for static inclusion through
+ <c>--enable-static-nifs</c>, you must define <c>STATIC_ERLANG_NIF</c>
+ before the <c>ERL_NIF_INIT</c> declaration.</p>
</item>
-
- <tag><marker id="load"/>int (*load)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)</tag>
- <item><p><c>load</c> is called when the NIF library is loaded
- and there is no previously loaded library for this module.</p>
+ <tag><marker id="load"/><c>int (*load)(ErlNifEnv* env, void** priv_data,
+ ERL_NIF_TERM load_info)</c></tag>
+ <item>
+ <p><c>load</c> is called when the NIF library is loaded
+ and no previously loaded library exists for this module.</p>
<p><c>*priv_data</c> can be set to point to some private data
- that the library needs in order to keep a state between NIF
- calls. <c>enif_priv_data</c> will return this pointer.
- <c>*priv_data</c> will be initialized to NULL when <c>load</c> is
- called.</p>
+ that the library needs to keep a state between NIF
+ calls. <c>enif_priv_data</c> returns this pointer.
+ <c>*priv_data</c> is initialized to <c>NULL</c> when <c>load</c> is
+ called.</p>
<p><c>load_info</c> is the second argument to <seealso
- marker="erlang#load_nif-2">erlang:load_nif/2</seealso>.</p>
- <p>The library will fail to load if <c>load</c> returns
- anything other than 0. <c>load</c> can be NULL in case no
- initialization is needed.</p>
- </item>
-
- <tag><marker id="upgrade"/>int (*upgrade)(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)</tag>
- <item><p><c>upgrade</c> is called when the NIF library is loaded
- and there is old code of this module with a loaded NIF library.</p>
- <p>Works the same as <c>load</c>. The only difference is that
- <c>*old_priv_data</c> already contains the value set by the
- last call to <c>load</c> or <c>reload</c> for the old module
- code. <c>*priv_data</c> will be initialized to NULL when <c>upgrade</c>
- is called. It is allowed to write to both *priv_data and *old_priv_data.</p>
- <p>The library will fail to load if <c>upgrade</c> returns
- anything other than 0 or if <c>upgrade</c> is NULL.</p>
+ marker="erlang#load_nif-2"><c>erlang:load_nif/2</c></seealso>.</p>
+ <p>The library fails to load if <c>load</c> returns
+ anything other than <c>0</c>. <c>load</c> can be <c>NULL</c> if
+ initialization is not needed.</p>
</item>
-
- <tag><marker id="unload"/>void (*unload)(ErlNifEnv* env, void* priv_data)</tag>
- <item><p><c>unload</c> is called when the module code that
- the NIF library belongs to is purged as old. New code
- of the same module may or may not exist. Note that <c>unload</c> is not
- called for a replaced library as a consequence of <c>reload</c>.</p>
+ <tag><marker id="upgrade"/><c>int (*upgrade)(ErlNifEnv* env, void**
+ priv_data, void** old_priv_data, ERL_NIF_TERM load_info)</c></tag>
+ <item>
+ <p><c>upgrade</c> is called when the NIF library is loaded
+ and there is old code of this module with a loaded NIF library.</p>
+ <p>Works as <c>load</c>, except that <c>*old_priv_data</c> already
+ contains the value set by the last call to <c>load</c> or
+ <c>reload</c> for the old module code. <c>*priv_data</c> is
+ initialized to <c>NULL</c> when <c>upgrade</c> is called. It is
+ allowed to write to both <c>*priv_data</c> and
+ <c>*old_priv_data.</c></p>
+ <p>The library fails to load if <c>upgrade</c> returns
+ anything other than <c>0</c> or if <c>upgrade</c> is <c>NULL</c>.</p>
</item>
-
- <tag><marker id="reload"/>int (*reload)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)</tag>
-
- <item>
- <note><p>The reload mechanism is <em>deprecated</em>. It was only intended
- as a development feature. Do not use it as an upgrade method for
- live production systems. It might be removed in future releases. Be sure
- to pass <c>reload</c> as <c>NULL</c> to <seealso marker="#ERL_NIF_INIT">ERL_NIF_INIT</seealso>
- to disable it when not used.</p>
- </note>
- <p><c>reload</c> is called when the NIF library is loaded
- and there is already a previously loaded library for this
- module code.</p>
- <p>Works the same as <c>load</c>. The only difference is that
- <c>*priv_data</c> already contains the value set by the
- previous call to <c>load</c> or <c>reload</c>.</p>
- <p>The library will fail to load if <c>reload</c> returns
- anything other than 0 or if <c>reload</c> is NULL.</p>
+ <tag><marker id="unload"/><c>void (*unload)(ErlNifEnv* env, void*
+ priv_data)</c></tag>
+ <item>
+ <p><c>unload</c> is called when the module code that
+ the NIF library belongs to is purged as old. New code of the same
+ module may or may not exist. Notice that <c>unload</c> is not
+ called for a replaced library as a consequence of <c>reload</c>.</p>
+ </item>
+ <tag><marker id="reload"/><c>int (*reload)(ErlNifEnv* env, void**
+ priv_data, ERL_NIF_TERM load_info)</c></tag>
+ <item>
+ <note>
+ <p><em>The reload mechanism is deprecated.</em> It was only intended
+ as a development feature. Do not use it as an upgrade method for
+ live production systems. It can be removed in future releases.
+ Ensure to pass <c>reload</c> as <c>NULL</c> to
+ <seealso marker="#ERL_NIF_INIT"><c>ERL_NIF_INIT</c></seealso>
+ to disable it when not used.</p>
+ </note>
+ <p><c>reload</c> is called when the NIF library is loaded and a
+ previously loaded library already exists for this module code.</p>
+ <p>Works as <c>load</c>, except that
+ <c>*priv_data</c> already contains the value set by the
+ previous call to <c>load</c> or <c>reload</c>.</p>
+ <p>The library fails to load if <c>reload</c> returns
+ anything other than <c>0</c> or if <c>reload</c> is <c>NULL</c>.</p>
</item>
-
</taglist>
</section>
<section>
- <title>DATA TYPES</title>
-
+ <title>Data Types</title>
<taglist>
- <tag><marker id="ERL_NIF_TERM"/>ERL_NIF_TERM</tag>
- <item>
+ <tag><marker id="ERL_NIF_TERM"/><c>ERL_NIF_TERM</c></tag>
+ <item>
<p>Variables of type <c>ERL_NIF_TERM</c> can refer to any Erlang term.
- This is an opaque type and values of it can only by used either as
- arguments to API functions or as return values from NIFs. All
- <c>ERL_NIF_TERM</c>'s belong to an environment
- (<seealso marker="#ErlNifEnv">ErlNifEnv</seealso>). A term can not be
- destructed individually, it is valid until its environment is destructed.</p>
+ This is an opaque type and values of it can only by used either as
+ arguments to API functions or as return values from NIFs. All
+ <c>ERL_NIF_TERM</c>s belong to an environment
+ (<seealso marker="#ErlNifEnv"><c>ErlNifEnv</c></seealso>).
+ A term cannot be destructed individually, it is valid until its
+ environment is destructed.</p>
</item>
- <tag><marker id="ErlNifEnv"/>ErlNifEnv</tag>
+ <tag><marker id="ErlNifEnv"/><c>ErlNifEnv</c></tag>
<item>
- <p><c>ErlNifEnv</c> represents an environment that can host Erlang terms.
- All terms in an environment are valid as long as the environment is valid.
- <c>ErlNifEnv</c> is an opaque type and pointers to it can only be passed
- on to API functions. There are two types of environments; process
- bound and process independent.</p>
- <p>A <em>process bound environment</em> is passed as the first argument to all NIFs.
- All function arguments passed to a NIF will belong to that environment.
- The return value from a NIF must also be a term belonging to the same
- environment.
- In addition a process bound environment contains transient information
- about the calling Erlang process. The environment is only valid in the
- thread where it was supplied as argument until the NIF returns. It is
- thus useless and dangerous to store pointers to process bound
- environments between NIF calls. </p>
- <p>A <em>process independent environment</em> is created by calling
- <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>. It can be
- used to store terms between NIF calls and to send terms with
- <seealso marker="#enif_send">enif_send</seealso>. A process
- independent environment with all its terms is valid until you explicitly
- invalidates it with <seealso marker="#enif_free_env">enif_free_env</seealso>
- or <c>enif_send</c>.</p>
+ <p><c>ErlNifEnv</c> represents an environment that can host Erlang
+ terms. All terms in an environment are valid as long as the
+ environment is valid. <c>ErlNifEnv</c> is an opaque type; pointers to
+ it can only be passed on to API functions. Two types of environments
+ exist:</p>
+ <taglist>
+ <tag>Process-bound environment</tag>
+ <item>
+ <p>Passed as the first argument to all NIFs. All function arguments
+ passed to a NIF belong to that environment. The return value from
+ a NIF must also be a term belonging to the same environment.</p>
+ <p>A process-bound environment contains transient information
+ about the calling Erlang process. The environment is only valid
+ in the thread where it was supplied as argument until the NIF
+ returns. It is thus useless and dangerous to store pointers to
+ process-bound environments between NIF calls.</p>
+ </item>
+ <tag>Process-independent environment</tag>
+ <item>
+ <p>Created by calling <seealso marker="#enif_alloc_env">
+ <c>enif_alloc_env</c></seealso>. This environment can be
+ used to store terms between NIF calls and to send terms with
+ <seealso marker="#enif_send"><c>enif_send</c></seealso>. A
+ process-independent environment with all its terms is valid until
+ you explicitly invalidate it with
+ <seealso marker="#enif_free_env"><c>enif_free_env</c></seealso>
+ or <c>enif_send</c>.</p>
+ </item>
+ </taglist>
<p>All contained terms of a list/tuple/map must belong to the same
- environment as the list/tuple/map itself. Terms can be copied between
- environments with
- <seealso marker="#enif_make_copy">enif_make_copy</seealso>.</p>
+ environment as the list/tuple/map itself. Terms can be copied between
+ environments with
+ <seealso marker="#enif_make_copy"><c>enif_make_copy</c></seealso>.</p>
</item>
- <tag><marker id="ErlNifFunc"/>ErlNifFunc</tag>
- <item>
- <p/>
- <code type="none">
+ <tag><marker id="ErlNifFunc"/><c>ErlNifFunc</c></tag>
+ <item>
+ <code type="none">
typedef struct {
const char* name;
unsigned arity;
ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
unsigned flags;
-} ErlNifFunc;
-</code>
- <p>Describes a NIF by its name, arity and implementation.
- <c>fptr</c> is a pointer to the function that implements the
- NIF. The argument <c>argv</c> of a NIF will contain the
- function arguments passed to the NIF and <c>argc</c> is the
- length of the array, i.e. the function arity. <c>argv[N-1]</c>
- will thus denote the Nth argument to the NIF. Note that the
- <c>argc</c> argument allows for the same C function to
- implement several Erlang functions with different arity (but
- same name probably). For a regular NIF, <c>flags</c> is 0 (and
- so its value can be omitted for statically initialized <c>ErlNifFunc</c>
- instances), or it can be used to indicate that the NIF is a <seealso
- marker="#dirty_nifs">dirty NIF</seealso> that should be executed
- on a dirty scheduler thread (<em>note that the dirty NIF functionality
- described here is experimental</em> and that you have to enable
- support for dirty schedulers when building OTP in order to try the
- functionality out). If the dirty NIF is expected to be
- CPU-bound, its <c>flags</c> field should be set to
- <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c>, or for I/O-bound jobs,
- <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c>.</p>
- <note><p>If one of the
- <c>ERL_NIF_DIRTY_JOB_*_BOUND</c> flags is set, and the runtime
- system has no support for dirty schedulers, the runtime system
- will refuse to load the NIF library.</p></note>
+} ErlNifFunc;</code>
+ <p>Describes a NIF by its name, arity, and implementation.</p>
+ <taglist>
+ <tag><c>fptr</c></tag>
+ <item>
+ <p>A pointer to the function that implements the NIF.</p>
+ </item>
+ <tag><c>argv</c></tag>
+ <item>
+ <p>Contains the function arguments passed to the NIF.</p>
+ </item>
+ <tag><c>argc</c></tag>
+ <item>
+ <p>The array length, that is, the function arity. <c>argv[N-1]</c>
+ thus denotes the Nth argument to the NIF. Notice that the argument
+ <c>argc</c> allows for the same C function to implement several
+ Erlang functions with different arity (but probably with the same
+ name).</p>
+ </item>
+ <tag><c>flags</c></tag>
+ <item>
+ <p>Is <c>0</c> for a regular NIF (and so its value can be omitted
+ for statically initialized <c>ErlNifFunc</c> instances).</p>
+ <p><c>flags</c> can be used to indicate that the NIF is a
+ <seealso marker="#dirty_nifs">dirty NIF</seealso> that is to be
+ executed on a dirty scheduler thread.</p>
+ <p><em>The dirty NIF functionality described here is
+ experimental.</em> You have to enable support for dirty
+ schedulers when building OTP to try out the functionality.</p>
+ <p>If the dirty NIF is expected to be CPU-bound, its <c>flags</c>
+ field is to be set to <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c> or
+ <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c>.</p>
+ <note>
+ <p>If one of the <c>ERL_NIF_DIRTY_JOB_*_BOUND</c> flags is set,
+ and the runtime system has no support for dirty schedulers,
+ the runtime system refuses to load the NIF library.</p>
+ </note>
+ </item>
+ </taglist>
</item>
- <tag><marker id="ErlNifBinary"/>ErlNifBinary</tag>
- <item>
- <p/>
- <code type="none">
+ <tag><marker id="ErlNifBinary"/><c>ErlNifBinary</c></tag>
+ <item>
+ <code type="none">
typedef struct {
unsigned size;
unsigned char* data;
-} ErlNifBinary;
-</code>
+} ErlNifBinary;</code>
<p><c>ErlNifBinary</c> contains transient information about an
inspected binary term. <c>data</c> is a pointer to a buffer
of <c>size</c> bytes with the raw content of the binary.</p>
- <p>Note that <c>ErlNifBinary</c> is a semi-opaque type and you are
+ <p>Notice that <c>ErlNifBinary</c> is a semi-opaque type and you are
only allowed to read fields <c>size</c> and <c>data</c>.</p>
</item>
-
- <tag><marker id="ErlNifBinaryToTerm"/>ErlNifBinaryToTerm</tag>
+ <tag><marker id="ErlNifBinaryToTerm"/><c>ErlNifBinaryToTerm</c></tag>
<item>
- <p>An enumeration of the options that can be given to
- <seealso marker="#enif_binary_to_term">enif_binary_to_term</seealso>.
- For default behavior, use the value <c>0</c>.</p>
- <taglist>
- <tag><c>ERL_NIF_BIN2TERM_SAFE</c></tag>
- <item><p>Use this option when receiving data from untrusted sources.</p></item>
- </taglist>
+ <p>An enumeration of the options that can be specified to
+ <seealso marker="#enif_binary_to_term">
+ <c>enif_binary_to_term</c></seealso>.
+ For default behavior, use value <c>0</c>.</p>
+ <p>When receiving data from untrusted sources, use option
+ <c>ERL_NIF_BIN2TERM_SAFE</c>.</p>
</item>
-
- <tag><marker id="ErlNifPid"/>ErlNifPid</tag>
- <item>
- <p><c>ErlNifPid</c> is a process identifier (pid). In contrast to
- pid terms (instances of <c>ERL_NIF_TERM</c>), <c>ErlNifPid</c>'s are self
- contained and not bound to any
- <seealso marker="#ErlNifEnv">environment</seealso>. <c>ErlNifPid</c>
- is an opaque type.</p>
- </item>
- <tag><marker id="ErlNifPort"/>ErlNifPort</tag>
- <item>
- <p><c>ErlNifPort</c> is a port identifier. In contrast to
- port id terms (instances of <c>ERL_NIF_TERM</c>), <c>ErlNifPort</c>'s are self
- contained and not bound to any
- <seealso marker="#ErlNifEnv">environment</seealso>. <c>ErlNifPort</c>
- is an opaque type.</p>
- </item>
-
- <tag><marker id="ErlNifResourceType"/>ErlNifResourceType</tag>
- <item>
- <p>Each instance of <c>ErlNifResourceType</c> represent a class of
- memory managed resource objects that can be garbage collected.
+ <tag><marker id="ErlNifPid"/><c>ErlNifPid</c></tag>
+ <item>
+ <p>A process identifier (pid). In contrast to pid terms (instances of
+ <c>ERL_NIF_TERM</c>), <c>ErlNifPid</c>s are self-contained and not
+ bound to any <seealso marker="#ErlNifEnv">environment</seealso>.
+ <c>ErlNifPid</c> is an opaque type.</p>
+ </item>
+ <tag><marker id="ErlNifPort"/><c>ErlNifPort</c></tag>
+ <item>
+ <p>A port identifier. In contrast to port ID terms (instances of
+ <c>ERL_NIF_TERM</c>), <c>ErlNifPort</c>s are self-contained and not
+ bound to any <seealso marker="#ErlNifEnv">environment</seealso>.
+ <c>ErlNifPort</c> is an opaque type.</p>
+ </item>
+ <tag><marker id="ErlNifResourceType"/><c>ErlNifResourceType</c></tag>
+ <item>
+ <p>Each instance of <c>ErlNifResourceType</c> represents a class of
+ memory-managed resource objects that can be garbage collected.
Each resource type has a unique name and a destructor function that
is called when objects of its type are released.</p>
- </item>
- <tag><marker id="ErlNifResourceDtor"/>ErlNifResourceDtor</tag>
- <item>
- <p/>
- <code type="none">
-typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj);
-</code>
- <p>The function prototype of a resource destructor function.</p>
- </item>
- <tag><marker id="ErlNifCharEncoding"/>ErlNifCharEncoding</tag>
- <item>
- <p/>
- <code type="none">
+ </item>
+ <tag><marker id="ErlNifResourceDtor"/><c>ErlNifResourceDtor</c></tag>
+ <item>
+ <code type="none">
+typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj);</code>
+ <p>The function prototype of a resource destructor function.</p>
+ </item>
+ <tag><marker id="ErlNifCharEncoding"/><c>ErlNifCharEncoding</c></tag>
+ <item>
+ <code type="none">
typedef enum {
ERL_NIF_LATIN1
-}ErlNifCharEncoding;
-</code>
- <p>The character encoding used in strings and atoms. The only
- supported encoding is currently <c>ERL_NIF_LATIN1</c> for
- iso-latin-1 (8-bit ascii).</p>
- </item>
- <tag><marker id="ErlNifSysInfo"/>ErlNifSysInfo</tag>
- <item>
- <p>Used by <seealso marker="#enif_system_info">enif_system_info</seealso>
- to return information about the runtime system. Contains currently
- the exact same content as <seealso marker="erl_driver#ErlDrvSysInfo">ErlDrvSysInfo</seealso>.</p>
- </item>
- <tag><marker id="ErlNifSInt64"/>ErlNifSInt64</tag>
- <item><p>A native signed 64-bit integer type.</p></item>
- <tag><marker id="ErlNifUInt64"/>ErlNifUInt64</tag>
- <item><p>A native unsigned 64-bit integer type.</p></item>
-
- <tag><marker id="ErlNifTime"/>ErlNifTime</tag>
+}ErlNifCharEncoding;</code>
+ <p>The character encoding used in strings and atoms. The only
+ supported encoding is <c>ERL_NIF_LATIN1</c> for
+ ISO Latin-1 (8-bit ASCII).</p>
+ </item>
+ <tag><marker id="ErlNifSysInfo"/><c>ErlNifSysInfo</c></tag>
+ <item>
+ <p>Used by <seealso marker="#enif_system_info">
+ <c>enif_system_info</c></seealso> to return information about the
+ runtime system. Contains the same content as
+ <seealso marker="erl_driver#ErlDrvSysInfo">
+ <c>ErlDrvSysInfo</c></seealso>.</p>
+ </item>
+ <tag><marker id="ErlNifSInt64"/><c>ErlNifSInt64</c></tag>
+ <item>
+ <p>A native signed 64-bit integer type.</p>
+ </item>
+ <tag><marker id="ErlNifUInt64"/><c>ErlNifUInt64</c></tag>
+ <item>
+ <p>A native unsigned 64-bit integer type.</p>
+ </item>
+ <tag><marker id="ErlNifTime"/><c>ErlNifTime</c></tag>
<item>
<p>A signed 64-bit integer type for representation of time.</p>
</item>
- <tag><marker id="ErlNifTimeUnit"/>ErlNifTimeUnit</tag>
+ <tag><marker id="ErlNifTimeUnit"/><c>ErlNifTimeUnit</c></tag>
<item>
<p>An enumeration of time units supported by the NIF API:</p>
- <taglist>
- <tag><c>ERL_NIF_SEC</c></tag>
- <item><p>Seconds</p></item>
- <tag><c>ERL_NIF_MSEC</c></tag>
- <item><p>Milliseconds</p></item>
- <tag><c>ERL_NIF_USEC</c></tag>
- <item><p>Microseconds</p></item>
- <tag><c>ERL_NIF_NSEC</c></tag>
- <item><p>Nanoseconds</p></item>
- </taglist>
+ <taglist>
+ <tag><c>ERL_NIF_SEC</c></tag>
+ <item>Seconds</item>
+ <tag><c>ERL_NIF_MSEC</c></tag>
+ <item>Milliseconds</item>
+ <tag><c>ERL_NIF_USEC</c></tag>
+ <item>Microseconds</item>
+ <tag><c>ERL_NIF_NSEC</c></tag>
+ <item>Nanoseconds</item>
+ </taglist>
</item>
-
- <tag><marker id="ErlNifUniqueInteger"/>ErlNifUniqueInteger</tag>
+ <tag><marker id="ErlNifUniqueInteger"/><c>ErlNifUniqueInteger</c></tag>
<item>
<p>An enumeration of the properties that can be requested from
- <seealso marker="#enif_make_unique_integer">enif_unique_integer</seealso>.
- For default properties, use the value <c>0</c>.</p>
+ <seealso marker="#enif_make_unique_integer">
+ <c>enif_unique_integer</c></seealso>.
+ For default properties, use value <c>0</c>.</p>
<taglist>
- <tag><c>ERL_NIF_UNIQUE_POSITIVE</c></tag>
- <item><p>Return only positive integers</p></item>
- <tag><c>ERL_NIF_UNIQUE_MONOTONIC</c></tag>
- <item><p>Return only
- <seealso marker="time_correction#Strictly_Monotonically_Increasing">strictly
- monotonically increasing</seealso> integer corresponding to creation time</p></item>
- </taglist>
+ <tag><c>ERL_NIF_UNIQUE_POSITIVE</c></tag>
+ <item>
+ <p>Return only positive integers.</p>
+ </item>
+ <tag><c>ERL_NIF_UNIQUE_MONOTONIC</c></tag>
+ <item>
+ <p>Return only <seealso
+ marker="time_correction#Strictly_Monotonically_Increasing">
+ strictly monotonically increasing</seealso> integer corresponding
+ to creation time.</p>
+ </item>
+ </taglist>
</item>
-
</taglist>
</section>
<funcs>
- <func><name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name>
- <fsummary>Allocate dynamic memory</fsummary>
- <desc><p>Allocate memory of <c>size</c> bytes. Return NULL if allocation failed.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_alloc_binary(size_t size, ErlNifBinary* bin)</nametext></name>
- <fsummary>Create a new binary</fsummary>
- <desc><p>Allocate a new binary of size <c>size</c>
- bytes. Initialize the structure pointed to by <c>bin</c> to
- refer to the allocated binary. The binary must either be released by
- <seealso marker="#enif_release_binary">enif_release_binary</seealso>
- or ownership transferred to an Erlang term with
- <seealso marker="#enif_make_binary">enif_make_binary</seealso>.
- An allocated (and owned) <c>ErlNifBinary</c> can be kept between NIF
- calls.</p>
- <p>Return true on success or false if allocation failed.</p>
- </desc>
- </func>
- <func><name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name>
- <fsummary>Create a new environment</fsummary>
- <desc><p>Allocate a new process independent environment. The environment can
- be used to hold terms that is not bound to any process. Such terms can
- later be copied to a process environment with
- <seealso marker="#enif_make_copy">enif_make_copy</seealso>
- or be sent to a process as a message with <seealso marker="#enif_send">enif_send</seealso>.</p>
- <p>Return pointer to the new environment.</p>
- </desc>
- </func>
- <func><name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name>
- <fsummary>Allocate a memory managed resource object</fsummary>
- <desc><p>Allocate a memory managed resource object of type <c>type</c> and size <c>size</c> bytes.</p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext></name>
- <fsummary>Clear an environment for reuse</fsummary>
- <desc><p>Free all terms in an environment and clear it for reuse. The environment must
- have been allocated with <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>.
- </p></desc>
- </func>
- <func><name><ret>size_t</ret><nametext>enif_binary_to_term(ErlNifEnv *env, const unsigned char* data, size_t size, ERL_NIF_TERM *term, ErlNifBinaryToTerm opts)</nametext></name>
- <fsummary>Create a term from the external format</fsummary>
- <desc>
- <p>Create a term that is the result of decoding the binary data
- at <c>data</c>, which must be encoded according to the Erlang external term format.
- No more than <c>size</c> bytes are read from <c>data</c>. Argument <c>opts</c>
- correspond to the second argument to <seealso marker="erlang#binary_to_term-2">
- <c>erlang:binary_to_term/2</c></seealso>, and must be either <c>0</c> or
- <c>ERL_NIF_BIN2TERM_SAFE</c>.</p>
- <p>On success, store the resulting term at <c>*term</c> and return
- the actual number of bytes read. Return zero if decoding fails or if <c>opts</c>
- is invalid.</p>
- <p>See also:
- <seealso marker="#ErlNifBinaryToTerm"><c>ErlNifBinaryToTerm</c></seealso>,
- <seealso marker="erlang#binary_to_term-2"><c>erlang:binary_to_term/2</c></seealso> and
- <seealso marker="#enif_term_to_binary"><c>enif_term_to_binary</c></seealso>.
- </p>
- </desc>
+ <func>
+ <name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name>
+ <fsummary>Allocate dynamic memory.</fsummary>
+ <desc>
+ <p>Allocates memory of <c>size</c> bytes.</p>
+ <p>Returns <c>NULL</c> if the allocation fails.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_alloc_binary(size_t size, ErlNifBinary* bin)</nametext>
+ </name>
+ <fsummary>Create a new binary.</fsummary>
+ <desc>
+ <p>Allocates a new binary of size <c>size</c> bytes.
+ Initializes the structure pointed to by <c>bin</c> to
+ refer to the allocated binary. The binary must either be released by
+ <seealso marker="#enif_release_binary">
+ <c>enif_release_binary</c></seealso>
+ or ownership transferred to an Erlang term with
+ <seealso marker="#enif_make_binary"><c>enif_make_binary</c></seealso>.
+ An allocated (and owned) <c>ErlNifBinary</c> can be kept between NIF
+ calls.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if allocation
+ fails.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name>
+ <fsummary>Create a new environment.</fsummary>
+ <desc>
+ <p>Allocates a new process-independent environment. The environment can
+ be used to hold terms that are not bound to any process. Such terms
+ can later be copied to a process environment with
+ <seealso marker="#enif_make_copy"><c>enif_make_copy</c></seealso> or
+ be sent to a process as a message with <seealso marker="#enif_send">
+ <c>enif_send</c></seealso>.</p>
+ <p>Returns pointer to the new environment.</p>
+ </desc>
</func>
- <func><name><ret>int</ret><nametext>enif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext></name>
- <fsummary>Compare two terms</fsummary>
- <desc><p>Return an integer less than, equal to, or greater than
- zero if <c>lhs</c> is found, respectively, to be less than,
- equal, or greater than <c>rhs</c>. Corresponds to the Erlang
- operators <c>==</c>, <c>/=</c>, <c>=&lt;</c>, <c>&lt;</c>,
- <c>&gt;=</c> and <c>&gt;</c> (but <em>not</em> <c>=:=</c> or <c>=/=</c>).</p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_cond_broadcast(ErlNifCond *cnd)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_broadcast">erl_drv_cond_broadcast</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ErlNifCond *</ret><nametext>enif_cond_create(char *name)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_create">erl_drv_cond_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_cond_destroy(ErlNifCond *cnd)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_destroy">erl_drv_cond_destroy</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_cond_signal(ErlNifCond *cnd)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_signal">erl_drv_cond_signal</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_cond_wait(ErlNifCond *cnd, ErlNifMutex *mtx)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_wait">erl_drv_cond_wait</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_consume_timeslice(ErlNifEnv *env, int percent)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Give the runtime system a hint about how much CPU time the current NIF call has consumed
- since last hint, or since the start of the NIF if no previous hint has been given.
- The time is given as a <c>percent</c> of the timeslice that a process is allowed to execute Erlang
- code until it may be suspended to give time for other runnable processes.
- The scheduling timeslice is not an exact entity, but can usually be
- approximated to about 1 millisecond.</p>
- <p>Note that it is up to the runtime system to determine if and how to use this information.
- Implementations on some platforms may use other means in order to determine consumed
- CPU time. Lengthy NIFs should regardless of this frequently call <c>enif_consume_timeslice</c>
- in order to determine if it is allowed to continue execution or not.</p>
-
- <p>Returns 1 if the timeslice is exhausted, or 0 otherwise. If 1 is returned the NIF should return
- as soon as possible in order for the process to yield.</p>
- <p>Argument <c>percent</c> must be an integer between 1 and 100. This function
- must only be called from a NIF-calling thread and argument <c>env</c> must be
- the environment of the calling process.</p>
- <p>This function is provided to better support co-operative scheduling, improve system responsiveness,
- and make it easier to prevent misbehaviors of the VM due to a NIF monopolizing a scheduler thread.
- It can be used to divide <seealso marker="#lengthy_work">length work</seealso> into
- a number of repeated NIF-calls without the need to create threads.
- See also the <seealso marker="#WARNING">warning</seealso> text at the beginning of this document.</p>
- </desc>
+ <func>
+ <name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType*
+ type, unsigned size)</nametext></name>
+ <fsummary>Allocate a memory-managed resource object.</fsummary>
+ <desc>
+ <p>Allocates a memory-managed resource object of type <c>type</c> and
+ size <c>size</c> bytes.</p>
+ </desc>
</func>
<func>
- <name><ret>ErlNifTime</ret><nametext>enif_convert_time_unit(ErlNifTime val, ErlNifTimeUnit from, ErlNifTimeUnit to)</nametext></name>
- <fsummary>Convert time unit of a time value</fsummary>
+ <name><ret>size_t</ret><nametext>enif_binary_to_term(ErlNifEnv *env,
+ const unsigned char* data, size_t size, ERL_NIF_TERM *term,
+ ErlNifBinaryToTerm opts)</nametext></name>
+ <fsummary>Create a term from the external format.</fsummary>
<desc>
- <marker id="enif_convert_time_unit"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>val</c></tag>
- <item>Value to convert time unit for.</item>
- <tag><c>from</c></tag>
- <item>Time unit of <c>val</c>.</item>
- <tag><c>to</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>Converts the <c>val</c> value of time unit <c>from</c> to
- the corresponding value of time unit <c>to</c>. The result is
- rounded using the floor function.</p>
- <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid
- time unit argument.</p>
- <p>See also:
- <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso> and
- <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.
- </p>
+ <p>Creates a term that is the result of decoding the binary data at
+ <c>data</c>, which must be encoded according to the Erlang external
+ term format. No more than <c>size</c> bytes are read from <c>data</c>.
+ Argument <c>opts</c> corresponds to the second argument to
+ <seealso marker="erlang#binary_to_term-2">
+ <c>erlang:binary_to_term/2</c></seealso> and must be either <c>0</c>
+ or <c>ERL_NIF_BIN2TERM_SAFE</c>.</p>
+ <p>On success, stores the resulting term at <c>*term</c> and returns
+ the number of bytes read. Returns <c>0</c> if decoding fails or if
+ <c>opts</c> is invalid.</p>
+ <p>See also <seealso marker="#ErlNifBinaryToTerm">
+ <c>ErlNifBinaryToTerm</c></seealso>,
+ <seealso marker="erlang#binary_to_term-2">
+ <c>erlang:binary_to_term/2</c></seealso>, and
+ <seealso marker="#enif_term_to_binary">
+ <c>enif_term_to_binary</c></seealso>.</p>
</desc>
</func>
<func>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_cpu_time(ErlNifEnv *)</nametext></name>
+ <name><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext>
+ </name>
+ <fsummary>Clear an environment for reuse.</fsummary>
+ <desc>
+ <p>Frees all terms in an environment and clears it for reuse.
+ The environment must have been allocated with
+ <seealso marker="#enif_alloc_env"><c>enif_alloc_env</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext>
+ </name>
+ <fsummary>Compare two terms.</fsummary>
+ <desc>
+ <p>Returns an integer &lt; <c>0</c> if <c>lhs</c> &lt; <c>rhs</c>,
+ <c>0</c> if <c>lhs</c> = <c>rhs</c>, and &gt; <c>0</c> if
+ <c>lhs</c> &gt; <c>rhs</c>. Corresponds to the Erlang
+ operators <c>==</c>, <c>/=</c>, <c>=&lt;</c>, <c>&lt;</c>,
+ <c>&gt;=</c>, and <c>&gt;</c> (but <em>not</em> <c>=:=</c> or
+ <c>=/=</c>).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_cond_broadcast(ErlNifCond *cnd)</nametext></name>
<fsummary></fsummary>
<desc>
- <p>Returns the CPU time in the same format as <seealso marker="erlang#timestamp-0">erlang:timestamp()</seealso>.
- The CPU time is the time the current logical cpu has spent executing since
- some arbitrary point in the past.
- If the OS does not support fetching of this value <c>enif_cpu_time</c>
- invokes <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.
- </p>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_broadcast">
+ <c>erl_drv_cond_broadcast</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifCond *</ret>
+ <nametext>enif_cond_create(char *name)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_create">
+ <c>erl_drv_cond_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_cond_destroy(ErlNifCond *cnd)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_destroy">
+ <c>erl_drv_cond_destroy</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_cond_signal(ErlNifCond *cnd)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_signal">
+ <c>erl_drv_cond_signal</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_cond_wait(ErlNifCond *cnd, ErlNifMutex *mtx)</nametext>
+ </name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_cond_wait">
+ <c>erl_drv_cond_wait</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_consume_timeslice(ErlNifEnv *env, int percent)</nametext>
+ </name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Gives the runtime system a hint about how much CPU time the current
+ NIF call has consumed since the last hint, or since the start of the
+ NIF if no previous hint has been specified. The time is specified as a
+ percent of the timeslice that a process is allowed to execute
+ Erlang code until it can be suspended to give time for other runnable
+ processes. The scheduling timeslice is not an exact entity, but can
+ usually be approximated to about 1 millisecond.</p>
+ <p>Notice that it is up to the runtime system to determine if and how
+ to use this information. Implementations on some platforms can use
+ other means to determine consumed CPU time. Lengthy NIFs should
+ regardless of this frequently call <c>enif_consume_timeslice</c> to
+ determine if it is allowed to continue execution.</p>
+ <p>Argument <c>percent</c> must be an integer between 1 and 100. This
+ function must only be called from a NIF-calling thread, and argument
+ <c>env</c> must be the environment of the calling process.</p>
+ <p>Returns <c>1</c> if the timeslice is exhausted, otherwise <c>0</c>.
+ If <c>1</c> is returned, the NIF is to return as soon as possible in
+ order for the process to yield.</p>
+ <p>This function is provided to better support co-operative scheduling,
+ improve system responsiveness, and make it easier to prevent
+ misbehaviors of the VM because of a NIF monopolizing a scheduler
+ thread. It can be used to divide <seealso marker="#lengthy_work">
+ length work</seealso> into a number of repeated NIF calls without the
+ need to create threads.</p>
+ <p>See also the <seealso marker="#WARNING">warning</seealso> text at
+ the beginning of this manual page.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifTime</ret><nametext>enif_convert_time_unit(ErlNifTime
+ val, ErlNifTimeUnit from, ErlNifTimeUnit to)</nametext></name>
+ <fsummary>Convert time unit of a time value.</fsummary>
+ <desc>
+ <marker id="enif_convert_time_unit"></marker>
+ <p>Converts the <c>val</c> value of time unit <c>from</c> to
+ the corresponding value of time unit <c>to</c>. The result is
+ rounded using the floor function.</p>
+ <taglist>
+ <tag><c>val</c></tag>
+ <item>Value to convert time unit for.</item>
+ <tag><c>from</c></tag>
+ <item>Time unit of <c>val</c>.</item>
+ <tag><c>to</c></tag>
+ <item>Time unit of returned value.</item>
+ </taglist>
+ <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid
+ time unit argument.</p>
+ <p>See also <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso>
+ and
+ <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_cpu_time(ErlNifEnv *)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Returns the CPU time in the same format as
+ <seealso marker="erlang#timestamp-0">
+ <c>erlang:timestamp()</c></seealso>.
+ The CPU time is the time the current logical CPU has spent executing
+ since some arbitrary point in the past. If the OS does not support
+ fetching this value, <c>enif_cpu_time</c> invokes
+ <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)</nametext>
+ </name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_equal_tids">
+ <c>erl_drv_equal_tids</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name>
+ <fsummary>Free dynamic memory.</fsummary>
+ <desc>
+ <p>Frees memory allocated by
+ <seealso marker="#enif_alloc"><c>enif_alloc</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_free_env(ErlNifEnv* env)</nametext></name>
+ <fsummary>Free an environment allocated with enif_alloc_env.</fsummary>
+ <desc>
+ <p>Frees an environment allocated with
+ <seealso marker="#enif_alloc_env"><c>enif_alloc_env</c></seealso>.
+ All terms created in the environment are freed as well.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM
+ term, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext>
+ </name>
+ <fsummary>Get the text representation of an atom term.</fsummary>
+ <desc>
+ <p>Writes a <c>NULL</c>-terminated string in the buffer pointed to by
+ <c>buf</c> of size <c>size</c>, consisting of the string
+ representation of the atom <c>term</c> with encoding
+ <seealso marker="#ErlNifCharEncoding">encode</seealso>.</p>
+ <p>Returns the number of bytes written (including terminating
+ <c>NULL</c> character) or <c>0</c> if <c>term</c> is not an atom with
+ maximum length of <c>size-1</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_atom_length(ErlNifEnv* env,
+ ERL_NIF_TERM term, unsigned* len, ErlNifCharEncoding encode)</nametext>
+ </name>
+ <fsummary>Get the length of atom <c>term</c>.</fsummary>
+ <desc>
+ <p>Sets <c>*len</c> to the length (number of bytes excluding
+ terminating <c>NULL</c> character) of the atom <c>term</c> with
+ encoding <c>encode</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not
+ an atom.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_double(ErlNifEnv* env,
+ ERL_NIF_TERM term, double* dp)</nametext></name>
+ <fsummary>Read a floating-point number term.</fsummary>
+ <desc>
+ <p>Sets <c>*dp</c> to the floating-point value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not
+ a float.</p>
</desc>
</func>
- <func><name><ret>int</ret><nametext>enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_equal_tids">erl_drv_equal_tids</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name>
- <fsummary>Free dynamic memory</fsummary>
- <desc><p>Free memory allocated by <c>enif_alloc</c>.</p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_free_env(ErlNifEnv* env)</nametext></name>
- <fsummary>Free an environment allocated with enif_alloc_env</fsummary>
- <desc><p>Free an environment allocated with <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>.
- All terms created in the environment will be freed as well.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM term, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext></name>
- <fsummary>Get the text representation of an atom term</fsummary>
- <desc><p>Write a null-terminated string, in the buffer pointed to by
- <c>buf</c> of size <c>size</c>, consisting of the string
- representation of the atom <c>term</c> with encoding
- <seealso marker="#ErlNifCharEncoding">encode</seealso>. Return
- the number of bytes written (including terminating null character) or 0 if
- <c>term</c> is not an atom with maximum length of
- <c>size-1</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_atom_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len, ErlNifCharEncoding encode)</nametext></name>
- <fsummary>Get the length of atom <c>term</c></fsummary>
- <desc><p>Set <c>*len</c> to the length (number of bytes excluding
- terminating null character) of the atom <c>term</c> with encoding
- <c>encode</c>. Return true on success or false if <c>term</c> is not an
- atom.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_double(ErlNifEnv* env, ERL_NIF_TERM term, double* dp)</nametext></name>
- <fsummary>Read a floating-point number term</fsummary>
- <desc><p>Set <c>*dp</c> to the floating point value of
- <c>term</c>. Return true on success or false if <c>term</c> is not a float.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_int(ErlNifEnv* env, ERL_NIF_TERM term, int* ip)</nametext></name>
- <fsummary>Read an integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the integer value of
- <c>term</c>. Return true on success or false if <c>term</c> is not an
- integer or is outside the bounds of type <c>int</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifSInt64* ip)</nametext></name>
- <fsummary>Read a 64-bit integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the integer value of
- <c>term</c>. Return true on success or false if <c>term</c> is not an
- integer or is outside the bounds of a signed 64-bit integer.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid)</nametext></name>
- <fsummary>Read an local pid term</fsummary>
- <desc><p>If <c>term</c> is the pid of a node local process, initialize the
- pid variable <c>*pid</c> from it and return true. Otherwise return false.
- No check if the process is alive is done.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_local_port(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPort* port_id)</nametext></name>
- <fsummary>Read an local port term</fsummary>
- <desc><p>If <c>term</c> identifies a node local port, initialize the
- port variable <c>*port_id</c> from it and return true. Otherwise return false.
- No check if the port is alive is done.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_list_cell(ErlNifEnv* env, ERL_NIF_TERM list, ERL_NIF_TERM* head, ERL_NIF_TERM* tail)</nametext></name>
- <fsummary>Get head and tail from a list</fsummary>
- <desc><p>Set <c>*head</c> and <c>*tail</c> from
- <c>list</c> and return true, or return false if <c>list</c> is not a
- non-empty list.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_list_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len)</nametext></name>
- <fsummary>Get the length of list <c>term</c></fsummary>
- <desc><p>Set <c>*len</c> to the length of list <c>term</c> and return true,
- or return false if <c>term</c> is not a proper list.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_long(ErlNifEnv* env, ERL_NIF_TERM term, long int* ip)</nametext></name>
- <fsummary>Read an long integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the long integer value of <c>term</c> and
- return true, or return false if <c>term</c> is not an integer or is
- outside the bounds of type <c>long int</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)</nametext></name>
- <fsummary>Read the size of a map term</fsummary>
- <desc><p>Set <c>*size</c> to the number of key-value pairs in the map <c>term</c> and
- return true, or return false if <c>term</c> is not a map.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_map_value(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)</nametext></name>
- <fsummary>Get the value of a key in a map</fsummary>
- <desc><p>Set <c>*value</c> to the value associated with <c>key</c> in the
- map <c>map</c> and return true. Return false if <c>map</c> is not a map
- or if <c>map</c> does not contain <c>key</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)</nametext></name>
- <fsummary>Get the pointer to a resource object</fsummary>
- <desc><p>Set <c>*objp</c> to point to the resource object referred to by <c>term</c>.</p>
- <p>Return true on success or false if <c>term</c> is not a handle to a resource object
- of type <c>type</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_string(ErlNifEnv* env,
- ERL_NIF_TERM list, char* buf, unsigned size,
- ErlNifCharEncoding encode)</nametext></name>
- <fsummary>Get a C-string from a list</fsummary>
- <desc><p>Write a null-terminated string, in the buffer pointed to by
- <c>buf</c> with size <c>size</c>, consisting of the characters
- in the string <c>list</c>. The characters are written using encoding
- <seealso marker="#ErlNifCharEncoding">encode</seealso>.
- Return the number of bytes written (including terminating null
- character), or <c>-size</c> if the string was truncated due to
- buffer space, or 0 if <c>list</c> is not a string that can be
- encoded with <c>encode</c> or if <c>size</c> was less than 1.
- The written string is always null-terminated unless buffer
- <c>size</c> is less than 1.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM term, int* arity, const ERL_NIF_TERM** array)</nametext></name>
- <fsummary>Inspect the elements of a tuple</fsummary>
- <desc><p>If <c>term</c> is a tuple, set <c>*array</c> to point
- to an array containing the elements of the tuple and set
- <c>*arity</c> to the number of elements. Note that the array
- is read-only and <c>(*array)[N-1]</c> will be the Nth element of
- the tuple. <c>*array</c> is undefined if the arity of the tuple
- is zero.</p><p>Return true on success or false if <c>term</c> is not a
- tuple.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM term, unsigned int* ip)</nametext></name>
- <fsummary>Read an unsigned integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the unsigned integer value of <c>term</c> and
- return true, or return false if <c>term</c> is not an unsigned integer or
- is outside the bounds of type <c>unsigned int</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip)</nametext></name>
- <fsummary>Read an unsigned 64-bit integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the unsigned integer value of <c>term</c> and
- return true, or return false if <c>term</c> is not an unsigned integer or
- is outside the bounds of an unsigned 64-bit integer.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM term, unsigned long* ip)</nametext></name>
- <fsummary>Read an unsigned integer term</fsummary>
- <desc><p>Set <c>*ip</c> to the unsigned long integer value of <c>term</c>
- and return true, or return false if <c>term</c> is not an unsigned integer or is
- outside the bounds of type <c>unsigned long</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_getenv(const char* key, char* value, size_t *value_size)</nametext></name>
- <fsummary>Get the value of an environment variable</fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_getenv">erl_drv_getenv</seealso>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)</nametext></name>
- <fsummary>Check if an exception has been raised</fsummary>
- <desc><p>Return true if a pending exception is associated
- with the environment <c>env</c>. If <c>reason</c> is a null pointer, ignore it.
- Otherwise, if there's a pending exception associated with <c>env</c>, set the ERL_NIF_TERM
- to which <c>reason</c> points to the value of the exception's term. For example, if
- <seealso marker="#enif_make_badarg">enif_make_badarg</seealso> is called to set a
- pending <c>badarg</c> exception, a subsequent call to <c>enif_has_pending_exception(env, &amp;reason)</c>
- will set <c>reason</c> to the atom <c>badarg</c>, then return true.</p>
- <p>See also: <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>
- and <seealso marker="#enif_raise_exception">enif_raise_exception</seealso>.</p>
- </desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin)</nametext></name>
- <fsummary>Inspect the content of a binary</fsummary>
- <desc><p>Initialize the structure pointed to by <c>bin</c> with
- information about the binary term
- <c>bin_term</c>. Return true on success or false if <c>bin_term</c> is not a binary.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_inspect_iolist_as_binary(ErlNifEnv*
- env, ERL_NIF_TERM term, ErlNifBinary* bin)
- </nametext></name>
- <fsummary>Inspect the content of an iolist</fsummary>
- <desc><p>Initialize the structure pointed to by <c>bin</c> with one
- continuous buffer with the same byte content as <c>iolist</c>. As with
- inspect_binary, the data pointed to by <c>bin</c> is transient and does
- not need to be released. Return true on success or false if <c>iolist</c> is not an
- iolist.</p>
+ <func>
+ <name><ret>int</ret><nametext>enif_get_int(ErlNifEnv* env, ERL_NIF_TERM
+ term, int* ip)</nametext></name>
+ <fsummary>Read an integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the integer value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not
+ an integer or is outside the bounds of type <c>int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM
+ term, ErlNifSInt64* ip)</nametext></name>
+ <fsummary>Read a 64-bit integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the integer value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not
+ an integer or is outside the bounds of a signed 64-bit integer.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env,
+ ERL_NIF_TERM term, ErlNifPid* pid)</nametext></name>
+ <fsummary>Read a local pid term.</fsummary>
+ <desc>
+ <p>If <c>term</c> is the pid of a node local process, this function
+ initializes the pid variable <c>*pid</c> from it and returns
+ <c>true</c>. Otherwise returns <c>false</c>. No check is done to see
+ if the process is alive.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_local_port(ErlNifEnv* env,
+ ERL_NIF_TERM term, ErlNifPort* port_id)</nametext></name>
+ <fsummary>Read a local port term.</fsummary>
+ <desc>
+ <p>If <c>term</c> identifies a node local port, this function
+ initializes the port variable <c>*port_id</c> from it and returns
+ <c>true</c>. Otherwise returns <c>false</c>. No check is done to see
+ if the port is alive.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_list_cell(ErlNifEnv* env,
+ ERL_NIF_TERM list, ERL_NIF_TERM* head, ERL_NIF_TERM* tail)</nametext>
+ </name>
+ <fsummary>Get head and tail from a list.</fsummary>
+ <desc>
+ <p>Sets <c>*head</c> and <c>*tail</c> from list <c>list</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if it is
+ not a list or the list is empty.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_list_length(ErlNifEnv* env,
+ ERL_NIF_TERM term, unsigned* len)</nametext></name>
+ <fsummary>Get the length of list <c>term</c>.</fsummary>
+ <desc>
+ <p>Sets <c>*len</c> to the length of list <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not a proper list.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_long(ErlNifEnv* env, ERL_NIF_TERM
+ term, long int* ip)</nametext></name>
+ <fsummary>Read a long integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the long integer value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not an integer or is outside the bounds of type <c>long int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_map_size(ErlNifEnv* env,
+ ERL_NIF_TERM term, size_t *size)</nametext></name>
+ <fsummary>Read the size of a map term.</fsummary>
+ <desc>
+ <p>Sets <c>*size</c> to the number of key-value pairs in the map
+ <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not a map.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_map_value(ErlNifEnv* env,
+ ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)</nametext>
+ </name>
+ <fsummary>Get the value of a key in a map.</fsummary>
+ <desc>
+ <p>Sets <c>*value</c> to the value associated with <c>key</c> in the
+ map <c>map</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>map</c> is not
+ a map or if <c>map</c> does not contain <c>key</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env,
+ ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)</nametext>
+ </name>
+ <fsummary>Get the pointer to a resource object.</fsummary>
+ <desc>
+ <p>Sets <c>*objp</c> to point to the resource object referred to by
+ <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not a handle to a resource object of type <c>type</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_string(ErlNifEnv* env,
+ ERL_NIF_TERM list, char* buf, unsigned size,
+ ErlNifCharEncoding encode)</nametext></name>
+ <fsummary>Get a C-string from a list.</fsummary>
+ <desc>
+ <p>Writes a <c>NULL</c>-terminated string in the buffer pointed to by
+ <c>buf</c> with size <c>size</c>, consisting of the characters
+ in the string <c>list</c>. The characters are written using encoding
+ <seealso marker="#ErlNifCharEncoding">encode</seealso>.</p>
+ <p>Returns one of the following:</p>
+ <list type="bulleted">
+ <item>The number of bytes written (including terminating <c>NULL</c>
+ character)</item>
+ <item><c>-size</c> if the string was truncated because of buffer
+ space</item>
+ <item><c>0</c> if <c>list</c> is not a string that can be encoded
+ with <c>encode</c> or if <c>size</c> was &lt; <c>1</c>.</item>
+ </list>
+ <p>The written string is always <c>NULL</c>-terminated, unless buffer
+ <c>size</c> is &lt; <c>1</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM
+ term, int* arity, const ERL_NIF_TERM** array)</nametext></name>
+ <fsummary>Inspect the elements of a tuple.</fsummary>
+ <desc>
+ <p>If <c>term</c> is a tuple, this function sets <c>*array</c> to point
+ to an array containing the elements of the tuple, and sets
+ <c>*arity</c> to the number of elements. Notice that the array
+ is read-only and <c>(*array)[N-1]</c> is the Nth element of
+ the tuple. <c>*array</c> is undefined if the arity of the tuple
+ is zero.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not a tuple.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM
+ term, unsigned int* ip)</nametext></name>
+ <fsummary>Read an unsigned integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the unsigned integer value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not an unsigned integer or is outside the bounds of type
+ <c>unsigned int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env,
+ ERL_NIF_TERM term, ErlNifUInt64* ip)</nametext></name>
+ <fsummary>Read an unsigned 64-bit integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the unsigned integer value of <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not an unsigned integer or is outside the bounds of an unsigned
+ 64-bit integer.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM
+ term, unsigned long* ip)</nametext></name>
+ <fsummary>Read an unsigned integer term.</fsummary>
+ <desc>
+ <p>Sets <c>*ip</c> to the unsigned long integer value of
+ <c>term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is
+ not an unsigned integer or is outside the bounds of type
+ <c>unsigned long</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_getenv(const char* key, char* value,
+ size_t *value_size)</nametext></name>
+ <fsummary>Get the value of an environment variable.</fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_getenv">
+ <c>erl_drv_getenv</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env,
+ ERL_NIF_TERM* reason)</nametext></name>
+ <fsummary>Check if an exception has been raised.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if a pending exception is associated with the
+ environment <c>env</c>. If <c>reason</c> is a <c>NULL</c> pointer,
+ ignore it. Otherwise, if a pending exception associated with
+ <c>env</c> exists, set <c>ERL_NIF_TERM</c> to which <c>reason</c>
+ points to the value of the exception's term. For example, if
+ <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso> is called to set a pending
+ <c>badarg</c> exception, a later call to
+ <c>enif_has_pending_exception(env, &amp;reason)</c> sets
+ <c>reason</c> to the atom <c>badarg</c>, then return <c>true</c>.</p>
+ <p>See also <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso> and
+ <seealso marker="#enif_raise_exception">
+ <c>enif_raise_exception</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env,
+ ERL_NIF_TERM bin_term, ErlNifBinary* bin)</nametext></name>
+ <fsummary>Inspect the content of a binary.</fsummary>
+ <desc>
+ <p>Initializes the structure pointed to by <c>bin</c> with information
+ about binary term <c>bin_term</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>bin_term</c>
+ is not a binary.</p>
</desc>
</func>
- <func><name><ret>int</ret><nametext>enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is an atom</fsummary>
- <desc><p>Return true if <c>term</c> is an atom.</p></desc>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_inspect_iolist_as_binary(ErlNifEnv*
+ env, ERL_NIF_TERM term, ErlNifBinary* bin)</nametext></name>
+ <fsummary>Inspect the content of an iolist.</fsummary>
+ <desc>
+ <p>Initializes the structure pointed to by <c>bin</c> with a
+ continuous buffer with the same byte content as <c>iolist</c>. As
+ with <c>inspect_binary</c>, the data pointed to by <c>bin</c> is
+ transient and does not need to be released.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>iolist</c> is
+ not an iolist.</p>
+ </desc>
</func>
- <func><name><ret>int</ret><nametext>enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a binary</fsummary>
- <desc><p>Return true if <c>term</c> is a binary</p></desc>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is an atom.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is an atom.</p>
+ </desc>
</func>
- <func><name><ret>int</ret><nametext>enif_is_current_process_alive(ErlNifEnv* env)</nametext></name>
- <fsummary>Determine if currently executing process is alive or not.</fsummary>
- <desc><p>Return true if currently executing process is currently alive; otherwise
- false.</p>
- <p>This function can only be used from a NIF-calling thread, and with an
- environment corresponding to currently executing processes.</p></desc>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a binary.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a binary.</p>
+ </desc>
</func>
- <func><name><ret>int</ret><nametext>enif_is_empty_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is an empty list</fsummary>
- <desc><p>Return true if <c>term</c> is an empty list.</p></desc>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_current_process_alive(ErlNifEnv* env)</nametext>
+ </name>
+ <fsummary>Determine if currently executing process is alive.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if the currently executing process is currently
+ alive, otherwise <c>false</c>.</p>
+ <p>This function can only be used from a NIF-calling thread, and with
+ an environment corresponding to currently executing processes.</p>
+ </desc>
</func>
- <func><name><ret>int</ret><nametext>enif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is an exception</fsummary>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_empty_list(ErlNifEnv* env,
+ ERL_NIF_TERM term)</nametext></name>
+ <fsummary>Determine if a term is an empty list.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is an empty list.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_exception(ErlNifEnv* env,
+ ERL_NIF_TERM term)</nametext></name>
+ <fsummary>Determine if a term is an exception.</fsummary>
<desc><marker id="enif_is_exception"/>
- <p>Return true if <c>term</c> is an exception.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a map</fsummary>
- <desc><p>Return true if <c>term</c> is a map, false otherwise.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a number (integer or float)</fsummary>
- <desc><p>Return true if <c>term</c> is a number.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a fun</fsummary>
- <desc><p>Return true if <c>term</c> is a fun.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_identical(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext></name>
- <fsummary>Erlang operator =:=</fsummary>
- <desc><p>Return true if the two terms are identical. Corresponds to the
- Erlang operators <c>=:=</c> and
- <c>=/=</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a pid</fsummary>
- <desc><p>Return true if <c>term</c> is a pid.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a port</fsummary>
- <desc><p>Return true if <c>term</c> is a port.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_port_alive(ErlNifEnv* env, ErlNifPort *port_id)</nametext></name>
- <fsummary>Determine if a local port is alive or not.</fsummary>
- <desc><p>Return true if <c>port_id</c> is currently alive.</p>
- <p>This function is only thread-safe when the emulator with SMP support is used.
- It can only be used in a non-SMP emulator from a NIF-calling thread.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_process_alive(ErlNifEnv* env, ErlNifPid *pid)</nametext></name>
- <fsummary>Determine if a local process is alive or not.</fsummary>
- <desc><p>Return true if <c>pid</c> is currently alive.</p>
- <p>This function is only thread-safe when the emulator with SMP support is used.
- It can only be used in a non-SMP emulator from a NIF-calling thread.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a reference</fsummary>
- <desc><p>Return true if <c>term</c> is a reference.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_tuple(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a tuple</fsummary>
- <desc><p>Return true if <c>term</c> is a tuple.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
- <fsummary>Determine if a term is a list</fsummary>
- <desc><p>Return true if <c>term</c> is a list.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_keep_resource(void* obj)</nametext></name>
- <fsummary>Add a reference to a resource object</fsummary>
- <desc><p>Add a reference to resource object <c>obj</c> obtained from
- <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
- Each call to <c>enif_keep_resource</c> for an object must be balanced by
- a call to <seealso marker="#enif_release_resource">enif_release_resource</seealso>
- before the object will be destructed.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom(ErlNifEnv* env, const char* name)</nametext></name>
- <fsummary>Create an atom term</fsummary>
- <desc><p>Create an atom term from the null-terminated C-string <c>name</c>
- with iso-latin-1 encoding. If the length of <c>name</c> exceeds the maximum length
- allowed for an atom (255 characters), <c>enif_make_atom</c> invokes
- <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len)</nametext></name>
- <fsummary>Create an atom term</fsummary>
- <desc><p>Create an atom term from the string <c>name</c> with length <c>len</c>.
- Null-characters are treated as any other characters. If <c>len</c> is greater than the maximum length
- allowed for an atom (255 characters), <c>enif_make_atom</c> invokes
- <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name>
- <fsummary>Make a badarg exception</fsummary>
- <desc><p>Make a badarg exception to be returned from a NIF, and associate
- it with the environment <c>env</c>. Once a NIF or any function
- it calls invokes <c>enif_make_badarg</c>, the runtime ensures that a
- <c>badarg</c> exception is raised when the NIF returns, even if the NIF
- attempts to return a non-exception term instead.
- The return value from <c>enif_make_badarg</c> may be used only as the
- return value from the NIF that invoked it (directly or indirectly)
- or be passed to
- <seealso marker="#enif_is_exception">enif_is_exception</seealso>, but
- not to any other NIF API function.</p>
- <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso>
- and <seealso marker="#enif_raise_exception">enif_raise_exception</seealso>.
- </p>
- <note><p>In earlier versions (older than erts-7.0, OTP 18) the return value
- from <c>enif_make_badarg</c> had to be returned from the NIF. This
- requirement is now lifted as the return value from the NIF is ignored
- if <c>enif_make_badarg</c> has been invoked.</p></note></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)</nametext></name>
- <fsummary>Make a binary term</fsummary>
- <desc><p>Make a binary term from <c>bin</c>. Any ownership of
- the binary data will be transferred to the created term and
- <c>bin</c> should be considered read-only for the rest of the NIF
- call and then as released.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term)</nametext></name>
- <fsummary>Make a copy of a term</fsummary>
- <desc><p>Make a copy of term <c>src_term</c>. The copy will be created in
- environment <c>dst_env</c>. The source term may be located in any
- environment.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_double(ErlNifEnv* env, double d)</nametext></name>
- <fsummary>Create a floating-point term</fsummary>
- <desc><p>Create a floating-point term from a <c>double</c>. If the <c>double</c> argument is
- not finite or is NaN, <c>enif_make_double</c> invokes
- <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding encode)</nametext></name>
- <fsummary>Create an existing atom term</fsummary>
- <desc><p>Try to create the term of an already existing atom from
- the null-terminated C-string <c>name</c> with encoding
- <seealso marker="#ErlNifCharEncoding">encode</seealso>. If the atom
- already exists store the term in <c>*atom</c> and return true, otherwise
- return false. If the length of <c>name</c> exceeds the maximum length
- allowed for an atom (255 characters), <c>enif_make_existing_atom</c>
- returns false.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding encoding)</nametext></name>
- <fsummary>Create an existing atom term</fsummary>
- <desc><p>Try to create the term of an already existing atom from the
- string <c>name</c> with length <c>len</c> and encoding
- <seealso marker="#ErlNifCharEncoding">encode</seealso>. Null-characters
- are treated as any other characters. If the atom already exists store the term
- in <c>*atom</c> and return true, otherwise return false. If <c>len</c> is greater
- than the maximum length allowed for an atom (255 characters),
- <c>enif_make_existing_atom_len</c> returns false.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_int(ErlNifEnv* env, int i)</nametext></name>
- <fsummary>Create an integer term</fsummary>
- <desc><p>Create an integer term.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i)</nametext></name>
- <fsummary>Create an integer term</fsummary>
- <desc><p>Create an integer term from a signed 64-bit integer.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list(ErlNifEnv* env, unsigned cnt, ...)</nametext></name>
- <fsummary>Create a list term</fsummary>
- <desc><p>Create an ordinary list term of length <c>cnt</c>. Expects
- <c>cnt</c> number of arguments (after <c>cnt</c>) of type ERL_NIF_TERM as the
- elements of the list. An empty list is returned if <c>cnt</c> is 0.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name>
- <fsummary>Create a list term</fsummary>
- <desc><p>Create an ordinary list term with length indicated by the
- function name. Prefer these functions (macros) over the variadic
- <c>enif_make_list</c> to get a compile time error if the number of
- arguments does not match.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_cell(ErlNifEnv* env, ERL_NIF_TERM head, ERL_NIF_TERM tail)</nametext></name>
- <fsummary>Create a list cell</fsummary>
- <desc><p>Create a list cell <c>[head | tail]</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name>
- <fsummary>Create a list term from an array</fsummary>
- <desc><p>Create an ordinary list containing the elements of array <c>arr</c>
- of length <c>cnt</c>. An empty list is returned if <c>cnt</c> is 0.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_long(ErlNifEnv* env, long int i)</nametext></name>
- <fsummary>Create an integer term from a long int</fsummary>
- <desc><p>Create an integer term from a <c>long int</c>.</p></desc>
- </func>
- <func><name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name>
- <fsummary>Allocate and create a new binary term</fsummary>
- <desc><p>Allocate a binary of size <c>size</c> bytes and create an owning
- term. The binary data is mutable until the calling NIF returns. This is a
- quick way to create a new binary without having to use
- <seealso marker="#ErlNifBinary">ErlNifBinary</seealso>. The drawbacks are
- that the binary can not be kept between NIF calls and it can not be
- reallocated.</p><p>Return a pointer to the raw binary data and set
- <c>*termp</c> to the binary term.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_new_map(ErlNifEnv* env)</nametext></name>
- <fsummary>Make an empty map term</fsummary>
- <desc><p>Make an empty map term.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_map_put(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)</nametext></name>
- <fsummary>Insert key-value pair in map</fsummary>
- <desc><p>Make a copy of map <c>map_in</c> and insert <c>key</c> with
- <c>value</c>. If <c>key</c> already exists in <c>map_in</c>, the old
- associated value is replaced by <c>value</c>. If successful set
- <c>*map_out</c> to the new map and return true. Return false if
- <c>map_in</c> is not a map.</p>
- <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_map_update(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM new_value, ERL_NIF_TERM* map_out)</nametext></name>
- <fsummary>Replace value for key in map</fsummary>
- <desc><p>Make a copy of map <c>map_in</c> and replace the old associated
- value for <c>key</c> with <c>new_value</c>. If successful set
- <c>*map_out</c> to the new map and return true. Return false if
- <c>map_in</c> is not a map or if it does no contain <c>key</c>.</p>
- <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_map_remove(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)</nametext></name>
- <fsummary>Remove key from map</fsummary>
- <desc><p>If map <c>map_in</c> contains <c>key</c>, make a copy of
- <c>map_in</c> in <c>*map_out</c> and remove <c>key</c> and associated
- value. If map <c>map_in</c> does not contain <c>key</c>, set
- <c>*map_out</c> to <c>map_in</c>. Return true for success or false if
- <c>map_in</c> is not a map.</p>
- <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_pid(ErlNifEnv* env, const ErlNifPid* pid)</nametext></name>
- <fsummary>Make a pid term</fsummary>
- <desc><p>Make a pid term from <c>*pid</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_ref(ErlNifEnv* env)</nametext></name>
- <fsummary>Create a reference</fsummary>
- <desc><p>Create a reference like <seealso marker="erlang#make_ref-0">erlang:make_ref/0</seealso>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_resource(ErlNifEnv* env, void* obj)</nametext></name>
- <fsummary>Create an opaque handle to a resource object</fsummary>
- <desc><p>Create an opaque handle to a memory managed resource object
- obtained by <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
- No ownership transfer is done, as the resource object still needs to be released by
- <seealso marker="#enif_release_resource">enif_release_resource</seealso>,
- but note that the call to <c>enif_release_resource</c> can occur
- immediately after obtaining the term from <c>enif_make_resource</c>,
- in which case the resource object will be deallocated when the
- term is garbage collected. See the
- <seealso marker="#enif_resource_example">example of creating and
- returning a resource object</seealso> for more details.</p>
- <p>Note that the only defined behaviour of using a resource term in
- an Erlang program is to store it and send it between processes on the
- same node. Other operations such as matching or <c>term_to_binary</c>
- will have unpredictable (but harmless) results.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_resource_binary(ErlNifEnv* env, void* obj, const void* data, size_t size)</nametext></name>
- <fsummary>Create a custom binary term</fsummary>
- <desc><p>Create a binary term that is memory managed by a resource object
- <c>obj</c> obtained by <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
- The returned binary term will consist of <c>size</c> bytes pointed to
- by <c>data</c>. This raw binary data must be kept readable and unchanged
- until the destructor of the resource is called. The binary data may be
- stored external to the resource object in which case it is the responsibility
- of the destructor to release the data.</p>
- <p>Several binary terms may be managed by the same resource object. The
- destructor will not be called until the last binary is garbage collected.
- This can be useful as a way to return different parts of a larger binary
- buffer.</p>
- <p>As with <seealso marker="#enif_make_resource">enif_make_resource</seealso>,
- no ownership transfer is done. The resource still needs to be released with
- <seealso marker="#enif_release_resource">enif_release_resource</seealso>.</p>
- </desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in, ERL_NIF_TERM *list_out)</nametext></name>
- <fsummary>Create the reverse of a list</fsummary>
- <desc><p>Set <c>*list_out</c> to the reverse list of the list <c>list_in</c> and return true,
- or return false if <c>list_in</c> is not a list. This function should only be used on
- short lists as a copy will be created of the list which will not be released until after the
- nif returns.</p>
- <p>The <c>list_in</c> term must belong to the environment <c>env</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding)</nametext></name>
- <fsummary>Create a string</fsummary>
- <desc><p>Create a list containing the characters of the
- null-terminated string <c>string</c> with encoding <seealso marker="#ErlNifCharEncoding">encoding</seealso>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string_len(ErlNifEnv* env, const char* string, size_t len, ErlNifCharEncoding encoding)</nametext></name>
- <fsummary>Create a string</fsummary>
- <desc><p>Create a list containing the characters of the string <c>string</c> with
- length <c>len</c> and encoding <seealso marker="#ErlNifCharEncoding">encoding</seealso>.
- Null-characters are treated as any other characters.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_sub_binary(ErlNifEnv*
- env, ERL_NIF_TERM bin_term, size_t pos, size_t size)</nametext></name>
- <fsummary>Make a subbinary term</fsummary>
- <desc><p>Make a subbinary of binary <c>bin_term</c>, starting at
- zero-based position <c>pos</c> with a length of <c>size</c> bytes.
- <c>bin_term</c> must be a binary or bitstring and
- <c>pos+size</c> must be less or equal to the number of whole
- bytes in <c>bin_term</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)</nametext></name>
- <fsummary>Create a tuple term</fsummary>
- <desc><p>Create a tuple term of arity <c>cnt</c>. Expects
- <c>cnt</c> number of arguments (after <c>cnt</c>) of type ERL_NIF_TERM as the
- elements of the tuple.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name>
- <fsummary>Create a tuple term</fsummary>
- <desc><p>Create a tuple term with length indicated by the
- function name. Prefer these functions (macros) over the variadic
- <c>enif_make_tuple</c> to get a compile time error if the number of
- arguments does not match.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name>
- <fsummary>Create a tuple term from an array</fsummary>
- <desc><p>Create a tuple containing the elements of array <c>arr</c>
- of length <c>cnt</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_uint(ErlNifEnv* env, unsigned int i)</nametext></name>
- <fsummary>Create an unsigned integer term</fsummary>
- <desc><p>Create an integer term from an <c>unsigned int</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)</nametext></name>
- <fsummary>Create an unsigned integer term</fsummary>
- <desc><p>Create an integer term from an unsigned 64-bit integer.</p></desc>
- </func>
- <func>
- <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_unique_integer(ErlNifEnv *env, ErlNifUniqueInteger properties)</nametext></name>
+ <p>Return true if <c>term</c> is an exception.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM
+ term)</nametext></name>
+ <fsummary>Determine if a term is a fun.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a fun.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_identical(ERL_NIF_TERM lhs,
+ ERL_NIF_TERM rhs)</nametext></name>
+ <fsummary>Erlang operator =:=.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if the two terms are identical. Corresponds to
+ the Erlang operators <c>=:=</c> and <c>=/=</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a list.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a list.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_map(ErlNifEnv* env, ERL_NIF_TERM
+ term)</nametext></name>
+ <fsummary>Determine if a term is a map.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a map, otherwise
+ <c>false</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_number(ErlNifEnv* env, ERL_NIF_TERM
+ term)</nametext></name>
+ <fsummary>Determine if a term is a number (integer or float).</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a number.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a pid.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a pid.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a port.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a port.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_port_alive(ErlNifEnv* env,
+ ErlNifPort *port_id)</nametext></name>
+ <fsummary>Determine if a local port is alive.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>port_id</c> is alive.</p>
+ <p>This function is only thread-safe when the emulator with SMP support
+ is used. It can only be used in a non-SMP emulator from a NIF-calling
+ thread.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_is_process_alive(ErlNifEnv* env,
+ ErlNifPid *pid)</nametext></name>
+ <fsummary>Determine if a local process is alive.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>pid</c> is alive.</p>
+ <p>This function is only thread-safe when the emulator with SMP support
+ is used. It can only be used in a non-SMP emulator from a NIF-calling
+ thread.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a reference.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a reference.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_is_tuple(ErlNifEnv* env, ERL_NIF_TERM term)</nametext>
+ </name>
+ <fsummary>Determine if a term is a tuple.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c>term</c> is a tuple.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_keep_resource(void* obj)</nametext>
+ </name>
+ <fsummary>Add a reference to a resource object.</fsummary>
+ <desc>
+ <p>Adds a reference to resource object <c>obj</c> obtained from
+ <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>. Each call to
+ <c>enif_keep_resource</c> for an object must be balanced by a call to
+ <seealso marker="#enif_release_resource">
+ <c>enif_release_resource</c></seealso>
+ before the object is destructed.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_atom(ErlNifEnv* env, const char* name)</nametext>
+ </name>
+ <fsummary>Create an atom term.</fsummary>
+ <desc>
+ <p>Creates an atom term from the <c>NULL</c>-terminated C-string
+ <c>name</c> with ISO Latin-1 encoding. If the length of <c>name</c>
+ exceeds the maximum length allowed for an atom (255 characters),
+ <c>enif_make_atom</c> invokes <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env,
+ const char* name, size_t len)</nametext></name>
+ <fsummary>Create an atom term.</fsummary>
+ <desc>
+ <p>Create an atom term from the string <c>name</c> with length
+ <c>len</c>. <c>NULL</c> characters are treated as any other
+ characters. If <c>len</c> exceeds the maximum length
+ allowed for an atom (255 characters), <c>enif_make_atom</c> invokes
+ <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name>
+ <fsummary>Make a badarg exception.</fsummary>
+ <desc>
+ <p>Makes a <c>badarg</c> exception to be returned from a NIF, and
+ associates it with environment <c>env</c>. Once a NIF or any function
+ it calls invokes <c>enif_make_badarg</c>, the runtime ensures that a
+ <c>badarg</c> exception is raised when the NIF returns, even if the
+ NIF attempts to return a non-exception term instead.</p>
+ <p>The return value from <c>enif_make_badarg</c> can be used only as
+ the return value from the NIF that invoked it (directly or indirectly)
+ or be passed to <seealso marker="#enif_is_exception">
+ <c>enif_is_exception</c></seealso>, but not to any other NIF API
+ function.</p>
+ <p>See also <seealso marker="#enif_has_pending_exception">
+ <c>enif_has_pending_exception</c></seealso> and
+ <seealso marker="#enif_raise_exception">
+ <c>enif_raise_exception</c></seealso>.</p>
+ <note>
+ <p>Before ERTS 7.0 (Erlang/OTP 18), the return value
+ from <c>enif_make_badarg</c> had to be returned from the NIF. This
+ requirement is now lifted as the return value from the NIF is
+ ignored if <c>enif_make_badarg</c> has been invoked.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)</nametext>
+ </name>
+ <fsummary>Make a binary term.</fsummary>
+ <desc>
+ <p>Makes a binary term from <c>bin</c>. Any ownership of
+ the binary data is transferred to the created term and
+ <c>bin</c> is to be considered read-only for the rest of the NIF
+ call and then as released.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_copy(ErlNifEnv* dst_env,
+ ERL_NIF_TERM src_term)</nametext></name>
+ <fsummary>Make a copy of a term.</fsummary>
+ <desc>
+ <p>Makes a copy of term <c>src_term</c>. The copy is created in
+ environment <c>dst_env</c>. The source term can be located in any
+ environment.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_double(ErlNifEnv* env, double d)</nametext></name>
+ <fsummary>Create a floating-point term.</fsummary>
+ <desc>
+ <p>Creates a floating-point term from a <c>double</c>. If argument
+ <c>double</c> is not finite or is NaN, <c>enif_make_double</c>
+ invokes <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env,
+ const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding
+ encode)</nametext></name>
+ <fsummary>Create an existing atom term.</fsummary>
+ <desc>
+ <p>Tries to create the term of an already existing atom from
+ the <c>NULL</c>-terminated C-string <c>name</c> with encoding
+ <seealso marker="#ErlNifCharEncoding">encode</seealso>.</p>
+ <p>If the atom already exists, this function stores the term in
+ <c>*atom</c> and returns <c>true</c>, otherwise <c>false</c>.
+ Also returns <c>false</c> if the length of <c>name</c> exceeds the
+ maximum length allowed for an atom (255 characters).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env,
+ const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding
+ encoding)</nametext></name>
+ <fsummary>Create an existing atom term.</fsummary>
+ <desc>
+ <p>Tries to create the term of an already existing atom from the
+ string <c>name</c> with length <c>len</c> and encoding
+ <seealso marker="#ErlNifCharEncoding">encode</seealso>. <c>NULL</c>
+ characters are treated as any other characters.</p>
+ <p>If the atom already exists, this function stores the term in
+ <c>*atom</c> and returns <c>true</c>, otherwise <c>false</c>.
+ Also returns <c>false</c> if <c>len</c> exceeds the maximum length
+ allowed for an atom (255 characters).</p>
+ </desc>
+ </func>
+
+ <func><name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_int(ErlNifEnv* env, int i)</nametext></name>
+ <fsummary>Create an integer term.</fsummary>
+ <desc>
+ <p>Creates an integer term.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i)</nametext>
+ </name>
+ <fsummary>Create an integer term.</fsummary>
+ <desc>
+ <p>Creates an integer term from a signed 64-bit integer.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_list(ErlNifEnv* env, unsigned cnt, ...)</nametext>
+ </name>
+ <fsummary>Create a list term.</fsummary>
+ <desc>
+ <p>Creates an ordinary list term of length <c>cnt</c>. Expects
+ <c>cnt</c> number of arguments (after <c>cnt</c>) of type
+ <c>ERL_NIF_TERM</c> as the elements of the list.</p>
+ <p>Returns an empty list if <c>cnt</c> is 0.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_list1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext>
+ </name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list2(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list3(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list4(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list5(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list6(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list7(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list8(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list9(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name>
+ <fsummary>Create a list term.</fsummary>
+ <desc>
+ <p>Creates an ordinary list term with length indicated by the
+ function name. Prefer these functions (macros) over the variadic
+ <c>enif_make_list</c> to get a compile-time error if the number of
+ arguments does not match.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_cell(ErlNifEnv*
+ env, ERL_NIF_TERM head, ERL_NIF_TERM tail)</nametext></name>
+ <fsummary>Create a list cell.</fsummary>
+ <desc>
+ <p>Creates a list cell <c>[head | tail]</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM
+ arr[], unsigned cnt)</nametext></name>
+ <fsummary>Create a list term from an array.</fsummary>
+ <desc>
+ <p>Creates an ordinary list containing the elements of array <c>arr</c>
+ of length <c>cnt</c>.</p>
+ <p>Returns an empty list if <c>cnt</c> is 0.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_long(ErlNifEnv* env, long int i)</nametext></name>
+ <fsummary>Create an integer term from a long int.</fsummary>
+ <desc>
+ <p>Creates an integer term from a <c>long int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_make_map_put(ErlNifEnv* env,
+ ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value,
+ ERL_NIF_TERM* map_out)</nametext></name>
+ <fsummary>Insert key-value pair in map.</fsummary>
+ <desc>
+ <p>Makes a copy of map <c>map_in</c> and inserts <c>key</c> with
+ <c>value</c>. If <c>key</c> already exists in <c>map_in</c>, the old
+ associated value is replaced by <c>value</c>.</p>
+ <p>If successful, this function sets <c>*map_out</c> to the new map and
+ returns <c>true</c>. Returns <c>false</c> if <c>map_in</c> is not a
+ map.</p>
+ <p>The <c>map_in</c> term must belong to environment <c>env</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_make_map_remove(ErlNifEnv* env,
+ ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)</nametext>
+ </name>
+ <fsummary>Remove key from map.</fsummary>
+ <desc>
+ <p>If map <c>map_in</c> contains <c>key</c>, this function makes a copy
+ of <c>map_in</c> in <c>*map_out</c>, and removes <c>key</c> and the
+ associated value. If map <c>map_in</c> does not contain <c>key</c>,
+ <c>*map_out</c> is set to <c>map_in</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if <c>map_in</c> is
+ not a map.</p>
+ <p>The <c>map_in</c> term must belong to environment <c>env</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_make_map_update(ErlNifEnv* env,
+ ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM new_value,
+ ERL_NIF_TERM* map_out)</nametext></name>
+ <fsummary>Replace value for key in map.</fsummary>
+ <desc>
+ <p>Makes a copy of map <c>map_in</c> and replace the old associated
+ value for <c>key</c> with <c>new_value</c>.</p>
+ <p>If successful, this function sets <c>*map_out</c> to the new map and
+ returns <c>true</c>. Returns <c>false</c> if <c>map_in</c> is not a
+ map or if it does not contain <c>key</c>.</p>
+ <p>The <c>map_in</c> term must belong to environment <c>env</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv*
+ env, size_t size, ERL_NIF_TERM* termp)</nametext></name>
+ <fsummary>Allocate and create a new binary term.</fsummary>
+ <desc>
+ <p>Allocates a binary of size <c>size</c> bytes and creates an owning
+ term. The binary data is mutable until the calling NIF returns.
+ This is a quick way to create a new binary without having to use
+ <seealso marker="#ErlNifBinary"><c>ErlNifBinary</c></seealso>.
+ The drawbacks are that the binary cannot be kept between NIF calls
+ and it cannot be reallocated.</p>
+ <p>Returns a pointer to the raw binary data and sets
+ <c>*termp</c> to the binary term.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_new_map(ErlNifEnv* env)</nametext></name>
+ <fsummary>Make an empty map term.</fsummary>
+ <desc>
+ <p>Makes an empty map term.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_pid(ErlNifEnv* env, const ErlNifPid* pid)</nametext>
+ </name>
+ <fsummary>Make a pid term.</fsummary>
+ <desc>
+ <p>Makes a pid term from <c>*pid</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_ref(ErlNifEnv* env)</nametext></name>
+ <fsummary>Create a reference.</fsummary>
+ <desc>
+ <p>Creates a reference like <seealso marker="erlang#make_ref-0">
+ <c>erlang:make_ref/0</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_resource(ErlNifEnv* env, void* obj)</nametext>
+ </name>
+ <fsummary>Create an opaque handle to a resource object.</fsummary>
+ <desc>
+ <p>Creates an opaque handle to a memory-managed resource object
+ obtained by <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>. No ownership transfer is done,
+ as the resource object still needs to be released by
+ <seealso marker="#enif_release_resource">
+ <c>enif_release_resource</c></seealso>. However, notice that the call
+ to <c>enif_release_resource</c> can occur immediately after obtaining
+ the term from <c>enif_make_resource</c>, in which case the resource
+ object is deallocated when the term is garbage collected. For more
+ details, see the <seealso marker="#enif_resource_example">example of
+ creating and returning a resource object</seealso> in the User's
+ Guide.</p>
+ <p>Notice that the only defined behavior of using a resource term in
+ an Erlang program is to store it and send it between processes on the
+ same node. Other operations, such as matching or
+ <c>term_to_binary</c>, have unpredictable (but harmless) results.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_resource_binary(ErlNifEnv* env, void* obj, const
+ void* data, size_t size)</nametext></name>
+ <fsummary>Create a custom binary term.</fsummary>
+ <desc>
+ <p>Creates a binary term that is memory-managed by a resource object
+ <c>obj</c> obtained by <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>. The returned binary term
+ consists of <c>size</c> bytes pointed to by <c>data</c>. This raw
+ binary data must be kept readable and unchanged until the destructor
+ of the resource is called. The binary data can be stored external to
+ the resource object, in which case the destructor is responsible
+ for releasing the data.</p>
+ <p>Several binary terms can be managed by the same resource object. The
+ destructor is not called until the last binary is garbage collected.
+ This can be useful to return different parts of a larger binary
+ buffer.</p>
+ <p>As with <seealso marker="#enif_make_resource">
+ <c>enif_make_resource</c></seealso>, no ownership transfer is done.
+ The resource still needs to be released with
+ <seealso marker="#enif_release_resource">
+ <c>enif_release_resource</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in,
+ ERL_NIF_TERM *list_out)</nametext></name>
+ <fsummary>Create the reverse of a list.</fsummary>
+ <desc>
+ <p>Sets <c>*list_out</c> to the reverse list of the list <c>list_in</c>
+ and returns <c>true</c>, or returns <c>false</c> if <c>list_in</c> is
+ not a list.</p>
+ <p>This function is only to be used on short lists, as a copy is
+ created of the list, which is not released until after the NIF
+ returns.</p>
+ <p>The <c>list_in</c> term must belong to environment <c>env</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string(ErlNifEnv* env,
+ const char* string, ErlNifCharEncoding encoding)</nametext></name>
+ <fsummary>Create a string.</fsummary>
+ <desc>
+ <p>Creates a list containing the characters of the
+ <c>NULL</c>-terminated string <c>string</c> with encoding
+ <seealso marker="#ErlNifCharEncoding">encoding</seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string_len(ErlNifEnv*
+ env, const char* string, size_t len, ErlNifCharEncoding
+ encoding)</nametext></name>
+ <fsummary>Create a string.</fsummary>
+ <desc>
+ <p>Creates a list containing the characters of the string <c>string</c>
+ with length <c>len</c> and encoding
+ <seealso marker="#ErlNifCharEncoding">encoding</seealso>.
+ <c>NULL</c> characters are treated as any other characters.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_sub_binary(ErlNifEnv*
+ env, ERL_NIF_TERM bin_term, size_t pos, size_t size)</nametext></name>
+ <fsummary>Make a subbinary term.</fsummary>
+ <desc>
+ <p>Makes a subbinary of binary <c>bin_term</c>, starting at
+ zero-based position <c>pos</c> with a length of <c>size</c> bytes.
+ <c>bin_term</c> must be a binary or bitstring. <c>pos+size</c> must
+ be less or equal to the number of whole bytes in <c>bin_term</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple(ErlNifEnv* env,
+ unsigned cnt, ...)</nametext></name>
+ <fsummary>Creates a tuple term.</fsummary>
+ <desc>
+ <p>Creates a tuple term of arity <c>cnt</c>. Expects <c>cnt</c> number
+ of arguments (after <c>cnt</c>) of type <c>ERL_NIF_TERM</c> as the
+ elements of the tuple.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple1(ErlNifEnv* env,
+ ERL_NIF_TERM e1)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple2(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple3(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple4(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple5(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple6(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple7(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple8(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple9(ErlNifEnv* env,
+ ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name>
+ <fsummary>Create a tuple term.</fsummary>
+ <desc>
+ <p>Creates a tuple term with length indicated by the
+ function name. Prefer these functions (macros) over the variadic
+ <c>enif_make_tuple</c> to get a compile-time error if the number of
+ arguments does not match.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM
+ arr[], unsigned cnt)</nametext></name>
+ <fsummary>Create a tuple term from an array.</fsummary>
+ <desc>
+ <p>Creates a tuple containing the elements of array <c>arr</c>
+ of length <c>cnt</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_uint(ErlNifEnv* env, unsigned int i)</nametext>
+ </name>
+ <fsummary>Create an unsigned integer term.</fsummary>
+ <desc>
+ <p>Creates an integer term from an <c>unsigned int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)</nametext>
+ </name>
+ <fsummary>Create an unsigned integer term.</fsummary>
+ <desc>
+ <p>Creates an integer term from an unsigned 64-bit integer.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_make_ulong(ErlNifEnv* env, unsigned long i)</nametext>
+ </name>
+ <fsummary>Create an integer term from an unsigned long int.</fsummary>
+ <desc>
+ <p>Creates an integer term from an <c>unsigned long int</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_unique_integer(ErlNifEnv
+ *env, ErlNifUniqueInteger properties)</nametext></name>
<fsummary></fsummary>
<desc>
- <p>Returns a unique integer with the same properties as given by <seealso marker="erlang#unique_integer-1">erlang:unique_integer/1</seealso>.</p>
+ <p>Returns a unique integer with the same properties as specified by
+ <seealso marker="erlang#unique_integer-1">
+ <c>erlang:unique_integer/1</c></seealso>.</p>
<p><c>env</c> is the environment to create the integer in.</p>
- <p>
- <c>ERL_NIF_UNIQUE_POSITIVE</c> and <c>ERL_NIF_UNIQUE_MONOTONIC</c> can
- be passed as the second argument to change the properties of the
- integer returned. It is possible to combine them by or:ing the
- two values together.
- </p>
- <p>See also:
- <seealso marker="#ErlNifUniqueInteger"><c>ErlNifUniqueInteger</c></seealso>.
- </p>
- </desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_ulong(ErlNifEnv* env, unsigned long i)</nametext></name>
- <fsummary>Create an integer term from an unsigned long int</fsummary>
- <desc><p>Create an integer term from an <c>unsigned long int</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)</nametext></name>
- <fsummary>Create a map iterator</fsummary>
- <desc><p>Create an iterator for the map <c>map</c> by initializing the
- structure pointed to by <c>iter</c>. The <c>entry</c> argument determines
- the start position of the iterator: <c>ERL_NIF_MAP_ITERATOR_FIRST</c> or
- <c>ERL_NIF_MAP_ITERATOR_LAST</c>. Return true on success or false if
- <c>map</c> is not a map.</p>
- <p>A map iterator is only useful during the lifetime of the environment
- <c>env</c> that the <c>map</c> belongs to. The iterator must be destroyed by
- calling <seealso marker="#enif_map_iterator_destroy">
- enif_map_iterator_destroy</seealso>.</p>
- <code type="none">
+ <p><c>ERL_NIF_UNIQUE_POSITIVE</c> and <c>ERL_NIF_UNIQUE_MONOTONIC</c>
+ can be passed as the second argument to change the properties of the
+ integer returned. They can be combined by OR:ing the two values
+ together.</p>
+ <p>See also <seealso marker="#ErlNifUniqueInteger">
+ <c>ErlNifUniqueInteger</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_create(ErlNifEnv *env,
+ ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry
+ entry)</nametext></name>
+ <fsummary>Create a map iterator.</fsummary>
+ <desc>
+ <p>Creates an iterator for the map <c>map</c> by initializing the
+ structure pointed to by <c>iter</c>. Argument <c>entry</c> determines
+ the start position of the iterator: <c>ERL_NIF_MAP_ITERATOR_FIRST</c>
+ or <c>ERL_NIF_MAP_ITERATOR_LAST</c>.</p>
+ <p>Returns <c>true</c> on success, or false if <c>map</c> is not a
+ map.</p>
+ <p>A map iterator is only useful during the lifetime of environment
+ <c>env</c> that the <c>map</c> belongs to. The iterator must be
+ destroyed by calling <seealso marker="#enif_map_iterator_destroy">
+ <c>enif_map_iterator_destroy</c></seealso>:</p>
+ <code type="none">
ERL_NIF_TERM key, value;
ErlNifMapIterator iter;
enif_map_iterator_create(env, my_map, &amp;iter, ERL_NIF_MAP_ITERATOR_FIRST);
@@ -1468,439 +2058,714 @@ while (enif_map_iterator_get_pair(env, &amp;iter, &amp;key, &amp;value)) {
do_something(key,value);
enif_map_iterator_next(env, &amp;iter);
}
-enif_map_iterator_destroy(env, &amp;iter);
- </code>
- <note><p>The key-value pairs of a map have no defined iteration
- order. The only guarantee is that the iteration order of a single map
- instance is preserved during the lifetime of the environment that the map
- belongs to.</p>
- </note>
+enif_map_iterator_destroy(env, &amp;iter);</code>
+ <note>
+ <p>The key-value pairs of a map have no defined iteration order.
+ The only guarantee is that the iteration order of a single map
+ instance is preserved during the lifetime of the environment that
+ the map belongs to.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>enif_map_iterator_destroy(ErlNifEnv *env,
+ ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Destroy a map iterator.</fsummary>
+ <desc>
+ <p>Destroys a map iterator created by
+ <seealso marker="#enif_map_iterator_create">
+ <c>enif_map_iterator_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_get_pair(ErlNifEnv *env,
+ ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM
+ *value)</nametext></name>
+ <fsummary>Get key and value at current map iterator position.</fsummary>
+ <desc>
+ <p>Gets key and value terms at the current map iterator position.</p>
+ <p>On success, sets <c>*key</c> and <c>*value</c> and returns
+ <c>true</c>. Returns <c>false</c> if the iterator is positioned at
+ head (before first entry) or tail (beyond last entry).</p>
</desc>
</func>
- <func><name><ret>void</ret><nametext>enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
- <fsummary>Destroy a map iterator</fsummary>
- <desc><p>Destroy a map iterator created by
- <seealso marker="#enif_map_iterator_create">enif_map_iterator_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)</nametext></name>
- <fsummary>Get key and value at current map iterator position</fsummary>
- <desc><p>Get key and value terms at current map iterator position.
- On success set <c>*key</c> and <c>*value</c> and return true.
- Return false if the iterator is positioned at head (before first entry)
- or tail (beyond last entry).</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
- <fsummary>Check if map iterator is positioned before first</fsummary>
- <desc><p>Return true if map iterator <c>iter</c> is positioned
- before first entry.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
- <fsummary>Check if map iterator is positioned after last</fsummary>
- <desc><p>Return true if map iterator <c>iter</c> is positioned
- after last entry.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
- <fsummary>Increment map iterator to point to next entry</fsummary>
- <desc><p>Increment map iterator to point to next key-value entry.
- Return true if the iterator is now positioned at a valid key-value entry,
- or false if the iterator is positioned at the tail (beyond the last
- entry).</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name>
- <fsummary>Decrement map iterator to point to previous entry</fsummary>
- <desc><p>Decrement map iterator to point to previous key-value entry.
- Return true if the iterator is now positioned at a valid key-value entry,
- or false if the iterator is positioned at the head (before the first
- entry).</p></desc>
- </func>
-
- <func>
- <name><ret>ErlNifTime</ret><nametext>enif_monotonic_time(ErlNifTimeUnit time_unit)</nametext></name>
- <fsummary>Get Erlang Monotonic Time</fsummary>
- <desc>
- <marker id="enif_monotonic_time"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>time_unit</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>
- Returns the current
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso>. Note that it is not uncommon with
- negative values.
- </p>
- <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid
- time unit argument, or if called from a thread that is not a
- scheduler thread.</p>
- <p>See also:
- <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso> and
- <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.
- </p>
- </desc>
- </func>
-
- <func><name><ret>ErlNifMutex *</ret><nametext>enif_mutex_create(char *name)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_create">erl_drv_mutex_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_mutex_destroy(ErlNifMutex *mtx)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_destroy">erl_drv_mutex_destroy</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_mutex_lock(ErlNifMutex *mtx)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_lock">erl_drv_mutex_lock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_mutex_trylock(ErlNifMutex *mtx)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_trylock">erl_drv_mutex_trylock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_mutex_unlock(ErlNifMutex *mtx)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_unlock">erl_drv_mutex_unlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_now_time(ErlNifEnv *env)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Retuns an <seealso marker="erlang#now-0">erlang:now()</seealso> timestamp.
- The enif_now_time function is <em>deprecated</em>.</p></desc>
- </func>
- <func><name><ret>ErlNifResourceType *</ret><nametext>enif_open_resource_type(ErlNifEnv* env,
- const char* module_str, const char* name,
- ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext></name>
- <fsummary>Create or takeover a resource type</fsummary>
- <desc><p>Create or takeover a resource type identified by the string
- <c>name</c> and give it the destructor function pointed to by <seealso marker="#ErlNifResourceDtor">dtor</seealso>.
- Argument <c>flags</c> can have the following values:</p>
- <taglist>
- <tag><c>ERL_NIF_RT_CREATE</c></tag>
- <item>Create a new resource type that does not already exist.</item>
- <tag><c>ERL_NIF_RT_TAKEOVER</c></tag>
- <item>Open an existing resource type and take over ownership of all its instances.
- The supplied destructor <c>dtor</c> will be called both for existing instances
- as well as new instances not yet created by the calling NIF library.</item>
- </taglist>
- <p>The two flag values can be combined with bitwise-or. The name of the
- resource type is local to the calling module. Argument <c>module_str</c>
- is not (yet) used and must be NULL. The <c>dtor</c> may be <c>NULL</c>
- in case no destructor is needed.</p>
- <p>On success, return a pointer to the resource type and <c>*tried</c>
- will be set to either <c>ERL_NIF_RT_CREATE</c> or
- <c>ERL_NIF_RT_TAKEOVER</c> to indicate what was actually done.
- On failure, return <c>NULL</c> and set <c>*tried</c> to <c>flags</c>.
- It is allowed to set <c>tried</c> to <c>NULL</c>.</p>
- <p>Note that <c>enif_open_resource_type</c> is only allowed to be called in the three callbacks
- <seealso marker="#load">load</seealso>, <seealso marker="#reload">reload</seealso>
- and <seealso marker="#upgrade">upgrade</seealso>.</p>
- </desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_port_command(ErlNifEnv* env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)</nametext></name>
- <fsummary>Send a port_command to to_port</fsummary>
- <desc>
- <p>This function works the same as <seealso marker="erlang#port_command-2">erlang:port_command/2</seealso>
- except that it is always completely asynchronous.</p>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_is_head(ErlNifEnv *env,
+ ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Check if map iterator is positioned before first.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if map iterator <c>iter</c> is positioned
+ before the first entry.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_is_tail(ErlNifEnv *env,
+ ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Check if map iterator is positioned after last.</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if map iterator <c>iter</c> is positioned
+ after the last entry.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_next(ErlNifEnv *env,
+ ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Increment map iterator to point to next entry.</fsummary>
+ <desc>
+ <p>Increments map iterator to point to the next key-value entry.</p>
+ <p>Returns <c>true</c> if the iterator is now positioned at a valid
+ key-value entry, or <c>false</c> if the iterator is positioned at
+ the tail (beyond the last entry).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_map_iterator_prev(ErlNifEnv *env,
+ ErlNifMapIterator *iter)</nametext></name>
+ <fsummary>Decrement map iterator to point to previous entry.</fsummary>
+ <desc>
+ <p>Decrements map iterator to point to the previous key-value entry.</p>
+ <p>Returns <c>true</c> if the iterator is now positioned at a valid
+ key-value entry, or <c>false</c> if the iterator is positioned at
+ the head (before the first entry).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifTime</ret>
+ <nametext>enif_monotonic_time(ErlNifTimeUnit time_unit)</nametext>
+ </name>
+ <fsummary>Get Erlang monotonic time.</fsummary>
+ <desc>
+ <marker id="enif_monotonic_time"></marker>
+ <p>Returns the current
+ <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso>. Notice that it is not uncommon with
+ negative values.</p>
+ <p><c>time_unit</c> is the time unit of the returned value.</p>
+ <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid time
+ unit argument, or if called from a thread that is not a scheduler
+ thread.</p>
+ <p>See also <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso>
+ and <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifMutex *</ret>
+ <nametext>enif_mutex_create(char *name)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_create">
+ <c>erl_drv_mutex_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_mutex_destroy(ErlNifMutex *mtx)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_destroy">
+ <c>erl_drv_mutex_destroy</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_mutex_lock(ErlNifMutex *mtx)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_lock">
+ <c>erl_drv_mutex_lock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_mutex_trylock(ErlNifMutex *mtx)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_trylock">
+ <c>erl_drv_mutex_trylock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_mutex_unlock(ErlNifMutex *mtx)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_mutex_unlock">
+ <c>erl_drv_mutex_unlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret>
+ <nametext>enif_now_time(ErlNifEnv *env)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Returns an <seealso marker="erlang#now-0">
+ <c>erlang:now()</c></seealso> time stamp.</p>
+ <p><em>This function is deprecated.</em></p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifResourceType *</ret>
+ <nametext>enif_open_resource_type(ErlNifEnv* env, const char*
+ module_str, const char* name, ErlNifResourceDtor* dtor,
+ ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext>
+ </name>
+ <fsummary>Create or takeover a resource type.</fsummary>
+ <desc>
+ <p>Creates or takes over a resource type identified by the string
+ <c>name</c> and gives it the destructor function pointed to by
+ <seealso marker="#ErlNifResourceDtor"><c>dtor</c></seealso>.
+ Argument <c>flags</c> can have the following values:</p>
+ <taglist>
+ <tag><c>ERL_NIF_RT_CREATE</c></tag>
+ <item>Creates a new resource type that does not already exist.</item>
+ <tag><c>ERL_NIF_RT_TAKEOVER</c></tag>
+ <item>Opens an existing resource type and takes over ownership of all
+ its instances. The supplied destructor <c>dtor</c> is called both
+ for existing instances and new instances not yet created by the
+ calling NIF library.</item>
+ </taglist>
+ <p>The two flag values can be combined with bitwise OR. The resource
+ type name is local to the calling module. Argument <c>module_str</c>
+ is not (yet) used and must be <c>NULL</c>. <c>dtor</c> can be
+ <c>NULL</c> if no destructor is needed.</p>
+ <p>On success, the function returns a pointer to the resource type and
+ <c>*tried</c> is set to either <c>ERL_NIF_RT_CREATE</c> or
+ <c>ERL_NIF_RT_TAKEOVER</c> to indicate what was done. On failure,
+ returns <c>NULL</c> and sets <c>*tried</c> to <c>flags</c>.
+ It is allowed to set <c>tried</c> to <c>NULL</c>.</p>
+ <p>Notice that <c>enif_open_resource_type</c> is only allowed to be
+ called in the three callbacks
+ <seealso marker="#load"><c>load</c></seealso>,
+ <seealso marker="#reload"><c>reload</c></seealso>, and
+ <seealso marker="#upgrade"><c>upgrade</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_port_command(ErlNifEnv* env, const
+ ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)</nametext>
+ </name>
+ <fsummary>Send a port_command to to_port.</fsummary>
+ <desc>
+ <p>Works as <seealso marker="erlang#port_command-2">
+ <c>erlang:port_command/2</c></seealso>,
+ except that it is always completely asynchronous.</p>
+ <taglist>
+ <tag><c>env</c></tag>
+ <item>The environment of the calling process. Must not be
+ <c>NULL</c>.</item>
+ <tag><c>*to_port</c></tag>
+ <item>The port ID of the receiving port. The port ID is to refer to a
+ port on the local node.</item>
+ <tag><c>msg_env</c></tag>
+ <item>The environment of the message term. Can be a process-independent
+ environment allocated with <seealso marker="#enif_alloc_env">
+ <c>enif_alloc_env</c></seealso> or <c>NULL</c>.</item>
+ <tag><c>msg</c></tag>
+ <item>The message term to send. The same limitations apply as on the
+ payload to <seealso marker="erlang#port_command-2">
+ <c>erlang:port_command/2</c></seealso>.</item>
+ </taglist>
+ <p>Using a <c>msg_env</c> of <c>NULL</c> is an optimization, which
+ groups together calls to <c>enif_alloc_env</c>, <c>enif_make_copy</c>,
+ <c>enif_port_command</c>, and <c>enif_free_env</c> into one call.
+ This optimization is only useful when a majority of the terms are to
+ be copied from <c>env</c> to <c>msg_env</c>.</p>
+ <p>Returns <c>true</c> if the command is successfully sent. Returns
+ <c>false</c> if the command fails, for example:</p>
+ <list type="bulleted">
+ <item><c>*to_port</c> does not refer to a local port.</item>
+ <item>The currently executing process (that is, the sender) is not
+ alive.</item>
+ <item><c>msg</c> is invalid.</item>
+ </list>
+ <p>See also <seealso marker="#enif_get_local_port">
+ <c>enif_get_local_port</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void *</ret>
+ <nametext>enif_priv_data(ErlNifEnv* env)</nametext></name>
+ <fsummary>Get the private data of a NIF library.</fsummary>
+ <desc>
+ <p>Returns the pointer to the private data that was set by
+ <seealso marker="#load"><c>load</c></seealso>,
+ <seealso marker="#reload"><c>reload</c></seealso>, or
+ <seealso marker="#upgrade"><c>upgrade</c></seealso>.</p>
+ <p>Was previously named <c>enif_get_data</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_raise_exception(ErlNifEnv*
+ env, ERL_NIF_TERM reason)</nametext></name>
+ <fsummary>Raise a NIF error exception.</fsummary>
+ <desc>
+ <p>Creates an error exception with the term <c>reason</c> to be
+ returned from a NIF, and associates it with environment <c>env</c>.
+ Once a NIF or any function it calls invokes
+ <c>enif_raise_exception</c>, the runtime ensures that the exception
+ it creates is raised when the NIF returns, even if the NIF attempts
+ to return a non-exception term instead.</p>
+ <p>The return value from <c>enif_raise_exception</c> can only be used
+ as the return value from the NIF that invoked it (directly or
+ indirectly) or be passed to <seealso marker="#enif_is_exception">
+ <c>enif_is_exception</c></seealso>, but not to any other NIF API
+ function.</p>
+ <p>See also <seealso marker="#enif_has_pending_exception">
+ <c>enif_has_pending_exception</c></seealso> and
+ <seealso marker="#enif_make_badarg">
+ <c>enif_make_badarg</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_realloc_binary(ErlNifBinary* bin, size_t size)</nametext>
+ </name>
+ <fsummary>Change the size of a binary.</fsummary>
+ <desc>
+ <p>Changes the size of a binary <c>bin</c>. The source binary
+ can be read-only, in which case it is left untouched and
+ a mutable copy is allocated and assigned to <c>*bin</c>.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if memory allocation
+ failed.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_release_binary(ErlNifBinary* bin)</nametext></name>
+ <fsummary>Release a binary.</fsummary>
+ <desc>
+ <p>Releases a binary obtained from
+ <seealso marker="#enif_alloc_binary">
+ <c>enif_alloc_binary</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_release_resource(void* obj)</nametext></name>
+ <fsummary>Release a resource object.</fsummary>
+ <desc>
+ <p>Removes a reference to resource object <c>obj</c>obtained from
+ <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>.
+ The resource object is destructed when the last reference is removed.
+ Each call to <c>enif_release_resource</c> must correspond to a
+ previous call to <c>enif_alloc_resource</c> or
+ <seealso marker="#enif_keep_resource">
+ <c>enif_keep_resource</c></seealso>.
+ References made by <seealso marker="#enif_make_resource">
+ <c>enif_make_resource</c></seealso>
+ can only be removed by the garbage collector.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifRWLock *</ret>
+ <nametext>enif_rwlock_create(char *name)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_create">
+ <c>erl_drv_rwlock_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_rwlock_destroy(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_destroy">
+ <c>erl_drv_rwlock_destroy</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_rwlock_rlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rlock">
+ <c>erl_drv_rwlock_rlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_rwlock_runlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_runlock">
+ <c>erl_drv_rwlock_runlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_rwlock_rwlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwlock">
+ <c>erl_drv_rwlock_rwlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_rwlock_rwunlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwunlock">
+ <c>erl_drv_rwlock_rwunlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_rwlock_tryrlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrlock">
+ <c>erl_drv_rwlock_tryrlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_rwlock_tryrwlock(ErlNifRWLock *rwlck)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock">
+ <c>erl_drv_rwlock_tryrwlock</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ERL_NIF_TERM</ret><nametext>enif_schedule_nif(ErlNifEnv* env,
+ const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int
+ argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM
+ argv[])</nametext></name>
+ <fsummary>Schedule a NIF for execution.</fsummary>
+ <desc>
+ <p>Schedules NIF <c>fp</c> to execute. This function allows an
+ application to break up long-running work into multiple regular NIF
+ calls or to schedule a <seealso marker="#dirty_nifs">
+ dirty NIF</seealso> to execute on a dirty scheduler thread.</p>
+ <p><em>The dirty NIF functionality described here is
+ experimental.</em> You have to enable support for dirty
+ schedulers when building OTP to try out the functionality.</p>
+ <taglist>
+ <tag><c>fun_name</c></tag>
+ <item>
+ <p>Provides a name for the NIF that is scheduled for execution.
+ If it cannot be converted to an atom, <c>enif_schedule_nif</c>
+ returns a <c>badarg</c> exception.</p>
+ </item>
+ <tag><c>flags</c></tag>
+ <item>
+ <p>Must be set to <c>0</c> for a regular NIF. If the emulator was
+ built with the experimental dirty scheduler support enabled,
+ <c>flags</c> can be set to either
+ <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c> if the job is expected to be
+ CPU-bound, or <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c> for
+ jobs that will be I/O-bound. If dirty scheduler threads are not
+ available in the emulator, an attempt to schedule such a job
+ results in a <c>badarg</c> exception.</p>
+ </item>
+ <tag><c>argc</c> and <c>argv</c></tag>
+ <item>
+ <p>Can either be the originals passed into the calling NIF,
+ or can be values created by the calling NIF.</p>
+ </item>
+ </taglist>
+ <p>The calling NIF must use the return value of
+ <c>enif_schedule_nif</c> as its own return value.</p>
+ <p>Be aware that <c>enif_schedule_nif</c>, as its name implies, only
+ schedules the NIF for future execution. The calling NIF does not
+ block waiting for the scheduled NIF to execute and return. This means
+ that the calling NIF cannot expect to receive the scheduled NIF
+ return value and use it for further operations.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifPid *</ret>
+ <nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext>
+ </name>
+ <fsummary>Get the pid of the calling process.</fsummary>
+ <desc>
+ <p>Initializes the pid variable <c>*pid</c> to represent the
+ calling process.</p>
+ <p>Returns <c>pid</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_send(ErlNifEnv* env, ErlNifPid* to_pid,
+ ErlNifEnv* msg_env, ERL_NIF_TERM msg)</nametext></name>
+ <fsummary>Send a message to a process.</fsummary>
+ <desc>
+ <p>Sends a message to a process.</p>
<taglist>
<tag><c>env</c></tag>
- <item>The environment of the calling process. May not be NULL.</item>
- <tag><c>*to_port</c></tag>
- <item>The port id of the receiving port. The port id should refer to a
- port on the local node.</item>
+ <item>The environment of the calling process. Must be <c>NULL</c>
+ only if calling from a created thread.</item>
+ <tag><c>*to_pid</c></tag>
+ <item>The pid of the receiving process. The pid is to refer to a
+ process on the local node.</item>
<tag><c>msg_env</c></tag>
- <item>The environment of the message term. Can be a process
- independent environment allocated with
- <seealso marker="#enif_alloc_env">enif_alloc_env</seealso> or NULL.</item>
+ <item>The environment of the message term. Must be a
+ process-independent environment allocated with
+ <seealso marker="#enif_alloc_env"><c>enif_alloc_env</c></seealso>
+ or NULL.</item>
<tag><c>msg</c></tag>
- <item>The message term to send. The same limitations apply as on the
- payload to <seealso marker="erlang#port_command-2">erlang:port_command/2</seealso>.</item>
+ <item>The message term to send.</item>
</taglist>
- <p>Using a <c>msg_env</c> of NULL is an optimization which groups together
- calls to <c>enif_alloc_env</c>, <c>enif_make_copy</c>, <c>enif_port_command</c>
- and <c>enif_free_env</c> into one call. This optimization is only usefull
- when a majority of the terms are to be copied from <c>env</c> to the <c>msg_env</c>.</p>
- <p>This function return true if the command was successfully sent; otherwise,
- false. The call may return false if it detects that the command failed for some
- reason. For example, <c>*to_port</c> does not refer to a local port, if currently
- executing process, i.e. the sender, is not alive, or if <c>msg</c> is invalid.</p>
- <p>See also: <seealso marker="#enif_get_local_port"><c>enif_get_local_port</c></seealso>.</p>
- </desc>
- </func>
- <func><name><ret>void *</ret><nametext>enif_priv_data(ErlNifEnv* env)</nametext></name>
- <fsummary>Get the private data of a NIF library</fsummary>
- <desc><p>Return the pointer to the private data that was set by <c>load</c>,
- <c>reload</c> or <c>upgrade</c>.</p>
- <p>Was previously named <c>enif_get_data</c>.</p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason)</nametext></name>
- <fsummary>Raise a NIF error exception</fsummary>
- <desc><p>Create an error exception with the term <c>reason</c> to be returned from a NIF,
- and associate it with the environment <c>env</c>. Once a NIF or any function it calls
- invokes <c>enif_raise_exception</c>, the runtime ensures that the exception it creates
- is raised when the NIF returns, even if the NIF attempts to return a non-exception
- term instead. The return value from <c>enif_raise_exception</c> may be used only as
- the return value from the NIF that invoked it (directly or indirectly) or be passed
- to <seealso marker="#enif_is_exception">enif_is_exception</seealso>, but
- not to any other NIF API function.</p>
- <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso>
- and <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_realloc_binary(ErlNifBinary* bin, size_t size)</nametext></name>
- <fsummary>Change the size of a binary</fsummary>
- <desc><p>Change the size of a binary <c>bin</c>. The source binary
- may be read-only, in which case it will be left untouched and
- a mutable copy is allocated and assigned to <c>*bin</c>. Return true on success,
- false if memory allocation failed.</p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_release_binary(ErlNifBinary* bin)</nametext></name>
- <fsummary>Release a binary</fsummary>
- <desc><p>Release a binary obtained from <c>enif_alloc_binary</c>.</p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_release_resource(void* obj)</nametext></name>
- <fsummary>Release a resource object</fsummary>
- <desc><p>Remove a reference to resource object <c>obj</c>obtained from
- <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
- The resource object will be destructed when the last reference is removed.
- Each call to <c>enif_release_resource</c> must correspond to a previous
- call to <c>enif_alloc_resource</c> or
- <seealso marker="#enif_keep_resource">enif_keep_resource</seealso>.
- References made by <seealso marker="#enif_make_resource">enif_make_resource</seealso>
- can only be removed by the garbage collector.</p></desc>
- </func>
- <func><name><ret>ErlNifRWLock *</ret><nametext>enif_rwlock_create(char *name)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_create">erl_drv_rwlock_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_rwlock_destroy(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_destroy">erl_drv_rwlock_destroy</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_rwlock_rlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rlock">erl_drv_rwlock_rlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_rwlock_runlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_runlock">erl_drv_rwlock_runlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_rwlock_rwlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwlock">erl_drv_rwlock_rwlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_rwlock_rwunlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_rwunlock">erl_drv_rwlock_rwunlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_rwlock_tryrlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrlock">erl_drv_rwlock_tryrlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_rwlock_tryrwlock(ErlNifRWLock *rwlck)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM argv[])</nametext></name>
- <fsummary>Schedule a NIF for execution</fsummary>
- <desc>
- <p>Schedule NIF <c>fp</c> to execute. This function allows an application to break up long-running
- work into multiple regular NIF calls or to schedule a <seealso marker="#dirty_nifs">dirty NIF</seealso>
- to execute on a dirty scheduler thread (<em>note that the dirty NIF functionality described here is
- experimental</em> and that you have to enable support for dirty schedulers when building OTP in
- order to try the functionality out).</p>
- <p>The <c>fun_name</c> argument provides a name for the NIF being scheduled for execution. If it cannot
- be converted to an atom, <c>enif_schedule_nif</c> returns a <c>badarg</c> exception.</p>
- <p>The <c>flags</c> argument must be set to 0 for a regular NIF, or if the emulator was built the
- experimental dirty scheduler support enabled, <c>flags</c> can be set to either <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c>
- if the job is expected to be CPU-bound, or <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c> for jobs that will
- be I/O-bound. If dirty scheduler threads are not available in the emulator, a try to schedule such a job
- will result in a <c>badarg</c> exception.</p>
-
- <p>The <c>argc</c> and <c>argv</c> arguments can either be the originals passed into the calling NIF, or
- they can be values created by the calling NIF.</p>
- <p>The calling NIF must use the return value of <c>enif_schedule_nif</c> as its own return value.</p>
- <p>Be aware that <c>enif_schedule_nif</c>, as its name implies, only schedules the
- NIF for future execution. The calling NIF does not block waiting for the scheduled NIF to
- execute and return, which means that the calling NIF can't expect to receive the scheduled NIF
- return value and use it for further operations.</p>
+ <p>Returns <c>true</c> if the message is successfully sent. Returns
+ <c>false</c> if the send operation fails, that is:</p>
+ <list type="bulleted">
+ <item><c>*to_pid</c> does not refer to an alive local process.</item>
+ <item>The currently executing process (that is, the sender) is not
+ alive.</item>
+ </list>
+ <p>The message environment <c>msg_env</c> with all its terms (including
+ <c>msg</c>) is invalidated by a successful call to <c>enif_send</c>.
+ The environment is to either be freed with
+ <seealso marker="#enif_free_env">
+ <c>enif_free_env</c></seealso> of cleared for reuse with
+ <seealso marker="#enif_clear_env"><c>enif_clear_env</c></seealso>.</p>
+ <p>If <c>msg_env</c> is set to <c>NULL</c>, the <c>msg</c> term is
+ copied and the original term and its environemt is still valid after
+ the call.</p>
+ <p>This function is only thread-safe when the emulator with SMP support
+ is used. It can only be used in a non-SMP emulator from a NIF-calling
+ thread.</p>
+ <note>
+ <p>Passing <c>msg_env</c> as <c>NULL</c> is only supported as from
+ ERTS 8.0 (Erlang/OTP 19).</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>unsigned</ret>
+ <nametext>enif_sizeof_resource(void* obj)</nametext></name>
+ <fsummary>Get the byte size of a resource object.</fsummary>
+ <desc>
+ <p>Gets the byte size of resource object <c>obj</c> obtained by
+ <seealso marker="#enif_alloc_resource">
+ <c>enif_alloc_resource</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_snprintf(char *str, size_t size, const
+ char *format, ...)</nametext></name>
+ <fsummary>Format strings and Erlang terms.</fsummary>
+ <desc>
+ <p>Similar to <c>snprintf</c> but this format string also accepts
+ <c>"%T"</c>, which formats Erlang terms.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>enif_system_info(ErlNifSysInfo
+ *sys_info_ptr, size_t size)</nametext></name>
+ <fsummary>Get information about the Erlang runtime system.</fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#driver_system_info">
+ <c>driver_system_info</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>enif_term_to_binary(ErlNifEnv *env,
+ ERL_NIF_TERM term, ErlNifBinary *bin)</nametext></name>
+ <fsummary>Convert a term to the external format.</fsummary>
+ <desc>
+ <p>Allocates a new binary with <seealso marker="#enif_alloc_binary">
+ <c>enif_alloc_binary</c></seealso> and stores the result of encoding
+ <c>term</c> according to the Erlang external term format.</p>
+ <p>Returns <c>true</c> on success, or <c>false</c> if the allocation
+ fails.</p>
+ <p>See also <seealso marker="erlang#term_to_binary-1">
+ <c>erlang:term_to_binary/1</c></seealso> and
+ <seealso marker="#enif_binary_to_term">
+ <c>enif_binary_to_term</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_thread_create(char *name,ErlNifTid
+ *tid,void * (*func)(void *),void *args,ErlNifThreadOpts
+ *opts)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_create">
+ <c>erl_drv_thread_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_thread_exit(void *resp)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_exit">
+ <c>erl_drv_thread_exit</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_thread_join(ErlNifTid, void **respp)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_join">
+ <c>erl_drv_thread_join</c></seealso>.</p>
</desc>
</func>
- <func><name><ret>ErlNifPid *</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name>
- <fsummary>Get the pid of the calling process</fsummary>
- <desc><p>Initialize the pid variable <c>*pid</c> to represent the
- calling process. Return <c>pid</c>.</p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_send(ErlNifEnv* env, ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg)</nametext></name>
- <fsummary>Send a message to a process</fsummary>
- <desc><p>Send a message to a process.</p>
- <taglist>
- <tag><c>env</c></tag>
- <item>The environment of the calling process. Must be NULL if and
- only if calling from a created thread.</item>
- <tag><c>*to_pid</c></tag>
- <item>The pid of the receiving process. The pid should refer to a process on the local node.</item>
- <tag><c>msg_env</c></tag>
- <item>The environment of the message term. Must be a process
- independent environment allocated with
- <seealso marker="#enif_alloc_env">enif_alloc_env</seealso> or NULL.</item>
- <tag><c>msg</c></tag>
- <item>The message term to send.</item>
- </taglist>
- <p>Return true if the message was successfully sent; otherwise, false. The send
- operation will fail if <c>*to_pid</c> does not refer to an alive local process,
- or if currently executing process, i.e. the sender, is not alive.</p>
- <p>The message environment <c>msg_env</c> with all its terms (including
- <c>msg</c>) will be invalidated by a successful call to <c>enif_send</c>. The environment
- should either be freed with <seealso marker="#enif_free_env">enif_free_env</seealso>
- of cleared for reuse with <seealso marker="#enif_clear_env">enif_clear_env</seealso>.</p>
- <p>If <c>msg_env</c> is set to NULL the <c>msg</c> term is copied and
- the original term and its environemt is still valid after the call.</p>
- <p>This function is only thread-safe when the emulator with SMP support is used.
- It can only be used in a non-SMP emulator from a NIF-calling thread.</p>
- <note><p>Passing <c>msg_env</c> as <c>NULL</c> is only supported since
- erts-8.0 (OTP 19).</p></note>
- </desc>
- </func>
- <func><name><ret>unsigned</ret><nametext>enif_sizeof_resource(void* obj)</nametext></name>
- <fsummary>Get the byte size of a resource object</fsummary>
- <desc><p>Get the byte size of a resource object <c>obj</c> obtained by
- <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.</p></desc>
- </func>
-
- <func><name><ret>int</ret><nametext>enif_snprintf(char *str, size_t size, const char *format, ...)</nametext></name>
- <fsummary>Format strings and Erlang terms</fsummary>
- <desc>
- <p>Similar to <c>snprintf</c> but this format string also accepts <c>"%T"</c> which formats Erlang terms.
- </p>
- </desc>
- </func>
-
- <func>
- <name><ret>void</ret><nametext>enif_system_info(ErlNifSysInfo *sys_info_ptr, size_t size)</nametext></name>
- <fsummary>Get information about the Erlang runtime system</fsummary>
- <desc><p>Same as <seealso marker="erl_driver#driver_system_info">driver_system_info</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_term_to_binary(ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)</nametext></name>
- <fsummary>Convert a term to the external format</fsummary>
- <desc>
- <p>Allocates a new binary with <seealso marker="#enif_alloc_binary">enif_alloc_binary</seealso>
- and stores the result of encoding <c>term</c> according to the Erlang external term format.</p>
- <p>Returns true on success or false if allocation failed.</p>
- <p>See also:
- <seealso marker="erlang#term_to_binary-1"><c>erlang:term_to_binary/1</c></seealso> and
- <seealso marker="#enif_binary_to_term"><c>enif_binary_to_term</c></seealso>.
- </p>
+
+ <func>
+ <name><ret>ErlNifThreadOpts *</ret>
+ <nametext>enif_thread_opts_create(char *name)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_create">
+ <c>erl_drv_thread_opts_create</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_thread_opts_destroy(ErlNifThreadOpts *opts)</nametext>
+ </name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_destroy">
+ <c>erl_drv_thread_opts_destroy</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifTid</ret>
+ <nametext>enif_thread_self(void)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_thread_self">
+ <c>erl_drv_thread_self</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_thread_type(void)</nametext></name>
+ <fsummary>Determine type of current thread</fsummary>
+ <desc>
+ <p>Determine the type of currently executing thread. A positive value
+ indicates a scheduler thread while a negative value or zero indicates
+ another type of thread. Currently the following specific types exist
+ (which may be extended in the future):</p>
+ <taglist>
+ <tag><c>ERL_NIF_THR_UNDEFINED</c></tag>
+ <item><p>Undefined thread that is not a scheduler thread.</p></item>
+ <tag><c>ERL_NIF_THR_NORMAL_SCHEDULER</c></tag>
+ <item><p>A normal scheduler thread.</p></item>
+ <tag><c>ERL_NIF_THR_DIRTY_CPU_SCHEDULER</c></tag>
+ <item><p>A dirty CPU scheduler thread.</p></item>
+ <tag><c>ERL_NIF_THR_DIRTY_IO_SCHEDULER</c></tag>
+ <item><p>A dirty I/O scheduler thread.</p></item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlNifTime</ret>
+ <nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name>
+ <fsummary>Get current time offset.</fsummary>
+ <desc>
+ <marker id="enif_time_offset"></marker>
+ <p>Returns the current time offset between
+ <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso> and
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso>
+ converted into the <c>time_unit</c> passed as argument.</p>
+ <p><c>time_unit</c> is the time unit of the returned value.</p>
+ <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid
+ time unit argument or if called from a thread that is not a
+ scheduler thread.</p>
+ <p>See also <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso>
+ and
+ <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void *</ret>
+ <nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_tsd_get">
+ <c>erl_drv_tsd_get</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_tsd_key_create(char *name, ErlNifTSDKey *key)</nametext>
+ </name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_create">
+ <c>erl_drv_tsd_key_create</c></seealso>.</p>
</desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_tsd_key_destroy(ErlNifTSDKey key)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_destroy">
+ <c>erl_drv_tsd_key_destroy</c></seealso>.</p>
+ </desc>
</func>
- <func><name><ret>int</ret><nametext>enif_thread_create(char *name,ErlNifTid *tid,void * (*func)(void *),void *args,ErlNifThreadOpts *opts)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_create">erl_drv_thread_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_thread_exit(void *resp)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_exit">erl_drv_thread_exit</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_thread_join(ErlNifTid, void **respp)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_join">erl_drv_thread_join </seealso>.
- </p></desc>
- </func>
- <func><name><ret>ErlNifThreadOpts *</ret><nametext>enif_thread_opts_create(char *name)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_create">erl_drv_thread_opts_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_thread_opts_destroy(ErlNifThreadOpts *opts)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_destroy">erl_drv_thread_opts_destroy</seealso>.
- </p></desc>
- </func>
- <func><name><ret>ErlNifTid</ret><nametext>enif_thread_self(void)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_self">erl_drv_thread_self</seealso>.
- </p></desc>
- </func>
- <func><name><ret>int</ret><nametext>enif_thread_type(void)</nametext></name>
- <fsummary>Determine type of current thread</fsummary>
- <desc>
- <p>Determine the type of currently executing thread. A positive value
- indicates a scheduler thread while a negative value or zero indicates
- another type of thread. Currently the following specific types exist
- (which may be extended in the future):</p>
- <taglist>
- <tag><c>ERL_NIF_THR_UNDEFINED</c></tag>
- <item><p>Undefined thread that is not a scheduler thread.</p></item>
- <tag><c>ERL_NIF_THR_NORMAL_SCHEDULER</c></tag>
- <item><p>A normal scheduler thread.</p></item>
- <tag><c>ERL_NIF_THR_DIRTY_CPU_SCHEDULER</c></tag>
- <item><p>A dirty CPU scheduler thread.</p></item>
- <tag><c>ERL_NIF_THR_DIRTY_IO_SCHEDULER</c></tag>
- <item><p>A dirty I/O scheduler thread.</p></item>
- </taglist>
- </desc>
- </func>
- <func>
- <name><ret>ErlNifTime</ret><nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name>
- <fsummary>Get current Time Offset</fsummary>
- <desc>
- <marker id="enif_time_offset"></marker>
- <p>Arguments:</p>
- <taglist>
- <tag><c>time_unit</c></tag>
- <item>Time unit of returned value.</item>
- </taglist>
- <p>Returns the current time offset between
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso>
- and
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- converted into the <c>time_unit</c> passed as argument.</p>
- <p>Returns <c>ERL_NIF_TIME_ERROR</c> if called with an invalid
- time unit argument, or if called from a thread that is not a
- scheduler thread.</p>
- <p>See also:
- <seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso> and
- <seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso>.
- </p>
- </desc>
- </func>
-
- <func><name><ret>int</ret><nametext>enif_tsd_key_create(char *name, ErlNifTSDKey *key)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_create">erl_drv_tsd_key_create</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_tsd_key_destroy(ErlNifTSDKey key)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void *</ret><nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_get">erl_drv_tsd_get</seealso>.
- </p></desc>
- </func>
- <func><name><ret>void</ret><nametext>enif_tsd_set(ErlNifTSDKey key, void *data)</nametext></name>
- <fsummary></fsummary>
- <desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_set">erl_drv_tsd_set</seealso>.
- </p></desc>
+
+ <func>
+ <name><ret>void</ret>
+ <nametext>enif_tsd_set(ErlNifTSDKey key, void *data)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <p>Same as <seealso marker="erl_driver#erl_drv_tsd_set">
+ <c>erl_drv_tsd_set</c></seealso>.</p>
+ </desc>
</func>
</funcs>
+
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="erlang#load_nif-2">erlang:load_nif/2</seealso></p>
+ <title>See Also</title>
+ <p><seealso marker="erlang#load_nif-2">
+ <c>erlang:load_nif/2</c></seealso></p>
</section>
</cref>
diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml
index d3ece37cc5..286bac6c93 100644
--- a/erts/doc/src/erl_prim_loader.xml
+++ b/erts/doc/src/erl_prim_loader.xml
@@ -30,70 +30,70 @@
<file>erl_prim_loader.xml</file>
</header>
<module>erl_prim_loader</module>
- <modulesummary>Low Level Erlang Loader</modulesummary>
+ <modulesummary>Low-level Erlang loader.</modulesummary>
<description>
- <p><c>erl_prim_loader</c> is used to load all Erlang modules into
- the system. The start script is also fetched with this low level
+ <p>This module is used to load all Erlang modules into
+ the system. The start script is also fetched with this low-level
loader.</p>
+
<p><c>erl_prim_loader</c> knows about the environment and how to
fetch modules.</p>
- <p>The <c>-loader Loader</c> command line flag can be used to
- choose the method used by the <c>erl_prim_loader</c>. Two
+
+ <p>Command-line flag <c>-loader Loader</c> can be used to
+ choose the method used by <c>erl_prim_loader</c>. Two
<c>Loader</c> methods are supported by the Erlang runtime system:
<c>efile</c> and <c>inet</c>.</p>
-
- <warning><p>The support for loading of code from archive files is
- experimental. The sole purpose of releasing it before it is ready
- is to obtain early feedback. The file format, semantics,
- interfaces etc. may be changed in a future release. The functions
- <c>list_dir/1</c> and <c>read_file_info/1</c> as well as the flag
- <c>-loader_debug</c> are also experimental</p></warning>
-
</description>
<funcs>
<func>
<name name="get_file" arity="1"/>
- <fsummary>Get a file</fsummary>
+ <fsummary>Get a file.</fsummary>
<desc>
- <p>This function fetches a file using the low level loader.
- <c><anno>Filename</anno></c> is either an absolute file name or just the name
- of the file, for example <c>"lists.beam"</c>. If an internal
+ <p>Fetches a file using the low-level loader.
+ <c><anno>Filename</anno></c> is either an absolute filename or only
+ the name of the file, for example, <c>"lists.beam"</c>. If an internal
path is set to the loader, this path is used to find the file.
<c><anno>FullName</anno></c> is the complete name of the fetched file.
<c><anno>Bin</anno></c> is the contents of the file as a binary.</p>
-
- <p>The <c><anno>Filename</anno></c> can also be a file in an archive. For example
+ <p><c><anno>Filename</anno></c> can also be a file in an archive,
+ for example,
<c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin/</c><c>mnesia.beam</c>.
- See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p>
+ For information about archive files, see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="get_path" arity="0"/>
- <fsummary>Get the path set in the loader</fsummary>
+ <fsummary>Get the path set in the loader.</fsummary>
<desc>
- <p>This function gets the path set in the loader. The path is
- set by the <c>init</c> process according to information found
- in the start script.</p>
+ <p>Gets the path set in the loader. The path is
+ set by the <seealso marker="init"><c>init(3)</c></seealso>
+ process according to information found in the start script.</p>
</desc>
</func>
+
<func>
<name name="list_dir" arity="1"/>
- <fsummary>List files in a directory</fsummary>
+ <fsummary>List files in a directory.</fsummary>
<desc>
<p>Lists all the files in a directory. Returns
- <c>{ok, <anno>Filenames</anno>}</c> if successful. Otherwise, it returns
+ <c>{ok, <anno>Filenames</anno>}</c> if successful, otherwise
<c>error</c>. <c><anno>Filenames</anno></c> is a list of
the names of all the files in the directory. The names are
not sorted.</p>
- <p>The <c><anno>Dir</anno></c> can also be a directory in an archive. For example
+ <p><c><anno>Dir</anno></c> can also be a directory in an archive,
+ for example,
<c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c>.
- See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p>
+ For information about archive files, see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="read_file_info" arity="1"/>
- <fsummary>Get information about a file</fsummary>
+ <fsummary>Get information about a file.</fsummary>
<desc>
<p>Retrieves information about a file. Returns
<c>{ok, <anno>FileInfo</anno>}</c> if successful, otherwise
@@ -103,22 +103,25 @@
from which the function is called:</p>
<code type="none">
-include_lib("kernel/include/file.hrl").</code>
- <p>See <seealso marker="kernel:file">file(3)</seealso> for more info about
- the record <c>file_info</c>.</p>
- <p>The <c><anno>Filename</anno></c> can also be a file in an archive. For example
+ <p>For more information about the record <c>file_info</c>, see
+ <seealso marker="kernel:file"><c>file(3)</c></seealso>.</p>
+ <p><c><anno>Filename</anno></c> can also be a file in an archive,
+ for example,
<c>$OTPROOT/lib/</c><c>mnesia-4.4.7.ez/mnesia-4.4.7/ebin/</c><c>mnesia</c>.
- See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p>
+ For information about archive files, see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="read_link_info" arity="1"/>
- <fsummary>Get information about a link or file</fsummary>
+ <fsummary>Get information about a link or file.</fsummary>
<desc>
- <p>This function works like
- <seealso marker="#read_file_info/1">read_file_info/1</seealso>
+ <p>Works like
+ <seealso marker="#read_file_info/1"><c>read_file_info/1</c></seealso>
except that if <c><anno>Filename</anno></c> is a symbolic link,
- information about the link will be returned in the <c>file_info</c>
- record and the <c>type</c> field of the record will be set to
+ information about the link is returned in the <c>file_info</c>
+ record and the <c>type</c> field of the record is set to
<c>symlink</c>.</p>
<p>If <c><anno>Filename</anno></c> is not a symbolic link, this function
returns exactly the same result as <c>read_file_info/1</c>.
@@ -126,20 +129,23 @@
is always equivalent to <c>read_file_info/1</c>.</p>
</desc>
</func>
+
<func>
<name name="set_path" arity="1"/>
- <fsummary>Set the path of the loader</fsummary>
+ <fsummary>Set the path of the loader.</fsummary>
<desc>
- <p>This function sets the path of the loader if <c>init</c>
+ <p>Sets the path of the loader if
+ <seealso marker="init"><c>init(3)</c></seealso>
interprets a <c>path</c> command in the start script.</p>
</desc>
</func>
</funcs>
<section>
- <title>Command Line Flags</title>
+ <title>Command-Line Flags</title>
<p>The <c>erl_prim_loader</c> module interprets the following
- command line flags:</p>
+ command-line flags:</p>
+
<taglist>
<tag><c>-loader Loader</c></tag>
<item>
@@ -147,37 +153,38 @@
<c>erl_prim_loader</c>. <c>Loader</c> can be <c>efile</c>
(use the local file system) or <c>inet</c> (load using
the <c>boot_server</c> on another Erlang node).</p>
- <p>If the <c>-loader</c> flag is omitted, it defaults to
+ <p>If flag <c>-loader</c> is omitted, it defaults to
<c>efile</c>.</p>
</item>
<tag><c>-loader_debug</c></tag>
<item>
<p>Makes the <c>efile</c> loader write some debug information,
- such as the reason for failures, while it handles files.</p>
+ such as the reason for failures, while it handles files.</p>
</item>
<tag><c>-hosts Hosts</c></tag>
<item>
<p>Specifies which other Erlang nodes the <c>inet</c> loader
- can use. This flag is mandatory if the <c>-loader inet</c>
- flag is present. On each host, there must be on Erlang node
- with the <seealso
- marker="kernel:erl_boot_server">erl_boot_server(3)</seealso>
- which handles the load requests.
- <c>Hosts</c> is a list of IP addresses (hostnames
+ can use. This flag is mandatory if flag <c>-loader inet</c>
+ is present. On each host, there must be on Erlang node
+ with the <seealso marker="kernel:erl_boot_server">
+ <c>erl_boot_server(3)</c></seealso>,
+ which handles the load requests.
+ <c>Hosts</c> is a list of IP addresses (hostnames
are not acceptable).</p>
</item>
<tag><c>-setcookie Cookie</c></tag>
<item>
<p>Specifies the cookie of the Erlang runtime system. This flag
- is mandatory if the <c>-loader inet</c> flag is present.</p>
+ is mandatory if flag <c>-loader inet</c> is present.</p>
</item>
</taglist>
</section>
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="init">init(3)</seealso>,
- <seealso marker="kernel:erl_boot_server">erl_boot_server(3)</seealso></p>
+ <title>See Also</title>
+ <p><seealso marker="init"><c>init(3)</c></seealso>,
+ <seealso marker="kernel:erl_boot_server">
+ <c>erl_boot_server(3)</c></seealso></p>
</section>
</erlref>
diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml
index 7841fdfd63..83eef374ca 100644
--- a/erts/doc/src/erl_tracer.xml
+++ b/erts/doc/src/erl_tracer.xml
@@ -29,487 +29,618 @@
<rev></rev>
</header>
<module>erl_tracer</module>
- <modulesummary>Erlang Tracer Behaviour</modulesummary>
+ <modulesummary>Erlang tracer behavior.</modulesummary>
<description>
- <p>A behaviour module for implementing the back end of the erlang
- tracing system. The functions in this module will be called whenever
- a trace probe is triggered. Both the <c>enabled</c> and <c>trace</c>
- functions are called in the context of the entity that triggered the
- trace probe.
- This means that the overhead by having the tracing enabled will be
- greatly effected by how much time is spent in these functions. So do as
- little work as possible in these functions.</p>
+ <p>This behavior module implements the back end of the Erlang
+ tracing system. The functions in this module are called whenever
+ a trace probe is triggered. Both the <c>enabled</c> and <c>trace</c>
+ functions are called in the context of the entity that triggered the
+ trace probe.
+ This means that the overhead by having the tracing enabled is
+ greatly effected by how much time is spent in these functions. So, do as
+ little work as possible in these functions.</p>
+
<note>
- <p>All functions in this behaviour have to be implemented as NIF's.
- This is a limitation that may the lifted in the future.
- There is an <seealso marker="#example">example tracer module nif</seealso>
- implementation at the end of this page.</p>
+ <p>All functions in this behavior must be implemented as NIFs.
+ This limitation can be removed in a future releases.
+ An <seealso marker="#example">example tracer module NIF</seealso>
+ implementation is provided at the end of this page.</p>
</note>
+
<warning>
<p>Do not send messages or issue port commands to the <c>Tracee</c>
- in any of the callbacks. Doing so is not allowed and can cause all
- sorts of strange behaviour, including but not limited to infinite
- recursions.</p>
+ in any of the callbacks. This is not allowed and can cause all
+ sorts of strange behavior, including, but not limited to, infinite
+ recursions.</p>
</warning>
</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" />
+ <name name="trace_tag_call"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_gc"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_ports"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_procs"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_receive"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_running_ports"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_running_procs"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag_send"/>
+ </datatype>
+ <datatype>
+ <name name="trace_tag"/>
<desc>
- <p>The different trace tags that the tracer will be called with.
- Each trace tag is described in greater detail in
- <seealso marker="#Module:trace/5">Module:trace/5</seealso>
- </p>
+ <p>The different trace tags that the tracer is called with.
+ Each trace tag is described in detail in
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>.</p>
</desc>
</datatype>
<datatype>
- <name name="tracee" />
+ <name name="tracee"/>
<desc>
- <p>The process or port that the trace belongs to.
- </p>
+ <p>The process or port that the trace belongs to.</p>
</desc>
</datatype>
<datatype>
- <name name="trace_opts" />
+ <name name="trace_opts"/>
<desc>
- <p>The options for the tracee.</p>
+ <p>The options for the tracee:</p>
<taglist>
<tag><c>timestamp</c></tag>
- <item>If set the tracer has been requested to include a timestamp.</item>
+ <item>If set the tracer has been requested to include a
+ time stamp.</item>
<tag><c>extra</c></tag>
<item>If set the tracepoint has included additonal data about
- the trace event. What the additional data is depends on which
- <c>TraceTag</c> has been triggered. The <c>extra</c> trace data
- corresponds to the fifth elemnt in the trace tuples described in
- <seealso marker="erlang#trace_3_trace_messages">erlang:trace/3</seealso>.</item>
+ the trace event. What the additional data is depends on which
+ <c>TraceTag</c> has been triggered. The <c>extra</c> trace data
+ corresponds to the fifth element in the trace tuples described in
+ <seealso marker="erlang#trace_3_trace_messages">
+ erlang:trace/3</seealso>.</item>
<tag><c>match_spec_result</c></tag>
<item>If set the tracer has been requested to include the output
- of a match specification that was run.</item>
+ of a match specification that was run.</item>
<tag><c>scheduler_id</c></tag>
- <item>Set the scheduler id is to be included by the tracer.</item>
+ <item>If set the scheduler id is to be included by the tracer.</item>
</taglist>
</desc>
</datatype>
<datatype>
- <name name="tracer_state" />
+ <name name="tracer_state"/>
<desc>
- <p>
- The state which is given when calling
- <seealso marker="erlang#trace-3"><c>erlang:trace(PidPortSpec,true,[{tracer,Module,TracerState}])</c></seealso>.
- The tracer state is an immutable value that is passed to erl_tracer callbacks and should
- contain all the data that is needed to generate the trace event.
- </p>
+ <p>The state specified when calling
+ <seealso marker="erlang#trace-3">
+ <c>erlang:trace(PidPortSpec,true,[{tracer,Module,TracerState}])</c></seealso>.
+ The tracer state is an immutable value that is passed to
+ <c>erl_tracer</c> callbacks and is to
+ contain all the data that is needed to generate the trace event.</p>
</desc>
</datatype>
</datatypes>
<section>
- <title>CALLBACK FUNCTIONS</title>
- <p>The following functions
- should be exported from a <c>erl_tracer</c> callback module.</p>
- <taglist>
- <tag><seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso></tag>
- <item>Mandatory</item>
- <tag><seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso></tag>
- <item>Mandatory</item>
- <tag><seealso marker="#Module:enabled_procs/3"><c>Module:enabled_procs/3</c></seealso></tag>
- <item>Optional</item>
- <tag><seealso marker="#Module:trace_procs/5"><c>Module:trace_procs/5</c></seealso></tag>
- <item>Optional</item>
- <tag><seealso marker="#Module:enabled_ports/3"><c>Module:enabled_ports/3</c></seealso></tag>
- <item>Optional</item>
- <tag><seealso marker="#Module:trace_ports/5"><c>Module:trace_ports/5</c></seealso></tag>
- <item>Optional</item>
- <tag><seealso marker="#Module:enabled_running_ports/3"><c>Module:enabled_running_ports/3</c></seealso></tag>
- <item>Optional</item>
- <tag><seealso marker="#Module:trace_running_ports/5"><c>Module:trace_running_ports/5</c></seealso></tag>
- <item>Optional</item>
- <tag><seealso marker="#Module:enabled_running_procs/3"><c>Module:enabled_running_procs/3</c></seealso></tag>
- <item>Optional</item>
- <tag><seealso marker="#Module:trace_running_procs/5"><c>Module:trace_running_procs/5</c></seealso></tag>
- <item>Optional</item>
+ <title>Callback Functions</title>
+ <p>The following functions are to be exported from an <c>erl_tracer</c>
+ callback module:</p>
+
+ <taglist>
+ <tag><seealso marker="#Module:enabled/3">
+ <c>Module:enabled/3</c></seealso></tag>
+ <item>Mandatory</item>
+ <tag><seealso marker="#Module:trace/5">
+ <c>Module:trace/5</c></seealso></tag>
+ <item>Mandatory</item>
+ <tag><seealso marker="#Module:enabled_call/3">
+ <c>Module:enabled_call/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_call/5">
+ <c>Module:trace_call/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_garbage_collection/3">
+ <c>Module:enabled_garbage_collection/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_garbage_collection/5">
+ <c>Module:trace_garbage_collection/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_ports/3">
+ <c>Module:enabled_ports/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_ports/5">
+ <c>Module:trace_ports/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_procs/3">
+ <c>Module:enabled_procs/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_procs/5">
+ <c>Module:trace_procs/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_receive/3">
+ <c>Module:enabled_receive/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_receive/5">
+ <c>Module:trace_receive/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_running_ports/3">
+ <c>Module:enabled_running_ports/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_running_ports/5">
+ <c>Module:trace_running_ports/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_running_procs/3">
+ <c>Module:enabled_running_procs/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_running_procs/5">
+ <c>Module:trace_running_procs/5</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:enabled_send/3">
+ <c>Module:enabled_send/3</c></seealso></tag>
+ <item>Optional</item>
+ <tag><seealso marker="#Module:trace_send/5">
+ <c>Module:trace_send/5</c></seealso></tag>
+ <item>Optional</item>
</taglist>
-
</section>
<funcs>
<func>
<name>Module:enabled(TraceTag, TracerState, Tracee) -> Result</name>
- <fsummary>Check if a trace event should be generated.</fsummary>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
<type>
- <v>TraceTag = <seealso marker="#type-trace_tag">trace_tag()</seealso> | trace_status</v>
+ <v>TraceTag = <seealso marker="#type-trace_tag">
+ trace_tag()</seealso> | trace_status</v>
<v>TracerState = term()</v>
<v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
- <v>Result = trace | discard | remove</v>
+ <v>Result = trace | discard | remove</v>
</type>
<desc>
- <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
- necessary trace data will be created and the trace call-back of the tracer
- will be called. If <c>discard</c> is returned, this trace call
- will be discarded and no call to trace will be done.
- </p>
- <p><c>trace_status</c> is a special type of <c>TraceTag</c> which is used
- 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. If <c>remove</c> is returned when the <c>trace_status</c>
- is checked, the tracer will be removed from the tracee.</p>
- <p>This function may be called multiple times per tracepoint, so it
- is important that it is both fast and side effect free.</p>
+ <p>This callback is called whenever a tracepoint is triggered. It
+ allows the tracer to decide whether a trace is to be generated or not.
+ This check is made as early as possible to limit the amount of
+ overhead associated with tracing. If <c>trace</c> is returned, the
+ necessary trace data is created and the trace callback of the tracer
+ is called. If <c>discard</c> is returned, this trace call is
+ discarded and no call to trace is done.</p>
+ <p><c>trace_status</c> is a special type of <c>TraceTag</c>, which is
+ used to check if the tracer is still to be active. It is called in
+ multiple scenarios, but most significantly it is used when tracing
+ is started using this tracer. If <c>remove</c> is returned when the
+ <c>trace_status</c> is checked, the tracer is removed from the
+ tracee.</p>
+ <p>This function can be called multiple times per tracepoint, so it
+ is important that it is both fast and without side effects.</p>
</desc>
</func>
+
<func>
- <name>Module:trace(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name>
- <fsummary>Check if a trace event should be generated.</fsummary>
+ <name>Module:enabled_call(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
<type>
- <v>TraceTag = <seealso marker="#type-trace_tag">trace_tag()</seealso></v>
+ <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>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
- <v>Result = ok</v>
+ <v>Result = trace | discard | remove</v>
</type>
<desc>
- <p>This callback will be called when a tracepoint is triggered and
- the <seealso marker="#Module:enabled/3">Module:enabled/3</seealso>
- callback returned <c>trace</c>. In it any side effects needed by
- the tracer should be done. The tracepoint payload is located in
- the <c>TraceTerm</c>. The content of the TraceTerm depends on which
- <c>TraceTag</c> has been triggered.
- The <c>TraceTerm</c> corresponds to the
- fourth element in the trace tuples described in
- <seealso marker="erlang#trace_3_trace_messages">erlang:trace/3</seealso>.
- If the trace tuple has five elements, the fifth element will be sent as
- the <c>extra</c> value in the <c>Opts</c> maps.</p>
+ <p>This callback is 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 undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
</desc>
</func>
+
<func>
- <name name="trace">Module:trace(seq_trace, TracerState, Label, SeqTraceInfo, Opts) -> Result</name>
- <fsummary>Check if a sequence trace event should be generated.</fsummary>
+ <name>Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
<type>
+ <v>TraceTag = <seealso marker="#type-trace_tag_gc">
+ trace_tag_gc()</seealso></v>
<v>TracerState = term()</v>
- <v>Label = term()</v>
- <v>SeqTraceInfo = term()</v>
- <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
- <v>Result = ok</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>Result = trace | discard | remove</v>
</type>
<desc>
- <p>The <c>TraceTag</c> <c>seq_trace</c> is handled a little bit
- differently. There is not <c>Tracee</c> for seq_trace, instead the
- <c>Label</c> associated with the seq_trace event is given.
- For more info on what <c>Label</c> and <c>SeqTraceInfo</c> can be
- see the <seealso marker="kernel:seq_trace">seq_trace</seealso> manual.</p>
+ <p>This callback is 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 undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
</desc>
</func>
<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>
+ <name>Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to 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 is 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 undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<func>
- <name>Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm, 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>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="#Module:enabled_procs/3">Module:enabled_procs/3</seealso>
- callback returned <c>trace</c>.</p>
- <p>If <c>trace_procs/5</c> is not defined <c>trace/5</c> will be called instead.</p>
- </desc>
+ <name>Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to 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 is 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 undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<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>
+ <name>Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result
+ </name>
+ <fsummary>Check if a trace event is to 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 is 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 undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<func>
- <name>Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm, 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>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="#Module:enabled_ports/3">Module:enabled_ports/3</seealso>
- callback returned <c>trace</c>.</p>
- <p>If <c>trace_ports/5</c> is not defined <c>trace/5</c> will be called instead.</p>
- </desc>
+ <name>Module:enabled_running_ports(TraceTag, TracerState, Tracee) ->
+ Result</name>
+ <fsummary>Check if a trace event is to 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 is 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 undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<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>
+ <name>Module:enabled_running_procs(TraceTag, TracerState, Tracee) ->
+ Result</name>
+ <fsummary>Check if a trace event is to 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 is 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 undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<func>
- <name>Module:trace_running_procs(TraceTag, TracerState, Tracee, TraceTerm, 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>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="#Module:enabled_running_procs/3">Module:enabled_running_procs/3</seealso>
- callback returned <c>trace</c>.</p>
- <p>If <c>trace_running_procs/5</c> is not defined <c>trace/5</c> will be called instead.</p>
- </desc>
+ <name>Module:enabled_send(TraceTag, TracerState, Tracee) -> Result</name>
+ <fsummary>Check if a trace event is to 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 is 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 undefined,
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<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>
+ <name>Module:trace(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to be generated.</fsummary>
+ <type>
+ <v>TraceTag = <seealso marker="#type-trace_tag">
+ trace_tag()</seealso></v>
+ <v>TracerState = term()</v>
+ <v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
+ <v>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso>
+ callback returned <c>trace</c>. In it any side effects needed by
+ the tracer are to be done. The tracepoint payload is located in
+ the <c>TraceTerm</c>. The content of the <c>TraceTerm</c>
+ depends on which <c>TraceTag</c> is triggered.
+ <c>TraceTerm</c> corresponds to the
+ fourth element in the trace tuples described in
+ <seealso marker="erlang#trace_3_trace_messages">
+ <c>erlang:trace/3</c></seealso>.</p>
+ <p>If the trace tuple has five elements, the fifth element
+ will be sent as the <c>extra</c> value in the <c>Opts</c> maps.</p>
+ </desc>
</func>
<func>
- <name>Module:trace_running_ports(TraceTag, TracerState, Tracee, TraceTerm, 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>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="#Module:enabled_running_ports/3">Module:enabled_running_ports/3</seealso>
- callback returned <c>trace</c>.</p>
- <p>If <c>trace_running_ports/5</c> is not defined <c>trace/5</c> will be called instead.</p>
- </desc>
+ <name name="trace">Module:trace(seq_trace, TracerState, Label,
+ SeqTraceInfo, Opts) -> Result</name>
+ <fsummary>Check if a sequence trace event is to be generated.</fsummary>
+ <type>
+ <v>TracerState = term()</v>
+ <v>Label = term()</v>
+ <v>SeqTraceInfo = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>The <c>TraceTag</c> <c>seq_trace</c> is handled slightly
+ differently. There is no <c>Tracee</c> for <c>seq_trace</c>, instead
+ the <c>Label</c> associated with the <c>seq_trace</c> event is
+ specified.</p>
+ <p>For more information on what <c>Label</c> and <c>SeqTraceInfo</c>
+ can be, see <seealso marker="kernel:seq_trace">
+ <c>seq_trace(3)</c></seealso>.</p>
+ </desc>
</func>
<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>
+ <name>Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to 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>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_call/3">
+ <c>Module:enabled_call/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_call/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<func>
- <name>Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm, 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>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="#Module:enabled_call/3">Module:enabled_call/3</seealso>
- callback returned <c>trace</c>.</p>
- <p>If <c>trace_call/5</c> is not defined <c>trace/5</c> will be called instead.</p>
- </desc>
+ <name>Module:trace_garbage_collection(TraceTag, TracerState, Tracee,
+ TraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event is to 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>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_garbage_collection/3">
+ <c>Module:enabled_garbage_collection/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_garbage_collection/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<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>
+ <name>Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to 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>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_ports/3">
+ <c>Module:enabled_ports/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_ports/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<func>
- <name>Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm, 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>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="#Module:enabled_send/3">Module:enabled_send/3</seealso>
- callback returned <c>trace</c>.</p>
- <p>If <c>trace_send/5</c> is not defined <c>trace/5</c> will be called instead.</p>
- </desc>
+ <name>Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to 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>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_procs/3">
+ <c>Module:enabled_procs/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_procs/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<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>
+ <name>Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to 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>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_receive/3">
+ <c>Module:enabled_receive/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_receive/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<func>
- <name>Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm, 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>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="#Module:enabled_receive/3">Module:enabled_receive/3</seealso>
- callback returned <c>trace</c>.</p>
- <p>If <c>trace_receive/5</c> is not defined <c>trace/5</c> will be called instead.</p>
- </desc>
+ <name>Module:trace_running_ports(TraceTag, TracerState, Tracee,
+ TraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event is to 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>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_running_ports/3">
+ <c>Module:enabled_running_ports/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_running_ports/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<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>
+ <name>Module:trace_running_procs(TraceTag, TracerState, Tracee,
+ TraceTerm, Opts) -> Result</name>
+ <fsummary>Check if a trace event is to 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>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_running_procs/3">
+ <c>Module:enabled_running_procs/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_running_procs/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
<func>
- <name>Module:trace_garbage_collection(TraceTag, TracerState, Tracee, TraceTerm, 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>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="#Module:enabled_garbage_collection/3">Module:enabled_garbage_collection/3</seealso>
- callback returned <c>trace</c>.</p>
- <p>If <c>trace_garbage_collection/5</c> is not defined <c>trace/5</c> will be called instead.</p>
- </desc>
+ <name>Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm,
+ Opts) -> Result</name>
+ <fsummary>Check if a trace event is to 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>TraceTerm = term()</v>
+ <v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
+ <v>Result = ok</v>
+ </type>
+ <desc>
+ <p>This callback is called when a tracepoint is triggered and the
+ <seealso marker="#Module:enabled_send/3">
+ <c>Module:enabled_send/3</c></seealso>
+ callback returned <c>trace</c>.</p>
+ <p>If <c>trace_send/5</c> is undefined,
+ <seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso>
+ is called instead.</p>
+ </desc>
</func>
-
</funcs>
+
<section>
<marker id="example"></marker>
- <title>Erl Tracer Module example</title>
- <p>In the example below a tracer module with a nif backend sends a message
- for each <c>send</c> trace tag containing only the sender and receiver.
- Using this tracer module, a much more lightweight message tracer is
- used that only records who sent messages to who.</p>
- <p>Here is an example session using it on Linux.</p>
+ <title>Erl Tracer Module Example</title>
+ <p>In this example, a tracer module with a NIF back end sends a
+ message for each <c>send</c> trace tag containing only the sender and
+ receiver. Using this tracer module, a much more lightweight message
+ tracer is used, which only records who sent messages to who.</p>
+
+ <p>The following is an example session using it on Linux:</p>
+
<pre>
$ gcc -I erts-8.0/include/ -fPIC -shared -o erl_msg_tracer.so erl_msg_tracer.c
$ erl
@@ -532,9 +663,10 @@ ok
{trace,#Port&lt;0.490&gt;,&lt;0.4.0&gt;}
{ok,&lt;0.40.0&gt;}
{trace,&lt;0.41.0&gt;,&lt;0.27.0&gt;}
-5&gt;
- </pre>
- <p>erl_msg_tracer.erl</p>
+5&gt;</pre>
+
+ <p><c>erl_msg_tracer.erl</c>:</p>
+
<pre>
-module(erl_msg_tracer).
@@ -546,10 +678,11 @@ load() ->
enabled(_, _, _) ->
error.
-trace(_, _, _,_, _) ->
- error.
- </pre>
- <p>erl_msg_tracer.c</p>
+trace(_, _, _, _, _) ->
+ error.</pre>
+
+ <p><c>erl_msg_tracer.c</c>:</p>
+
<pre>
#include "erl_nif.h"
@@ -640,7 +773,6 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
}
return enif_make_atom(env, "ok");
-}
- </pre>
+}</pre>
</section>
</erlref>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 6289f033b2..24a091073d 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2016</year>
+ <year>1996</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,18 +32,21 @@
<module>erlang</module>
<modulesummary>The Erlang BIFs.</modulesummary>
<description>
- <p>By convention, most Built-In Functions (BIFs) are seen as being
+ <p>By convention, most Built-In Functions (BIFs) are included
in this module. Some of the BIFs are viewed more
or less as part of the Erlang programming language and are
<em>auto-imported</em>. Thus, it is not necessary to specify the
module name. For example, the calls <c>atom_to_list(Erlang)</c>
and <c>erlang:atom_to_list(Erlang)</c> are identical.</p>
+
<p>Auto-imported BIFs are listed without module prefix.
BIFs listed with module prefix are not auto-imported.</p>
+
<p>BIFs can fail for various reasons. All BIFs fail with
reason <c>badarg</c> if they are called with arguments of an
incorrect type. The other reasons are described in the
description of each individual BIF.</p>
+
<p>Some BIFs can be used in guard tests and are marked with
"Allowed in guard tests".</p>
</description>
@@ -53,100 +56,129 @@
<name>ext_binary()</name>
<desc>
<p>A binary data object, structured according to
- the Erlang external term format.</p>
+ the Erlang external term format.</p>
</desc>
</datatype>
-
<datatype>
<name name="message_queue_data"></name>
- <desc><p>See <seealso marker="#process_flag_message_queue_data"><c>erlang:process_flag(message_queue_data, MQD)</c></seealso>.</p>
+ <desc>
+ <p>See <seealso marker="#process_flag_message_queue_data">
+ <c>process_flag(message_queue_data, MQD)</c></seealso>.</p>
</desc>
</datatype>
-
<datatype>
<name name="timestamp"></name>
- <desc><p>See <seealso marker="#timestamp/0">erlang:timestamp/0</seealso>.</p>
+ <desc>
+ <p>See <seealso marker="#timestamp/0">
+ <c>erlang:timestamp/0</c></seealso>.</p>
</desc>
</datatype>
<datatype>
<name name="time_unit"></name>
- <desc><p><marker id="type_time_unit"/>
- Supported time unit representations:</p>
+ <desc>
+ <marker id="type_time_unit"/>
+ <p>Supported time unit representations:</p>
<taglist>
- <tag><c>PartsPerSecond :: integer() >= 1</c></tag>
- <item><p>Time unit expressed in parts per second. That is,
- the time unit equals <c>1/PartsPerSecond</c> second.</p></item>
-
+ <tag><c>PartsPerSecond :: integer() >= 1</c></tag>
+ <item>
+ <p>Time unit expressed in parts per second. That is,
+ the time unit equals <c>1/PartsPerSecond</c> second.</p>
+ </item>
+ <tag><c>second</c></tag>
+ <item>
+ <p>Symbolic representation of the time unit
+ represented by the integer <c>1</c>.</p>
+ </item>
+ <tag><c>millisecond</c></tag>
+ <item>
+ <p>Symbolic representation of the time unit
+ represented by the integer <c>1000</c>.</p>
+ </item>
+ <tag><c>microsecond</c></tag>
+ <item>
+ <p>Symbolic representation of the time unit
+ represented by the integer <c>1000000</c>.</p>
+ </item>
+ <tag><c>nanosecond</c></tag>
+ <item>
+ <p>Symbolic representation of the time unit
+ represented by the integer <c>1000000000</c>.</p>
+ </item>
+ <tag><c>native</c></tag>
+ <item>
+ <p>Symbolic representation of the native time unit
+ used by the Erlang runtime system.</p>
+ <p>The <c>native</c> time unit is determined at
+ runtime system start, and remains the same until
+ the runtime system terminates. If a runtime system
+ is stopped and then started again (even on the same
+ machine), the <c>native</c> time unit of the new
+ runtime system instance can differ from the
+ <c>native</c> time unit of the old runtime system
+ instance.</p>
+ <p>One can get an approximation of the <c>native</c>
+ time unit by calling
+ <seealso marker="erlang:convert_time_unit/3">
+ <c>erlang:convert_time_unit(1, second, native)</c></seealso>.
+ The result equals the number
+ of whole <c>native</c> time units per second. If
+ the number of <c>native</c> time units per second does not
+ add up to a whole number, the result is rounded downwards.</p>
+ <note>
+ <p>The value of the <c>native</c> time unit gives
+ you more or less no information about the
+ quality of time values. It sets a limit for the
+ <seealso marker="time_correction#Time_Resolution">
+ resolution</seealso> and for the
+ <seealso marker="time_correction#Time_Precision">
+ precision</seealso> of time values,
+ but it gives no information about the
+ <seealso marker="time_correction#Time_Accuracy">
+ accuracy</seealso> of time values. The resolution of
+ the <c>native</c> time unit and the resolution of time
+ values can differ significantly.</p>
+ </note>
+ </item>
+ <tag><c>perf_counter</c></tag>
+ <item>
+ <p>Symbolic representation of the performance counter
+ time unit used by the Erlang runtime system.</p>
+ <p>The <c>perf_counter</c> time unit behaves much in the same way
+ as the <c>native</c> time unit. That is, it can differ between
+ runtime restarts. To get values of this type, call
+ <seealso marker="kernel:os#perf_counter/0">
+ <c>os:perf_counter/0</c></seealso>.</p>
+ </item>
+ <tag><seealso marker="#type_deprecated_time_unit"><c>deprecated_time_unit()</c></seealso></tag>
+ <item><p>
+ Deprecated symbolic representations kept for backwards-compatibility.
+ </p></item>
+ </taglist>
+ <p>The <c>time_unit/0</c> type can be extended.
+ To convert time values between time units, use
+ <seealso marker="#convert_time_unit/3">
+ <c>erlang:convert_time_unit/3</c></seealso>.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="deprecated_time_unit"></name>
+ <desc><marker id="type_deprecated_time_unit"/>
+ <p>The <seealso marker="#type_time_unit"><c>time_unit()</c></seealso>
+ type also consist of the following <em>deprecated</em> symbolic
+ time units:</p>
+ <taglist>
<tag><c>seconds</c></tag>
- <item><p>Symbolic representation of the time unit
- represented by the integer <c>1</c>.</p></item>
+ <item><p>Same as <seealso marker="#type_time_unit"><c>second</c></seealso>.</p></item>
<tag><c>milli_seconds</c></tag>
- <item><p>Symbolic representation of the time unit
- represented by the integer <c>1000</c>.</p></item>
+ <item><p>Same as <seealso marker="#type_time_unit"><c>millisecond</c></seealso>.</p></item>
<tag><c>micro_seconds</c></tag>
- <item><p>Symbolic representation of the time unit
- represented by the integer <c>1000000</c>.</p></item>
+ <item><p>Same as <seealso marker="#type_time_unit"><c>microsecond</c></seealso>.</p></item>
<tag><c>nano_seconds</c></tag>
- <item><p>Symbolic representation of the time unit
- represented by the integer <c>1000000000</c>.</p></item>
-
- <tag><c>native</c></tag>
- <item><p>Symbolic representation of the native time unit
- used by the Erlang runtime system.</p>
-
- <p>The <c>native</c> time unit is determined at
- runtime system start, and remains the same until
- the runtime system terminates. If a runtime system
- is stopped and then started again (even on the same
- machine), the <c>native</c> time unit of the new
- runtime system instance can differ from the
- <c>native</c> time unit of the old runtime system
- instance.</p>
-
- <p>One can get an approximation of the <c>native</c>
- time unit by calling <c>erlang:convert_time_unit(1,
- seconds, native)</c>. The result equals the number
- of whole <c>native</c> time units per second. In case
- the number of <c>native</c> time units per second does
- not add up to a whole number, the result is rounded downwards.</p>
-
- <note>
- <p>The value of the <c>native</c> time unit gives
- you more or less no information at all about the
- quality of time values. It sets a limit for
- the
- <seealso marker="time_correction#Time_Resolution">resolution</seealso>
- as well as for the
- <seealso marker="time_correction#Time_Precision">precision</seealso>
- of time values,
- but it gives absolutely no information at all about the
- <seealso marker="time_correction#Time_Accuracy">accuracy</seealso>
- of time values. The resolution of the <c>native</c> time
- unit and the resolution of time values can differ
- significantly.</p>
- </note>
- </item>
-
- <tag><c>perf_counter</c></tag>
- <item><p>Symbolic representation of the performance counter
- time unit used by the Erlang runtime system.</p>
-
- <p>The <c>perf_counter</c> time unit behaves much in the same way
- as the <c>native</c> time unit. That is it might differ inbetween
- run-time restarts. You get values of this type by calling
- <seealso marker="kernel:os#perf_counter/0"><c>os:perf_counter()</c></seealso>
- </p>
- </item>
-
+ <item><p>Same as <seealso marker="#type_time_unit"><c>nanosecond</c></seealso>.</p></item>
</taglist>
-
- <p>The <c>time_unit/0</c> type may be extended. Use
- <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit/3</c></seealso>
- in order to convert time values between time units.</p>
-
</desc>
</datatype>
</datatypes>
@@ -175,61 +207,60 @@
<func>
<name name="adler32" arity="1"/>
- <fsummary>Computes adler32 checksum.</fsummary>
+ <fsummary>Compute adler32 checksum.</fsummary>
<desc>
<p>Computes and returns the adler32 checksum for
- <c><anno>Data</anno></c>.</p>
+ <c><anno>Data</anno></c>.</p>
</desc>
</func>
<func>
<name name="adler32" arity="2"/>
- <fsummary>Computes adler32 checksum.</fsummary>
+ <fsummary>Compute adler32 checksum.</fsummary>
<desc>
<p>Continues computing the adler32 checksum by combining
- the previous checksum, <c><anno>OldAdler</anno></c>, with
- the checksum of <c><anno>Data</anno></c>.</p>
+ the previous checksum, <c><anno>OldAdler</anno></c>, with
+ the checksum of <c><anno>Data</anno></c>.</p>
<p>The following code:</p>
<code>
- X = erlang:adler32(Data1),
- Y = erlang:adler32(X,Data2).</code>
- <p>assigns the same value to <c>Y</c> as this:</p>
+X = erlang:adler32(Data1),
+Y = erlang:adler32(X,Data2).</code>
+ <p>assigns the same value to <c>Y</c> as this:</p>
<code>
- Y = erlang:adler32([Data1,Data2]).</code>
+Y = erlang:adler32([Data1,Data2]).</code>
</desc>
</func>
<func>
<name name="adler32_combine" arity="3"/>
- <fsummary>Combines two adler32 checksums.</fsummary>
+ <fsummary>Combine two adler32 checksums.</fsummary>
<desc>
<p>Combines two previously computed adler32 checksums.
- This computation requires the size of the data object for
- the second checksum to be known.</p>
+ This computation requires the size of the data object for
+ the second checksum to be known.</p>
<p>The following code:</p>
<code>
- Y = erlang:adler32(Data1),
- Z = erlang:adler32(Y,Data2).</code>
+Y = erlang:adler32(Data1),
+Z = erlang:adler32(Y,Data2).</code>
<p>assigns the same value to <c>Z</c> as this:</p>
<code>
- X = erlang:adler32(Data1),
- Y = erlang:adler32(Data2),
- Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
+X = erlang:adler32(Data1),
+Y = erlang:adler32(Data2),
+Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</desc>
</func>
<func>
<name name="append_element" arity="2"/>
- <fsummary>Appends an extra element to a tuple.</fsummary>
+ <fsummary>Append an extra element to a tuple.</fsummary>
<desc>
<p>Returns a new tuple that has one element more than
<c><anno>Tuple1</anno></c>, and contains the elements in
- <c><anno>Tuple1</anno></c>
+ <c><anno>Tuple1</anno></c>
followed by <c><anno>Term</anno></c> as the last element.
- Semantically equivalent to
+ Semantically equivalent to
<c>list_to_tuple(tuple_to_list(<anno>Tuple1</anno>) ++
- [<anno>Term</anno>])</c>, but much faster.</p>
- <p>Example:</p>
+ [<anno>Term</anno>])</c>, but much faster. Example:</p>
<pre>
> <input>erlang:append_element({one, two}, three).</input>
{one,two,three}</pre>
@@ -238,32 +269,31 @@
<func>
<name name="apply" arity="2"/>
- <fsummary>Applies a function to an argument list.</fsummary>
+ <fsummary>Apply a function to an argument list.</fsummary>
<desc>
<p>Calls a fun, passing the elements in <c><anno>Args</anno></c>
- as arguments.</p>
+ as arguments.</p>
<p>If the number of elements in the arguments are known at
compile time, the call is better written as
<c><anno>Fun</anno>(Arg1, Arg2, ... ArgN)</c>.</p>
<warning>
- <p>Earlier, <c><anno>Fun</anno></c> could also be given as
+ <p>Earlier, <c><anno>Fun</anno></c> could also be specified as
<c>{Module, Function}</c>, equivalent to
- <c>apply(Module, Function, Args)</c>. This use is
- deprecated and will stop working in a future release.</p>
+ <c>apply(Module, Function, Args)</c>. <em>This use is
+ deprecated and will stop working in a future release.</em></p>
</warning>
</desc>
</func>
<func>
<name name="apply" arity="3"/>
- <fsummary>Applies a function to an argument list.</fsummary>
+ <fsummary>Apply a function to an argument list.</fsummary>
<desc>
<p>Returns the result of applying <c>Function</c> in
<c><anno>Module</anno></c> to <c><anno>Args</anno></c>.
- The applied function must
+ The applied function must
be exported from <c>Module</c>. The arity of the function is
- the length of <c>Args</c>.</p>
- <p>Example:</p>
+ the length of <c>Args</c>. Example:</p>
<pre>
> <input>apply(lists, reverse, [[a, b, c]]).</input>
[c,b,a]
@@ -271,39 +301,43 @@
"Erlang"</pre>
<p>If the number of arguments are known at compile time,
the call is better written as
- <c><anno>Module</anno>:<anno>Function</anno>(Arg1, Arg2, ..., ArgN)</c>.</p>
- <p>Failure: <c>error_handler:undefined_function/3</c> is called
+ <c><anno>Module</anno>:<anno>Function</anno>(Arg1, Arg2, ...,
+ ArgN)</c>.</p>
+ <p>Failure: <seealso marker="kernel:error_handler#undefined_function/3">
+ <c>error_handler:undefined_function/3</c></seealso> is called
if the applied function is not exported. The error handler
can be redefined (see
- <seealso marker="#process_flag/2">process_flag/2</seealso>).
+ <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>).
If <c>error_handler</c> is undefined, or if the user has
redefined the default <c>error_handler</c> so the replacement
- module is undefined, an error with the reason <c>undef</c>
+ module is undefined, an error with reason <c>undef</c>
is generated.</p>
</desc>
</func>
<func>
<name name="atom_to_binary" arity="2"/>
- <fsummary>Returns the binary representation of an atom.</fsummary>
+ <fsummary>Return the binary representation of an atom.</fsummary>
<desc>
<p>Returns a binary corresponding to the text
representation of <c><anno>Atom</anno></c>.
If <c><anno>Encoding</anno></c>
- is <c>latin1</c>, there is one byte for each character
+ is <c>latin1</c>, one byte exists for each character
in the text representation. If <c><anno>Encoding</anno></c> is
<c>utf8</c> or
<c>unicode</c>, the characters are encoded using UTF-8
(that is, characters from 128 through 255 are
encoded in two bytes).</p>
- <note><p><c>atom_to_binary(<anno>Atom</anno>, latin1)</c> never
- fails because the text representation of an atom can only
- contain characters from 0 through 255. In a future release,
- the text representation
- of atoms can be allowed to contain any Unicode character and
- <c>atom_to_binary(<anno>Atom</anno>, latin1)</c> will then fail if the
- text representation for <c><anno>Atom</anno></c> contains a Unicode
- character greater than 255.</p></note>
+ <note>
+ <p><c>atom_to_binary(<anno>Atom</anno>, latin1)</c> never
+ fails, as the text representation of an atom can only
+ contain characters from 0 through 255. In a future release,
+ the text representation
+ of atoms can be allowed to contain any Unicode character and
+ <c>atom_to_binary(<anno>Atom</anno>, latin1)</c> then fails if the
+ text representation for <c><anno>Atom</anno></c> contains a Unicode
+ character &gt; 255.</p>
+ </note>
<p>Example:</p>
<pre>
> <input>atom_to_binary('Erlang', latin1).</input>
@@ -325,12 +359,12 @@
<func>
<name name="binary_part" arity="2"/>
- <fsummary>Extracts a part of a binary.</fsummary>
+ <fsummary>Extract a part of a binary.</fsummary>
<desc>
<p>Extracts the part of the binary described by
<c><anno>PosLen</anno></c>.</p>
<p>Negative length can be used to extract bytes at the end
- of a binary, for example:</p>
+ of a binary, for example:</p>
<code>
1> Bin = &lt;&lt;1,2,3,4,5,6,7,8,9,10&gt;&gt;.
2> binary_part(Bin,{byte_size(Bin), -5}).
@@ -342,43 +376,44 @@
1> Bin = &lt;&lt;1,2,3&gt;&gt;
2> binary_part(Bin,{0,2}).
&lt;&lt;1,2&gt;&gt;</code>
- <p>For details about the <c><anno>PosLen</anno></c> semantics, see the
- <seealso marker="stdlib:binary">binary</seealso>
- manual page in <c>STDLIB</c>.</p>
+ <p>For details about the <c><anno>PosLen</anno></c> semantics, see
+ <seealso marker="stdlib:binary"><c>binary(3)</c></seealso>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
<name name="binary_part" arity="3"/>
- <fsummary>Extracts a part of a binary.</fsummary>
+ <fsummary>Extract a part of a binary.</fsummary>
<desc>
<p>The same as <c>binary_part(<anno>Subject</anno>,
- {<anno>Start</anno>, <anno>Length</anno>})</c>.</p>
+ {<anno>Start</anno>, <anno>Length</anno>})</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
<name name="binary_to_atom" arity="2"/>
- <fsummary>Converts from text representation to an atom.</fsummary>
+ <fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>Returns the atom whose text representation is
- <c><anno>Binary</anno></c>.
- If <c><anno>Encoding</anno></c> is <c>latin1</c>, no
- translation of bytes in the binary is done.
- If <c><anno>Encoding</anno></c>
- is <c>utf8</c> or <c>unicode</c>, the binary must contain
- valid UTF-8 sequences. Only Unicode characters up
- to 255 are allowed.</p>
- <note><p><c>binary_to_atom(<anno>Binary</anno>, utf8)</c> fails if
- the binary contains Unicode characters greater than 255.
- In a future release, such Unicode characters can be allowed
- and <c>binary_to_atom(<anno>Binary</anno>, utf8)</c> does then not fail.
- For more information on Unicode support in atoms, see the
- <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8
- encoded atoms</seealso>
- in Section "External Term Format" in the User's Guide.</p></note>
+ <c><anno>Binary</anno></c>.
+ If <c><anno>Encoding</anno></c> is <c>latin1</c>, no
+ translation of bytes in the binary is done.
+ If <c><anno>Encoding</anno></c>
+ is <c>utf8</c> or <c>unicode</c>, the binary must contain
+ valid UTF-8 sequences. Only Unicode characters up
+ to 255 are allowed.</p>
+ <note>
+ <p><c>binary_to_atom(<anno>Binary</anno>, utf8)</c> fails if
+ the binary contains Unicode characters &gt; 255.
+ In a future release, such Unicode characters can be allowed and
+ <c>binary_to_atom(<anno>Binary</anno>, utf8)</c> does then not fail.
+ For more information about Unicode support in atoms, see the
+ <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8
+ encoded atoms</seealso>
+ in section "External Term Format" in the User's Guide.</p>
+ </note>
<p>Examples:</p>
<pre>
> <input>binary_to_atom(&lt;&lt;"Erlang"&gt;&gt;, latin1).</input>
@@ -392,10 +427,10 @@
<func>
<name name="binary_to_existing_atom" arity="2"/>
- <fsummary>Converts from text representation to an atom.</fsummary>
+ <fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>As
- <seealso marker="#binary_to_atom/2">binary_to_atom/2</seealso>,
+ <seealso marker="#binary_to_atom/2"><c>binary_to_atom/2</c></seealso>,
but the atom must exist.</p>
<p>Failure: <c>badarg</c> if the atom does not exist.</p>
</desc>
@@ -403,7 +438,7 @@
<func>
<name name="binary_to_float" arity="1"/>
- <fsummary>Converts from text representation to a float.</fsummary>
+ <fsummary>Convert from text representation to a float.</fsummary>
<desc>
<p>Returns the float whose text representation is
<c><anno>Binary</anno></c>, for example:</p>
@@ -417,7 +452,7 @@
<func>
<name name="binary_to_integer" arity="1"/>
- <fsummary>Converts from text representation to an integer.</fsummary>
+ <fsummary>Convert from text representation to an integer.</fsummary>
<desc>
<p>Returns an integer whose text representation is
<c><anno>Binary</anno></c>, for example:</p>
@@ -431,10 +466,11 @@
<func>
<name name="binary_to_integer" arity="2"/>
- <fsummary>Converts from text representation to an integer.</fsummary>
+ <fsummary>Convert from text representation to an integer.</fsummary>
<desc>
<p>Returns an integer whose text representation in base
- <c><anno>Base</anno></c> is <c><anno>Binary</anno></c>, for example:</p>
+ <c><anno>Base</anno></c> is <c><anno>Binary</anno></c>, for
+ example:</p>
<pre>
> <input>binary_to_integer(&lt;&lt;"3FF"&gt;&gt;, 16).</input>
1023</pre>
@@ -445,7 +481,7 @@
<func>
<name name="binary_to_list" arity="1"/>
- <fsummary>Converts a binary to a list.</fsummary>
+ <fsummary>Convert a binary to a list.</fsummary>
<desc>
<p>Returns a list of integers corresponding to the bytes of
<c><anno>Binary</anno></c>.</p>
@@ -454,86 +490,98 @@
<func>
<name name="binary_to_list" arity="3"/>
- <fsummary>Converts part of a binary to a list.</fsummary>
- <type_desc variable="Start">1..byte_size(<c><anno>Binary</anno></c>)</type_desc>
+ <fsummary>Convert part of a binary to a list.</fsummary>
+ <type_desc variable="Start">1..byte_size(<c><anno>Binary</anno></c>)
+ </type_desc>
<desc>
<p>As <c>binary_to_list/1</c>, but returns a list of integers
corresponding to the bytes from position <c><anno>Start</anno></c> to
position <c><anno>Stop</anno></c> in <c><anno>Binary</anno></c>.
- The positions in the
+ The positions in the
binary are numbered starting from 1.</p>
- <note><p>The one-based indexing for binaries used by
- this function is deprecated. New code is to use
- <seealso marker="stdlib:binary#bin_to_list/3">binary:bin_to_list/3</seealso>
- in <c>STDLIB</c> instead. All functions in module
- <c>binary</c> consistently use zero-based indexing.</p></note>
- </desc>
- </func>
-
- <func>
- <name name="bitstring_to_list" arity="1"/>
- <fsummary>Converts a bitstring to a list.</fsummary>
- <desc>
- <p>Returns a list of integers corresponding to the bytes of
- <c><anno>Bitstring</anno></c>. If the number of bits in the binary
- is not divisible by 8, the last element of the list is a bitstring
- containing the remaining 1-7 bits.</p>
+ <note>
+ <p><em>The one-based indexing for binaries used by
+ this function is deprecated.</em> New code is to use
+ <seealso marker="stdlib:binary#bin_to_list/3">
+ <c>binary:bin_to_list/3</c></seealso>
+ in STDLIB instead. All functions in module
+ <c>binary</c> consistently use zero-based indexing.</p>
+ </note>
</desc>
</func>
<func>
<name name="binary_to_term" arity="1"/>
- <fsummary>Decodes an Erlang external term format binary.</fsummary>
+ <fsummary>Decode an Erlang external term format binary.</fsummary>
<desc>
<p>Returns an Erlang term that is the result of decoding
binary object <c><anno>Binary</anno></c>, which must be encoded
- according to the Erlang external term format.</p>
- <warning><p>When decoding binaries from untrusted sources,
- consider using <c>binary_to_term/2</c> to prevent Denial
- of Service attacks.</p></warning>
+ according to the <seealso marker="erts:erl_ext_dist">
+ Erlang external term format</seealso>.</p>
+ <pre>
+> <input>Bin = term_to_binary(hello).</input>
+&lt;&lt;131,100,0,5,104,101,108,108,111>>
+> <input>hello = binary_to_term(Bin).</input>
+hello
+</pre>
+ <warning>
+ <p>When decoding binaries from untrusted sources,
+ consider using <c>binary_to_term/2</c> to prevent Denial
+ of Service attacks.</p>
+ </warning>
<p>See also
- <seealso marker="#term_to_binary/1">term_to_binary/1</seealso>
- and
- <seealso marker="#binary_to_term/2">binary_to_term/2</seealso>.</p>
+ <seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso>
+ and <seealso marker="#binary_to_term/2">
+ <c>binary_to_term/2</c></seealso>.</p>
</desc>
</func>
<func>
<name name="binary_to_term" arity="2"/>
- <fsummary>Decodes an Erlang external term format binary.</fsummary>
+ <fsummary>Decode an Erlang external term format binary.</fsummary>
<desc>
<p>As <c>binary_to_term/1</c>, but takes options that affect decoding
- of the binary.</p>
+ of the binary.</p>
+ <p>Option:</p>
<taglist>
<tag><c>safe</c></tag>
<item>
<p>Use this option when receiving binaries from an untrusted
- source.</p>
+ source.</p>
<p>When enabled, it prevents decoding data that can be used to
- attack the Erlang system. In the event of receiving unsafe
- data, decoding fails with a <c>badarg</c> error.</p>
+ attack the Erlang system. In the event of receiving unsafe
+ data, decoding fails with a <c>badarg</c> error.</p>
<p>This prevents creation of new atoms directly,
- creation of new atoms indirectly (as they are embedded in
- certain structures, such as process identifiers,
- refs, and funs), and
- creation of new external function references.
- None of those resources are garbage collected, so unchecked
- creation of them can exhaust available memory.</p>
+ creation of new atoms indirectly (as they are embedded in
+ certain structures, such as process identifiers,
+ refs, and funs), and
+ creation of new external function references.
+ None of those resources are garbage collected, so unchecked
+ creation of them can exhaust available memory.</p>
</item>
</taglist>
<p>Failure: <c>badarg</c> if <c>safe</c> is specified and unsafe
- data is decoded.</p>
+ data is decoded.</p>
+ <pre>
+> <input>binary_to_term(&lt;&lt;131,100,0,5,104,101,108,108,111>>, [safe]).</input>
+** exception error: bad argument
+> <input>hello.</input>
+hello
+> <input>binary_to_term(&lt;&lt;131,100,0,5,104,101,108,108,111>>, [safe]).</input>
+hello
+</pre>
<p>See also
- <seealso marker="#term_to_binary/1">term_to_binary/1</seealso>,
- <seealso marker="#binary_to_term/1">binary_to_term/1</seealso>,
- and
- <seealso marker="#list_to_existing_atom/1">list_to_existing_atom/1</seealso>.</p>
+ <seealso marker="#term_to_binary/1"><c>term_to_binary/1</c></seealso>,
+ <seealso marker="#binary_to_term/1">
+ <c>binary_to_term/1</c></seealso>, and
+ <seealso marker="#list_to_existing_atom/1">
+ <c>list_to_existing_atom/1</c></seealso>.</p>
</desc>
</func>
<func>
<name name="bit_size" arity="1"/>
- <fsummary>Returns the size of a bitstring.</fsummary>
+ <fsummary>Return the size of a bitstring.</fsummary>
<desc>
<p>Returns an integer that is the size in bits of
<c><anno>Bitstring</anno></c>, for example:</p>
@@ -547,15 +595,26 @@
</func>
<func>
+ <name name="bitstring_to_list" arity="1"/>
+ <fsummary>Convert a bitstring to a list.</fsummary>
+ <desc>
+ <p>Returns a list of integers corresponding to the bytes of
+ <c><anno>Bitstring</anno></c>. If the number of bits in the binary
+ is not divisible by 8, the last element of the list is a bitstring
+ containing the remaining 1-7 bits.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="bump_reductions" arity="1"/>
- <fsummary>Increments the reduction counter.</fsummary>
+ <fsummary>Increment the reduction counter.</fsummary>
<desc>
<p>This implementation-dependent function increments
the reduction counter for the calling process. In the Beam
emulator, the reduction counter is normally incremented by
one for each function and BIF call. A context switch is
forced when the counter reaches the maximum number of
- reductions for a process (2000 reductions in OTP R12B).</p>
+ reductions for a process (2000 reductions in Erlang/OTP R12B).</p>
<warning>
<p>This BIF can be removed in a future version of the Beam
machine without prior warning. It is unlikely to be
@@ -566,13 +625,12 @@
<func>
<name name="byte_size" arity="1"/>
- <fsummary>Returns the size of a bitstring (or binary).</fsummary>
+ <fsummary>Return the size of a bitstring (or binary).</fsummary>
<desc>
<p>Returns an integer that is the number of bytes needed to
contain <c><anno>Bitstring</anno></c>. That is, if the number of bits
in <c><anno>Bitstring</anno></c> is not divisible by 8, the resulting
- number of bytes is rounded <em>up</em>.</p>
- <p>Examples:</p>
+ number of bytes is rounded <em>up</em>. Examples:</p>
<pre>
> <input>byte_size(&lt;&lt;433:16,3:3&gt;&gt;).</input>
3
@@ -583,134 +641,127 @@
</func>
<func>
- <name name="cancel_timer" arity="2"/>
- <fsummary>Cancels a timer.</fsummary>
+ <name name="cancel_timer" arity="1"/>
+ <fsummary>Cancel a timer.</fsummary>
<desc>
- <p>
- Cancels a timer that has been created by
- <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>,
- or <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>.
- <c><anno>TimerRef</anno></c> identifies the timer, and
- was returned by the BIF that created the timer.
- </p>
- <p>Available <c><anno>Option</anno></c>s:</p>
+ <p>Cancels a timer. The same as calling
+ <seealso marker="#cancel_timer/2">
+ <c>erlang:cancel_timer(TimerRef, [])</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="cancel_timer" arity="2"/>
+ <fsummary>Cancel a timer.</fsummary>
+ <desc>
+ <p>Cancels a timer that has been created by
+ <seealso marker="#start_timer/4">
+ <c>erlang:start_timer</c></seealso> or
+ <seealso marker="#send_after/4"><c>erlang:send_after</c></seealso>.
+ <c><anno>TimerRef</anno></c> identifies the timer, and
+ was returned by the BIF that created the timer.</p>
+ <p><c><anno>Option</anno></c>s:</p>
<taglist>
<tag><c>{async, Async}</c></tag>
<item>
- <p>
- Asynchronous request for cancellation. <c>Async</c>
- defaults to <c>false</c> which will cause the
- cancellation to be performed synchronously. When
- <c>Async</c> is set to <c>true</c>, the cancel
- operation is performed asynchronously. That is,
- <c>erlang:cancel_timer()</c> will send an asynchronous
- request for cancellation to the timer service that
- manages the timer, and then return <c>ok</c>.
- </p>
- </item>
+ <p>Asynchronous request for cancellation. <c>Async</c>
+ defaults to <c>false</c>, which causes the
+ cancellation to be performed synchronously. When
+ <c>Async</c> is set to <c>true</c>, the cancel
+ operation is performed asynchronously. That is,
+ <c>cancel_timer()</c> sends an asynchronous
+ request for cancellation to the timer service that
+ manages the timer, and then returns <c>ok</c>.</p>
+ </item>
<tag><c>{info, Info}</c></tag>
<item>
- <p>
- Request information about the <c><anno>Result</anno></c>
- of the cancellation. <c>Info</c> defaults to <c>true</c>
- which means the <c><anno>Result</anno></c> is
- given. When <c>Info</c> is set to <c>false</c>, no
- information about the result of the cancellation
- is given. When the operation is performed</p>
- <taglist>
- <tag>synchronously</tag>
- <item>
- <p>
- If <c>Info</c> is <c>true</c>, the <c>Result</c> is
- returned by <c>erlang:cancel_timer()</c>; otherwise,
- <c>ok</c> is returned.
- </p>
- </item>
- <tag>asynchronously</tag>
- <item>
- <p>
- If <c>Info</c> is <c>true</c>, a message on the form
- <c>{cancel_timer, <anno>TimerRef</anno>,
- <anno>Result</anno>}</c> is sent to the
- caller of <c>erlang:cancel_timer()</c> when the
- cancellation operation has been performed; otherwise,
- no message is sent.
- </p>
- </item>
- </taglist>
- </item>
- </taglist>
- <p>
- More <c><anno>Option</anno></c>s may be added in the future.
- </p>
- <p>If <c><anno>Result</anno></c> is an integer, it represents
- the time in milli-seconds left until the canceled timer would
- have expired.</p>
- <p>
- If <c><anno>Result</anno></c> is <c>false</c>, a
- timer corresponding to <c><anno>TimerRef</anno></c> could not
- be found. This can be either because the timer had expired,
- already had been canceled, or because <c><anno>TimerRef</anno></c>
- never corresponded to a timer. Even if the timer had expired,
- it does not tell you whether or not the timeout message has
- arrived at its destination yet.
- </p>
- <note>
- <p>
- The timer service that manages the timer may be co-located
- with another scheduler than the scheduler that the calling
- process is executing on. If this is the case, communication
- with the timer service takes much longer time than if it
- is located locally. If the calling process is in critical
- path, and can do other things while waiting for the result
- of this operation, or is not interested in the result of
- the operation, you want to use option <c>{async, true}</c>.
- If using option <c>{async, false}</c>, the calling
- process blocks until the operation has been performed.
- </p>
- </note>
- <p>See also
+ <p>Requests information about the <c><anno>Result</anno></c>
+ of the cancellation. <c>Info</c> defaults to <c>true</c>,
+ which means the <c><anno>Result</anno></c> is
+ given. When <c>Info</c> is set to <c>false</c>, no
+ information about the result of the cancellation
+ is given.</p>
+ <list type="bulleted">
+ <item>
+ <p>When <c>Async</c> is <c>false</c>:
+ if <c>Info</c> is <c>true</c>, the <c>Result</c> is
+ returned by <c>erlang:cancel_timer()</c>. otherwise
+ <c>ok</c> is returned.</p>
+ </item>
+ <item>
+ <p>When <c>Async</c> is <c>true</c>:
+ if <c>Info</c> is <c>true</c>, a message on the form
+ <c>{cancel_timer, <anno>TimerRef</anno>,
+ <anno>Result</anno>}</c> is sent to the
+ caller of <c>erlang:cancel_timer()</c> when the
+ cancellation operation has been performed, otherwise
+ no message is sent.</p>
+ </item>
+ </list>
+ </item>
+ </taglist>
+ <p>More <c><anno>Option</anno></c>s may be added in the future.</p>
+ <p>If <c><anno>Result</anno></c> is an integer, it represents
+ the time in milliseconds left until the canceled timer would
+ have expired.</p>
+ <p>If <c><anno>Result</anno></c> is <c>false</c>, a
+ timer corresponding to <c><anno>TimerRef</anno></c> could not
+ be found. This can be either because the timer had expired,
+ already had been canceled, or because <c><anno>TimerRef</anno></c>
+ never corresponded to a timer. Even if the timer had expired,
+ it does not tell you if the time-out message has
+ arrived at its destination yet.</p>
+ <note>
+ <p>The timer service that manages the timer can be co-located
+ with another scheduler than the scheduler that the calling
+ process is executing on. If so, communication
+ with the timer service takes much longer time than if it
+ is located locally. If the calling process is in critical
+ path, and can do other things while waiting for the result
+ of this operation, or is not interested in the result of
+ the operation, you want to use option <c>{async, true}</c>.
+ If using option <c>{async, false}</c>, the calling
+ process blocks until the operation has been performed.</p>
+ </note>
+ <p>See also
<seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>,
- <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>,
- and
- <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p>
- </desc>
- </func>
- <func>
- <name name="cancel_timer" arity="1"/>
- <fsummary>Cancels a timer.</fsummary>
- <desc>
- <p>Cancels a timer. The same as calling
- <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer(TimerRef,
- [])</c></seealso>.</p>
+ <seealso marker="#start_timer/4">
+ <c>erlang:start_timer/4</c></seealso>, and
+ <seealso marker="#read_timer/2">
+ <c>erlang:read_timer/2</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="check_old_code" arity="1"/>
- <fsummary>Checks if a module has old code.</fsummary>
+ <fsummary>Check if a module has old code.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Module</anno></c> has old code,
- otherwise <c>false</c>.</p>
- <p>See also <seealso marker="kernel:code">code(3)</seealso>.</p>
+ otherwise <c>false</c>.</p>
+ <p>See also <seealso marker="kernel:code">
+ <c>code(3)</c></seealso>.</p>
</desc>
</func>
<func>
<name name="check_process_code" arity="2"/>
- <fsummary>Checks if a process executes old code for a module.</fsummary>
+ <fsummary>Check if a process executes old code for a module.</fsummary>
<desc>
<p>The same as
- <seealso marker="#check_process_code/3"><c>erlang:check_process_code(<anno>Pid</anno>, <anno>Module</anno>, [])</c></seealso>.</p>
+ <seealso marker="#check_process_code/3">
+ <c>check_process_code(<anno>Pid</anno>, <anno>Module</anno>, [])</c>
+ </seealso>.</p>
</desc>
</func>
<func>
<name name="check_process_code" arity="3"/>
- <fsummary>Checks if a process executes old code for a module.</fsummary>
+ <fsummary>Check if a process executes old code for a module.</fsummary>
<desc>
- <p>Checks if the node local process identified by <c><anno>Pid</anno></c>
- executes old code for <c><anno>Module</anno></c>.</p>
- <p>The available <c><anno>Option</anno></c>s are as follows:</p>
+ <p>Checks if the node local process identified by
+ <c><anno>Pid</anno></c>
+ executes old code for <c><anno>Module</anno></c>.</p>
+ <p><c><anno>Option</anno></c>s:</p>
<taglist>
<tag><c>{allow_gc, boolean()}</c></tag>
<item>
@@ -718,30 +769,30 @@
the operation. If <c>{allow_gc, false}</c> is passed, and
a garbage collection is needed to determine the
result of the operation, the operation is aborted (see
- information on <c><anno>CheckResult</anno></c> in the following).
+ information on <c><anno>CheckResult</anno></c> below).
The default is to allow garbage collection, that is,
<c>{allow_gc, true}</c>.</p>
- </item>
+ </item>
<tag><c>{async, RequestId}</c></tag>
<item>
<p>The function <c>check_process_code/3</c> returns
the value <c>async</c> immediately after the request
has been sent. When the request has been processed, the
process that called this function is passed a
- message on the form
- <c>{check_process_code, <anno>RequestId</anno>, <anno>CheckResult</anno>}</c>.</p>
- </item>
+ message on the form <c>{check_process_code, <anno>RequestId</anno>,
+ <anno>CheckResult</anno>}</c>.</p>
+ </item>
</taglist>
<p>If <c><anno>Pid</anno></c> equals <c>self()</c>, and
- no <c>async</c> option has been passed, the operation
- is performed at once. Otherwise a request for
- the operation is sent to the process identified by
- <c><anno>Pid</anno></c>, and is handled when
- appropriate. If no <c>async</c> option has been passed,
- the caller blocks until <c><anno>CheckResult</anno></c>
- is available and can be returned.</p>
+ no <c>async</c> option has been passed, the operation
+ is performed at once. Otherwise a request for
+ the operation is sent to the process identified by
+ <c><anno>Pid</anno></c>, and is handled when
+ appropriate. If no <c>async</c> option has been passed,
+ the caller blocks until <c><anno>CheckResult</anno></c>
+ is available and can be returned.</p>
<p><c><anno>CheckResult</anno></c> informs about the result of
- the request as follows:</p>
+ the request as follows:</p>
<taglist>
<tag><c>true</c></tag>
<item>
@@ -751,54 +802,82 @@
code for this module, or the process has references
to old code for this module, or the process contains
funs that references old code for this module.</p>
- </item>
+ </item>
<tag><c>false</c></tag>
<item>
<p>The process identified by <c><anno>Pid</anno></c> does
not execute old code for <c><anno>Module</anno></c>.</p>
- </item>
+ </item>
<tag><c>aborted</c></tag>
<item>
<p>The operation was aborted, as the process needed to
be garbage collected to determine the operation result,
and the operation was requested
- by passing option <c>{allow_gc, false}</c>.</p></item>
+ by passing option <c>{allow_gc, false}</c>.</p>
+ </item>
</taglist>
- <p>See also <seealso marker="kernel:code">code(3)</seealso>.</p>
+ <note>
+ <p>
+ Up until ERTS version 8.*, the check process code operation
+ checks for all types of references to the old code. That is,
+ direct references (e.g. return addresses on the process
+ stack), indirect references (<c>fun</c>s in process
+ context), and references to literals in the code.
+ </p>
+ <p>
+ As of ERTS version 9.0, the check process code operation
+ only checks for direct references to the code. Indirect
+ references via <c>fun</c>s will be ignored. If such
+ <c>fun</c>s exist and are used after a purge of the old
+ code, an exception will be raised upon usage (same as
+ the case when the <c>fun</c> is received by the process
+ after the purge). Literals will be taken care of (copied)
+ at a later stage. This behavior can as of ERTS version
+ 8.1 be enabled when
+ <seealso marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP_Configuring">building OTP</seealso>,
+ and will automatically be enabled if dirty scheduler
+ support is enabled.
+ </p>
+ </note>
+ <p>See also <seealso marker="kernel:code">
+ <c>code(3)</c></seealso>.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
- <item>If <c><anno>Pid</anno></c> is not a node local process identifier.
- </item>
+ <item>If <c><anno>Pid</anno></c> is not a node local process
+ identifier.
+ </item>
<tag><c>badarg</c></tag>
<item>If <c><anno>Module</anno></c> is not an atom.
- </item>
+ </item>
<tag><c>badarg</c></tag>
<item>If <c><anno>OptionList</anno></c> is an invalid list of options.
- </item>
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="convert_time_unit" arity="3"/>
- <fsummary>Converts time unit of a time value.</fsummary>
+ <fsummary>Convert time unit of a time value.</fsummary>
<desc>
- <p>Converts the <c><anno>Time</anno></c> value of time unit
- <c><anno>FromUnit</anno></c> to the corresponding
- <c><anno>ConvertedTime</anno></c> value of time unit
- <c><anno>ToUnit</anno></c>. The result is rounded
- using the floor function.</p>
-
- <warning><p>You may lose accuracy and precision when converting
- between time units. In order to minimize such loss, collect all
- data at <c>native</c> time unit and do the conversion on the end
- result.</p></warning>
+ <p>Converts the <c><anno>Time</anno></c> value of time unit
+ <c><anno>FromUnit</anno></c> to the corresponding
+ <c><anno>ConvertedTime</anno></c> value of time unit
+ <c><anno>ToUnit</anno></c>. The result is rounded
+ using the floor function.</p>
+ <warning>
+ <p>You can lose accuracy and precision when converting
+ between time units. To minimize such loss, collect all
+ data at <c>native</c> time unit and do the conversion on the end
+ result.</p>
+ </warning>
</desc>
</func>
+
<func>
<name name="crc32" arity="1"/>
- <fsummary>Computes crc32 (IEEE 802.3) checksum.</fsummary>
+ <fsummary>Compute crc32 (IEEE 802.3) checksum.</fsummary>
<desc>
<p>Computes and returns the crc32 (IEEE 802.3 style) checksum
for <c><anno>Data</anno></c>.</p>
@@ -807,37 +886,37 @@
<func>
<name name="crc32" arity="2"/>
- <fsummary>Computes crc32 (IEEE 802.3) checksum.</fsummary>
+ <fsummary>Compute crc32 (IEEE 802.3) checksum.</fsummary>
<desc>
<p>Continues computing the crc32 checksum by combining
- the previous checksum, <c><anno>OldCrc</anno></c>, with the checksum of
- <c><anno>Data</anno></c>.</p>
+ the previous checksum, <c><anno>OldCrc</anno></c>, with the checksum
+ of <c><anno>Data</anno></c>.</p>
<p>The following code:</p>
<code>
- X = erlang:crc32(Data1),
- Y = erlang:crc32(X,Data2).</code>
+X = erlang:crc32(Data1),
+Y = erlang:crc32(X,Data2).</code>
<p>assigns the same value to <c>Y</c> as this:</p>
<code>
- Y = erlang:crc32([Data1,Data2]).</code>
+Y = erlang:crc32([Data1,Data2]).</code>
</desc>
</func>
<func>
<name name="crc32_combine" arity="3"/>
- <fsummary>Combines two crc32 (IEEE 802.3) checksums.</fsummary>
+ <fsummary>Combine two crc32 (IEEE 802.3) checksums.</fsummary>
<desc>
<p>Combines two previously computed crc32 checksums.
- This computation requires the size of the data object for
- the second checksum to be known.</p>
+ This computation requires the size of the data object for
+ the second checksum to be known.</p>
<p>The following code:</p>
<code>
- Y = erlang:crc32(Data1),
- Z = erlang:crc32(Y,Data2).</code>
+Y = erlang:crc32(Data1),
+Z = erlang:crc32(Y,Data2).</code>
<p>assigns the same value to <c>Z</c> as this:</p>
<code>
- X = erlang:crc32(Data1),
- Y = erlang:crc32(Data2),
- Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code>
+X = erlang:crc32(Data1),
+Y = erlang:crc32(Data2),
+Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code>
</desc>
</func>
@@ -847,8 +926,7 @@
<desc>
<p>Returns the current date as <c>{Year, Month, Day}</c>.</p>
<p>The time zone and Daylight Saving Time correction depend on
- the underlying OS.</p>
- <p>Example:</p>
+ the underlying OS. Example:</p>
<pre>
> <input>date().</input>
{1995,2,19}</pre>
@@ -857,28 +935,29 @@
<func>
<name name="decode_packet" arity="3"/>
- <fsummary>Extracts a protocol packet from a binary.</fsummary>
+ <fsummary>Extract a protocol packet from a binary.</fsummary>
<desc>
<p>Decodes the binary <c><anno>Bin</anno></c> according to the packet
- protocol specified by <c><anno>Type</anno></c>. Similar to the packet
- handling done by sockets with option {packet,<anno>Type</anno>}.</p>
+ protocol specified by <c><anno>Type</anno></c>. Similar to the packet
+ handling done by sockets with option
+ <c>{packet,<anno>Type</anno>}.</c></p>
<p>If an entire packet is contained in <c><anno>Bin</anno></c>, it is
- returned together with the remainder of the binary as
- <c>{ok,<anno>Packet</anno>,<anno>Rest</anno>}</c>.</p>
+ returned together with the remainder of the binary as
+ <c>{ok,<anno>Packet</anno>,<anno>Rest</anno>}</c>.</p>
<p>If <c><anno>Bin</anno></c> does not contain the entire packet,
- <c>{more,<anno>Length</anno>}</c> is returned.
- <c><anno>Length</anno></c> is either the
- expected <em>total size</em> of the packet, or <c>undefined</c>
- if the expected packet size is unknown. <c>decode_packet</c>
- can then be called again with more data added.</p>
+ <c>{more,<anno>Length</anno>}</c> is returned.
+ <c><anno>Length</anno></c> is either the
+ expected <em>total size</em> of the packet, or <c>undefined</c>
+ if the expected packet size is unknown. <c>decode_packet</c>
+ can then be called again with more data added.</p>
<p>If the packet does not conform to the protocol format,
- <c>{error,<anno>Reason</anno>}</c> is returned.</p>
- <p>The following <c>Type</c>s are valid:</p>
+ <c>{error,<anno>Reason</anno>}</c> is returned.</p>
+ <p><c>Type</c>s:</p>
<taglist>
<tag><c>raw | 0</c></tag>
<item>
<p>No packet handling is done. The entire binary is
- returned unless it is empty.</p>
+ returned unless it is empty.</p>
</item>
<tag><c>1 | 2 | 4</c></tag>
<item>
@@ -890,10 +969,10 @@
</item>
<tag><c>line</c></tag>
<item>
- <p>A packet is a line terminated by a delimiter byte,
- default is the latin1 newline character. The delimiter
- byte is included in the returned packet unless the line
- was truncated according to option <c>line_length</c>.</p>
+ <p>A packet is a line-terminated by a delimiter byte,
+ default is the latin-1 newline character. The delimiter
+ byte is included in the returned packet unless the line
+ was truncated according to option <c>line_length</c>.</p>
</item>
<tag><c>asn1 | cdr | sunrm | fcgi | tpkt</c></tag>
<item>
@@ -910,50 +989,51 @@
<tag><c>http | httph | http_bin | httph_bin</c></tag>
<item>
<p>The Hypertext Transfer Protocol. The packets
- are returned with the format according to
- <c><anno>HttpPacket</anno></c> described earlier.
- A packet is either a
- request, a response, a header, or an end of header
- mark. Invalid lines are returned as
- <c><anno>HttpError</anno></c>.</p>
+ are returned with the format according to
+ <c><anno>HttpPacket</anno></c> described earlier.
+ A packet is either a
+ request, a response, a header, or an end of header
+ mark. Invalid lines are returned as
+ <c><anno>HttpError</anno></c>.</p>
<p>Recognized request methods and header fields are returned
- as atoms. Others are returned as strings. Strings of
- unrecognized header fields are formatted with only
- capital letters first and after hyphen characters, for
- example, <c>"Sec-Websocket-Key"</c>.</p>
+ as atoms. Others are returned as strings. Strings of
+ unrecognized header fields are formatted with only
+ capital letters first and after hyphen characters, for
+ example, <c>"Sec-Websocket-Key"</c>.</p>
<p>The protocol type <c>http</c> is only to be used for
- the first line when an <c><anno>HttpRequest</anno></c> or an
- <c><anno>HttpResponse</anno></c> is expected.
- The following calls are to use <c>httph</c> to get
- <c><anno>HttpHeader</anno></c>s until
- <c>http_eoh</c> is returned, which marks the end of the
- headers and the beginning of any following message body.</p>
+ the first line when an <c><anno>HttpRequest</anno></c> or an
+ <c><anno>HttpResponse</anno></c> is expected.
+ The following calls are to use <c>httph</c> to get
+ <c><anno>HttpHeader</anno></c>s until
+ <c>http_eoh</c> is returned, which marks the end of the
+ headers and the beginning of any following message body.</p>
<p>The variants <c>http_bin</c> and <c>httph_bin</c> return
- strings (<c>HttpString</c>) as binaries instead of lists.</p>
+ strings (<c>HttpString</c>) as binaries instead of lists.</p>
</item>
</taglist>
- <p>The following options are available:</p>
- <taglist>
- <tag><c>{packet_size, integer() >= 0}</c></tag>
- <item><p>Sets the maximum allowed size of the packet body.
- If the packet header indicates that the length of the
- packet is longer than the maximum allowed length, the
- packet is considered invalid. Default is 0, which means
- no size limit.</p>
- </item>
- <tag><c>{line_length, integer() >= 0}</c></tag>
- <item><p>For packet type <c>line</c>, lines longer than
+ <p>Options:</p>
+ <taglist>
+ <tag><c>{packet_size, integer() >= 0}</c></tag>
+ <item><p>Sets the maximum allowed size of the packet body.
+ If the packet header indicates that the length of the
+ packet is longer than the maximum allowed length, the
+ packet is considered invalid. Defaults to 0, which means
+ no size limit.</p>
+ </item>
+ <tag><c>{line_length, integer() >= 0}</c></tag>
+ <item>
+ <p>For packet type <c>line</c>, lines longer than
the indicated length are truncated.</p>
- <p>Option <c>line_length</c> also applies to <c>http*</c>
- packet types as an alias for option <c>packet_size</c>
- if <c>packet_size</c> itself is not set. This use is
- only intended for backward compatibility.</p>
- </item>
- <tag><c>{line_delimiter, 0 =&lt; byte() =&lt; 255}</c></tag>
- <item><p>For packet type <c>line</c>, sets the delimiting byte.
- Default is the latin1 character <c>$\n</c>.</p>
- </item>
- </taglist>
+ <p>Option <c>line_length</c> also applies to <c>http*</c>
+ packet types as an alias for option <c>packet_size</c>
+ if <c>packet_size</c> itself is not set. This use is
+ only intended for backward compatibility.</p>
+ </item>
+ <tag><c>{line_delimiter, 0 =&lt; byte() =&lt; 255}</c></tag>
+ <item><p>For packet type <c>line</c>, sets the delimiting byte.
+ Default is the latin-1 character <c>$\n</c>.</p>
+ </item>
+ </taglist>
<p>Examples:</p>
<pre>
> <input>erlang:decode_packet(1,&lt;&lt;3,"abcd"&gt;&gt;,[]).</input>
@@ -965,7 +1045,7 @@
<func>
<name name="delete_element" arity="2"/>
- <fsummary>Deletes element at index in a tuple.</fsummary>
+ <fsummary>Delete element at index in a tuple.</fsummary>
<type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>)</type_desc>
<desc>
<p>Returns a new tuple with element at <c><anno>Index</anno></c>
@@ -978,16 +1058,16 @@
<func>
<name name="delete_module" arity="1"/>
- <fsummary>Makes the current code for a module old.</fsummary>
+ <fsummary>Make the current code for a module old.</fsummary>
<desc>
- <p>Makes the current code for <c><anno>Module</anno></c> become old code,
- and deletes all references for this module from the export table.
+ <p>Makes the current code for <c><anno>Module</anno></c> become old
+ code and deletes all references for this module from the export table.
Returns <c>undefined</c> if the module does not exist,
otherwise <c>true</c>.</p>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code">code(3)</seealso>) and is not
- to be used elsewhere.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
+ and is not to be used elsewhere.</p>
</warning>
<p>Failure: <c>badarg</c> if there already is an old version of
<c>Module</c>.</p>
@@ -996,54 +1076,56 @@
<func>
<name name="demonitor" arity="1"/>
- <fsummary>Stops monitoring.</fsummary>
+ <fsummary>Stop monitoring.</fsummary>
<desc>
<p>If <c><anno>MonitorRef</anno></c> is a reference that the
calling process obtained by calling
- <seealso marker="#monitor/2">monitor/2</seealso>,
+ <seealso marker="#monitor/2"><c>monitor/2</c></seealso>,
this monitoring is turned off. If the monitoring is already
turned off, nothing happens.</p>
<p>Once <c>demonitor(<anno>MonitorRef</anno>)</c> has returned, it is
guaranteed that no <c>{'DOWN',
<anno>MonitorRef</anno>, _, _, _}</c> message,
because of the monitor, will be placed in the caller message queue
- in the future. A <c>{'DOWN',
+ in the future. However, a <c>{'DOWN',
<anno>MonitorRef</anno>, _, _, _}</c> message
can have been placed in the caller message queue before
- the call, though. It is therefore usually advisable
+ the call. It is therefore usually advisable
to remove such a <c>'DOWN'</c> message from the message queue
after monitoring has been stopped.
- <seealso marker="#demonitor/2"><c>demonitor(<anno>MonitorRef</anno>, [flush])</c></seealso>
- can be used instead of
- <c>demonitor(<anno>MonitorRef</anno>)</c> if this cleanup is wanted.</p>
+ <seealso marker="#demonitor/2">
+ <c>demonitor(<anno>MonitorRef</anno>, [flush])</c></seealso>
+ can be used instead of <c>demonitor(<anno>MonitorRef</anno>)</c>
+ if this cleanup is wanted.</p>
<note>
- <p>Prior to OTP release R11B (ERTS version 5.5) <c>demonitor/1</c>
- behaved completely asynchronously, i.e., the monitor was active
+ <p>Before Erlang/OTP R11B (ERTS 5.5) <c>demonitor/1</c>
+ behaved completely asynchronously, that is, the monitor was active
until the "demonitor signal" reached the monitored entity. This
had one undesirable effect. You could never know when
you were guaranteed <em>not</em> to receive a <c>DOWN</c> message
- due to the monitor.</p>
- <p>Current behavior can be viewed as two combined operations:
+ because of the monitor.</p>
+ <p>The current behavior can be viewed as two combined operations:
asynchronously send a "demonitor signal" to the monitored entity
- and ignore any future results of the monitor. </p>
+ and ignore any future results of the monitor.</p>
</note>
<p>Failure: It is an error if <c><anno>MonitorRef</anno></c> refers to a
monitoring started by another process. Not all such cases are
cheap to check. If checking is cheap, the call fails with
- <c>badarg</c> for example, if <c><anno>MonitorRef</anno></c> is a
+ <c>badarg</c>, for example if <c><anno>MonitorRef</anno></c> is a
remote reference.</p>
</desc>
</func>
<func>
<name name="demonitor" arity="2"/>
- <fsummary>Stops monitoring.</fsummary>
+ <fsummary>Stop monitoring.</fsummary>
<desc>
<p>The returned value is <c>true</c> unless <c>info</c> is part
of <c><anno>OptionList</anno></c>.</p>
<p><c>demonitor(<anno>MonitorRef</anno>, [])</c> is equivalent to
- <seealso marker="#demonitor/1"><c>demonitor(<anno>MonitorRef</anno>)</c></seealso>.</p>
- <p>The available <c><anno>Option</anno></c>s are as follows:</p>
+ <seealso marker="#demonitor/1">
+ <c>demonitor(<anno>MonitorRef</anno>)</c></seealso>.</p>
+ <p><c><anno>Option</anno></c>s:</p>
<taglist>
<tag><c>flush</c></tag>
<item>
@@ -1054,28 +1136,28 @@
<p>Calling <c>demonitor(<anno>MonitorRef</anno>, [flush])</c>
is equivalent to the following, but more efficient:</p>
<code type="none">
- demonitor(MonitorRef),
- receive
- {_, MonitorRef, _, _, _} ->
- true
- after 0 ->
- true
- end</code>
+demonitor(MonitorRef),
+receive
+ {_, MonitorRef, _, _, _} ->
+ true
+after 0 ->
+ true
+end</code>
</item>
<tag><c>info</c></tag>
<item>
<p>The returned value is one of the following:</p>
<taglist>
<tag><c>true</c></tag>
- <item>The monitor was found and removed. In this case,
+ <item><p>The monitor was found and removed. In this case,
no <c>'DOWN'</c> message corresponding to this
- monitor has been delivered and will not be delivered.
+ monitor has been delivered and will not be delivered.</p>
</item>
<tag><c>false</c></tag>
- <item>The monitor was not found and could not be removed.
+ <item><p>The monitor was not found and could not be removed.
This probably because someone already has placed a
<c>'DOWN'</c> message corresponding to this monitor
- in the caller message queue.
+ in the caller message queue.</p>
</item>
</taglist>
<p>If option <c>info</c> is combined with option <c>flush</c>,
@@ -1090,21 +1172,21 @@
<taglist>
<tag><c>badarg</c></tag>
<item>If <c><anno>OptionList</anno></c> is not a list.
- </item>
+ </item>
<tag><c>badarg</c></tag>
<item>If <c><anno>Option</anno></c> is an invalid option.
- </item>
+ </item>
<tag><c>badarg</c></tag>
<item>The same failure as for
- <seealso marker="#demonitor/1">demonitor/1</seealso>.
- </item>
+ <seealso marker="#demonitor/1"><c>demonitor/1</c></seealso>.
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="disconnect_node" arity="1"/>
- <fsummary>Forces the disconnection of a node.</fsummary>
+ <fsummary>Force the disconnection of a node.</fsummary>
<desc>
<p>Forces the disconnection of a node. This appears to
the node <c><anno>Node</anno></c> as if the local node has crashed.
@@ -1118,7 +1200,7 @@
<func>
<name name="display" arity="1"/>
- <fsummary>Prints a term on standard output.</fsummary>
+ <fsummary>Print a term on standard output.</fsummary>
<desc>
<p>Prints a text representation of <c><anno>Term</anno></c> on the
standard output.</p>
@@ -1130,7 +1212,7 @@
<func>
<name name="element" arity="2"/>
- <fsummary>Returns the Nth element of a tuple.</fsummary>
+ <fsummary>Return the Nth element of a tuple.</fsummary>
<type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc>
<desc>
<p>Returns the <c><anno>N</anno></c>th element (numbering from 1) of
@@ -1144,7 +1226,7 @@ b</pre>
<func>
<name name="erase" arity="0"/>
- <fsummary>Returns and deletes the process dictionary.</fsummary>
+ <fsummary>Return and delete the process dictionary.</fsummary>
<desc>
<p>Returns the process dictionary and deletes it, for
example:</p>
@@ -1158,13 +1240,13 @@ b</pre>
<func>
<name name="erase" arity="1"/>
- <fsummary>Returns and deletes a value from the process dictionary.</fsummary>
+ <fsummary>Return and delete a value from the process dictionary.
+ </fsummary>
<desc>
<p>Returns the value <c><anno>Val</anno></c> associated with
<c><anno>Key</anno></c> and deletes it from the process dictionary.
Returns <c>undefined</c> if no value is associated with
- <c><anno>Key</anno></c>.</p>
- <p>Example:</p>
+ <c><anno>Key</anno></c>. Example:</p>
<pre>
> <input>put(key1, {merry, lambs, are, playing}),</input>
<input>X = erase(key1),</input>
@@ -1175,16 +1257,15 @@ b</pre>
<func>
<name name="error" arity="1"/>
- <fsummary>Stops execution with a given reason.</fsummary>
+ <fsummary>Stop execution with a specified reason.</fsummary>
<desc>
<p>Stops the execution of the calling process with the reason
<c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c>
is any term. The exit reason is
<c>{<anno>Reason</anno>, Where}</c>, where <c>Where</c>
is a list of the functions most recently called (the current
- function first). Since evaluating this function causes
- the process to terminate, it has no return value.</p>
- <p>Example:</p>
+ function first). As evaluating this function causes
+ the process to terminate, it has no return value. Example:</p>
<pre>
> <input>catch error(foobar).</input>
{'EXIT',{foobar,[{erl_eval,do_apply,5},
@@ -1197,7 +1278,7 @@ b</pre>
<func>
<name name="error" arity="2"/>
- <fsummary>Stops execution with a given reason.</fsummary>
+ <fsummary>Stop execution with a specified reason.</fsummary>
<desc>
<p>Stops the execution of the calling process with the reason
<c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c>
@@ -1207,21 +1288,20 @@ b</pre>
function first). <c><anno>Args</anno></c> is expected to be the
list of arguments for the current function; in Beam it is used
to provide the arguments for the current function in
- the term <c>Where</c>. Since evaluating this function causes
+ the term <c>Where</c>. As evaluating this function causes
the process to terminate, it has no return value.</p>
</desc>
</func>
<func>
<name name="exit" arity="1"/>
- <fsummary>Stops execution with a given reason.</fsummary>
+ <fsummary>Stop execution with a specified reason.</fsummary>
<desc>
<p>Stops the execution of the calling process with exit reason
<c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c>
- is any term. Since
+ is any term. As
evaluating this function causes the process to terminate, it
- has no return value.</p>
- <p>Example:</p>
+ has no return value. Example:</p>
<pre>
> <input>exit(foobar).</input>
** exception exit: foobar
@@ -1232,43 +1312,44 @@ b</pre>
<func>
<name name="exit" arity="2"/>
- <fsummary>Sends an exit signal to a process or a port.</fsummary>
+ <fsummary>Send an exit signal to a process or a port.</fsummary>
<desc>
<p>Sends an exit signal with exit reason <c><anno>Reason</anno></c> to
the process or port identified by <c><anno>Pid</anno></c>.</p>
<p>The following behavior applies if <c><anno>Reason</anno></c>
is any term, except <c>normal</c> or <c>kill</c>:</p>
- <list type="bulleted">
- <item>If <c><anno>Pid</anno></c> is not trapping exits,
- <c><anno>Pid</anno></c>
- itself exits with exit reason <c><anno>Reason</anno></c>.
- </item>
- <item>If <c><anno>Pid</anno></c> is trapping exits, the exit
- signal is transformed into a message
- <c>{'EXIT', From, <anno>Reason</anno>}</c>
- and delivered to the message queue of <c><anno>Pid</anno></c>.
- </item>
- <item><c>From</c> is the process identifier of the process
- that sent the exit signal. See also
- <seealso marker="#process_flag/2">process_flag/2</seealso>.
- </item>
- </list>
- <p>If <c><anno>Reason</anno></c> is the atom <c>normal</c>,
+ <list type="bulleted">
+ <item><p>If <c><anno>Pid</anno></c> is not trapping exits,
<c><anno>Pid</anno></c>
- does not exit. If it is trapping exits, the exit signal is
- transformed into a message <c>{'EXIT', From, normal}</c>
- and delivered to its message queue.</p>
- <p>If <c><anno>Reason</anno></c> is the atom <c>kill</c>,
- that is, if <c>exit(<anno>Pid</anno>, kill)</c> is called,
- an untrappable exit signal is sent to <c><anno>Pid</anno></c>,
- which unconditionally exits with exit reason <c>killed</c>.
- </p>
+ itself exits with exit reason <c><anno>Reason</anno></c>.</p>
+ </item>
+ <item><p>If <c><anno>Pid</anno></c> is trapping exits, the exit
+ signal is transformed into a message
+ <c>{'EXIT', From, <anno>Reason</anno>}</c>
+ and delivered to the message queue of <c><anno>Pid</anno></c>.</p>
+ </item>
+ <item><p><c>From</c> is the process identifier of the process
+ that sent the exit signal. See also
+ <seealso marker="#process_flag/2">
+ <c>process_flag/2</c></seealso>.</p>
+ </item>
+ </list>
+ <p>If <c><anno>Reason</anno></c> is the atom <c>normal</c>,
+ <c><anno>Pid</anno></c>
+ does not exit. If it is trapping exits, the exit signal is
+ transformed into a message <c>{'EXIT', From, normal}</c>
+ and delivered to its message queue.</p>
+ <p>If <c><anno>Reason</anno></c> is the atom <c>kill</c>,
+ that is, if <c>exit(<anno>Pid</anno>, kill)</c> is called,
+ an untrappable exit signal is sent to <c><anno>Pid</anno></c>,
+ which unconditionally exits with exit reason <c>killed</c>.</p>
</desc>
</func>
<func>
<name name="external_size" arity="1"/>
- <fsummary>Calculates the maximum size for a term encoded in the Erlang external term format.</fsummary>
+ <fsummary>Calculate the maximum size for a term encoded in the Erlang
+ external term format.</fsummary>
<desc>
<p>Calculates, without doing the encoding, the maximum byte size for
a term encoded in the Erlang external term format. The following
@@ -1279,13 +1360,15 @@ b</pre>
> <input>true = Size1 =&lt; Size2.</input>
true</pre>
<p>This is equivalent to a call to:</p>
-<code>erlang:external_size(<anno>Term</anno>, [])</code>
+<code>
+erlang:external_size(<anno>Term</anno>, [])</code>
</desc>
</func>
<func>
<name name="external_size" arity="2"/>
- <fsummary>Calculates the maximum size for a term encoded in the Erlang external term format.</fsummary>
+ <fsummary>Calculate the maximum size for a term encoded in the Erlang
+ external term format.</fsummary>
<desc>
<p>Calculates, without doing the encoding, the maximum byte size for
a term encoded in the Erlang external term format. The following
@@ -1297,13 +1380,14 @@ true</pre>
true</pre>
<p>Option <c>{minor_version, <anno>Version</anno>}</c> specifies how
floats are encoded. For a detailed description, see
- <seealso marker="#term_to_binary/2">term_to_binary/2</seealso>.</p>
+ <seealso marker="#term_to_binary/2">
+ <c>term_to_binary/2</c></seealso>.</p>
</desc>
</func>
<func>
<name name="float" arity="1"/>
- <fsummary>Converts a number to a float.</fsummary>
+ <fsummary>Convert a number to a float.</fsummary>
<desc>
<p>Returns a float by converting <c><anno>Number</anno></c> to a float,
for example:</p>
@@ -1314,7 +1398,8 @@ true</pre>
<note>
<p>If used on the top level in a guard, it tests whether the
argument is a floating point number; for clarity, use
- <seealso marker="#is_float/1">is_float/1</seealso> instead.</p>
+ <seealso marker="#is_float/1"><c>is_float/1</c></seealso>
+ instead.</p>
<p>When <c>float/1</c> is used in an expression in a guard,
such as '<c>float(A) == 4.0</c>', it converts a number as
described earlier.</p>
@@ -1333,13 +1418,14 @@ true</pre>
<func>
<name name="float_to_binary" arity="2"/>
- <fsummary>Text representation of a float formatted using given options.</fsummary>
+ <fsummary>Text representation of a float formatted using specified
+ options.</fsummary>
<desc>
<p>Returns a binary corresponding to the text
representation of <c><anno>Float</anno></c> using fixed decimal
point formatting. <c><anno>Options</anno></c> behaves in the same
- way as <seealso marker="#float_to_list/2">float_to_list/2</seealso>.</p>
- <p>Examples:</p>
+ way as <seealso marker="#float_to_list/2">
+ <c>float_to_list/2</c></seealso>. Examples:</p>
<pre>
> <input>float_to_binary(7.12, [{decimals, 4}]).</input>
&lt;&lt;"7.1200">>
@@ -1359,27 +1445,29 @@ true</pre>
<func>
<name name="float_to_list" arity="2"/>
- <fsummary>Text representation of a float formatted using given options.</fsummary>
+ <fsummary>Text representation of a float formatted using specified
+ options.</fsummary>
<desc>
<p>Returns a string corresponding to the text representation
- of <c>Float</c> using fixed decimal point formatting. The
- options are as follows:</p>
+ of <c>Float</c> using fixed decimal point formatting.</p>
+ <p>Available options:</p>
<list type="bulleted">
- <item>If option <c>decimals</c> is specified, the returned value
+ <item><p>If option <c>decimals</c> is specified, the returned value
contains at most <c>Decimals</c> number of digits past the
- decimal point. If the number does not fit in the internal
- static buffer of 256 bytes, the function throws <c>badarg</c>.
+ decimal point. If the number does not fit in the internal
+ static buffer of 256 bytes, the function throws <c>badarg</c>.</p>
</item>
- <item>If option <c>compact</c> is provided, the trailing zeros
+ <item><p>If option <c>compact</c> is specified, the trailing zeros
at the end of the list are truncated. This option is only
- meaningful together with option <c>decimals</c>.
+ meaningful together with option <c>decimals</c>.</p>
</item>
- <item>If option <c>scientific</c> is provided, the float is
+ <item><p>If option <c>scientific</c> is specified, the float is
formatted using scientific notation with <c>Decimals</c>
- digits of precision.
+ digits of precision.</p>
</item>
- <item>If <c>Options</c> is <c>[]</c>, the function behaves as
- <seealso marker="#float_to_list/1">float_to_list/1</seealso>.
+ <item><p>If <c>Options</c> is <c>[]</c>, the function behaves as
+ <seealso marker="#float_to_list/1">
+ <c>float_to_list/1</c></seealso>.</p>
</item>
</list>
<p>Examples:</p>
@@ -1406,16 +1494,16 @@ true</pre>
</warning>
<p>Two types of funs have slightly different semantics:</p>
<list type="bulleted">
- <item>A fun created by <c>fun M:F/A</c> is called an
+ <item><p>A fun created by <c>fun M:F/A</c> is called an
<em>external</em> fun. Calling it will always call the
function <c>F</c> with arity <c>A</c> in the latest code for
module <c>M</c>. Notice that module <c>M</c> does not even
- need to be loaded when the fun <c>fun M:F/A</c> is created.
+ need to be loaded when the fun <c>fun M:F/A</c> is created.</p>
</item>
- <item>All other funs are called <em>local</em>. When a local fun
+ <item><p>All other funs are called <em>local</em>. When a local fun
is called, the same version of the code that created the fun
is called (even if a newer version of the module has been
- loaded).
+ loaded).</p>
</item>
</list>
<p>The following elements are always present in the list
@@ -1479,14 +1567,14 @@ true</pre>
<tag><c>{new_uniq, Uniq}</c></tag>
<item>
<p><c>Uniq</c> (a binary) is a unique value for this fun. It
- is calculated from the compiled code for the entire module.</p>
+ is calculated from the compiled code for the entire module.</p>
</item>
<tag><c>{uniq, Uniq}</c></tag>
<item>
<p><c>Uniq</c> (an integer) is a unique value for this fun.
- As from OTP R15, this integer is calculated from the
- compiled code for the entire module. Before OTP R15, this
- integer was based on only the body of the fun.</p>
+ As from Erlang/OTP R15, this integer is calculated from the
+ compiled code for the entire module. Before Erlang/OTP R15, this
+ integer was based on only the body of the fun.</p>
</item>
</taglist>
</desc>
@@ -1508,7 +1596,7 @@ true</pre>
<c>uniq</c>, and <c>pid</c>. For an external fun, the value
of any of these items is always the atom <c>undefined</c>.</p>
<p>See
- <seealso marker="#fun_info/1">erlang:fun_info/1</seealso>.</p>
+ <seealso marker="#fun_info/1"><c>erlang:fun_info/1</c></seealso>.</p>
</desc>
</func>
@@ -1523,20 +1611,24 @@ true</pre>
<func>
<name name="function_exported" arity="3"/>
- <fsummary>Checks if a function is exported and loaded.</fsummary>
+ <fsummary>Check if a function is exported and loaded.</fsummary>
<desc>
- <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is loaded
- and contains an exported function <c><anno>Function</anno>/<anno>Arity</anno></c>,
- or if there is a BIF (a built-in function implemented in C)
- with the given name, otherwise returns <c>false</c>.</p>
- <note><p>This function used to return false for built-in
- functions before the 18.0 release.</p></note>
+ <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is
+ loaded and contains an exported function
+ <c><anno>Function</anno>/<anno>Arity</anno></c>,
+ or if there is a BIF (a built-in function implemented in C)
+ with the specified name, otherwise returns <c>false</c>.</p>
+ <note>
+ <p>This function used to return <c>false</c> for BIFs
+ before Erlang/OTP 18.0.</p>
+ </note>
</desc>
</func>
<func>
<name name="garbage_collect" arity="0"/>
- <fsummary>Forces an immediate garbage collection of the calling process.</fsummary>
+ <fsummary>Force an immediate garbage collection of the calling process.
+ </fsummary>
<desc>
<p>Forces an immediate garbage collection of the
executing process. The function is not to be used unless
@@ -1551,73 +1643,76 @@ true</pre>
<func>
<name name="garbage_collect" arity="1"/>
- <fsummary>Garbage collects a process.</fsummary>
+ <fsummary>Garbage collect a process.</fsummary>
<desc>
<p>The same as
- <seealso marker="#garbage_collect/2"><c>garbage_collect(<anno>Pid</anno>, [])</c></seealso>.</p>
+ <seealso marker="#garbage_collect/2">
+ <c>garbage_collect(<anno>Pid</anno>, [])</c></seealso>.</p>
</desc>
</func>
<func>
<name name="garbage_collect" arity="2"/>
- <fsummary>Garbage collects a process.</fsummary>
+ <fsummary>Garbage collect a process.</fsummary>
<desc>
<p>Garbage collects the node local process identified by
- <c><anno>Pid</anno></c>.</p>
- <p>The available <c><anno>Option</anno></c>s are as follows:</p>
+ <c><anno>Pid</anno></c>.</p>
+ <p><c><anno>Option</anno></c>:</p>
<taglist>
<tag><c>{async, RequestId}</c></tag>
<item>The function <c>garbage_collect/2</c> returns
- the value <c>async</c> immediately after the request
- has been sent. When the request has been processed, the
- process that called this function is passed a message on
+ the value <c>async</c> immediately after the request
+ has been sent. When the request has been processed, the
+ process that called this function is passed a message on
the form <c>{garbage_collect,
<anno>RequestId</anno>, <anno>GCResult</anno>}</c>.
</item>
</taglist>
- <p>If <c><anno>Pid</anno></c> equals <c>self()</c>, and
- no <c>async</c> option has been passed, the garbage
- collection is performed at once, that is, the same as calling
- <seealso marker="#garbage_collect/0">garbage_collect/0</seealso>.
- Otherwise a request for garbage collection
- is sent to the process identified by <c><anno>Pid</anno></c>,
- and will be handled when appropriate. If no <c>async</c>
- option has been passed, the caller blocks until
- <c><anno>GCResult</anno></c> is available and can be returned.</p>
- <p><c><anno>GCResult</anno></c> informs about the result of
- the garbage collection request as follows:</p>
+ <p>If <c><anno>Pid</anno></c> equals <c>self()</c>, and
+ no <c>async</c> option has been passed, the garbage
+ collection is performed at once, that is, the same as calling
+ <seealso marker="#garbage_collect/0">
+ <c>garbage_collect/0</c></seealso>.
+ Otherwise a request for garbage collection
+ is sent to the process identified by <c><anno>Pid</anno></c>,
+ and will be handled when appropriate. If no <c>async</c>
+ option has been passed, the caller blocks until
+ <c><anno>GCResult</anno></c> is available and can be returned.</p>
+ <p><c><anno>GCResult</anno></c> informs about the result of
+ the garbage collection request as follows:</p>
<taglist>
<tag><c>true</c></tag>
<item>
- The process identified by <c><anno>Pid</anno></c> has
- been garbage collected.
- </item>
+ The process identified by <c><anno>Pid</anno></c> has
+ been garbage collected.
+ </item>
<tag><c>false</c></tag>
<item>
- No garbage collection was performed, as
- the process identified by <c><anno>Pid</anno></c>
- terminated before the request could be satisfied.
- </item>
+ No garbage collection was performed, as
+ the process identified by <c><anno>Pid</anno></c>
+ terminated before the request could be satisfied.
+ </item>
</taglist>
<p>Notice that the same caveats apply as for
- <seealso marker="#garbage_collect/0">garbage_collect/0</seealso>.</p>
+ <seealso marker="#garbage_collect/0">
+ <c>garbage_collect/0</c></seealso>.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Pid</anno></c> is not a node local process identifier.
- </item>
+ If <c><anno>Pid</anno></c> is not a node local process identifier.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>OptionList</anno></c> is an invalid list of options.
- </item>
+ If <c><anno>OptionList</anno></c> is an invalid list of options.
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="get" arity="0"/>
- <fsummary>Returns the process dictionary.</fsummary>
+ <fsummary>Return the process dictionary.</fsummary>
<desc>
<p>Returns the process dictionary as a list of
<c>{<anno>Key</anno>, <anno>Val</anno>}</c> tuples, for example:</p>
@@ -1632,12 +1727,11 @@ true</pre>
<func>
<name name="get" arity="1"/>
- <fsummary>Returns a value from the process dictionary.</fsummary>
+ <fsummary>Return a value from the process dictionary.</fsummary>
<desc>
- <p>Returns the value <c><anno>Val</anno></c> associated with <c><anno>Key</anno></c> in
- the process dictionary, or <c>undefined</c> if <c><anno>Key</anno></c>
- does not exist.</p>
- <p>Example:</p>
+ <p>Returns the value <c><anno>Val</anno></c> associated with
+ <c><anno>Key</anno></c> in the process dictionary, or <c>undefined</c>
+ if <c><anno>Key</anno></c> does not exist. Example:</p>
<pre>
> <input>put(key1, merry),</input>
<input>put(key2, lambs),</input>
@@ -1649,7 +1743,7 @@ true</pre>
<func>
<name name="get_cookie" arity="0"/>
- <fsummary>Gets the magic cookie of the local node.</fsummary>
+ <fsummary>Get the magic cookie of the local node.</fsummary>
<desc>
<p>Returns the magic cookie of the local node if the node is
alive, otherwise the atom <c>nocookie</c>.</p>
@@ -1658,9 +1752,11 @@ true</pre>
<func>
<name name="get_keys" arity="0"/>
- <fsummary>Return a list of all keys from the process dictionary</fsummary>
+ <fsummary>Return a list of all keys from the process dictionary.
+ </fsummary>
<desc>
- <p>Returns a list of keys all keys present in the process dictionary.</p>
+ <p>Returns a list of all keys present in the process dictionary,
+ for example:</p>
<pre>
> <input>put(dog, {animal,1}),</input>
<input>put(cow, {animal,2}),</input>
@@ -1669,9 +1765,10 @@ true</pre>
[dog,cow,lamb]</pre>
</desc>
</func>
+
<func>
<name name="get_keys" arity="1"/>
- <fsummary>Returns a list of keys from the process dictionary.</fsummary>
+ <fsummary>Return a list of keys from the process dictionary.</fsummary>
<desc>
<p>Returns a list of keys that are associated with the value
<c><anno>Val</anno></c> in the process dictionary, for example:</p>
@@ -1689,48 +1786,49 @@ true</pre>
<func>
<name name="get_stacktrace" arity="0"/>
- <fsummary>Gets the call stack back-trace of the last exception.</fsummary>
+ <fsummary>Get the call stack back-trace of the last exception.</fsummary>
<type name="stack_item"/>
<desc>
<p>Gets the call stack back-trace (<em>stacktrace</em>) of the
last exception in the calling process as a list of
- <c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c> tuples.
- Field <c><anno>Arity</anno></c> in the first tuple can be the
+ <c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c>
+ tuples. Field <c><anno>Arity</anno></c> in the first tuple can be the
argument list of that function call instead of an arity integer,
depending on the exception.</p>
<p>If there has not been any exceptions in a process, the
stacktrace is <c>[]</c>. After a code change for the process,
the stacktrace can also be reset to <c>[]</c>.</p>
- <p>The stacktrace is the same data as the <c>catch</c> operator
+ <p>The stacktrace is the same data as operator <c>catch</c>
returns, for example:</p>
- <p><c>{'EXIT',{badarg,Stacktrace}} = catch abs(x)</c></p>
- <p><c><anno>Location</anno></c> is a (possibly empty) list
+ <pre>
+{'EXIT',{badarg,Stacktrace}} = catch abs(x)</pre>
+ <p><c><anno>Location</anno></c> is a (possibly empty) list
of two-tuples that
- can indicate the location in the source code of the function.
- The first element is an atom describing the type of
- information in the second element. The following
- items can occur:</p>
- <taglist>
- <tag><c>file</c></tag>
- <item>The second element of the tuple is a string (list of
- characters) representing the file name of the source file
- of the function.
- </item>
- <tag><c>line</c></tag>
- <item>The second element of the tuple is the line number
- (an integer greater than zero) in the source file
- where the exception occurred or the function was called.
- </item>
- </taglist>
+ can indicate the location in the source code of the function.
+ The first element is an atom describing the type of
+ information in the second element. The following
+ items can occur:</p>
+ <taglist>
+ <tag><c>file</c></tag>
+ <item>The second element of the tuple is a string (list of
+ characters) representing the filename of the source file
+ of the function.
+ </item>
+ <tag><c>line</c></tag>
+ <item>The second element of the tuple is the line number
+ (an integer &gt; 0) in the source file
+ where the exception occurred or the function was called.
+ </item>
+ </taglist>
<p>See also
- <seealso marker="#error/1">erlang:error/1</seealso> and
- <seealso marker="#error/2">erlang:error/2</seealso>.</p>
+ <seealso marker="#error/1"><c>error/1</c></seealso> and
+ <seealso marker="#error/2"><c>error/2</c></seealso>.</p>
</desc>
</func>
<func>
<name name="group_leader" arity="0"/>
- <fsummary>Gets the group leader for the calling process.</fsummary>
+ <fsummary>Get the group leader for the calling process.</fsummary>
<desc>
<p>Returns the process identifier of the group leader for the
process evaluating the function.</p>
@@ -1738,14 +1836,14 @@ true</pre>
groups have a <em>group leader</em>. All I/O from the group
is channeled to the group leader. When a new process is
spawned, it gets the same group leader as the spawning
- process. Initially, at system start-up, <c>init</c> is both
+ process. Initially, at system startup, <c>init</c> is both
its own group leader and the group leader of all processes.</p>
</desc>
</func>
<func>
<name name="group_leader" arity="2"/>
- <fsummary>Sets the group leader for a process.</fsummary>
+ <fsummary>Set the group leader for a process.</fsummary>
<desc>
<p>Sets the group leader of <c><anno>Pid</anno></c>
to <c><anno>GroupLeader</anno></c>.
@@ -1753,72 +1851,72 @@ true</pre>
certain shell is to have another group leader than
<c>init</c>.</p>
<p>See also
- <seealso marker="#group_leader/0">group_leader/0</seealso>.</p>
+ <seealso marker="#group_leader/0"><c>group_leader/0</c></seealso>.</p>
</desc>
</func>
<func>
<name name="halt" arity="0"/>
- <fsummary>Halts the Erlang runtime system and indicates normal exit to the calling environment.</fsummary>
+ <fsummary>Halt the Erlang runtime system and indicate normal exit to
+ the calling environment.</fsummary>
<desc>
- <p>The same as
- <seealso marker="#halt/2"><c>halt(0, [])</c></seealso>.</p>
- <p>Example:</p>
+ <p>The same as
+ <seealso marker="#halt/2"><c>halt(0, [])</c></seealso>. Example:</p>
<pre>
> <input>halt().</input>
-os_prompt% </pre>
+os_prompt%</pre>
</desc>
</func>
<func>
<name name="halt" arity="1"/>
- <fsummary>Halts the Erlang runtime system.</fsummary>
+ <fsummary>Halt the Erlang runtime system.</fsummary>
<desc>
- <p>The same as
- <seealso marker="#halt/2"><c>halt(<anno>Status</anno>, [])</c></seealso>.</p>
- <p>Example:</p>
+ <p>The same as <seealso marker="#halt/2">
+ <c>halt(<anno>Status</anno>, [])</c></seealso>. Example:</p>
<pre>
> <input>halt(17).</input>
os_prompt% <input>echo $?</input>
17
-os_prompt% </pre>
+os_prompt%</pre>
</desc>
</func>
<func>
<name name="halt" arity="2"/>
- <fsummary>Halts the Erlang runtime system.</fsummary>
+ <fsummary>Halt the Erlang runtime system.</fsummary>
<desc>
<p><c><anno>Status</anno></c> must be a non-negative integer, a string,
- or the atom <c>abort</c>.
- Halts the Erlang runtime system. Has no return value.
- Depending on <c><anno>Status</anno></c>, the following occurs:</p>
- <taglist>
- <tag>integer()</tag>
- <item>The runtime system exits with integer value
- <c><anno>Status</anno></c>
- as status code to the calling environment (OS).
- </item>
- <tag>string()</tag>
- <item>An Erlang crash dump is produced with <c><anno>Status</anno></c>
- as slogan. Then the runtime system exits with status code <c>1</c>.
- Note that only code points in the range 0-255 may be used
- and the string will be truncated if longer than 200 characters.
- </item>
- <tag><c>abort</c></tag>
- <item>
- The runtime system aborts producing a core dump, if that is
- enabled in the OS.
- </item>
- </taglist>
- <note><p>On many platforms, the OS supports only status
- codes 0-255. A too large status code will be truncated by clearing
- the high bits.</p></note>
- <p>For integer <c><anno>Status</anno></c>, the Erlang runtime system
+ or the atom <c>abort</c>.
+ Halts the Erlang runtime system. Has no return value.
+ Depending on <c><anno>Status</anno></c>, the following occurs:</p>
+ <taglist>
+ <tag>integer()</tag>
+ <item>The runtime system exits with integer value
+ <c><anno>Status</anno></c>
+ as status code to the calling environment (OS).
+ </item>
+ <tag>string()</tag>
+ <item>An Erlang crash dump is produced with <c><anno>Status</anno></c>
+ as slogan. Then the runtime system exits with status code <c>1</c>.
+ Note that only code points in the range 0-255 may be used
+ and the string will be truncated if longer than 200 characters.
+ </item>
+ <tag><c>abort</c></tag>
+ <item>The runtime system aborts producing a core dump, if that is
+ enabled in the OS.
+ </item>
+ </taglist>
+ <note>
+ <p>On many platforms, the OS supports only status
+ codes 0-255. A too large status code is truncated by clearing
+ the high bits.</p>
+ </note>
+ <p>For integer <c><anno>Status</anno></c>, the Erlang runtime system
closes all ports and allows async threads to finish their
operations before exiting. To exit without such flushing, use
- <c><anno>Option</anno></c> as <c>{flush,false}</c>.</p>
- <p>For statuses <c>string()</c> and <c>abort</c>, option
+ <c><anno>Option</anno></c> as <c>{flush,false}</c>.</p>
+ <p>For statuses <c>string()</c> and <c>abort</c>, option
<c>flush</c> is ignored and flushing is <em>not</em> done.</p>
</desc>
</func>
@@ -1830,13 +1928,15 @@ os_prompt% </pre>
<p>Returns a hash value for <c><anno>Term</anno></c> within the range
<c>1..<anno>Range</anno></c>. The maximum range is 1..2^27-1.</p>
<warning>
- <p>This BIF is deprecated, as the hash value can differ on
- different architectures. The hash values for integer
- terms higher than 2^27 and large binaries are
+ <p><em>This BIF is deprecated, as the hash value can differ on
+ different architectures.</em> The hash values for integer
+ terms &gt; 2^27 and large binaries are
poor. The BIF is retained for backward compatibility
reasons (it can have been used to hash records into a file),
but all new code is to use one of the BIFs
- <c>erlang:phash/2</c> or <c>erlang:phash2/1,2</c> instead.</p>
+ <seealso marker="#phash/2"><c>erlang:phash/2</c></seealso> or
+ <seealso marker="#phash2/1"><c>erlang:phash2/1,2</c></seealso>
+ instead.</p>
</warning>
</desc>
</func>
@@ -1858,7 +1958,7 @@ os_prompt% </pre>
<func>
<name name="hibernate" arity="3"/>
- <fsummary>Hibernates a process until a message is sent to it.</fsummary>
+ <fsummary>Hibernate a process until a message is sent to it.</fsummary>
<desc>
<p>Puts the calling process into a wait state where its memory
allocation has been reduced as much as possible. This is
@@ -1866,15 +1966,15 @@ os_prompt% </pre>
soon.</p>
<p>The process is awaken when a message is sent to it, and control
resumes in <c><anno>Module</anno>:<anno>Function</anno></c> with
- the arguments given by <c><anno>Args</anno></c> with the call
+ the arguments specified by <c><anno>Args</anno></c> with the call
stack emptied, meaning that the process terminates when that
function returns. Thus <c>erlang:hibernate/3</c> never
returns to its caller.</p>
<p>If the process has any message in its message queue,
the process is awakened immediately in the same way as
described earlier.</p>
- <p>In more technical terms, what <c>erlang:hibernate/3</c> does
- is the following. It discards the call stack for the process,
+ <p>In more technical terms, <c>erlang:hibernate/3</c>
+ discards the call stack for the process,
and then garbage collects the process. After this,
all live data is in one continuous heap. The heap
is then shrunken to the exact same size as the live data
@@ -1886,11 +1986,12 @@ os_prompt% </pre>
size is changed to a size not smaller than the minimum heap
size.</p>
<p>Notice that emptying the call stack means that any surrounding
- <c>catch</c> is removed and must be reinserted after
+ <c>catch</c> is removed and must be re-inserted after
hibernation. One effect of this is that processes started
using <c>proc_lib</c> (also indirectly, such as
<c>gen_server</c> processes), are to use
- <seealso marker="stdlib:proc_lib#hibernate/3">proc_lib:hibernate/3</seealso>
+ <seealso marker="stdlib:proc_lib#hibernate/3">
+ <c>proc_lib:hibernate/3</c></seealso>
instead, to ensure that the exception handler continues to work
when the process wakes up.</p>
</desc>
@@ -1898,15 +1999,16 @@ os_prompt% </pre>
<func>
<name name="insert_element" arity="3"/>
- <fsummary>Inserts an element at index in a tuple.</fsummary>
- <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>) + 1</type_desc>
+ <fsummary>Insert an element at index in a tuple.</fsummary>
+ <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>)
+ + 1</type_desc>
<desc>
<p>Returns a new tuple with element <c><anno>Term</anno></c>
- inserted at position
- <c><anno>Index</anno></c> in tuple <c><anno>Tuple1</anno></c>.
- All elements from position <c><anno>Index</anno></c> and upwards are
- pushed one step higher in the new tuple <c><anno>Tuple2</anno></c>.</p>
- <p>Example:</p>
+ inserted at position
+ <c><anno>Index</anno></c> in tuple <c><anno>Tuple1</anno></c>.
+ All elements from position <c><anno>Index</anno></c> and upwards are
+ pushed one step higher in the new tuple <c><anno>Tuple2</anno></c>.
+ Example:</p>
<pre>
> <input>erlang:insert_element(2, {one, two, three}, new).</input>
{one,new,two,three}</pre>
@@ -1930,8 +2032,8 @@ os_prompt% </pre>
<fsummary>Text representation of an integer.</fsummary>
<desc>
<p>Returns a binary corresponding to the text
- representation of <c><anno>Integer</anno></c> in base
- <c><anno>Base</anno></c>, for example:</p>
+ representation of <c><anno>Integer</anno></c> in base
+ <c><anno>Base</anno></c>, for example:</p>
<pre>
> <input>integer_to_binary(1023, 16).</input>
&lt;&lt;"3FF">></pre>
@@ -1964,8 +2066,21 @@ os_prompt% </pre>
</func>
<func>
+ <name name="iolist_size" arity="1"/>
+ <fsummary>Size of an iolist.</fsummary>
+ <desc>
+ <p>Returns an integer, that is the size in bytes,
+ of the binary that would be the result of
+ <c>iolist_to_binary(<anno>Item</anno>)</c>, for example:</p>
+ <pre>
+> <input>iolist_size([1,2|&lt;&lt;3,4>>]).</input>
+4</pre>
+ </desc>
+ </func>
+
+ <func>
<name name="iolist_to_binary" arity="1"/>
- <fsummary>Converts an iolist to a binary.</fsummary>
+ <fsummary>Convert an iolist to a binary.</fsummary>
<desc>
<p>Returns a binary that is made from the integers and
binaries in <c><anno>IoListOrBinary</anno></c>, for example:</p>
@@ -1982,21 +2097,8 @@ os_prompt% </pre>
</func>
<func>
- <name name="iolist_size" arity="1"/>
- <fsummary>Size of an iolist.</fsummary>
- <desc>
- <p>Returns an integer that is the size in bytes
- of the binary that would be the result of
- <c>iolist_to_binary(<anno>Item</anno>)</c>, for example:</p>
- <pre>
-> <input>iolist_size([1,2|&lt;&lt;3,4>>]).</input>
-4</pre>
- </desc>
- </func>
-
- <func>
<name name="is_alive" arity="0"/>
- <fsummary>Checks whether the local node is alive.</fsummary>
+ <fsummary>Check whether the local node is alive.</fsummary>
<desc>
<p>Returns <c>true</c> if the local node is alive (that is, if
the node can be part of a distributed system), otherwise
@@ -2006,7 +2108,7 @@ os_prompt% </pre>
<func>
<name name="is_atom" arity="1"/>
- <fsummary>Checks whether a term is an atom.</fsummary>
+ <fsummary>Check whether a term is an atom.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is an atom,
otherwise <c>false</c>.</p>
@@ -2016,18 +2118,18 @@ os_prompt% </pre>
<func>
<name name="is_binary" arity="1"/>
- <fsummary>Checks whether a term is a binary.</fsummary>
+ <fsummary>Check whether a term is a binary.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a binary,
otherwise <c>false</c>.</p>
- <p>A binary always contains a complete number of bytes.</p>
+ <p>A binary always contains a complete number of bytes.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
<name name="is_bitstring" arity="1"/>
- <fsummary>Checks whether a term is a bitstring.</fsummary>
+ <fsummary>Check whether a term is a bitstring.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a
bitstring (including a binary), otherwise <c>false</c>.</p>
@@ -2037,7 +2139,7 @@ os_prompt% </pre>
<func>
<name name="is_boolean" arity="1"/>
- <fsummary>Checks whether a term is a boolean.</fsummary>
+ <fsummary>Check whether a term is a boolean.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is the
atom <c>true</c> or the atom <c>false</c> (that is, a boolean).
@@ -2048,18 +2150,18 @@ os_prompt% </pre>
<func>
<name name="is_builtin" arity="3"/>
- <fsummary>Checks if a function is a BIF implemented in C.</fsummary>
+ <fsummary>Check if a function is a BIF implemented in C.</fsummary>
<desc>
<p>This BIF is useful for builders of cross-reference tools.</p>
<p>Returns <c>true</c> if
<c><anno>Module</anno>:<anno>Function</anno>/<anno>Arity</anno></c>
- is a BIF implemented in C, otherwise <c>false</c>.</p>
+ is a BIF implemented in C, otherwise <c>false</c>.</p>
</desc>
</func>
<func>
<name name="is_float" arity="1"/>
- <fsummary>Checks whether a term is a float.</fsummary>
+ <fsummary>Check whether a term is a float.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a floating point
number, otherwise <c>false</c>.</p>
@@ -2069,7 +2171,7 @@ os_prompt% </pre>
<func>
<name name="is_function" arity="1"/>
- <fsummary>Checks whether a term is a fun.</fsummary>
+ <fsummary>Check whether a term is a fun.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun, otherwise
<c>false</c>.</p>
@@ -2079,7 +2181,8 @@ os_prompt% </pre>
<func>
<name name="is_function" arity="2"/>
- <fsummary>Checks whether a term is a fun with a given arity.</fsummary>
+ <fsummary>Check whether a term is a fun with a specified given arity.
+ </fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun that can be
applied with <c><anno>Arity</anno></c> number of arguments, otherwise
@@ -2090,7 +2193,7 @@ os_prompt% </pre>
<func>
<name name="is_integer" arity="1"/>
- <fsummary>Checks whether a term is an integer.</fsummary>
+ <fsummary>Check whether a term is an integer.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer,
otherwise <c>false</c>.</p>
@@ -2100,7 +2203,7 @@ os_prompt% </pre>
<func>
<name name="is_list" arity="1"/>
- <fsummary>Checks whether a term is a list.</fsummary>
+ <fsummary>Check whether a term is a list.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a list with
zero or more elements, otherwise <c>false</c>.</p>
@@ -2110,7 +2213,7 @@ os_prompt% </pre>
<func>
<name name="is_map" arity="1"/>
- <fsummary>Checks whether a term is a map.</fsummary>
+ <fsummary>Check whether a term is a map.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a map,
otherwise <c>false</c>.</p>
@@ -2120,7 +2223,7 @@ os_prompt% </pre>
<func>
<name name="is_number" arity="1"/>
- <fsummary>Checks whether a term is a number.</fsummary>
+ <fsummary>Check whether a term is a number.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer or a
floating point number. Otherwise returns <c>false</c>.</p>
@@ -2130,7 +2233,7 @@ os_prompt% </pre>
<func>
<name name="is_pid" arity="1"/>
- <fsummary>Checks whether a term is a process identifier.</fsummary>
+ <fsummary>Check whether a term is a process identifier.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a process
identifier, otherwise <c>false</c>.</p>
@@ -2140,7 +2243,7 @@ os_prompt% </pre>
<func>
<name name="is_port" arity="1"/>
- <fsummary>Checks whether a term is a port.</fsummary>
+ <fsummary>Check whether a term is a port.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a port identifier,
otherwise <c>false</c>.</p>
@@ -2150,26 +2253,26 @@ os_prompt% </pre>
<func>
<name name="is_process_alive" arity="1"/>
- <fsummary>Checks whether a process is alive.</fsummary>
+ <fsummary>Check whether a process is alive.</fsummary>
<desc>
- <p><c><anno>Pid</anno></c> must refer to a process at the local node.</p>
+ <p><c><anno>Pid</anno></c> must refer to a process at the local
+ node.</p>
<p>Returns <c>true</c> if the process exists and is alive, that
is, is not exiting and has not exited. Otherwise returns
- <c>false</c>.
- </p>
+ <c>false</c>.</p>
</desc>
</func>
<func>
<name name="is_record" arity="2"/>
- <fsummary>Checks whether a term appears to be a record.</fsummary>
+ <fsummary>Check whether a term appears to be a record.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple and its
first element is <c><anno>RecordTag</anno></c>.
Otherwise returns <c>false</c>.</p>
<note>
<p>Normally the compiler treats calls to <c>is_record/2</c>
- specially. It emits code to verify that <c><anno>Term</anno></c>
+ especially. It emits code to verify that <c><anno>Term</anno></c>
is a tuple, that its first element is
<c><anno>RecordTag</anno></c>, and that the
size is correct. However, if <c><anno>RecordTag</anno></c> is
@@ -2183,7 +2286,7 @@ os_prompt% </pre>
<func>
<name name="is_record" arity="3"/>
- <fsummary>Checks whether a term appears to be a record.</fsummary>
+ <fsummary>Check whether a term appears to be a record.</fsummary>
<desc>
<p><c><anno>RecordTag</anno></c> must be an atom.</p>
<p>Returns <c>true</c> if
@@ -2202,7 +2305,7 @@ os_prompt% </pre>
<func>
<name name="is_reference" arity="1"/>
- <fsummary>Checks whether a term is a reference.</fsummary>
+ <fsummary>Check whether a term is a reference.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a reference,
otherwise <c>false</c>.</p>
@@ -2212,7 +2315,7 @@ os_prompt% </pre>
<func>
<name name="is_tuple" arity="1"/>
- <fsummary>Checks whether a term is a tuple.</fsummary>
+ <fsummary>Check whether a term is a tuple.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple,
otherwise <c>false</c>.</p>
@@ -2234,7 +2337,7 @@ os_prompt% </pre>
<func>
<name name="link" arity="1"/>
- <fsummary>Creates a link to another process (or port).</fsummary>
+ <fsummary>Create a link to another process (or port).</fsummary>
<desc>
<p>Creates a link between the calling process and another
process (or port) <c><anno>PidOrPort</anno></c>, if there is
@@ -2244,34 +2347,34 @@ os_prompt% </pre>
<p>If <c><anno>PidOrPort</anno></c> does not exist, the behavior
of the BIF
depends on if the calling process is trapping exits or not (see
- <seealso marker="#process_flag/2">process_flag/2</seealso>):</p>
+ <seealso marker="#process_flag/2">
+ <c>process_flag/2</c></seealso>):</p>
<list type="bulleted">
- <item>If the calling process is not trapping exits, and
- checking <c><anno>PidOrPort</anno></c> is cheap
- (that is, if <c><anno>PidOrPort</anno></c>
- is local), <c>link/1</c> fails with reason <c>noproc</c>.</item>
- <item>Otherwise, if the calling process is trapping exits,
- and/or <c><anno>PidOrPort</anno></c> is remote, <c>link/1</c>
- returns <c>true</c>, but an exit signal with reason <c>noproc</c>
- is sent to the calling process.</item>
+ <item><p>If the calling process is not trapping exits, and
+ checking <c><anno>PidOrPort</anno></c> is cheap
+ (that is, if <c><anno>PidOrPort</anno></c>
+ is local), <c>link/1</c> fails with reason <c>noproc</c>.</p></item>
+ <item><p>Otherwise, if the calling process is trapping exits,
+ and/or <c><anno>PidOrPort</anno></c> is remote, <c>link/1</c>
+ returns <c>true</c>, but an exit signal with reason <c>noproc</c>
+ is sent to the calling process.</p></item>
</list>
</desc>
</func>
<func>
<name name="list_to_atom" arity="1"/>
- <fsummary>Converts from text representation to an atom.</fsummary>
+ <fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>Returns the atom whose text representation is
<c><anno>String</anno></c>.</p>
- <p><c><anno>String</anno></c> can only contain ISO-latin-1
- characters (that is,
- numbers less than 256) as the implementation does not
- allow unicode characters equal to or above 256 in atoms.
- For more information on Unicode support in atoms, see
- <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8
+ <p><c><anno>String</anno></c> can only contain ISO-latin-1
+ characters (that is, numbers &lt; 256) as the implementation does not
+ allow Unicode characters equal to or above 256 in atoms.
+ For more information on Unicode support in atoms, see
+ <seealso marker="erl_ext_dist#utf8_atoms">note on UTF-8
encoded atoms</seealso>
- in Section "External Term Format" in the User's Guide.</p>
+ in section "External Term Format" in the User's Guide.</p>
<p>Example:</p>
<pre>
> <input>list_to_atom("Erlang").</input>
@@ -2281,7 +2384,7 @@ os_prompt% </pre>
<func>
<name name="list_to_binary" arity="1"/>
- <fsummary>Converts a list to a binary.</fsummary>
+ <fsummary>Convert a list to a binary.</fsummary>
<desc>
<p>Returns a binary that is made from the integers and
binaries in <c><anno>IoList</anno></c>, for example:</p>
@@ -2299,13 +2402,13 @@ os_prompt% </pre>
<func>
<name name="list_to_bitstring" arity="1"/>
- <fsummary>Converts a list to a bitstring.</fsummary>
+ <fsummary>Convert a list to a bitstring.</fsummary>
<type name="bitstring_list"/>
<desc>
<p>Returns a bitstring that is made from the integers and
bitstrings in <c><anno>BitstringList</anno></c>. (The last tail in
- <c><anno>BitstringList</anno></c> is allowed to be a bitstring.)</p>
- <p>Example:</p>
+ <c><anno>BitstringList</anno></c> is allowed to be a bitstring.)
+ Example:</p>
<pre>
> <input>Bin1 = &lt;&lt;1,2,3&gt;&gt;.</input>
&lt;&lt;1,2,3&gt;&gt;
@@ -2320,7 +2423,7 @@ os_prompt% </pre>
<func>
<name name="list_to_existing_atom" arity="1"/>
- <fsummary>Converts from text representation to an atom.</fsummary>
+ <fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>Returns the atom whose text representation is
<c><anno>String</anno></c>,
@@ -2332,7 +2435,7 @@ os_prompt% </pre>
<func>
<name name="list_to_float" arity="1"/>
- <fsummary>Converts from text representation to a float.</fsummary>
+ <fsummary>Convert from text representation to a float.</fsummary>
<desc>
<p>Returns the float whose text representation is
<c><anno>String</anno></c>, for example:</p>
@@ -2346,7 +2449,7 @@ os_prompt% </pre>
<func>
<name name="list_to_integer" arity="1"/>
- <fsummary>Converts from text representation to an integer.</fsummary>
+ <fsummary>Convert from text representation to an integer.</fsummary>
<desc>
<p>Returns an integer whose text representation is
<c><anno>String</anno></c>, for example:</p>
@@ -2360,7 +2463,7 @@ os_prompt% </pre>
<func>
<name name="list_to_integer" arity="2"/>
- <fsummary>Converts from text representation to an integer.</fsummary>
+ <fsummary>Convert from text representation to an integer.</fsummary>
<desc>
<p>Returns an integer whose text representation in base
<c><anno>Base</anno></c> is <c><anno>String</anno></c>,
@@ -2375,7 +2478,7 @@ os_prompt% </pre>
<func>
<name name="list_to_pid" arity="1"/>
- <fsummary>Converts from text representation to a pid.</fsummary>
+ <fsummary>Convert from text representation to a pid.</fsummary>
<desc>
<p>Returns a process identifier whose text representation is a
<c><anno>String</anno></c>, for example:</p>
@@ -2386,14 +2489,14 @@ os_prompt% </pre>
representation of a process identifier.</p>
<warning>
<p>This BIF is intended for debugging and is not to be used
- in application programs.</p>
+ in application programs.</p>
</warning>
</desc>
</func>
<func>
<name name="list_to_tuple" arity="1"/>
- <fsummary>Converts a list to a tuple.</fsummary>
+ <fsummary>Convert a list to a tuple.</fsummary>
<desc>
<p>Returns a tuple corresponding to <c><anno>List</anno></c>,
for example</p>
@@ -2406,7 +2509,7 @@ os_prompt% </pre>
<func>
<name name="load_module" arity="2"/>
- <fsummary>Loads object code for a module.</fsummary>
+ <fsummary>Load object code for a module.</fsummary>
<desc>
<p>If <c><anno>Binary</anno></c> contains the object code for module
<c><anno>Module</anno></c>, this BIF loads that object code. If
@@ -2417,23 +2520,21 @@ os_prompt% </pre>
that code.</p>
<p>Returns either <c>{module, <anno>Module</anno>}</c>, or
<c>{error, <anno>Reason</anno>}</c> if loading fails.
- <c><anno>Reason</anno></c> is any of the following:</p>
+ <c><anno>Reason</anno></c> is one of the following:</p>
<taglist>
<tag><c>badfile</c></tag>
- <item>
- <p>The object code in <c><anno>Binary</anno></c> has an
- incorrect format <em>or</em> the object code contains code
- for another module than <c><anno>Module</anno></c>.</p>
+ <item>The object code in <c><anno>Binary</anno></c> has an
+ incorrect format <em>or</em> the object code contains code
+ for another module than <c><anno>Module</anno></c>.
</item>
<tag><c>not_purged</c></tag>
- <item>
- <p><c><anno>Binary</anno></c> contains a module that cannot be
- loaded because old code for this module already exists.</p>
+ <item><c><anno>Binary</anno></c> contains a module that cannot be
+ loaded because old code for this module already exists.
</item>
</taglist>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code">code(3)</seealso>)
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
and is not to be used elsewhere.</p>
</warning>
</desc>
@@ -2441,33 +2542,36 @@ os_prompt% </pre>
<func>
<name name="load_nif" arity="2"/>
- <fsummary>Loads NIF library.</fsummary>
+ <fsummary>Load NIF library.</fsummary>
<desc>
<note>
- <p>Before OTP R14B, NIFs were an
- experimental feature. Versions before OTP R14B can
- have different and possibly incompatible NIF semantics and
- interfaces. For example, in OTP R13B03 the return value on
- failure was <c>{error,Reason,Text}</c>.</p>
+ <p>Before Erlang/OTP R14B, NIFs were an
+ experimental feature. Versions before Erlang/OTP R14B can
+ have different and possibly incompatible NIF semantics and
+ interfaces. For example, in Erlang/OTP R13B03 the return value on
+ failure was <c>{error,Reason,Text}</c>.</p>
</note>
<p>Loads and links a dynamic library containing native
- implemented functions (NIFs) for a module. <c><anno>Path</anno></c>
- is a file path to the shareable object/dynamic library file minus
- the OS-dependent file extension (<c>.so</c> for Unix and
- <c>.dll</c> for Windows. For information on how to
- implement a NIF library, see
- <seealso marker="erl_nif">erl_nif</seealso>.</p>
+ implemented functions (NIFs) for a module. <c><anno>Path</anno></c>
+ is a file path to the shareable object/dynamic library file minus
+ the OS-dependent file extension (<c>.so</c> for Unix and
+ <c>.dll</c> for Windows). Notice that on most OSs the library has
+ to have a different name on disc when an upgrade of the nif is
+ done. If the name is the same, but the contents differ, the
+ old library may be loaded instead. For information on how to
+ implement a NIF library, see
+ <seealso marker="erl_nif"><c>erl_nif(3)</c></seealso>.</p>
<p><c><anno>LoadInfo</anno></c> can be any term. It is passed on to
- the library as part of the initialization. A good practice is
- to include a module version number to support future code
- upgrade scenarios.</p>
+ the library as part of the initialization. A good practice is
+ to include a module version number to support future code
+ upgrade scenarios.</p>
<p>The call to <c>load_nif/2</c> must be made
- <em>directly</em> from the Erlang code of the module that the
- NIF library belongs to. It returns either <c>ok</c>, or
- <c>{error,{<anno>Reason</anno>,Text}}</c> if loading fails.
- <c><anno>Reason</anno></c> is one of the following atoms
- while <c><anno>Text</anno></c> is a human readable string that
- can give more information about the failure:</p>
+ <em>directly</em> from the Erlang code of the module that the
+ NIF library belongs to. It returns either <c>ok</c>, or
+ <c>{error,{<anno>Reason</anno>,Text}}</c> if loading fails.
+ <c><anno>Reason</anno></c> is one of the following atoms
+ while <c><anno>Text</anno></c> is a human readable string that
+ can give more information about the failure:</p>
<taglist>
<tag><c>load_failed</c></tag>
<item>The OS failed to load the NIF library.
@@ -2490,11 +2594,12 @@ os_prompt% </pre>
<func>
<name name="loaded" arity="0"/>
- <fsummary>Lists all loaded modules.</fsummary>
+ <fsummary>List all loaded modules.</fsummary>
<desc>
<p>Returns a list of all loaded Erlang modules (current and
old code), including preloaded modules.</p>
- <p>See also <seealso marker="kernel:code">code(3)</seealso>.</p>
+ <p>See also <seealso marker="kernel:code">
+ <c>code(3)</c></seealso>.</p>
</desc>
</func>
@@ -2515,13 +2620,13 @@ os_prompt% </pre>
<func>
<name name="localtime_to_universaltime" arity="1"/>
- <fsummary>Converts from local to Universal Time Coordinated (UTC) date and time.</fsummary>
+ <fsummary>Convert from local to Universal Time Coordinated (UTC) date
+ and time.</fsummary>
<desc>
<p>Converts local date and time to Universal Time Coordinated
(UTC), if supported by the underlying OS. Otherwise
no conversion is done and <c><anno>Localtime</anno></c>
- is returned.</p>
- <p>Example:</p>
+ is returned. Example:</p>
<pre>
> <input>erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}).</input>
{{1996,11,6},{13,45,17}}</pre>
@@ -2532,15 +2637,16 @@ os_prompt% </pre>
<func>
<name name="localtime_to_universaltime" arity="2"/>
- <fsummary>Converts from local to Universal Time Coordinated (UTC) date and time.</fsummary>
+ <fsummary>Convert from local to Universal Time Coordinated (UTC) date
+ and time.</fsummary>
<desc>
<p>Converts local date and time to Universal Time Coordinated
(UTC) as <c>erlang:localtime_to_universaltime/1</c>,
but the caller decides if Daylight Saving Time is active.</p>
- <p>If <c><anno>IsDst</anno> == true</c>, <c><anno>Localtime</anno></c> is
- during Daylight Saving Time, if <c><anno>IsDst</anno> == false</c> it is
- not. If <c><anno>IsDst</anno> == undefined</c>, the underlying OS can
- guess, which is the same as calling
+ <p>If <c><anno>IsDst</anno> == true</c>, <c><anno>Localtime</anno></c>
+ is during Daylight Saving Time, if <c><anno>IsDst</anno> == false</c>
+ it is not. If <c><anno>IsDst</anno> == undefined</c>, the underlying
+ OS can guess, which is the same as calling
<c>erlang:localtime_to_universaltime(<anno>Localtime</anno>)</c>.</p>
<p>Examples:</p>
<pre>
@@ -2557,24 +2663,27 @@ os_prompt% </pre>
<func>
<name name="make_ref" arity="0"/>
- <fsummary>Returns a unique reference.</fsummary>
+ <fsummary>Return a unique reference.</fsummary>
<desc>
- <p>Returns a <seealso marker="doc/efficiency_guide:advanced#unique_references">unique
- reference</seealso>. The reference is unique among
- connected nodes.</p>
- <warning><p>Known issue: When a node is restarted multiple
- times with the same node name, references created
- on a newer node can be mistaken for a reference
- created on an older node with the same node name.</p></warning>
+ <p>Returns a
+ <seealso marker="doc/efficiency_guide:advanced#unique_references">
+ unique reference</seealso>. The reference is unique among
+ connected nodes.</p>
+ <warning>
+ <p>Known issue: When a node is restarted multiple
+ times with the same node name, references created
+ on a newer node can be mistaken for a reference
+ created on an older node with the same node name.</p>
+ </warning>
</desc>
</func>
<func>
<name name="make_tuple" arity="2"/>
- <fsummary>Creates a new tuple of a given arity.</fsummary>
+ <fsummary>Create a new tuple of a specified arity.</fsummary>
<desc>
- <p>Creates a new tuple of the given <c><anno>Arity</anno></c>, where all
- elements are <c><anno>InitialValue</anno></c>, for example:</p>
+ <p>Creates a new tuple of the specified <c><anno>Arity</anno></c>, where
+ all elements are <c><anno>InitialValue</anno></c>, for example:</p>
<pre>
> <input>erlang:make_tuple(4, []).</input>
{[],[],[],[]}</pre>
@@ -2583,26 +2692,25 @@ os_prompt% </pre>
<func>
<name name="make_tuple" arity="3"/>
- <fsummary>Creates a new tuple with given arity and contents.</fsummary>
+ <fsummary>Create a new tuple with specifed arity and contents.</fsummary>
<desc>
<p>Creates a tuple of size <c><anno>Arity</anno></c>, where each element
has value <c><anno>DefaultValue</anno></c>, and then fills in
- values from <c><anno>InitList</anno></c>.
+ values from <c><anno>InitList</anno></c>.
Each list element in <c><anno>InitList</anno></c>
- must be a two-tuple, where the first element is a position in the
- newly created tuple and the second element is any term. If a
- position occurs more than once in the list, the term corresponding
- to the last occurrence is used.</p>
- <p>Example:</p>
+ must be a two-tuple, where the first element is a position in the
+ newly created tuple and the second element is any term. If a
+ position occurs more than once in the list, the term corresponding
+ to the last occurrence is used. Example:</p>
<pre>
> <input>erlang:make_tuple(5, [], [{2,ignored},{5,zz},{2,aa}]).</input>
-{{[],aa,[],[],zz}</pre>
+{[],aa,[],[],zz}</pre>
</desc>
</func>
<func>
<name name="map_size" arity="1"/>
- <fsummary>Returns the size of a map.</fsummary>
+ <fsummary>Return the size of a map.</fsummary>
<desc>
<p>Returns an integer, which is the number of key-value pairs
in <c><anno>Map</anno></c>, for example:</p>
@@ -2615,74 +2723,74 @@ os_prompt% </pre>
<func>
<name name="match_spec_test" arity="3"/>
- <fsummary>Test that a match specification works</fsummary>
- <desc>
- <p>
- This function is a utility to test a match_spec used in calls to
- <seealso marker="stdlib:ets#select/2">ets:select/2</seealso> and
- <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.
- The function both tests MatchSpec for "syntactic" correctness and
- runs the match_spec against the object. If the match_spec contains
- errors, the tuple {error, Errors} is returned where Errors is a list
- of natural language descriptions of what was wrong with the match_spec.
- </p>
- <p>
- If the <c><anno>Type</anno></c> is <c>table</c> the object to match
- against should be a tuple. The function then returns
- {ok,Result,[],Warnings} where Result is what would have been the
- result in a real ets:select/2 call or false if the match_spec does
- not match the object tuple.
- </p>
-
- <p>
- If <c><anno>Type</anno></c> is <c>trace</c> the object to match
- against should be a list. The function returns
- {ok, Result, Flags, Warnings} where Result is <c>true</c> if a trace
- message should be emitted, <c>false</c> if a trace message should not
- be emitted or the message term to be appended to the trace message.
- Flags is a list containing all the trace flags that will be enabled,
- at the moment this is only <c>return_trace</c>.
- </p>
-
- <p>
- This is a useful debugging and test tool, especially when writing complicated
- match specifications.
- </p>
- <p>
- See also
- <seealso marker="stdlib:ets#test_ms/2">ets:test_ms/2</seealso>.
- </p>
+ <fsummary>Test that a match specification works.</fsummary>
+ <desc>
+ <p>Tests a match specification used in calls to
+ <seealso marker="stdlib:ets#select/2"><c>ets:select/2</c></seealso>
+ and <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern/3</c></seealso>.
+ The function tests both a match specification for "syntactic"
+ correctness and runs the match specification against the object. If
+ the match specification contains errors, the tuple <c>{error,
+ Errors}</c> is returned, where <c>Errors</c> is a list of natural
+ language descriptions of what was wrong with the match
+ specification.</p>
+ <p>If <c><anno>Type</anno></c> is <c>table</c>, the object to match
+ against is to be a tuple. The function then returns
+ <c>{ok,Result,[],Warnings}</c>, where <c>Result</c> is what would
+ have been the result in a real <c>ets:select/2</c> call, or
+ <c>false</c> if the match specification does not match the object
+ tuple.</p>
+ <p>If <c><anno>Type</anno></c> is <c>trace</c>, the object to match
+ against is to be a list. The function returns
+ <c>{ok, Result, Flags, Warnings}</c>, where <c>Result</c> is one of
+ the following:</p>
+ <list type="bulleted">
+ <item><c>true</c> if a trace message is to be emitted</item>
+ <item><c>false</c> if a trace message is not to be emitted</item>
+ <item>The message term to be appended to the trace message</item>
+ </list>
+ <p><c>Flags</c> is a list containing all the trace flags to be enabled,
+ currently this is only <c>return_trace</c>.</p>
+ <p>This is a useful debugging and test tool, especially when writing
+ complicated match specifications.</p>
+ <p>See also
+ <seealso marker="stdlib:ets#test_ms/2"><c>ets:test_ms/2</c></seealso>.</p>
</desc>
</func>
<func>
<name name="max" arity="2"/>
- <fsummary>Returns the largest of two terms.</fsummary>
+ <fsummary>Return the largest of two terms.</fsummary>
<desc>
<p>Returns the largest of <c><anno>Term1</anno></c> and
<c><anno>Term2</anno></c>.
- If the terms are equal, <c><anno>Term1</anno></c> is returned.</p>
+ If the terms are equal, <c><anno>Term1</anno></c> is returned.</p>
</desc>
</func>
<func>
<name name="md5" arity="1"/>
- <fsummary>Computes an MD5 message digest.</fsummary>
+ <fsummary>Compute an MD5 message digest.</fsummary>
<desc>
<p>Computes an MD5 message digest from <c><anno>Data</anno></c>, where
the length of the digest is 128 bits (16 bytes).
<c><anno>Data</anno></c>
is a binary or a list of small integers and binaries.</p>
- <p>For more information about MD5, see RFC 1321 - The
- MD5 Message-Digest Algorithm.</p>
- <warning><p>The MD5 Message-Digest Algorithm is <em>not</em> considered
- safe for code-signing or software-integrity purposes.</p></warning>
+ <p>For more information about MD5, see
+ <url href="https://www.ietf.org/rfc/rfc1321.txt">
+ RFC 1321 - The MD5 Message-Digest Algorithm</url>.</p>
+ <warning>
+ <p>The MD5 Message-Digest Algorithm is <em>not</em> considered
+ safe for code-signing or software-integrity purposes.</p>
+ </warning>
</desc>
</func>
<func>
<name name="md5_final" arity="1"/>
- <fsummary>Finishes the update of an MD5 context and returns the computed MD5 message digest.</fsummary>
+ <fsummary>Finish the update of an MD5 context and return the computed
+ MD5 message digest.</fsummary>
<desc>
<p>Finishes the update of an MD5 <c><anno>Context</anno></c> and returns
the computed <c>MD5</c> message digest.</p>
@@ -2691,18 +2799,19 @@ os_prompt% </pre>
<func>
<name name="md5_init" arity="0"/>
- <fsummary>Creates an MD5 context.</fsummary>
+ <fsummary>Create an MD5 context.</fsummary>
<desc>
- <p>Creates an MD5 context, to be used in subsequent calls to
+ <p>Creates an MD5 context, to be used in the following calls to
<c>md5_update/2</c>.</p>
</desc>
</func>
<func>
<name name="md5_update" arity="2"/>
- <fsummary>Updates an MD5 context with data and returns a new context.</fsummary>
+ <fsummary>Update an MD5 context with data and return a new context.
+ </fsummary>
<desc>
- <p>Updates an MD5 <c><anno>Context</anno></c> with
+ <p>Update an MD5 <c><anno>Context</anno></c> with
<c><anno>Data</anno></c> and returns a
<c><anno>NewContext</anno></c>.</p>
</desc>
@@ -2718,7 +2827,7 @@ os_prompt% </pre>
element is a tuple <c>{Type, Size}</c>. The first element
<c><anno>Type</anno></c> is an atom describing memory type. The second
element <c><anno>Size</anno></c> is the memory size in bytes.</p>
- <p>The memory types are as follows:</p>
+ <p>Memory types:</p>
<taglist>
<tag><c>total</c></tag>
<item>
@@ -2770,7 +2879,7 @@ os_prompt% </pre>
</item>
<tag><c>ets</c></tag>
<item>
- <p>The total amount of memory currently allocated for ets
+ <p>The total amount of memory currently allocated for ETS
tables. This memory is part of the memory presented as
<c>system</c> memory.</p>
</item>
@@ -2778,9 +2887,9 @@ os_prompt% </pre>
<item>
<p>Only on 64-bit halfword emulator.
The total amount of memory allocated in low memory areas
- that are restricted to less than 4 GB, although
- the system can have more memory.</p>
- <p>Can be removed in a future release of the halfword
+ that are restricted to &lt; 4 GB, although
+ the system can have more memory.</p>
+ <p>Can be removed in a future release of the halfword
emulator.</p>
</item>
<tag><c>maximum</c></tag>
@@ -2790,8 +2899,9 @@ os_prompt% </pre>
when the emulator is run with instrumentation.</p>
<p>For information on how to run the emulator with
instrumentation, see
- <seealso marker="tools:instrument">instrument(3)</seealso>
- and/or <seealso marker="erts:erl">erl(1)</seealso>.</p>
+ <seealso marker="tools:instrument">
+ <c>instrument(3)</c></seealso>
+ and/or <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
</item>
</taglist>
<note>
@@ -2808,20 +2918,20 @@ os_prompt% </pre>
<p>As the <c>total</c> value is the sum of <c>processes</c>
and <c>system</c>, the error in <c>system</c> propagates
to the <c>total</c> value.</p>
- <p>The different amounts of memory that are summed are
- <em>not</em> gathered atomically, which introduces
- an error in the result.</p>
+ <p>The different amounts of memory that are summed are
+ <em>not</em> gathered atomically, which introduces
+ an error in the result.</p>
</note>
<p>The different values have the following relation to each
other. Values beginning with an uppercase letter is not part
of the result.</p>
<code type="none">
- total = processes + system
- processes = processes_used + ProcessesNotUsed
- system = atom + binary + code + ets + OtherSystem
- atom = atom_used + AtomNotUsed
- RealTotal = processes + RealSystem
- RealSystem = system + MissedSystem</code>
+total = processes + system
+processes = processes_used + ProcessesNotUsed
+system = atom + binary + code + ets + OtherSystem
+atom = atom_used + AtomNotUsed
+RealTotal = processes + RealSystem
+RealSystem = system + MissedSystem</code>
<p>More tuples in the returned list can be added in a
future release.</p>
<note>
@@ -2831,20 +2941,20 @@ os_prompt% </pre>
the emulator stacks are not supposed to be included. That
is, the <c>total</c> value is <em>not</em> supposed to be
equal to the total size of all pages mapped to the emulator.</p>
- <p>Furthermore, because of fragmentation and prereservation of
+ <p>Also, because of fragmentation and prereservation of
memory areas, the size of the memory segments containing
the dynamically allocated memory blocks can be much
larger than the total size of the dynamically allocated
memory blocks.</p>
</note>
- <note>
- <p>As from <c>ERTS</c> 5.6.4, <c>erlang:memory/0</c> requires that
- all <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>
- allocators are enabled (default behavior).</p>
- </note>
+ <note>
+ <p>As from ERTS 5.6.4, <c>erlang:memory/0</c> requires that
+ all <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso>
+ allocators are enabled (default behavior).</p>
+ </note>
<p>Failure: <c>notsup</c> if an
- <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>
- allocator has been disabled.</p>
+ <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso>
+ allocator has been disabled.</p>
</desc>
</func>
@@ -2854,61 +2964,61 @@ os_prompt% </pre>
<fsummary>Information about dynamically allocated memory.</fsummary>
<type name="memory_type"/>
<desc>
- <p>Returns the memory size in bytes allocated for memory of
- type <c><anno>Type</anno></c>. The argument can also be given as a list
+ <p>Returns the memory size in bytes allocated for memory of type
+ <c><anno>Type</anno></c>. The argument can also be specified as a list
of <c>memory_type()</c> atoms, in which case a corresponding list of
<c>{memory_type(), Size :: integer >= 0}</c> tuples is returned.</p>
- <note>
- <p>As from <c>ERTS</c> version 5.6.4,
+ <note>
+ <p>As from ERTS 5.6.4,
<c>erlang:memory/1</c> requires that
- all <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>
- allocators are enabled (default behavior).</p>
- </note>
+ all <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>
+ allocators are enabled (default behavior).</p>
+ </note>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Type</anno></c> is not one of the memory types
+ If <c><anno>Type</anno></c> is not one of the memory types
listed in the description of
- <seealso marker="#memory/0">erlang:memory/0</seealso>.
- </item>
+ <seealso marker="#memory/0"><c>erlang:memory/0</c></seealso>.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c>maximum</c> is passed as <c><anno>Type</anno></c> and
+ If <c>maximum</c> is passed as <c><anno>Type</anno></c> and
the emulator is not run in instrumented mode.
- </item>
+ </item>
<tag><c>notsup</c></tag>
<item>
- If an <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>
- allocator has been disabled.
- </item>
- </taglist>
+ If an <seealso marker="erts_alloc"><c>erts_alloc(3)</c></seealso>
+ allocator has been disabled.
+ </item>
+ </taglist>
<p>See also
- <seealso marker="#memory/0">erlang:memory/0</seealso>.</p>
+ <seealso marker="#memory/0"><c>erlang:memory/0</c></seealso>.</p>
</desc>
</func>
<func>
<name name="min" arity="2"/>
- <fsummary>Returns the smallest of two terms.</fsummary>
+ <fsummary>Return the smallest of two terms.</fsummary>
<desc>
<p>Returns the smallest of <c><anno>Term1</anno></c> and
<c><anno>Term2</anno></c>.
- If the terms are equal, <c><anno>Term1</anno></c> is returned.</p>
+ If the terms are equal, <c><anno>Term1</anno></c> is returned.</p>
</desc>
</func>
<func>
<name name="module_loaded" arity="1"/>
- <fsummary>Checks if a module is loaded.</fsummary>
+ <fsummary>Check if a module is loaded.</fsummary>
<desc>
<p>Returns <c>true</c> if the module <c><anno>Module</anno></c>
is loaded, otherwise <c>false</c>. It does not attempt to load
the module.</p>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code">code(3)</seealso>) and is not to be
- used elsewhere.</p>
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
+ and is not to be used elsewhere.</p>
</warning>
</desc>
</func>
@@ -2917,7 +3027,7 @@ os_prompt% </pre>
<name name="monitor" arity="2" clause_i="1"/>
<name name="monitor" arity="2" clause_i="2"/>
<name name="monitor" arity="2" clause_i="3"/>
- <fsummary>Starts monitoring.</fsummary>
+ <fsummary>Start monitoring.</fsummary>
<type name="registered_name"/>
<type name="registered_process_identifier"/>
<type name="monitor_process_identifier"/>
@@ -2925,35 +3035,39 @@ os_prompt% </pre>
<desc>
<p>Sends a monitor request of type <c><anno>Type</anno></c> to the
entity identified by <c><anno>Item</anno></c>. If the monitored entity
- does not exist or when it dies, the caller of <c>monitor/2</c> will
- be notified by a message on the following format:</p>
- <code type="none">{Tag, <anno>MonitorRef</anno>, <anno>Type</anno>, Object, Info}</code>
- <note><p>The monitor request is an asynchronous signal. That is, it
- takes time before the signal reaches its destination.</p></note>
+ does not exist or it changes monitored state, the caller of
+ <c>monitor/2</c> is notified by a message on the following format:</p>
+ <code type="none">
+{Tag, <anno>MonitorRef</anno>, <anno>Type</anno>, Object, Info}</code>
+ <note>
+ <p>The monitor request is an asynchronous signal. That is, it
+ takes time before the signal reaches its destination.</p>
+ </note>
<p><c><anno>Type</anno></c> can be one of the following atoms:
<c>process</c>, <c>port</c> or <c>time_offset</c>.</p>
- <p>A monitor is triggered only once, after that it is removed from
- both monitoring process and the monitored entity.
- Monitors are fired when the monitored process or port terminates,
- does not exist at the moment of creation, or if the connection to
- it is lost. In the case with connection, we lose knowledge about
- the fact if it still exists or not. The monitoring is also turned off
- when <seealso marker="#demonitor/1">demonitor/1</seealso>
- is called.</p>
-
- <p>When monitoring by name please note, that the <c>RegisteredName</c>
- is resolved to <c>pid()</c> or <c>port()</c> only once
- at the moment of monitor instantiation, later changes to the name
- registration will not affect the existing monitor.</p>
-
- <p>When a monitor is triggered, a <c>'DOWN'</c> message that has the
- following pattern <c>{'DOWN', MonitorRef, Type, Object, Info}</c>
- is sent to the monitoring process.</p>
-
- <p>In monitor message <c>MonitorRef</c> and <c>Type</c> are the same as
- described earlier, and:</p>
+ <p>A <c>process</c> or <c>port</c> monitor is triggered only once,
+ after that it is removed from both monitoring process and
+ the monitored entity. Monitors are fired when the monitored process
+ or port terminates, does not exist at the moment of creation,
+ or if the connection to it is lost. If the connection to it is lost,
+ we do not know if it still exists. The monitoring is also turned off
+ when <seealso marker="#demonitor/1">demonitor/1</seealso> is
+ called.</p>
+
+ <p>A <c>process</c> or <c>port</c> monitor by name
+ resolves the <c>RegisteredName</c> to <c>pid()</c> or <c>port()</c>
+ only once at the moment of monitor instantiation, later changes to
+ the name registration will not affect the existing monitor.</p>
+
+ <p>When a <c>process</c> or <c>port</c> monitor is triggered,
+ a <c>'DOWN'</c> message is sent that has the following pattern:</p>
+ <code type="none">
+{'DOWN', MonitorRef, Type, Object, Info}</code>
+
+ <p>In the monitor message <c>MonitorRef</c> and <c>Type</c> are the
+ same as described earlier, and:</p>
<taglist>
<tag><c>Object</c></tag>
<item>
@@ -2980,14 +3094,14 @@ os_prompt% </pre>
implemented), the call fails with <c>badarg</c>.</p>
<note>
<p>The format of the <c>'DOWN'</c> message changed in ERTS
- version 5.2 (OTP R9B) for monitoring
+ 5.2 (Erlang/OTP R9B) for monitoring
<em>by registered name</em>. Element <c>Object</c> of
the <c>'DOWN'</c> message could in earlier versions
sometimes be the process identifier of the monitored process and sometimes
be the registered name. Now element <c>Object</c> is
always a tuple consisting of the registered name and
- the node name. Processes on new nodes (ERTS version 5.2
- or higher) always get <c>'DOWN'</c> messages on
+ the node name. Processes on new nodes (ERTS 5.2
+ or higher versions) always get <c>'DOWN'</c> messages on
the new format even if they are monitoring processes on old
nodes. Processes on old nodes always get <c>'DOWN'</c>
messages on the old format.</p>
@@ -2996,11 +3110,11 @@ os_prompt% </pre>
<taglist>
<tag>Monitoring a <marker id="monitor_process"/><c>process</c></tag>
<item>
- <p>Creates monitor between the current process and another
- process identified by <c><anno>Item</anno></c>, which can be a
- <c>pid()</c> (local or remote), an atom <c>RegisteredName</c> or
- a tuple <c>{RegisteredName, Node}</c> for a registered process,
- located elsewhere.</p>
+ <p>Creates monitor between the current process and another
+ process identified by <c><anno>Item</anno></c>, which can be a
+ <c>pid()</c> (local or remote), an atom <c>RegisteredName</c> or
+ a tuple <c>{RegisteredName, Node}</c> for a registered process,
+ located elsewhere.</p>
</item>
<tag>Monitoring a <marker id="monitor_port"/><c>port</c></tag>
@@ -3016,60 +3130,62 @@ os_prompt% </pre>
<tag>Monitoring a
<marker id="monitor_time_offset"/><c>time_offset</c></tag>
<item>
- <p>Monitor changes in
- <seealso marker="#time_offset/0">time offset</seealso>
- between
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> and
- <seealso marker="time_correction#Erlang_System_Time">Erlang
- system time</seealso>. There is only one valid
- <c><anno>Item</anno></c> in combination with the
- <c>time_offset <anno>Type</anno></c>, namely the atom
- <c>clock_service</c>. Note that the atom <c>clock_service</c> is
- <em>not</em> the registered name of a process. In this specific
- case it serves as an identifier of the runtime system internal
- clock service at current runtime system instance.</p>
+ <p>Monitors changes in
+ <seealso marker="#time_offset/0"><c>time offset</c></seealso>
+ between
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> and
+ <seealso marker="time_correction#Erlang_System_Time">Erlang
+ system time</seealso>. One valid <c><anno>Item</anno></c>
+ exists in combination with the
+ <c>time_offset <anno>Type</anno></c>, namely the atom
+ <c>clock_service</c>. Notice that the atom <c>clock_service</c> is
+ <em>not</em> the registered name of a process. In this
+ case it serves as an identifier of the runtime system internal
+ clock service at current runtime system instance.</p>
<p>The monitor is triggered when the time offset is changed.
- This either if the time offset value is changed, or if the
- offset is changed from preliminary to final during
- <seealso marker="#system_flag_time_offset">finalization
- of the time offset</seealso> when the
- <seealso marker="time_correction#Single_Time_Warp_Mode">single
- time warp mode</seealso> is used. When a change from preliminary
- to final time offset is made, the monitor will be triggered once
- regardless of whether the time offset value was actually changed
- or not.</p>
+ This either if the time offset value is changed, or if the
+ offset is changed from preliminary to final during
+ <seealso marker="#system_flag_time_offset">finalization
+ of the time offset</seealso> when the
+ <seealso marker="time_correction#Single_Time_Warp_Mode">single
+ time warp mode</seealso> is used. When a change from preliminary
+ to final time offset is made, the monitor is triggered once
+ regardless of whether the time offset value was changed
+ or not.</p>
<p>If the runtime system is in
- <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
- time warp mode</seealso>, the time offset will be changed when
- the runtime system detects that the
- <seealso marker="time_correction#OS_System_Time">OS system
- time</seealso> has changed. The runtime system will, however,
- not detect this immediately when it happens. A task checking
- the time offset is scheduled to execute at least once a minute,
- so under normal operation this should be detected within a
- minute, but during heavy load it might take longer time.</p>
-
- <p>The monitor will <em>not</em> be automatically removed
- after it has been triggered. That is, repeated changes of
- the time offset will trigger the monitor repeatedly.</p>
-
- <p>When the monitor is triggered a <c>'CHANGE'</c> message will
- be sent to the monitoring process. A <c>'CHANGE'</c> message has
- the following pattern:</p>
- <code type="none">{'CHANGE', MonitorRef, Type, Item, NewTimeOffset}</code>
- <p>where <c>MonitorRef</c>, <c><anno>Type</anno></c>, and
- <c><anno>Item</anno></c> are the same as described above, and
- <c>NewTimeOffset</c> is the new time offset.</p>
+ <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
+ time warp mode</seealso>, the time offset is changed when
+ the runtime system detects that the
+ <seealso marker="time_correction#OS_System_Time">OS system
+ time</seealso> has changed. The runtime system does, however,
+ not detect this immediately when it occurs. A task checking
+ the time offset is scheduled to execute at least once a minute,
+ so under normal operation this is to be detected within a
+ minute, but during heavy load it can take longer time.</p>
+
+ <p>The monitor is <em>not</em> automatically removed
+ after it has been triggered. That is, repeated changes of
+ the time offset trigger the monitor repeatedly.</p>
+
+ <p>When the monitor is triggered a <c>'CHANGE'</c> message is
+ sent to the monitoring process. A <c>'CHANGE'</c> message has
+ the following pattern:</p>
+ <code type="none">
+{'CHANGE', MonitorRef, Type, Item, NewTimeOffset}</code>
+ <p>where <c>MonitorRef</c>, <c><anno>Type</anno></c>, and
+ <c><anno>Item</anno></c> are the same as described above, and
+ <c>NewTimeOffset</c> is the new time offset.</p>
<p>When the <c>'CHANGE'</c> message has been received you are
- guaranteed not to retrieve the old time offset when calling
- <seealso marker="#time_offset/0"><c>erlang:time_offset()</c></seealso>.
- Note that you can observe the change of the time offset
- when calling <c>erlang:time_offset()</c> before you
- get the <c>'CHANGE'</c> message.</p>
+ guaranteed not to retrieve the old time offset when calling
+ <seealso marker="#time_offset/0">
+ <c>erlang:time_offset()</c></seealso>.
+ Notice that you can observe the change of the time offset
+ when calling <c>erlang:time_offset()</c> before you
+ get the <c>'CHANGE'</c> message.</p>
</item>
</taglist>
@@ -3080,20 +3196,19 @@ os_prompt% </pre>
<p>The monitor functionality is expected to be extended. That is,
other <c><anno>Type</anno></c>s and <c><anno>Item</anno></c>s
are expected to be supported in a future release.</p>
-
<note>
<p>If or when <c>monitor/2</c> is extended, other
- possible values for <c>Tag</c>, <c>Object</c> and
- <c>Info</c> in the monitor message will be introduced.</p>
+ possible values for <c>Tag</c>, <c>Object</c>, and
+ <c>Info</c> in the monitor message will be introduced.</p>
</note>
</desc>
</func>
<func>
<name name="monitor_node" arity="2"/>
- <fsummary>Monitors the status of a node.</fsummary>
+ <fsummary>Monitor the status of a node.</fsummary>
<desc>
- <p>Monitors the status of the node <c><anno>Node</anno></c>.
+ <p>Monitor the status of the node <c><anno>Node</anno></c>.
If <c><anno>Flag</anno></c>
is <c>true</c>, monitoring is turned on. If <c><anno>Flag</anno></c>
is <c>false</c>, monitoring is turned off.</p>
@@ -3115,23 +3230,23 @@ os_prompt% </pre>
<func>
<name name="monitor_node" arity="3"/>
- <fsummary>Monitors the status of a node.</fsummary>
+ <fsummary>Monitor the status of a node.</fsummary>
<desc>
<p>Behaves as
- <seealso marker="#monitor_node/2">monitor_node/2</seealso>
+ <seealso marker="#monitor_node/2"><c>monitor_node/2</c></seealso>
except that it allows an
- extra option to be given, namely <c>allow_passive_connect</c>.
+ extra option to be specified, namely <c>allow_passive_connect</c>.
This option allows the BIF to wait the normal network connection
time-out for the <em>monitored node</em> to connect itself,
even if it cannot be actively connected from this node
(that is, it is blocked). The state where this can be useful
- can only be achieved by using the <c>Kernel</c> option
+ can only be achieved by using the Kernel option
<c>dist_auto_connect once</c>. If that option is not
used, option <c>allow_passive_connect</c> has no effect.</p>
<note>
<p>Option <c>allow_passive_connect</c> is used
internally and is seldom needed in applications where the
- network topology and the <c>Kernel</c> options in effect
+ network topology and the Kernel options in effect
are known in advance.</p>
</note>
<p>Failure: <c>badarg</c> if the local node is not alive or the
@@ -3143,70 +3258,77 @@ os_prompt% </pre>
<name name="monotonic_time" arity="0"/>
<fsummary>Current Erlang monotonic time.</fsummary>
<desc>
- <p>Returns the current
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>. This
- is a monotonically increasing time since some unspecified point in
- time.</p>
-
- <note><p>This is a
- <seealso marker="time_correction#Monotonically_Increasing">monotonically increasing</seealso> time, but <em>not</em> a
- <seealso marker="time_correction#Strictly_Monotonically_Increasing">strictly monotonically increasing</seealso>
- time. That is, consecutive calls to
- <c>erlang:monotonic_time/0</c> can produce the same result.</p>
-
- <p>Different runtime system instances will use different
- unspecified points in time as base for their Erlang monotonic clocks.
- That is, it is <em>pointless</em> comparing monotonic times from
- different runtime system instances. Different runtime system instances
- may also place this unspecified point in time different relative
- runtime system start. It may be placed in the future (time at start
- is a negative value), the past (time at start is a
- positive value), or the runtime system start (time at start is
- zero). The monotonic time at runtime system start can be
- retrieved by calling
- <seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso>.</p></note>
+ <p>Returns the current
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>. This
+ is a monotonically increasing time since some unspecified point in
+ time.</p>
+ <note>
+ <p>This is a
+ <seealso marker="time_correction#Monotonically_Increasing">
+ monotonically increasing</seealso> time, but <em>not</em> a
+ <seealso marker="time_correction#Strictly_Monotonically_Increasing">
+ strictly monotonically increasing</seealso>
+ time. That is, consecutive calls to
+ <c>erlang:monotonic_time/0</c> can produce the same result.</p>
+ <p>Different runtime system instances will use different unspecified
+ points in time as base for their Erlang monotonic clocks.
+ That is, it is <em>pointless</em> comparing monotonic times from
+ different runtime system instances. Different runtime system
+ instances can also place this unspecified point in time different
+ relative runtime system start. It can be placed in the future (time
+ at start is a negative value), the past (time at start is a
+ positive value), or the runtime system start (time at start is
+ zero). The monotonic time at runtime system start can be
+ retrieved by calling
+ <seealso marker="#system_info_start_time">
+ <c>erlang:system_info(start_time)</c></seealso>.</p>
+ </note>
</desc>
</func>
+
<func>
<name name="monotonic_time" arity="1"/>
- <fsummary>Current Erlang monotonic time</fsummary>
+ <fsummary>Current Erlang monotonic time.</fsummary>
<desc>
- <p>Returns the current
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> converted
- into the <c><anno>Unit</anno></c> passed as argument.</p>
-
- <p>Same as calling
- <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso><c>,
- native, <anno>Unit</anno>)</c>
- however optimized for commonly used <c><anno>Unit</anno></c>s.</p>
+ <p>Returns the current
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> converted
+ into the <c><anno>Unit</anno></c> passed as argument.</p>
+ <p>Same as calling
+ <seealso marker="#convert_time_unit/3">
+ <c>erlang:convert_time_unit</c></seealso><c>(</c><seealso
+ marker="#monotonic_time/0">
+ <c>erlang:monotonic_time()</c></seealso><c>,
+ native, <anno>Unit</anno>)</c>,
+ however optimized for commonly used <c><anno>Unit</anno></c>s.</p>
</desc>
</func>
+
<func>
<name name="nif_error" arity="1"/>
- <fsummary>Stops execution with a given reason.</fsummary>
+ <fsummary>Stop execution with a specified reason.</fsummary>
<desc>
<p>Works exactly like
- <seealso marker="#error/1">erlang:error/1</seealso>, but
- <c>Dialyzer</c> thinks that this BIF will return an arbitrary
- term. When used in a stub function for a NIF to generate an
- exception when the NIF library is not loaded, <c>Dialyzer</c>
- does not generate false warnings.</p>
+ <seealso marker="#error/1"><c>error/1</c></seealso>, but
+ Dialyzer thinks that this BIF will return an arbitrary
+ term. When used in a stub function for a NIF to generate an
+ exception when the NIF library is not loaded, Dialyzer
+ does not generate false warnings.</p>
</desc>
</func>
<func>
<name name="nif_error" arity="2"/>
- <fsummary>Stops execution with a given reason.</fsummary>
+ <fsummary>Stop execution with a specified reason.</fsummary>
<desc>
<p>Works exactly like
- <seealso marker="#error/2">erlang:error/2</seealso>, but
- <c>Dialyzer</c> thinks that this BIF will return an arbitrary
- term. When used in a stub function for a NIF to generate an
- exception when the NIF library is not loaded, <c>Dialyzer</c>
- does not generate false warnings.</p>
+ <seealso marker="#error/2"><c>error/2</c></seealso>, but
+ Dialyzer thinks that this BIF will return an arbitrary
+ term. When used in a stub function for a NIF to generate an
+ exception when the NIF library is not loaded, Dialyzer
+ does not generate false warnings.</p>
</desc>
</func>
@@ -3246,10 +3368,10 @@ os_prompt% </pre>
<name name="nodes" arity="1"/>
<fsummary>All nodes of a certain type in the system.</fsummary>
<desc>
- <p>Returns a list of nodes according to the argument given.
- The returned result when the argument is a list, is the list
+ <p>Returns a list of nodes according to the argument specified.
+ The returned result, when the argument is a list, is the list
of nodes satisfying the disjunction(s) of the list elements.</p>
- <p><c><anno>NodeType</anno></c> can be any of the following:</p>
+ <p><c><anno>NodeType</anno></c>s:</p>
<taglist>
<tag><c>visible</c></tag>
<item>
@@ -3270,13 +3392,13 @@ os_prompt% </pre>
<tag><c>known</c></tag>
<item>
<p>Nodes that are known to this node. That is, connected
- nodes and nodes referred to by process identifiers, port
- identifiers and references located on this node.
- The set of known nodes is garbage collected. Notice that
- this garbage collection can be delayed. For more
- information, see
- <seealso marker="erlang#system_info_delayed_node_table_gc">delayed_node_table_gc</seealso>.
- </p>
+ nodes and nodes referred to by process identifiers, port
+ identifiers, and references located on this node.
+ The set of known nodes is garbage collected. Notice that
+ this garbage collection can be delayed. For more
+ information, see
+ <seealso marker="erlang#system_info_delayed_node_table_gc">
+ <c>erlang:system_info(delayed_node_table_gc)</c></seealso>.</p>
</item>
</taglist>
<p>Some equalities: <c>[node()] = nodes(this)</c>,
@@ -3290,20 +3412,22 @@ os_prompt% </pre>
<fsummary>Elapsed time since 00:00 GMT.</fsummary>
<type name="timestamp"/>
<desc>
- <warning><p><em>This function is deprecated! Do not use it!</em>
- See the users guide chapter
- <seealso marker="time_correction">Time and Time Correction</seealso>
- for more information. Specifically the
- <seealso marker="time_correction#Dos_and_Donts">Dos and Dont's</seealso>
- section for information on what to use instead of <c>erlang:now/0</c>.
- </p></warning>
- <p>Returns the tuple <c>{MegaSecs, Secs, MicroSecs}</c> which is
+ <warning>
+ <p><em>This function is deprecated. Do not use it.</em></p>
+ <p>For more information, see section
+ <seealso marker="time_correction">Time and Time Correction</seealso>
+ in the User's Guide. Specifically, section
+ <seealso marker="time_correction#Dos_and_Donts">
+ Dos and Dont's</seealso> describes what to use instead of
+ <c>erlang:now/0</c>.</p>
+ </warning>
+ <p>Returns the tuple <c>{MegaSecs, Secs, MicroSecs}</c>, which is
the elapsed time since 00:00 GMT, January 1, 1970 (zero hour),
- on the assumption that the underlying OS supports this.
+ if provided by the underlying OS.
Otherwise some other point in time is chosen. It is also
- guaranteed that subsequent calls to this BIF return
+ guaranteed that the following calls to this BIF return
continuously increasing values. Hence, the return value from
- <c>now()</c> can be used to generate unique time-stamps.
+ <c>erlang:now/0</c> can be used to generate unique time stamps.
If it is called in a tight loop on a fast machine,
the time of the node can become skewed.</p>
<p>Can only be used to check the local time of day if
@@ -3314,28 +3438,31 @@ os_prompt% </pre>
<func>
<name name="open_port" arity="2"/>
- <fsummary>Opens a port.</fsummary>
+ <fsummary>Open a port.</fsummary>
<desc>
<p>Returns a port identifier as the result of opening a
new Erlang port. A port can be seen as an external Erlang
process.</p>
- <p>The name of the executable as well as the arguments
- given in <c>cd</c>, <c>env</c>, <c>args</c>, and <c>arg0</c> are
- subject to Unicode file name translation if the system is running
- in Unicode file name mode. To avoid
- translation or to force, for example UTF-8, supply the executable
- and/or arguments as a binary in the correct
- encoding. For details, see the module
- <seealso marker="kernel:file">file</seealso>, the function
- <seealso marker="kernel:file#native_name_encoding/0">file:native_name_encoding/0</seealso>, and the
- <seealso marker="stdlib:unicode_usage">STDLIB </seealso>
- User's Guide.</p>
- <note><p>The characters in the name (if given as a list) can
- only be higher than 255 if the Erlang Virtual Machine is started
- in Unicode file name translation mode. Otherwise the name
- of the executable is limited to the ISO-latin-1
- character set.</p></note>
- <p><c><anno>PortName</anno></c> can be any of the following:</p>
+ <p>The name of the executable as well as the arguments
+ specifed in <c>cd</c>, <c>env</c>, <c>args</c>, and <c>arg0</c> are
+ subject to Unicode filename translation if the system is running
+ in Unicode filename mode. To avoid
+ translation or to force, for example UTF-8, supply the executable
+ and/or arguments as a binary in the correct
+ encoding. For details, see the module
+ <seealso marker="kernel:file"><c>file(3)</c></seealso>, the
+ function <seealso marker="kernel:file#native_name_encoding/0">
+ <c>file:native_name_encoding/0</c></seealso> in Kernel, and
+ the <seealso marker="stdlib:unicode_usage">
+ <c>Using Unicode in Erlang</c></seealso> User's Guide.</p>
+ <note>
+ <p>The characters in the name (if specified as a list) can
+ only be &gt; 255 if the Erlang virtual machine is started
+ in Unicode filename translation mode. Otherwise the name
+ of the executable is limited to the ISO Latin-1
+ character set.</p>
+ </note>
+ <p><c><anno>PortName</anno></c>s:</p>
<taglist>
<tag><c>{spawn, <anno>Command</anno>}</c></tag>
<item>
@@ -3354,55 +3481,57 @@ os_prompt% </pre>
<c>vfork</c>, setting environment variable
<c>ERL_NO_VFORK</c> to any value causes <c>fork</c>
to be used instead.</p>
- <p>For external programs, <c>PATH</c> is searched
- (or an equivalent method is used to find programs,
- depending on OS). This is done by invoking
- the shell on certain platforms. The first space-separated
- token of the command is considered as the
- name of the executable (or driver). This (among other
- things) makes this option unsuitable for running
- programs having spaces in file names or directory names.
- If spaces in executable file names are desired, use
- <c>{spawn_executable, <anno>Command</anno>}</c> instead.</p>
+ <p>For external programs, <c>PATH</c> is searched
+ (or an equivalent method is used to find programs,
+ depending on the OS). This is done by invoking
+ the shell on certain platforms. The first space-separated
+ token of the command is considered as the
+ name of the executable (or driver). This (among other
+ things) makes this option unsuitable for running
+ programs with spaces in filenames or directory names.
+ If spaces in executable filenames are desired, use
+ <c>{spawn_executable, <anno>Command</anno>}</c> instead.</p>
</item>
<tag><c>{spawn_driver, <anno>Command</anno>}</c></tag>
<item>
- <p>Works like <c>{spawn, <anno>Command</anno>}</c>, but demands the
- first (space-separated) token of the command to be the name of a
- loaded driver. If no driver with that name is loaded, a
- <c>badarg</c> error is raised.</p>
+ <p>Works like <c>{spawn, <anno>Command</anno>}</c>, but demands
+ the first (space-separated) token of the command to be the name
+ of a loaded driver. If no driver with that name is loaded, a
+ <c>badarg</c> error is raised.</p>
</item>
<tag><c>{spawn_executable, <anno>FileName</anno>}</c></tag>
<item>
- <p>Works like <c>{spawn, <anno>FileName</anno>}</c>, but only runs
- external executables. <c><anno>FileName</anno></c> in its whole
- is used as the name of the executable, including any spaces.
- If arguments are to be passed, the <c><anno>PortSettings</anno></c>
- <c>args</c> and <c>arg0</c> can be used.</p>
- <p>The shell is usually not invoked to start the
- program, it is executed directly. <c>PATH</c> (or
+ <p>Works like <c>{spawn, <anno>FileName</anno>}</c>, but only runs
+ external executables. <c><anno>FileName</anno></c> in its whole
+ is used as the name of the executable, including any spaces.
+ If arguments are to be passed, the
+ <c><anno>PortSettings</anno></c>
+ <c>args</c> and <c>arg0</c> can be used.</p>
+ <p>The shell is usually not invoked to start the
+ program, it is executed directly. <c>PATH</c> (or
equivalent) is not searched. To find a program
- in <c>PATH</c> to execute, use
- <seealso marker="kernel:os#find_executable/1">os:find_executable/1</seealso>.</p>
- <p>Only if a shell script or <c>.bat</c> file is
- executed, the appropriate command interpreter is
- invoked implicitly, but there is still no
- command argument expansion or implicit <c>PATH</c> search.</p>
- <p>If <c><anno>FileName</anno></c> cannot be run, an error
- exception is raised, with the POSIX error code as the reason.
- The error reason can differ between OSs.
- Typically the error <c>enoent</c> is raised when an
- attempt is made to run a program that is not found and
- <c>eacces</c> is raised when the given file is not
- executable.</p>
+ in <c>PATH</c> to execute, use
+ <seealso marker="kernel:os#find_executable/1">
+ <c>os:find_executable/1</c></seealso>.</p>
+ <p>Only if a shell script or <c>.bat</c> file is
+ executed, the appropriate command interpreter is
+ invoked implicitly, but there is still no
+ command-argument expansion or implicit <c>PATH</c> search.</p>
+ <p>If <c><anno>FileName</anno></c> cannot be run, an error
+ exception is raised, with the POSIX error code as the reason.
+ The error reason can differ between OSs.
+ Typically the error <c>enoent</c> is raised when an
+ attempt is made to run a program that is not found and
+ <c>eacces</c> is raised when the specified file is not
+ executable.</p>
</item>
<tag><c>{fd, <anno>In</anno>, <anno>Out</anno>}</c></tag>
<item>
<p>Allows an Erlang process to access any currently opened
file descriptors used by Erlang. The file descriptor
- <c><anno>In</anno></c> can be used for standard input, and the file
- descriptor <c><anno>Out</anno></c> for standard output. It is only
- used for various servers in the Erlang OS (<c>shell</c>
+ <c><anno>In</anno></c> can be used for standard input, and the
+ file descriptor <c><anno>Out</anno></c> for standard output.
+ It is only used for various servers in the Erlang OS (<c>shell</c>
and <c>user</c>). Hence, its use is limited.</p>
</item>
</taglist>
@@ -3411,7 +3540,8 @@ os_prompt% </pre>
<taglist>
<tag><c>{packet, <anno>N</anno>}</c></tag>
<item>
- <p>Messages are preceded by their length, sent in <c><anno>N</anno></c>
+ <p>Messages are preceded by their length, sent in
+ <c><anno>N</anno></c>
bytes, with the most significant byte first. The valid values
for <c>N</c> are 1, 2, and 4.</p>
</item>
@@ -3424,16 +3554,16 @@ os_prompt% </pre>
<tag><c>{line, <anno>L</anno>}</c></tag>
<item>
<p>Messages are delivered on a per line basis. Each line
- (delimited by the OS-dependent new line sequence) is
+ (delimited by the OS-dependent newline sequence) is
delivered in a single message. The message data format
is <c>{Flag, Line}</c>, where <c>Flag</c> is
<c>eol</c> or <c>noeol</c>, and <c>Line</c> is the
- data delivered (without the new line sequence).</p>
+ data delivered (without the newline sequence).</p>
<p><c><anno>L</anno></c> specifies the maximum line length in bytes.
Lines longer than this are delivered in more than one
message, with <c>Flag</c> set to <c>noeol</c> for all
but the last message. If end of file is encountered
- anywhere else than immediately following a new line
+ anywhere else than immediately following a newline
sequence, the last line is also delivered with
<c>Flag</c> set to <c>noeol</c>. Otherwise
lines are delivered with <c>Flag</c> set to <c>eol</c>.</p>
@@ -3443,14 +3573,14 @@ os_prompt% </pre>
<tag><c>{cd, <anno>Dir</anno>}</c></tag>
<item>
<p>Only valid for <c>{spawn, <anno>Command</anno>}</c> and
- <c>{spawn_executable, <anno>FileName</anno>}</c>.
+ <c>{spawn_executable, <anno>FileName</anno>}</c>.
The external program starts using <c><anno>Dir</anno></c> as its
working directory. <c><anno>Dir</anno></c> must be a string.</p>
</item>
<tag><c>{env, <anno>Env</anno>}</c></tag>
<item>
- <p>Only valid for <c>{spawn, <anno>Command</anno>}</c> and
- <c>{spawn_executable, <anno>FileName</anno>}</c>.
+ <p>Only valid for <c>{spawn, <anno>Command</anno>}</c>, and
+ <c>{spawn_executable, <anno>FileName</anno>}</c>.
The environment of the started process is extended using
the environment specifications in <c><anno>Env</anno></c>.</p>
<p><c><anno>Env</anno></c> is to be a list of tuples
@@ -3461,56 +3591,58 @@ os_prompt% </pre>
port process. Both <c><anno>Name</anno></c> and
<c><anno>Val</anno></c> must be strings. The one
exception is <c><anno>Val</anno></c> being the atom
- <c>false</c> (in analogy with <c>os:getenv/1</c>), which
- removes the environment variable.</p>
- </item>
- <tag><c>{args, [ string() | binary() ]}</c></tag>
- <item>
- <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
- and specifies arguments to the executable. Each argument
- is given as a separate string and (on Unix) eventually
- ends up as one element each in the argument vector. On
- other platforms, a similar behavior is mimicked.</p>
- <p>The arguments are not expanded by the shell before
- being supplied to the executable. Most notably this
- means that file wild card expansion does not happen.
- To expand wild cards for the arguments, use
- <seealso marker="stdlib:filelib#wildcard/1">filelib:wildcard/1</seealso>.
- Notice that even if
- the program is a Unix shell script, meaning that the
- shell ultimately is invoked, wild card expansion
- does not happen, and the script is provided with the
- untouched arguments. On Windows, wild card expansion
- is always up to the program itself, therefore this is
- not an issue issue.</p>
- <p>The executable name (also known as <c>argv[0]</c>)
- is not to be given in this list. The proper executable name
- is automatically used as argv[0], where applicable.</p>
- <p>If you explicitly want to set the
- program name in the argument vector, option <c>arg0</c>
- can be used.</p>
- </item>
- <tag><c>{arg0, string() | binary()}</c></tag>
- <item>
- <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
- and explicitly specifies the program name argument when
- running an executable. This can in some circumstances,
- on some OSs, be desirable. How the program
- responds to this is highly system-dependent and no specific
- effect is guaranteed.</p>
- </item>
+ <c>false</c> (in analogy with
+ <seealso marker="kernel:os#getenv/1"><c>os:getenv/1</c></seealso>,
+ which removes the environment variable.</p>
+ </item>
+ <tag><c>{args, [ string() | binary() ]}</c></tag>
+ <item>
+ <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
+ and specifies arguments to the executable. Each argument
+ is specified as a separate string and (on Unix) eventually
+ ends up as one element each in the argument vector. On
+ other platforms, a similar behavior is mimicked.</p>
+ <p>The arguments are not expanded by the shell before
+ they are supplied to the executable. Most notably this
+ means that file wildcard expansion does not occur.
+ To expand wildcards for the arguments, use
+ <seealso marker="stdlib:filelib#wildcard/1">
+ <c>filelib:wildcard/1</c></seealso>.
+ Notice that even if
+ the program is a Unix shell script, meaning that the
+ shell ultimately is invoked, wildcard expansion
+ does not occur, and the script is provided with the
+ untouched arguments. On Windows, wildcard expansion
+ is always up to the program itself, therefore this is
+ not an issue.</p>
+ <p>The executable name (also known as <c>argv[0]</c>)
+ is not to be specified in this list. The proper executable name
+ is automatically used as <c>argv[0]</c>, where applicable.</p>
+ <p>If you explicitly want to set the
+ program name in the argument vector, option <c>arg0</c>
+ can be used.</p>
+ </item>
+ <tag><c>{arg0, string() | binary()}</c></tag>
+ <item>
+ <p>Only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
+ and explicitly specifies the program name argument when
+ running an executable. This can in some circumstances,
+ on some OSs, be desirable. How the program
+ responds to this is highly system-dependent and no specific
+ effect is guaranteed.</p>
+ </item>
<tag><c>exit_status</c></tag>
<item>
<p>Only valid for <c>{spawn, <anno>Command</anno>}</c>, where
<c><anno>Command</anno></c> refers to an external program, and
- for <c>{spawn_executable, <anno>FileName</anno>}</c>.</p>
+ for <c>{spawn_executable, <anno>FileName</anno>}</c>.</p>
<p>When the external process connected to the port exits, a
message of the form <c>{Port,{exit_status,Status}}</c> is
sent to the connected process, where <c>Status</c> is the
exit status of the external process. If the program
aborts on Unix, the same convention is used as the shells
do (that is, 128+signal).</p>
- <p>If option <c>eof</c> is also given, the messages <c>eof</c>
+ <p>If option <c>eof</c> is specified also, the messages <c>eof</c>
and <c>exit_status</c> appear in an unspecified order.</p>
<p>If the port program closes its <c>stdout</c> without exiting,
option <c>exit_status</c> does not work.</p>
@@ -3518,7 +3650,7 @@ os_prompt% </pre>
<tag><c>use_stdio</c></tag>
<item>
<p>Only valid for <c>{spawn, <anno>Command</anno>}</c> and
- <c>{spawn_executable, <anno>FileName</anno>}</c>. It
+ <c>{spawn_executable, <anno>FileName</anno>}</c>. It
allows the standard input and output (file descriptors 0
and 1) of the spawned (Unix) process for communication
with Erlang.</p>
@@ -3538,14 +3670,14 @@ os_prompt% </pre>
<tag><c>overlapped_io</c></tag>
<item>
<p>Affects ports to external programs on Windows only. The
- standard input and standard output handles of the port program
- are, if this option is supplied, opened with flag
- <c>FILE_FLAG_OVERLAPPED</c>, so that the port program can
- (and must) do
- overlapped I/O on its standard handles. This is not normally
- the case for simple port programs, but an option of value for the
- experienced Windows programmer. <em>On all other platforms, this
- option is silently discarded.</em></p>
+ standard input and standard output handles of the port program
+ are, if this option is supplied, opened with flag
+ <c>FILE_FLAG_OVERLAPPED</c>, so that the port program can
+ (and must) do
+ overlapped I/O on its standard handles. This is not normally
+ the case for simple port programs, but an option of value for the
+ experienced Windows programmer. <em>On all other platforms, this
+ option is silently discarded.</em></p>
</item>
<tag><c>in</c></tag>
<item>
@@ -3570,27 +3702,28 @@ os_prompt% </pre>
<tag><c>hide</c></tag>
<item>
<p>When running on Windows, suppresses creation of a new
- console window when spawning the port program.
- (This option has no effect on other platforms.)</p>
+ console window when spawning the port program.
+ (This option has no effect on other platforms.)</p>
</item>
<tag><c>{parallelism, Boolean}</c></tag>
<item>
- <marker id="open_port_parallelism"></marker>
+ <marker id="open_port_parallelism"></marker>
<p>Sets scheduler hint for port parallelism. If set to
- <c>true</c>, the Virtual Machine schedules port tasks;
- when doing so, it improves parallelism in the system. If set
- to <c>false</c>, the Virtual Machine tries to
- perform port tasks immediately, improving latency at the
- expense of parallelism. The default can be set at system startup
- by passing command-line argument
- <seealso marker="erl#+spp">+spp</seealso> to <c>erl(1)</c>.</p>
+ <c>true</c>, the virtual machine schedules port tasks;
+ when doing so, it improves parallelism in the system. If set
+ to <c>false</c>, the virtual machine tries to
+ perform port tasks immediately, improving latency at the
+ expense of parallelism. The default can be set at system startup
+ by passing command-line argument
+ <seealso marker="erl#+spp"><c>+spp</c></seealso> to
+ <c>erl(1)</c>.</p>
</item>
</taglist>
<p>Default is <c>stream</c> for all port types and
<c>use_stdio</c> for spawned ports.</p>
- <p>Failure: If the port cannot be opened, the exit reason is
- <c>badarg</c>, <c>system_limit</c>, or the POSIX error code that
- most closely describes the error, or <c>einval</c> if no POSIX
+ <p>Failure: if the port cannot be opened, the exit reason is
+ <c>badarg</c>, <c>system_limit</c>, or the POSIX error code that
+ most closely describes the error, or <c>einval</c> if no POSIX
code is appropriate:</p>
<taglist>
<tag><c>badarg</c></tag>
@@ -3616,11 +3749,11 @@ os_prompt% </pre>
<item>Full file table (for the entire OS).
</item>
<tag><c>eacces</c></tag>
- <item><c>Command</c> given in <c>{spawn_executable, Command}</c>
+ <item><c>Command</c> specified in <c>{spawn_executable, Command}</c>
does not point out an executable file.
</item>
<tag><c>enoent</c></tag>
- <item><c><anno>FileName</anno></c> given in
+ <item><c><anno>FileName</anno></c> specified in
<c>{spawn_executable, <anno>FileName</anno>}</c>
does not point out an existing file.
</item>
@@ -3630,13 +3763,12 @@ os_prompt% </pre>
errors arising when sending messages to it are reported to
the owning process using signals of the form
<c>{'EXIT', Port, PosixCode}</c>. For the possible values of
- <c>PosixCode</c>, see the
- <seealso marker="kernel:file">file(3)</seealso>
- manual page in <c>Kernel</c>.</p>
- <p>The maximum number of ports that can be open at the same
+ <c>PosixCode</c>, see
+ <seealso marker="kernel:file"><c>file(3)</c></seealso>.</p>
+ <p>The maximum number of ports that can be open at the same
time can be configured by passing command-line flag
- <seealso marker="erl#max_ports"><c>+Q</c></seealso> to
- <c>erl(1)</c>.</p>
+ <seealso marker="erl#max_ports"><c>+Q</c></seealso> to
+ <c>erl(1)</c>.</p>
</desc>
</func>
@@ -3647,11 +3779,11 @@ os_prompt% </pre>
<desc>
<p>Portable hash function that gives the same hash for
the same Erlang term regardless of machine architecture and
- <c>ERTS</c> version (the BIF was introduced in <c>ERTS</c> 4.9.1.1).
+ ERTS version (the BIF was introduced in ERTS 4.9.1.1).
The function returns a hash value for
<c><anno>Term</anno></c> within the range
<c>1..<anno>Range</anno></c>. The maximum value for
- <c><anno>Range</anno></c> is 2^32.</p>
+ <c><anno>Range</anno></c> is 2^32.</p>
<p>This BIF can be used instead of the old deprecated BIF
<c>erlang:hash/2</c>, as it calculates better hashes for
all data types, but consider using <c>phash2/1,2</c> instead.</p>
@@ -3667,11 +3799,11 @@ os_prompt% </pre>
<desc>
<p>Portable hash function that gives the same hash for
the same Erlang term regardless of machine architecture and
- <c>ERTS</c> version (the BIF was introduced in <c>ERTS</c> 5.2).
+ ERTS version (the BIF was introduced in ERTS 5.2).
The function returns a hash value for
- <c><anno>Term</anno></c> within the range
- <c>0..<anno>Range</anno>-1</c>. The maximum value for
- <c><anno>Range</anno></c> is 2^32. When without argument
+ <c><anno>Term</anno></c> within the range
+ <c>0..<anno>Range</anno>-1</c>. The maximum value for
+ <c><anno>Range</anno></c> is 2^32. When without argument
<c><anno>Range</anno></c>, a value in the range
0..2^27-1 is returned.</p>
<p>This BIF is always to be used for hashing terms. It
@@ -3691,66 +3823,109 @@ os_prompt% </pre>
representation of <c><anno>Pid</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and is not to be used
- in application programs.</p>
+ in application programs.</p>
</warning>
</desc>
</func>
<func>
+ <name name="port_call" arity="3"/>
+ <fsummary>Perform a synchronous call to a port with term data.</fsummary>
+ <desc>
+ <p>Performs a synchronous call to a port. The meaning of
+ <c><anno>Operation</anno></c> and <c><anno>Data</anno></c>
+ depends on the port, that is,
+ on the port driver. Not all port drivers support this feature.</p>
+ <p><c><anno>Port</anno></c> is a port identifier,
+ referring to a driver.</p>
+ <p><c><anno>Operation</anno></c> is an integer, which is passed on to
+ the driver.</p>
+ <p><c><anno>Data</anno></c> is any Erlang term. This data is converted
+ to binary term format and sent to the port.</p>
+ <p>Returns a term from the driver. The meaning of the returned
+ data also depends on the port driver.</p>
+ <p>Failures:</p>
+ <taglist>
+ <tag><c>badarg</c></tag>
+ <item>
+ If <c><anno>Port</anno></c> is not an identifier of an open port,
+ or the registered name of an open port. If the calling
+ process was previously linked to the closed port,
+ identified by <c><anno>Port</anno></c>, the exit signal
+ from the port is guaranteed to be delivered before this
+ <c>badarg</c> exception occurs.
+ </item>
+ <tag><c>badarg</c></tag>
+ <item>
+ If <c><anno>Operation</anno></c> does not fit in a 32-bit integer.
+ </item>
+ <tag><c>badarg</c></tag>
+ <item>
+ If the port driver does not support synchronous control operations.
+ </item>
+ <tag><c>badarg</c></tag>
+ <item>
+ If the port driver so decides for any reason (probably
+ something wrong with <c><anno>Operation</anno></c>
+ or <c><anno>Data</anno></c>).
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
<name name="port_close" arity="1"/>
- <fsummary>Closes an open port.</fsummary>
- <desc>
- <p>Closes an open port. Roughly the same as
- <c><anno>Port</anno> ! {self(), close}</c> except for the error behavior
- (see the following), being synchronous, and that the port does
- <em>not</em> reply with <c>{Port, closed}</c>. Any process can
- close a port with <c>port_close/1</c>, not only the port owner
- (the connected process). If the calling process is linked to
+ <fsummary>Close an open port.</fsummary>
+ <desc>
+ <p>Closes an open port. Roughly the same as <c><anno>Port</anno> !
+ {self(), close}</c> except for the error behavior
+ (see below), being synchronous, and that the port does
+ <em>not</em> reply with <c>{Port, closed}</c>. Any process can
+ close a port with <c>port_close/1</c>, not only the port owner
+ (the connected process). If the calling process is linked to
the port identified by <c><anno>Port</anno></c>, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_close/1</c> returns.</p>
+ signal from the port is guaranteed to be delivered before
+ <c>port_close/1</c> returns.</p>
<p>For comparison: <c><anno>Port</anno> ! {self(), close}</c>
- only fails with <c>badarg</c> if <c><anno>Port</anno></c> does
- not refer to a port or a process. If <c><anno>Port</anno></c>
- is a closed port, nothing happens. If <c><anno>Port</anno></c>
+ only fails with <c>badarg</c> if <c><anno>Port</anno></c> does
+ not refer to a port or a process. If <c><anno>Port</anno></c>
+ is a closed port, nothing happens. If <c><anno>Port</anno></c>
is an open port and the calling process is the port owner,
- the port replies with <c>{Port, closed}</c> when all buffers
- have been flushed and the port really closes. If the calling
- process is not the port owner, the <em>port owner</em> fails
- with <c>badsig</c>.</p>
+ the port replies with <c>{Port, closed}</c> when all buffers
+ have been flushed and the port really closes. If the calling
+ process is not the port owner, the <em>port owner</em> fails
+ with <c>badsig</c>.</p>
<p>Notice that any process can close a port using
<c><anno>Port</anno> ! {PortOwner, close}</c> as if it itself was
the port owner, but the reply always goes to the port owner.</p>
- <p>As from OTP R16, <c><anno>Port</anno> ! {PortOwner, close}</c> is truly
- asynchronous. Notice that this operation has always been
- documented as an asynchronous operation, while the underlying
- implementation has been synchronous. <c>port_close/1</c> is
- however still fully synchronous. This because of its error
- behavior.</p>
- <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an identifier
- of an open port, or the registered name of an open port.
- If the calling process was previously linked to the closed
- port, identified by <c><anno>Port</anno></c>, the exit
- signal from the port is guaranteed to be delivered before
- this <c>badarg</c> exception occurs.</p>
+ <p>As from Erlang/OTP R16,
+ <c><anno>Port</anno> ! {PortOwner, close}</c> is truly
+ asynchronous. Notice that this operation has always been
+ documented as an asynchronous operation, while the underlying
+ implementation has been synchronous. <c>port_close/1</c> is
+ however still fully synchronous because of its error behavior.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an
+ identifier of an open port, or the registered name of an open port.
+ If the calling process was previously linked to the closed
+ port, identified by <c><anno>Port</anno></c>, the exit
+ signal from the port is guaranteed to be delivered before
+ this <c>badarg</c> exception occurs.</p>
</desc>
</func>
<func>
<name name="port_command" arity="2"/>
- <fsummary>Sends data to a port.</fsummary>
+ <fsummary>Send data to a port.</fsummary>
<desc>
<p>Sends data to a port. Same as
- <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> except
- for the error
- behavior and being synchronous (see the following). Any process
- can send data to a port with <c>port_command/2</c>, not only the
- port owner (the connected process).</p>
- <p>For comparison: <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c>
- only fails with <c>badarg</c> if <c><anno>Port</anno></c>
- does not refer to a port or a process. If
- <c><anno>Port</anno></c> is a closed port, the data message
- disappears
+ <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c> except for
+ the error behavior and being synchronous (see below). Any process
+ can send data to a port with <c>port_command/2</c>, not only the
+ port owner (the connected process).</p>
+ <p>For comparison: <c><anno>Port</anno> ! {PortOwner, {command,
+ Data}}</c> only fails with <c>badarg</c> if <c><anno>Port</anno></c>
+ does not refer to a port or a process. If <c><anno>Port</anno></c> is
+ a closed port, the data message disappears
without a sound. If <c><anno>Port</anno></c> is open and the calling
process is not the port owner, the <em>port owner</em> fails
with <c>badsig</c>. The port owner fails with <c>badsig</c>
@@ -3758,57 +3933,58 @@ os_prompt% </pre>
<p>Notice that any process can send to a port using
<c><anno>Port</anno> ! {PortOwner, {command, <anno>Data</anno>}}</c>
as if it itself was the port owner.</p>
- <p>If the port is busy, the calling process is suspended
- until the port is not busy any more.</p>
- <p>As from OTP-R16, <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c>
- is truly asynchronous. Notice that this operation has always been
- documented as an asynchronous operation, while the underlying
- implementation has been synchronous. <c>port_command/2</c> is
- however still fully synchronous. This because of its error
- behavior.</p>
+ <p>If the port is busy, the calling process is suspended
+ until the port is not busy any more.</p>
+ <p>As from Erlang/OTP R16,
+ <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c>
+ is truly asynchronous. Notice that this operation has always been
+ documented as an asynchronous operation, while the underlying
+ implementation has been synchronous. <c>port_command/2</c> is
+ however still fully synchronous because of its error behavior.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Port</anno></c> is not an identifier of an open
- port, or the registered name of an open port. If the
- calling process was previously linked to the closed port,
- identified by <c><anno>Port</anno></c>, the exit signal
- from the port is guaranteed to be delivered before this
- <c>badarg</c> exception occurs.
- </item>
+ <p>If <c><anno>Port</anno></c> is not an identifier of an open
+ port, or the registered name of an open port. If the
+ calling process was previously linked to the closed port,
+ identified by <c><anno>Port</anno></c>, the exit signal
+ from the port is guaranteed to be delivered before this
+ <c>badarg</c> exception occurs.</p>
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Data</anno></c> is an invalid I/O list.
- </item>
- </taglist>
+ <p>If <c><anno>Data</anno></c> is an invalid I/O list.</p>
+ </item>
+ </taglist>
</desc>
</func>
<func>
<name name="port_command" arity="3"/>
- <fsummary>Sends data to a port.</fsummary>
+ <fsummary>Send data to a port.</fsummary>
<desc>
<p>Sends data to a port. <c>port_command(Port, Data, [])</c>
- equals <c>port_command(Port, Data)</c>.</p>
- <p>If the port command is aborted, <c>false</c> is returned,
- otherwise <c>true</c>.</p>
- <p>If the port is busy, the calling process is suspended
- until the port is not busy any more.</p>
- <p>The following <c><anno>Option</anno></c>s are valid:</p>
+ equals <c>port_command(Port, Data)</c>.</p>
+ <p>If the port command is aborted, <c>false</c> is returned,
+ otherwise <c>true</c>.</p>
+ <p>If the port is busy, the calling process is suspended
+ until the port is not busy anymore.</p>
+ <p><c><anno>Option</anno></c>s:</p>
<taglist>
<tag><c>force</c></tag>
<item>The calling process is not suspended if the port is
- busy, instead the port command is forced through. The
- call fails with a <c>notsup</c> exception if the
- driver of the port does not support this. For more
- information, see driver flag
- <seealso marker="driver_entry#driver_flags"><![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso>.
+ busy, instead the port command is forced through. The
+ call fails with a <c>notsup</c> exception if the
+ driver of the port does not support this. For more
+ information, see driver flag
+ <seealso marker="driver_entry#driver_flags">
+ <c>![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]</c></seealso>.
</item>
<tag><c>nosuspend</c></tag>
<item>The calling process is not suspended if the port is
- busy, instead the port command is aborted and
- <c>false</c> is returned.
+ busy, instead the port command is aborted and
+ <c>false</c> is returned.
</item>
</taglist>
<note>
@@ -3818,34 +3994,34 @@ os_prompt% </pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Port</anno></c> is not an identifier of an open
- port, or the registered name of an open port. If the
- calling process was previously linked to the closed port,
- identified by <c><anno>Port</anno></c>, the exit signal
- from the port is guaranteed to be delivered before this
- <c>badarg</c> exception occurs.
- </item>
+ If <c><anno>Port</anno></c> is not an identifier of an open
+ port, or the registered name of an open port. If the
+ calling process was previously linked to the closed port,
+ identified by <c><anno>Port</anno></c>, the exit signal
+ from the port is guaranteed to be delivered before this
+ <c>badarg</c> exception occurs.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Data</anno></c> is an invalid I/O list.
- </item>
+ If <c><anno>Data</anno></c> is an invalid I/O list.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>OptionList</anno></c> is an invalid option list.
- </item>
+ If <c><anno>OptionList</anno></c> is an invalid option list.
+ </item>
<tag><c>notsup</c></tag>
<item>
- If option <c>force</c> has been passed, but the
- driver of the port does not allow forcing through
- a busy port.
- </item>
- </taglist>
+ If option <c>force</c> has been passed, but the
+ driver of the port does not allow forcing through
+ a busy port.
+ </item>
+ </taglist>
</desc>
</func>
<func>
<name name="port_connect" arity="2"/>
- <fsummary>Sets the owner of a port.</fsummary>
+ <fsummary>Set the owner of a port.</fsummary>
<desc>
<p>Sets the port owner (the connected port) to <c><anno>Pid</anno></c>.
Roughly the same as
@@ -3853,14 +4029,14 @@ os_prompt% </pre>
except for the following:</p>
<list type="bulleted">
<item>
- <p>The error behavior differs, see the following.</p>
+ <p>The error behavior differs, see below.</p>
</item>
<item>
<p>The port does <em>not</em> reply with
<c>{Port,connected}</c>.</p>
</item>
<item>
- <p><c>port_connect/1</c> is synchronous, see the following.</p>
+ <p><c>port_connect/1</c> is synchronous, see below.</p>
</item>
<item>
<p>The new port owner gets linked to the port.</p>
@@ -3872,7 +4048,7 @@ os_prompt% </pre>
<c>port_connect/2</c>.</p>
<p>For comparison:
<c><anno>Port</anno> ! {self(), {connect, <anno>Pid</anno>}}</c>
- only fails with <c>badarg</c> if <c><anno>Port</anno></c>
+ only fails with <c>badarg</c> if <c><anno>Port</anno></c>
does not refer to a port or a process. If
<c><anno>Port</anno></c> is a closed port, nothing happens.
If <c><anno>Port</anno></c>
@@ -3882,40 +4058,39 @@ os_prompt% </pre>
the port, while the new is not. If <c><anno>Port</anno></c> is an open
port and the calling process is not the port owner,
the <em>port owner</em> fails with <c>badsig</c>. The port
- owner fails with <c>badsig</c> also if <c><anno>Pid</anno></c> is not an
- existing local process identifier.</p>
+ owner fails with <c>badsig</c> also if <c><anno>Pid</anno></c> is not
+ an existing local process identifier.</p>
<p>Notice that any process can set the port owner using
<c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c>
as if it itself was the port owner, but the reply always goes to
the port owner.</p>
- <p>As from OTP-R16,
- <c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c> is
- truly asynchronous. Notice that this operation has always been
- documented as an asynchronous operation, while the underlying
- implementation has been synchronous. <c>port_connect/2</c> is
- however still fully synchronous. This because of its error
- behavior.</p>
+ <p>As from Erlang/OTP R16,
+ <c><anno>Port</anno> ! {PortOwner, {connect, <anno>Pid</anno>}}</c>
+ is truly asynchronous. Notice that this operation has always been
+ documented as an asynchronous operation, while the underlying
+ implementation has been synchronous. <c>port_connect/2</c> is
+ however still fully synchronous because of its error behavior.</p>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Port</anno></c> is not an identifier of an open port, or
- the registered name of an open port. If the calling
- process was previously linked to the closed port,
- identified by <c><anno>Port</anno></c>, the exit signal
- from the port is guaranteed to be delivered before this
- <c>badarg</c> exception occurs.
- </item>
+ If <c><anno>Port</anno></c> is not an identifier of an open port,
+ or the registered name of an open port. If the calling
+ process was previously linked to the closed port,
+ identified by <c><anno>Port</anno></c>, the exit signal
+ from the port is guaranteed to be delivered before this
+ <c>badarg</c> exception occurs.
+ </item>
<tag><c>badarg</c></tag>
- <item>If process identified by <c>Pid</c> is not an existing
- local process.</item>
- </taglist>
+ <item>If the process identified by <c>Pid</c> is not an existing
+ local process.</item>
+ </taglist>
</desc>
</func>
<func>
<name name="port_control" arity="3"/>
- <fsummary>Performs a synchronous control operation on a port.</fsummary>
+ <fsummary>Perform a synchronous control operation on a port.</fsummary>
<desc>
<p>Performs a synchronous control operation on a port.
The meaning of <c><anno>Operation</anno></c> and
@@ -3929,71 +4104,24 @@ os_prompt% </pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Port</anno></c> is not an open port or the registered
- name of an open port.
- </item>
+ If <c><anno>Port</anno></c> is not an open port or the registered
+ name of an open port.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Operation</anno></c> cannot fit in a 32-bit integer.
- </item>
+ If <c><anno>Operation</anno></c> cannot fit in a 32-bit integer.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the port driver does not support synchronous control
- operations.
- </item>
+ If the port driver does not support synchronous control operations.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the port driver so decides for any reason (probably
+ If the port driver so decides for any reason (probably
something wrong with <c><anno>Operation</anno></c> or
<c><anno>Data</anno></c>).
- </item>
- </taglist>
- </desc>
- </func>
-
- <func>
- <name name="port_call" arity="3"/>
- <fsummary>Performs a synchronous call to a port with term data.</fsummary>
- <desc>
- <p>Performs a synchronous call to a port. The meaning of
- <c><anno>Operation</anno></c> and <c><anno>Data</anno></c>
- depends on the port, that is,
- on the port driver. Not all port drivers support this feature.</p>
- <p><c><anno>Port</anno></c> is a port identifier,
- referring to a driver.</p>
- <p><c><anno>Operation</anno></c> is an integer, which is passed on to
- the driver.</p>
- <p><c><anno>Data</anno></c> is any Erlang term. This data is converted
- to binary term format and sent to the port.</p>
- <p>Returns a term from the driver. The meaning of the returned
- data also depends on the port driver.</p>
- <p>Failures:</p>
- <taglist>
- <tag><c>badarg</c></tag>
- <item>
- If <c><anno>Port</anno></c> is not an identifier of an open port,
- or the registered name of an open port. If the calling
- process was previously linked to the closed port,
- identified by <c><anno>Port</anno></c>, the exit signal
- from the port is guaranteed to be delivered before this
- <c>badarg</c> exception occurs.
- </item>
- <tag><c>badarg</c></tag>
- <item>
- If <c><anno>Operation</anno></c> does not fit in a 32-bit integer.
- </item>
- <tag><c>badarg</c></tag>
- <item>
- If the port driver does not support synchronous control
- operations.
- </item>
- <tag><c>badarg</c></tag>
- <item>
- If the port driver so decides for any reason (probably
- something wrong with <c><anno>Operation</anno></c>
- or <c><anno>Data</anno></c>).
- </item>
- </taglist>
+ </item>
+ </taglist>
</desc>
</func>
@@ -4005,11 +4133,11 @@ os_prompt% </pre>
<c><anno>Port</anno></c>, or <c>undefined</c> if the port is not open.
The order of the tuples is undefined, and all the
tuples are not mandatory.
- If the port is closed and the calling process
- was previously linked to the port, the exit signal from the
- port is guaranteed to be delivered before <c>port_info/1</c>
- returns <c>undefined</c>.</p>
- <p>The result contains information about the following
+ If the port is closed and the calling process
+ was previously linked to the port, the exit signal from the
+ port is guaranteed to be delivered before <c>port_info/1</c>
+ returns <c>undefined</c>.</p>
+ <p>The result contains information about the following
<c>Item</c>s:</p>
<list type="bulleted">
<item><c>registered_name</c> (if the port has a registered
@@ -4022,9 +4150,9 @@ os_prompt% </pre>
<item><c>output</c></item>
</list>
<p>For more information about the different <c>Item</c>s, see
- <seealso marker="#port_info/2">port_info/2</seealso>.</p>
+ <seealso marker="#port_info/2"><c>port_info/2</c></seealso>.</p>
<p>Failure: <c>badarg</c> if <c>Port</c> is not a local port
- identifier, or an atom.</p>
+ identifier, or an atom.</p>
</desc>
</func>
@@ -4032,15 +4160,15 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="1"/>
<fsummary>Information about the connected process of a port.</fsummary>
<desc>
- <p><c><anno>Pid</anno></c> is the process identifier of the process
- connected to the port.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Pid</anno></c> is the process identifier of the process
+ connected to the port.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4048,15 +4176,15 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="2"/>
<fsummary>Information about the internal index of a port.</fsummary>
<desc>
- <p><c><anno>Index</anno></c> is the internal index of the port. This
- index can be used to separate ports.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Index</anno></c> is the internal index of the port. This
+ index can be used to separate ports.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4064,15 +4192,15 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="3"/>
<fsummary>Information about the input of a port.</fsummary>
<desc>
- <p><c><anno>Bytes</anno></c> is the total number of bytes
- read from the port.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Bytes</anno></c> is the total number of bytes
+ read from the port.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4080,15 +4208,15 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="4"/>
<fsummary>Information about the links of a port.</fsummary>
<desc>
- <p><c><anno>Pids</anno></c> is a list of the process identifiers
- of the processes that the port is linked to.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Pids</anno></c> is a list of the process identifiers
+ of the processes that the port is linked to.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4096,7 +4224,7 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="5"/>
<fsummary>Information about the locking of a port.</fsummary>
<desc>
- <p><c><anno>Locking</anno></c> is one of the following:</p>
+ <p><c><anno>Locking</anno></c> is one of the following:</p>
<list type="bulleted">
<item><c>false</c> (emulator without SMP support)</item>
<item><c>port_level</c> (port-specific locking)</item>
@@ -4104,13 +4232,13 @@ os_prompt% </pre>
</list>
<p>Notice that these results are highly implementation-specific
and can change in a future release.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4118,17 +4246,17 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="6"/>
<fsummary>Information about the memory size of a port.</fsummary>
<desc>
- <p><c><anno>Bytes</anno></c> is the total number of
- bytes allocated for this port by the runtime system. The
- port itself can have allocated memory that is not
- included in <c><anno>Bytes</anno></c>.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Bytes</anno></c> is the total number of
+ bytes allocated for this port by the runtime system. The
+ port itself can have allocated memory that is not
+ included in <c><anno>Bytes</anno></c>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4136,15 +4264,15 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="7"/>
<fsummary>Information about the monitors of a port.</fsummary>
<desc>
- <p><c><anno>Monitors</anno></c> represent processes that this port
- monitors.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Monitors</anno></c> represent processes monitored by
+ this port.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4168,15 +4296,15 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="9"/>
<fsummary>Information about the name of a port.</fsummary>
<desc>
- <p><c><anno>Name</anno></c> is the command name set by
- <seealso marker="#open_port/2">open_port/2</seealso>.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Name</anno></c> is the command name set by
+ <seealso marker="#open_port/2"><c>open_port/2</c></seealso>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4184,18 +4312,18 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="10"/>
<fsummary>Information about the OS pid of a port.</fsummary>
<desc>
- <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent)
- of an OS process created with
- <seealso marker="#open_port/2">open_port({spawn | spawn_executable,
- Command}, Options)</seealso>. If the port is not the result of spawning
- an OS process, the value is <c>undefined</c>.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent)
+ of an OS process created with
+ <seealso marker="#open_port/2"><c>open_port({spawn | spawn_executable,
+ Command}, Options)</c></seealso>. If the port is not the result of
+ spawning an OS process, the value is <c>undefined</c>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4203,18 +4331,18 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="11"/>
<fsummary>Information about the output of a port.</fsummary>
<desc>
- <p><c><anno>Bytes</anno></c> is the total number of bytes written
- to the port from Erlang processes using
- <seealso marker="#port_command/2">port_command/2</seealso>,
- <seealso marker="#port_command/3">port_command/3</seealso>,
- or <c><anno>Port</anno> ! {Owner, {command, Data}</c>.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Bytes</anno></c> is the total number of bytes written
+ to the port from Erlang processes using
+ <seealso marker="#port_command/2"><c>port_command/2</c></seealso>,
+ <seealso marker="#port_command/3"><c>port_command/3</c></seealso>,
+ or <c><anno>Port</anno> ! {Owner, {command, Data}</c>.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4222,10 +4350,10 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="12"/>
<fsummary>Information about the parallelism hint of a port.</fsummary>
<desc>
- <p><c><anno>Boolean</anno></c> corresponds to the port parallelism
- hint being used by this port. For more information, see option
- <seealso marker="#open_port_parallelism">parallelism</seealso>
- of <seealso marker="#open_port/2">open_port/2</seealso>.</p>
+ <p><c><anno>Boolean</anno></c> corresponds to the port parallelism
+ hint used by this port. For more information, see option
+ <seealso marker="#open_port_parallelism"><c>parallelism</c></seealso>
+ of <seealso marker="#open_port/2"><c>open_port/2</c></seealso>.</p>
</desc>
</func>
@@ -4233,16 +4361,16 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="13"/>
<fsummary>Information about the queue size of a port.</fsummary>
<desc>
- <p><c><anno>Bytes</anno></c> is the total number
- of bytes queued by the port using the <c>ERTS</c> driver queue
- implementation.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>Bytes</anno></c> is the total number
+ of bytes queued by the port using the ERTS driver queue
+ implementation.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4250,15 +4378,16 @@ os_prompt% </pre>
<name name="port_info" arity="2" clause_i="14"/>
<fsummary>Information about the registered name of a port.</fsummary>
<desc>
- <p><c><anno>RegisteredName</anno></c> is the registered name of
- the port. If the port has no registered name, <c>[]</c> is returned.</p>
- <p>If the port identified by <c><anno>Port</anno></c> is not open,
- <c>undefined</c> is returned. If the port is closed and the
- calling process was previously linked to the port, the exit
- signal from the port is guaranteed to be delivered before
- <c>port_info/2</c> returns <c>undefined</c>.</p>
+ <p><c><anno>RegisteredName</anno></c> is the registered name of
+ the port. If the port has no registered name, <c>[]</c> is
+ returned.</p>
+ <p>If the port identified by <c><anno>Port</anno></c> is not open,
+ <c>undefined</c> is returned. If the port is closed and the
+ calling process was previously linked to the port, the exit
+ signal from the port is guaranteed to be delivered before
+ <c>port_info/2</c> returns <c>undefined</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local
- port identifier, or an atom.</p>
+ port identifier, or an atom.</p>
</desc>
</func>
@@ -4270,24 +4399,24 @@ os_prompt% </pre>
representation of the port identifier <c><anno>Port</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging. It is not to be used
- in application programs.</p>
+ in application programs.</p>
</warning>
</desc>
</func>
<func>
<name name="ports" arity="0"/>
- <fsummary>Lists all existing ports.</fsummary>
+ <fsummary>List all existing ports.</fsummary>
<desc>
- <p>Returns a list of port identifiers corresponding to all the
- ports existing on the local node.</p>
- <p>Notice that an exiting port exists, but is not open.</p>
+ <p>Returns a list of port identifiers corresponding to all the
+ ports existing on the local node.</p>
+ <p>Notice that an exiting port exists, but is not open.</p>
</desc>
</func>
<func>
<name name="pre_loaded" arity="0"/>
- <fsummary>Lists all pre-loaded modules.</fsummary>
+ <fsummary>List all preloaded modules.</fsummary>
<desc>
<p>Returns a list of Erlang modules that are preloaded in
the system. As all loading of code is done through the file
@@ -4298,12 +4427,13 @@ os_prompt% </pre>
<func>
<name name="process_display" arity="2"/>
- <fsummary>Writes information about a local process on standard error.</fsummary>
+ <fsummary>Write information about a local process on standard error.
+ </fsummary>
<desc>
<p>Writes information about the local process <c><anno>Pid</anno></c> on
standard error. The only allowed value for the atom
- <c><anno>Type</anno></c> is <c>backtrace</c>, which shows the contents of
- the call stack, including information about the call chain, with
+ <c><anno>Type</anno></c> is <c>backtrace</c>, which shows the contents
+ of the call stack, including information about the call chain, with
the current function printed first. The format of the output
is not further defined.</p>
</desc>
@@ -4311,7 +4441,7 @@ os_prompt% </pre>
<func>
<name name="process_flag" arity="2" clause_i="1"/>
- <fsummary>Sets process flag <c>trap_exit</c> for the calling process.</fsummary>
+ <fsummary>Set process flag trap_exit for the calling process.</fsummary>
<desc>
<p>When <c>trap_exit</c> is set to <c>true</c>, exit signals
arriving to a process are converted to <c>{'EXIT', From, Reason}</c>
@@ -4322,13 +4452,14 @@ os_prompt% </pre>
linked processes. Application processes are normally
not to trap exits.</p>
<p>Returns the old value of the flag.</p>
- <p>See also <seealso marker="#exit/2">exit/2</seealso>.</p>
+ <p>See also <seealso marker="#exit/2"><c>exit/2</c></seealso>.</p>
</desc>
</func>
<func>
<name name="process_flag" arity="2" clause_i="2"/>
- <fsummary>Sets process flag <c>error_handler</c> for the calling process.</fsummary>
+ <fsummary>Set process flag error_handler for the calling process.
+ </fsummary>
<desc>
<p>Used by a process to redefine the error handler
for undefined function calls and undefined registered
@@ -4339,11 +4470,12 @@ 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>
+ <fsummary>Set process flag min_heap_size for the calling process.
+ </fsummary>
<desc>
+ <marker id="process_flag_min_heap_size"/>
<p>Changes the minimum heap size for the calling process.</p>
<p>Returns the old value of the flag.</p>
</desc>
@@ -4351,90 +4483,79 @@ os_prompt% </pre>
<func>
<name name="process_flag" arity="2" clause_i="4"/>
- <fsummary>Sets process flag <c>min_bin_vheap_size</c> for the calling process.</fsummary>
+ <fsummary>Set process flag min_bin_vheap_size for the calling process.
+ </fsummary>
<desc>
<p>Changes the minimum binary virtual heap size for the calling
process.</p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
- <marker id="process_flag_max_heap_size"/>
+
<func>
<name name="process_flag" arity="2" clause_i="5"/>
- <fsummary>Sets process flag <c>max_heap_size</c> for the calling process.</fsummary>
+ <fsummary>Set process flag max_heap_size for the calling process.
+ </fsummary>
<type name="max_heap_size"/>
<desc>
- <p>
- This flag sets the maximum heap size for the calling process.
+ <marker id="process_flag_max_heap_size"/>
+ <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.
</p>
<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,
+ <p>The maximum size in words of the process. If set to zero, the
+ heap size limit is disabled. <c>badarg</c> is 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
+ 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>
+ 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
+ <p>When set to <c>true</c>, the runtime system sends 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
+ that triggered the <c>kill</c> is not completed, instead the
+ process exits as soon as possible. When set to <c>false</c>,
+ no exit signal is sent to the process, instead it continues
+ 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>
+ is <c>true</c>. It can be changed by either option
+ <seealso marker="erl#+hmaxk">+hmaxk</seealso> in <c>erl(1)</c>,
+ 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>
+ <p>When set to <c>true</c>, the runtime system sends 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>
+ heap size is reached. One <c>error_logger</c> report is sent
+ each time the limit is reached.</p>
+ <p>If <c>error_logger</c> is not defined in the map, the system
+ default is used. The default system default is <c>true</c>.
+ It can be changed by either the option
+ <seealso marker="erl#+hmaxel">+hmaxel</seealso> int <c>erl(1)</c>,
+ or <seealso marker="#system_flag_max_heap_size">
+ <c>erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
</item>
</taglist>
- <p>
- The heap size of a process is quite hard to predict, especially the
+ <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
@@ -4444,60 +4565,57 @@ os_prompt% </pre>
</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>
+ <fsummary>Set process flag message_queue_data for the calling process.
+ </fsummary>
<type name="message_queue_data"/>
<desc>
- <p>This flag determines how messages in the message queue
- are stored. When the flag is:</p>
+ <marker id="process_flag_message_queue_data"/>
+ <p>This flag determines how messages in the message queue
+ are stored, as follows:</p>
<taglist>
<tag><c>off_heap</c></tag>
- <item><p>
- <em>All</em> messages in the message queue will be stored
- outside of the process heap. This implies that <em>no</em>
- messages in the message queue will be part of a garbage
- collection of the process.
- </p></item>
+ <item>
+ <p><em>All</em> messages in the message queue will be stored
+ outside of the process heap. This implies that <em>no</em>
+ messages in the message queue will be part of a garbage
+ collection of the process.</p>
+ </item>
<tag><c>on_heap</c></tag>
- <item><p>
- All messages in the message queue will eventually be
- placed on heap. They may however temporarily be stored
- off heap. This is how messages always have been stored
- up until ERTS version 8.0.
- </p></item>
+ <item>
+ <p>All messages in the message queue will eventually be
+ placed on heap. They can however temporarily be stored
+ off heap. This is how messages always have been stored
+ up until ERTS 8.0.</p>
+ </item>
</taglist>
- <p>
- The default <c>message_queue_data</c> process flag is determined
- by the <seealso marker="erl#+hmqd"><c>+hmqd</c></seealso>
- <c>erl</c> command line argument.
- </p>
- <p>
- If the process potentially may get a hugh amount of messages,
- you are recommended to set the flag to <c>off_heap</c>. This
- since a garbage collection with lots of messages placed on
- the heap may become extremly expensive and the process may
- consume large amounts of memory. Performance of the
- actual message passing is however generally better when not
- using the <c>off_heap</c> flag.
- </p>
- <p>
- When changing this flag messages will be moved. This work
- has been initiated but not completed when this function
- call returns.
- </p>
+ <p>The default <c>message_queue_data</c> process flag is determined
+ by command-line argument <seealso marker="erl#+hmqd">
+ <c>+hmqd</c></seealso> in <c>erl(1)</c>.</p>
+ <p>If the process potentially can get many messages,
+ you are advised to set the flag to <c>off_heap</c>. This
+ because a garbage collection with many messages placed on
+ the heap can become extremly expensive and the process can
+ consume large amounts of memory. Performance of the
+ actual message passing is however generally better when not
+ using flag <c>off_heap</c>.</p>
+ <p>When changing this flag messages will be moved. This work
+ has been initiated but not completed when this function
+ call returns.</p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
+
<func>
<name name="process_flag" arity="2" clause_i="7"/>
- <fsummary>Sets process flag <c>priority</c> for the calling process.</fsummary>
+ <fsummary>Set process flag priority for the calling process.</fsummary>
<type name="priority_level"/>
<desc>
<p><marker id="process_flag_priority"></marker>
Sets the process priority. <c><anno>Level</anno></c> is an atom.
- There are four priority levels: <c>low</c>,
+ Four priority levels exist: <c>low</c>,
<c>normal</c>, <c>high</c>, and <c>max</c>. Default
is <c>normal</c>.</p>
<note>
@@ -4511,23 +4629,23 @@ os_prompt% </pre>
<c>low</c> are interleaved. Processes on priority
<c>low</c> are selected for execution less
frequently than processes on priority <c>normal</c>.</p>
- <p>When there are runnable processes on priority <c>high</c>,
+ <p>When runnable processes on priority <c>high</c> exist,
no processes on priority <c>low</c> or <c>normal</c> are
- selected for execution. Notice however, that this does
+ selected for execution. Notice however that this does
<em>not</em> mean that no processes on priority <c>low</c>
- or <c>normal</c> can run when there are processes
- running on priority <c>high</c>. On the runtime
+ or <c>normal</c> can run when processes
+ are running on priority <c>high</c>. On the runtime
system with SMP support, more processes can be running
- in parallel than processes on priority <c>high</c>, that is,
+ in parallel than processes on priority <c>high</c>. That is,
a <c>low</c> and a <c>high</c> priority process can
execute at the same time.</p>
- <p>When there are runnable processes on priority <c>max</c>,
+ <p>When runnable processes on priority <c>max</c> exist,
no processes on priority <c>low</c>, <c>normal</c>, or
<c>high</c> are selected for execution. As with priority
<c>high</c>, processes on lower priorities can
execute in parallel with processes on priority <c>max</c>.</p>
- <p>Scheduling is preemptive. Regardless of priority, a process
- is preempted when it has consumed more than a certain number
+ <p>Scheduling is pre-emptive. Regardless of priority, a process
+ is pre-empted when it has consumed more than a certain number
of reductions since the last time it was selected for
execution.</p>
<note>
@@ -4543,7 +4661,7 @@ os_prompt% </pre>
take this into account and handle such scenarios by
yourself.</p>
<p>Making calls from a <c>high</c> priority process into code
- that you have no control over can cause the <c>high</c>
+ that you has no control over can cause the <c>high</c>
priority process to wait for a process with lower
priority. That is, effectively decreasing the priority of the
<c>high</c> priority process during the call. Even if this
@@ -4557,8 +4675,8 @@ os_prompt% </pre>
<em>especially</em> priority <c>high</c>. A
process on priority <c>high</c> is only
to perform work for short periods. Busy looping for
- long periods in a <c>high</c> priority process does
- most likely cause problems, as important OTP servers
+ long periods in a <c>high</c> priority process causes
+ most likely problems, as important OTP servers
run on priority <c>normal</c>.</p>
<p>Returns the old value of the flag.</p>
</desc>
@@ -4566,10 +4684,10 @@ os_prompt% </pre>
<func>
<name name="process_flag" arity="2" clause_i="8"/>
- <fsummary>Sets process flag <c>save_calls</c> for the calling process.</fsummary>
+ <fsummary>Set process flag save_calls for the calling process.</fsummary>
<desc>
<p><c><anno>N</anno></c> must be an integer in the interval 0..10000.
- If <c><anno>N</anno></c> is greater than 0, call saving is made
+ If <c><anno>N</anno></c> &gt; 0, call saving is made
active for the
process. This means that information about the <c><anno>N</anno></c>
most recent global function calls, BIF calls, sends, and
@@ -4580,12 +4698,12 @@ os_prompt% </pre>
explicitly mentioned. Only a fixed amount of information
is saved, as follows:</p>
<list type="bulleted">
- <item>A tuple <c>{Module, Function, Arity}</c> for
- function calls</item>
- <item> The atoms <c>send</c>, <c>'receive'</c>, and
+ <item><p>A tuple <c>{Module, Function, Arity}</c> for
+ function calls</p></item>
+ <item><p>The atoms <c>send</c>, <c>'receive'</c>, and
<c>timeout</c> for sends and receives (<c>'receive'</c>
when a message is received and <c>timeout</c> when a
- receive times out)</item>
+ receive times out)</p></item>
</list>
<p>If <c>N</c> = 0,
call saving is disabled for the process, which is the
@@ -4597,7 +4715,7 @@ os_prompt% </pre>
<func>
<name name="process_flag" arity="2" clause_i="9"/>
- <fsummary>Sets process flag <c>sensitive</c> for the calling process.</fsummary>
+ <fsummary>Set process flag sensitive for the calling process.</fsummary>
<desc>
<p>Sets or clears flag <c>sensitive</c> for the current process.
When a process has been marked as sensitive by calling
@@ -4607,13 +4725,13 @@ os_prompt% </pre>
<p>Features that are disabled include (but are not limited to)
the following:</p>
<list type="bulleted">
- <item>Tracing: Trace flags can still be set for the process,
+ <item><p>Tracing. Trace flags can still be set for the process,
but no trace messages of any kind are generated. (If flag
<c>sensitive</c> is turned off, trace messages are again
- generated if any trace flags are set.)</item>
- <item>Sequential tracing: The sequential trace token is
+ generated if any trace flags are set.)</p></item>
+ <item><p>Sequential tracing. The sequential trace token is
propagated as usual, but no sequential trace messages are
- generated.</item>
+ generated.</p></item>
</list>
<p><c>process_info/1,2</c> cannot be used to read out the
message queue or the process dictionary (both are returned
@@ -4623,19 +4741,19 @@ os_prompt% </pre>
are omitted.</p>
<p>If <c>{save_calls,N}</c> has been set for the process, no
function calls are saved to the call saving list.
- (The call saving list is not cleared. Furthermore, send, receive,
- and timeout events are still added to the list.)</p>
+ (The call saving list is not cleared. Also, send, receive,
+ and time-out events are still added to the list.)</p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
<func>
<name name="process_flag" arity="3"/>
- <fsummary>Sets process flags for a process.</fsummary>
+ <fsummary>Set process flags for a process.</fsummary>
<desc>
<p>Sets certain flags for the process <c><anno>Pid</anno></c>,
in the same manner as
- <seealso marker="#process_flag/2">process_flag/2</seealso>.
+ <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>.
Returns the old value of the flag. The valid values for
<c><anno>Flag</anno></c> are only a subset of those allowed in
<c>process_flag/2</c>, namely <c>save_calls</c>.</p>
@@ -4650,46 +4768,46 @@ 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" />
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
<desc>
<p>Returns a list containing <c><anno>InfoTuple</anno></c>s with
- miscellaneous information about the process identified by
- <c>Pid</c>, or <c>undefined</c> if the process is not alive.</p>
- <p>The order of the <c><anno>InfoTuple</anno></c>s is undefined and
- all <c><anno>InfoTuple</anno></c>s are not mandatory.
+ miscellaneous information about the process identified by
+ <c>Pid</c>, or <c>undefined</c> if the process is not alive.</p>
+ <p>The order of the <c><anno>InfoTuple</anno></c>s is undefined and
+ all <c><anno>InfoTuple</anno></c>s are not mandatory.
The <c><anno>InfoTuple</anno></c>s
- part of the result can be changed without prior notice.</p>
- <p>The <c><anno>InfoTuple</anno></c>s with the following items
- are part of the result:</p>
+ part of the result can be changed without prior notice.</p>
+ <p>The <c><anno>InfoTuple</anno></c>s with the following items
+ are part of the result:</p>
<list type="bulleted">
<item><c>current_function</c></item>
<item><c>initial_call</c></item>
<item><c>status</c></item>
- <item><c>message_queue_len</c></item>
+ <item><c>message_queue_len</c></item>
<item><c>messages</c></item>
<item><c>links</c></item>
- <item><c>dictionary</c></item>
+ <item><c>dictionary</c></item>
<item><c>trap_exit</c></item>
<item><c>error_handler</c></item>
- <item><c>priority</c></item>
+ <item><c>priority</c></item>
<item><c>group_leader</c></item>
<item><c>total_heap_size</c></item>
- <item><c>heap_size</c></item>
+ <item><c>heap_size</c></item>
<item><c>stack_size</c></item>
<item><c>reductions</c></item>
- <item><c>garbage_collection</c></item>
+ <item><c>garbage_collection</c></item>
</list>
<p>If the process identified by <c><anno>Pid</anno></c> has a
registered name,
- also an <c><anno>InfoTuple</anno></c> with item <c>registered_name</c>
- appears.</p>
- <p>For information about specific <c><anno>InfoTuple</anno></c>s, see
- <seealso marker="#process_info/2">process_info/2</seealso>.</p>
+ also an <c><anno>InfoTuple</anno></c> with item <c>registered_name</c>
+ is included.</p>
+ <p>For information about specific <c><anno>InfoTuple</anno></c>s, see
+ <seealso marker="#process_info/2"><c>process_info/2</c></seealso>.</p>
<warning>
<p>This BIF is intended for <em>debugging only</em>. For
- all other purposes, use
- <seealso marker="#process_info/2">process_info/2</seealso>.</p>
+ all other purposes, use <seealso marker="#process_info/2">
+ <c>process_info/2</c></seealso>.</p>
</warning>
<p>Failure: <c>badarg</c> if <c><anno>Pid</anno></c> is not a
local process.</p>
@@ -4704,38 +4822,39 @@ 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" />
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
<desc>
<p>Returns information about the process identified by
- <c><anno>Pid</anno></c>, as specified by
- <c><anno>Item</anno></c> or <c><anno>ItemList</anno></c>.
- Returns <c>undefined</c> if the process is not alive.</p>
- <p>If the process is alive and a single <c><anno>Item</anno></c>
- is given, the returned value is the corresponding
- <c><anno>InfoTuple</anno></c>, unless <c>Item =:= registered_name</c>
- and the process has no registered name. In this case,
- <c>[]</c> is returned. This strange behavior is because of
- historical reasons, and is kept for backward compatibility.</p>
- <p>If <c><anno>ItemList</anno></c> is given, the result is
- <c><anno>InfoTupleList</anno></c>.
- The <c><anno>InfoTuple</anno></c>s in
- <c><anno>InfoTupleList</anno></c> appear with the corresponding
- <c><anno>Item</anno></c>s in the same order as the
- <c><anno>Item</anno></c>s appeared
- in <c><anno>ItemList</anno></c>. Valid <c><anno>Item</anno></c>s can
- appear multiple times in <c><anno>ItemList</anno></c>.</p>
- <note><p>If <c>registered_name</c> is part of <c><anno>ItemList</anno></c>
- and the process has no name registered a
- <c>{registered_name, []}</c>, <c><anno>InfoTuple</anno></c>
- <em>will</em> appear in the resulting
- <c><anno>InfoTupleList</anno></c>. This
- behavior is different when a single
- <c>Item =:= registered_name</c> is given, and when
- <c>process_info/1</c> is used.</p>
- </note>
- <p>The following <c><anno>InfoTuple</anno></c>s with corresponding
- <c><anno>Item</anno></c>s are valid:</p>
+ <c><anno>Pid</anno></c>, as specified by
+ <c><anno>Item</anno></c> or <c><anno>ItemList</anno></c>.
+ Returns <c>undefined</c> if the process is not alive.</p>
+ <p>If the process is alive and a single <c><anno>Item</anno></c>
+ is specified, the returned value is the corresponding
+ <c><anno>InfoTuple</anno></c>, unless <c>Item =:= registered_name</c>
+ and the process has no registered name. In this case,
+ <c>[]</c> is returned. This strange behavior is because of
+ historical reasons, and is kept for backward compatibility.</p>
+ <p>If <c><anno>ItemList</anno></c> is specified, the result is
+ <c><anno>InfoTupleList</anno></c>.
+ The <c><anno>InfoTuple</anno></c>s in
+ <c><anno>InfoTupleList</anno></c> are included with the corresponding
+ <c><anno>Item</anno></c>s in the same order as the
+ <c><anno>Item</anno></c>s were included
+ in <c><anno>ItemList</anno></c>. Valid <c><anno>Item</anno></c>s can
+ be included multiple times in <c><anno>ItemList</anno></c>.</p>
+ <note>
+ <p>If <c>registered_name</c> is part of <c><anno>ItemList</anno></c>
+ and the process has no name registered, a
+ <c>{registered_name, []}</c>, <c><anno>InfoTuple</anno></c>
+ <em>will</em> be included in the resulting
+ <c><anno>InfoTupleList</anno></c>. This
+ behavior is different when a single
+ <c>Item =:= registered_name</c> is specified, and when
+ <c>process_info/1</c> is used.</p>
+ </note>
+ <p>Valid <c><anno>InfoTuple</anno></c>s with corresponding
+ <c><anno>Item</anno></c>s:</p>
<taglist>
<tag><c>{backtrace, <anno>Bin</anno>}</c></tag>
<item>
@@ -4748,15 +4867,17 @@ os_prompt% </pre>
<tag><c>{binary, <anno>BinInfo</anno>}</c></tag>
<item>
<p><c><anno>BinInfo</anno></c> is a list containing miscellaneous
- information about binaries currently being referred to by this
+ information about binaries currently referred to by this
process. This <c><anno>InfoTuple</anno></c> can be changed or
- removed without prior notice.</p>
+ removed without prior notice. In the current implementation
+ <c><anno>BinInfo</anno></c> is a list of tuples. The tuples
+ contain; <c>BinaryId</c>, <c>BinarySize</c>, <c>BinaryRefcCount</c>.</p>
</item>
<tag><c>{catchlevel, <anno>CatchLevel</anno>}</c></tag>
<item>
<p><c><anno>CatchLevel</anno></c> is the number of currently active
- catches in this process. This <c><anno>InfoTuple</anno></c> can be
- changed or removed without prior notice.</p>
+ catches in this process. This <c><anno>InfoTuple</anno></c> can be
+ changed or removed without prior notice.</p>
</item>
<tag><c>{current_function, {<anno>Module</anno>,
<anno>Function</anno>, Arity}}</c></tag>
@@ -4772,14 +4893,17 @@ os_prompt% </pre>
<p><c><anno>Module</anno></c>, <c><anno>Function</anno></c>,
<c><anno>Arity</anno></c> is
the current function call of the process.
- <c><anno>Location</anno></c> is a list of two-tuples describing the
- location in the source code.</p>
+ <c><anno>Location</anno></c> is a list of two-tuples describing
+ the location in the source code.</p>
</item>
<tag><c>{current_stacktrace, <anno>Stack</anno>}</c></tag>
<item>
<p>Returns the current call stack back-trace (<em>stacktrace</em>)
of the process. The stack has the same format as returned by
- <seealso marker="#get_stacktrace/0">erlang:get_stacktrace/0</seealso>.</p>
+ <seealso marker="#get_stacktrace/0">
+ <c>erlang:get_stacktrace/0</c></seealso>. The depth of the
+ stacktrace is truncated according to the <c>backtrace_depth</c>
+ system flag setting.</p>
</item>
<tag><c>{dictionary, <anno>Dictionary</anno>}</c></tag>
<item>
@@ -4793,9 +4917,9 @@ os_prompt% </pre>
<tag><c>{garbage_collection, <anno>GCInfo</anno>}</c></tag>
<item>
<p><c><anno>GCInfo</anno></c> is a list containing miscellaneous
- information about garbage collection for this process.
- The content of <c><anno>GCInfo</anno></c> can be changed without
- prior notice.</p>
+ information about garbage collection for this process.
+ The content of <c><anno>GCInfo</anno></c> can be changed without
+ prior notice.</p>
</item>
<tag>
<marker id="process_info_garbage_collection_info"/>
@@ -4803,24 +4927,22 @@ os_prompt% </pre>
</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_minor_start">gc_minor_start</seealso> in
- <seealso marker="#trace/3">erlang:trace/3</seealso> for details about
- what each item means.
- </p>
+ detailed information about garbage collection for this process.
+ The content of <c><anno>GCInfo</anno></c> can be changed without
+ prior notice. For details about the meaning of each item, see
+ <seealso marker="#gc_minor_start"><c>gc_minor_start</c></seealso>
+ in <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>.</p>
</item>
<tag><c>{group_leader, <anno>GroupLeader</anno>}</c></tag>
<item>
- <p><c><anno>GroupLeader</anno></c> is group leader for the I/O of
- the process.</p>
+ <p><c><anno>GroupLeader</anno></c> is the group leader for the I/O
+ of the process.</p>
</item>
<tag><c>{heap_size, <anno>Size</anno>}</c></tag>
<item>
- <p><c><anno>Size</anno></c> is the size in words of the youngest heap
- generation of the process. This generation includes
- the process stack. This information is highly
+ <p><c><anno>Size</anno></c> is the size in words of the youngest
+ heap generation of the process. This generation includes
+ the process stack. This information is highly
implementation-dependent, and can change if the
implementation changes.</p>
</item>
@@ -4834,29 +4956,29 @@ os_prompt% </pre>
</item>
<tag><c>{links, <anno>PidsAndPorts</anno>}</c></tag>
<item>
- <p><c><anno>PidsAndPorts</anno></c> is a list of process identifiers
- and port identifiers, with processes or ports to which the process
- has a link.</p>
+ <p><c><anno>PidsAndPorts</anno></c> is a list of process identifiers
+ and port identifiers, with processes or ports to which the process
+ has a link.</p>
</item>
<tag><c>{last_calls, false|Calls}</c></tag>
<item>
<p>The value is <c>false</c> if call saving is not active
- for the process (see
- <seealso marker="#process_flag/3">process_flag/3</seealso>).
+ for the process (see <seealso marker="#process_flag/3">
+ <c>process_flag/3</c></seealso>).
If call saving is active, a list is returned, in which
the last element is the most recent called.</p>
</item>
<tag><c>{memory, <anno>Size</anno>}</c></tag>
<item>
- <p><c><anno>Size</anno></c> is the size in bytes of the process. This
- includes call stack, heap, and internal structures.</p>
+ <p><c><anno>Size</anno></c> is the size in bytes of the process.
+ This includes call stack, heap, and internal structures.</p>
</item>
<tag><c>{message_queue_len, <anno>MessageQueueLen</anno>}</c></tag>
<item>
<p><c><anno>MessageQueueLen</anno></c> is the number of messages
- currently in the message queue of the process. This is
- the length of the list <c><anno>MessageQueue</anno></c> returned as
- the information item <c>messages</c> (see the following).</p>
+ currently in the message queue of the process. This is the
+ length of the list <c><anno>MessageQueue</anno></c> returned as
+ the information item <c>messages</c> (see below).</p>
</item>
<tag><c>{messages, <anno>MessageQueue</anno>}</c></tag>
<item>
@@ -4899,36 +5021,37 @@ os_prompt% </pre>
</item>
<tag><c>{message_queue_data, <anno>MQD</anno>}</c></tag>
<item>
- <p>Returns the current state of the <c>message_queue_data</c>
- process flag. <c><anno>MQD</anno></c> is either <c>off_heap</c>,
- or <c>on_heap</c>. For more information, see the
- documentation of
- <seealso marker="#process_flag_message_queue_data"><c>process_flag(message_queue_data,
- MQD)</c></seealso>.</p>
+ <p>Returns the current state of process flag
+ <c>message_queue_data</c>. <c><anno>MQD</anno></c> is either
+ <c>off_heap</c> or <c>on_heap</c>. For more
+ information, see the documentation of
+ <seealso marker="#process_flag_message_queue_data">
+ <c>process_flag(message_queue_data, MQD)</c></seealso>.</p>
</item>
<tag><c>{priority, <anno>Level</anno>}</c></tag>
<item>
<p><c><anno>Level</anno></c> is the current priority level for
- the process. For more information on priorities, see
- <seealso marker="#process_flag_priority">process_flag(priority,
- Level)</seealso>.</p>
+ the process. For more information on priorities, see
+ <seealso marker="#process_flag_priority">
+ <c>process_flag(priority, Level)</c></seealso>.</p>
</item>
<tag><c>{reductions, <anno>Number</anno>}</c></tag>
<item>
- <p><c><anno>Number</anno></c> is the number of reductions executed by
- the process.</p>
+ <p><c><anno>Number</anno></c> is the number of reductions executed
+ by the process.</p>
</item>
<tag><c>{registered_name, <anno>Atom</anno>}</c></tag>
<item>
- <p><c><anno>Atom</anno></c> is the registered name of the process. If
- the process has no registered name, this tuple is not
+ <p><c><anno>Atom</anno></c> is the registered process name.
+ If the process has no registered name, this tuple is not
present in the list.</p>
</item>
- <tag><c>{sequential_trace_token, [] | <anno>SequentialTraceToken</anno>}</c></tag>
+ <tag><c>{sequential_trace_token, [] |
+ <anno>SequentialTraceToken</anno>}</c></tag>
<item>
<p><c><anno>SequentialTraceToken</anno></c> is the sequential trace
- token for the process. This <c><anno>InfoTuple</anno></c> can be
- changed or removed without prior notice.</p>
+ token for the process. This <c><anno>InfoTuple</anno></c> can be
+ changed or removed without prior notice.</p>
</item>
<tag><c>{stack_size, <anno>Size</anno>}</c></tag>
<item>
@@ -4937,9 +5060,9 @@ os_prompt% </pre>
</item>
<tag><c>{status, <anno>Status</anno>}</c></tag>
<item>
- <p><c><anno>Status</anno></c> is the status of the process and is one
- of the following:</p>
- <list type="bulleted">
+ <p><c><anno>Status</anno></c> is the status of the process and is
+ one of the following:</p>
+ <list type="bulleted">
<item><c>exiting</c></item>
<item><c>garbage_collecting</c></item>
<item><c>waiting</c> (for a message)</item>
@@ -4947,46 +5070,44 @@ os_prompt% </pre>
<item><c>runnable</c> (ready to run, but another process is
running)</item>
<item><c>suspended</c> (suspended on a "busy" port
- or by the BIF <c>erlang:suspend_process/[1,2]</c>)</item>
+ or by the BIF <c>erlang:suspend_process/1,2</c>)</item>
</list>
</item>
<tag><c>{suspending, <anno>SuspendeeList</anno>}</c></tag>
<item>
<p><c><anno>SuspendeeList</anno></c> is a list of
- <c>{<anno>Suspendee</anno>, <anno>ActiveSuspendCount</anno>,
- <anno>OutstandingSuspendCount</anno>}</c> tuples.
- <c><anno>Suspendee</anno></c> is the process identifier of a
- process that has been, or is to be,
- suspended by the process identified by <c><anno>Pid</anno></c>
- through one of the following BIFs:</p>
+ <c>{<anno>Suspendee</anno>, <anno>ActiveSuspendCount</anno>,
+ <anno>OutstandingSuspendCount</anno>}</c> tuples.
+ <c><anno>Suspendee</anno></c> is the process identifier of a
+ process that has been, or is to be,
+ suspended by the process identified by <c><anno>Pid</anno></c>
+ through the BIF <seealso marker="#suspend_process/2">
+ <c>erlang:suspend_process/2</c></seealso> or
+ <seealso marker="#suspend_process/1">
+ <c>erlang:suspend_process/1</c></seealso>.</p>
+ <p><c><anno>ActiveSuspendCount</anno></c> is the number of
+ times <c><anno>Suspendee</anno></c> has been suspended by
+ <c><anno>Pid</anno></c>.
+ <c><anno>OutstandingSuspendCount</anno></c> is the number of not
+ yet completed suspend requests sent by <c><anno>Pid</anno></c>,
+ that is:</p>
<list type="bulleted">
<item>
- <seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso>
- </item>
- <item>
- <seealso marker="#suspend_process/1">erlang:suspend_process/1</seealso>
- </item>
- </list>
- <p><c><anno>ActiveSuspendCount</anno></c> is the number of
- times <c><anno>Suspendee</anno></c> has been suspended by
- <c><anno>Pid</anno></c>.
- <c><anno>OutstandingSuspendCount</anno></c> is the number of not yet
- completed suspend requests sent by <c><anno>Pid</anno></c>, that is:</p>
- <list type="bulleted">
- <item>If <c><anno>ActiveSuspendCount</anno> =/= 0</c>,
- <c><anno>Suspendee</anno></c> is
- currently in the suspended state.
+ <p>If <c><anno>ActiveSuspendCount</anno> =/= 0</c>,
+ <c><anno>Suspendee</anno></c> is
+ currently in the suspended state.</p>
</item>
- <item>If <c><anno>OutstandingSuspendCount</anno> =/= 0</c>, option
- <c>asynchronous</c> of <c>erlang:suspend_process/2</c>
- has been used and the suspendee has not yet been
- suspended by <c><anno>Pid</anno></c>.
+ <item>
+ <p>If <c><anno>OutstandingSuspendCount</anno> =/= 0</c>,
+ option <c>asynchronous</c> of <c>erlang:suspend_process/2</c>
+ has been used and the suspendee has not yet been
+ suspended by <c><anno>Pid</anno></c>.</p>
</item>
</list>
- <p>Notice that <c><anno>ActiveSuspendCount</anno></c> and
- <c><anno>OutstandingSuspendCount</anno></c> are not the
- total suspend count on <c><anno>Suspendee</anno></c>,
- only the parts contributed by <c><anno>Pid</anno></c>.</p>
+ <p>Notice that <c><anno>ActiveSuspendCount</anno></c> and
+ <c><anno>OutstandingSuspendCount</anno></c> are not the
+ total suspend count on <c><anno>Suspendee</anno></c>,
+ only the parts contributed by <c><anno>Pid</anno></c>.</p>
</item>
<tag>
<marker id="process_info_total_heap_size"/>
@@ -4994,21 +5115,21 @@ os_prompt% </pre>
</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 and
- any unreceived messages that are considered to be part of the
- heap. </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>
<p><c><anno>InternalTraceFlags</anno></c> is an integer
- representing the internal trace flag for this process.
- This <c><anno>InfoTuple</anno></c>
- can be changed or removed without prior notice.</p>
+ representing the internal trace flag for this process.
+ This <c><anno>InfoTuple</anno></c>
+ can be changed or removed without prior notice.</p>
</item>
<tag><c>{trap_exit, <anno>Boolean</anno>}</c></tag>
<item>
<p><c><anno>Boolean</anno></c> is <c>true</c> if the process
- is trapping exits, otherwise <c>false</c>.</p>
+ is trapping exits, otherwise <c>false</c>.</p>
</item>
</taglist>
<p>Notice that not all implementations support all
@@ -5016,9 +5137,9 @@ os_prompt% </pre>
<p>Failures:</p>
<taglist>
<tag><c>badarg</c></tag>
- <item>If <c><anno>Pid</anno></c> is not a local process.</item>
+ <item>If <c><anno>Pid</anno></c> is not a local process.</item>
<tag><c>badarg</c></tag>
- <item>If <c><anno>Item</anno></c> is an invalid item.</item>
+ <item>If <c><anno>Item</anno></c> is an invalid item.</item>
</taglist>
</desc>
</func>
@@ -5028,11 +5149,11 @@ os_prompt% </pre>
<fsummary>All processes.</fsummary>
<desc>
<p>Returns a list of process identifiers corresponding to
- all the processes currently existing on the local node.</p>
- <p>Notice that an exiting process exists, but is not alive.
- That is, <c>is_process_alive/1</c> returns <c>false</c>
- for an exiting process, but its process identifier is part
- of the result returned from <c>processes/0</c>.</p>
+ all the processes currently existing on the local node.</p>
+ <p>Notice that an exiting process exists, but is not alive.
+ That is, <c>is_process_alive/1</c> returns <c>false</c>
+ for an exiting process, but its process identifier is part
+ of the result returned from <c>processes/0</c>.</p>
<p>Example:</p>
<pre>
> <input>processes().</input>
@@ -5042,22 +5163,23 @@ os_prompt% </pre>
<func>
<name name="purge_module" arity="1"/>
- <fsummary>Removes old code for a module.</fsummary>
+ <fsummary>Remove old code for a module.</fsummary>
<desc>
<p>Removes old code for <c><anno>Module</anno></c>.
Before this BIF is used,
- <c>erlang:check_process_code/2</c> is to be called to check
+ <seealso marker="#check_process_code/2">
+ <c>check_process_code/2</c></seealso>is to be called to check
that no processes execute old code in the module.</p>
<warning>
<p>This BIF is intended for the code server (see
- <seealso marker="kernel:code">code(3)</seealso>)
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>)
and is not to be used elsewhere.</p>
</warning>
<note>
- <p>As from <c>ERTS</c> 8.0 (OTP 19), any lingering processes
- that still execute the old code will be killed by this function.
- In earlier versions, such incorrect use could cause much
- more fatal failures, like emulator crash.</p>
+ <p>As from ERTS 8.0 (Erlang/OTP 19), any lingering processes
+ that still execute the old code is killed by this function.
+ In earlier versions, such incorrect use could cause much
+ more fatal failures, like emulator crash.</p>
</note>
<p>Failure: <c>badarg</c> if there is no old code for
<c><anno>Module</anno></c>.</p>
@@ -5066,14 +5188,13 @@ os_prompt% </pre>
<func>
<name name="put" arity="2"/>
- <fsummary>Adds a new value to the process dictionary.</fsummary>
+ <fsummary>Add a new value to the process dictionary.</fsummary>
<desc>
<p>Adds a new <c><anno>Key</anno></c> to the process dictionary,
associated with the value <c><anno>Val</anno></c>, and returns
<c>undefined</c>. If <c><anno>Key</anno></c> exists, the old
value is deleted and replaced by <c><anno>Val</anno></c>, and
- the function returns the old value.</p>
- <p>Example:</p>
+ the function returns the old value. Example:</p>
<pre>
> <input>X = put(name, walrus), Y = put(name, carpenter),</input>
<input>Z = get(name),</input>
@@ -5089,17 +5210,18 @@ os_prompt% </pre>
<func>
<name name="raise" arity="3"/>
- <fsummary>Stops execution with an exception of given class, reason, and call stack backtrace.</fsummary>
+ <fsummary>Stop execution with an exception of specified class, reason,
+ and call stack backtrace.</fsummary>
<type name="raise_stacktrace"/>
<desc>
<p>Stops the execution of the calling process with an
- exception of given class, reason, and call stack backtrace
+ exception of the specified class, reason, and call stack backtrace
(<em>stacktrace</em>).</p>
<p><c><anno>Class</anno></c> is <c>error</c>, <c>exit</c>, or
<c>throw</c>. So, if it were not for the stacktrace,
<c>erlang:raise(<anno>Class</anno>, <anno>Reason</anno>,
- <anno>Stacktrace</anno>)</c> is
- equivalent to <c>erlang:<anno>Class</anno>(<anno>Reason</anno>)</c>.</p>
+ <anno>Stacktrace</anno>)</c> is equivalent to
+ <c>erlang:<anno>Class</anno>(<anno>Reason</anno>)</c>.</p>
<p><c><anno>Reason</anno></c> is any term.
<c><anno>Stacktrace</anno></c> is a list as
returned from <c>get_stacktrace()</c>, that is, a list of
@@ -5109,12 +5231,12 @@ os_prompt% </pre>
argument list. The stacktrace can also contain <c>{Fun,
Args, Location}</c> tuples, where <c>Fun</c> is a local
fun and <c>Args</c> is an argument list.</p>
- <p>Element <c>Location</c> at the end is optional.
- Omitting it is equivalent to specifying an empty list.</p>
+ <p>Element <c>Location</c> at the end is optional.
+ Omitting it is equivalent to specifying an empty list.</p>
<p>The stacktrace is used as the exception stacktrace for the
calling process; it is truncated to the current
maximum stacktrace depth.</p>
- <p>Since evaluating this function causes the process to
+ <p>As evaluating this function causes the process to
terminate, it has no return value unless the arguments are
invalid, in which case the function <em>returns the error
reason</em> <c>badarg</c>. If you want to be
@@ -5126,78 +5248,68 @@ os_prompt% </pre>
</func>
<func>
- <name name="read_timer" arity="2"/>
- <fsummary>Reads the state of a timer.</fsummary>
+ <name name="read_timer" arity="1"/>
+ <fsummary>Read the state of a timer.</fsummary>
<desc>
- <p>
- Read the state of a timer that has been created by either
- <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>,
- or <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>.
- <c><anno>TimerRef</anno></c> identifies the timer, and
- was returned by the BIF that created the timer.
- </p>
- <p>Available <c><anno>Option</anno>s</c>:</p>
+ <p>Reads the state of a timer. The same as calling
+ <seealso marker="#read_timer/2"><c>erlang:read_timer(TimerRef,
+ [])</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="read_timer" arity="2"/>
+ <fsummary>Read the state of a timer.</fsummary>
+ <desc>
+ <p>Reads the state of a timer that has been created by either
+ <seealso marker="#start_timer/4"><c>erlang:start_timer</c></seealso>
+ or <seealso marker="#send_after/4"><c>erlang:send_after</c></seealso>.
+ <c><anno>TimerRef</anno></c> identifies the timer, and
+ was returned by the BIF that created the timer.</p>
+ <p><c><anno>Option</anno>s</c>:</p>
<taglist>
- <tag><c>{async, Async}</c></tag>
- <item>
- <p>
- Asynchronous request for state information. <c>Async</c>
- defaults to <c>false</c> which will cause the operation
- to be performed synchronously. In this case, the <c>Result</c>
- is returned by <c>erlang:read_timer()</c>. When
- <c>Async</c> is <c>true</c>, <c>erlang:read_timer()</c>
- sends an asynchronous request for the state information
- to the timer service that manages the timer, and then returns
- <c>ok</c>. A message on the format <c>{read_timer,
- <anno>TimerRef</anno>, <anno>Result</anno>}</c> is
- sent to the caller of <c>erlang:read_timer()</c> when the
- operation has been processed.
- </p>
- </item>
+ <tag><c>{async, Async}</c></tag>
+ <item>
+ <p>Asynchronous request for state information. <c>Async</c>
+ defaults to <c>false</c>, which causes the operation
+ to be performed synchronously. In this case, the <c>Result</c>
+ is returned by <c>erlang:read_timer</c>. When
+ <c>Async</c> is <c>true</c>, <c>erlang:read_timer</c>
+ sends an asynchronous request for the state information
+ to the timer service that manages the timer, and then returns
+ <c>ok</c>. A message on the format <c>{read_timer,
+ <anno>TimerRef</anno>, <anno>Result</anno>}</c> is
+ sent to the caller of <c>erlang:read_timer</c> when the
+ operation has been processed.</p>
+ </item>
</taglist>
- <p>
- More <c><anno>Option</anno></c>s may be added in the future.
- </p>
- <p>
- If <c><anno>Result</anno></c> is an integer, it represents the
- time in milli-seconds left until the timer expires.</p>
- <p>
- If <c><anno>Result</anno></c> is <c>false</c>, a
- timer corresponding to <c><anno>TimerRef</anno></c> could not
- be found. This can be because the timer had expired,
- it had been canceled, or because <c><anno>TimerRef</anno></c>
- never has corresponded to a timer. Even if the timer has expired,
- it does not tell you whether or not the timeout message has
- arrived at its destination yet.
- </p>
- <note>
- <p>
- The timer service that manages the timer may be co-located
- with another scheduler than the scheduler that the calling
- process is executing on. If this is the case, communication
- with the timer service takes much longer time than if it
- is located locally. If the calling process is in critical
- path, and can do other things while waiting for the result
- of this operation, you want to use option <c>{async, true}</c>.
- If using option <c>{async, false}</c>, the calling
- process will be blocked until the operation has been
- performed.
- </p>
- </note>
+ <p>More <c><anno>Option</anno></c>s can be added in the future.</p>
+ <p>If <c><anno>Result</anno></c> is an integer, it represents the
+ time in milliseconds left until the timer expires.</p>
+ <p>If <c><anno>Result</anno></c> is <c>false</c>, a
+ timer corresponding to <c><anno>TimerRef</anno></c> could not
+ be found. This because the timer had expired,
+ or been canceled, or because <c><anno>TimerRef</anno></c>
+ never has corresponded to a timer. Even if the timer has expired,
+ it does not tell you whether or not the time-out message has
+ arrived at its destination yet.</p>
+ <note>
+ <p>The timer service that manages the timer can be co-located
+ with another scheduler than the scheduler that the calling
+ process is executing on. If so, communication
+ with the timer service takes much longer time than if it
+ is located locally. If the calling process is in a critical
+ path, and can do other things while waiting for the result
+ of this operation, you want to use option <c>{async, true}</c>.
+ If using option <c>{async, false}</c>, the calling
+ process is blocked until the operation has been performed.</p>
+ </note>
<p>See also
<seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>,
- <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>,
- and
- <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>.</p>
- </desc>
- </func>
- <func>
- <name name="read_timer" arity="1"/>
- <fsummary>Reads the state of a timer.</fsummary>
- <desc>
- <p>Read the state of a timer. The same as calling
- <seealso marker="#read_timer/2"><c>erlang:read_timer(TimerRef,
- [])</c></seealso>.</p>
+ <seealso marker="#start_timer/4">
+ <c>erlang:start_timer/4</c></seealso>, and
+ <seealso marker="#cancel_timer/2">
+ <c>erlang:cancel_timer/2</c></seealso>.</p>
</desc>
</func>
@@ -5209,21 +5321,20 @@ os_prompt% </pre>
representation of <c><anno>Ref</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and is not to be used
- in application programs.</p>
+ in application programs.</p>
</warning>
</desc>
</func>
<func>
<name name="register" arity="2"/>
- <fsummary>Registers a name for a pid (or port).</fsummary>
+ <fsummary>Register a name for a pid (or port).</fsummary>
<desc>
<p>Associates the name <c><anno>RegName</anno></c> with a process
identifier (pid) or a port identifier.
<c><anno>RegName</anno></c>, which must be an atom, can be used
instead of the pid or port identifier in send operator
- (<c><anno>RegName</anno> ! Message</c>).</p>
- <p>Example:</p>
+ (<c><anno>RegName</anno> ! Message</c>). Example:</p>
<pre>
> <input>register(db, Pid).</input>
true</pre>
@@ -5249,7 +5360,7 @@ true</pre>
<fsummary>All registered names.</fsummary>
<desc>
<p>Returns a list of names that have been registered using
- <seealso marker="#register/2">register/2</seealso>, for
+ <seealso marker="#register/2"><c>register/2</c></seealso>, for
example:</p>
<pre>
> <input>registered().</input>
@@ -5259,20 +5370,21 @@ true</pre>
<func>
<name name="resume_process" arity="1"/>
- <fsummary>Resumes a suspended process.</fsummary>
+ <fsummary>Resume a suspended process.</fsummary>
<desc>
<p>Decreases the suspend count on the process identified by
- <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c>
- is previously to have been suspended through
- <seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso>
- or
- <seealso marker="#suspend_process/1">erlang:suspend_process/1</seealso>
- by the process calling
- <c>erlang:resume_process(<anno>Suspendee</anno>)</c>. When the
- suspend count on <c><anno>Suspendee</anno></c> reaches zero,
- <c><anno>Suspendee</anno></c> is resumed, that is, its state
- is changed from suspended into the state it had before it was
- suspended.</p>
+ <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c>
+ is previously to have been suspended through
+ <seealso marker="#suspend_process/2">
+ <c>erlang:suspend_process/2</c></seealso> or
+ <seealso marker="#suspend_process/1">
+ <c>erlang:suspend_process/1</c></seealso>
+ by the process calling
+ <c>erlang:resume_process(<anno>Suspendee</anno>)</c>. When the
+ suspend count on <c><anno>Suspendee</anno></c> reaches zero,
+ <c><anno>Suspendee</anno></c> is resumed, that is, its state
+ is changed from suspended into the state it had before it was
+ suspended.</p>
<warning>
<p>This BIF is intended for debugging only.</p>
</warning>
@@ -5280,29 +5392,29 @@ true</pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Suspendee</anno></c> is not a process identifier.
- </item>
+ If <c><anno>Suspendee</anno></c> is not a process identifier.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the process calling <c>erlang:resume_process/1</c> had
- not previously increased the suspend count on the process
- identified by <c><anno>Suspendee</anno></c>.
- </item>
+ If the process calling <c>erlang:resume_process/1</c> had
+ not previously increased the suspend count on the process
+ identified by <c><anno>Suspendee</anno></c>.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c><anno>Suspendee</anno></c>
- is not alive.
- </item>
+ If the process identified by <c><anno>Suspendee</anno></c>
+ is not alive.
+ </item>
</taglist>
</desc>
</func>
<func>
<name name="round" arity="1"/>
- <fsummary>Returns an integer by rounding a number.</fsummary>
+ <fsummary>Return an integer by rounding a number.</fsummary>
<desc>
<p>Returns an integer by rounding <c><anno>Number</anno></c>,
- for example:</p>
+ for example:</p>
<pre>
<input>round(5.5).</input>
6</pre>
@@ -5312,7 +5424,7 @@ true</pre>
<func>
<name name="self" arity="0"/>
- <fsummary>Returns pid of the calling process.</fsummary>
+ <fsummary>Return pid of the calling process.</fsummary>
<desc>
<p>Returns the process identifier of the calling process, for
example:</p>
@@ -5325,7 +5437,7 @@ true</pre>
<func>
<name name="send" arity="2"/>
- <fsummary>Sends a message.</fsummary>
+ <fsummary>Send a message.</fsummary>
<type name="dst"/>
<desc>
<p>Sends a message and returns <c><anno>Msg</anno></c>. This
@@ -5339,27 +5451,27 @@ true</pre>
<func>
<name name="send" arity="3"/>
- <fsummary>Sends a message conditionally.</fsummary>
+ <fsummary>Send a message conditionally.</fsummary>
<type name="dst"/>
<desc>
<p>Either sends a message and returns <c>ok</c>, or does not send
- the message but returns something else (see the following).
+ the message but returns something else (see below).
Otherwise the same as
- <seealso marker="#send/2">erlang:send/2</seealso>.
+ <seealso marker="#send/2"><c>erlang:send/2</c></seealso>.
For more detailed explanation and warnings, see
- <seealso marker="#send_nosuspend/2">erlang:send_nosuspend/2,3</seealso>.</p>
- <p>The options are as follows:</p>
+ <seealso marker="#send_nosuspend/2">
+ <c>erlang:send_nosuspend/2,3</c></seealso>.</p>
+ <p>Options:</p>
<taglist>
<tag><c>nosuspend</c></tag>
- <item>
- <p>If the sender would have to be suspended to do the send,
- <c>nosuspend</c> is returned instead.</p>
+ <item>If the sender would have to be suspended to do the send,
+ <c>nosuspend</c> is returned instead.
</item>
<tag><c>noconnect</c></tag>
<item>
- <p>If the destination node would have to be auto-connected
- to do the send, <c>noconnect</c> is returned
- instead.</p>
+ If the destination node would have to be auto-connected
+ to do the send, <c>noconnect</c> is returned
+ instead.
</item>
</taglist>
<warning>
@@ -5370,36 +5482,37 @@ true</pre>
</func>
<func>
- <name name="send_after" arity="4"/>
- <fsummary>Start a timer</fsummary>
+ <name name="send_after" arity="3"/>
+ <fsummary>Start a timer.</fsummary>
<desc>
- <p>
- Starts a timer. When the timer expires, the message
- <c><anno>Msg</anno></c> is sent to the process
- identified by <c><anno>Dest</anno></c>. Apart from
- the format of the timeout message,
- <c>erlang:send_after/4</c> works exactly as
- <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>.</p>
+ <p>Starts a timer. The same as calling
+ <seealso marker="#send_after/4">
+ <c>erlang:send_after(<anno>Time</anno>, <anno>Dest</anno>,
+ <anno>Msg</anno>, [])</c></seealso>.</p>
</desc>
</func>
+
<func>
- <name name="send_after" arity="3"/>
- <fsummary>Starts a timer.</fsummary>
+ <name name="send_after" arity="4"/>
+ <fsummary>Start a timer.</fsummary>
<desc>
- <p>Starts a timer. The same as calling
- <seealso marker="#send_after/4"><c>erlang:send_after(<anno>Time</anno>,
- <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p>
+ <p>Starts a timer. When the timer expires, the message
+ <c><anno>Msg</anno></c> is sent to the process
+ identified by <c><anno>Dest</anno></c>. Apart from
+ the format of the time-out message, this function works exactly as
+ <seealso marker="#start_timer/4">
+ <c>erlang:start_timer/4</c></seealso>.</p>
</desc>
</func>
<func>
<name name="send_nosuspend" arity="2"/>
- <fsummary>Tries to send a message without ever blocking.</fsummary>
+ <fsummary>Try to send a message without ever blocking.</fsummary>
<type name="dst"/>
<desc>
<p>The same as
- <seealso marker="#send/3">erlang:send(<anno>Dest</anno>,
- <anno>Msg</anno>, [nosuspend])</seealso>,
+ <seealso marker="#send/3"><c>erlang:send(<anno>Dest</anno>,
+ <anno>Msg</anno>, [nosuspend])</c></seealso>,
but returns <c>true</c> if
the message was sent and <c>false</c> if the message was not
sent because the sender would have had to be suspended.</p>
@@ -5425,12 +5538,12 @@ true</pre>
contradictory to the Erlang programming model. The message is
<em>not</em> sent if this function returns <c>false</c>.</p>
<p>In many systems, transient states of
- overloaded queues are normal. The fact that this function
+ overloaded queues are normal. Although this function
returns <c>false</c> does not mean that the other
node is guaranteed to be non-responsive, it could be a
temporary overload. Also, a return value of <c>true</c> does
only mean that the message can be sent on the (TCP) channel
- without blocking, the message is not guaranteed to
+ without blocking; the message is not guaranteed to
arrive at the remote node. For a disconnected
non-responsive node, the return value is <c>true</c> (mimics
the behavior of operator <c>!</c>). The expected
@@ -5444,15 +5557,16 @@ true</pre>
<func>
<name name="send_nosuspend" arity="3"/>
- <fsummary>Tries to send a message without ever blocking.</fsummary>
+ <fsummary>Try to send a message without ever blocking.</fsummary>
<type name="dst"/>
<desc>
<p>The same as
- <seealso marker="#send/3">erlang:send(<anno>Dest</anno>,
- <anno>Msg</anno>, [nosuspend | <anno>Options</anno>])</seealso>,
+ <seealso marker="#send/3"><c>erlang:send(<anno>Dest</anno>,
+ <anno>Msg</anno>, [nosuspend | <anno>Options</anno>])</c></seealso>,
but with a Boolean return value.</p>
<p>This function behaves like
- <seealso marker="#send_nosuspend/2">erlang:send_nosuspend/2</seealso>,
+ <seealso marker="#send_nosuspend/2">
+ <c>erlang:send_nosuspend/2</c></seealso>,
but takes a third parameter, a list of options.
The only option is <c>noconnect</c>, which
makes the function return <c>false</c> if
@@ -5476,14 +5590,15 @@ true</pre>
<func>
<name name="set_cookie" arity="2"/>
- <fsummary>Sets the magic cookie of a node.</fsummary>
+ <fsummary>Set the magic cookie of a node.</fsummary>
<desc>
<p>Sets the magic cookie of <c><anno>Node</anno></c> to the atom
<c><anno>Cookie</anno></c>. If <c><anno>Node</anno></c> is the
local node, the function
also sets the cookie of all other unknown nodes to
- <c><anno>Cookie</anno></c> (see Section
- <seealso marker="doc/reference_manual:distributed">Distributed Erlang</seealso>
+ <c><anno>Cookie</anno></c> (see section
+ <seealso marker="doc/reference_manual:distributed">
+ Distributed Erlang</seealso>
in the Erlang Reference Manual in System Documentation).</p>
<p>Failure: <c>function_clause</c> if the local node is not
alive.</p>
@@ -5492,12 +5607,12 @@ true</pre>
<func>
<name name="setelement" arity="3"/>
- <fsummary>Sets the Nth element of a tuple.</fsummary>
+ <fsummary>Set the Nth element of a tuple.</fsummary>
<type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno></type_desc>
<desc>
<p>Returns a tuple that is a copy of argument
<c><anno>Tuple1</anno></c>
- with the element given by integer argument
+ with the element specified by integer argument
<c><anno>Index</anno></c>
(the first element is the element with index 1) replaced by
argument <c><anno>Value</anno></c>, for example:</p>
@@ -5512,52 +5627,52 @@ true</pre>
<fsummary>Size of a tuple or binary.</fsummary>
<desc>
<p>Returns the number of elements in a tuple or the number of
- bytes in a binary or bitstring, for example:</p>
+ bytes in a binary or bitstring, for example:</p>
<pre>
> <input>size({morni, mulle, bwange}).</input>
3
> <input>size(&lt;&lt;11, 22, 33&gt;&gt;).</input>
-3
-</pre>
- <p>For bitstrings the number of whole bytes is returned. That is, if the number of bits
+3</pre>
+ <p>For bitstrings, the number of whole bytes is returned.
+ That is, if the number of bits
in the bitstring is not divisible by 8, the resulting
number of bytes is rounded <em>down</em>.</p>
<p>Allowed in guard tests.</p>
<p>See also
<seealso marker="#tuple_size/1"><c>tuple_size/1</c></seealso>,
- <seealso marker="#byte_size/1"><c>byte_size/1</c></seealso>
- and
+ <seealso marker="#byte_size/1"><c>byte_size/1</c></seealso>, and
<seealso marker="#bit_size/1"><c>bit_size/1</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn" arity="1"/>
- <fsummary>Creates a new process with a fun as entry point.</fsummary>
+ <fsummary>Create a new process with a fun as entry point.</fsummary>
<desc>
<p>Returns the process identifier of a new process started by the
application of <c><anno>Fun</anno></c> to the empty list
<c>[]</c>. Otherwise
- works like <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ works like <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn" arity="2"/>
- <fsummary>Creates a new process with a fun as entry point on a given node.</fsummary>
+ <fsummary>Create a new process with a fun as entry point on a specified
+ node.</fsummary>
<desc>
<p>Returns the process identifier of a new process started
by the application of <c><anno>Fun</anno></c> to the
empty list <c>[]</c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is
returned. Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn" arity="3"/>
- <fsummary>Creates a new process with a function as entry point.</fsummary>
+ <fsummary>Create a new process with a function as entry point.</fsummary>
<desc>
<p>Returns the process identifier of a new process started by
the application of <c><anno>Module</anno>:<anno>Function</anno></c>
@@ -5569,7 +5684,7 @@ true</pre>
does not exist (where <c>Arity</c> is the length of
<c><anno>Args</anno></c>). The error handler
can be redefined (see
- <seealso marker="#process_flag/2">process_flag/2</seealso>).
+ <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>).
If <c>error_handler</c> is undefined, or the user has
redefined the default <c>error_handler</c> and its replacement is
undefined, a failure with reason <c>undef</c> occurs.</p>
@@ -5582,7 +5697,8 @@ true</pre>
<func>
<name name="spawn" arity="4"/>
- <fsummary>Creates a new process with a function as entry point on a given node.</fsummary>
+ <fsummary>Create a new process with a function as entry point on a
+ specified node.</fsummary>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application
@@ -5590,26 +5706,28 @@ true</pre>
to <c><anno>Args</anno></c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is returned.
Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_link" arity="1"/>
- <fsummary>Creates and links to a new process with a fun as entry point.</fsummary>
+ <fsummary>Create and link to a new process with a fun as entry point.
+ </fsummary>
<desc>
<p>Returns the process identifier of a new process started by
the application of <c><anno>Fun</anno></c> to the empty list
<c>[]</c>. A link is created between
the calling process and the new process, atomically.
Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_link" arity="2"/>
- <fsummary>Creates and links to a new process with a fun as entry point on a specified node.</fsummary>
+ <fsummary>Create and link to a new process with a fun as entry point on
+ a specified node.</fsummary>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application of <c><anno>Fun</anno></c> to the empty
@@ -5618,26 +5736,29 @@ true</pre>
atomically. If <c><anno>Node</anno></c> does not exist,
a useless pid is returned and an exit signal with
reason <c>noconnection</c> is sent to the calling
- process. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ process. Otherwise works like <seealso marker="#spawn/3">
+ <c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_link" arity="3"/>
- <fsummary>Creates and links to a new process with a function as entry point.</fsummary>
+ <fsummary>Create and link to a new process with a function as entry point.
+ </fsummary>
<desc>
<p>Returns the process identifier of a new process started by
the application of <c><anno>Module</anno>:<anno>Function</anno></c>
to <c><anno>Args</anno></c>. A link is created
between the calling process and the new process, atomically.
Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_link" arity="4"/>
- <fsummary>Creates and links to a new process with a function as entry point on a given node.</fsummary>
+ <fsummary>Create and link to a new process with a function as entry point
+ on a specified node.</fsummary>
<desc>
<p>Returns the process identifier (pid) of a new process
started by the application
@@ -5647,109 +5768,113 @@ true</pre>
process, atomically. If <c><anno>Node</anno></c> does
not exist, a useless pid is returned and an exit signal with
reason <c>noconnection</c> is sent to the calling
- process. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ process. Otherwise works like <seealso marker="#spawn/3">
+ <c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_monitor" arity="1"/>
- <fsummary>Creates and monitors a new process with a fun as entry point.</fsummary>
+ <fsummary>Create and monitor a new process with a fun as entry point.
+ </fsummary>
<desc>
<p>Returns the process identifier of a new process, started by
the application of <c><anno>Fun</anno></c> to the empty list
<c>[]</c>,
and a reference for a monitor created to the new process.
Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_monitor" arity="3"/>
- <fsummary>Creates and monitors a new process with a function as entry point.</fsummary>
+ <fsummary>Create and monitor a new process with a function as entry point.
+ </fsummary>
<desc>
<p>A new process is started by the application
of <c><anno>Module</anno>:<anno>Function</anno></c>
to <c><anno>Args</anno></c>. The process is
monitored at the same time. Returns the process identifier
and a reference for the monitor. Otherwise works like
- <seealso marker="#spawn/3">spawn/3</seealso>.</p>
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_opt" arity="2"/>
- <fsummary>Creates a new process with a fun as entry point.</fsummary>
+ <fsummary>Create 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" />
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
+ <type name="spawn_opt_option"/>
<desc>
<p>Returns the process identifier (pid) of a new process
started by the application of <c><anno>Fun</anno></c>
to the empty list <c>[]</c>. Otherwise works like
- <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p>
- <p>If option <c>monitor</c> is given, the newly created
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
+ <p>If option <c>monitor</c> is specified, the newly created
process is monitored, and both the pid and reference for
- the monitor is returned.</p>
+ the monitor are returned.</p>
</desc>
</func>
<func>
<name name="spawn_opt" arity="3"/>
- <fsummary>Creates a new process with a fun as entry point on a given node.</fsummary>
+ <fsummary>Create a new process with a fun as entry point on a specified
+ node.</fsummary>
<type name="priority_level"/>
- <type name="max_heap_size" />
- <type name="message_queue_data" />
- <type name="spawn_opt_option" />
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
+ <type name="spawn_opt_option"/>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application of <c><anno>Fun</anno></c> to the
empty list <c>[]</c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is
returned. Otherwise works like
- <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
</desc>
</func>
<func>
<name name="spawn_opt" arity="4"/>
- <fsummary>Creates a new process with a function as entry point.</fsummary>
+ <fsummary>Create 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" />
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
+ <type name="spawn_opt_option"/>
<desc>
<p>Works as
- <seealso marker="#spawn/3">spawn/3</seealso>, except that an
- extra option list is given when creating the process.</p>
- <p>If option <c>monitor</c> is given, the newly created
+ <seealso marker="#spawn/3"><c>spawn/3</c></seealso>, except that an
+ extra option list is specified when creating the process.</p>
+ <p>If option <c>monitor</c> is specified, the newly created
process is monitored, and both the pid and reference for
- the monitor is returned.</p>
- <p>The options are as follows:</p>
+ the monitor are returned.</p>
+ <p>Options:</p>
<taglist>
<tag><c>link</c></tag>
<item>
<p>Sets a link to the parent process (like
- <c>spawn_link/3</c> does).</p>
+ <seealso marker="#spawn_link/3"><c>spawn_link/3</c></seealso>
+ does).</p>
</item>
<tag><c>monitor</c></tag>
<item>
<p>Monitors the new process (like
- <seealso marker="#monitor/2">monitor/2</seealso> does).</p>
+ <seealso marker="#monitor/2"><c>monitor/2</c></seealso> does).</p>
</item>
<tag><c>{priority, <anno>Level</anno></c></tag>
<item>
<p>Sets the priority of the new process. Equivalent to
- executing
- <seealso marker="#process_flag_priority">process_flag(priority,
- <anno>Level</anno>)</seealso>
+ executing <seealso marker="#process_flag_priority">
+ <c>process_flag(priority, <anno>Level</anno>)</c></seealso>
in the start function of the new process,
except that the priority is set before the process is
selected for execution for the first time. For more
information on priorities, see
- <seealso marker="#process_flag_priority">process_flag(priority,
- <anno>Level</anno>)</seealso>.</p>
+ <seealso marker="#process_flag_priority">
+ <c>process_flag(priority, <anno>Level</anno>)</c></seealso>.</p>
</item>
<tag><c>{fullsweep_after, <anno>Number</anno>}</c></tag>
<item>
@@ -5772,21 +5897,22 @@ true</pre>
<p>A few cases when it can be useful to change
<c>fullsweep_after</c>:</p>
<list type="bulleted">
- <item>If binaries that are no longer used are to be
+ <item><p>If binaries that are no longer used are to be
thrown away as soon as possible. (Set
- <c><anno>Number</anno></c> to zero.)
+ <c><anno>Number</anno></c> to zero.)</p>
</item>
- <item>A process that mostly have short-lived data is
+ <item><p>A process that mostly have short-lived data is
fullsweeped seldom or never, that is, the old heap
contains mostly garbage. To ensure a fullsweep
occasionally, set <c><anno>Number</anno></c> to a
- suitable value, such as 10 or 20.
+ suitable value, such as 10 or 20.</p>
</item>
<item>In embedded systems with a limited amount of RAM
and no virtual memory, you might want to preserve memory
by setting <c><anno>Number</anno></c> to zero.
(The value can be set globally, see
- <seealso marker="#system_flag/2">erlang:system_flag/2</seealso>.)
+ <seealso marker="#system_flag/2">
+ <c>erlang:system_flag/2</c></seealso>.)
</item>
</list>
</item>
@@ -5811,7 +5937,7 @@ true</pre>
option unless you know that there is problem with
execution times or memory consumption, and
ensure that the option improves matters.</p>
- <p>Gives a minimum binary virtual heap size, in words.
+ <p>Gives a minimum binary virtual heap size, in words.
Setting this value
higher than the system default can speed up some
processes because less garbage collection is done.
@@ -5823,24 +5949,25 @@ true</pre>
<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>
+ <c>max_heap_size</c> is determined by command-line argument
+ <seealso marker="erl#+hmax"><c>+hmax</c></seealso>
+ in <c>erl(1)</c>. 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>,
- or <c>on_heap</c>. The default
- <c>message_queue_data</c> process flag is determined by the
- <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,
- <anno>MQD</anno>)</c></seealso>.</p>
+ flag. <c><anno>MQD</anno></c> is to be either <c>off_heap</c>
+ or <c>on_heap</c>. The default
+ <c>message_queue_data</c> process flag is determined by
+ command-line argument <seealso marker="erl#+hmqd">
+ <c>+hmqd</c></seealso> in <c>erl(1)</c>.
+ For more information, see the documentation of
+ <seealso marker="#process_flag_message_queue_data">
+ <c>process_flag(message_queue_data,
+ <anno>MQD</anno>)</c></seealso>.</p>
</item>
</taglist>
</desc>
@@ -5848,11 +5975,12 @@ true</pre>
<func>
<name name="spawn_opt" arity="5"/>
- <fsummary>Creates a new process with a function as entry point on a given node.</fsummary>
+ <fsummary>Create a new process with a function as entry point on a
+ specified node.</fsummary>
<type name="priority_level"/>
- <type name="max_heap_size" />
- <type name="message_queue_data" />
- <type name="spawn_opt_option" />
+ <type name="max_heap_size"/>
+ <type name="message_queue_data"/>
+ <type name="spawn_opt_option"/>
<desc>
<p>Returns the process identifier (pid) of a new process started
by the application
@@ -5860,23 +5988,24 @@ true</pre>
<c><anno>Args</anno></c> on <c><anno>Node</anno></c>. If
<c><anno>Node</anno></c> does not exist, a useless pid is returned.
Otherwise works like
- <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p>
- <note><p>Option <c>monitor</c> is not supported by
- <c>spawn_opt/5</c>.</p></note>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso>.</p>
+ <note>
+ <p>Option <c>monitor</c> is not supported by
+ <c>spawn_opt/5</c>.</p>
+ </note>
</desc>
</func>
<func>
<name name="split_binary" arity="2"/>
- <fsummary>Splits a binary into two.</fsummary>
+ <fsummary>Split a binary into two.</fsummary>
<type_desc variable="Pos">0..byte_size(Bin)</type_desc>
<desc>
<p>Returns a tuple containing the binaries that are the result
of splitting <c><anno>Bin</anno></c> into two parts at
position <c><anno>Pos</anno></c>.
This is not a destructive operation. After the operation,
- there are three binaries altogether.</p>
- <p>Example:</p>
+ there are three binaries altogether. Example:</p>
<pre>
> <input>B = list_to_binary("0123456789").</input>
&lt;&lt;"0123456789">>
@@ -5892,104 +6021,96 @@ true</pre>
</func>
<func>
+ <name name="start_timer" arity="3"/>
+ <fsummary>Start a timer.</fsummary>
+ <desc>
+ <p>Starts a timer. The same as calling
+ <seealso marker="#start_timer/4">
+ <c>erlang:start_timer(<anno>Time</anno>,
+ <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="start_timer" arity="4"/>
- <fsummary>Starts a timer.</fsummary>
+ <fsummary>Start a timer.</fsummary>
<desc>
- <p>
- Starts a timer. When the timer expires, the message
+ <p>Starts a timer. When the timer expires, the message
<c>{timeout, <anno>TimerRef</anno>, <anno>Msg</anno>}</c>
- is sent to the process identified by
- <c><anno>Dest</anno></c>.
- </p>
- <p>Available <c><anno>Option</anno></c>s:</p>
+ is sent to the process identified by <c><anno>Dest</anno></c>.</p>
+ <p><c><anno>Option</anno></c>s:</p>
<taglist>
<tag><c>{abs, false}</c></tag>
<item>
- <p>
- This is the default. It means the
- <c><anno>Time</anno></c> value is interpreted
- as a time in milli-seconds <em>relative</em> current
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso>.
- </p>
- </item>
+ <p>This is the default. It means the
+ <c><anno>Time</anno></c> value is interpreted
+ as a time in milliseconds <em>relative</em> current
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso>.</p>
+ </item>
<tag><c>{abs, true}</c></tag>
<item>
- <p>
- Absolute <c><anno>Time</anno></c> value. The
- <c><anno>Time</anno></c> value is interpreted as an
- absolute Erlang monotonic time in milli-seconds.
- </p>
- </item>
+ <p>Absolute <c><anno>Time</anno></c> value. The
+ <c><anno>Time</anno></c> value is interpreted as an
+ absolute Erlang monotonic time in milliseconds.</p>
+ </item>
</taglist>
- <p>
- More <c><anno>Option</anno></c>s may be added in the future.
- </p>
- <p>
- The absolute point in time, the timer is set to expire on,
- has to be in the interval
- <c>[</c><seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso><c>,
- </c><seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso><c>]</c>.
- Further, if a relative time is specified, the <c><anno>Time</anno></c> value
- is not allowed to be negative.
- </p>
- <p>
- If <c><anno>Dest</anno></c> is a <c>pid()</c>, it must
- be a <c>pid()</c> of a process created on the current
- runtime system instance. This process may or may not
- have terminated. If <c><anno>Dest</anno></c> is an
- <c>atom()</c>, it is interpreted as the name of a
- locally registered process. The process referred to by the
- name is looked up at the time of timer expiration. No error
- is given if the name does not refer to a process.
- </p>
- <p>
- If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer is
- automatically canceled if the process referred to by the
- <c>pid()</c> is not alive, or if the process exits. This
- feature was introduced in ERTS version 5.4.11. Notice that
- timers are not automatically canceled when
- <c><anno>Dest</anno></c> is an <c>atom()</c>.
- </p>
+ <p>More <c><anno>Option</anno></c>s can be added in the future.</p>
+ <p>The absolute point in time, the timer is set to expire on,
+ must be in the interval
+ <c>[</c><seealso marker="#system_info_start_time">
+ <c>erlang:system_info(start_time)</c></seealso><c>,
+ </c><seealso marker="#system_info_end_time">
+ <c>erlang:system_info(end_time)</c></seealso><c>]</c>.
+ If a relative time is specified, the <c><anno>Time</anno></c>
+ value is not allowed to be negative.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, it must
+ be a <c>pid()</c> of a process created on the current
+ runtime system instance. This process has either terminated
+ or not. If <c><anno>Dest</anno></c> is an
+ <c>atom()</c>, it is interpreted as the name of a
+ locally registered process. The process referred to by the
+ name is looked up at the time of timer expiration. No error
+ is returned if the name does not refer to a process.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer is
+ automatically canceled if the process referred to by the
+ <c>pid()</c> is not alive, or if the process exits. This
+ feature was introduced in ERTS 5.4.11. Notice that
+ timers are not automatically canceled when
+ <c><anno>Dest</anno></c> is an <c>atom()</c>.</p>
<p>See also
<seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>,
- <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>,
- and
- <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p>
+ <seealso marker="#cancel_timer/2">
+ <c>erlang:cancel_timer/2</c></seealso>, and
+ <seealso marker="#read_timer/2">
+ <c>erlang:read_timer/2</c></seealso>.</p>
<p>Failure: <c>badarg</c> if the arguments do not satisfy
the requirements specified here.</p>
</desc>
</func>
<func>
- <name name="start_timer" arity="3"/>
- <fsummary>Starts a timer.</fsummary>
- <desc>
- <p>Starts a timer. The same as calling
- <seealso marker="#start_timer/4"><c>erlang:start_timer(<anno>Time</anno>,
- <anno>Dest</anno>, <anno>Msg</anno>, [])</c></seealso>.</p>
- </desc>
- </func>
-
- <func>
<name name="statistics" arity="1" clause_i="1"/>
<fsummary>Information about active processes and ports.</fsummary>
- <desc><marker id="statistics_active_tasks"></marker>
- <p>
- Returns a list where each element represents the amount
- of active processes and ports on each run queue and its
- associated scheduler. That is, the number of processes and
- ports that are ready to run, or are currently running. The
- element location in the list corresponds to the scheduler
- and its run queue. The first element corresponds to scheduler
- number 1 and so on. The information is <em>not</em> gathered
- atomically. That is, the result is not necessarily a
- consistent snapshot of the state, but instead quite
- efficiently gathered. See also,
- <seealso marker="#statistics_total_active_tasks"><c>statistics(total_active_tasks)</c></seealso>,
- <seealso marker="#statistics_run_queue_lengths"><c>statistics(run_queue_lengths)</c></seealso>, and
- <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>.
- </p>
+ <desc>
+ <marker id="statistics_active_tasks"></marker>
+ <p>Returns a list where each element represents the amount
+ of active processes and ports on each run queue and its
+ associated scheduler. That is, the number of processes and
+ ports that are ready to run, or are currently running. The
+ element location in the list corresponds to the scheduler
+ and its run queue. The first element corresponds to scheduler
+ number 1 and so on. The information is <em>not</em> gathered
+ atomically. That is, the result is not necessarily a
+ consistent snapshot of the state, but instead quite
+ efficiently gathered.</p>
+ <p>See also
+ <seealso marker="#statistics_total_active_tasks">
+ <c>statistics(total_active_tasks)</c></seealso>,
+ <seealso marker="#statistics_run_queue_lengths">
+ <c>statistics(run_queue_lengths)</c></seealso>, and
+ <seealso marker="#statistics_total_run_queue_lengths">
+ <c>statistics(total_run_queue_lengths)</c></seealso>.</p>
</desc>
</func>
@@ -6008,11 +6129,13 @@ true</pre>
<desc>
<marker id="statistics_exact_reductions"></marker>
<p>Returns the number of exact reductions.</p>
- <note><p><c>statistics(exact_reductions)</c> is
- a more expensive operation than
- <seealso marker="#statistics_reductions">statistics(reductions)</seealso>,
- especially on an Erlang machine with SMP support.</p>
- </note>
+ <note>
+ <p><c>statistics(exact_reductions)</c> is
+ a more expensive operation than
+ <seealso marker="#statistics_reductions">
+ statistics(reductions)</seealso>,
+ especially on an Erlang machine with SMP support.</p>
+ </note>
</desc>
</func>
@@ -6021,7 +6144,7 @@ true</pre>
<fsummary>Information about garbage collection.</fsummary>
<desc>
<p>Returns information about garbage collection, for example:</p>
- <pre>
+ <pre>
> <input>statistics(garbage_collection).</input>
{85,23961,0}</pre>
<p>This information can be invalid for some implementations.</p>
@@ -6033,9 +6156,9 @@ true</pre>
<fsummary>Information about I/O.</fsummary>
<desc>
<p>Returns <c><anno>Input</anno></c>,
- which is the total number of bytes
- received through ports, and <c><anno>Output</anno></c>,
- which is the total number of bytes output to ports.</p>
+ which is the total number of bytes
+ received through ports, and <c><anno>Output</anno></c>,
+ which is the total number of bytes output to ports.</p>
</desc>
</func>
@@ -6044,23 +6167,21 @@ true</pre>
<fsummary>Information about microstate accounting.</fsummary>
<desc>
<marker id="statistics_microstate_accounting"></marker>
- <p>
- Microstate accounting can be used to measure how much time the Erlang
+ <p>Microstate accounting can be used to measure how much time the Erlang
runtime system spends doing various tasks. It is designed to be as
- lightweight as possible, but there will be some overhead when this
+ lightweight as possible, but some overhead exists when this
is enabled. Microstate accounting is meant to be a profiling tool
- to help figure out performance bottlenecks.
- To <c>start</c>/<c>stop</c>/<c>reset</c> microstate_accounting you use
- the system_flag
- <seealso marker="#system_flag_microstate_accounting">
- <c>microstate_accounting</c></seealso>.
- </p>
- <p>
- <c>erlang:statistics(microstate_accounting)</c> returns a list of maps
- representing some of the OS threads within ERTS. Each map contains
- <c>type</c> and <c>id</c> fields that can be used to identify what
+ to help finding performance bottlenecks.
+ To <c>start</c>/<c>stop</c>/<c>reset</c> microstate accounting, use
+ system flag <seealso marker="#system_flag_microstate_accounting">
+ <c>microstate_accounting</c></seealso>.</p>
+ <p><c>statistics(microstate_accounting)</c> returns a list of maps
+ representing some of the OS threads within ERTS. Each map
+ contains <c>type</c> and <c>id</c> fields that can be used to
+ identify what
thread it is, and also a counters field that contains data about how
much time has been spent in the various states.</p>
+ <p>Example:</p>
<pre>
> <input>erlang:statistics(microstate_accounting).</input>
[#{counters => #{aux => 1899182914,
@@ -6071,12 +6192,11 @@ true</pre>
port => 221631,
sleep => 5150294100},
id => 1,
- type => scheduler}|...]
- </pre>
+ type => scheduler}|...]</pre>
<p>The time unit is the same as returned by
- <seealso marker="kernel:os#perf_counter/0">
+ <seealso marker="kernel:os#perf_counter/0">
<c>os:perf_counter/0</c></seealso>.
- So to convert it to milliseconds you could do something like this:</p>
+ So, to convert it to milliseconds, you can do something like this:</p>
<pre>
lists:map(
fun(#{ counters := Cnt } = M) ->
@@ -6084,39 +6204,40 @@ lists:map(
erlang:convert_time_unit(PerfCount, perf_counter, 1000)
end, Cnt),
M#{ counters := MsCnt }
- end, erlang:statistics(microstate_accounting)).
- </pre>
- <p>
- It is important to note that these values are not guaranteed to be
+ end, erlang:statistics(microstate_accounting)).</pre>
+ <p>Notice that these values are not guaranteed to be
the exact time spent in each state. This is because of various
- optimisation done in order to keep the overhead as small as possible.
- </p>
-
- <p>Currently the following <c><anno>MSAcc_Thread_Type</anno></c> are available:</p>
+ optimisation done to keep the overhead as small as possible.</p>
+ <p><c><anno>MSAcc_Thread_Type</anno></c>s:</p>
<taglist>
<tag><c>scheduler</c></tag>
<item>The main execution threads that do most of the work.</item>
- <tag><c>async</c></tag><item>Async threads are used by various
- linked-in drivers (mainly the file drivers) do offload non-cpu
- intensive work.</item>
- <tag><c>aux</c></tag><item>Takes care of any work that is not
- specifically assigned to a scheduler.</item>
+ <tag><c>dirty_cpu_scheduler</c></tag>
+ <item>The threads for long running cpu intensive work.</item>
+ <tag><c>dirty_io_scheduler</c></tag>
+ <item>The threads for long running I/O work.</item>
+ <tag><c>async</c></tag>
+ <item>Async threads are used by various linked-in drivers (mainly the
+ file drivers) do offload non-CPU intensive work.</item>
+ <tag><c>aux</c></tag>
+ <item>Takes care of any work that is not
+ specifically assigned to a scheduler.</item>
</taglist>
- <p>Currently the following <c><anno>MSAcc_Thread_State</anno></c>s are available.
- All states are exclusive, meaning that a thread cannot be in two states
- at once. So if you add the numbers of all counters in a thread
- you will get the total run-time for that thread.</p>
+ <p>The following <c><anno>MSAcc_Thread_State</anno></c>s are available.
+ All states are exclusive, meaning that a thread cannot be in two
+ states at once. So, if you add the numbers of all counters in a
+ thread, you get the total runtime for that thread.</p>
<taglist>
<tag><c>aux</c></tag>
<item>Time spent handling auxiliary jobs.</item>
<tag><c>check_io</c></tag>
<item>Time spent checking for new I/O events.</item>
<tag><c>emulator</c></tag>
- <item>Time spent executing erlang processes.</item>
+ <item>Time spent executing Erlang processes.</item>
<tag><c>gc</c></tag>
<item>Time spent doing garbage collection. When extra states are
- enabled this is the time spent doing non-fullsweep garbage
- collections.</item>
+ enabled this is the time spent doing non-fullsweep garbage
+ collections.</item>
<tag><c>other</c></tag>
<item>Time spent doing unaccounted things.</item>
<tag><c>port</c></tag>
@@ -6124,61 +6245,59 @@ lists:map(
<tag><c>sleep</c></tag>
<item>Time spent sleeping.</item>
</taglist>
- <p>It is possible to add more fine grained <c><anno>MSAcc_Thread_State</anno></c>s
- through configure.
- (e.g. <c>./configure --with-microstate-accounting=extra</c>).
- Enabling these states will cause a performance degradation when
- microstate accounting is turned off and increase the overhead when
- it is turned on.</p>
+ <p>More fine-grained <c><anno>MSAcc_Thread_State</anno></c>s can
+ be added through configure (such as
+ <c>./configure --with-microstate-accounting=extra</c>).
+ Enabling these states causes performance degradation when
+ microstate accounting is turned off and increases the overhead when
+ it is turned on.</p>
<taglist>
<tag><c>alloc</c></tag>
<item>Time spent managing memory. Without extra states this time is
- spread out over all other states.</item>
+ spread out over all other states.</item>
<tag><c>bif</c></tag>
- <item>Time spent in bifs. Without extra states this time is part of
- the <c>emulator</c> state.</item>
+ <item>Time spent in BIFs. Without extra states this time is part of
+ the <c>emulator</c> state.</item>
<tag><c>busy_wait</c></tag>
<item>Time spent busy waiting. This is also the state where a
- scheduler no longer reports that it is active when using
- <seealso marker="#statistics_scheduler_wall_time">
- <c>erlang:statistics(scheduler_wall_time)</c></seealso>.
- So if you add all other states but this and sleep and then divide that
- by all time in the thread you should get something very similar to the
- scheduler_wall_time fraction. Without extra states this time is part
- of the <c>other</c> state.</item>
+ scheduler no longer reports that it is active when using
+ <seealso marker="#statistics_scheduler_wall_time">
+ <c>statistics(scheduler_wall_time)</c></seealso>. So, if you add
+ all other states but this and sleep, and then divide that by all
+ time in the thread, you should get something very similar to the
+ <c>scheduler_wall_time</c> fraction. Without extra states this
+ time is part of the <c>other</c> state.</item>
<tag><c>ets</c></tag>
- <item>Time spent executing ETS bifs. Without extra states this time is
- part of the <c>emulator</c> state.</item>
+ <item>Time spent executing ETS BIFs. Without extra states
+ this time is part of the <c>emulator</c> state.</item>
<tag><c>gc_full</c></tag>
<item>Time spent doing fullsweep garbage collection. Without extra
- states this time is part of the <c>gc</c> state.</item>
+ states this time is part of the <c>gc</c> state.</item>
<tag><c>nif</c></tag>
- <item>Time spent in nifs. Without extra states this time is part of
- the <c>emulator</c> state.</item>
+ <item>Time spent in NIFs. Without extra states this time is part of
+ the <c>emulator</c> state.</item>
<tag><c>send</c></tag>
<item>Time spent sending messages (processes only). Without extra
- states this time is part of the <c>emulator</c> state.</item>
+ states this time is part of the <c>emulator</c> state.</item>
<tag><c>timers</c></tag>
<item>Time spent managing timers. Without extra states this time is
- part of the <c>other</c> state.</item>
+ part of the <c>other</c> state.</item>
</taglist>
- <p>There is a utility module called
- <seealso marker="runtime_tools:msacc"><c>msacc</c></seealso> in
- runtime_tools that can be used to more easily analyse these
- statistics.</p>
-
- <p>
- Returns <c>undefined</c> if the system flag
+ <p>The utility module
+ <seealso marker="runtime_tools:msacc"><c>msacc(3)</c></seealso>
+ can be used to more easily analyse these statistics.</p>
+ <p>Returns <c>undefined</c> if system flag
<seealso marker="#system_flag_microstate_accounting">
- <c>microstate_accounting</c></seealso>
- is turned off.
- </p>
- <p>The list of thread information is unsorted and may appear in
- different order between calls.</p>
- <note><p>The threads and states are subject to change without any
- prior notice.</p></note>
+ <c>microstate_accounting</c></seealso> is turned off.</p>
+ <p>The list of thread information is unsorted and can appear in
+ different order between calls.</p>
+ <note>
+ <p>The threads and states are subject to change without any
+ prior notice.</p>
+ </note>
</desc>
</func>
+
<func>
<name name="statistics" arity="1" clause_i="7"/>
<fsummary>Information about reductions.</fsummary>
@@ -6188,11 +6307,12 @@ lists:map(
<pre>
> <input>statistics(reductions).</input>
{2046,11}</pre>
- <note><p>As from <c>ERTS</c> 5.5 (OTP R11B),
+ <note><p>As from ERTS 5.5 (Erlang/OTP R11B),
this value does not include reductions performed in current
time slices of currently scheduled processes. If an
exact value is wanted, use
- <seealso marker="#statistics_exact_reductions">statistics(exact_reductions)</seealso>.</p>
+ <seealso marker="#statistics_exact_reductions">
+ <c>statistics(exact_reductions)</c></seealso>.</p>
</note>
</desc>
</func>
@@ -6201,15 +6321,14 @@ lists:map(
<name name="statistics" arity="1" clause_i="8"/>
<fsummary>Information about the run-queues.</fsummary>
<desc><marker id="statistics_run_queue"></marker>
- <p>
- Returns the total length of the run-queues. That is, the number
+ <p>Returns the total length of the run-queues. That is, the number
of processes and ports that are ready to run on all available
- run-queues. The information is gathered atomically. That
- is, the result is a consistent snapshot of the state, but
- this operation is much more expensive compared to
- <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>.
- This especially when a large amount of schedulers is used.
- </p>
+ run-queues. The information is gathered atomically. That
+ is, the result is a consistent snapshot of the state, but
+ this operation is much more expensive compared to
+ <seealso marker="#statistics_total_run_queue_lengths">
+ <c>statistics(total_run_queue_lengths)</c></seealso>,
+ especially when a large amount of schedulers is used.</p>
</desc>
</func>
@@ -6217,19 +6336,21 @@ lists:map(
<name name="statistics" arity="1" clause_i="9"/>
<fsummary>Information about the run-queue lengths.</fsummary>
<desc><marker id="statistics_run_queue_lengths"></marker>
- <p>
- Returns a list where each element represents the amount
- of processes and ports ready to run for each run queue. The
- element location in the list corresponds to the run queue
- of a scheduler. The first element corresponds to the run
- queue of scheduler number 1 and so on. The information is
- <em>not</em> gathered atomically. That is, the result is
- not necessarily a consistent snapshot of the state, but
- instead quite efficiently gathered. See also,
- <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>,
- <seealso marker="#statistics_active_tasks"><c>statistics(active_tasks)</c></seealso>, and
- <seealso marker="#statistics_total_active_tasks"><c>statistics(total_active_tasks)</c></seealso>.
- </p>
+ <p>Returns a list where each element represents the amount
+ of processes and ports ready to run for each run queue. The
+ element location in the list corresponds to the run queue
+ of a scheduler. The first element corresponds to the run
+ queue of scheduler number 1 and so on. The information is
+ <em>not</em> gathered atomically. That is, the result is
+ not necessarily a consistent snapshot of the state, but
+ instead quite efficiently gathered.</p>
+ <p>See also
+ <seealso marker="#statistics_total_run_queue_lengths">
+ <c>statistics(total_run_queue_lengths)</c></seealso>,
+ <seealso marker="#statistics_active_tasks">
+ <c>statistics(active_tasks)</c></seealso>, and
+ <seealso marker="#statistics_total_active_tasks">
+ <c>statistics(total_active_tasks)</c></seealso>.</p>
</desc>
</func>
@@ -6239,8 +6360,11 @@ lists:map(
<desc>
<p>Returns information about runtime, in milliseconds.</p>
<p>This is the sum of the runtime for all threads
- in the Erlang runtime system and can therefore be greater
- than the wall clock time.</p>
+ in the Erlang runtime system and can therefore be greater
+ than the wall clock time.</p>
+ <warning><p>This value might wrap due to limitations in the
+ underlying functionality provided by the operating system
+ that is used.</p></warning>
<p>Example:</p>
<pre>
> <input>statistics(runtime).</input>
@@ -6252,7 +6376,7 @@ lists:map(
<name name="statistics" arity="1" clause_i="11"/>
<fsummary>Information about each schedulers work time.</fsummary>
<desc>
- <marker id="statistics_scheduler_wall_time"></marker>
+ <marker id="statistics_scheduler_wall_time"></marker>
<p>Returns a list of tuples with
<c>{<anno>SchedulerId</anno>, <anno>ActiveTime</anno>,
<anno>TotalTime</anno>}</c>, where
@@ -6260,7 +6384,8 @@ lists:map(
<c><anno>ActiveTime</anno></c> is
the duration the scheduler has been busy, and
<c><anno>TotalTime</anno></c> is the total time duration since
- <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
+ <seealso marker="#system_flag_scheduler_wall_time">
+ <c>scheduler_wall_time</c></seealso>
activation. The time unit is undefined and can be subject
to change between releases, OSs, and system restarts.
<c>scheduler_wall_time</c> is only to be used to
@@ -6272,28 +6397,28 @@ lists:map(
that is:</p>
<list type="bulleted">
<item>Executing process code</item>
- <item>Executing linked-in-driver or NIF code</item>
- <item>Executing built-in-functions, or any other runtime
- handling</item>
+ <item>Executing linked-in driver or NIF code</item>
+ <item>Executing BIFs, or any other runtime handling</item>
<item>Garbage collecting</item>
<item>Handling any other memory management</item>
</list>
- <p>Notice that a scheduler can also be busy even if the
- OS has scheduled out the scheduler thread.</p>
+ <p>Notice that a scheduler can also be busy even if the
+ OS has scheduled out the scheduler thread.</p>
<p>Returns <c>undefined</c> if system flag
- <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
- is turned off.</p>
+ <seealso marker="#system_flag_scheduler_wall_time">
+ <c>scheduler_wall_time</c></seealso> is turned off.</p>
<p>The list of scheduler information is unsorted and can
appear in different order between calls.</p>
- <p>Using <c>scheduler_wall_time</c> to calculate scheduler-utilization:</p>
-<pre>
+ <p>Using <c>scheduler_wall_time</c> to calculate
+ scheduler-utilization:</p>
+ <pre>
> <input>erlang:system_flag(scheduler_wall_time, true).</input>
false
> <input>Ts0 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.</input>
ok</pre>
<p>Some time later the user takes another snapshot and calculates
scheduler-utilization per scheduler, for example:</p>
-<pre>
+ <pre>
> <input>Ts1 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.</input>
ok
> <input>lists:map(fun({{I, A0, T0}, {I, A1, T1}}) ->
@@ -6306,8 +6431,9 @@ ok
{6,0.9739235846420741},
{7,0.973237033077876},
{8,0.9741297293248656}]</pre>
- <p>Using the same snapshots to calculate a total scheduler-utilization:</p>
-<pre>
+ <p>Using the same snapshots to calculate a total
+ scheduler-utilization:</p>
+ <pre>
> <input>{A, T} = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}}, {Ai,Ti}) ->
{Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0,Ts1)), A/T.</input>
0.9769136803764825</pre>
@@ -6323,17 +6449,19 @@ ok
<name name="statistics" arity="1" clause_i="12"/>
<fsummary>Information about active processes and ports.</fsummary>
<desc><marker id="statistics_total_active_tasks"></marker>
- <p>
- Returns the total amount of active processes and ports in
- the system. That is, the number of processes and ports that
- are ready to run, or are currently running. The information
- is <em>not</em> gathered atomically. That is, the result
- is not necessarily a consistent snapshot of the state, but
- instead quite efficiently gathered. See also,
- <seealso marker="#statistics_active_tasks"><c>statistics(active_tasks)</c></seealso>,
- <seealso marker="#statistics_run_queue_lengths"><c>statistics(run_queue_lengths)</c></seealso>, and
- <seealso marker="#statistics_total_run_queue_lengths"><c>statistics(total_run_queue_lengths)</c></seealso>.
- </p>
+ <p>Returns the total amount of active processes and ports in
+ the system. That is, the number of processes and ports that
+ are ready to run, or are currently running. The information
+ is <em>not</em> gathered atomically. That is, the result
+ is not necessarily a consistent snapshot of the state, but
+ instead quite efficiently gathered.</p>
+ <p>See also
+ <seealso marker="#statistics_active_tasks">
+ <c>statistics(active_tasks)</c></seealso>,
+ <seealso marker="#statistics_run_queue_lengths">
+ <c>statistics(run_queue_lengths)</c></seealso>, and
+ <seealso marker="#statistics_total_run_queue_lengths">
+ <c>statistics(total_run_queue_lengths)</c></seealso>.</p>
</desc>
</func>
@@ -6341,18 +6469,19 @@ ok
<name name="statistics" arity="1" clause_i="13"/>
<fsummary>Information about the run-queue lengths.</fsummary>
<desc><marker id="statistics_total_run_queue_lengths"></marker>
- <p>
- Returns the total length of the run-queues. That is, the number
+ <p>Returns the total length of the run queues. That is, the number
of processes and ports that are ready to run on all available
- run-queues. The information is <em>not</em> gathered atomically.
- That is, the result is not necessarily a consistent snapshot of
- the state, but much more efficiently gathered compared to
- <seealso marker="#statistics_run_queue"><c>statistics(run_queue)</c></seealso>.
- See also,
- <seealso marker="#statistics_run_queue_lengths"><c>statistics(run_queue_lengths)</c></seealso>,
- <seealso marker="#statistics_total_active_tasks"><c>statistics(total_active_tasks)</c></seealso>, and
- <seealso marker="#statistics_active_tasks"><c>statistics(active_tasks)</c></seealso>.
- </p>
+ run queues. The information is <em>not</em> gathered atomically.
+ That is, the result is not necessarily a consistent snapshot of
+ the state, but much more efficiently gathered compared to
+ <seealso marker="#statistics_run_queue">
+ <c>statistics(run_queue)</c></seealso>.</p>
+ <p>See also <seealso marker="#statistics_run_queue_lengths">
+ <c>statistics(run_queue_lengths)</c></seealso>,
+ <seealso marker="#statistics_total_active_tasks">
+ <c>statistics(total_active_tasks)</c></seealso>, and
+ <seealso marker="#statistics_active_tasks">
+ <c>statistics(active_tasks)</c></seealso>.</p>
</desc>
</func>
@@ -6368,64 +6497,80 @@ ok
</func>
<func>
+ <name name="suspend_process" arity="1"/>
+ <fsummary>Suspend a process.</fsummary>
+ <desc>
+ <p>Suspends the process identified by
+ <c><anno>Suspendee</anno></c>. The same as calling
+ <seealso marker="#suspend_process/2">
+ <c>erlang:suspend_process(<anno>Suspendee</anno>,
+ [])</c></seealso>.</p>
+ <warning>
+ <p>This BIF is intended for debugging only.</p>
+ </warning>
+ </desc>
+ </func>
+
+ <func>
<name name="suspend_process" arity="2"/>
- <fsummary>Suspends a process.</fsummary>
+ <fsummary>Suspend a process.</fsummary>
<desc>
<p>Increases the suspend count on the process identified by
- <c><anno>Suspendee</anno></c> and puts it in the suspended
- state if it is not
- already in that state. A suspended process will not be
- scheduled for execution until the process has been resumed.</p>
- <p>A process can be suspended by multiple processes and can
- be suspended multiple times by a single process. A suspended
- process does not leave the suspended state until its suspend
- count reaches zero. The suspend count of
- <c><anno>Suspendee</anno></c> is decreased when
- <seealso marker="#resume_process/1">erlang:resume_process(<anno>Suspendee</anno>)</seealso>
- is called by the same process that called
- <c>erlang:suspend_process(<anno>Suspendee</anno>)</c>.
- All increased suspend
- counts on other processes acquired by a process are automatically
- decreased when the process terminates.</p>
- <p>The options (<c><anno>Opt</anno></c>s) are as follows:</p>
+ <c><anno>Suspendee</anno></c> and puts it in the suspended
+ state if it is not
+ already in that state. A suspended process is not
+ scheduled for execution until the process has been resumed.</p>
+ <p>A process can be suspended by multiple processes and can
+ be suspended multiple times by a single process. A suspended
+ process does not leave the suspended state until its suspend
+ count reaches zero. The suspend count of
+ <c><anno>Suspendee</anno></c> is decreased when
+ <seealso marker="#resume_process/1">
+ <c>erlang:resume_process(<anno>Suspendee</anno>)</c></seealso>
+ is called by the same process that called
+ <c>erlang:suspend_process(<anno>Suspendee</anno>)</c>.
+ All increased suspend
+ counts on other processes acquired by a process are automatically
+ decreased when the process terminates.</p>
+ <p>Options (<c><anno>Opt</anno></c>s):</p>
<taglist>
<tag><c>asynchronous</c></tag>
<item>
- A suspend request is sent to the process identified by
- <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c>
- eventually suspends
- unless it is resumed before it could suspend. The caller
- of <c>erlang:suspend_process/2</c> returns immediately,
- regardless of whether <c><anno>Suspendee</anno></c> has
- suspended yet or not. The point in time when
- <c><anno>Suspendee</anno></c> suspends cannot be deduced
- from other events in the system. It is only guaranteed that
- <c><anno>Suspendee</anno></c> <em>eventually</em> suspends
- (unless it
- is resumed). If option <c>asynchronous</c> has <em>not</em>
- been passed, the caller of <c>erlang:suspend_process/2</c> is
- blocked until <c><anno>Suspendee</anno></c> has suspended.
- </item>
+ <p>A suspend request is sent to the process identified by
+ <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c>
+ eventually suspends
+ unless it is resumed before it could suspend. The caller
+ of <c>erlang:suspend_process/2</c> returns immediately,
+ regardless of whether <c><anno>Suspendee</anno></c> has
+ suspended yet or not. The point in time when
+ <c><anno>Suspendee</anno></c> suspends cannot be deduced
+ from other events in the system. It is only guaranteed that
+ <c><anno>Suspendee</anno></c> <em>eventually</em> suspends
+ (unless it
+ is resumed). If option <c>asynchronous</c> has <em>not</em>
+ been passed, the caller of <c>erlang:suspend_process/2</c> is
+ blocked until <c><anno>Suspendee</anno></c> has suspended.</p>
+ </item>
<tag><c>unless_suspending</c></tag>
<item>
- The process identified by <c><anno>Suspendee</anno></c> is
- suspended unless the calling process already is suspending
- <c><anno>Suspendee</anno></c>.
- If <c>unless_suspending</c> is combined
- with option <c>asynchronous</c>, a suspend request is
- sent unless the calling process already is suspending
- <c><anno>Suspendee</anno></c> or if a suspend request
- already has been sent and is in transit. If the calling
- process already is suspending <c><anno>Suspendee</anno></c>,
- or if combined with option <c>asynchronous</c>
- and a send request already is in transit,
- <c>false</c> is returned and the suspend count on
- <c><anno>Suspendee</anno></c> remains unchanged.
- </item>
+ <p>The process identified by <c><anno>Suspendee</anno></c> is
+ suspended unless the calling process already is suspending
+ <c><anno>Suspendee</anno></c>.
+ If <c>unless_suspending</c> is combined
+ with option <c>asynchronous</c>, a suspend request is
+ sent unless the calling process already is suspending
+ <c><anno>Suspendee</anno></c> or if a suspend request
+ already has been sent and is in transit. If the calling
+ process already is suspending <c><anno>Suspendee</anno></c>,
+ or if combined with option <c>asynchronous</c>
+ and a send request already is in transit,
+ <c>false</c> is returned and the suspend count on
+ <c><anno>Suspendee</anno></c> remains unchanged.</p>
+ </item>
</taglist>
- <p>If the suspend count on the process identified by
- <c><anno>Suspendee</anno></c> is increased, <c>true</c>
- is returned, otherwise <c>false</c>.</p>
+ <p>If the suspend count on the process identified by
+ <c><anno>Suspendee</anno></c> is increased, <c>true</c>
+ is returned, otherwise <c>false</c>.</p>
<warning>
<p>This BIF is intended for debugging only.</p>
</warning>
@@ -6433,68 +6578,56 @@ ok
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>Suspendee</anno></c> is not a process identifier.
- </item>
+ If <c><anno>Suspendee</anno></c> is not a process identifier.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c><anno>Suspendee</anno></c>
- is the same process
- as the process calling <c>erlang:suspend_process/2</c>.
- </item>
+ If the process identified by <c><anno>Suspendee</anno></c>
+ is the same process
+ as the process calling <c>erlang:suspend_process/2</c>.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c><anno>Suspendee</anno></c>
- is not alive.
- </item>
+ If the process identified by <c><anno>Suspendee</anno></c>
+ is not alive.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c><anno>Suspendee</anno></c>
- resides on another node.
- </item>
+ If the process identified by <c><anno>Suspendee</anno></c>
+ resides on another node.
+ </item>
<tag><c>badarg</c></tag>
<item>
- If <c><anno>OptList</anno></c> is not a proper list of valid
- <c><anno>Opt</anno></c>s.
- </item>
+ If <c><anno>OptList</anno></c> is not a proper list of valid
+ <c><anno>Opt</anno></c>s.
+ </item>
<tag><c>system_limit</c></tag>
<item>
- If the process identified by <c><anno>Suspendee</anno></c>
- has been suspended
- more times by the calling process than can be represented by the
- currently used internal data structures. The system limit is
- higher than 2,000,000,000 suspends and will never be lower.
- </item>
+ If the process identified by <c><anno>Suspendee</anno></c>
+ has been suspended
+ more times by the calling process than can be represented by the
+ currently used internal data structures. The system limit is
+ &gt; 2,000,000,000 suspends and will never be lower.
+ </item>
</taglist>
</desc>
</func>
<func>
- <name name="suspend_process" arity="1"/>
- <fsummary>Suspends a process.</fsummary>
- <desc>
- <p>Suspends the process identified by
- <c><anno>Suspendee</anno></c>. The same as calling
- <seealso marker="#suspend_process/2">erlang:suspend_process(<anno>Suspendee</anno>,
- [])</seealso>.</p>
- <warning>
- <p>This BIF is intended for debugging only.</p>
- </warning>
- </desc>
- </func>
-
- <func>
<name name="system_flag" arity="2" clause_i="1"/>
- <fsummary>Sets system flag <c>backtrace_depth</c>.</fsummary>
+ <fsummary>Set system flag <c>backtrace_depth</c>.</fsummary>
<desc>
<p>Sets the maximum depth of call stack back-traces in the
- exit reason element of <c>'EXIT'</c> tuples.</p>
+ exit reason element of <c>'EXIT'</c> tuples. The flag
+ also limits the stacktrace depth returned by <c>process_info</c>
+ item <c>current_stacktrace.</c></p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
<func>
<name name="system_flag" arity="2" clause_i="2"/>
- <fsummary>Sets system flag <c>cpu_topology</c>.</fsummary>
+ <fsummary>Set system flag <c>cpu_topology</c>.</fsummary>
<type name="cpu_topology"/>
<type name="level_entry"/>
<type name="level_tag"/>
@@ -6503,52 +6636,52 @@ ok
<desc>
<warning>
<p><marker id="system_flag_cpu_topology"></marker>
- This argument is <em>deprecated</em> and scheduled for
- removal in <c>ERTS</c> 5.10/OTP R16. Instead of using this
- argument, use command-line argument
- <seealso marker="erts:erl#+sct">+sct</seealso> in
- <c>erl(1)</c>.</p>
+ <em>This argument is deprecated.</em>
+ Instead of using this argument, use command-line argument
+ <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> in
+ <c>erl(1)</c>.</p>
<p>When this argument is removed, a final CPU topology
- to use is determined at emulator boot time.</p>
+ to use is determined at emulator boot time.</p>
</warning>
<p>Sets the user-defined <c><anno>CpuTopology</anno></c>.
- The user-defined
- CPU topology overrides any automatically detected
- CPU topology. By passing <c>undefined</c> as
- <c><anno>CpuTopology</anno></c>,
- the system reverts to the CPU topology automatically
- detected. The returned value equals the value returned
- from <c>erlang:system_info(cpu_topology)</c> before the
- change was made.</p>
+ The user-defined
+ CPU topology overrides any automatically detected
+ CPU topology. By passing <c>undefined</c> as
+ <c><anno>CpuTopology</anno></c>,
+ the system reverts to the CPU topology automatically
+ detected. The returned value equals the value returned
+ from <c>erlang:system_info(cpu_topology)</c> before the
+ change was made.</p>
<p>Returns the old value of the flag.</p>
<p>The CPU topology is used when binding schedulers to logical
- processors. If schedulers are already bound when the CPU
- topology is changed, the schedulers are sent a request
- to rebind according to the new CPU topology.</p>
+ processors. If schedulers are already bound when the CPU
+ topology is changed, the schedulers are sent a request
+ to rebind according to the new CPU topology.</p>
<p>The user-defined CPU topology can also be set by passing
- command-line argument
- <seealso marker="erts:erl#+sct">+sct</seealso> to
- <c>erl(1)</c>.</p>
+ command-line argument
+ <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> to
+ <c>erl(1)</c>.</p>
<p>For information on type <c><anno>CpuTopology</anno></c>
- and more, see
- <seealso marker="#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>
- as well as the command-line flags
- <seealso marker="erts:erl#+sct">+sct</seealso> and
- <seealso marker="erts:erl#+sbt">+sbt</seealso> in
- <c>erl(1)</c>.</p>
+ and more, see
+ <seealso marker="#system_info_cpu_topology">
+ <c>erlang:system_info(cpu_topology)</c></seealso>
+ as well as command-line flags
+ <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> and
+ <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso> in
+ <c>erl(1)</c>.</p>
</desc>
</func>
<func>
<name name="system_flag" arity="2" clause_i="3"/>
- <fsummary>Sets <c>system_flag_dirty_cpu_schedulers_online</c>.</fsummary>
+ <fsummary>Set system_flag_dirty_cpu_schedulers_online.</fsummary>
<desc>
<p><marker id="system_flag_dirty_cpu_schedulers_online"></marker>
- Sets the number of dirty CPU schedulers online. Range is
- <![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]>, where <c>N</c>
- is the smallest of the return values of
- <c>erlang:system_info(dirty_cpu_schedulers)</c> and
- <c>erlang:system_info(schedulers_online)</c>.</p>
+ Sets the number of dirty CPU schedulers online. Range is
+ <c><![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]></c>, where <c>N</c>
+ is the smallest of the return values of
+ <c>erlang:system_info(dirty_cpu_schedulers)</c> and
+ <c>erlang:system_info(schedulers_online)</c>.</p>
<p>Returns the old value of the flag.</p>
<p>The number of dirty CPU schedulers online can change if the
number of schedulers online changes. For example, if 12
@@ -6559,20 +6692,22 @@ ok
down to 3. Similarly, the number of dirty CPU schedulers
online increases proportionally to increases in the number of
schedulers online.</p>
- <note><p>The dirty schedulers functionality is experimental.
- Enable support for dirty schedulers when building OTP to
- try out the functionality.</p>
+ <note>
+ <p>The dirty schedulers functionality is experimental.
+ Enable support for dirty schedulers when building OTP to
+ try out the functionality.</p>
</note>
<p>For more information, see
- <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso>
- and
- <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>.</p>
+ <seealso marker="#system_info_dirty_cpu_schedulers">
+ <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso> and
+ <seealso marker="#system_info_dirty_cpu_schedulers_online">
+ <c>erlang:system_info(dirty_cpu_schedulers_online)</c></seealso>.</p>
</desc>
</func>
<func>
<name name="system_flag" arity="2" clause_i="4"/>
- <fsummary>Sets system flag <c>fullsweep_after</c>.</fsummary>
+ <fsummary>Set system flag fullsweep_after.</fsummary>
<desc>
<p>Sets system flag <c>fullsweep_after</c>.
<c><anno>Number</anno></c> is a non-negative integer indicating
@@ -6591,276 +6726,297 @@ ok
<func>
<name name="system_flag" arity="2" clause_i="5"/>
- <fsummary>Set system flag microstate_accounting</fsummary>
- <desc><p><marker id="system_flag_microstate_accounting"></marker>
- Turns on/off microstate accounting measurements. By passing reset it is possible to reset
- all counters to 0.</p>
- <p>For more information see,
- <seealso marker="#statistics_microstate_accounting">erlang:statistics(microstate_accounting)</seealso>.
- </p>
+ <fsummary>Set system flag microstate_accounting.</fsummary>
+ <desc>
+ <p><marker id="system_flag_microstate_accounting"></marker>
+ Turns on/off microstate accounting measurements. When passing reset,
+ all counters are reset to 0.</p>
+ <p>For more information see
+ <seealso marker="#statistics_microstate_accounting">
+ <c>statistics(microstate_accounting)</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="system_flag" arity="2" clause_i="6"/>
- <fsummary>Sets system flag <c>min_heap_size</c>.</fsummary>
+ <fsummary>Set system flag min_heap_size.</fsummary>
<desc>
<p>Sets the default minimum heap size for processes. The size
- is given in words. The new <c>min_heap_size</c> effects
+ is specified in words. The new <c>min_heap_size</c> effects
only processes spawned after the change of
<c>min_heap_size</c> has been made. <c>min_heap_size</c>
can be set for individual processes by using
- <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
- <seealso marker="#process_flag/2">process_flag/2</seealso>.</p>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/4</c></seealso> or
+ <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>.</p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
<func>
<name name="system_flag" arity="2" clause_i="7"/>
- <fsummary>Sets system flag <c>min_bin_vheap_size</c>.</fsummary>
+ <fsummary>Set system flag min_bin_vheap_size.</fsummary>
<desc>
<p>Sets the default minimum binary virtual heap size for
- processes. The size is given in words.
+ processes. The size is specified in words.
The new <c>min_bin_vhheap_size</c> effects only
processes spawned after the change of
- <c>min_bin_vhheap_size</c> has been made.
+ <c>min_bin_vheap_size</c> has been made.
<c>min_bin_vheap_size</c> can be set for individual
processes by using
- <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
- <seealso marker="#process_flag/2">process_flag/2</seealso>.</p>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/2,3,4</c></seealso> or
+ <seealso marker="#process_flag/2"><c>process_flag/2</c></seealso>.</p>
<p>Returns the old value of the flag.</p>
</desc>
</func>
- <marker id="system_flag_max_heap_size"></marker>
<func>
<name name="system_flag" arity="2" clause_i="8"/>
- <fsummary>Sets system flag <c>max_heap_size</c></fsummary>
+ <fsummary>Set system flag max_heap_size.</fsummary>
<type name="max_heap_size"/>
<desc>
+ <marker id="system_flag_max_heap_size"></marker>
<p>
Sets the default maximum heap size settings for processes.
- The size is given in words. The new <c>max_heap_size</c>
+ The size is specified 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>
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/2,3,4</c></seealso> or
+ <seealso marker="#process_flag_message_queue_data">
+ <c>process_flag/2</c></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>
+ <fsummary>Set system flag multi_scheduling.</fsummary>
<desc>
<p><marker id="system_flag_multi_scheduling"></marker>
If multi-scheduling is enabled, more than one scheduler
thread is used by the emulator. Multi-scheduling can be
blocked in two different ways. Either all schedulers but
- one is blocked, or all <em>normal</em> schedulers but
- one is blocked. When only normal schedulers are blocked
- dirty schedulers are free to continue to schedule
- processes.</p>
+ one is blocked, or all <em>normal</em> schedulers but
+ one is blocked. When only normal schedulers are blocked,
+ dirty schedulers are free to continue to schedule
+ processes.</p>
<p>If <c><anno>BlockState</anno> =:= block</c>, multi-scheduling is
blocked. That is, one and only one scheduler thread will
- execute. If <c><anno>BlockState</anno> =:= unblock</c> and no one
+ execute. If <c><anno>BlockState</anno> =:= unblock</c> and no one
else blocks multi-scheduling, and this process has
blocked only once, multi-scheduling is unblocked.</p>
<p>If <c><anno>BlockState</anno> =:= block_normal</c>, normal
- multi-scheduling is blocked. That is, only one normal scheduler
- thread will execute, but multiple dirty schedulers may execute.
- If <c><anno>BlockState</anno> =:= unblock_normal</c> and no one
+ multi-scheduling is blocked. That is, only one normal scheduler
+ thread will execute, but multiple dirty schedulers can execute.
+ If <c><anno>BlockState</anno> =:= unblock_normal</c> and no one
else blocks normal multi-scheduling, and this process has
blocked only once, normal multi-scheduling is unblocked.</p>
- <p>One process can block multi-scheduling as well as normal
- multi-scheduling multiple times. If a process has blocked
- multiple times, it must unblock exactly as many times as it
- has blocked before it has released its multi-scheduling
- block. If a process that has blocked multi-scheduling or normal
- multi scheduling exits, it automatically releases its blocking
- of multi-scheduling and normal multi-scheduling.</p>
+ <p>One process can block multi-scheduling and normal
+ multi-scheduling multiple times. If a process has blocked
+ multiple times, it must unblock exactly as many times as it
+ has blocked before it has released its multi-scheduling
+ block. If a process that has blocked multi-scheduling or normal
+ multi-scheduling exits, it automatically releases its blocking
+ of multi-scheduling and normal multi-scheduling.</p>
<p>The return values are <c>disabled</c>, <c>blocked</c>,
<c>blocked_normal</c>, or <c>enabled</c>. The returned value
- describes the state just after the call to
+ describes the state just after the call to
<c>erlang:system_flag(multi_scheduling, <anno>BlockState</anno>)</c>
has been made. For information about the return values, see
- <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>.</p>
+ <seealso marker="#system_info_multi_scheduling">
+ <c>erlang:system_info(multi_scheduling)</c></seealso>.</p>
<note><p>Blocking of multi-scheduling and normal multi-scheduling
- is normally not needed. If you feel that you need to use these
- features, consider it a few more times again. Blocking
- multi-scheduling is only to be used as a last resort, as it is
- most likely a <em>very inefficient</em> way to solve the problem.</p>
+ is normally not needed. If you feel that you need to use these
+ features, consider it a few more times again. Blocking
+ multi-scheduling is only to be used as a last resort, as it is
+ most likely a <em>very inefficient</em> way to solve the problem.</p>
</note>
<p>See also
- <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>,
- <seealso marker="#system_info_normal_multi_scheduling_blockers">erlang:system_info(normal_multi_scheduling_blockers)</seealso>,
- <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>, and
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
+ <seealso marker="#system_info_multi_scheduling">
+ <c>erlang:system_info(multi_scheduling)</c></seealso>,
+ <seealso marker="#system_info_normal_multi_scheduling_blockers">
+ <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling_blockers">
+ <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>, and
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.</p>
</desc>
</func>
<func>
<name name="system_flag" arity="2" clause_i="10"/>
- <fsummary>Sets system flag <c>scheduler_bind_type</c>.</fsummary>
+ <fsummary>Set system flag scheduler_bind_type.</fsummary>
<type name="scheduler_bind_type"/>
<desc>
<warning>
<p><marker id="system_flag_scheduler_bind_type"></marker>
- This argument is <em>deprecated</em> and scheduled for
- removal in <c>ERTS</c> 5.10/OTP R16. Instead of using this
- argument, use command-line argument
- <seealso marker="erts:erl#+sbt">+sbt</seealso> in <c>erl(1)</c>.
- When this argument is removed, a final scheduler bind
- type to use is determined at emulator boot time.</p>
+ <em>This argument is deprecated.</em>
+ Instead of using this argument, use command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso> in
+ <c>erl(1)</c>. When this argument is removed, a final scheduler bind
+ type to use is determined at emulator boot time.</p>
</warning>
<p>Controls if and how schedulers are bound to logical
- processors.</p>
+ processors.</p>
<p>When <c>erlang:system_flag(scheduler_bind_type, <anno>How</anno>)</c>
- is called, an asynchronous signal is sent to all schedulers
- online, causing them to try to bind or unbind as requested.</p>
+ is called, an asynchronous signal is sent to all schedulers
+ online, causing them to try to bind or unbind as requested.</p>
<note><p>If a scheduler fails to bind, this is often silently
- ignored, as it is not always possible to verify valid
- logical processor identifiers. If an error is reported,
- it is reported to <c>error_logger</c>. To verify that the
- schedulers have bound as requested, call
- <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.</p>
+ ignored, as it is not always possible to verify valid
+ logical processor identifiers. If an error is reported,
+ it is reported to <c>error_logger</c>. To verify that the
+ schedulers have bound as requested, call
+ <seealso marker="#system_info_scheduler_bindings">
+ <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p>
</note>
<p>Schedulers can be bound on newer Linux,
- Solaris, FreeBSD, and Windows systems, but more systems will be
- supported in future releases.</p>
+ Solaris, FreeBSD, and Windows systems, but more systems will be
+ supported in future releases.</p>
<p>In order for the runtime system to be able to bind schedulers,
- the CPU topology must be known. If the runtime system fails
- to detect the CPU topology automatically, it can be defined.
- For more information on how to define the CPU topology, see
- command-line flag <seealso marker="erts:erl#+sct">+sct</seealso>
- in <c>erl(1)</c>.</p>
+ the CPU topology must be known. If the runtime system fails
+ to detect the CPU topology automatically, it can be defined.
+ For more information on how to define the CPU topology, see
+ command-line flag <seealso marker="erts:erl#+sct">
+ <c>+sct</c></seealso> in <c>erl(1)</c>.</p>
<p>The runtime system does by default <em>not</em> bind schedulers
- to logical processors.</p>
+ to logical processors.</p>
<note><p>If the Erlang runtime system is the only OS
- process binding threads to logical processors, this
- improves the performance of the runtime system. However,
- if other OS processes (for example, another Erlang
- runtime system) also bind threads to logical processors,
- there can be a performance penalty instead. Sometimes this
- performance penalty can be severe. If so, it is recommended
- to not bind the schedulers.</p>
+ process binding threads to logical processors, this
+ improves the performance of the runtime system. However,
+ if other OS processes (for example, another Erlang
+ runtime system) also bind threads to logical processors,
+ there can be a performance penalty instead. Sometimes this
+ performance penalty can be severe. If so, it is recommended
+ to not bind the schedulers.</p>
</note>
<p>Schedulers can be bound in different ways. Argument
- <c><anno>How</anno></c> determines how schedulers are
- bound and can be any of the following:</p>
+ <c><anno>How</anno></c> determines how schedulers are
+ bound and can be any of the following:</p>
<taglist>
<tag><c>unbound</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt u</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt u</c></seealso> in
+ <c>erl(1)</c>.
+ </item>
<tag><c>no_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt ns</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt ns</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>thread_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt ts</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt ts</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>processor_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt ps</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt ps</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt s</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt s</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>no_node_thread_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt nnts</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt nnts</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>no_node_processor_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt nnps</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt nnps</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>thread_no_node_processor_spread</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt tnnps</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt tnnps</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
<tag><c>default_bind</c></tag>
- <item><p>Same as command-line argument
- <seealso marker="erts:erl#+sbt">+sbt db</seealso> in <c>erl(1)</c>.
- </p></item>
+ <item>Same as command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt db</c></seealso>
+ in <c>erl(1)</c>.
+ </item>
</taglist>
<p>The returned value equals <c><anno>How</anno></c> before flag
<c>scheduler_bind_type</c> was changed.</p>
<p>Failures:</p>
<taglist>
<tag><c>notsup</c></tag>
- <item>
- <p>If binding of schedulers is not supported.</p>
+ <item>If binding of schedulers is not supported.
</item>
<tag><c>badarg</c></tag>
- <item>
- <p>If <c><anno>How</anno></c> is not one of the documented
- alternatives.</p>
+ <item>If <c><anno>How</anno></c> is not one of the documented
+ alternatives.
</item>
<tag><c>badarg</c></tag>
- <item>
- <p>If CPU topology information is unavailable.</p>
+ <item>If CPU topology information is unavailable.
</item>
</taglist>
- <p>The scheduler bind type can also be set by passing
- command-line argument
- <seealso marker="erts:erl#+sbt">+sbt</seealso> to <c>erl(1)</c>.</p>
+ <p>The scheduler bind type can also be set by passing command-line
+ argument <seealso marker="erts:erl#+sbt">
+ <c>+sbt</c></seealso> to <c>erl(1)</c>.</p>
<p>For more information, see
- <seealso marker="#system_info_scheduler_bind_type">erlang:system_info(scheduler_bind_type)</seealso>,
- <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>,
- as well as command-line flags
- <seealso marker="erts:erl#+sbt">+sbt</seealso>
- and <seealso marker="erts:erl#+sct">+sct</seealso>
- in <c>erl(1)</c>.</p>
+ <seealso marker="#system_info_scheduler_bind_type">
+ <c>erlang:system_info(scheduler_bind_type)</c></seealso>,
+ <seealso marker="#system_info_scheduler_bindings">
+ <c>erlang:system_info(scheduler_bindings)</c></seealso>,
+ as well as command-line flags
+ <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso>
+ and <seealso marker="erts:erl#+sct"><c>+sct</c></seealso>
+ in <c>erl(1)</c>.</p>
</desc>
</func>
<func>
<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>
- <p>For more information, see
- <seealso marker="#statistics_scheduler_wall_time">erlang:statistics(scheduler_wall_time)</seealso>.</p>
+ <fsummary>Set system flag scheduler_wall_time.</fsummary>
+ <desc>
+ <p><marker id="system_flag_scheduler_wall_time"></marker>
+ Turns on or off scheduler wall time measurements.</p>
+ <p>For more information, see
+ <seealso marker="#statistics_scheduler_wall_time">
+ <c>statistics(scheduler_wall_time)</c></seealso>.</p>
</desc>
</func>
<func>
<name name="system_flag" arity="2" clause_i="12"/>
- <fsummary>Sets system flag <c>schedulers_online</c>.</fsummary>
+ <fsummary>Set system flag schedulers_online.</fsummary>
<desc>
<p><marker id="system_flag_schedulers_online"></marker>
- Sets the number of schedulers online. Range is
- <![CDATA[1 <= SchedulersOnline <= erlang:system_info(schedulers)]]>.</p>
+ Sets the number of schedulers online. Range is
+ <c><![CDATA[1 <= SchedulersOnline <=
+ erlang:system_info(schedulers)]]></c>.</p>
<p>Returns the old value of the flag.</p>
<p>If the emulator was built with support for
- <seealso marker="#system_flag_dirty_cpu_schedulers_online">dirty schedulers</seealso>,
- changing the number of schedulers online can also change the
- number of dirty CPU schedulers online. For example, if 12
- schedulers and 6 dirty CPU schedulers are online, and
- <c>system_flag/2</c> is used to set the number of schedulers
- online to 6, then the number of dirty CPU schedulers online
- is automatically decreased by half as well, down to 3.
- Similarly, the number of dirty CPU schedulers online increases
- proportionally to increases in the number of schedulers online.</p>
+ <seealso marker="#system_flag_dirty_cpu_schedulers_online">
+ dirty schedulers</seealso>,
+ changing the number of schedulers online can also change the
+ number of dirty CPU schedulers online. For example, if 12
+ schedulers and 6 dirty CPU schedulers are online, and
+ <c>system_flag/2</c> is used to set the number of schedulers
+ online to 6, then the number of dirty CPU schedulers online
+ is automatically decreased by half as well, down to 3.
+ Similarly, the number of dirty CPU schedulers online increases
+ proportionally to increases in the number of schedulers online.</p>
<p>For more information, see
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>
- and
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>.</p>
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso> and
+ <seealso marker="#system_info_schedulers_online">
+ <c>erlang:system_info(schedulers_online)</c></seealso>.</p>
</desc>
</func>
<func>
<name name="system_flag" arity="2" clause_i="13"/>
- <fsummary>Sets system flag <c>trace_control_word</c>.</fsummary>
+ <fsummary>Set system flag trace_control_word.</fsummary>
<desc>
<p>Sets the value of the node trace control word to
<c><anno>TCW</anno></c>, which is to be an unsigned integer.
- For more information, see the function
- <seealso marker="erts:match_spec#set_tcw">set_tcw</seealso>
- in Section "Match Specifications in Erlang" in the
+ For more information, see function
+ <seealso marker="erts:match_spec#set_tcw"><c>set_tcw</c></seealso>
+ in section "Match Specifications in Erlang" in the
User's Guide.</p>
<p>Returns the old value of the flag.</p>
</desc>
@@ -6868,29 +7024,30 @@ ok
<func>
<name name="system_flag" arity="2" clause_i="14"/>
- <fsummary>Finalize the Time Offset</fsummary>
+ <fsummary>Finalize the time offset.</fsummary>
<desc>
<p><marker id="system_flag_time_offset"></marker>
- Finalizes the <seealso marker="#time_offset/0">time offset</seealso>
- when <seealso marker="time_correction#Single_Time_Warp_Mode">single
- time warp mode</seealso> is used. If another time warp mode
- is used, the time offset state is left unchanged.</p>
- <p>Returns the old state identifier. That is:</p>
- <list>
- <item><p>If <c>preliminary</c> is returned, finalization was
- performed and the time offset is now final.</p></item>
-
- <item><p>If <c>final</c> is returned, the time offset was
- already in the final state. This either because another
- <c>erlang:system_flag(time_offset, finalize)</c> call, or
- because <seealso marker="time_correction#No_Time_Warp_Mode">no
- time warp mode</seealso> is used.</p></item>
-
- <item><p>If <c>volatile</c> is returned, the time offset
- cannot be finalized because
- <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
- time warp mode</seealso> is used.</p></item>
- </list>
+ Finalizes the <seealso marker="#time_offset/0">time offset</seealso>
+ when <seealso marker="time_correction#Single_Time_Warp_Mode">single
+ time warp mode</seealso> is used. If another time warp mode
+ is used, the time offset state is left unchanged.</p>
+ <p>Returns the old state identifier, that is:</p>
+ <list>
+ <item><p>If <c>preliminary</c> is returned, finalization was
+ performed and the time offset is now final.</p>
+ </item>
+ <item><p>If <c>final</c> is returned, the time offset was
+ already in the final state. This either because another
+ <c>erlang:system_flag(time_offset, finalize)</c> call or
+ because <seealso marker="time_correction#No_Time_Warp_Mode">no
+ time warp mode</seealso> is used.</p>
+ </item>
+ <item><p>If <c>volatile</c> is returned, the time offset
+ cannot be finalized because
+ <seealso marker="time_correction#Multi_Time_Warp_Mode">multi-time
+ warp mode</seealso> is used.</p>
+ </item>
+ </list>
</desc>
</func>
@@ -6907,7 +7064,7 @@ ok
<type variable="Settings" name_i="2"/>
<type variable="Alloc" name_i="3"/>
<desc>
- <marker id="system_info_allocator_tags"></marker>
+ <marker id="system_info_allocator_tags"></marker>
<p>Returns various information about the allocators of the
current system (emulator) as specified by
<c><anno>Item</anno></c>:</p>
@@ -6926,17 +7083,18 @@ ok
<p><c>erlang:system_info(allocated_areas)</c> is intended
for debugging, and the content is highly
implementation-dependent. The content of the results
- therefore changes when needed without prior notice.</p>
+ therefore changes when needed without prior notice.</p>
<p>Notice that the sum of these values is <em>not</em>
the total amount of memory allocated by the emulator.
Some values are part of other values, and some memory
areas are not part of the result. For information about
the total amount of memory allocated by the emulator, see
- <seealso marker="#memory/0">erlang:memory/0,1</seealso>.</p>
+ <seealso marker="#memory/0">
+ <c>erlang:memory/0,1</c></seealso>.</p>
</item>
<tag><c>allocator</c></tag>
<item>
- <marker id="system_info_allocator"></marker>
+ <marker id="system_info_allocator"></marker>
<p>Returns <c>{<anno>Allocator</anno>, <anno>Version</anno>,
<anno>Features</anno>, <anno>Settings</anno></c>, where:</p>
<list type="bulleted">
@@ -6966,27 +7124,30 @@ ok
</item>
</list>
<p>See also "System Flags Effecting erts_alloc" in
- <seealso marker="erts:erts_alloc#flags">erts_alloc(3)</seealso>.</p>
+ <seealso marker="erts:erts_alloc#flags">
+ <c>erts_alloc(3)</c></seealso>.</p>
</item>
<tag><c>alloc_util_allocators</c></tag>
<item>
- <marker id="system_info_alloc_util_allocators"></marker>
- <p>Returns a list of the names of all allocators using
- the <c>ERTS</c> internal <c>alloc_util</c> framework
- as atoms. For more information, see Section
- <seealso marker="erts:erts_alloc#alloc_util">"The
- alloc_util framework" in erts_alloc(3)</seealso>.</p>
+ <marker id="system_info_alloc_util_allocators"></marker>
+ <p>Returns a list of the names of all allocators using
+ the ERTS internal <c>alloc_util</c> framework
+ as atoms. For more information, see section
+ <seealso marker="erts:erts_alloc#alloc_util">The
+ alloc_util framework</seealso>
+ in <c>erts_alloc(3)</c>.</p>
</item>
<tag><c>{allocator, <anno>Alloc</anno>}</c></tag>
<item>
- <marker id="system_info_allocator_tuple"></marker>
+ <marker id="system_info_allocator_tuple"></marker>
<p>Returns information about the specified allocator.
- 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. If <c><anno>Alloc</anno></c> is not a
- recognized allocator, <c>undefined</c> is returned.
- If <c><anno>Alloc</anno></c> is disabled,
+ As from ERTS 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.
+ 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>
<p>Notice that the information returned is highly
implementation-dependent and can be changed or removed
@@ -6995,15 +7156,14 @@ ok
as it can be of interest for others it has been
briefly documented.</p>
<p>The recognized allocators are listed in
- <seealso marker="erts:erts_alloc">erts_alloc(3)</seealso>.
- 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>
-
+ <seealso marker="erts:erts_alloc"><c>erts_alloc(3)</c></seealso>.
+ Information about super carriers can be obtained from
+ ERTS 8.0 with <c>{allocator, erts_mmap}</c> or from
+ ERTS 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
+ the returned information
more or less speaks for itself, but it can be worth
explaining some things. Call counts are presented by two
values, the first value is giga calls, and the second
@@ -7019,19 +7179,20 @@ ok
<item>The third is the maximum value since the emulator
was started.</item>
</list>
- <p>If only one value is present, it is the current value.
+ <p>If only one value is present, it is the current value.
<c>fix_alloc</c> memory block types are presented by two
values. The first value is the memory pool size and
the second value is the used memory size.</p>
</item>
<tag><c>{allocator_sizes, <anno>Alloc</anno>}</c></tag>
<item>
- <marker id="system_info_allocator_sizes"></marker>
- <p>Returns various size information for the specified
- allocator. The information returned is a subset of the
- information returned by
- <seealso marker="#system_info_allocator_tuple"><c>erlang:system_info({allocator, <anno>Alloc</anno>})</c></seealso>.
- </p>
+ <marker id="system_info_allocator_sizes"></marker>
+ <p>Returns various size information for the specified
+ allocator. The information returned is a subset of the
+ information returned by
+ <seealso marker="#system_info_allocator_tuple">
+ <c>erlang:system_info({allocator,
+ <anno>Alloc</anno>})</c></seealso>.</p>
</item>
</taglist>
</desc>
@@ -7065,69 +7226,74 @@ ok
The <c>info_list()</c> can be extended in a future release.
</type_desc>
<desc>
- <marker id="system_info_cpu_topology_tags"></marker>
- <marker id="system_info_cpu_topology"></marker>
+ <marker id="system_info_cpu_topology_tags"></marker>
+ <marker id="system_info_cpu_topology"></marker>
<p>Returns various information about the CPU topology of
the current system (emulator) as specified by
<c><anno>Item</anno></c>:</p>
<taglist>
<tag><c>cpu_topology</c></tag>
<item>
- <p>Returns the <c><anno>CpuTopology</anno></c> currently used by
- the emulator. The CPU topology is used when binding schedulers
- to logical processors. The CPU topology used is the
- <seealso marker="erlang#system_info_cpu_topology_defined">user-defined CPU topology</seealso>,
- if such exists, otherwise the
- <seealso marker="erlang#system_info_cpu_topology_detected">automatically detected CPU topology</seealso>,
- if such exists. If no CPU topology
- exists, <c>undefined</c> is returned.</p>
- <p><c>node</c> refers to Non-Uniform Memory Access (NUMA)
- nodes. <c>thread</c> refers to hardware threads
- (for example, Intel hyper-threads).</p>
+ <p>Returns the <c><anno>CpuTopology</anno></c> currently used by
+ the emulator. The CPU topology is used when binding schedulers
+ to logical processors. The CPU topology used is the
+ <seealso marker="erlang#system_info_cpu_topology_defined">
+ user-defined CPU topology</seealso>,
+ if such exists, otherwise the
+ <seealso marker="erlang#system_info_cpu_topology_detected">
+ automatically detected CPU topology</seealso>,
+ if such exists. If no CPU topology
+ exists, <c>undefined</c> is returned.</p>
+ <p><c>node</c> refers to Non-Uniform Memory Access (NUMA)
+ nodes. <c>thread</c> refers to hardware threads
+ (for example, Intel hyper-threads).</p>
<p>A level in term <c><anno>CpuTopology</anno></c> can be
- omitted if only one entry exists and
- <c><anno>InfoList</anno></c> is empty.</p>
- <p><c>thread</c> can only be a sub level to <c>core</c>.
- <c>core</c> can be a sub level to <c>processor</c>
- or <c>node</c>. <c>processor</c> can be on the
- top level or a sub level to <c>node</c>. <c>node</c>
- can be on the top level or a sub level to
- <c>processor</c>. That is, NUMA nodes can be processor
- internal or processor external. A CPU topology can
- consist of a mix of processor internal and external
- NUMA nodes, as long as each logical CPU belongs to
- <em>one</em> NUMA node. Cache hierarchy is not part of
- the <c><anno>CpuTopology</anno></c> type, but will be in a
- future release. Other things can also make it into the CPU
- topology in a future release. In other words, expect the
- <c><anno>CpuTopology</anno></c> type to change.</p>
+ omitted if only one entry exists and
+ <c><anno>InfoList</anno></c> is empty.</p>
+ <p><c>thread</c> can only be a sublevel to <c>core</c>.
+ <c>core</c> can be a sublevel to <c>processor</c>
+ or <c>node</c>. <c>processor</c> can be on the
+ top level or a sublevel to <c>node</c>. <c>node</c>
+ can be on the top level or a sublevel to
+ <c>processor</c>. That is, NUMA nodes can be processor
+ internal or processor external. A CPU topology can
+ consist of a mix of processor internal and external
+ NUMA nodes, as long as each logical CPU belongs to
+ <em>one</em> NUMA node. Cache hierarchy is not part of
+ the <c><anno>CpuTopology</anno></c> type, but will be in a
+ future release. Other things can also make it into the CPU
+ topology in a future release. So, expect the
+ <c><anno>CpuTopology</anno></c> type to change.</p>
</item>
<tag><c>{cpu_topology, defined}</c></tag>
<item>
- <marker id="system_info_cpu_topology_defined"></marker>
+ <marker id="system_info_cpu_topology_defined"></marker>
<p>Returns the user-defined <c><anno>CpuTopology</anno></c>.
- For more information, see command-line flag
- <seealso marker="erts:erl#+sct">+sct</seealso> in
- <c>erl(1)</c> and argument
- <seealso marker="#system_info_cpu_topology">cpu_topology</seealso>.</p>
+ For more information, see command-line flag
+ <seealso marker="erts:erl#+sct"><c>+sct</c></seealso> in
+ <c>erl(1)</c> and argument
+ <seealso marker="#system_info_cpu_topology">
+ <c>cpu_topology</c></seealso>.</p>
</item>
<tag><c>{cpu_topology, detected}</c></tag>
<item>
- <marker id="system_info_cpu_topology_detected"></marker>
+ <marker id="system_info_cpu_topology_detected"></marker>
<p>Returns the automatically detected
- <c><anno>CpuTopology</anno>y</c>. The
- emulator detects the CPU topology on some newer
- Linux, Solaris, FreeBSD, and Windows systems.
- On Windows system with more than 32 logical processors,
- the CPU topology is not detected.</p>
+ <c><anno>CpuTopology</anno>y</c>. The
+ emulator detects the CPU topology on some newer
+ Linux, Solaris, FreeBSD, and Windows systems.
+ On Windows system with more than 32 logical processors,
+ the CPU topology is not detected.</p>
<p>For more information, see argument
- <seealso marker="#system_info_cpu_topology">cpu_topology</seealso>.</p>
+ <seealso marker="#system_info_cpu_topology">
+ <c>cpu_topology</c></seealso>.</p>
</item>
<tag><c>{cpu_topology, used}</c></tag>
<item>
<p>Returns <c><anno>CpuTopology</anno></c> used by the emulator.
For more information, see argument
- <seealso marker="#system_info_cpu_topology">cpu_topology</seealso>.</p>
+ <seealso marker="#system_info_cpu_topology">
+ <c>cpu_topology</c></seealso>.</p>
</item>
</taglist>
</desc>
@@ -7144,13 +7310,14 @@ ok
<type name="message_queue_data"/>
<type name="max_heap_size"/>
<desc>
+ <p>Returns information about the default process heap settings:</p>
<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>
+ <c>garbage_collection</c> described below.</p>
</item>
<tag><c>garbage_collection</c></tag>
<item>
@@ -7159,50 +7326,52 @@ ok
<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>
+ <seealso marker="#system_flag/2">
+ <c>erlang:system_flag/2</c></seealso>.
+ <seealso marker="#spawn_opt/4"><c>spawn_opt/2,3,4</c></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>,
+ <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>,
+ system-wide maximum heap size settings for spawned processes.
+ This setting can be set using the 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="erl#+hmaxel"><c>+hmaxel</c></seealso> in
+ <c>erl(1)</c>. It can also be changed at runtime 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
+ <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>
+ <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p>
</item>
- <tag><c>min_heap_size</c></tag>
+ <tag><c>min_heap_size</c></tag>
<item>
- <p>Returns <c>{min_heap_size, <anno>MinHeapSize</anno>}</c>,
+ <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></tag>
+ <tag><marker id="system_info_message_queue_data"/>
+ <c>message_queue_data</c></tag>
<item>
<p>Returns the default value of the <c>message_queue_data</c>
- process flag which is either <c>off_heap</c>, or <c>on_heap</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>
+ process flag, which is either <c>off_heap</c> or <c>on_heap</c>.
+ This default is set by command-line argument
+ <seealso marker="erl#+hmqd"><c>+hmqd</c></seealso> in
+ <c>erl(1)</c>. 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>
+ <tag><c>min_bin_vheap_size</c></tag>
<item>
- <p>Returns <c>{min_bin_vheap_size,
+ <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>
+ minimum binary virtual heap size for spawned processes.</p>
</item>
</taglist>
</desc>
@@ -7273,24 +7442,25 @@ ok
<tag><c>allocated_areas</c>, <c>allocator</c>,
<c>alloc_util_allocators</c>, <c>allocator_sizes</c></tag>
<item>
- <p>See <seealso marker="#system_info_allocator_tags">above</seealso>.</p>
+ <p>See <seealso marker="#system_info_allocator_tags">
+ above</seealso>.</p>
</item>
<tag><c>build_type</c></tag>
<item>
<p>Returns an atom describing the build type of the runtime
- system. This is normally the atom <c>opt</c> for optimized.
- Other possible return values are <c>debug</c>, <c>purify</c>,
- <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>,
- <c>gprof</c>, and <c>lcnt</c>. Possible return values
- can be added or removed at any time without prior notice.</p>
+ system. This is normally the atom <c>opt</c> for optimized.
+ Other possible return values are <c>debug</c>, <c>purify</c>,
+ <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>,
+ <c>gprof</c>, and <c>lcnt</c>. Possible return values
+ can be added or removed at any time without prior notice.</p>
</item>
<tag><c>c_compiler_used</c></tag>
<item>
<p>Returns a two-tuple describing the C compiler used when
- compiling the runtime system. The first element is an
- atom describing the name of the compiler, or <c>undefined</c>
- if unknown. The second element is a term describing the
- version of the compiler, or <c>undefined</c> if unknown.</p>
+ compiling the runtime system. The first element is an
+ atom describing the name of the compiler, or <c>undefined</c>
+ if unknown. The second element is a term describing the
+ version of the compiler, or <c>undefined</c> if unknown.</p>
</item>
<tag><c>check_io</c></tag>
<item>
@@ -7307,12 +7477,13 @@ ok
Erlang/OTP release that the current emulator has been
set to be backward compatible with. The compatibility
mode can be configured at startup by using command-line flag
- <seealso marker="erts:erl#compat_rel">+R</seealso> in
+ <seealso marker="erts:erl#compat_rel"><c>+R</c></seealso> in
<c>erl(1)</c>.</p>
</item>
<tag><c>cpu_topology</c></tag>
<item>
- <p>See <seealso marker="#system_info_cpu_topology_tags">above</seealso>.</p>
+ <p>See <seealso
+ marker="#system_info_cpu_topology_tags">above</seealso>.</p>
</item>
<tag><c>creation</c></tag>
<item>
@@ -7328,22 +7499,22 @@ ok
</item>
<tag><c>debug_compiled</c></tag>
<item>
- <p>Returns <c>true</c> if the emulator has been debug
- compiled, otherwise <c>false</c>.</p>
+ <p>Returns <c>true</c> if the emulator has been
+ debug-compiled, otherwise <c>false</c>.</p>
</item>
<tag><c>delayed_node_table_gc</c></tag>
<item>
<marker id="system_info_delayed_node_table_gc"></marker>
<p>Returns the amount of time in seconds garbage collection
- of an entry in a node table is delayed. This limit can be set
- on startup by passing the command line flag
- <seealso marker="erts:erl#+zdntgc">+zdntgc</seealso>
- to <c>erl</c>. For more information see the documentation of the
- command line flag.</p>
+ of an entry in a node table is delayed. This limit can be set
+ on startup by passing command-line flag
+ <seealso marker="erts:erl#+zdntgc"><c>+zdntgc</c></seealso>
+ to <c>erl(1)</c>. For more information, see the documentation of
+ the command-line flag.</p>
</item>
<tag><c>dirty_cpu_schedulers</c></tag>
<item>
- <marker id="system_info_dirty_cpu_schedulers"></marker>
+ <marker id="system_info_dirty_cpu_schedulers"></marker>
<p>Returns the number of dirty CPU scheduler threads used by
the emulator. Dirty CPU schedulers execute CPU-bound
native functions, such as NIFs, linked-in driver code,
@@ -7355,23 +7526,31 @@ ok
can be changed at any time. The number of dirty CPU
schedulers can be set at startup by passing
command-line flag
- <seealso marker="erts:erl#+SDcpu">+SDcpu</seealso> or
- <seealso marker="erts:erl#+SDPcpu">+SDPcpu</seealso> in
+ <seealso marker="erts:erl#+SDcpu"><c>+SDcpu</c></seealso> or
+ <seealso marker="erts:erl#+SDPcpu"><c>+SDPcpu</c></seealso> in
<c>erl(1)</c>.</p>
- <p>Notice that the dirty schedulers functionality is
+ <p>Notice that the dirty schedulers functionality is
experimental. Enable support for dirty schedulers when
building OTP to try out the functionality.</p>
<p>See also
- <seealso marker="#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>,
- <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>,
- <seealso marker="#system_info_dirty_io_schedulers">erlang:system_info(dirty_io_schedulers)</seealso>,
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>,
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>, and
- <seealso marker="#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.</p>
+ <seealso marker="#system_flag_dirty_cpu_schedulers_online">
+ <c>erlang:system_flag(dirty_cpu_schedulers_online,
+ DirtyCPUSchedulersOnline)</c></seealso>,
+ <seealso marker="#system_info_dirty_cpu_schedulers_online">
+ <c>erlang:system_info(dirty_cpu_schedulers_online)</c></seealso>,
+ <seealso marker="#system_info_dirty_io_schedulers">
+ <c>erlang:system_info(dirty_io_schedulers)</c></seealso>,
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>,
+ <seealso marker="#system_info_schedulers_online">
+ <c>erlang:system_info(schedulers_online)</c></seealso>, and
+ <seealso marker="#system_flag_schedulers_online">
+ <c>erlang:system_flag(schedulers_online,
+ SchedulersOnline)</c></seealso>.</p>
</item>
<tag><c>dirty_cpu_schedulers_online</c></tag>
<item>
- <marker id="system_info_dirty_cpu_schedulers_online"></marker>
+ <marker id="system_info_dirty_cpu_schedulers_online"></marker>
<p>Returns the number of dirty CPU schedulers online.
The return value satisfies
<c><![CDATA[1 <= DirtyCPUSchedulersOnline <= N]]></c>,
@@ -7380,51 +7559,60 @@ ok
<c>erlang:system_info(schedulers_online)</c>.</p>
<p>The number of dirty CPU schedulers online can be set at
startup by passing command-line flag
- <seealso marker="erts:erl#+SDcpu">+SDcpu</seealso> in
+ <seealso marker="erts:erl#+SDcpu"><c>+SDcpu</c></seealso> in
<c>erl(1)</c>.</p>
<p>Notice that the dirty schedulers functionality is
experimental. Enable support for dirty schedulers when
building OTP to try out the functionality.</p>
<p>For more information, see
- <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso>,
- <seealso marker="#system_info_dirty_io_schedulers">erlang:system_info(dirty_io_schedulers)</seealso>,
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>, and
- <seealso marker="#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.</p>
+ <seealso marker="#system_info_dirty_cpu_schedulers">
+ <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso>,
+ <seealso marker="#system_info_dirty_io_schedulers">
+ <c>erlang:system_info(dirty_io_schedulers)</c></seealso>,
+ <seealso marker="#system_info_schedulers_online">
+ <c>erlang:system_info(schedulers_online)</c></seealso>, and
+ <seealso marker="#system_flag_dirty_cpu_schedulers_online">
+ <c>erlang:system_flag(dirty_cpu_schedulers_online,
+ DirtyCPUSchedulersOnline)</c></seealso>.</p>
</item>
<tag><c>dirty_io_schedulers</c></tag>
<item>
- <marker id="system_info_dirty_io_schedulers"></marker>
+ <marker id="system_info_dirty_io_schedulers"></marker>
<p>Returns the number of dirty I/O schedulers as an integer.
Dirty I/O schedulers execute I/O-bound native functions,
such as NIFs and linked-in driver code, which cannot be
managed cleanly by the normal emulator schedulers.</p>
<p>This value can be set at startup by passing command-line
- argument <seealso marker="erts:erl#+SDio">+SDio</seealso>
+ argument <seealso marker="erts:erl#+SDio"><c>+SDio</c></seealso>
in <c>erl(1)</c>.</p>
- <p>Notice that the dirty schedulers functionality is
+ <p>Notice that the dirty schedulers functionality is
experimental. Enable support for dirty schedulers when
building OTP to try out the functionality.</p>
<p>For more information, see
- <seealso marker="#system_info_dirty_cpu_schedulers">erlang:system_info(dirty_cpu_schedulers)</seealso>,
- <seealso marker="#system_info_dirty_cpu_schedulers_online">erlang:system_info(dirty_cpu_schedulers_online)</seealso>, and
- <seealso marker="#system_flag_dirty_cpu_schedulers_online">erlang:system_flag(dirty_cpu_schedulers_online, DirtyCPUSchedulersOnline)</seealso>.</p>
+ <seealso marker="#system_info_dirty_cpu_schedulers">
+ <c>erlang:system_info(dirty_cpu_schedulers)</c></seealso>,
+ <seealso marker="#system_info_dirty_cpu_schedulers_online">
+ <c>erlang:system_info(dirty_cpu_schedulers_online)</c></seealso>,
+ and <seealso marker="#system_flag_dirty_cpu_schedulers_online">
+ <c>erlang:system_flag(dirty_cpu_schedulers_online,
+ DirtyCPUSchedulersOnline)</c></seealso>.</p>
</item>
<tag><c>dist</c></tag>
<item>
<p>Returns a binary containing a string of distribution
information formatted as in Erlang crash dumps. For more
- information, see Section
- <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso>
+ information, see section <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
in the User's Guide.</p>
</item>
<tag><c>dist_buf_busy_limit</c></tag>
<item>
- <marker id="system_info_dist_buf_busy_limit"></marker>
+ <marker id="system_info_dist_buf_busy_limit"></marker>
<p>Returns the value of the distribution buffer busy limit
- in bytes. This limit can be set at startup by passing
- command-line flag
- <seealso marker="erts:erl#+zdbbl">+zdbbl</seealso>
- to <c>erl</c>.</p>
+ in bytes. This limit can be set at startup by passing
+ command-line flag
+ <seealso marker="erts:erl#+zdbbl"><c>+zdbbl</c></seealso>
+ to <c>erl(1)</c>.</p>
</item>
<tag><c>dist_ctrl</c></tag>
<item>
@@ -7442,61 +7630,63 @@ ok
<item>
<p>Returns a string containing the Erlang driver version
used by the runtime system. It has the form
- <seealso marker="erts:erl_driver#version_management">"&lt;major ver&gt;.&lt;minor ver&gt;"</seealso>.</p>
+ <seealso marker="erts:erl_driver#version_management">
+ "&lt;major ver&gt;.&lt;minor ver&gt;"</seealso>.</p>
</item>
<tag><c>dynamic_trace</c></tag>
<item>
<p>Returns an atom describing the dynamic trace framework
- compiled into the virtual machine. It can be
- <c>dtrace</c>, <c>systemtap</c>, or <c>none</c>. For a
- commercial or standard build, it is always <c>none</c>.
- The other return values indicate a custom configuration
- (for example, <c>./configure --with-dynamic-trace=dtrace</c>).
- For more information about dynamic tracing, see the
- <seealso marker="runtime_tools:dyntrace">dyntrace</seealso>
- manual page and the
- <c>README.dtrace</c>/<c>README.systemtap</c> files in the
- Erlang source code top directory.</p>
+ compiled into the virtual machine. It can be
+ <c>dtrace</c>, <c>systemtap</c>, or <c>none</c>. For a
+ commercial or standard build, it is always <c>none</c>.
+ The other return values indicate a custom configuration
+ (for example, <c>./configure --with-dynamic-trace=dtrace</c>).
+ For more information about dynamic tracing, see
+ <seealso marker="runtime_tools:dyntrace">
+ <c>dyntrace(3)</c></seealso> manual page and the
+ <c>README.dtrace</c>/<c>README.systemtap</c> files in the
+ Erlang source code top directory.</p>
</item>
<tag><c>dynamic_trace_probes</c></tag>
<item>
<p>Returns a <c>boolean()</c> indicating if dynamic trace
- probes (<c>dtrace</c> or <c>systemtap</c>) are built into
- the emulator. This can only be <c>true</c> if the Virtual
- Machine was built for dynamic tracing (that is,
- <c>system_info(dynamic_trace)</c> returns
- <c>dtrace</c> or <c>systemtap</c>).</p>
- </item>
- <tag><marker id="system_info_end_time"/><c>end_time</c></tag>
- <item><p>The last <seealso marker="#monotonic_time/0">Erlang monotonic
- time</seealso> in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso> that
- can be represented internally in the current Erlang runtime system
- instance. The time between the
- <seealso marker="#system_info_start_time">start time</seealso> and
- the end time is at least a quarter of a millennium.</p></item>
+ probes (<c>dtrace</c> or <c>systemtap</c>) are built into
+ the emulator. This can only be <c>true</c> if the virtual
+ machine was built for dynamic tracing (that is,
+ <c>system_info(dynamic_trace)</c> returns
+ <c>dtrace</c> or <c>systemtap</c>).</p>
+ </item>
+ <tag><marker id="system_info_end_time"/><c>end_time</c></tag>
+ <item>
+ <p>The last <seealso marker="#monotonic_time/0">Erlang monotonic
+ time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso> that
+ can be represented internally in the current Erlang runtime system
+ instance. The time between the
+ <seealso marker="#system_info_start_time">start time</seealso> and
+ the end time is at least a quarter of a millennium.</p>
+ </item>
<tag><c>elib_malloc</c></tag>
<item>
<p>This option will be removed in a future release.
- The return value will always be <c>false</c>, as the
- <c>elib_malloc</c> allocator has been removed.</p>
+ The return value will always be <c>false</c>, as the
+ <c>elib_malloc</c> allocator has been removed.</p>
</item>
- <tag><marker id="system_info_eager_check_io"/><c>eager_check_io</c></tag>
+ <tag><marker id="system_info_eager_check_io"/>
+ <c>eager_check_io</c></tag>
<item>
- <p>
- Returns the value of the <c>erl</c> command line flag
- <seealso marker="erl#+secio">+secio</seealso>
- which is either <c>true</c> or <c>false</c>. See the
- documentation of the command line flag for information about
- the different values.
- </p>
+ <p>Returns the value of command-line flag
+ <seealso marker="erl#+secio"><c>+secio</c></seealso> in
+ <c>erl(1)</c>, which is either <c>true</c> or <c>false</c>.
+ For information about the different values, see the
+ documentation of the command-line flag.</p>
</item>
<tag><c>ets_limit</c></tag>
<item>
<p>Returns the maximum number of ETS tables allowed. This
limit can be increased at startup by passing
command-line flag
- <seealso marker="erts:erl#+e">+e</seealso> to
+ <seealso marker="erts:erl#+e"><c>+e</c></seealso> to
<c>erl(1)</c> or by setting environment variable
<c>ERL_MAX_ETS_TABLES</c> before starting the Erlang
runtime system.</p>
@@ -7514,10 +7704,10 @@ ok
<taglist>
<tag><c>private</c></tag>
<item>
- <p>Each process has a heap reserved for its use and no
- references between heaps of different processes are
- allowed. Messages passed between processes are copied
- between heaps.</p>
+ Each process has a heap reserved for its use and no
+ references between heaps of different processes are
+ allowed. Messages passed between processes are copied
+ between heaps.
</item>
</taglist>
</item>
@@ -7525,8 +7715,9 @@ ok
<item>
<p>Returns a binary containing a string of miscellaneous
system information formatted as in Erlang crash dumps.
- For more information, see Section
- <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso>
+ For more information, see section
+ <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
in the User's Guide.</p>
</item>
<tag><c>kernel_poll</c></tag>
@@ -7538,38 +7729,39 @@ ok
<item>
<p>Returns a binary containing a string of loaded module
information formatted as in Erlang crash dumps. For more
- information, see Section
- <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso>
- in the User's Guide.</p>
+ information, see section
+ <seealso marker="erts:crash_dump">How to interpret the Erlang
+ crash dumps</seealso> in the User's Guide.</p>
</item>
- <tag><c>logical_processors</c></tag>
+ <tag><c>logical_processors</c></tag>
<item>
- <marker id="logical_processors"></marker>
+ <marker id="logical_processors"></marker>
<p>Returns the detected number of logical processors configured
- in the system. The return value is either an integer, or
- the atom <c>unknown</c> if the emulator cannot
- detect the configured logical processors.</p>
+ in the system. The return value is either an integer, or
+ the atom <c>unknown</c> if the emulator cannot
+ detect the configured logical processors.</p>
</item>
- <tag><c>logical_processors_available</c></tag>
+ <tag><c>logical_processors_available</c></tag>
<item>
- <marker id="logical_processors_available"></marker>
+ <marker id="logical_processors_available"></marker>
<p>Returns the detected number of logical processors available
- to the Erlang runtime system. The return value is either an
- integer, or the atom <c>unknown</c> if the emulator
- cannot detect the available logical processors. The number
- of available logical processors is less than or equal to
- the number of
- <seealso marker="#logical_processors_online">logical processors online</seealso>.</p>
+ to the Erlang runtime system. The return value is either an
+ integer, or the atom <c>unknown</c> if the emulator
+ cannot detect the available logical processors. The number
+ of available logical processors is less than or equal to
+ the number of <seealso marker="#logical_processors_online">
+ logical processors online</seealso>.</p>
</item>
- <tag><c>logical_processors_online</c></tag>
+ <tag><c>logical_processors_online</c></tag>
<item>
- <marker id="logical_processors_online"></marker>
+ <marker id="logical_processors_online"></marker>
<p>Returns the detected number of logical processors online on
- the system. The return value is either an integer,
- or the atom <c>unknown</c> if the emulator cannot
- detect logical processors online. The number of logical
- processors online is less than or equal to the number of
- <seealso marker="#logical_processors">logical processors configured</seealso>.</p>
+ the system. The return value is either an integer,
+ or the atom <c>unknown</c> if the emulator cannot
+ detect logical processors online. The number of logical
+ processors online is less than or equal to the number of
+ <seealso marker="#logical_processors">logical processors
+ configured</seealso>.</p>
</item>
<tag><c>machine</c></tag>
<item>
@@ -7578,17 +7770,16 @@ ok
<tag><c>modified_timing_level</c></tag>
<item>
<p>Returns the modified timing-level (an integer) if
- modified timing is enabled, otherwise, <c>undefined</c>.
+ modified timing is enabled, otherwise <c>undefined</c>.
For more information about modified timing, see
command-line flag
- <seealso marker="erts:erl#+T">+T</seealso>
+ <seealso marker="erts:erl#+T"><c>+T</c></seealso>
in <c>erl(1)</c></p>
</item>
<tag><c>multi_scheduling</c></tag>
<item>
- <marker id="system_info_multi_scheduling"></marker>
- <p>Returns <c>disabled</c>, <c>blocked</c>, <c>blocked_normal</c>,
- or <c>enabled</c>:</p>
+ <marker id="system_info_multi_scheduling"></marker>
+ <p>Returns one of the following:</p>
<taglist>
<tag><c>disabled</c></tag>
<item>
@@ -7607,9 +7798,9 @@ ok
<item>
<p>The emulator has more than one scheduler thread,
but all normal scheduler threads except one are
- blocked. Note that dirty schedulers are not
- blocked, and may schedule Erlang processes and
- execute native code.</p>
+ blocked. Notice that dirty schedulers are not
+ blocked, and can schedule Erlang processes and
+ execute native code.</p>
</item>
<tag><c>enabled</c></tag>
<item>
@@ -7620,189 +7811,204 @@ ok
</item>
</taglist>
<p>See also
- <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>,
- <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>,
- <seealso marker="#system_info_normal_multi_scheduling_blockers">erlang:system_info(normal_multi_scheduling_blockers)</seealso>,
- and
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
+ <seealso marker="#system_flag_multi_scheduling">
+ <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling_blockers">
+ <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>,
+ <seealso marker="#system_info_normal_multi_scheduling_blockers">
+ <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>,
+ and <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
<tag><c>multi_scheduling_blockers</c></tag>
<item>
- <marker id="system_info_multi_scheduling_blockers"></marker>
+ <marker id="system_info_multi_scheduling_blockers"></marker>
<p>Returns a list of <c><anno>Pid</anno></c>s when
multi-scheduling is blocked, otherwise the empty list is
returned. The <c><anno>Pid</anno></c>s in the list
- represent all the processes currently
+ represent all the processes currently
blocking multi-scheduling. A <c><anno>Pid</anno></c> occurs
only once in the list, even if the corresponding
process has blocked multiple times.</p>
<p>See also
- <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>,
- <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>,
- <seealso marker="#system_info_normal_multi_scheduling_blockers">erlang:system_info(normal_multi_scheduling_blockers)</seealso>,
-
- and
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
+ <seealso marker="#system_flag_multi_scheduling">
+ <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling">
+ <c>erlang:system_info(multi_scheduling)</c></seealso>,
+ <seealso marker="#system_info_normal_multi_scheduling_blockers">
+ <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>,
+ and <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
<tag><c>nif_version</c></tag>
<item>
- <p>Returns a string containing the version of the Erlang NIF interface
- used by the runtime system. It is on the form
- "&lt;major ver&gt;.&lt;minor ver&gt;".</p>
+ <p>Returns a string containing the version of the Erlang NIF
+ interface used by the runtime system. It is on the form
+ "&lt;major ver&gt;.&lt;minor ver&gt;".</p>
</item>
<tag><c>normal_multi_scheduling_blockers</c></tag>
<item>
- <marker id="system_info_normal_multi_scheduling_blockers"></marker>
+ <marker id="system_info_normal_multi_scheduling_blockers"></marker>
<p>Returns a list of <c><anno>Pid</anno></c>s when
- normal multi-scheduling is blocked (i.e. all normal schedulers
- but one is blocked), otherwise the empty list is returned.
- The <c><anno>Pid</anno></c>s in the list represent all the
- processes currently blocking normal multi-scheduling.
- A <c><anno>Pid</anno></c> occurs only once in the list, even if
- the corresponding process has blocked multiple times.</p>
+ normal multi-scheduling is blocked (that is, all normal schedulers
+ but one is blocked), otherwise the empty list is returned.
+ The <c><anno>Pid</anno></c>s in the list represent all the
+ processes currently blocking normal multi-scheduling.
+ A <c><anno>Pid</anno></c> occurs only once in the list, even if
+ the corresponding process has blocked multiple times.</p>
<p>See also
- <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>,
- <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>,
- <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>,
-
- and
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
- </item>
- <tag><marker id="system_info_otp_release"/><c>otp_release</c></tag>
- <item>
- <marker id="system_info_otp_release"></marker>
+ <seealso marker="#system_flag_multi_scheduling">
+ <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling">
+ <c>erlang:system_info(multi_scheduling)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling_blockers">
+ <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>,
+ and <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.</p>
+ </item>
+ <tag><marker id="system_info_otp_release"/>
+ <c>otp_release</c></tag>
+ <item>
+ <marker id="system_info_otp_release"></marker>
<p>Returns a string containing the OTP release number of the
- OTP release that the currently executing <c>ERTS</c> application is
- part of.</p>
- <p>As from OTP 17, the OTP release number corresponds to
- the major OTP version number. No
- <c>erlang:system_info()</c> argument gives the exact OTP
- version. This is because the exact OTP version in the general case
- is difficult to determine. For more information, see the description
- of versions in <seealso marker="doc/system_principles:versions">
- System principles</seealso> in System Documentation.</p>
- </item>
- <tag><marker id="system_info_os_monotonic_time_source"/><c>os_monotonic_time_source</c></tag>
+ OTP release that the currently executing ERTS application
+ is part of.</p>
+ <p>As from Erlang/OTP 17, the OTP release number corresponds to
+ the major OTP version number. No
+ <c>erlang:system_info()</c> argument gives the exact OTP
+ version. This is because the exact OTP version in the general case
+ is difficult to determine. For more information, see the
+ description of versions in
+ <seealso marker="doc/system_principles:versions">
+ System principles</seealso> in System Documentation.</p>
+ </item>
+ <tag><marker id="system_info_os_monotonic_time_source"/>
+ <c>os_monotonic_time_source</c></tag>
<item>
<p>Returns a list containing information about the source of
- <seealso marker="erts:time_correction#OS_Monotonic_Time">OS
- monotonic time</seealso> that is used by the runtime system.</p>
- <p>If <c>[]</c> is returned, no OS monotonic time is
- available. The list contains two-tuples with <c>Key</c>s
- as first element, and <c>Value</c>s as second element. The
- order of these tuples is undefined. The following
- tuples can be part of the list, but more tuples can be
- introduced in the future:</p>
- <taglist>
- <tag><c>{function, Function}</c></tag>
- <item><p><c>Function</c> is the name of the function
- used. This tuple always exist if OS monotonic time is
- available to the runtime system.</p></item>
-
- <tag><c>{clock_id, ClockId}</c></tag>
- <item><p>This tuple only exist if <c>Function</c>
- can be used with different clocks. <c>ClockId</c>
- corresponds to the clock identifier used when calling
- <c>Function</c>.</p></item>
-
- <tag><c>{resolution, OsMonotonicTimeResolution}</c></tag>
- <item><p>Highest possible
- <seealso marker="time_correction#Time_Resolution">resolution</seealso>
- of current OS monotonic time source as parts per
- second. If no resolution information can be retrieved
- from the OS, <c>OsMonotonicTimeResolution</c> is
- set to the resolution of the time unit of
- <c>Function</c>s return value. That is, the actual
- resolution can be lower than
- <c>OsMonotonicTimeResolution</c>. Also note that
- the resolution does not say anything about the
- <seealso marker="time_correction#Time_Accuracy">accuracy</seealso>,
- and whether the
- <seealso marker="time_correction#Time_Precision">precision</seealso>
- do align with the resolution. You do,
- however, know that the precision is not better than
- <c>OsMonotonicTimeResolution</c>.</p></item>
-
- <tag><c>{extended, Extended}</c></tag>
- <item><p><c>Extended</c> equals <c>yes</c> if
- the range of time values has been extended;
- otherwise, <c>Extended</c> equals <c>no</c>. The
- range needs to be extended if <c>Function</c>
- returns values that wrap fast. This typically
- is the case when the return value is a 32-bit
- value.</p></item>
-
- <tag><c>{parallel, Parallel}</c></tag>
- <item><p><c>Parallel</c> equals <c>yes</c> if
- <c>Function</c> is called in parallel from multiple
- threads. If it is not called in parallel, because
- calls needs to be serialized, <c>Parallel</c> equals
- <c>no</c>.</p></item>
-
- <tag><c>{time, OsMonotonicTime}</c></tag>
- <item><p><c>OsMonotonicTime</c> equals current OS
- monotonic time in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>.</p></item>
- </taglist>
- </item>
- <tag><marker id="system_info_os_system_time_source"/><c>os_system_time_source</c></tag>
+ <seealso marker="erts:time_correction#OS_Monotonic_Time">OS
+ monotonic time</seealso> that is used by the runtime system.</p>
+ <p>If <c>[]</c> is returned, no OS monotonic time is
+ available. The list contains two-tuples with <c>Key</c>s
+ as first element, and <c>Value</c>s as second element. The
+ order of these tuples is undefined. The following
+ tuples can be part of the list, but more tuples can be
+ introduced in the future:</p>
+ <taglist>
+ <tag><c>{function, Function}</c></tag>
+ <item><p><c>Function</c> is the name of the function
+ used. This tuple always exists if OS monotonic time is
+ available to the runtime system.</p>
+ </item>
+ <tag><c>{clock_id, ClockId}</c></tag>
+ <item><p>This tuple only exists if <c>Function</c>
+ can be used with different clocks. <c>ClockId</c>
+ corresponds to the clock identifier used when calling
+ <c>Function</c>.</p>
+ </item>
+ <tag><c>{resolution, OsMonotonicTimeResolution}</c></tag>
+ <item><p>Highest possible
+ <seealso marker="time_correction#Time_Resolution">
+ resolution</seealso>
+ of current OS monotonic time source as parts per
+ second. If no resolution information can be retrieved
+ from the OS, <c>OsMonotonicTimeResolution</c> is
+ set to the resolution of the time unit of
+ <c>Function</c>s return value. That is, the actual
+ resolution can be lower than
+ <c>OsMonotonicTimeResolution</c>. Notice that
+ the resolution does not say anything about the
+ <seealso marker="time_correction#Time_Accuracy">
+ accuracy</seealso> or whether the
+ <seealso marker="time_correction#Time_Precision">
+ precision</seealso> aligns with the resolution. You do,
+ however, know that the precision is not better than
+ <c>OsMonotonicTimeResolution</c>.</p>
+ </item>
+ <tag><c>{extended, Extended}</c></tag>
+ <item><p><c>Extended</c> equals <c>yes</c> if
+ the range of time values has been extended;
+ otherwise <c>Extended</c> equals <c>no</c>. The
+ range must be extended if <c>Function</c>
+ returns values that wrap fast. This typically
+ is the case when the return value is a 32-bit value.</p>
+ </item>
+ <tag><c>{parallel, Parallel}</c></tag>
+ <item><p><c>Parallel</c> equals <c>yes</c> if
+ <c>Function</c> is called in parallel from multiple
+ threads. If it is not called in parallel, because
+ calls must be serialized, <c>Parallel</c> equals
+ <c>no</c>.</p>
+ </item>
+ <tag><c>{time, OsMonotonicTime}</c></tag>
+ <item><p><c>OsMonotonicTime</c> equals current OS
+ monotonic time in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_os_system_time_source"/>
+ <c>os_system_time_source</c></tag>
<item>
<p>Returns a list containing information about the source of
- <seealso marker="erts:time_correction#OS_System_Time">OS
- system time</seealso> that is used by the runtime system.</p>
- <p>The list contains two-tuples with <c>Key</c>s
- as first element, and <c>Value</c>s as second element. The
- order if these tuples is undefined. The following
- tuples can be part of the list, but more tuples can be
- introduced in the future:</p>
- <taglist>
- <tag><c>{function, Function}</c></tag>
- <item><p><c>Function</c> is the name of the funcion
- used.</p></item>
-
- <tag><c>{clock_id, ClockId}</c></tag>
- <item><p>This tuple only exist if <c>Function</c>
- can be used with different clocks. <c>ClockId</c>
- corresponds to the clock identifier used when calling
- <c>Function</c>.</p></item>
-
- <tag><c>{resolution, OsSystemTimeResolution}</c></tag>
- <item><p>Highest possible
- <seealso marker="time_correction#Time_Resolution">resolution</seealso>
- of current OS system time source as parts per
- second. If no resolution information can be retrieved
- from the OS, <c>OsSystemTimeResolution</c> is
- set to the resolution of the time unit of
- <c>Function</c>s return value. That is, the actual
- resolution may be lower than
- <c>OsSystemTimeResolution</c>. Also note that
- the resolution does not say anything about the
- <seealso marker="time_correction#Time_Accuracy">accuracy</seealso>,
- and whether the
- <seealso marker="time_correction#Time_Precision">precision</seealso>
- do align with the resolution. You do,
- however, know that the precision is not better than
- <c>OsSystemTimeResolution</c>.</p></item>
-
- <tag><c>{parallel, Parallel}</c></tag>
- <item><p><c>Parallel</c> equals <c>yes</c> if
- <c>Function</c> is called in parallel from multiple
- threads. If it is not called in parallel, because
- calls needs to be serialized, <c>Parallel</c> equals
- <c>no</c>.</p></item>
-
- <tag><c>{time, OsSystemTime}</c></tag>
- <item><p><c>OsSystemTime</c> equals current OS
- system time in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>.</p></item>
- </taglist>
- </item>
- <tag><c>port_parallelism</c></tag>
- <item>
- <marker id="system_info_port_parallelism"></marker>
- <p>Returns the default port parallelism scheduling hint used.
- For more information, see command-line argument
- <seealso marker="erl#+spp">+spp</seealso> in <c>erl(1)</c>.</p></item>
+ <seealso marker="erts:time_correction#OS_System_Time">OS
+ system time</seealso> that is used by the runtime system.</p>
+ <p>The list contains two-tuples with <c>Key</c>s
+ as first element, and <c>Value</c>s as second element. The
+ order if these tuples is undefined. The following
+ tuples can be part of the list, but more tuples can be
+ introduced in the future:</p>
+ <taglist>
+ <tag><c>{function, Function}</c></tag>
+ <item><p><c>Function</c> is the name of the funcion used.</p>
+ </item>
+ <tag><c>{clock_id, ClockId}</c></tag>
+ <item><p>Exists only if <c>Function</c>
+ can be used with different clocks. <c>ClockId</c>
+ corresponds to the clock identifier used when calling
+ <c>Function</c>.</p>
+ </item>
+ <tag><c>{resolution, OsSystemTimeResolution}</c></tag>
+ <item><p>Highest possible
+ <seealso marker="time_correction#Time_Resolution">
+ resolution</seealso>
+ of current OS system time source as parts per
+ second. If no resolution information can be retrieved
+ from the OS, <c>OsSystemTimeResolution</c> is
+ set to the resolution of the time unit of
+ <c>Function</c>s return value. That is, the actual
+ resolution can be lower than
+ <c>OsSystemTimeResolution</c>. Notice that
+ the resolution does not say anything about the
+ <seealso marker="time_correction#Time_Accuracy">
+ accuracy</seealso> or whether the
+ <seealso marker="time_correction#Time_Precision">
+ precision</seealso> do align with the resolution. You do,
+ however, know that the precision is not better than
+ <c>OsSystemTimeResolution</c>.</p>
+ </item>
+ <tag><c>{parallel, Parallel}</c></tag>
+ <item><p><c>Parallel</c> equals <c>yes</c> if
+ <c>Function</c> is called in parallel from multiple
+ threads. If it is not called in parallel, because
+ calls needs to be serialized, <c>Parallel</c> equals
+ <c>no</c>.</p>
+ </item>
+ <tag><c>{time, OsSystemTime}</c></tag>
+ <item><p><c>OsSystemTime</c> equals current OS
+ system time in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><c>port_parallelism</c></tag>
+ <item>
+ <marker id="system_info_port_parallelism"></marker>
+ <p>Returns the default port parallelism scheduling hint used.
+ For more information, see command-line argument
+ <seealso marker="erl#+spp"><c>+spp</c></seealso>
+ in <c>erl(1)</c>.</p>
+ </item>
<tag><marker id="system_info_port_count"/><c>port_count</c></tag>
<item>
<p>Returns the number of ports currently existing at the
@@ -7816,9 +8022,10 @@ ok
<p>Returns the maximum number of simultaneously existing
ports at the local node as an integer. This limit can be
configured at startup by using command-line flag
- <seealso marker="erl#+Q">+Q</seealso> in <c>erl(1)</c>.</p>
+ <seealso marker="erl#+Q"><c>+Q</c></seealso> in <c>erl(1)</c>.</p>
</item>
- <tag><marker id="system_info_process_count"/><c>process_count</c></tag>
+ <tag><marker id="system_info_process_count"/>
+ <c>process_count</c></tag>
<item>
<p>Returns the number of processes currently existing at the
local node. The value is given as an integer. This is
@@ -7827,74 +8034,78 @@ ok
</item>
<tag><c>process_limit</c></tag>
<item>
- <marker id="system_info_process_limit"></marker>
+ <marker id="system_info_process_limit"></marker>
<p>Returns the maximum number of simultaneously existing
processes at the local node. The value is given as an
integer. This limit can be configured at startup by using
- command-line flag <seealso marker="erl#+P">+P</seealso>
- in <c>erl(1)</c>.</p>
+ command-line flag <seealso marker="erl#+P"><c>+P</c></seealso>
+ in <c>erl(1)</c>.</p>
</item>
<tag><c>procs</c></tag>
<item>
<p>Returns a binary containing a string of process and port
information formatted as in Erlang crash dumps. For more
- information, see Section
- <seealso marker="erts:crash_dump">"How to interpret the Erlang crash dumps"</seealso>
+ information, see section <seealso marker="erts:crash_dump">
+ How to interpret the Erlang crash dumps</seealso>
in the User's Guide.</p>
</item>
<tag><c>scheduler_bind_type</c></tag>
<item>
- <marker id="system_info_scheduler_bind_type"></marker>
- <p>Returns information about how the user has requested
- schedulers to be bound or not bound.</p>
- <p>Notice that even though a user has requested
- schedulers to be bound, they can silently have failed
- to bind. To inspect the scheduler bindings, call
- <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.</p>
- <p>For more information, see command-line argument
- <seealso marker="erts:erl#+sbt">+sbt</seealso>
- in <c>erl(1)</c> and
- <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.</p>
+ <marker id="system_info_scheduler_bind_type"></marker>
+ <p>Returns information about how the user has requested
+ schedulers to be bound or not bound.</p>
+ <p>Notice that although a user has requested
+ schedulers to be bound, they can silently have failed
+ to bind. To inspect the scheduler bindings, call
+ <seealso marker="#system_info_scheduler_bindings">
+ <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p>
+ <p>For more information, see command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso>
+ in <c>erl(1)</c> and
+ <seealso marker="#system_info_scheduler_bindings">
+ <c>erlang:system_info(scheduler_bindings)</c></seealso>.</p>
</item>
<tag><c>scheduler_bindings</c></tag>
<item>
- <marker id="system_info_scheduler_bindings"></marker>
- <p>Returns information about the currently used scheduler
- bindings.</p>
- <p>A tuple of a size equal to
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>
- is returned. The tuple elements are integers
- or the atom <c>unbound</c>. Logical processor identifiers
- are represented as integers. The <c>N</c>th
- element of the tuple equals the current binding for
- the scheduler with the scheduler identifier equal to
- <c>N</c>. For example, if the schedulers are bound,
- <c>element(erlang:system_info(scheduler_id),
- erlang:system_info(scheduler_bindings))</c> returns
- the identifier of the logical processor that the calling
- process is executing on.</p>
- <p>Notice that only schedulers online can be bound to logical
- processors.</p>
- <p>For more information, see command-line argument
- <seealso marker="erts:erl#+sbt">+sbt</seealso>
- in <c>erl(1)</c> and
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>.
- </p>
+ <marker id="system_info_scheduler_bindings"></marker>
+ <p>Returns information about the currently used scheduler
+ bindings.</p>
+ <p>A tuple of a size equal to
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>
+ is returned. The tuple elements are integers
+ or the atom <c>unbound</c>. Logical processor identifiers
+ are represented as integers. The <c>N</c>th
+ element of the tuple equals the current binding for
+ the scheduler with the scheduler identifier equal to
+ <c>N</c>. For example, if the schedulers are bound,
+ <c>element(erlang:system_info(scheduler_id),
+ erlang:system_info(scheduler_bindings))</c> returns
+ the identifier of the logical processor that the calling
+ process is executing on.</p>
+ <p>Notice that only schedulers online can be bound to logical
+ processors.</p>
+ <p>For more information, see command-line argument
+ <seealso marker="erts:erl#+sbt"><c>+sbt</c></seealso>
+ in <c>erl(1)</c> and
+ <seealso marker="#system_info_schedulers_online">
+ <c>erlang:system_info(schedulers_online)</c></seealso>.</p>
</item>
<tag><c>scheduler_id</c></tag>
<item>
- <marker id="system_info_scheduler_id"></marker>
+ <marker id="system_info_scheduler_id"></marker>
<p>Returns the scheduler ID (<c>SchedulerId</c>) of the
scheduler thread that the calling process is executing
on. <c><anno>SchedulerId</anno></c> is a positive integer,
- where
- <c><![CDATA[1 <= SchedulerId <= erlang:system_info(schedulers)]]></c>.
- See also
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
+ where <c><![CDATA[1 <= SchedulerId <=
+ erlang:system_info(schedulers)]]></c>.</p>
+ <p>See also
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso>.</p>
</item>
<tag><c>schedulers</c></tag>
<item>
- <marker id="system_info_schedulers"></marker>
+ <marker id="system_info_schedulers"></marker>
<p>Returns the number of scheduler threads used by
the emulator. Scheduler threads online schedules Erlang
processes and Erlang ports, and execute Erlang code
@@ -7902,44 +8113,57 @@ ok
<p>The number of scheduler threads is determined at
emulator boot time and cannot be changed later.
However, the number of schedulers online can
- be changed at any time.</p>
+ be changed at any time.</p>
<p>See also
- <seealso marker="#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>,
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>,
- <seealso marker="#system_info_scheduler_id">erlang:system_info(scheduler_id)</seealso>,
- <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>,
- <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>,
- <seealso marker="#system_info_normal_multi_scheduling_blockers">erlang:system_info(normal_multi_scheduling_blockers)</seealso>
- and
- <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>.</p>
+ <seealso marker="#system_flag_schedulers_online">
+ <c>erlang:system_flag(schedulers_online,
+ SchedulersOnline)</c></seealso>,
+ <seealso marker="#system_info_schedulers_online">
+ <c>erlang:system_info(schedulers_online)</c></seealso>,
+ <seealso marker="#system_info_scheduler_id">
+ <c>erlang:system_info(scheduler_id)</c></seealso>,
+ <seealso marker="#system_flag_multi_scheduling">
+ <c>erlang:system_flag(multi_scheduling, BlockState)</c></seealso>,
+ <seealso marker="#system_info_multi_scheduling">
+ <c>erlang:system_info(multi_scheduling)</c></seealso>,
+ <seealso marker="#system_info_normal_multi_scheduling_blockers">
+ <c>erlang:system_info(normal_multi_scheduling_blockers)</c></seealso>
+ and <seealso marker="#system_info_multi_scheduling_blockers">
+ <c>erlang:system_info(multi_scheduling_blockers)</c></seealso>.
+ </p>
</item>
<tag><c>schedulers_online</c></tag>
<item>
- <marker id="system_info_schedulers_online"></marker>
+ <marker id="system_info_schedulers_online"></marker>
<p>Returns the number of schedulers online. The scheduler
- identifiers of schedulers online satisfy the relationship
- <c><![CDATA[1 <= SchedulerId <= erlang:system_info(schedulers_online)]]></c>.</p>
- <p>For more information, see
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>
- and
- <seealso marker="#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.</p>
+ identifiers of schedulers online satisfy the relationship
+ <c><![CDATA[1 <= SchedulerId <=
+ erlang:system_info(schedulers_online)]]></c>.</p>
+ <p>For more information, see
+ <seealso marker="#system_info_schedulers">
+ <c>erlang:system_info(schedulers)</c></seealso> and
+ <seealso marker="#system_flag_schedulers_online">
+ <c>erlang:system_flag(schedulers_online,
+ SchedulersOnline)</c></seealso>.</p>
</item>
<tag><c>smp_support</c></tag>
<item>
<p>Returns <c>true</c> if the emulator has been compiled
with SMP support, otherwise <c>false</c> is returned.</p>
</item>
- <tag><marker id="system_info_start_time"/><c>start_time</c></tag>
- <item><p>The <seealso marker="#monotonic_time/0">Erlang monotonic
- time</seealso> in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso> at the
- time when current Erlang runtime system instance started. See also
- <seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso>.
- </p></item>
+ <tag><marker id="system_info_start_time"/><c>start_time</c></tag>
+ <item>
+ <p>The <seealso marker="#monotonic_time/0">Erlang monotonic
+ time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso> at the
+ time when current Erlang runtime system instance started.</p>
+ <p>See also <seealso marker="#system_info_end_time">
+ <c>erlang:system_info(end_time)</c></seealso>.</p>
+ </item>
<tag><c>system_version</c></tag>
<item>
<p>Returns a string containing version number and
- some important properties, such as the number of schedulers.</p>
+ some important properties, such as the number of schedulers.</p>
</item>
<tag><c>system_architecture</c></tag>
<item>
@@ -7953,105 +8177,114 @@ ok
</item>
<tag><c>thread_pool_size</c></tag>
<item>
- <marker id="system_info_thread_pool_size"></marker>
+ <marker id="system_info_thread_pool_size"></marker>
<p>Returns the number of async threads in the async thread
pool used for asynchronous driver calls
- (<seealso marker="erts:erl_driver#driver_async">driver_async()</seealso>).
+ (<seealso marker="erts:erl_driver#driver_async">
+ <c>erl_driver:driver_async()</c></seealso>).
The value is given as an integer.</p>
</item>
-
- <tag><c>time_correction</c></tag>
- <item>
- <marker id="system_info_time_correction"></marker>
- <p>Returns a boolean value indicating whether
- <seealso marker="time_correction#Time_Correction">time correction</seealso>
- is enabled or not.
- </p></item>
- <tag><c>time_offset</c></tag>
- <item>
- <marker id="system_info_time_offset"></marker>
- <p>Returns the state of the time offset:</p>
- <taglist>
- <tag><c>preliminary</c></tag>
- <item><p>The time offset is preliminary, and will be changed
- at a later time when being finalized. The preliminary time offset
- is used during the preliminary phase of the
- <seealso marker="time_correction#Single_Time_Warp_Mode">single
- time warp mode</seealso>.</p></item>
-
- <tag><c>final</c></tag>
- <item><p>The time offset is final. This either because
- <seealso marker="time_correction#No_Time_Warp_Mode">no
- time warp mode</seealso> is used, or because the time
- offset have been finalized when
- <seealso marker="time_correction#Single_Time_Warp_Mode">single
- time warp mode</seealso> is used.</p></item>
-
- <tag><c>volatile</c></tag>
- <item><p>The time offset is volatile. That is, it can
- change at any time. This is because
- <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
- time warp mode</seealso> is used.</p></item>
- </taglist>
- </item>
- <tag><marker id="system_info_time_warp_mode"/><c>time_warp_mode</c></tag>
- <item><p>Returns a value identifying the
- <seealso marker="time_correction#Time_Warp_Modes">time warp
- mode</seealso> being used:</p>
- <taglist>
- <tag><c>no_time_warp</c></tag>
- <item><p>The <seealso marker="time_correction#No_Time_Warp_Mode">no
- time warp mode</seealso> is used.</p></item>
-
- <tag><c>single_time_warp</c></tag>
- <item><p>The <seealso marker="time_correction#Single_Time_Warp_Mode">single
- time warp mode</seealso> is used.</p></item>
-
- <tag><c>multi_time_warp</c></tag>
- <item><p>The <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
- time warp mode</seealso> is used.</p></item>
- </taglist>
- </item>
+ <tag><c>time_correction</c></tag>
+ <item>
+ <marker id="system_info_time_correction"></marker>
+ <p>Returns a boolean value indicating whether
+ <seealso marker="time_correction#Time_Correction">
+ time correction</seealso> is enabled or not.</p>
+ </item>
+ <tag><c>time_offset</c></tag>
+ <item>
+ <marker id="system_info_time_offset"></marker>
+ <p>Returns the state of the time offset:</p>
+ <taglist>
+ <tag><c>preliminary</c></tag>
+ <item>
+ <p>The time offset is preliminary, and will be changed
+ and finalized later. The preliminary time offset
+ is used during the preliminary phase of the
+ <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso>.</p>
+ </item>
+ <tag><c>final</c></tag>
+ <item>
+ <p>The time offset is final. This either because
+ <seealso marker="time_correction#No_Time_Warp_Mode">
+ no time warp mode</seealso> is used, or because the time
+ offset have been finalized when
+ <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso> is used.</p>
+ </item>
+ <tag><c>volatile</c></tag>
+ <item>
+ <p>The time offset is volatile. That is, it can
+ change at any time. This is because
+ <seealso marker="time_correction#Multi_Time_Warp_Mode">
+ multi-time warp mode</seealso> is used.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="system_info_time_warp_mode"/>
+ <c>time_warp_mode</c></tag>
+ <item>
+ <p>Returns a value identifying the
+ <seealso marker="time_correction#Time_Warp_Modes">
+ time warp mode</seealso> that is used:</p>
+ <taglist>
+ <tag><c>no_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#No_Time_Warp_Mode">
+ no time warp mode</seealso> is used.
+ </item>
+ <tag><c>single_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#Single_Time_Warp_Mode">
+ single time warp mode</seealso> is used.
+ </item>
+ <tag><c>multi_time_warp</c></tag>
+ <item>The <seealso marker="time_correction#Multi_Time_Warp_Mode">
+ multi-time warp mode</seealso> is used.
+ </item>
+ </taglist>
+ </item>
<tag><c>tolerant_timeofday</c></tag>
<item>
- <marker id="system_info_tolerant_timeofday"></marker>
- <p>Returns whether a pre erts-7.0 backwards compatible compensation
- for sudden changes of system time is <c>enabled</c> or <c>disabled</c>.
- Such compensation is <c>enabled</c> when the
- <seealso marker="#system_info_time_offset">time offset</seealso> is
- <c>final</c>, and
- <seealso marker="#system_info_time_correction">time correction</seealso>
- is enabled.</p>
+ <marker id="system_info_tolerant_timeofday"></marker>
+ <p>Returns whether a pre ERTS 7.0 backwards compatible
+ compensation for sudden changes of system time is <c>enabled</c>
+ or <c>disabled</c>. Such compensation is <c>enabled</c> when the
+ <seealso marker="#system_info_time_offset">time offset</seealso>
+ is <c>final</c>, and
+ <seealso marker="#system_info_time_correction">
+ time correction</seealso> is enabled.</p>
</item>
<tag><c>trace_control_word</c></tag>
<item>
<p>Returns the value of the node trace control word. For
- more information, see function <c>get_tcw</c> in Section
- <seealso marker="erts:match_spec#get_tcw">Match Specifications in Erlang</seealso> in the User's Guide.</p>
+ more information, see function <c>get_tcw</c> in section
+ <seealso marker="erts:match_spec#get_tcw">
+ Match Specifications in Erlang</seealso> in the User's Guide.</p>
</item>
<tag><c>update_cpu_info</c></tag>
<item>
- <marker id="update_cpu_info"></marker>
+ <marker id="update_cpu_info"></marker>
<p>The runtime system rereads the CPU information available
- and updates its internally stored information about the
- <seealso marker="#system_info_cpu_topology_detected">detected
- CPU topology</seealso> and the number of logical processors
- <seealso marker="#logical_processors">configured</seealso>,
- <seealso marker="#logical_processors_online">online</seealso>, and
- <seealso marker="#logical_processors_available">available</seealso>.</p>
- <p>If the CPU information has changed since the last time
- it was read, the atom <c>changed</c> is returned, otherwise
- the atom <c>unchanged</c>. If the CPU information has changed,
- you probably want to
- <seealso marker="#system_flag_schedulers_online">adjust the
- number of schedulers online</seealso>. You typically want
- to have as many schedulers online as
- <seealso marker="#logical_processors_available">logical
- processors available</seealso>.</p>
+ and updates its internally stored information about the
+ <seealso marker="#system_info_cpu_topology_detected">detected
+ CPU topology</seealso> and the number of logical processors
+ <seealso marker="#logical_processors">configured</seealso>,
+ <seealso marker="#logical_processors_online">online</seealso>,
+ and <seealso marker="#logical_processors_available">
+ available</seealso>.</p>
+ <p>If the CPU information has changed since the last time
+ it was read, the atom <c>changed</c> is returned, otherwise
+ the atom <c>unchanged</c>. If the CPU information has changed,
+ you probably want to
+ <seealso marker="#system_flag_schedulers_online">adjust the
+ number of schedulers online</seealso>. You typically want
+ to have as many schedulers online as
+ <seealso marker="#logical_processors_available">logical
+ processors available</seealso>.</p>
</item>
<tag><c>version</c></tag>
<item>
- <marker id="system_info_version"></marker>
+ <marker id="system_info_version"></marker>
<p>Returns a string containing the version number of the
emulator.</p>
</item>
@@ -8070,19 +8303,19 @@ ok
</item>
<tag><c>{wordsize, external}</c></tag>
<item>
- <p>Returns the true word size of the emulator, that is,
- the size of a pointer. The value is given in bytes
- as an integer. On a pure 32-bit architecture, 4 is
- returned. On both a half word and on a pure
- 64-bit architecture, 8 is returned.</p>
+ <p>Returns the true word size of the emulator, that is,
+ the size of a pointer. The value is given in bytes
+ as an integer. On a pure 32-bit architecture, 4 is
+ returned. On both a half word and on a pure
+ 64-bit architecture, 8 is returned.</p>
</item>
</taglist>
<note>
<p>Argument <c>scheduler</c> has changed name to
<c>scheduler_id</c> to avoid mix up with argument
<c>schedulers</c>. Argument <c>scheduler</c> was
- introduced in <c>ERTS</c> 5.5 and renamed in
- <c>ERTS</c> 5.5.1.</p>
+ introduced in ERTS 5.5 and renamed in
+ ERTS 5.5.1.</p>
</note>
</desc>
</func>
@@ -8093,32 +8326,35 @@ ok
<type name="system_monitor_option"/>
<desc>
<p>Returns the current system monitoring settings set by
- <seealso marker="#system_monitor/2">erlang:system_monitor/2</seealso>
+ <seealso marker="#system_monitor/2">
+ <c>erlang:system_monitor/2</c></seealso>
as <c>{<anno>MonitorPid</anno>, <anno>Options</anno>}</c>,
- or <c>undefined</c> if there
- are no settings. The order of the options can be different
- from the one that was set.</p>
+ or <c>undefined</c> if no settings exist. The order of the
+ options can be different from the one that was set.</p>
</desc>
</func>
<func>
<name name="system_monitor" arity="1"/>
- <fsummary>Sets or clears system performance monitoring options.</fsummary>
+ <fsummary>Set or clear system performance monitoring options.</fsummary>
<type name="system_monitor_option"/>
<desc>
<p>When called with argument <c>undefined</c>, all
system performance monitoring settings are cleared.</p>
<p>Calling the function with <c>{<anno>MonitorPid</anno>,
<anno>Options</anno>}</c> as argument is the same as calling
- <seealso marker="#system_monitor/2"><c>erlang:system_monitor(<anno>MonitorPid</anno>, <anno>Options</anno>)</c></seealso>.</p>
+ <seealso marker="#system_monitor/2">
+ <c>erlang:system_monitor(<anno>MonitorPid</anno>,
+ <anno>Options</anno>)</c></seealso>.</p>
<p>Returns the previous system monitor settings just like
- <seealso marker="#system_monitor/0">erlang:system_monitor/0</seealso>.</p>
+ <seealso marker="#system_monitor/0">
+ <c>erlang:system_monitor/0</c></seealso>.</p>
</desc>
</func>
<func>
<name name="system_monitor" arity="2"/>
- <fsummary>Sets system performance monitoring options.</fsummary>
+ <fsummary>Set system performance monitoring options.</fsummary>
<type name="system_monitor_option"/>
<desc>
<p>Sets the system performance monitoring options.
@@ -8137,12 +8373,12 @@ ok
<p>One of the tuples is <c>{timeout, GcTime}</c>, where
<c>GcTime</c> is the time for the garbage
collection in milliseconds. The other tuples are
- tagged with <c>heap_size</c>, <c>heap_block_size</c>
+ tagged with <c>heap_size</c>, <c>heap_block_size</c>,
<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_minor_start">gc_minor_start</seealso> (see
- <seealso marker="#trace/3">erlang:trace/3</seealso>).
+ <seealso marker="#gc_minor_start"><c>gc_minor_start</c></seealso>
+ (see <seealso marker="#trace/3"><c>erlang:trace/3</c></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
prior notice.</p>
@@ -8173,7 +8409,7 @@ ok
<c>timeout</c>, <c>ready_input</c>, <c>ready_output</c>,
<c>event</c>, and <c>outputv</c> (when the port
is used by distribution). Value <c>Millis</c> in
- the <c>timeout</c> tuple informs about the
+ tuple <c>timeout</c> informs about the
uninterrupted execution time of the process or port, which
always is equal to or higher than the <c>Time</c> value
supplied when starting the trace. New tuples can be
@@ -8184,7 +8420,7 @@ ok
drivers that take too long to execute. 1 ms is
considered a good maximum time for a driver callback
or a NIF. However, a time-sharing system is usually to
- consider everything below 100 ms as "possible" and
+ consider everything &lt; 100 ms as "possible" and
fairly "normal". However, longer schedule times can
indicate swapping or a misbehaving NIF/driver.
Misbehaving NIFs and drivers can cause bad resource
@@ -8202,10 +8438,11 @@ ok
<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">
+ <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>
+ is sent.</p>
</item>
<tag><c>busy_port</c></tag>
<item>
@@ -8227,7 +8464,8 @@ ok
</item>
</taglist>
<p>Returns the previous system monitor settings just like
- <seealso marker="#system_monitor/0">erlang:system_monitor/0</seealso>.</p>
+ <seealso marker="#system_monitor/0">
+ <c>erlang:system_monitor/0</c></seealso>.</p>
<note>
<p>If a monitoring process gets so large that it itself
starts to cause system monitor messages when garbage
@@ -8252,7 +8490,8 @@ ok
<type name="system_profile_option"/>
<desc>
<p>Returns the current system profiling settings set by
- <seealso marker="#system_profile/2">erlang:system_profile/2</seealso>
+ <seealso marker="#system_profile/2">
+ <c>erlang:system_profile/2</c></seealso>
as <c>{<anno>ProfilerPid</anno>, <anno>Options</anno>}</c>,
or <c>undefined</c> if there
are no settings. The order of the options can be different
@@ -8272,172 +8511,190 @@ ok
<taglist>
<tag><c>exclusive</c></tag>
<item>
- <p>If a synchronous call to a port from a process is done, the
- calling process is considered not runnable during the call
- runtime to the port. The calling process is notified as
- <c>inactive</c>, and later <c>active</c> when the port
- callback returns.</p>
+ <p>If a synchronous call to a port from a process is done, the
+ calling process is considered not runnable during the call
+ runtime to the port. The calling process is notified as
+ <c>inactive</c>, and later <c>active</c> when the port
+ callback returns.</p>
</item>
<tag><c>monotonic_timestamp</c></tag>
<item>
- <p>Timestamps in profile messages will use
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso>. The time-stamp (Ts) has the same
- format and value as produced by
- <c>erlang:monotonic_time(nano_seconds)</c>.</p>
+ <p>Time stamps in profile messages use
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso>. The time stamp (Ts) has the same
+ format and value as produced by
+ <c>erlang:monotonic_time(nanosecond)</c>.</p>
</item>
<tag><c>runnable_procs</c></tag>
<item>
- <p>If a process is put into or removed from the run queue, a
- message, <c>{profile, Pid, State, Mfa, Ts}</c>, is sent to
- <c><anno>ProfilerPid</anno></c>. Running processes that
- are reinserted into the run queue after having been
- preempted do not trigger this message.</p>
+ <p>If a process is put into or removed from the run queue, a
+ message, <c>{profile, Pid, State, Mfa, Ts}</c>, is sent to
+ <c><anno>ProfilerPid</anno></c>. Running processes that
+ are reinserted into the run queue after having been
+ pre-empted do not trigger this message.</p>
</item>
<tag><c>runnable_ports</c></tag>
<item>
- <p>If a port is put into or removed from the run queue, a
- message, <c>{profile, Port, State, 0, Ts}</c>, is sent to
- <c><anno>ProfilerPid</anno></c>.</p>
+ <p>If a port is put into or removed from the run queue, a
+ message, <c>{profile, Port, State, 0, Ts}</c>, is sent to
+ <c><anno>ProfilerPid</anno></c>.</p>
</item>
<tag><c>scheduler</c></tag>
<item>
- <p>If a scheduler is put to sleep or awoken, a message,
- <c>{profile, scheduler, Id, State, NoScheds, Ts}</c>, is
- sent to <c><anno>ProfilerPid</anno></c>.</p>
+ <p>If a scheduler is put to sleep or awoken, a message,
+ <c>{profile, scheduler, Id, State, NoScheds, Ts}</c>, is
+ sent to <c><anno>ProfilerPid</anno></c>.</p>
</item>
<tag><c>strict_monotonic_timestamp</c></tag>
<item>
- <p>Timestamps in profile messages will consisting of
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> and a monotonically increasing
- integer. The time-stamp (Ts) has the same format and value
- as produced by <c>{erlang:monotonic_time(nano_seconds),
- erlang:unique_integer([monotonic])}</c>.</p>
+ <p>Time stamps in profile messages consist of
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> and a monotonically increasing
+ integer. The time stamp (Ts) has the same format and value
+ as produced by <c>{erlang:monotonic_time(nanosecond),
+ erlang:unique_integer([monotonic])}</c>.</p>
</item>
<tag><c>timestamp</c></tag>
<item>
- <p>Timestamps in profile messages will include a
- time-stamp (Ts) that has the same form as returned by
- <c>erlang:now()</c>. This is also the default if no
- timestamp flag is given. If <c>cpu_timestamp</c> has
- been enabled via <c>erlang:trace/3</c>, this will also
- effect the timestamp produced in profiling messages
- when <c>timestamp</c> flag is enabled.</p>
+ <p>Time stamps in profile messages include a
+ time stamp (Ts) that has the same form as returned by
+ <c>erlang:now()</c>. This is also the default if no
+ time stamp flag is specified. If <c>cpu_timestamp</c> has
+ been enabled through
+ <seealso marker="erlang:trace/3"><c>erlang:trace/3</c></seealso>,
+ this also effects the time stamp produced in profiling messages
+ when flag <c>timestamp</c> is enabled.</p>
</item>
</taglist>
- <note><p><c>erlang:system_profile</c> is considered experimental
- and its behavior can change in a future release.</p>
+ <note>
+ <p><c>erlang:system_profile</c> behavior can change
+ in a future release.</p>
</note>
</desc>
</func>
+
<func>
<name name="system_time" arity="0"/>
- <fsummary>Current Erlang system time</fsummary>
- <desc>
- <p>Returns current
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- in <c>native</c>
- <seealso marker="#type_time_unit">time unit</seealso>.</p>
-
- <p>Calling <c>erlang:system_time()</c> is equivalent to:
- <seealso marker="#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso><c>
- +
- </c><seealso marker="#time_offset/0"><c>erlang:time_offset()</c></seealso>.</p>
-
- <note><p>This time is <em>not</em> a monotonically increasing time
- in the general case. For more information, see the documentation of
- <seealso marker="time_correction#Time_Warp_Modes">time warp modes</seealso> in the
- ERTS User's Guide.</p></note>
+ <fsummary>Current Erlang system time.</fsummary>
+ <desc>
+ <p>Returns current
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso>.</p>
+ <p>Calling <c>erlang:system_time()</c> is equivalent to
+ <seealso marker="#monotonic_time/0">
+ <c>erlang:monotonic_time()</c></seealso><c> +
+ </c><seealso marker="#time_offset/0">
+ <c>erlang:time_offset()</c></seealso>.</p>
+ <note>
+ <p>This time is <em>not</em> a monotonically increasing time
+ in the general case. For more information, see the documentation of
+ <seealso marker="time_correction#Time_Warp_Modes">
+ time warp modes</seealso> in the User's Guide.</p>
+ </note>
</desc>
</func>
+
<func>
<name name="system_time" arity="1"/>
- <fsummary>Current Erlang system time</fsummary>
- <desc>
- <p>Returns current
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- converted into the <c><anno>Unit</anno></c> passed as argument.</p>
-
- <p>Calling <c>erlang:system_time(<anno>Unit</anno>)</c> is equivalent to:
- <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#system_time/0"><c>erlang:system_time()</c></seealso><c>,
- native, <anno>Unit</anno>)</c>.</p>
-
- <note><p>This time is <em>not</em> a monotonically increasing time
- in the general case. For more information, see the documentation of
- <seealso marker="time_correction#Time_Warp_Modes">time warp modes</seealso> in the
- ERTS User's Guide.</p></note>
+ <fsummary>Current Erlang system time.</fsummary>
+ <desc>
+ <p>Returns current
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso>
+ converted into the <c><anno>Unit</anno></c> passed as argument.</p>
+ <p>Calling <c>erlang:system_time(<anno>Unit</anno>)</c> is equivalent
+ to <seealso marker="#convert_time_unit/3">
+ <c>erlang:convert_time_unit</c></seealso><c>(</c><seealso
+ marker="#system_time/0"><c>erlang:system_time()</c></seealso><c>,
+ native, <anno>Unit</anno>)</c>.</p>
+ <note>
+ <p>This time is <em>not</em> a monotonically increasing time
+ in the general case. For more information, see the documentation of
+ <seealso marker="time_correction#Time_Warp_Modes">
+ time warp modes</seealso> in the User's Guide.</p>
+ </note>
</desc>
</func>
+
<func>
<name name="term_to_binary" arity="1"/>
- <fsummary>Encodes a term to an Erlang external term format binary.</fsummary>
+ <fsummary>Encode a term to an Erlang external term format binary.
+ </fsummary>
<desc>
<p>Returns a binary data object that is the result of encoding
- <c><anno>Term</anno></c> according to the Erlang external
- term format.</p>
+ <c><anno>Term</anno></c> according to the
+ <seealso marker="erts:erl_ext_dist">Erlang external
+ term format.</seealso></p>
<p>This can be used for various purposes, for example,
writing a term to a file in an efficient way, or sending an
Erlang term to some type of communications channel not
supported by distributed Erlang.</p>
- <p>See also
- <seealso marker="#binary_to_term/1">binary_to_term/1</seealso>.</p>
+ <pre>
+> <input>Bin = term_to_binary(hello).</input>
+&lt;&lt;131,100,0,5,104,101,108,108,111>>
+> <input>hello = binary_to_term(Bin).</input>
+hello
+</pre>
+ <p>See also <seealso marker="#binary_to_term/1">
+ <c>binary_to_term/1</c></seealso>.</p>
</desc>
</func>
<func>
<name name="term_to_binary" arity="2"/>
- <fsummary>Encodes a term to en Erlang external term format binary.</fsummary>
+ <fsummary>Encode a term to en Erlang external term format binary.
+ </fsummary>
<desc>
<p>Returns a binary data object that is the result of encoding
<c><anno>Term</anno></c> according to the Erlang external
term format.</p>
<p>If option <c>compressed</c> is provided, the external term
format is compressed. The compressed format is automatically
- recognized by <c>binary_to_term/1</c> as from Erlang R7B.</p>
+ recognized by <c>binary_to_term/1</c> as from Erlang/OTP R7B.</p>
<p>A compression level can be specified by giving option
<c>{compressed, <anno>Level</anno>}</c>.
<c><anno>Level</anno></c> is an integer
with range 0..9, where:</p>
<list type="bulleted">
- <item><c>0</c> - No compression is done (it is the same as
- giving no <c>compressed</c> option).</item>
- <item><c>1</c> - Takes least time but may not compress
- as well as the higher levels.</item>
- <item><c>6</c> - Default level when option <c>compressed</c>
- is provided.</item>
- <item><c>9</c> - Takes most time and tries to produce a smaller
+ <item><p><c>0</c> - No compression is done (it is the same as
+ giving no <c>compressed</c> option).</p></item>
+ <item><p><c>1</c> - Takes least time but may not compress
+ as well as the higher levels.</p></item>
+ <item><p><c>6</c> - Default level when option <c>compressed</c>
+ is provided.</p></item>
+ <item><p><c>9</c> - Takes most time and tries to produce a smaller
result. Notice "tries" in the preceding sentence; depending
on the input term, level 9 compression either does or does
- not produce a smaller result than level 1 compression.</item>
+ not produce a smaller result than level 1 compression.</p></item>
</list>
<p>Option <c>{minor_version, <anno>Version</anno>}</c>
- can be used to control
- some encoding details. This option was introduced in OTP R11B-4.
+ can be used to control some
+ encoding details. This option was introduced in Erlang/OTP R11B-4.
The valid values for <c><anno>Version</anno></c> are
<c>0</c> and <c>1</c>.</p>
- <p>As from OTP 17.0, <c>{minor_version, 1}</c> is the default. It
+ <p>As from Erlang/OTP 17.0, <c>{minor_version, 1}</c> is the default. It
forces any floats in the term to be encoded in a more
space-efficient and exact way (namely in the 64-bit IEEE format,
rather than converted to a textual representation).</p>
- <p>As from OTP R11B-4, <c>binary_to_term/1</c> can decode this
+ <p>As from Erlang/OTP R11B-4, <c>binary_to_term/1</c> can decode this
representation.</p>
<p><c>{minor_version, 0}</c> means that floats are encoded
using a textual representation. This option is useful to
- ensure that releases before OTP R11B-4 can decode resulting
+ ensure that releases before Erlang/OTP R11B-4 can decode resulting
binary.</p>
- <p>See also
- <seealso marker="#binary_to_term/1">binary_to_term/1</seealso>.</p>
+ <p>See also <seealso marker="#binary_to_term/1">
+ <c>binary_to_term/1</c></seealso>.</p>
</desc>
</func>
<func>
<name name="throw" arity="1"/>
- <fsummary>Throws an exception.</fsummary>
+ <fsummary>Throw an exception.</fsummary>
<desc>
<p>A non-local return from a function. If evaluated within a
- <c>catch</c>, <c>catch</c> returns value <c><anno>Any</anno></c>.</p>
- <p>Example:</p>
+ <c>catch</c>, <c>catch</c> returns value <c><anno>Any</anno></c>.
+ Example:</p>
<pre>
> <input>catch throw({hello, there}).</input>
{hello,there}</pre>
@@ -8451,8 +8708,7 @@ ok
<desc>
<p>Returns the current time as <c>{Hour, Minute, Second}</c>.</p>
<p>The time zone and Daylight Saving Time correction depend on
- the underlying OS.</p>
- <p>Example:</p>
+ the underlying OS. Example:</p>
<pre>
> <input>time().</input>
{9,42,44}</pre>
@@ -8461,86 +8717,94 @@ ok
<func>
<name name="time_offset" arity="0"/>
- <fsummary>Current time offset</fsummary>
- <desc>
- <p>Returns the current time offset between
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso>
- and
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso> in
- <c>native</c> <seealso marker="#type_time_unit">time unit</seealso>.
- Current time offset added to an Erlang monotonic time gives
- corresponding Erlang system time.</p>
-
- <p>The time offset may or may not change during operation depending
- on the <seealso marker="time_correction#Time_Warp_Modes">time
- warp mode</seealso> used.</p>
-
- <note>
- <p>A change in time offset may be observed at slightly
- different points in time by different processes.</p>
-
- <p>If the runtime system is in
- <seealso marker="time_correction#Multi_Time_Warp_Mode">multi
- time warp mode</seealso>, the time offset will be changed when
- the runtime system detects that the
- <seealso marker="time_correction#OS_System_Time">OS system
- time</seealso> has changed. The runtime system will, however,
- not detect this immediately when it happens. A task checking
- the time offset is scheduled to execute at least once a minute,
- so under normal operation this should be detected within a
- minute, but during heavy load it might take longer time.</p>
- </note>
+ <fsummary>Current time offset.</fsummary>
+ <desc>
+ <p>Returns the current time offset between
+ <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso> and
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso> in
+ <c>native</c> <seealso marker="#type_time_unit">time unit</seealso>.
+ Current time offset added to an Erlang monotonic time gives
+ corresponding Erlang system time.</p>
+ <p>The time offset may or may not change during operation depending
+ on the <seealso marker="time_correction#Time_Warp_Modes">time
+ warp mode</seealso> used.</p>
+ <note>
+ <p>A change in time offset can be observed at slightly
+ different points in time by different processes.</p>
+ <p>If the runtime system is in
+ <seealso marker="time_correction#Multi_Time_Warp_Mode">multi-time
+ warp mode</seealso>, the time offset is changed when
+ the runtime system detects that the
+ <seealso marker="time_correction#OS_System_Time">OS system
+ time</seealso> has changed. The runtime system will, however,
+ not detect this immediately when it occurs. A task checking
+ the time offset is scheduled to execute at least once a minute;
+ so, under normal operation this is to be detected within a
+ minute, but during heavy load it can take longer time.</p>
+ </note>
</desc>
</func>
+
<func>
<name name="time_offset" arity="1"/>
- <fsummary>Current time offset</fsummary>
+ <fsummary>Current time offset.</fsummary>
<desc>
- <p>Returns the current time offset between
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso>
- and
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- converted into the <c><anno>Unit</anno></c> passed as argument.</p>
-
- <p>Same as calling
- <seealso marker="#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#time_offset/0"><c>erlang:time_offset()</c></seealso><c>, native, <anno>Unit</anno>)</c>
- however optimized for commonly used <c><anno>Unit</anno></c>s.</p>
+ <p>Returns the current time offset between
+ <seealso marker="time_correction#Erlang_Monotonic_Time">
+ Erlang monotonic time</seealso> and
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso>
+ converted into the <c><anno>Unit</anno></c> passed as argument.</p>
+ <p>Same as calling
+ <seealso marker="#convert_time_unit/3">
+ <c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#time_offset/0">
+ <c>erlang:time_offset()</c></seealso><c>, native,
+ <anno>Unit</anno>)</c>
+ however optimized for commonly used <c><anno>Unit</anno></c>s.</p>
</desc>
</func>
+
<func>
<name name="timestamp" arity="0"/>
- <fsummary>Current Erlang System time</fsummary>
+ <fsummary>Current Erlang System time.</fsummary>
<type name="timestamp"/>
<desc>
- <p>Returns current
- <seealso marker="time_correction#Erlang_System_Time">Erlang system time</seealso>
- on the format <c>{MegaSecs, Secs, MicroSecs}</c>. This format is
- the same as <seealso marker="kernel:os#timestamp/0"><c>os:timestamp/0</c></seealso>
- and the deprecated <seealso marker="#now/0"><c>erlang:now/0</c></seealso>
- uses. The reason for the existence of <c>erlang:timestamp()</c> is
- purely to simplify usage for existing code that assumes this timestamp
- format. Current Erlang system time can more efficiently be retrieved in
- the time unit of your choice using
- <seealso marker="#system_time/1"><c>erlang:system_time/1</c></seealso>.</p>
-
- <p>The <c>erlang:timestamp()</c> BIF is equivalent to:</p><code type="none">
+ <p>Returns current
+ <seealso marker="time_correction#Erlang_System_Time">
+ Erlang system time</seealso>
+ on the format <c>{MegaSecs, Secs, MicroSecs}</c>. This format is
+ the same as <seealso marker="kernel:os#timestamp/0">
+ <c>os:timestamp/0</c></seealso>
+ and the deprecated <seealso marker="#now/0">
+ <c>erlang:now/0</c></seealso>
+ use. The reason for the existence of <c>erlang:timestamp()</c> is
+ purely to simplify use for existing code that assumes this time stamp
+ format. Current Erlang system time can more efficiently be retrieved
+ in the time unit of your choice using
+ <seealso marker="#system_time/1">
+ <c>erlang:system_time/1</c></seealso>.</p>
+ <p>The <c>erlang:timestamp()</c> BIF is equivalent to:</p>
+<code type="none">
timestamp() ->
- ErlangSystemTime = erlang:system_time(micro_seconds),
+ ErlangSystemTime = erlang:system_time(microsecond),
MegaSecs = ErlangSystemTime div 1000000000000,
Secs = ErlangSystemTime div 1000000 - MegaSecs*1000000,
MicroSecs = ErlangSystemTime rem 1000000,
{MegaSecs, Secs, MicroSecs}.</code>
- <p>It, however, uses a native implementation which does
- not build garbage on the heap and with slightly better
- performance.</p>
-
- <note><p>This time is <em>not</em> a monotonically increasing time
- in the general case. For more information, see the documentation of
- <seealso marker="time_correction#Time_Warp_Modes">time warp modes</seealso> in the
- ERTS User's Guide.</p></note>
+ <p>It, however, uses a native implementation that does
+ not build garbage on the heap and with slightly better
+ performance.</p>
+ <note>
+ <p>This time is <em>not</em> a monotonically increasing time
+ in the general case. For more information, see the documentation of
+ <seealso marker="time_correction#Time_Warp_Modes">
+ time warp modes</seealso> in the User's Guide.</p>
+ </note>
</desc>
-
</func>
+
<func>
<name name="tl" arity="1"/>
<fsummary>Tail of a list.</fsummary>
@@ -8558,7 +8822,7 @@ timestamp() ->
<func>
<name name="trace" arity="3"/>
- <fsummary>Sets trace flags for a process or processes.</fsummary>
+ <fsummary>Set trace flags for a process or processes.</fsummary>
<type name="trace_flag"/>
<desc>
<p>Turns on (if <c><anno>How</anno> == true</c>) or off (if
@@ -8568,49 +8832,43 @@ timestamp() ->
<c><anno>PidPortSpec</anno></c>.</p>
<p><c><anno>PidPortSpec</anno></c> is either a process identifier
(pid) for a local process, a port identifier,
- or one of the following atoms:</p>
+ or one of the following atoms:</p>
<taglist>
<tag><c>all</c></tag>
- <item>
- <p>All currently existing processes and ports and all that
- will be created in the future.</p>
+ <item>All currently existing processes and ports and all that
+ will be created in the future.
</item>
<tag><c>processes</c></tag>
- <item>
- <p>All currently existing processes and all that will be created in the future.</p>
+ <item>All currently existing processes and all that will be created
+ in the future.
</item>
<tag><c>ports</c></tag>
- <item>
- <p>All currently existing ports and all that will be created in the future.</p>
+ <item>All currently existing ports and all that will be created in
+ the future.
</item>
<tag><c>existing</c></tag>
- <item>
- <p>All currently existing processes and ports.</p>
+ <item>All currently existing processes and ports.
</item>
<tag><c>existing_processes</c></tag>
- <item>
- <p>All currently existing processes.</p>
+ <item>All currently existing processes.
</item>
<tag><c>existing_ports</c></tag>
- <item>
- <p>All currently existing ports.</p>
+ <item>All currently existing ports.
</item>
<tag><c>new</c></tag>
- <item>
- <p>All processes and ports that will be created in the future.</p>
+ <item>All processes and ports that will be created in the future.
</item>
<tag><c>new_processes</c></tag>
- <item>
- <p>All processes that will be created in the future.</p>
+ <item>All processes that will be created in the future.
</item>
<tag><c>new_ports</c></tag>
- <item>
- <p>All ports that will be created in the future.</p>
+ <item>All ports that will be created in the future.
</item>
</taglist>
<p><c><anno>FlagList</anno></c> can contain any number of the
following flags (the "message tags" refers to the list of
- <seealso marker="#trace_3_trace_messages">trace messages</seealso>):</p>
+ <seealso marker="#trace_3_trace_messages">
+ <c>trace messages</c></seealso>):</p>
<taglist>
<tag><c>all</c></tag>
<item>
@@ -8621,21 +8879,29 @@ timestamp() ->
<tag><c>send</c></tag>
<item>
<p>Traces sending of messages.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_send"><c>send</c></seealso> and
- <seealso marker="#trace_3_trace_messages_send_to_non_existing_process"><c>send_to_non_existing_process</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_send">
+ <c>send</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_send_to_non_existing_process">
+ <c>send_to_non_existing_process</c></seealso>.</p>
</item>
<tag><c>'receive'</c></tag>
<item>
<p>Traces receiving of messages.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_receive"><c>'receive'</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_receive">
+ <c>'receive'</c></seealso>.</p>
</item>
-<tag><c>call</c></tag>
+ <tag><c>call</c></tag>
<item>
<p>Traces certain function calls. Specify which function
- calls to trace by calling
- <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_call"><c>call</c></seealso> and
- <seealso marker="#trace_3_trace_messages_return_from"><c>return_from</c></seealso>.</p>
+ calls to trace by calling <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern/3</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_call">
+ <c>call</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_return_from">
+ <c>return_from</c></seealso>.</p>
</item>
<tag><c>silent</c></tag>
<item>
@@ -8653,17 +8919,21 @@ timestamp() ->
specification function <c>{silent,Bool}</c>, giving
a high degree of control of which functions with which
arguments that trigger the trace.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_call"><c>call</c></seealso>,
- <seealso marker="#trace_3_trace_messages_return_from"><c>return_from</c></seealso>, and
- <seealso marker="#trace_3_trace_messages_return_to"><c>return_to</c></seealso>. Or rather, the absence of.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_call">
+ <c>call</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_return_from">
+ <c>return_from</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_return_to">
+ <c>return_to</c></seealso>. Or rather, the absence of.</p>
</item>
<tag><c>return_to</c></tag>
<item>
<p>Used with the <c>call</c> trace flag.
Traces the return from a traced function back to
its caller. Only works for functions traced with
- option <c>local</c> to
- <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p>
+ option <c>local</c> to <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern/3</c></seealso>.</p>
<p>The semantics is that a trace message is sent when a
call traced function returns, that is, when a
chain of tail recursive calls ends. Only one trace
@@ -8676,105 +8946,144 @@ timestamp() ->
<p>To get trace messages containing return values from
functions, use the <c>{return_trace}</c> match
specification action instead.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_return_to"><c>return_to</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_return_to">
+ <c>return_to</c></seealso>.</p>
</item>
<tag><c>procs</c></tag>
<item>
<p>Traces process-related events.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_spawn"><c>spawn</c></seealso>,
- <seealso marker="#trace_3_trace_messages_spawned"><c>spawned</c></seealso>,
- <seealso marker="#trace_3_trace_messages_exit"><c>exit</c></seealso>,
- <seealso marker="#trace_3_trace_messages_register"><c>register</c></seealso>,
- <seealso marker="#trace_3_trace_messages_unregister"><c>unregister</c></seealso>,
- <seealso marker="#trace_3_trace_messages_link"><c>link</c></seealso>,
- <seealso marker="#trace_3_trace_messages_unlink"><c>unlink</c></seealso>,
- <seealso marker="#trace_3_trace_messages_getting_linked"><c>getting_linked</c></seealso>, and
- <seealso marker="#trace_3_trace_messages_getting_unlinked"><c>getting_unlinked</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_spawn">
+ <c>spawn</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_spawned">
+ <c>spawned</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_exit">
+ <c>exit</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_register">
+ <c>register</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_unregister">
+ <c>unregister</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_link">
+ <c>link</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_unlink">
+ <c>unlink</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_getting_linked">
+ <c>getting_linked</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_getting_unlinked">
+ <c>getting_unlinked</c></seealso>.</p>
</item>
<tag><c>ports</c></tag>
<item>
<p>Traces port-related events.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_open"><c>open</c></seealso>,
- <seealso marker="#trace_3_trace_messages_closed"><c>closed</c></seealso>,
- <seealso marker="#trace_3_trace_messages_register"><c>register</c></seealso>,
- <seealso marker="#trace_3_trace_messages_unregister"><c>unregister</c></seealso>,
- <seealso marker="#trace_3_trace_messages_getting_linked"><c>getting_linked</c></seealso>, and
- <seealso marker="#trace_3_trace_messages_getting_unlinked"><c>getting_unlinked</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_open">
+ <c>open</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_closed">
+ <c>closed</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_register">
+ <c>register</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_unregister">
+ <c>unregister</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_getting_linked">
+ <c>getting_linked</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_getting_unlinked">
+ <c>getting_unlinked</c></seealso>.</p>
</item>
<tag><c>running</c></tag>
<item>
<p>Traces scheduling of processes.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_in_proc"><c>in</c></seealso> and
- <seealso marker="#trace_3_trace_messages_out_proc"><c>out</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_in_proc">
+ <c>in</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_out_proc">
+ <c>out</c></seealso>.</p>
</item>
<tag><c>exiting</c></tag>
<item>
<p>Traces scheduling of exiting processes.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_in_exiting_proc"><c>in_exiting</c></seealso>,
- <seealso marker="#trace_3_trace_messages_out_exiting_proc"><c>out_exiting</c></seealso>, and
- <seealso marker="#trace_3_trace_messages_out_exited_proc"><c>out_exited</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_in_exiting_proc">
+ <c>in_exiting</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_out_exiting_proc">
+ <c>out_exiting</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_out_exited_proc">
+ <c>out_exited</c></seealso>.</p>
</item>
<tag><c>running_procs</c></tag>
<item>
<p>Traces scheduling of processes just like <c>running</c>.
- However this option also includes schedule events when the
- process executes within the context of a port without
- being scheduled out itself.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_in_proc"><c>in</c></seealso> and
- <seealso marker="#trace_3_trace_messages_out_proc"><c>out</c></seealso>.</p>
+ However, this option also includes schedule events when the
+ process executes within the context of a port without
+ being scheduled out itself.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_in_proc">
+ <c>in</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_out_proc">
+ <c>out</c></seealso>.</p>
</item>
<tag><c>running_ports</c></tag>
<item>
<p>Traces scheduling of ports.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_in_port"><c>in</c></seealso> and
- <seealso marker="#trace_3_trace_messages_out_port"><c>out</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_in_port">
+ <c>in</c></seealso> and
+ <seealso marker="#trace_3_trace_messages_out_port">
+ <c>out</c></seealso>.</p>
</item>
<tag><c>garbage_collection</c></tag>
<item>
<p>Traces garbage collections of processes.</p>
- <p>Message tags: <seealso marker="#trace_3_trace_messages_gc_minor_start"><c>gc_minor_start</c></seealso>,
- <seealso marker="#trace_3_trace_messages_gc_max_heap_size"><c>gc_max_heap_size</c></seealso> and
- <seealso marker="#trace_3_trace_messages_gc_minor_end"><c>gc_minor_end</c></seealso>.</p>
+ <p>Message tags:
+ <seealso marker="#trace_3_trace_messages_gc_minor_start">
+ <c>gc_minor_start</c></seealso>,
+ <seealso marker="#trace_3_trace_messages_gc_max_heap_size">
+ <c>gc_max_heap_size</c></seealso>, and
+ <seealso marker="#trace_3_trace_messages_gc_minor_end">
+ <c>gc_minor_end</c></seealso>.</p>
</item>
<tag><c>timestamp</c></tag>
<item>
- <p>Includes a time-stamp in all trace messages. The
- time-stamp (Ts) has the same form as returned by
+ <p>Includes a time stamp in all trace messages. The
+ time stamp (Ts) has the same form as returned by
<c>erlang:now()</c>.</p>
</item>
<tag><c>cpu_timestamp</c></tag>
<item>
<p>A global trace flag for the Erlang node that makes all
- trace time-stamps using the <c>timestamp</c> flag to be
- in CPU time, not wall clock time. That is, <c>cpu_timestamp</c>
- will not be used if <c>monotonic_timestamp</c>, or
- <c>strict_monotonic_timestamp</c> is enabled.
+ trace time stamps using flag <c>timestamp</c> to be
+ in CPU time, not wall clock time. That is, <c>cpu_timestamp</c>
+ is not be used if <c>monotonic_timestamp</c> or
+ <c>strict_monotonic_timestamp</c> is enabled.
Only allowed with <c><anno>PidPortSpec</anno>==all</c>. If the
host machine OS does not support high-resolution
CPU time measurements, <c>trace/3</c> exits with
<c>badarg</c>. Notice that most OS do
not synchronize this value across cores, so be prepared
- that time might seem to go backwards when using this option.</p>
+ that time can seem to go backwards when using this option.</p>
</item>
<tag><c>monotonic_timestamp</c></tag>
<item>
<p>Includes an
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> time-stamp in all trace messages. The
- time-stamp (Ts) has the same format and value as produced by
- <seealso marker="#monotonic_time-1"><c>erlang:monotonic_time(nano_seconds)</c></seealso>.
- This flag overrides the <c>cpu_timestamp</c> flag.</p>
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> time stamp in all trace messages. The
+ time stamp (Ts) has the same format and value as produced by
+ <seealso marker="#monotonic_time-1">
+ <c>erlang:monotonic_time(nanosecond)</c></seealso>.
+ This flag overrides flag <c>cpu_timestamp</c>.</p>
</item>
<tag><c>strict_monotonic_timestamp</c></tag>
<item>
- <p>Includes an timestamp consisting of
- <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
- monotonic time</seealso> and a monotonically increasing
- integer in all trace messages. The time-stamp (Ts) has the
- same format and value as produced by
- <c>{</c><seealso marker="#monotonic_time-1"><c>erlang:monotonic_time(nano_seconds)</c></seealso><c>,</c>
- <seealso marker="#unique_integer-1"><c>erlang:unique_integer([monotonic])</c></seealso><c>}</c>.
- This flag overrides the <c>cpu_timestamp</c> flag.</p>
+ <p>Includes an time stamp consisting of
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso> and a monotonically increasing
+ integer in all trace messages. The time stamp (Ts) has the
+ same format and value as produced by <c>{</c>
+ <seealso marker="#monotonic_time-1">
+ <c>erlang:monotonic_time(nanosecond)</c></seealso><c>,</c>
+ <seealso marker="#unique_integer-1">
+ <c>erlang:unique_integer([monotonic])</c></seealso><c>}</c>.
+ This flag overrides flag <c>cpu_timestamp</c>.</p>
</item>
<tag><c>arity</c></tag>
<item>
@@ -8812,34 +9121,34 @@ timestamp() ->
</item>
<tag><c>{tracer, TracerModule, TracerState}</c></tag>
<item>
- <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="erts:erl_tracer"><c>erl_tracer</c></seealso>
- </p>
+ <p>Specifies that a tracer module is to 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="erts:erl_tracer"><c>erl_tracer(3)</c></seealso>.</p>
</item>
</taglist>
- <p>If no <c>tracer</c> is given, the calling process
- will be receiving all of the trace messages</p>
+ <p>If no <c>tracer</c> is specified, the calling process
+ receives all the trace messages.</p>
<p>The effect of combining <c>set_on_first_link</c> with
- <c>set_on_link</c> is the same as having
+ <c>set_on_link</c> is the same as
<c>set_on_first_link</c> alone. Likewise for
<c>set_on_spawn</c> and <c>set_on_first_spawn</c>.</p>
<p>The tracing process receives the <em>trace messages</em> described
- in the following list. <c>Pid</c> is the process identifier of the
- traced process in which the traced event has occurred. The
- third tuple element is the message tag.</p>
+ in the following list. <c>Pid</c> is the process identifier of the
+ traced process in which the traced event has occurred. The
+ third tuple element is the message tag.</p>
<p>If flag <c>timestamp</c>, <c>strict_monotonic_timestamp</c>, or
- <c>monotonic_timestamp</c> is given, the first tuple
- element is <c>trace_ts</c> instead, and the time-stamp
- is added as an extra element last in the message tuple. If
- multiple timestamp flags are passed, <c>timestamp</c> has
- precedence over <c>strict_monotonic_timestamp</c> which
- in turn has precedence over <c>monotonic_timestamp</c>. All
- timestamp flags are remembered, so if two are passed
- and the one with highest precedence later is disabled
- the other one will become active.</p>
+ <c>monotonic_timestamp</c> is specified, the first tuple
+ element is <c>trace_ts</c> instead, and the time stamp
+ is added as an extra element last in the message tuple. If
+ multiple time stamp flags are passed, <c>timestamp</c> has
+ precedence over <c>strict_monotonic_timestamp</c>, which
+ in turn has precedence over <c>monotonic_timestamp</c>. All
+ time stamp flags are remembered, so if two are passed
+ and the one with highest precedence later is disabled,
+ the other one becomes active.</p>
+ <p>Trace messages:</p>
<marker id="trace_3_trace_messages"></marker>
<taglist>
<tag>
@@ -8851,7 +9160,7 @@ timestamp() ->
process <c>To</c>.</p>
</item>
<tag>
- <marker id="trace_3_trace_messages_send_to_non_existing_process"></marker>
+ <marker id="trace_3_trace_messages_send_to_non_existing_process"/>
<c>{trace, PidPort, send_to_non_existing_process, Msg, To}</c>
</tag>
<item>
@@ -8864,9 +9173,9 @@ timestamp() ->
</tag>
<item>
<p>When <c>PidPort</c> receives message <c>Msg</c>.
- If <c>Msg</c> is set to timeout, then a receive
- statement may have timedout, or the process received
- a message with the payload <c>timeout</c>.</p>
+ If <c>Msg</c> is set to time-out, a receive
+ statement can have timed out, or the process received
+ a message with the payload <c>timeout</c>.</p>
</item>
<tag>
<marker id="trace_3_trace_messages_call"></marker>
@@ -9000,7 +9309,7 @@ timestamp() ->
</tag>
<item>
<p>When <c>Pid</c> opens a new port <c>Port</c> with
- the running the <c>Driver</c>.</p>
+ the running <c>Driver</c>.</p>
<p><c>Driver</c> is the name of the driver as an atom.</p>
</item>
<tag>
@@ -9008,7 +9317,7 @@ timestamp() ->
<c>{trace, Port, closed, Reason}</c>
</tag>
<item>
- <p>When <c>Port</c> closed with <c>Reason</c>.</p>
+ <p>When <c>Port</c> closes with <c>Reason</c>.</p>
</item>
<tag>
<marker id="trace_3_trace_messages_in_proc"></marker>
@@ -9025,7 +9334,8 @@ timestamp() ->
<marker id="trace_3_trace_messages_out_proc"></marker>
<marker id="trace_3_trace_messages_out_exiting_proc"></marker>
<marker id="trace_3_trace_messages_out_exited_proc"></marker>
- <c>{trace, Pid, out | out_exiting | out_exited, {M, F, Arity} | 0}</c>
+ <c>{trace, Pid, out | out_exiting | out_exited, {M, F, Arity}
+ | 0}</c>
</tag>
<item>
<p>When <c>Pid</c> is scheduled out. The process was
@@ -9039,11 +9349,13 @@ timestamp() ->
</tag>
<item>
<p>When <c>Port</c> is scheduled to run. <c>Command</c> is the
- first thing the port will execute, it may however run several
- commands before being scheduled out. On some rare
- occasions, the current function cannot be determined,
- then the last element is <c>0</c>.</p>
- <p>The possible commands are: <c>call | close | command | connect | control | flush | info | link | open | unlink</c></p>
+ first thing the port will execute, it can however run several
+ commands before being scheduled out. On some rare
+ occasions, the current function cannot be determined,
+ then the last element is <c>0</c>.</p>
+ <p>The possible commands are <c>call</c>, <c>close</c>,
+ <c>command</c>, <c>connect</c>, <c>control</c>, <c>flush</c>,
+ <c>info</c>, <c>link</c>, <c>open</c>, and <c>unlink</c>.</p>
</item>
<tag>
<marker id="trace_3_trace_messages_out_port"></marker>
@@ -9054,8 +9366,7 @@ timestamp() ->
was <c>Command</c>. On some rare occasions,
the current function cannot be determined, then the last
element is <c>0</c>. <c>Command</c> can contain the same
- commands as <c>in</c>
- </p>
+ commands as <c>in</c></p>
</item>
<tag>
<marker id="trace_3_trace_messages_gc_minor_start"></marker>
@@ -9071,13 +9382,13 @@ timestamp() ->
<taglist>
<tag><c>heap_size</c></tag>
<item>The size of the used part of the heap.</item>
- <tag><c>heap_block_size</c></tag>
- <item>The size of the memory block used for storing
- the heap and the stack.</item>
+ <tag><c>heap_block_size</c></tag>
+ <item>The size of the memory block used for storing
+ the heap and the stack.</item>
<tag><c>old_heap_size</c></tag>
<item>The size of the used part of the old heap.</item>
- <tag><c>old_heap_block_size</c></tag>
- <item>The size of the memory block used for storing
+ <tag><c>old_heap_block_size</c></tag>
+ <item>The size of the memory block used for storing
the old heap.</item>
<tag><c>stack_size</c></tag>
<item>The size of the stack.</item>
@@ -9091,14 +9402,15 @@ timestamp() ->
<item>The total size of unique off-heap binaries referenced
from the process heap.</item>
<tag><c>bin_vheap_block_size</c></tag>
- <item>The total size of binaries allowed in the virtual
- heap in the process before doing a garbage collection.</item>
+ <item>The total size of binaries allowed in the virtual
+ heap in the process before doing a garbage collection.</item>
<tag><c>bin_old_vheap_size</c></tag>
<item>The total size of unique off-heap binaries referenced
from the process old heap.</item>
<tag><c>bin_old_vheap_block_size</c></tag>
- <item>The total size of binaries allowed in the virtual
- old heap in the process before doing a garbage collection.</item>
+ <item>The total size of binaries allowed in the virtual
+ old heap in the process before doing a garbage
+ collection.</item>
</taglist>
<p>All sizes are in words.</p>
</item>
@@ -9107,13 +9419,12 @@ timestamp() ->
<c>{trace, Pid, gc_max_heap_size, Info}</c>
</tag>
<item>
- <p>
- Sent when the <seealso marker="#process_flag_max_heap_size"><c>max_heap_size</c></seealso>
+ <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>
+ but the sizes reflect the sizes that triggered
+ <c>max_heap_size</c> to be reached.</p>
</item>
<tag>
<marker id="trace_3_trace_messages_gc_minor_end"></marker>
@@ -9121,7 +9432,8 @@ timestamp() ->
</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>,
+ 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>
@@ -9130,8 +9442,9 @@ timestamp() ->
<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>
+ <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>
@@ -9139,15 +9452,16 @@ timestamp() ->
</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>
+ 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>
+ <c>remove</c>, the flags are silently removed.</p>
<p>Each process can only be traced by one tracer. Therefore,
attempts to trace an already traced process fail.</p>
- <p>Returns: A number indicating the number of processes that
+ <p>Returns a number indicating the number of processes that
matched <c><anno>PidPortSpec</anno></c>.
If <c><anno>PidPortSpec</anno></c> is a process
identifier, the return value is <c>1</c>.
@@ -9167,22 +9481,24 @@ timestamp() ->
<fsummary>Notification when trace has been delivered.</fsummary>
<desc>
<p>The delivery of trace messages (generated by
- <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>,
- <seealso marker="kernel:seq_trace"><c>seq_trace</c></seealso> or
- <seealso marker="#system_profile/2"><c>erlang:system_profile/2</c></seealso>)
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>,
+ <seealso marker="kernel:seq_trace"><c>seq_trace(3)</c></seealso>,
+ or <seealso marker="#system_profile/2">
+ <c>erlang:system_profile/2</c></seealso>)
is dislocated on the time-line
compared to other events in the system. If you know that
<c><anno>Tracee</anno></c> has passed some specific point
in its execution,
and you want to know when at least all trace messages
corresponding to events up to this point have reached the
- tracer, use <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>. A
- <c>{trace_delivered, <anno>Tracee</anno>, <anno>Ref</anno>}</c> message is sent to
- the caller of <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> when it
- is guaranteed that all trace messages are delivered to
+ tracer, use <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>.</p>
+ <p>When it is guaranteed that all trace messages are delivered to
the tracer up to the point that <c><anno>Tracee</anno></c> reached
at the time of the call to
- <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>.</p>
+ <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>, then a
+ <c>{trace_delivered, <anno>Tracee</anno>, <anno>Ref</anno>}</c>
+ message is sent to the caller of
+ <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> .</p>
<p>Notice that message <c>trace_delivered</c> does <em>not</em>
imply that trace messages have been delivered.
Instead it implies that all trace messages that
@@ -9192,22 +9508,22 @@ timestamp() ->
<em>no</em> trace messages have been delivered when the
<c>trace_delivered</c> message arrives.</p>
<p>Notice that <c><anno>Tracee</anno></c> must refer
- to a process currently,
+ to a process currently
or previously existing on the same node as the caller of
<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="erts:erl_tracer">
- Tracer Module</seealso> any message sent in the trace callback
- is guaranteed to have reached it's recipient before the
+ <p>When used together with a <seealso marker="erts:erl_tracer">
+ Tracer Module</seealso>, any message sent in the trace callback
+ is guaranteed to have reached its recipient before the
<c>trace_delivered</c> message is sent.</p>
<p>Example: Process <c>A</c> is <c><anno>Tracee</anno></c>,
- port <c>B</c> is tracer, and process <c>C</c> is the port
- owner of <c>B</c>. <c>C</c> wants to close <c>B</c> when
- <c>A</c> exits. To ensure that the trace is not truncated,
- <c>C</c> can call <c>erlang:trace_delivered(A)</c>, when
- <c>A</c> exits, and wait for message <c>{trace_delivered, A,
- <anno>Ref</anno>}</c> before closing <c>B</c>.</p>
+ port <c>B</c> is tracer, and process <c>C</c> is the port
+ owner of <c>B</c>. <c>C</c> wants to close <c>B</c> when
+ <c>A</c> exits. To ensure that the trace is not truncated,
+ <c>C</c> can call <c>erlang:trace_delivered(A)</c> when
+ <c>A</c> exits, and wait for message <c>{trace_delivered, A,
+ <anno>Ref</anno>}</c> before closing <c>B</c>.</p>
<p>Failure: <c>badarg</c> if <c><anno>Tracee</anno></c>
does not refer to a
process (dead or alive) on the same node as the caller of
@@ -9217,22 +9533,27 @@ timestamp() ->
<func>
<name name="trace_info" arity="2"/>
- <fsummary>Trace information about a process or function.</fsummary>
+ <fsummary>Trace information about a process or function.</fsummary>
<type name="trace_info_return"/>
<type name="trace_info_item_result"/>
<type name="trace_info_flag"/>
<type name="trace_match_spec"/>
+ <type name="match_variable"/>
+ <type_desc name="match_variable">
+ Approximation of '$1' | '$2' | '$3' | ...
+ </type_desc>
<desc>
- <p>Returns trace information about a port, process, function or event.</p>
+ <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 for ports and processes:</p>
+ <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>, or <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>Valid <c>Item</c>s for ports and processes:</p>
<taglist>
<tag><c>flags</c></tag>
<item>
@@ -9241,30 +9562,32 @@ timestamp() ->
traces are enabled, and one or more of the followings
atoms if traces are enabled: <c>send</c>,
<c>'receive'</c>, <c>set_on_spawn</c>, <c>call</c>,
- <c>return_to</c>, <c>procs</c>, <c>ports</c>, <c>set_on_first_spawn</c>,
+ <c>return_to</c>, <c>procs</c>, <c>ports</c>,
+ <c>set_on_first_spawn</c>,
<c>set_on_link</c>, <c>running</c>, <c>running_procs</c>,
- <c>running_ports</c>, <c>silent</c>, <c>exiting</c>
+ <c>running_ports</c>, <c>silent</c>, <c>exiting</c>,
<c>monotonic_timestamp</c>, <c>strict_monotonic_timestamp</c>,
<c>garbage_collection</c>, <c>timestamp</c>, and
<c>arity</c>. The order is arbitrary.</p>
</item>
<tag><c>tracer</c></tag>
<item>
- <p>Returns the identifier for process, port or a tuple containing
+ <p>Returns the identifier for process, port, or a tuple containing
the tracer module and tracer state tracing this
- process. If this process is not being traced, the return
+ process. If this process is not traced, the return
value is <c>[]</c>.</p>
</item>
</taglist>
- <p><em>To get information about a function</em>, <c><anno>PidPortFuncEvent</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
+ the atom <c>on_load</c>. No wildcards are allowed. Returns
<c>undefined</c> if the function does not exist, or
- <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>
+ <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>Valid <c>Item</c>s for functions:</p>
<taglist>
<tag><c>traced</c></tag>
<item>
@@ -9283,11 +9606,12 @@ timestamp() ->
</item>
<tag><c>meta</c></tag>
<item>
- <p>Returns the meta-trace tracer process, port or trace module
+ <p>Returns the meta-trace tracer process, port, or trace module
for this function, if it has one. If the function is not
meta-traced, the returned value is <c>false</c>. If
the function is meta-traced but has once detected that
- the tracer process is invalid, the returned value is [].</p>
+ the tracer process is invalid, the returned value is
+ <c>[]</c>.</p>
</item>
<tag><c>meta_match_spec</c></tag>
<item>
@@ -9300,21 +9624,22 @@ timestamp() ->
<item>
<p>Returns the call count value for this function or
<c>true</c> for the pseudo function <c>on_load</c> if call
- count tracing is active. Otherwise <c>false</c> is returned.
- See also
- <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p>
+ count tracing is active. Otherwise <c>false</c> is returned.</p>
+ <p>See also <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern/3</c></seealso>.</p>
</item>
<tag><c>call_time</c></tag>
<item>
- <p>Returns the call time values for this function or
+ <p>Returns the call time values for this function or
<c>true</c> for the pseudo function <c>on_load</c> if call
- time tracing is active. Otherwise <c>false</c> is returned.
- The call time values returned, <c>[{Pid, Count, S, Us}]</c>,
- is a list of each process that executed the function
- and its specific counters. See also
- <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p>
+ time tracing is active. Otherwise <c>false</c> is returned.
+ The call time values returned, <c>[{Pid, Count, S, Us}]</c>,
+ is a list of each process that executed the function
+ and its specific counters.</p>
+ <p>See also
+ <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern/3</c></seealso>.</p>
</item>
-
<tag><c>all</c></tag>
<item>
<p>Returns a list containing the
@@ -9323,80 +9648,92 @@ 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
+ <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>
+ <p>One valid <c>Item</c> for events exists:</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>
+ 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
+ If a pid for a dead process was specified, or the name of a
non-existing function, <c>Value</c> is <c>undefined</c>.</p>
</desc>
</func>
<func>
<name name="trace_pattern" arity="2" clause_i="1"/>
- <fsummary>Sets trace patterns for call, send or 'receive' tracing.</fsummary>
+ <fsummary>Set trace patterns for call, send, or 'receive' tracing.
+ </fsummary>
<type name="trace_pattern_mfa"/>
<type name="trace_match_spec"/>
+ <type_desc name="match_variable">
+ Approximation of '$1' | '$2' | '$3' | ...
+ </type_desc>
+ <type name="match_variable"/>
<desc>
<p>The same as
- <seealso marker="#trace_pattern/3">erlang:trace_pattern(Event, MatchSpec, [])</seealso>,
+ <seealso marker="#trace_pattern/3">
+ <c>erlang:trace_pattern(Event, MatchSpec, [])</c></seealso>,
retained for backward compatibility.</p>
</desc>
</func>
<func>
<name name="trace_pattern" arity="3" clause_i="1"/>
- <fsummary>Sets trace pattern for message sending.</fsummary>
+ <fsummary>Set trace pattern for message sending.</fsummary>
<type name="trace_match_spec"/>
+ <type name="match_variable"/>
+ <type_desc name="match_variable">
+ Approximation of '$1' | '$2' | '$3' | ...
+ </type_desc>
<desc>
<p>Sets trace pattern for <em>message sending</em>.
- Must be combined with
- <seealso marker="#trace/3">erlang:trace/3</seealso>
+ Must be combined with
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></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>
+ By default all messages sent from <c>send</c> traced processes
+ are traced. To limit
+ traced send events based on the message content, the sender
+ and/or the receiver, use <c>erlang:trace_pattern/3</c>.</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>
+ 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>.
+ For more information, see section
+ <seealso marker="erts:match_spec">
+ Match Specifications in Erlang</seealso> in the User's Guide.</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>
+ 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>
+ 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>
+ <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>Examples:</p>
+ <p>Only trace messages to a specific process <c>Pid</c>:</p>
<pre>
> <input>erlang:trace_pattern(send, [{[Pid, '_'],[],[]}], []).</input>
1</pre>
@@ -9412,58 +9749,64 @@ timestamp() ->
<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>
+ <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>
+ <fsummary>Set trace pattern for tracing of message receiving.</fsummary>
<type name="trace_match_spec"/>
+ <type name="match_variable"/>
+ <type_desc name="match_variable">
+ Approximation of '$1' | '$2' | '$3' | ...
+ </type_desc>
<desc>
- <p></p>
<p>Sets trace pattern for <em>message receiving</em>.
- Must be combined with
- <seealso marker="#trace/3">erlang:trace/3</seealso>
+ Must be combined with
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></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>
+ By default all messages received by <c>'receive'</c> traced
+ processes are traced. To limit
+ traced receive events based on the message content, the sender
+ and/or the receiver, use <c>erlang:trace_pattern/3</c>.</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>
+ 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 can
+ 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>. For more information, see
+ section <seealso marker="erts:match_spec">
+ Match Specifications in Erlang</seealso> in the User's Guide.</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>
+ 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>
+ 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>
+ <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>Examples:</p>
+ <p>Only trace messages from a specific process <c>Pid</c>:</p>
<pre>
> <input>erlang:trace_pattern('receive', [{['_',Pid, '_'],[],[]}], []).</input>
1</pre>
@@ -9475,33 +9818,40 @@ timestamp() ->
<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>
+ <note>
+ <p>A match specification for <c>'receive'</c> trace can
+ use all guard and body functions except <c>caller</c>,
+ <c>is_seq_trace</c>, <c>get_seq_token</c>, <c>set_seq_token</c>,
+ <c>enable_trace</c>, <c>disable_trace</c>, <c>trace</c>,
+ <c>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>
+ <fsummary>Set trace patterns for tracing of function calls.</fsummary>
<type name="trace_pattern_mfa"/>
<type name="trace_match_spec"/>
<type name="trace_pattern_flag"/>
+ <type name="match_variable"/>
+ <type_desc name="match_variable">
+ Approximation of '$1' | '$2' | '$3' | ...
+ </type_desc>
<desc>
<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>
+ Must be combined with
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>
to set the <c>call</c> trace flag
- for one or more processes.</p>
+ for one or more processes.</p>
<p>Conceptually, call tracing works as follows. Inside
- the Erlang Virtual Machine, a set of processes and
+ the Erlang virtual machine, a set of processes and
a set of functions are to be traced. If a traced process
calls a traced function, the trace action is taken.
Otherwise, nothing happens.</p>
<p>To add or remove one or more processes to the set of traced
processes, use
- <seealso marker="#trace/3">erlang:trace/3</seealso>.</p>
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>.</p>
<p>To add or remove functions to the set of traced
functions, use <c>erlang:trace_pattern/3</c>.</p>
<p>The BIF <c>erlang:trace_pattern/3</c> can also add match
@@ -9513,10 +9863,10 @@ timestamp() ->
fails, the action is not executed.</p>
<p>Argument <c><anno>MFA</anno></c> is to be a tuple, such as
<c>{Module, Function, Arity}</c>, or the atom <c>on_load</c>
- (described in the following). It can be the module, function,
+ (described below). It can be the module, function,
and arity for a function (or a BIF in any module).
- The atom <c>'_'</c> can be used as a wild card in any of the
- following ways:</p>
+ The atom <c>'_'</c> can be used as a wildcard in any of the
+ following ways:</p>
<taglist>
<tag><c>{Module,Function,'_'}</c></tag>
<item>
@@ -9533,7 +9883,7 @@ timestamp() ->
</item>
</taglist>
<p>Other combinations, such as <c>{Module,'_',Arity}</c>, are
- not allowed. Local functions match wild cards only if
+ not allowed. Local functions match wildcards only if
option <c>local</c> is in <c><anno>FlagList</anno></c>.</p>
<p>If argument <c><anno>MFA</anno></c> is the atom <c>on_load</c>,
the match specification and flag list are used on all
@@ -9549,13 +9899,14 @@ timestamp() ->
<tag><c>true</c></tag>
<item>
<p>Enables tracing for the matching functions.
- Any match specification is removed.</p>
+ Any match specification is removed.</p>
</item>
<tag><c><anno>MatchSpecList</anno></c></tag>
<item>
<p>A list of match specifications. An empty list is
equivalent to <c>true</c>. For a description of match
- specifications, see the User's Guide.</p>
+ specifications, see section <seealso marker="erts:match_spec">
+ Match Specifications in Erlang</seealso> in the User's Guide.</p>
</item>
<tag><c>restart</c></tag>
<item>
@@ -9566,7 +9917,7 @@ timestamp() ->
</item>
<tag><c>pause</c></tag>
<item>
- <p>For the <c><anno>FlagList</anno></c> options
+ <p>For the <c><anno>FlagList</anno></c> options
<c>call_count</c> and <c>call_time</c>: pauses
the existing counters. The behavior is undefined for
other <c><anno>FlagList</anno></c> options.</p>
@@ -9591,7 +9942,8 @@ timestamp() ->
the process, a <c>return_to</c> message is also sent
when this function returns to its caller.</p>
</item>
- <tag><c>meta | {meta, <anno>Pid</anno>} | {meta, <anno>TracerModule</anno>, <anno>TracerState</anno>}</c>
+ <tag><c>meta | {meta, <anno>Pid</anno>} |
+ {meta, <anno>TracerModule</anno>, <anno>TracerState</anno>}</c>
</tag>
<item>
<p>Turns on or off meta-tracing for all types of function
@@ -9599,7 +9951,7 @@ timestamp() ->
the specified functions are called. If no tracer is specified,
<c>self()</c> is used as a default tracer process.</p>
<p>Meta-tracing traces all processes and does not care
- about the process trace flags set by <c>trace/3</c>,
+ about the process trace flags set by <c>erlang:trace/3</c>,
the trace flags are instead fixed to
<c>[call, timestamp]</c>.</p>
<p>The match specification function <c>{return_trace}</c>
@@ -9620,7 +9972,8 @@ timestamp() ->
Paused and running counters can be restarted from zero with
<c><anno>MatchSpec</anno> == restart</c>.</p>
<p>To read the counter value, use
- <seealso marker="#trace_info/2">erlang:trace_info/2</seealso>.</p>
+ <seealso marker="#trace_info/2">
+ <c>erlang:trace_info/2</c></seealso>.</p>
</item>
<tag><c>call_time</c></tag>
<item>
@@ -9628,17 +9981,18 @@ timestamp() ->
(<c><anno>MatchSpec</anno> == false</c>) call time
tracing for all
types of function calls. For every function, a counter is
- incremented when the function is called.
+ incremented when the function is called.
Time spent in the function is accumulated in
two other counters, seconds and microseconds.
- The counters are stored for each call traced process.</p>
+ The counters are stored for each call traced process.</p>
<p>If call time tracing is started while already running,
- the count and time is restarted from zero. To pause
+ the count and time restart from zero. To pause
running counters, use <c><anno>MatchSpec</anno> == pause</c>.
Paused and running counters can be restarted from zero with
<c><anno>MatchSpec</anno> == restart</c>.</p>
<p>To read the counter value, use
- <seealso marker="#trace_info/2">erlang:trace_info/2</seealso>.</p>
+ <seealso marker="#trace_info/2">
+ <c>erlang:trace_info/2</c></seealso>.</p>
</item>
</taglist>
<p>The options <c>global</c> and <c>local</c> are mutually
@@ -9653,12 +10007,12 @@ timestamp() ->
<p>When disabling trace, the option must match the type of trace
set on the function. That is, local tracing must be
disabled with option <c>local</c> and global tracing with
- option <c>global</c> (or no option), and so forth.</p>
+ option <c>global</c> (or no option), and so on.</p>
<p>Part of a match specification list cannot be changed directly.
If a function has a match specification, it can be replaced
with a new one. To change an existing match specification,
use the BIF
- <seealso marker="#trace_info/2">erlang:trace_info/2</seealso>
+ <seealso marker="#trace_info/2"><c>erlang:trace_info/2</c></seealso>
to retrieve the existing match specification.</p>
<p>Returns the number of functions matching
argument <c><anno>MFA</anno></c>. This is zero if none matched.</p>
@@ -9667,7 +10021,7 @@ timestamp() ->
<func>
<name name="trunc" arity="1"/>
- <fsummary>Returns an integer by truncating a number</fsummary>
+ <fsummary>Return an integer by truncating a number.</fsummary>
<desc>
<p>Returns an integer by truncating <c><anno>Number</anno></c>,
for example:</p>
@@ -9680,7 +10034,7 @@ timestamp() ->
<func>
<name name="tuple_size" arity="1"/>
- <fsummary>Returns the size of a tuple.</fsummary>
+ <fsummary>Return the size of a tuple.</fsummary>
<desc>
<p>Returns an integer that is the number of elements in
<c><anno>Tuple</anno></c>, for example:</p>
@@ -9693,11 +10047,11 @@ timestamp() ->
<func>
<name name="tuple_to_list" arity="1"/>
- <fsummary>Converts a tuple to a list.</fsummary>
+ <fsummary>Convert a tuple to a list.</fsummary>
<desc>
<p>Returns a list corresponding to <c><anno>Tuple</anno></c>.
- <c><anno>Tuple</anno></c> can contain any Erlang terms.</p>
- <p>Example:</p>
+ <c><anno>Tuple</anno></c> can contain any Erlang terms.
+ Example:</p>
<pre>
> <input>tuple_to_list({share, {'Ericsson_B', 163}}).</input>
[share,{'Ericsson_B',163}]</pre>
@@ -9705,16 +10059,111 @@ timestamp() ->
</func>
<func>
+ <name name="unique_integer" arity="0"/>
+ <fsummary>Get a unique integer value.</fsummary>
+ <desc>
+ <p>Generates and returns an
+ <seealso marker="doc/efficiency_guide:advanced#unique_integers">
+ integer unique on current runtime system instance</seealso>.
+ The same as calling
+ <seealso marker="#unique_integer/1">
+ <c>erlang:unique_integer([])</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="unique_integer" arity="1"/>
+ <fsummary>Get a unique integer value.</fsummary>
+ <desc>
+ <p>Generates and returns an
+ <seealso marker="doc/efficiency_guide:advanced#unique_integers">
+ integer unique on current runtime system
+ instance</seealso>. The integer is unique in the
+ sense that this BIF, using the same set of
+ modifiers, does not return the same integer more
+ than once on the current runtime system instance.
+ Each integer value can of course be constructed
+ by other means.</p>
+ <p>By default, when <c>[]</c> is passed as
+ <c><anno>ModifierList</anno></c>, both negative and
+ positive integers can be returned. This
+ to use the range of integers that do
+ not need heap memory allocation as much as possible.
+ By default the returned integers are also only
+ guaranteed to be unique, that is, any returned integer
+ can be smaller or larger than previously
+ returned integers.</p>
+ <p><c><anno>Modifier</anno></c>s:</p>
+ <taglist>
+ <tag>positive</tag>
+ <item>
+ <p>Returns only positive integers.</p>
+ <p>Notice that by passing the <c>positive</c> modifier
+ you will get heap allocated integers (bignums) quicker.</p>
+ </item>
+ <tag>monotonic</tag>
+ <item>
+ <p>Returns <seealso
+ marker="time_correction#Strictly_Monotonically_Increasing">
+ strictly monotonically increasing</seealso> integers
+ corresponding to creation time. That is, the integer
+ returned is always larger than previously
+ returned integers on the current runtime system
+ instance.</p>
+ <p>These values can be used to determine order between events
+ on the runtime system instance. That is, if both
+ <c>X = erlang:unique_integer([monotonic])</c> and
+ <c>Y = erlang:unique_integer([monotonic])</c> are
+ executed by different processes (or the same
+ process) on the same runtime system instance and
+ <c>X &lt; Y</c>, we know that <c>X</c> was created
+ before <c>Y</c>.</p>
+ <warning>
+ <p>Strictly monotonically increasing values
+ are inherently quite expensive to generate and scales
+ poorly. This is because the values need to be synchronized
+ between CPU cores. That is, do not pass the <c>monotonic</c>
+ modifier unless you really need strictly monotonically
+ increasing values.</p>
+ </warning>
+ </item>
+ </taglist>
+ <p>All valid <c><anno>Modifier</anno></c>s
+ can be combined. Repeated (valid)
+ <c><anno>Modifier</anno></c>s in the <c>ModifierList</c>
+ are ignored.</p>
+ <note>
+ <p>The set of integers returned by
+ <c>erlang:unique_integer/1</c> using different sets of
+ <c><anno>Modifier</anno></c>s <em>will overlap</em>.
+ For example, by calling <c>unique_integer([monotonic])</c>,
+ and <c>unique_integer([positive, monotonic])</c>
+ repeatedly, you will eventually see some integers that are
+ returned by both calls.</p>
+ </note>
+ <p>Failures:</p>
+ <taglist>
+ <tag><c>badarg</c></tag>
+ <item>if <c><anno>ModifierList</anno></c> is not a
+ proper list.</item>
+ <tag><c>badarg</c></tag>
+ <item>if <c><anno>Modifier</anno></c> is not a
+ valid modifier.</item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
<name name="universaltime" arity="0"/>
- <fsummary>Current date and time according to Universal Time Coordinated (UTC).</fsummary>
+ <fsummary>Current date and time according to Universal Time Coordinated
+ (UTC).</fsummary>
<desc>
<p>Returns the current date and time according to Universal
Time Coordinated (UTC) in the form
<c>{{Year, Month, Day}, {Hour, Minute, Second}}</c> if
supported by the underlying OS.
Otherwise <c>erlang:universaltime()</c> is equivalent to
- <c>erlang:localtime()</c>.</p>
- <p>Example:</p>
+ <c>erlang:localtime()</c>. Example:</p>
<pre>
> <input>erlang:universaltime().</input>
{{1996,11,6},{14,18,43}}</pre>
@@ -9723,15 +10172,15 @@ timestamp() ->
<func>
<name name="universaltime_to_localtime" arity="1"/>
- <fsummary>Converts from Universal Time Coordinated (UTC) to local date and time.</fsummary>
+ <fsummary>Convert from Universal Time Coordinated (UTC) to local date
+ and time.</fsummary>
<desc>
<p>Converts Universal Time Coordinated (UTC) date and time to
local date and time in the form
<c>{{Year, Month, Day}, {Hour, Minute, Second}}</c> if
supported by the underlying OS.
Otherwise no conversion is done, and
- <c><anno>Universaltime</anno></c> is returned.</p>
- <p>Example:</p>
+ <c><anno>Universaltime</anno></c> is returned. Example:</p>
<pre>
> <input>erlang:universaltime_to_localtime({{1996,11,6},{14,18,43}}).</input>
{{1996,11,7},{15,18,43}}</pre>
@@ -9741,102 +10190,8 @@ timestamp() ->
</func>
<func>
- <name name="unique_integer" arity="0"/>
- <fsummary>Get a unique integer value</fsummary>
- <desc>
- <p>Generates and returns an
- <seealso marker="doc/efficiency_guide:advanced#unique_integers">integer
- unique on current runtime system instance</seealso>. The same as calling
- <seealso marker="#unique_integer/1"><c>erlang:unique_integer([])</c></seealso>.</p>
- </desc>
- </func>
- <func>
- <name name="unique_integer" arity="1"/>
- <fsummary>Get a unique integer value</fsummary>
- <desc>
- <p>Generates and returns an
- <seealso marker="doc/efficiency_guide:advanced#unique_integers">integer
- unique on current runtime system
- instance</seealso>. The integer is unique in the
- sense that this BIF, using the same set of
- modifiers, will not return the same integer more
- than once on the current runtime system instance.
- Each integer value can of course be constructed
- by other means.</p>
-
- <p>By default, when <c>[]</c> is passed as
- <c><anno>ModifierList</anno></c>, both negative and
- positive integers can be returned. This in order
- to utilize the range of integers that do
- not need heap memory allocation as much as possible.
- By default the returned integers are also only
- guaranteed to be unique, that is, any returned integer
- can be smaller or larger than previously
- returned integers.</p>
-
- <p>Valid <c><anno>Modifier</anno></c>s:</p>
- <taglist>
-
- <tag>positive</tag>
- <item><p>Return only positive integers.</p>
- <p>Note that by passing the <c>positive</c> modifier
- you will get heap allocated integers (bignums)
- quicker.</p>
- </item>
-
- <tag>monotonic</tag>
- <item><p>Return
- <seealso marker="time_correction#Strictly_Monotonically_Increasing">strictly
- monotonically increasing</seealso> integers
- corresponding to creation time. That is, the integer
- returned will always be larger than previously
- returned integers on the current runtime system
- instance.</p>
- <p>These values can be used to determine order between events
- on the runtime system instance. That is, if both
- <c>X = erlang:unique_integer([monotonic])</c> and
- <c>Y = erlang:unique_integer([monotonic])</c> are
- executed by different processes (or the same
- process) on the same runtime system instance and
- <c>X &lt; Y</c> we know that <c>X</c> was created
- before <c>Y</c>.</p>
- <warning><p>Strictly monotonically increasing values
- are inherently quite expensive to generate and scales
- poorly. This is because the values need to be
- synchronized between cpu cores. That is, do not pass the <c>monotonic</c>
- modifier unless you really need strictly monotonically
- increasing values.</p></warning>
- </item>
-
- </taglist>
-
- <p>All valid <c><anno>Modifier</anno></c>s
- can be combined. Repeated (valid)
- <c><anno>Modifier</anno></c>s in the <c>ModifierList</c>
- are ignored.</p>
-
- <note><p>Note that the set of integers returned by
- <c>unique_integer/1</c> using different sets of
- <c><anno>Modifier</anno></c>s <em>will overlap</em>.
- For example, by calling <c>unique_integer([monotonic])</c>,
- and <c>unique_integer([positive, monotonic])</c>
- repeatedly, you will eventually see some integers being
- returned by both calls.</p></note>
-
- <p>Failures:</p>
- <taglist>
- <tag><c>badarg</c></tag>
- <item>if <c><anno>ModifierList</anno></c> is not a
- proper list.</item>
- <tag><c>badarg</c></tag>
- <item>if <c><anno>Modifier</anno></c> is not a
- valid modifier.</item>
- </taglist>
- </desc>
- </func>
- <func>
<name name="unlink" arity="1"/>
- <fsummary>Removes a link to another process or port.</fsummary>
+ <fsummary>Remove a link to another process or port.</fsummary>
<desc>
<p>Removes the link, if there is one, between the calling
process and the process or port referred to by
@@ -9851,8 +10206,8 @@ timestamp() ->
in the future (unless
the link is setup again). If the caller is trapping exits, an
<c>{'EXIT', <anno>Id</anno>, _}</c> message from the link
- can have been placed in the caller's message queue before
- the call.</p>
+ can have been placed in the caller's message queue before
+ the call.</p>
<p>Notice that the <c>{'EXIT', <anno>Id</anno>, _}</c>
message can be the
result of the link, but can also be the result of <c>Id</c>
@@ -9860,16 +10215,16 @@ timestamp() ->
appropriate to clean up the message queue when trapping exits
after the call to <c>unlink(<anno>Id</anno>)</c>, as follows:</p>
<code type="none">
- unlink(Id),
- receive
- {'EXIT', Id, _} ->
- true
- after 0 ->
- true
- end</code>
+unlink(Id),
+receive
+ {'EXIT', Id, _} ->
+ true
+after 0 ->
+ true
+end</code>
<note>
- <p>Prior to OTP release R11B (ERTS version 5.5) <c>unlink/1</c>
- behaved completely asynchronously, i.e., the link was active
+ <p>Before Erlang/OTP R11B (ERTS 5.5) <c>unlink/1</c>
+ behaved completely asynchronously, that is, the link was active
until the "unlink signal" reached the linked entity. This
had an undesirable effect, as you could never know when
you were guaranteed <em>not</em> to be effected by the link.</p>
@@ -9882,7 +10237,7 @@ timestamp() ->
<func>
<name name="unregister" arity="1"/>
- <fsummary>Removes the registered name for a process (or port).</fsummary>
+ <fsummary>Remove the registered name for a process (or port).</fsummary>
<desc>
<p>Removes the registered name <c><anno>RegName</anno></c>
associated with a
@@ -9898,12 +10253,12 @@ true</pre>
<func>
<name name="whereis" arity="1"/>
- <fsummary>Gets the pid (or port) with a given registered name.</fsummary>
+ <fsummary>Get the pid (or port) with a specified registered name.
+ </fsummary>
<desc>
<p>Returns the process identifier or port identifier with
the registered name <c>RegName</c>. Returns <c>undefined</c>
- if the name is not registered.</p>
- <p>Example:</p>
+ if the name is not registered. Example:</p>
<pre>
> <input>whereis(db).</input>
&lt;0.43.0></pre>
@@ -9912,17 +10267,19 @@ true</pre>
<func>
<name name="yield" arity="0"/>
- <fsummary>Lets other processes get a chance to execute.</fsummary>
+ <fsummary>Let other processes get a chance to execute.</fsummary>
<desc>
<p>Voluntarily lets other processes (if any) get a chance to
- execute. Using <c>erlang:yield()</c> is similar to
+ execute. Using this function is similar to
<c>receive after 1 -> ok end</c>, except that <c>yield()</c>
is faster.</p>
- <warning><p>There is seldom or never any need to use this BIF,
- especially in the SMP emulator, as other processes have a
- chance to run in another scheduler thread anyway.
- Using this BIF without a thorough grasp of how the scheduler
- works can cause performance degradation.</p></warning>
+ <warning>
+ <p>There is seldom or never any need to use this BIF,
+ especially in the SMP emulator, as other processes have a
+ chance to run in another scheduler thread anyway.
+ Using this BIF without a thorough grasp of how the scheduler
+ works can cause performance degradation.</p>
+ </warning>
</desc>
</func>
</funcs>
diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml
index a64927fec2..7355be488b 100644
--- a/erts/doc/src/erlc.xml
+++ b/erts/doc/src/erlc.xml
@@ -28,7 +28,7 @@
<docno>1</docno>
<approved>Bjarne D&auml;cker</approved>
<checked></checked>
- <date>97-03-24</date>
+ <date>1997-03-24</date>
<rev>A</rev>
<file>erlc.xml</file>
</header>
@@ -38,167 +38,162 @@
<p>The <c><![CDATA[erlc]]></c> program provides a common way to run
all compilers in the Erlang system.
Depending on the extension of each input file, <c><![CDATA[erlc]]></c>
- will invoke the appropriate compiler.
- Regardless of which compiler is used, the same flags are used to provide parameters such as include paths and output directory.</p>
- <p>The current working directory, <c>"."</c>, will not be included
- in the code path when running the compiler (to avoid loading
- Beam files from the current working directory that could potentially
- be in conflict with the compiler or Erlang/OTP system used by the
- compiler).</p>
+ invokes the appropriate compiler.
+ Regardless of which compiler is used, the same flags are used to provide
+ parameters, such as include paths and output directory.</p>
+ <p>The current working directory, <c>"."</c>, is not included
+ in the code path when running the compiler. This to avoid loading
+ Beam files from the current working directory that could potentially
+ be in conflict with the compiler or the Erlang/OTP system used by the
+ compiler.</p>
</description>
+
<funcs>
<func>
<name>erlc flags file1.ext file2.ext...</name>
- <fsummary>Compile files</fsummary>
+ <fsummary>Compile files.</fsummary>
<desc>
- <p><c><![CDATA[Erlc]]></c> compiles one or more files.
- The files must include the extension, for example <c><![CDATA[.erl]]></c>
- for Erlang source code, or <c><![CDATA[.yrl]]></c> for Yecc source code.
- <c><![CDATA[Erlc]]></c> uses the extension to invoke the correct compiler.</p>
+ <p>Compiles one or more files. The files must include the extension,
+ for example, <c><![CDATA[.erl]]></c> for Erlang source code, or
+ <c><![CDATA[.yrl]]></c> for Yecc source code.
+ <c><![CDATA[Erlc]]></c> uses the extension to invoke the correct
+ compiler.</p>
</desc>
</func>
</funcs>
<section>
<title>Generally Useful Flags</title>
- <p>The following flags are supported:
- </p>
+ <p>The following flags are supported:</p>
<taglist>
- <tag>-I <em>directory</em></tag>
+ <tag><c>-I &lt;Directory&gt;</c></tag>
<item>
<p>Instructs the compiler to search for include files in
- the specified directory. When encountering an
- <c><![CDATA[-include]]></c> or <c><![CDATA[-include_lib]]></c> directive, the
- compiler searches for header files in the following
+ the <c>Directory</c>. When encountering an
+ <c><![CDATA[-include]]></c> or <c><![CDATA[-include_lib]]></c>
+ directive, the compiler searches for header files in the following
directories:</p>
- <list type="ordered">
+ <list type="bulleted">
<item>
<p><c><![CDATA["."]]></c>, the current working directory of the
- file server;</p>
+ file server</p>
</item>
<item>
- <p>the base name of the compiled file;</p>
+ <p>The base name of the compiled file</p>
</item>
<item>
- <p>the directories specified using the <c><![CDATA[-I]]></c> option.
- The directory specified last is searched first.</p>
+ <p>The directories specified using option <c><![CDATA[-I]]></c>;
+ the directory specified last is searched first</p>
</item>
</list>
</item>
- <tag>-o <em>directory</em></tag>
+ <tag><c>-o &lt;Directory&gt;</c></tag>
<item>
- <p>The directory where the compiler should place the output files.
- If not specified, output files will be placed in the current working
- directory.</p>
+ <p>The directory where the compiler is to place the output files.
+ Defaults to the current working directory.</p>
</item>
- <tag>-D<em>name</em></tag>
+ <tag><c>-D&lt;Name&gt;</c></tag>
<item>
<p>Defines a macro.</p>
</item>
- <tag>-D<em>name</em>=<em>value</em></tag>
+ <tag><c>-D&lt;Name&gt;=&lt;Value&gt;</c></tag>
<item>
- <p>Defines a macro with the given value.
+ <p>Defines a macro with the specified value.
The value can be any Erlang term.
Depending on the platform, the value may need to be
quoted if the shell itself interprets certain characters.
- On Unix, terms which contain tuples and list
- must be quoted. Terms which contain spaces
+ On Unix, terms containing tuples and lists
+ must be quoted. Terms containing spaces
must be quoted on all platforms.</p>
</item>
- <tag>-W<em>error</em></tag>
+ <tag><c>-W&lt;Error&gt;</c></tag>
<item>
<p>Makes all warnings into errors.</p>
</item>
- <tag>-W<em>number</em></tag>
+ <tag><c>-W&lt;Number&gt;</c></tag>
<item>
- <p>Sets warning level to <em>number</em>. Default is <c><![CDATA[1]]></c>.
- Use <c><![CDATA[-W0]]></c> to turn off warnings.</p>
+ <p>Sets warning level to <c>Number</c>. Defaults to
+ <c><![CDATA[1]]></c>. To turn off warnings,
+ use <c><![CDATA[-W0]]></c>.</p>
</item>
- <tag>-W</tag>
+ <tag><c>-W</c></tag>
<item>
<p>Same as <c><![CDATA[-W1]]></c>. Default.</p>
</item>
- <tag>-v</tag>
+ <tag><c>-v</c></tag>
<item>
<p>Enables verbose output.</p>
</item>
- <tag>-b <em>output-type</em></tag>
+ <tag><c>-b &lt;Output_type&gt;</c></tag>
<item>
<p>Specifies the type of output file.
- Generally, <em>output-type</em> is the same as the file extension
- of the output file but without the period.
- This option will be ignored by compilers that have a
+ <c>Output_type</c> is the same as the file extension
+ of the output file, but without the period.
+ This option is ignored by compilers that have
a single output format.</p>
</item>
- <tag>-smp</tag>
+ <tag><c>-smp</c></tag>
<item>
- <p>Compile using the SMP emulator. This is mainly useful
- for compiling native code, which needs to be compiled with the same
- run-time system that it should be run on.</p>
+ <p>Compiles using the SMP emulator. This is mainly useful
+ for compiling native code, which must be compiled with the same
+ runtime system that it is to be run on.</p>
</item>
- <tag>-M</tag>
+ <tag><c>-M</c></tag>
<item>
- <p>Produces a Makefile rule to track headers dependencies. The
- rule is sent to stdout. No object file is produced.
- </p>
+ <p>Produces a Makefile rule to track header dependencies. The
+ rule is sent to <c>stdout</c>. No object file is produced.</p>
</item>
- <tag>-MF <em>Makefile</em></tag>
+ <tag><c>-MF &lt;Makefile&gt;</c></tag>
<item>
- <p>Like the <c><![CDATA[-M]]></c> option above, except that the
- Makefile is written to <em>Makefile</em>. No object
- file is produced.
- </p>
+ <p>As option <c><![CDATA[-M]]></c>, except that the
+ Makefile is written to <c>Makefile</c>. No object
+ file is produced.</p>
</item>
- <tag>-MD</tag>
+ <tag><c>-MD</c></tag>
<item>
- <p>Same as <c><![CDATA[-M -MF <File>.Pbeam]]></c>.
- </p>
+ <p>Same as <c><![CDATA[-M -MF <File>.Pbeam]]></c>.</p>
</item>
- <tag>-MT <em>Target</em></tag>
+ <tag><c>-MT &lt;Target&gt;</c></tag>
<item>
- <p>In conjunction with <c><![CDATA[-M]]></c> or
- <c><![CDATA[-MF]]></c>, change the name of the rule emitted
- to <em>Target</em>.
- </p>
+ <p>In conjunction with option <c><![CDATA[-M]]></c> or
+ <c><![CDATA[-MF]]></c>, changes the name of the rule emitted
+ to <c>Target</c>.</p>
</item>
- <tag>-MQ <em>Target</em></tag>
+ <tag><c>-MQ &lt;Target&gt;</c></tag>
<item>
- <p>Like the <c><![CDATA[-MT]]></c> option above, except that
- characters special to make(1) are quoted.
- </p>
+ <p>As option <c><![CDATA[-MT]]></c>, except that characters special to
+ <c>make/1</c> are quoted.</p>
</item>
- <tag>-MP</tag>
+ <tag><c>-MP</c></tag>
<item>
- <p>In conjunction with <c><![CDATA[-M]]></c> or
- <c><![CDATA[-MF]]></c>, add a phony target for each dependency.
- </p>
+ <p>In conjunction with option <c><![CDATA[-M]]></c> or
+ <c><![CDATA[-MF]]></c>, adds a phony target for each dependency.</p>
</item>
- <tag>-MG</tag>
+ <tag><c>-MG</c></tag>
<item>
- <p>In conjunction with <c><![CDATA[-M]]></c> or
- <c><![CDATA[-MF]]></c>, consider missing headers as generated
- files and add them to the dependencies.
- </p>
+ <p>In conjunction with option <c><![CDATA[-M]]></c> or
+ <c><![CDATA[-MF]]></c>, considers missing headers as generated
+ files and adds them to the dependencies.</p>
</item>
- <tag>--</tag>
+ <tag><c>--</c></tag>
<item>
<p>Signals that no more options will follow.
- The rest of the arguments will be treated as file names,
+ The rest of the arguments is treated as filenames,
even if they start with hyphens.</p>
</item>
- <tag>+<em>term</em></tag>
+ <tag><c>+&lt;Term&gt;</c></tag>
<item>
- <p>A flag starting with a plus ('<em>+</em>') rather than a hyphen
- will be converted to an Erlang term and passed unchanged to
+ <p>A flag starting with a plus (<c>+</c>) rather than a hyphen
+ is converted to an Erlang term and passed unchanged to
the compiler.
- For instance, the <c><![CDATA[export_all]]></c> option for the Erlang
+ For example, option <c><![CDATA[export_all]]></c> for the Erlang
compiler can be specified as follows:</p>
<pre>
erlc +export_all file.erl</pre>
<p>Depending on the platform, the value may need to be
quoted if the shell itself interprets certain characters.
- On Unix, terms which contain tuples and list
- must be quoted. Terms which contain spaces
+ On Unix, terms containing tuples and lists
+ must be quoted. Terms containing spaces
must be quoted on all platforms.</p>
</item>
</taglist>
@@ -206,19 +201,19 @@ erlc +export_all file.erl</pre>
<section>
<title>Special Flags</title>
- <p>The flags in this section are useful in special situations
- such as re-building the OTP system.</p>
+ <p>The following flags are useful in special situations,
+ such as rebuilding the OTP system:</p>
<taglist>
- <tag>-pa <em>directory</em></tag>
+ <tag><c>-pa &lt;Directory&gt;</c></tag>
<item>
- <p>Appends <em>directory</em> to the front of the code path in
+ <p>Appends <c>Directory</c> to the front of the code path in
the invoked Erlang emulator.
This can be used to invoke another
compiler than the default one.</p>
</item>
- <tag>-pz <em>directory</em></tag>
+ <tag><c>-pz &lt;Directory&gt;</c></tag>
<item>
- <p>Appends <em>directory</em> to the code path in
+ <p>Appends <c>Directory</c> to the code path in
the invoked Erlang emulator.</p>
</item>
</taglist>
@@ -226,63 +221,70 @@ erlc +export_all file.erl</pre>
<section>
<title>Supported Compilers</title>
+ <p>The following compilers are supported:</p>
<taglist>
- <tag>.erl</tag>
+ <tag><c>.erl</c></tag>
<item>
<p>Erlang source code. It generates a <c><![CDATA[.beam]]></c> file.</p>
- <p>The options -P, -E, and -S are equivalent to +'P',
- +'E', and +'S', except that it is not necessary to include the single quotes to protect them
- from the shell.</p>
- <p>Supported options: -I, -o, -D, -v, -W, -b.</p>
+ <p>Options <c>-P</c>, <c>-E</c>, and <c>-S</c> are equivalent to
+ <c>+'P'</c>, <c>+'E'</c>, and <c>+'S'</c>, except that it is not
+ necessary to include the single quotes to protect them from the
+ shell.</p>
+ <p>Supported options: <c>-I</c>, <c>-o</c>, <c>-D</c>, <c>-v</c>,
+ <c>-W</c>, <c>-b</c>.</p>
</item>
- <tag>.S</tag>
+ <tag><c>.S</c></tag>
<item>
- <p>Erlang assembler source code. It generates a <c><![CDATA[.beam]]></c> file.</p>
- <p>Supported options: same as for .erl.</p>
+ <p>Erlang assembler source code. It generates a <c><![CDATA[.beam]]></c>
+ file.</p>
+ <p>Supported options: same as for <c>.erl</c>.</p>
</item>
- <tag>.core</tag>
+ <tag><c>.core</c></tag>
<item>
- <p>Erlang core source code. It generates a <c><![CDATA[.beam]]></c> file.</p>
- <p>Supported options: same as for .erl.</p>
+ <p>Erlang core source code. It generates a <c><![CDATA[.beam]]></c>
+ file.</p>
+ <p>Supported options: same as for <c>.erl</c>.</p>
</item>
- <tag>.yrl</tag>
+ <tag><c>.yrl</c></tag>
<item>
<p>Yecc source code. It generates an <c><![CDATA[.erl]]></c> file.</p>
- <p>Use the -I option with the name of a file to use that file
- as a customized prologue file (the <c><![CDATA[includefile]]></c> option).</p>
- <p>Supported options: -o, -v, -I, -W (see above).</p>
+ <p>Use option <c>-I</c> with the name of a file to use that file
+ as a customized prologue file (option
+ <c><![CDATA[includefile]]></c>).</p>
+ <p>Supported options: <c>-o</c>, <c>-v</c>, <c>-I</c>, <c>-W</c>.</p>
</item>
- <tag>.mib</tag>
+ <tag><c>.mib</c></tag>
<item>
<p>MIB for SNMP. It generates a <c><![CDATA[.bin]]></c> file.</p>
- <p>Supported options: -I, -o, -W.</p>
+ <p>Supported options: <c>-I</c>, <c>-o</c>, <c>-W</c>.</p>
</item>
- <tag>.bin</tag>
+ <tag><c>.bin</c></tag>
<item>
- <p>A compiled MIB for SNMP. It generates a <c><![CDATA[.hrl]]></c> file.</p>
- <p>Supported options: -o, -v.</p>
+ <p>A compiled MIB for SNMP. It generates a <c><![CDATA[.hrl]]></c>
+ file.</p>
+ <p>Supported options: <c>-o</c>, <c>-v</c>.</p>
</item>
- <tag>.rel</tag>
+ <tag><c>.rel</c></tag>
<item>
<p>Script file. It generates a boot file.</p>
- <p>Use the -I to name directories to be searched for application
- files (equivalent to the <c><![CDATA[path]]></c> in the option list for
- <c><![CDATA[systools:make_script/2]]></c>).</p>
- <p>Supported options: -o.</p>
+ <p>Use option <c>-I</c> to name directories to be searched for
+ application files (equivalent to the <c><![CDATA[path]]></c> in the
+ option list for <c><![CDATA[systools:make_script/2]]></c>).</p>
+ <p>Supported option: <c>-o</c>.</p>
</item>
- <tag>.asn1</tag>
+ <tag><c>.asn1</c></tag>
<item>
- <p>ASN1 file.</p>
- <p>Creates an <c><![CDATA[.erl]]></c>, <c><![CDATA[.hrl]]></c>, and <c><![CDATA[.asn1db]]></c> file from
- an <c><![CDATA[.asn1]]></c> file. Also compiles the <c><![CDATA[.erl]]></c> using the Erlang
- compiler unless the <c><![CDATA[+noobj]]></c> options is given.</p>
- <p>Supported options: -I, -o, -b, -W.</p>
+ <p>ASN1 file. It creates an <c><![CDATA[.erl]]></c>,
+ <c><![CDATA[.hrl]]></c>, and <c><![CDATA[.asn1db]]></c> file from
+ an <c><![CDATA[.asn1]]></c> file. Also compiles the
+ <c><![CDATA[.erl]]></c> using the Erlang compiler unless option
+ <c><![CDATA[+noobj]]></c> is specified.</p>
+ <p>Supported options: <c>-I</c>, <c>-o</c>, <c>-b</c>, <c>-W</c>.</p>
</item>
- <tag>.idl</tag>
+ <tag><c>.idl</c></tag>
<item>
- <p>IC file.</p>
- <p>Runs the IDL compiler.</p>
- <p>Supported options: -I, -o.</p>
+ <p>IC file. It runs the IDL compiler.</p>
+ <p>Supported options: <c>-I</c>, <c>-o</c>.</p>
</item>
</taglist>
</section>
@@ -290,20 +292,20 @@ erlc +export_all file.erl</pre>
<section>
<title>Environment Variables</title>
<taglist>
- <tag>ERLC_EMULATOR</tag>
- <item>The command for starting the emulator.
- Default is <em>erl</em> in the same directory as the <em>erlc</em> program
- itself, or if it doesn't exist, <em>erl</em> in any of the directories
- given in the <em>PATH</em> environment variable.</item>
+ <tag><c>ERLC_EMULATOR</c></tag>
+ <item>The command for starting the emulator. Defaults to <c>erl</c>
+ in the same directory as the <c>erlc</c> program itself,
+ or, if it does not exist, <c>erl</c> in any of the directories
+ specified in environment variable <c>PATH</c>.</item>
</taglist>
</section>
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="erl">erl(1)</seealso>,
- <seealso marker="compiler:compile">compile(3)</seealso>,
- <seealso marker="parsetools:yecc">yecc(3)</seealso>,
- <seealso marker="snmp:snmp">snmp(3)</seealso></p>
+ <title>See Also</title>
+ <p><seealso marker="erl"><c>erl(1)</c></seealso>,
+ <seealso marker="compiler:compile"><c>compile(3)</c></seealso>,
+ <seealso marker="parsetools:yecc"><c>yecc(3)</c></seealso>,
+ <seealso marker="snmp:snmp"><c>snmp(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/erlsrv.xml b/erts/doc/src/erlsrv.xml
index fb00444aa4..6c08b25220 100644
--- a/erts/doc/src/erlsrv.xml
+++ b/erts/doc/src/erlsrv.xml
@@ -28,343 +28,447 @@
<docno></docno>
<approved></approved>
<checked></checked>
- <date>98-04-29</date>
+ <date>1998-04-29</date>
<rev></rev>
<file>erlsrv.xml</file>
</header>
<com>erlsrv</com>
- <comsummary>Run the Erlang emulator as a service on Windows NT&reg;</comsummary>
+ <comsummary>Run the Erlang emulator as a service on Windows</comsummary>
<description>
- <p>This utility is specific to Windows NT/2000/XP&reg; (and subsequent versions of Windows) It allows Erlang
+ <p>This utility is specific to Windows NT/2000/XP (and later
+ versions of Windows). It allows Erlang
emulators to run as services on the Windows system, allowing embedded
- systems to start without any user needing to log in. The
+ systems to start without any user needing to log on. The
emulator started in this way can be manipulated through the
- Windows&reg; services applet in a manner similar to other
- services.</p>
- <p>Note that erlsrv is not a general service utility for Windows, but designed for embedded Erlang systems.</p>
- <p>As well as being the actual service, erlsrv also provides a
- command line interface for registering, changing, starting and
- stopping services.</p>
- <p>To manipulate services, the logged in user should have
- Administrator privileges on the machine. The Erlang machine
+ Windows services applet in a manner similar to other services.</p>
+
+ <p>Notice that <c>erlsrv</c> is not a general service utility for Windows,
+ but designed for embedded Erlang systems.</p>
+
+ <p><c>erlsrv</c> also provides a command-line interface for registering,
+ changing, starting, and stopping services.</p>
+
+ <p>To manipulate services, the logged on user is to have
+ administrator privileges on the machine. The Erlang machine
itself is (default) run as the local administrator. This can be
- changed with the Services applet in Windows &reg;.</p>
+ changed with the Services applet in Windows.</p>
+
<p>The processes created by the service can, as opposed to normal
- services, be "killed" with the task manager. Killing a emulator
- that is started by a service will trigger the "OnFail" action
- specified for that service, which may be a reboot.</p>
- <p>The following parameters may be specified for each Erlang
- service:</p>
- <list type="bulleted">
+ services, be "killed" with the task manager. Killing an emulator
+ that is started by a service triggers the "OnFail" action
+ specified for that service, which can be a reboot.</p>
+
+ <p>The following parameters can be specified for each Erlang service:</p>
+
+ <taglist>
+ <tag><c><![CDATA[StopAction]]></c></tag>
<item>
- <p><c><![CDATA[StopAction]]></c>: This tells <c><![CDATA[erlsrv]]></c> how to stop
+ <p>Tells <c><![CDATA[erlsrv]]></c> how to stop
the Erlang emulator. Default is to kill it (Win32
TerminateProcess), but this action can specify any Erlang
shell command that will be executed in the emulator to make
it stop. The emulator is expected to stop within 30 seconds
after the command is issued in the shell. If the emulator is
- not stopped, it will report a running state to the service
+ not stopped, it reports a running state to the service
manager.</p>
</item>
+ <tag><c><![CDATA[OnFail]]></c></tag>
<item>
- <p><c><![CDATA[OnFail]]></c>: This can be either of <c><![CDATA[reboot]]></c>,
- <c><![CDATA[restart]]></c>, <c><![CDATA[restart_always]]></c> or <c><![CDATA[ignore]]></c> (the
- default). In case of <c><![CDATA[reboot]]></c>, the NT system is
- rebooted whenever the emulator stops (a more simple form of
- watchdog), this could be useful for less critical systems,
- otherwise use the heart functionality to accomplish
- this. The restart value makes the Erlang emulator be
- restarted (with whatever parameters are registered for the
- service at the occasion) when it stops. If the emulator
- stops again within 10 seconds, it is not restarted to avoid
- an infinite loop which could completely hang the NT
- system. <c><![CDATA[restart_always]]></c> is similar to restart, but
- does not try to detect cyclic restarts, it is expected that
- some other mechanism is present to avoid the problem. The
- default (ignore) just reports the service as stopped to the
- service manager whenever it fails, it has to be manually
- restarted.</p>
- <p>On a system where release handling is
- used, this should always be set to <c><![CDATA[ignore]]></c>. Use
- <c><![CDATA[heart]]></c> to restart the service on failure instead.</p>
+ <p>Can be one of the following:</p>
+ <taglist>
+ <tag><c><![CDATA[reboot]]></c></tag>
+ <item>
+ <p>The Windows system is rebooted whenever the emulator stops
+ (a more simple form of watchdog). This can be useful for
+ less critical systems, otherwise use the heart functionality
+ to accomplish this.</p>
+ </item>
+ <tag><c><![CDATA[restart]]></c></tag>
+ <item>
+ <p>Makes the Erlang emulator be
+ restarted (with whatever parameters are registered for the
+ service at the occasion) when it stops. If the emulator
+ stops again within 10 seconds, it is not restarted to avoid
+ an infinite loop, which could hang the Windows system.</p>
+ </item>
+ <tag><c><![CDATA[restart_always]]></c></tag>
+ <item>
+ <p>Similar to <c><![CDATA[restart]]></c>, but does
+ not try to detect cyclic restarts; it is expected that
+ some other mechanism is present to avoid the problem.</p>
+ </item>
+ <tag><c><![CDATA[ignore]]></c> (the default)</tag>
+ <item>
+ <p>Reports the service as stopped to the service manager
+ whenever it fails; it must be manually restarted.</p>
+ </item>
+ </taglist>
+ <p>On a system where release handling is used,
+ this is always to be set to <c><![CDATA[ignore]]></c>. Use
+ <c><![CDATA[heart]]></c> to restart the service on failure
+ instead.</p>
</item>
+ <tag><c><![CDATA[Machine]]></c></tag>
<item>
- <p><c><![CDATA[Machine]]></c>: The location of the Erlang
- emulator. The default is the <c><![CDATA[erl.exe]]></c> located in the
- same directory as erlsrv.exe. Do not specify <c><![CDATA[werl.exe]]></c>
- as this emulator, it will not work.</p>
- <p>If the system
- uses release handling, this should be set to a program
- similar to <c><![CDATA[start_erl.exe]]></c>.</p>
+ <p>The location of the Erlang emulator.
+ The default is the <c><![CDATA[erl.exe]]></c> located in the same
+ directory as <c>erlsrv.exe</c>. Do not specify
+ <c><![CDATA[werl.exe]]></c> as this emulator, it will not work.</p>
+ <p>If the system uses release handling, this is to be set to a
+ program similar to <c><![CDATA[start_erl.exe]]></c>.</p>
</item>
+ <tag><c><![CDATA[Env]]></c></tag>
<item>
- <p><c><![CDATA[Env]]></c>: Specifies an <em>additional</em> environment
+ <p>Specifies an <em>extra</em> environment
for the emulator. The environment variables specified
- here are added to the system wide environment block that is
+ here are added to the system-wide environment block that is
normally present when a service starts up. Variables present
- in both the system wide environment and in the service
+ in both the system-wide environment and in the service
environment specification will be set to the value specified
in the service.</p>
</item>
+ <tag><c><![CDATA[WorkDir]]></c></tag>
<item>
- <p><c><![CDATA[WorkDir]]></c>: The working directory for the Erlang
- emulator, has to be on a local drive (there are no network
- drives mounted when a service starts). Default working
- directory for services is <c><![CDATA[%SystemDrive%%SystemPath%]]></c>.
+ <p>The working directory for the Erlang emulator.
+ Must be on a local drive (no network drives are mounted when a
+ service starts). Default working directory for services is
+ <c><![CDATA[%SystemDrive%%SystemPath%]]></c>.
Debug log files will be placed in this directory.</p>
</item>
+ <tag><c><![CDATA[Priority]]></c></tag>
<item>
- <p><c><![CDATA[Priority]]></c>: The process priority of the emulator,
- this can be one of <c><![CDATA[realtime]]></c>, <c><![CDATA[high]]></c>, <c><![CDATA[low]]></c>
- or <c><![CDATA[default]]></c> (the default). Real-time priority is not
- recommended, the machine will possibly be inaccessible to
- interactive users. High priority could be used if two Erlang
- nodes should reside on one dedicated system and one should
- have precedence over the other. Low process priority may be
- used if interactive performance should not be affected by
- the emulator process.</p>
+ <p>The process priority of the emulator. Can be one of the
+ following:</p>
+ <taglist>
+ <tag><c><![CDATA[realtime]]></c></tag>
+ <item>
+ <p>Not recommended, as the machine will possibly be
+ inaccessible to interactive users.</p>
+ </item>
+ <tag><c><![CDATA[high]]></c></tag>
+ <item>
+ <p>Can be used if two Erlang nodes are to reside on one dedicated
+ system and one is to have precedence over the other.</p>
+ </item>
+ <tag><c><![CDATA[low]]></c></tag>
+ <item>
+ <p>Can be used if interactive performance is not to be affected
+ by the emulator process.</p>
+ </item>
+ <tag><c><![CDATA[default]]></c> (the default></tag>
+ <item>
+ </item>
+ </taglist>
</item>
+ <tag><c><![CDATA[SName or Name]]></c></tag>
<item>
- <p><c><![CDATA[SName or Name]]></c>: Specifies the short or long
- node-name of the Erlang emulator. The Erlang services are
- always distributed, default is to use the service name as
- (short) node-name.</p>
+ <p>Specifies the short or long
+ node name of the Erlang emulator. The Erlang services are
+ always distributed. Default is to use the service name as
+ (short) nodename.</p>
</item>
+ <tag><c><![CDATA[DebugType]]></c></tag>
<item>
- <p><c><![CDATA[DebugType]]></c>: Can be one of <c><![CDATA[none]]></c> (default),
- <c><![CDATA[new]]></c>, <c><![CDATA[reuse]]></c> or <c><![CDATA[console]]></c>.
- Specifies that output from the Erlang shell should be
+ <p>Specifies that output from the Erlang shell is to be
sent to a "debug log". The log file is named
&lt;servicename&gt;<c><![CDATA[.debug]]></c> or
- &lt;servicename&gt;<c><![CDATA[.debug.]]></c>&lt;N&gt;, where &lt;N&gt; is
- an integer between 1 and 99. The log-file is placed in the
- working directory of the service (as specified in WorkDir). The
- <c><![CDATA[reuse]]></c> option always reuses the same log file
- (&lt;servicename&gt;<c><![CDATA[.debug]]></c>) and the <c><![CDATA[new]]></c> option
- uses a separate log file for every invocation of the service
- (&lt;servicename&gt;<c><![CDATA[.debug.]]></c>&lt;N&gt;). The <c><![CDATA[console]]></c>
- option opens an interactive Windows&reg; console window for
- the Erlang shell of the service. The <c><![CDATA[console]]></c> option
- automatically
- disables the <c><![CDATA[StopAction]]></c> and a service started with an
- interactive console window will not survive logouts,
- <c><![CDATA[OnFail]]></c> actions do not work with debug-consoles either.
- If no <c><![CDATA[DebugType]]></c> is specified (<c><![CDATA[none]]></c>), the
- output of the Erlang shell is discarded.</p>
- <p>The <c><![CDATA[console]]></c><c><![CDATA[DebugType]]></c> is <em>not in any way</em>
- intended for production. It is <em>only</em> a convenient way to
- debug Erlang services during development. The <c><![CDATA[new]]></c> and
- <c><![CDATA[reuse]]></c> options might seem convenient to have in a
- production system, but one has to take into account that the
- logs will grow indefinitely during the systems lifetime and
- there is no way, short of restarting the service, to
- truncate those logs. In short, the <c><![CDATA[DebugType]]></c> is
- intended for debugging only. Logs during production are
- better produced with the standard Erlang logging
- facilities.</p>
+ &lt;servicename&gt;<c><![CDATA[.debug.]]></c>&lt;N&gt;,
+ where &lt;N&gt; is an integer from 1 through 99.
+ The log file is placed in the working directory of the
+ service (as specified in <c>WorkDir</c>).</p>
+ <p>Can be one of the following:</p>
+ <taglist>
+ <tag><c><![CDATA[new]]></c></tag>
+ <item>
+ <p>Uses a separate log file for every invocation of the service
+ (&lt;servicename&gt;<c><![CDATA[.debug.]]></c>&lt;N&gt;).</p>
+ </item>
+ <tag><c><![CDATA[reuse]]></c></tag>
+ <item>
+ <p>Reuses the same log file
+ (&lt;servicename&gt;<c><![CDATA[.debug]]></c>).</p>
+ </item>
+ <tag><c><![CDATA[console]]></c></tag>
+ <item>
+ <p>Opens an interactive Windows console window for the Erlang
+ shell of the service. Automatically disables the
+ <c><![CDATA[StopAction]]></c>. A service started with an
+ interactive console window does not survive logouts.
+ <c><![CDATA[OnFail]]></c> actions do not work with
+ debug consoles either.</p>
+ </item>
+ <tag><c><![CDATA[none]]></c> (the default)</tag>
+ <item>
+ <p>The output of the Erlang shell is discarded.</p>
+ </item>
+ </taglist>
+ <note>
+ <p>The <c><![CDATA[console]]></c> option is <em>not</em> intended
+ for production. It is <em>only</em> a convenient way to debug
+ Erlang services during development.</p>
+ <p>The <c><![CDATA[new]]></c> and <c><![CDATA[reuse]]></c> options
+ might seem convenient in a production system, but consider that
+ the logs grow indefinitely during the system lifetime and cannot
+ be truncated, except if the service is restarted.</p>
+ <p>In short, the <c><![CDATA[DebugType]]></c> is
+ intended for debugging only. Logs during production are
+ better produced with the standard Erlang logging facilities.</p>
+ </note>
</item>
+ <tag><c><![CDATA[Args]]></c></tag>
<item>
- <p><c><![CDATA[Args]]></c>: Additional arguments passed to the
- emulator startup program <c><![CDATA[erl.exe]]></c> (or
- <c><![CDATA[start_erl.exe]]></c>). Arguments that cannot be specified
- here are <c><![CDATA[-noinput]]></c> (StopActions would not work),
- <c><![CDATA[-name]]></c> and <c><![CDATA[-sname]]></c> (they are specified in any
- way. The most common use is for specifying cookies and flags
- to be passed to init:boot() (<c><![CDATA[-s]]></c>).</p>
+ <p>Passes extra arguments to the emulator startup program
+ <c><![CDATA[erl.exe]]></c> (or <c><![CDATA[start_erl.exe]]></c>).
+ Arguments that cannot be specified here are
+ <c><![CDATA[-noinput]]></c> (<c>StopActions</c> would not work),
+ <c><![CDATA[-name]]></c>, and <c><![CDATA[-sname]]></c> (they are
+ specified in any way). The most common use is for specifying cookies
+ and flags to be passed to <c>init:boot()</c>
+ (<c><![CDATA[-s]]></c>).</p>
</item>
+ <tag><c><![CDATA[InternalServiceName]]></c></tag>
<item>
- <p><c><![CDATA[InternalServiceName]]></c>: Specifies the Windows&reg; internal service name (not the display name, which is the one <c>erlsrv</c> uses to identify the service).</p>
- <p>This internal name can not be changed, it is fixed even if the service is renamed. <c>Erlsrv</c> generates a unique internal name when a service is created, it is recommended to keep to the defaut if release-handling is to be used for the application.</p>
- <p>The internal service name can be seen in the Windows&reg; service manager if viewing <c>Properties</c> for an erlang service.</p>
+ <p>Specifies the Windows-internal service name (not the display name,
+ which is the one <c>erlsrv</c> uses to identify the service).</p>
+ <p>This internal name cannot be changed, it is fixed even if the
+ service is renamed. <c>erlsrv</c> generates a unique internal name
+ when a service is created. It is recommended to keep to the default
+ if release handling is to be used for the application.</p>
+ <p>The internal service name can be seen in the Windows service
+ manager if viewing <c>Properties</c> for an Erlang service.</p>
</item>
+ <tag><c><![CDATA[Comment]]></c></tag>
<item>
- <p><c><![CDATA[Comment]]></c>: A textual comment describing the service. Not mandatory, but shows up as the service description in the Windows&reg; service manager.</p>
+ <p>A textual comment describing the service. Not mandatory, but shows
+ up as the service description in the Windows service manager.</p>
</item>
- </list>
- <p> <marker id="001"></marker>
- The naming of the service in a system that
- uses release handling has to follow the convention
+ </taglist>
+
+ <p><marker id="001"></marker>
+ The naming of the service in a system that
+ uses release handling must follow the convention
<em>NodeName</em>_<em>Release</em>, where <em>NodeName</em> is
- the first part of the Erlang nodename (up to, but not including
+ the first part of the Erlang node name (up to, but not including
the "@") and <em>Release</em> is the current release of the
application.</p>
</description>
+
<funcs>
<func>
<name>erlsrv {set | add} &lt;service-name> [&lt;service options>]</name>
- <fsummary>Add or modify an Erlang service</fsummary>
+ <fsummary>Add or modify an Erlang service.</fsummary>
<desc>
- <p>The set and add commands adds or modifies a Erlang service
- respectively. The simplest form of an add command would be
- completely without options in which case all default values
+ <p>The <c>set</c> and <c>add</c> commands modifies or adds an Erlang
+ service, respectively. The simplest form of an <c>add</c> command is
+ without any options in which case all default values
(described above) apply. The service name is mandatory.</p>
- <p>Every option can be given without parameters, in which case
- the default value is applied. Values to the options are
- supplied <em>only</em> when the default should not be used
- (i.e. <c><![CDATA[erlsrv set myservice -prio -arg]]></c> sets the
- default priority and removes all arguments).</p>
- <p>The following service options are currently available:</p>
+ <p>Every option can be specified without parameters, the
+ default value is then applied. Values to the options are
+ supplied <em>only</em> when the default is not to be used.
+ For example, <c><![CDATA[erlsrv set myservice -prio -arg]]></c>
+ sets the default priority and removes all arguments.</p>
+ <p>Service options:</p>
<taglist>
- <tag>-st[opaction] [&lt;erlang shell command&gt;]</tag>
- <item>Defines the StopAction, the command given to the Erlang
- shell when the service is stopped. Default is none.</item>
- <tag>-on[fail] [{reboot | restart | restart_always}]</tag>
- <item>Specifies the action to take when the Erlang emulator
- stops unexpectedly. Default is to ignore.</item>
- <tag>-m[achine] [&lt;erl-command&gt;]</tag>
- <item>The complete path to the Erlang emulator, never use the
- werl program for this. Default is the <c><![CDATA[erl.exe]]></c> in the
- same directory as <c><![CDATA[erlsrv.exe]]></c>. When release handling
- is used, this should be set to a program similar to
- <c><![CDATA[start_erl.exe]]></c>.</item>
- <tag>-e[nv] [&lt;variable&gt;[=&lt;value&gt;]] ...</tag>
- <item>Edits the environment block for the service. Every
- environment variable specified will add to the system
- environment block. If a variable specified here has the same
- name as a system wide environment variable, the specified
- value overrides the system wide. Environment variables are
- added to this list by specifying
- &lt;variable&gt;=&lt;value&gt; and deleted from the list by
- specifying &lt;variable&gt; alone. The environment block is
- automatically sorted. Any number of <c><![CDATA[-env]]></c> options can
- be specified in one command. Default is to use the system
- environment block unmodified (except for two additions, see
- <seealso marker="#002">below</seealso>).</item>
- <tag>-w[orkdir] [&lt;directory&gt;]</tag>
- <item>The initial working directory of the Erlang
- emulator. Default is the system directory.</item>
- <tag>-p[riority] [{low|high|realtime}]</tag>
- <item>The priority of the Erlang emulator. The default is the
- Windows&reg; default priority.</item>
- <tag>{-sn[ame] | -n[ame]} [&lt;node-name&gt;]</tag>
- <item>The node-name of the Erlang machine, distribution is
- mandatory. Default is <c><![CDATA[-sname <service name>]]></c>.
+ <tag><c>-st[opaction] [&lt;erlang shell command&gt;]</c></tag>
+ <item>
+ <p>Defines the <c><![CDATA[StopAction]]></c>, the command given
+ to the Erlang shell when the service is stopped.
+ Default is none.</p>
+ </item>
+ <tag><c>-on[fail] [{reboot | restart | restart_always}]</c></tag>
+ <item>
+ <p>The action to take when the Erlang emulator
+ stops unexpectedly. Default is to ignore.</p>
+ </item>
+ <tag><c>-m[achine] [&lt;erl-command&gt;]</c></tag>
+ <item>
+ <p>The complete path to the Erlang emulator. Never use the
+ <c>werl</c> program for this. Defaults to the
+ <c><![CDATA[erl.exe]]></c> in the same directory as
+ <c><![CDATA[erlsrv.exe]]></c>. When release handling
+ is used, this is to be set to a program similar to
+ <c><![CDATA[start_erl.exe]]></c>.</p>
+ </item>
+ <tag><c>-e[nv] [&lt;variable&gt;[=&lt;value&gt;]] ...</c></tag>
+ <item>
+ <p>Edits the environment block for the service. Every
+ environment variable specified is added to the system
+ environment block. If a variable specified here has the same
+ name as a system-wide environment variable, the specified
+ value overrides the system-wide. Environment variables are
+ added to this list by specifying
+ &lt;variable&gt;=&lt;value&gt; and deleted from the list by
+ specifying &lt;variable&gt; alone. The environment block is
+ automatically sorted. Any number of <c><![CDATA[-env]]></c>
+ options can be specified in one command. Default is to use the
+ system environment block unmodified (except for two additions,
+ see section <seealso marker="#002">Environment</seealso>
+ below).</p>
+ </item>
+ <tag><c>-w[orkdir] [&lt;directory&gt;]</c></tag>
+ <item>
+ <p>The initial working directory of the Erlang
+ emulator. Defaults to the system directory.</p>
+ </item>
+ <tag><c>-p[riority] [{low|high|realtime}]</c></tag>
+ <item>
+ <p>The priority of the Erlang emulator. Default to the
+ Windows default priority.</p>
+ </item>
+ <tag><c>{-sn[ame] | -n[ame]} [&lt;node-name&gt;]</c></tag>
+ <item>
+ <p>The node name of the Erlang machine. Distribution is mandatory.
+ Defaults to <c><![CDATA[-sname <service name>]]></c>.</p>
+ </item>
+ <tag><c>-d[ebugtype] [{new|reuse|console}]</c></tag>
+ <item>
+ <p>Specifies where shell output is to be sent.
+ Default is that shell output is discarded.
+ To be used only for debugging.</p>
+ </item>
+ <tag><c>-ar[gs] [&lt;limited erl arguments&gt;]</c></tag>
+ <item>
+ <p>Extra arguments to the Erlang emulator. Avoid
+ <c><![CDATA[-noinput]]></c>, <c><![CDATA[-noshell]]></c>, and
+ <c><![CDATA[-sname]]></c>/<c><![CDATA[-name]]></c>. Default is
+ no extra arguments. Remember that the services cookie file is not
+ necessarily the same as the interactive users. The service
+ runs as the local administrator. Specify all arguments
+ together in one string, use double quotes (") to specify an
+ argument string containing spaces, and use quoted quotes (\")
+ to specify a quote within the argument string if necessary.</p>
+ </item>
+ <tag><c>-i[nternalservicename] [&lt;internal name&gt;]</c></tag>
+ <item>
+ <p><em>Only</em> allowed for <c>add</c>. Specifies a
+ Windows-internal service name for the service, which by
+ default is set to something unique (prefixed with the
+ original service name) by <c>erlsrv</c> when adding a new
+ service. Specifying this is a purely cosmethic action and is
+ <em>not</em> recommended if release handling is to be
+ performed. The internal service name cannot be changed once
+ the service is created. The internal name is <em>not</em> to
+ be confused with the ordinary service name, which is the name
+ used to identify a service to <c>erlsrv</c>.</p>
+ </item>
+ <tag><c>-c[omment] [&lt;short description&gt;]</c></tag>
+ <item>
+ <p>Specifies a textual comment describing the
+ service. This comment shows up as the service description
+ in the Windows service manager.</p>
</item>
- <tag>-d[ebugtype] [{new|reuse|console}]</tag>
- <item>Specifies where shell output should be sent,
- default is that shell output is discarded.
- To be used only for debugging.</item>
- <tag>-ar[gs] [&lt;limited erl arguments&gt;]</tag>
- <item>Additional arguments to the Erlang emulator, avoid
- <c><![CDATA[-noinput]]></c>, <c><![CDATA[-noshell]]></c> and
- <c><![CDATA[-sname]]></c>/<c><![CDATA[-name]]></c>. Default is no additional
- arguments. Remember that the services cookie file is not
- necessarily the same as the interactive users. The service
- runs as the local administrator. All arguments should be given
- together in one string, use double quotes (") to give an
- argument string containing spaces and use quoted quotes (\")
- to give an quote within the argument string if
- necessary.</item>
- <tag>-i[nternalservicename] [&lt;internal name&gt;]</tag>
- <item><em>Only</em> allowed for <c>add</c>. Specifies a
- Windows&reg; internal service name for the service, which by
- default is set to something unique (prefixed with the
- original service name) by erlsrv when adding a new
- service. Specifying this is a purely cosmethic action and is
- <em>not</em> recommended if release handling is to be
- performed. The internal service name cannot be changed once
- the service is created. The internal name is <em>not</em> to
- be confused with the ordinary service name, which is the name
- used to identify a service to erlsrv.</item>
- <tag>-c[omment] [&lt;short description&gt;]</tag>
- <item>Specifies a textual comment describing the
- service. This comment will show upp as the service description
- in the Windows&reg; service manager.</item>
</taglist>
</desc>
</func>
+
<func>
- <name>erlsrv {start | start_disabled | stop | disable | enable} &lt;service-name></name>
+ <name>erlsrv {start | start_disabled | stop | disable |
+ enable} &lt;service-name></name>
<fsummary>Manipulate the current service status.</fsummary>
<desc>
<p>These commands are only added for convenience, the normal
way to manipulate the state of a service is through the
- control panels services applet. The <c><![CDATA[start]]></c> and
+ control panels services applet.</p>
+ <p>The <c><![CDATA[start]]></c> and
<c><![CDATA[stop]]></c> commands communicates
- with the service manager for stopping and starting a
- service. The commands wait until the service is actually
- stopped or started. When disabling a service, it is not
- stopped, the disabled state will not take effect until the
- service actually is stopped. Enabling a service sets it in
- automatic mode, that is started at boot. This command cannot
- set the service to manual. </p>
-
- <p>The <c>start_disabled</c> command operates on a service
- regardless of if it's enabled/disabled or started/stopped. It
- does this by first enabling it (regardless of if it's enabled
- or not), then starting it (if it's not already started) and
- then disabling it. The result will be a disabled but started
- service, regardless of its earlier state. This is useful for
- starting services temporarily during a release upgrade. The
- difference between using <c>start_disabled</c> and the
- sequence <c>enable</c>, <c>start</c> and <c>disable</c> is
- that all other <c>erlsrv</c> commands are locked out during
- the sequence of operations in <c>start_disable</c>, making the
- operation atomic from an <c>erlsrv</c> user's point of
- view.</p>
-
+ with the service manager for starting and stopping a
+ service. The commands wait until the service is
+ started or stopped. When disabling a service, it is not
+ stopped, the disabled state does not take effect until the
+ service is stopped. Enabling a service sets it in
+ automatic mode, which is started at boot. This command cannot
+ set the service to manual.</p>
+ <p>The <c>start_disabled</c> command operates on a service
+ regardless of if it is enabled/disabled or started/stopped. It
+ does this by first enabling it (regardless of if it is enabled
+ or not), then starting it (if not already started), and
+ then disabling it. The result is a disabled but started
+ service, regardless of its earlier state. This is useful for
+ starting services temporarily during a release upgrade. The
+ difference between using <c>start_disabled</c> and the
+ sequence <c>enable</c>, <c>start</c>, and <c>disable</c> is
+ that all other <c>erlsrv</c> commands are locked out during
+ the sequence of operations in <c>start_disable</c>, making the
+ operation atomic from an <c>erlsrv</c> user's point of view.</p>
</desc>
</func>
+
<func>
<name>erlsrv remove &lt;service-name&gt;</name>
<fsummary>Remove the service.</fsummary>
<desc>
- <p>This command removes the service completely with all its registered
- options. It will be stopped before it is removed.</p>
+ <p>Removes the service completely with all its registered
+ options. It is stopped before it is removed.</p>
</desc>
</func>
+
<func>
<name>erlsrv list [&lt;service-name&gt;]</name>
- <fsummary>List all Erlang services or all options for one service.</fsummary>
+ <fsummary>List all Erlang services or all options for one service.
+ </fsummary>
<desc>
- <p>If no service name is supplied, a brief listing of all Erlang services
- is presented. If a service-name is supplied, all options for that
- service are presented.</p>
+ <p>If no service name is specified, a brief listing of all Erlang
+ services is presented. If a service name is supplied, all options
+ for that service are presented.</p>
</desc>
</func>
+
<func>
<name>erlsrv help</name>
- <fsummary>Display a brief help text</fsummary>
+ <fsummary>Display a brief help text.</fsummary>
+ <desc>
+ <p>Displays a brief help text.</p>
+ </desc>
</func>
</funcs>
<section>
- <title>ENVIRONMENT</title>
- <p> <marker id="002"></marker>
-The environment of an Erlang machine started
- as a service will contain two special variables,
- <c><![CDATA[ERLSRV_SERVICE_NAME]]></c>, which is the name of the service that
- started the machine and <c><![CDATA[ERLSRV_EXECUTABLE]]></c> which is the
- full path to the <c><![CDATA[erlsrv.exe]]></c> that can be used to manipulate
- the service. This will come in handy when defining a heart command for
- your service. A command file for restarting a service will
- simply look like this:</p>
+ <title>Environment</title>
+ <p><marker id="002"></marker>
+ The environment of an Erlang machine started
+ as a service contains two special variables:</p>
+
+ <taglist>
+ <tag><c><![CDATA[ERLSRV_SERVICE_NAME]]></c></tag>
+ <item>The name of the service that started the machine.</item>
+ <tag><c><![CDATA[ERLSRV_EXECUTABLE]]></c></tag>
+ <item>The full path to the <c><![CDATA[erlsrv.exe]]></c>, which can be
+ used to manipulate the service. This comes in handy when defining a
+ heart command for your service.</item>
+ </taglist>
+
+ <p>A command file for restarting a service looks as follows:</p>
+
<code type="none"><![CDATA[
@echo off
%ERLSRV_EXECUTABLE% stop %ERLSRV_SERVICE_NAME%
%ERLSRV_EXECUTABLE% start %ERLSRV_SERVICE_NAME% ]]></code>
+
<p>This command file is then set as heart command.</p>
+
<p>The environment variables can also be used to detect that we
are running as a service and make port programs react correctly
- to the control events generated on logout (see below).</p>
+ to the control events generated on logout (see the next section).</p>
</section>
<section>
- <title>PORT PROGRAMS</title>
+ <title>Port Programs</title>
<p>When a program runs in
- the service context, it has to handle the control events that is
+ the service context, it must handle the control events that are
sent to every program in the system when the interactive user
logs off. This is done in different ways for programs running in
the console subsystem and programs running as window
- applications. An application which runs in the console subsystem
+ applications. An application running in the console subsystem
(normal for port programs) uses the win32 function
<c><![CDATA[SetConsoleCtrlHandler]]></c> to register a control handler
- that returns TRUE in answer to the <c><![CDATA[CTRL_LOGOFF_EVENT]]></c>
+ that returns <c>true</c> in answer to the
+ <c><![CDATA[CTRL_LOGOFF_EVENT]]></c>
and <c><![CDATA[CTRL_SHUTDOWN_EVENT]]></c> events. Other applications
- just forward <c><![CDATA[WM_ENDSESSION]]></c> and
- <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure.
- Here is a brief example in C of how to set the console control
- handler:</p>
+ only forward <c><![CDATA[WM_ENDSESSION]]></c> and
+ <c><![CDATA[WM_QUERYENDSESSION]]></c> to the default window procedure.</p>
+
+ <p>A brief example in C of how to set the console control handler:</p>
+
<code type="none"><![CDATA[
#include <windows.h>
/*
@@ -383,7 +487,7 @@ void initialize_handler(void){
char buffer[2];
/*
* We assume we are running as a service if this
- * environment variable is defined
+ * environment variable is defined.
*/
if(GetEnvironmentVariable("ERLSRV_SERVICE_NAME",buffer,
(DWORD) 2)){
@@ -396,29 +500,34 @@ void initialize_handler(void){
</section>
<section>
- <title>NOTES</title>
- <p>Even though the options are described in a Unix-like format, the case of
- the options or commands is not relevant, and the "/" character for options
- can be used as well as the "-" character. </p>
- <p>Note that the program resides in the emulators
- <c><![CDATA[bin]]></c>-directory, not in the <c><![CDATA[bin]]></c>-directory directly under
+ <title>Notes</title>
+ <p>Although the options are described in a Unix-like format, the case of
+ the options or commands is not relevant, and both character "/" and "-"
+ can be used for options.</p>
+
+ <p>Notice that the program resides in the emulator's <c><![CDATA[bin]]></c>
+ directory, not in the <c><![CDATA[bin]]></c> directory directly under
the Erlang root. The reasons for this are the subtle problem of
upgrading the emulator on a running system, where a new version of
the runtime system should not need to overwrite existing (and probably
used) executables.</p>
- <p>To easily manipulate the Erlang services, put
+
+ <p>To manipulate the Erlang services easily, put
the <c><![CDATA[<erlang_root>\erts-<version>\bin]]></c> directory in
- the path instead of <c><![CDATA[<erlang_root>\bin]]></c>. The erlsrv program
- can be found from inside Erlang by using the
+ the path instead of <c><![CDATA[<erlang_root>\bin]]></c>. The
+ <c>erlsrv</c> program can be found from inside Erlang by using the
<c><![CDATA[os:find_executable/1]]></c> Erlang function.</p>
- <p>For release handling to work, use <c><![CDATA[start_erl]]></c> as the Erlang
- machine. It is also worth mentioning again that the name of the
- service is significant (see <seealso marker="#001">above</seealso>).</p>
+
+ <p>For release handling to work, use <c><![CDATA[start_erl]]></c> as the
+ Erlang machine. As stated <seealso marker="#001">above</seealso>,
+ the service name is significant.</p>
</section>
<section>
- <title>SEE ALSO</title>
- <p>start_erl(1), release_handler(3)</p>
+ <title>See Also</title>
+ <p><seealso marker="start_erl"><c>start_erl(1)</c></seealso>,
+ <seealso marker="sasl:release_handler">
+ <c>release_handler(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml
index 9aef1c0b1f..8ab35851c1 100644
--- a/erts/doc/src/erts_alloc.xml
+++ b/erts/doc/src/erts_alloc.xml
@@ -25,63 +25,68 @@
<title>erts_alloc</title>
<prepared>Rickard Green</prepared>
<docno>1</docno>
- <date>03-06-11</date>
+ <date>2003-06-11</date>
<rev>1</rev>
<file>erts_alloc.xml</file>
</header>
<lib>erts_alloc</lib>
- <libsummary>An Erlang Run-Time System internal memory allocator library.</libsummary>
+ <libsummary>An Erlang runtime system internal memory allocator library.
+ </libsummary>
<description>
- <p><c>erts_alloc</c> is an Erlang Run-Time System internal memory
+ <p><c>erts_alloc</c> is an Erlang runtime system internal memory
allocator library. <c>erts_alloc</c> provides the Erlang
- Run-Time System with a number of memory allocators.</p>
+ runtime system with a number of memory allocators.</p>
</description>
<section>
<title>Allocators</title>
<marker id="allocators"></marker>
- <p>Currently the following allocators are present:</p>
+ <p>The following allocators are present:</p>
+
<taglist>
<tag><c>temp_alloc</c></tag>
<item>Allocator used for temporary allocations.</item>
<tag><c>eheap_alloc</c></tag>
- <item>Allocator used for Erlang heap data, such as Erlang process heaps.</item>
+ <item>Allocator used for Erlang heap data, such as Erlang process heaps.
+ </item>
<tag><c>binary_alloc</c></tag>
<item>Allocator used for Erlang binary data.</item>
<tag><c>ets_alloc</c></tag>
- <item>Allocator used for ETS data.</item>
+ <item>Allocator used for <c>ets</c> data.</item>
<tag><c>driver_alloc</c></tag>
<item>Allocator used for driver data.</item>
<tag><c>literal_alloc</c></tag>
<item>Allocator used for constant terms in Erlang code.</item>
<tag><c>sl_alloc</c></tag>
<item>Allocator used for memory blocks that are expected to be
- short-lived.</item>
+ short-lived.</item>
<tag><c>ll_alloc</c></tag>
<item>Allocator used for memory blocks that are expected to be
- long-lived, for example Erlang code.</item>
+ long-lived, for example, Erlang code.</item>
<tag><c>fix_alloc</c></tag>
<item>A fast allocator used for some frequently used
fixed size data types.</item>
<tag><c>exec_alloc</c></tag>
- <item>Allocator used by hipe for native executable code
- on specific architectures (x86_64).</item>
+ <item>Allocator used by the <seealso marker="hipe:HiPE_app"><c>HiPE</c></seealso>
+ application for native executable code on specific architectures
+ (x86_64).</item>
<tag><c>std_alloc</c></tag>
- <item>Allocator used for most memory blocks not allocated via any of
- the other allocators described above.</item>
+ <item>Allocator used for most memory blocks not allocated through any of
+ the other allocators described above.</item>
<tag><c>sys_alloc</c></tag>
<item>This is normally the default <c>malloc</c> implementation
- used on the specific OS.</item>
+ used on the specific OS.</item>
<tag><c>mseg_alloc</c></tag>
- <item>A memory segment allocator. <c>mseg_alloc</c> is used by other
- allocators for allocating memory segments and is currently only
- available on systems that have the <c>mmap</c> system
- call. Memory segments that are deallocated are kept for a
- while in a segment cache before they are destroyed. When
- segments are allocated, cached segments are used if possible
- instead of creating new segments. This in order to reduce
- the number of system calls made.</item>
+ <item>A memory segment allocator. It is used by other
+ allocators for allocating memory segments and is only
+ available on systems that have the <c>mmap</c> system
+ call. Memory segments that are deallocated are kept for a
+ while in a segment cache before they are destroyed. When
+ segments are allocated, cached segments are used if possible
+ instead of creating new segments. This to reduce
+ the number of system calls made.</item>
</taglist>
+
<p><c>sys_alloc</c> and <c>literal_alloc</c> are always enabled and
cannot be disabled. <c>exec_alloc</c> is only available if it is needed
and cannot be disabled. <c>mseg_alloc</c> is always enabled if it is
@@ -90,9 +95,10 @@
By default all allocators are enabled.
When an allocator is disabled, <c>sys_alloc</c> is used instead of
the disabled allocator.</p>
+
<p>The main idea with the <c>erts_alloc</c> library is to separate
memory blocks that are used differently into different memory
- areas, and by this achieving less memory fragmentation. By
+ areas, to achieve less memory fragmentation. By
putting less effort in finding a good fit for memory blocks that
are frequently allocated than for those less frequently
allocated, a performance gain can be achieved.</p>
@@ -100,61 +106,85 @@
<section>
<marker id="alloc_util"></marker>
- <title>The alloc_util framework</title>
+ <title>The alloc_util Framework</title>
<p>Internally a framework called <c>alloc_util</c> is used for
- implementing allocators. <c>sys_alloc</c>, and
- <c>mseg_alloc</c> do not use this framework; hence, the
+ implementing allocators. <c>sys_alloc</c> and
+ <c>mseg_alloc</c> do not use this framework, so the
following does <em>not</em> apply to them.</p>
+
<p>An allocator manages multiple areas, called carriers, in which
memory blocks are placed. A carrier is either placed in a
- separate memory segment (allocated via <c>mseg_alloc</c>), or in
- the heap segment (allocated via <c>sys_alloc</c>). Multiblock
- carriers are used for storage of several blocks. Singleblock
- carriers are used for storage of one block. Blocks that are
- larger than the value of the singleblock carrier threshold
- (<seealso marker="#M_sbct">sbct</seealso>) parameter are placed
- in singleblock carriers. Blocks that are smaller than the value
- of the <c>sbct</c> parameter are placed in multiblock
- carriers. Normally an allocator creates a "main multiblock
+ separate memory segment (allocated through <c>mseg_alloc</c>), or in
+ the heap segment (allocated through <c>sys_alloc</c>).</p>
+
+ <list type="bulleted">
+ <item>
+ <p>Multiblock carriers are used for storage of several blocks.</p>
+ </item>
+ <item>
+ <p>Singleblock carriers are used for storage of one block.</p>
+ </item>
+ <item>
+ <p>Blocks that are larger than the value of the singleblock carrier
+ threshold (<seealso marker="#M_sbct"><c>sbct</c></seealso>) parameter
+ are placed in singleblock carriers.</p>
+ </item>
+ <item>
+ <p>Blocks that are smaller than the value of parameter <c>sbct</c>
+ are placed in multiblock carriers.</p></item>
+ </list>
+
+ <p>Normally an allocator creates a "main multiblock
carrier". Main multiblock carriers are never deallocated. The
- size of the main multiblock carrier is determined by the value
- of the <seealso marker="#M_mmbcs">mmbcs</seealso> parameter.</p>
+ size of the main multiblock carrier is determined by the value of
+ parameter <seealso marker="#M_mmbcs"><c>mmbcs</c></seealso>.</p>
+
<p><marker id="mseg_mbc_sizes"></marker>Sizes of multiblock carriers
- allocated via <c>mseg_alloc</c> are
- decided based on the values of the largest multiblock carrier
- size (<seealso marker="#M_lmbcs">lmbcs</seealso>), the smallest
- multiblock carrier size (<seealso marker="#M_smbcs">smbcs</seealso>),
- and the multiblock carrier growth stages
- (<seealso marker="#M_mbcgs">mbcgs</seealso>) parameters. If
- <c>nc</c> is the current number of multiblock carriers (the main
+ allocated through <c>mseg_alloc</c> are decided based on the
+ following parameters:</p>
+
+ <list type="bulleted">
+ <item>The values of the largest multiblock carrier size
+ (<seealso marker="#M_lmbcs"><c>lmbcs</c></seealso>)</item>
+ <item>The smallest multiblock carrier size
+ (<seealso marker="#M_smbcs"><c>smbcs</c></seealso>)</item>
+ <item>The multiblock carrier growth stages
+ (<seealso marker="#M_mbcgs"><c>mbcgs</c></seealso>)</item>
+ </list>
+
+ <p>If <c>nc</c> is the current number of multiblock carriers (the main
multiblock carrier excluded) managed by an allocator, the size
of the next <c>mseg_alloc</c> multiblock carrier allocated by
- this allocator will roughly be
+ this allocator is roughly
<c><![CDATA[smbcs+nc*(lmbcs-smbcs)/mbcgs]]></c> when
<c><![CDATA[nc <= mbcgs]]></c>,
- and <c>lmbcs</c> when <c><![CDATA[nc > mbcgs]]></c>. If the value of the
- <c>sbct</c> parameter should be larger than the value of the
- <c>lmbcs</c> parameter, the allocator may have to create
- multiblock carriers that are larger than the value of the
- <c>lmbcs</c> parameter, though.
- Singleblock carriers allocated via <c>mseg_alloc</c> are sized
+ and <c>lmbcs</c> when <c><![CDATA[nc > mbcgs]]></c>. If the value of
+ parameter <c>sbct</c> is larger than the value of parameter
+ <c>lmbcs</c>, the allocator may have to create
+ multiblock carriers that are larger than the value of
+ parameter <c>lmbcs</c>, though.
+ Singleblock carriers allocated through <c>mseg_alloc</c> are sized
to whole pages.</p>
- <p>Sizes of carriers allocated via <c>sys_alloc</c> are
+
+ <p>Sizes of carriers allocated through <c>sys_alloc</c> are
decided based on the value of the <c>sys_alloc</c> carrier size
- (<seealso marker="#Muycs">ycs</seealso>) parameter. The size of
- a carrier is the least number of multiples of the value of the
- <c>ycs</c> parameter that satisfies the request.</p>
+ (<seealso marker="#Muycs"><c>ycs</c></seealso>) parameter. The size of
+ a carrier is the least number of multiples of the value of
+ parameter <c>ycs</c> satisfying the request.</p>
+
<p>Coalescing of free blocks are always performed immediately.
- Boundary tags (headers and footers) in free blocks are used
+ Boundary tags (headers and footers) in free blocks are used,
which makes the time complexity for coalescing constant.</p>
+
<p><marker id="strategy"></marker>The memory allocation strategy
- used for multiblock carriers by an
- allocator is configurable via the <seealso marker="#M_as">as</seealso>
- parameter. Currently the following strategies are available:</p>
+ used for multiblock carriers by an allocator can be
+ configured using parameter <seealso marker="#M_as"><c>as</c></seealso>.
+ The following strategies are available:</p>
+
<taglist>
<tag>Best fit</tag>
<item>
- <p>Strategy: Find the smallest block that satisfies the
+ <p>Strategy: Find the smallest block satisfying the
requested block size.</p>
<p>Implementation: A balanced binary search tree is
used. The time complexity is proportional to log N, where
@@ -162,7 +192,7 @@
</item>
<tag>Address order best fit</tag>
<item>
- <p>Strategy: Find the smallest block that satisfies the
+ <p>Strategy: Find the smallest block satisfying the
requested block size. If multiple blocks are found, choose
the one with the lowest address.</p>
<p>Implementation: A balanced binary search tree is
@@ -171,7 +201,7 @@
</item>
<tag>Address order first fit</tag>
<item>
- <p>Strategy: Find the block with the lowest address that satisfies the
+ <p>Strategy: Find the block with the lowest address satisfying the
requested block size.</p>
<p>Implementation: A balanced binary search tree is
used. The time complexity is proportional to log N, where
@@ -180,8 +210,8 @@
<tag>Address order first fit carrier best fit</tag>
<item>
<p>Strategy: Find the <em>carrier</em> with the lowest address that
- can satisfy the requested block size, then find a block within
- that carrier using the "best fit" strategy.</p>
+ can satisfy the requested block size, then find a block within
+ that carrier using the "best fit" strategy.</p>
<p>Implementation: Balanced binary search trees are
used. The time complexity is proportional to log N, where
N is the number of free blocks.</p>
@@ -189,8 +219,8 @@
<tag>Address order first fit carrier address order best fit</tag>
<item>
<p>Strategy: Find the <em>carrier</em> with the lowest address that
- can satisfy the requested block size, then find a block within
- that carrier using the "adress order best fit" strategy.</p>
+ can satisfy the requested block size, then find a block within
+ that carrier using the "address order best fit" strategy.</p>
<p>Implementation: Balanced binary search trees are
used. The time complexity is proportional to log N, where
N is the number of free blocks.</p>
@@ -200,12 +230,12 @@
<p>Strategy: Try to find the best fit, but settle for the best fit
found during a limited search.</p>
<p>Implementation: The implementation uses segregated free
- lists with a maximum block search depth (in each list) in
- order to find a good fit fast. When the maximum block
- search depth is small (by default 3) this implementation
+ lists with a maximum block search depth (in each list)
+ to find a good fit fast. When the maximum block
+ search depth is small (by default 3), this implementation
has a time complexity that is constant. The maximum block
- search depth is configurable via the
- <seealso marker="#M_mbsd">mbsd</seealso> parameter.</p>
+ search depth can be configured using parameter
+ <seealso marker="#M_mbsd"><c>mbsd</c></seealso>.</p>
</item>
<tag>A fit</tag>
<item>
@@ -213,43 +243,47 @@
block to see if it satisfies the request. This strategy is
only intended to be used for temporary allocations.</p>
<p>Implementation: Inspect the first block in a free-list.
- If it satisfies the request, it is used; otherwise, a new
+ If it satisfies the request, it is used, otherwise a new
carrier is created. The implementation has a time
complexity that is constant.</p>
- <p>As of erts version 5.6.1 the emulator will refuse to
- use this strategy on other allocators than <c>temp_alloc</c>.
- This since it will only cause problems for other allocators.</p>
+ <p>As from ERTS 5.6.1 the emulator refuses to
+ use this strategy on other allocators than <c>temp_alloc</c>.
+ This because it only causes problems for other allocators.</p>
</item>
</taglist>
- <p>Apart from the ordinary allocators described above a number of
- pre-allocators are used for some specific data types. These
- pre-allocators pre-allocate a fixed amount of memory for certain data
- types when the run-time system starts. As long as pre-allocated memory
- is available, it will be used. When no pre-allocated memory is
- available, memory will be allocated in ordinary allocators. These
- pre-allocators are typically much faster than the ordinary allocators,
- but can only satisfy a limited amount of requests.</p>
+
+ <p>Apart from the ordinary allocators described above, some
+ pre-allocators are used for some specific data types. These
+ pre-allocators pre-allocate a fixed amount of memory for certain data
+ types when the runtime system starts. As long as pre-allocated memory
+ is available, it is used. When no pre-allocated memory is
+ available, memory is allocated in ordinary allocators. These
+ pre-allocators are typically much faster than the ordinary allocators,
+ but can only satisfy a limited number of requests.</p>
</section>
<section>
<marker id="flags"></marker>
<title>System Flags Effecting erts_alloc</title>
<warning>
- <p>Only use these flags if you are absolutely sure what you are
- doing. Unsuitable settings may cause serious performance
+ <p>Only use these flags if you are sure what you are
+ doing. Unsuitable settings can cause serious performance
degradation and even a system crash at any time during
operation.</p>
</warning>
+
<p>Memory allocator system flags have the following syntax:
- <c><![CDATA[+M<S><P> <V>]]></c>
+ <c><![CDATA[+M<S><P> <V>]]></c>,
where <c><![CDATA[<S>]]></c> is a letter identifying a subsystem,
<c><![CDATA[<P>]]></c> is a parameter, and <c><![CDATA[<V>]]></c> is the
value to use. The flags can be passed to the Erlang emulator
- (<seealso marker="erl">erl</seealso>) as command line
+ (<seealso marker="erl"><c>erl(1)</c></seealso>) as command-line
arguments.</p>
- <p>System flags effecting specific allocators have an upper-case
+
+ <p>System flags effecting specific allocators have an uppercase
letter as <c><![CDATA[<S>]]></c>. The following letters are used for
- the currently present allocators:</p>
+ the allocators:</p>
+
<list type="bulleted">
<item><c>B: binary_alloc</c></item>
<item><c>D: std_alloc</c></item>
@@ -265,421 +299,501 @@
<item><c>X: exec_alloc</c></item>
<item><c>Y: sys_alloc</c></item>
</list>
- <p>The following flags are available for configuration of
- <c>mseg_alloc</c>:</p>
- <taglist>
- <tag><marker id="MMamcbf"/><c><![CDATA[+MMamcbf <size>]]></c></tag>
- <item>
- Absolute max cache bad fit (in kilobytes). A segment in the
- memory segment cache is not reused if its size exceeds the
- requested size with more than the value of this
- parameter. Default value is 4096. </item>
- <tag><marker id="MMrmcbf"/><c><![CDATA[+MMrmcbf <ratio>]]></c></tag>
- <item>
- Relative max cache bad fit (in percent). A segment in the
- memory segment cache is not reused if its size exceeds the
- requested size with more than relative max cache bad fit
- percent of the requested size. Default value is 20.</item>
- <tag><marker id="MMsco"/><c><![CDATA[+MMsco true|false]]></c></tag>
- <item>
- Set <seealso marker="#MMscs">super carrier</seealso> only flag. This
- flag defaults to <c>true</c>. When a super carrier is used and this
- flag is <c>true</c>, <c>mseg_alloc</c> will only create carriers
- in the super carrier. Note that the <c>alloc_util</c> framework may
- create <c>sys_alloc</c> carriers, so if you want all carriers to
- be created in the super carrier, you therefore want to disable use
- of <c>sys_alloc</c> carriers by also passing
- <seealso marker="#Musac"><c>+Musac false</c></seealso>. When the flag
- is <c>false</c>, <c>mseg_alloc</c> will try to create carriers outside
- of the super carrier when the super carrier is full.
- <br/><br/>
- <em>NOTE</em>: Setting this flag to <c>false</c> may not be supported
- on all systems. This flag will in that case be ignored.
- <br/><br/>
- <em>NOTE</em>: The super carrier cannot be enabled nor
- disabled on halfword heap systems. This flag will be
- ignored on halfword heap systems.
- </item>
- <tag><marker id="MMscrfsd"/><c><![CDATA[+MMscrfsd <amount>]]></c></tag>
- <item>
- Set <seealso marker="#MMscs">super carrier</seealso> reserved
- free segment descriptors. This parameter defaults to <c>65536</c>.
- This parameter determines the amount of memory to reserve for
- free segment descriptors used by the super carrier. If the system
- runs out of reserved memory for free segment descriptors, other
- memory will be used. This may however cause fragmentation issues,
- so you want to ensure that this never happens. The maximum amount
- of free segment descriptors used can be retrieved from the
- <c>erts_mmap</c> tuple part of the result from calling
- <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, mseg_alloc})</seealso>.
- </item>
- <tag><marker id="MMscrpm"/><c><![CDATA[+MMscrpm true|false]]></c></tag>
- <item>
- Set <seealso marker="#MMscs">super carrier</seealso> reserve physical
- memory flag. This flag defaults to <c>true</c>. When this flag is
- <c>true</c>, physical memory will be reserved for the whole super
- carrier at once when it is created. The reservation will after that
- be left unchanged. When this flag is set to <c>false</c> only virtual
- address space will be reserved for the super carrier upon creation.
- The system will attempt to reserve physical memory upon carrier
- creations in the super carrier, and attempt to unreserve physical
- memory upon carrier destructions in the super carrier.
- <br/><br/>
- <em>NOTE</em>: What reservation of physical memory actually means
- highly depends on the operating system, and how it is configured. For
- example, different memory overcommit settings on Linux drastically
- change the behaviour. Also note, setting this flag to <c>false</c>
- may not be supported on all systems. This flag will in that case
- be ignored.
- <br/><br/>
- <em>NOTE</em>: The super carrier cannot be enabled nor
- disabled on halfword heap systems. This flag will be
- ignored on halfword heap systems.
- </item>
- <tag><marker id="MMscs"/><c><![CDATA[+MMscs <size in MB>]]></c></tag>
- <item>
- Set super carrier size (in MB). The super carrier size defaults to
- zero; i.e, the super carrier is by default disabled. The super
- carrier is a large continuous area in the virtual address space.
- <c>mseg_alloc</c> will always try to create new carriers in the super
- carrier if it exists. Note that the <c>alloc_util</c> framework may
- create <c>sys_alloc</c> carriers. For more information on this, see the
- documentation of the <seealso marker="#MMsco"><c>+MMsco</c></seealso>
- flag.
- <br/><br/>
- <em>NOTE</em>: The super carrier cannot be enabled nor
- disabled on halfword heap systems. This flag will be
- ignored on halfword heap systems.
- </item>
- <tag><marker id="MMmcs"/><c><![CDATA[+MMmcs <amount>]]></c></tag>
- <item>
- Max cached segments. The maximum number of memory segments
- stored in the memory segment cache. Valid range is
- 0-30. Default value is 10.</item>
- </taglist>
- <p>The following flags are available for configuration of
- <c>sys_alloc</c>:</p>
- <taglist>
- <tag><marker id="MYe"/><c>+MYe true</c></tag>
- <item>
- Enable <c>sys_alloc</c>. Note: <c>sys_alloc</c> cannot be disabled.</item>
- <tag><marker id="MYm"/><c>+MYm libc</c></tag>
- <item>
- <c>malloc</c> library to use. Currently only
- <c>libc</c> is available. <c>libc</c> enables the standard
- <c>libc</c> malloc implementation. By default <c>libc</c> is used.</item>
- <tag><marker id="MYtt"/><c><![CDATA[+MYtt <size>]]></c></tag>
- <item>
- Trim threshold size (in kilobytes). This is the maximum amount
- of free memory at the top of the heap (allocated by
- <c>sbrk</c>) that will be kept by <c>malloc</c> (not
- released to the operating system). When the amount of free
- memory at the top of the heap exceeds the trim threshold,
- <c>malloc</c> will release it (by calling
- <c>sbrk</c>). Trim threshold is given in kilobytes. Default
- trim threshold is 128. <em>Note:</em> This flag will
- only have any effect when the emulator has been linked with
- the GNU C library, and uses its <c>malloc</c> implementation.</item>
- <tag><marker id="MYtp"/><c><![CDATA[+MYtp <size>]]></c></tag>
- <item>
- Top pad size (in kilobytes). This is the amount of extra
- memory that will be allocated by <c>malloc</c> when
- <c>sbrk</c> is called to get more memory from the operating
- system. Default top pad size is 0. <em>Note:</em> This flag
- will only have any effect when the emulator has been linked
- with the GNU C library, and uses its <c>malloc</c>
- implementation.</item>
- </taglist>
- <p>The following flags are available for configuration of allocators
- based on <c>alloc_util</c>. If <c>u</c> is used as subsystem
- identifier (i.e., <c><![CDATA[<S> = u]]></c>) all allocators based on
- <c>alloc_util</c> will be effected. If <c>B</c>, <c>D</c>, <c>E</c>,
- <c>F</c>, <c>H</c>, <c>L</c>, <c>R</c>, <c>S</c>, or <c>T</c> is used as
- subsystem identifier, only the specific allocator identified will be
- effected:</p>
- <taglist>
- <tag><marker id="M_acul"/><c><![CDATA[+M<S>acul <utilization>|de]]></c></tag>
- <item>
- Abandon carrier utilization limit. A valid
- <c><![CDATA[<utilization>]]></c> is an integer in the range
- <c>[0, 100]</c> representing utilization in percent. When a
- utilization value larger than zero is used, allocator instances
- are allowed to abandon multiblock carriers. If <c>de</c> (default
- enabled) is passed instead of a <c><![CDATA[<utilization>]]></c>,
- a recomended non zero utilization value will be used. The actual
- value chosen depend on allocator type and may be changed between
- ERTS versions. Currently the default equals <c>de</c>, but this
- may be changed in the future. Carriers will be abandoned when
- memory utilization in the allocator instance falls below the
- utilization value used. Once a carrier has been abandoned, no new
- allocations will be made in it. When an allocator instance gets an
- increased multiblock carrier need, it will first try to fetch an
- abandoned carrier from an allocator instances of the same
- allocator type. If no abandoned carrier could be fetched, it will
- create a new empty carrier. When an abandoned carrier has been
- fetched it will function as an ordinary carrier. This feature has
- special requirements on the
- <seealso marker="#M_as">allocation strategy</seealso> used. Currently
- only the strategies <c>aoff</c>, <c>aoffcbf</c> and <c>aoffcaobf</c> support
- abandoned carriers. This feature also requires
- <seealso marker="#M_t">multiple thread specific instances</seealso>
- to be enabled. When enabling this feature, multiple thread specific
- instances will be enabled if not already enabled, and the
- <c>aoffcbf</c> strategy will be enabled if current strategy does not
- support abandoned carriers. This feature can be enabled on all
- allocators based on the <c>alloc_util</c> framework with the
- exception of <c>temp_alloc</c> (which would be pointless).
- </item>
- <tag><marker id="M_as"/><c><![CDATA[+M<S>as bf|aobf|aoff|aoffcbf|aoffcaobf|gf|af]]></c></tag>
- <item>
- Allocation strategy. Valid strategies are <c>bf</c> (best fit),
- <c>aobf</c> (address order best fit), <c>aoff</c> (address order first fit),
- <c>aoffcbf</c> (address order first fit carrier best fit),
- <c>aoffcaobf</c> (address order first fit carrier address order best fit),
- <c>gf</c> (good fit), and <c>af</c> (a fit). See
- <seealso marker="#strategy">the description of allocation strategies</seealso> in "the <c>alloc_util</c> framework" section.</item>
- <tag><marker id="M_asbcst"/><c><![CDATA[+M<S>asbcst <size>]]></c></tag>
- <item>
- Absolute singleblock carrier shrink threshold (in
- kilobytes). When a block located in an
- <c>mseg_alloc</c> singleblock carrier is shrunk, the carrier
- will be left unchanged if the amount of unused memory is less
- than this threshold; otherwise, the carrier will be shrunk.
- See also <seealso marker="#M_rsbcst">rsbcst</seealso>.</item>
- <tag><marker id="M_e"/><c><![CDATA[+M<S>e true|false]]></c></tag>
- <item>
- Enable allocator <c><![CDATA[<S>]]></c>.</item>
- <tag><marker id="M_lmbcs"/><c><![CDATA[+M<S>lmbcs <size>]]></c></tag>
- <item>
- Largest (<c>mseg_alloc</c>) multiblock carrier size (in
- kilobytes). See <seealso marker="#mseg_mbc_sizes">the description
- on how sizes for mseg_alloc multiblock carriers are decided</seealso>
- in "the <c>alloc_util</c> framework" section. On 32-bit Unix style OS
- this limit can not be set higher than 128 megabyte.</item>
- <tag><marker id="M_mbcgs"/><c><![CDATA[+M<S>mbcgs <ratio>]]></c></tag>
- <item>
- (<c>mseg_alloc</c>) multiblock carrier growth stages. See
- <seealso marker="#mseg_mbc_sizes">the description on how sizes for
- mseg_alloc multiblock carriers are decided</seealso>
- in "the <c>alloc_util</c> framework" section.</item>
- <tag><marker id="M_mbsd"/><c><![CDATA[+M<S>mbsd <depth>]]></c></tag>
- <item>
- Max block search depth. This flag has effect only if the
- good fit strategy has been selected for allocator
- <c><![CDATA[<S>]]></c>. When the good fit strategy is used, free
- blocks are placed in segregated free-lists. Each free list
- contains blocks of sizes in a specific range. The max block
- search depth sets a limit on the maximum number of blocks to
- inspect in a free list during a search for suitable block
- satisfying the request.</item>
- <tag><marker id="M_mmbcs"/><c><![CDATA[+M<S>mmbcs <size>]]></c></tag>
- <item>
- Main multiblock carrier size. Sets the size of the main
- multiblock carrier for allocator <c><![CDATA[<S>]]></c>. The main
- multiblock carrier is allocated via <c><![CDATA[sys_alloc]]></c> and is
- never deallocated.</item>
- <tag><marker id="M_mmmbc"/><c><![CDATA[+M<S>mmmbc <amount>]]></c></tag>
- <item>
- Max <c>mseg_alloc</c> multiblock carriers. Maximum number of
- multiblock carriers allocated via <c>mseg_alloc</c> by
- allocator <c><![CDATA[<S>]]></c>. When this limit has been reached,
- new multiblock carriers will be allocated via
- <c>sys_alloc</c>.</item>
- <tag><marker id="M_mmsbc"/><c><![CDATA[+M<S>mmsbc <amount>]]></c></tag>
- <item>
- Max <c>mseg_alloc</c> singleblock carriers. Maximum number of
- singleblock carriers allocated via <c>mseg_alloc</c> by
- allocator <c><![CDATA[<S>]]></c>. When this limit has been reached,
- new singleblock carriers will be allocated via
- <c>sys_alloc</c>.</item>
- <tag><marker id="M_ramv"/><c><![CDATA[+M<S>ramv <bool>]]></c></tag>
- <item>
- Realloc always moves. When enabled, reallocate operations will
- more or less be translated into an allocate, copy, free sequence.
- This often reduce memory fragmentation, but costs performance.
- </item>
- <tag><marker id="M_rmbcmt"/><c><![CDATA[+M<S>rmbcmt <ratio>]]></c></tag>
- <item>
- Relative multiblock carrier move threshold (in percent). When
- a block located in a multiblock carrier is shrunk,
- the block will be moved if the ratio of the size of the returned
- memory compared to the previous size is more than this threshold;
- otherwise, the block will be shrunk at current location.</item>
- <tag><marker id="M_rsbcmt"/><c><![CDATA[+M<S>rsbcmt <ratio>]]></c></tag>
- <item>
- Relative singleblock carrier move threshold (in percent). When
- a block located in a singleblock carrier is shrunk to
- a size smaller than the value of the
- <seealso marker="#M_sbct">sbct</seealso> parameter,
- the block will be left unchanged in the singleblock carrier if
- the ratio of unused memory is less than this threshold;
- otherwise, it will be moved into a multiblock carrier. </item>
- <tag><marker id="M_rsbcst"/><c><![CDATA[+M<S>rsbcst <ratio>]]></c></tag>
- <item>
- Relative singleblock carrier shrink threshold (in
- percent). When a block located in an <c>mseg_alloc</c>
- singleblock carrier is shrunk, the carrier will be left
- unchanged if the ratio of unused memory is less than this
- threshold; otherwise, the carrier will be shrunk.
- See also <seealso marker="#M_asbcst">asbcst</seealso>.</item>
- <tag><marker id="M_sbct"/><c><![CDATA[+M<S>sbct <size>]]></c></tag>
- <item>
- Singleblock carrier threshold. Blocks larger than this
- threshold will be placed in singleblock carriers. Blocks
- smaller than this threshold will be placed in multiblock
- carriers. On 32-bit Unix style OS this threshold can not be set higher
- than 8 megabytes.</item>
- <tag><marker id="M_smbcs"/><c><![CDATA[+M<S>smbcs <size>]]></c></tag>
- <item>
- Smallest (<c>mseg_alloc</c>) multiblock carrier size (in
- kilobytes). See <seealso marker="#mseg_mbc_sizes">the description
- on how sizes for mseg_alloc multiblock carriers are decided</seealso>
- in "the <c>alloc_util</c> framework" section.</item>
- <tag><marker id="M_t"/><c><![CDATA[+M<S>t true|false]]></c></tag>
- <item>
- <p>Multiple, thread specific instances of the allocator.
- This option will only have any effect on the runtime system
- with SMP support. Default behaviour on the runtime system with
- SMP support is <c>NoSchedulers+1</c> instances. Each scheduler will use
- a lock-free instance of its own and other threads will use
- a common instance.</p>
- <p>It was previously (before ERTS version 5.9) possible to configure
- a smaller amount of thread specific instances than schedulers.
- This is, however, not possible any more.</p>
- </item>
- </taglist>
- <p>Currently the following flags are available for configuration of
- <c>alloc_util</c>, i.e. all allocators based on <c>alloc_util</c>
- will be effected:</p>
- <taglist>
- <tag><marker id="Muycs"/><c><![CDATA[+Muycs <size>]]></c></tag>
- <item>
- <c>sys_alloc</c> carrier size. Carriers allocated via
- <c>sys_alloc</c> will be allocated in sizes which are
- multiples of the <c>sys_alloc</c> carrier size. This is not
- true for main multiblock carriers and carriers allocated
- during a memory shortage, though.</item>
- <tag><marker id="Mummc"/><c><![CDATA[+Mummc <amount>]]></c></tag>
- <item>
- Max <c>mseg_alloc</c> carriers. Maximum number of carriers
- placed in separate memory segments. When this limit has been
- reached, new carriers will be placed in memory retrieved from
- <c>sys_alloc</c>.</item>
- <tag><marker id="Musac"/><c><![CDATA[+Musac <bool>]]></c></tag>
- <item>
- Allow <c>sys_alloc</c> carriers. By default <c>true</c>. If
- set to <c>false</c>, <c>sys_alloc</c> carriers will never be
- created by allocators using the <c>alloc_util</c> framework.</item>
- </taglist>
- <p>The following flag is special for <c>literal_alloc</c>:</p>
- <taglist>
- <tag><marker id="MIscs"/><c><![CDATA[+MIscs <size in MB>]]></c></tag>
- <item>
- <c>literal_alloc</c> super carrier size (in MB). The amount of
- <em>virtual</em> address space reserved for literal terms in
- Erlang code on 64-bit architectures. The default is 1024 (1GB)
- and is usually sufficient. The flag is ignored on 32-bit
- architectures.</item>
- </taglist>
- <p>The following flag is special for <c>exec_alloc</c>:</p>
- <taglist>
- <tag><marker id="MXscs"/><c><![CDATA[+MXscs <size in MB>]]></c></tag>
- <item>
- <c>exec_alloc</c> super carrier size (in MB). The amount of
- <em>virtual</em> address space reserved for native executable code
- used by hipe on specific architectures (x86_64). The default is 512 MB.
- </item>
- </taglist>
- <p>Instrumentation flags:</p>
- <taglist>
- <tag><marker id="Mim"/><c>+Mim true|false</c></tag>
- <item>
- A map over current allocations is kept by the emulator. The
- allocation map can be retrieved via the <c>instrument</c>
- module. <c>+Mim true</c> implies <c>+Mis true</c>.
- <c>+Mim true</c> is the same as
- <seealso marker="erl#instr">-instr</seealso>.</item>
- <tag><marker id="Mis"/><c>+Mis true|false</c></tag>
- <item>
- Status over allocated memory is kept by the emulator. The
- allocation status can be retrieved via the <c>instrument</c>
- module.</item>
- <tag><marker id="Mit"/><c>+Mit X</c></tag>
- <item>
- Reserved for future use. Do <em>not</em> use this flag.</item>
- </taglist>
- <note>
- <p>When instrumentation of the emulator is enabled, the emulator
- uses more memory and runs slower.</p>
- </note>
- <p>Other flags:</p>
- <taglist>
- <tag><marker id="Mea"/><c>+Mea min|max|r9c|r10b|r11b|config</c></tag>
- <item>
+
+ <section>
+ <title>Flags for Configuration of mseg_alloc</title>
+ <taglist>
+ <tag><marker id="MMamcbf"/><c><![CDATA[+MMamcbf <size>]]></c></tag>
+ <item>
+ <p>Absolute maximum cache bad fit (in kilobytes). A segment in the
+ memory segment cache is not reused if its size exceeds the
+ requested size with more than the value of this
+ parameter. Defaults to <c>4096</c>.</p>
+ </item>
+ <tag><marker id="MMrmcbf"/><c><![CDATA[+MMrmcbf <ratio>]]></c></tag>
+ <item>
+ <p>Relative maximum cache bad fit (in percent). A segment in the
+ memory segment cache is not reused if its size exceeds the
+ requested size with more than relative maximum cache bad fit
+ percent of the requested size. Defaults to <c>20</c>.</p>
+ </item>
+ <tag><marker id="MMsco"/><c><![CDATA[+MMsco true|false]]></c></tag>
+ <item>
+ <p>Sets <seealso marker="#MMscs">super carrier</seealso> only flag.
+ Defaults to <c>true</c>. When a super carrier is used and this
+ flag is <c>true</c>, <c>mseg_alloc</c> only creates carriers in
+ the super carrier. Notice that the <c>alloc_util</c> framework can
+ create <c>sys_alloc</c> carriers, so if you want all carriers to
+ be created in the super carrier, you therefore want to disable use
+ of <c>sys_alloc</c> carriers by also passing
+ <seealso marker="#Musac"><c>+Musac false</c></seealso>. When
+ the flag is <c>false</c>, <c>mseg_alloc</c> tries to create carriers
+ outside of the super carrier when the super carrier is full.</p>
+ <note>
+ <p>Setting this flag to <c>false</c> is not supported
+ on all systems. The flag is then ignored.</p>
+ </note>
+ </item>
+ <tag><marker id="MMscrfsd"/><c><![CDATA[+MMscrfsd <amount>]]></c></tag>
+ <item>
+ <p>Sets <seealso marker="#MMscs">super carrier</seealso> reserved
+ free segment descriptors. Defaults to <c>65536</c>.
+ This parameter determines the amount of memory to reserve for
+ free segment descriptors used by the super carrier. If the system
+ runs out of reserved memory for free segment descriptors, other
+ memory is used. This can however cause fragmentation issues,
+ so you want to ensure that this never happens. The maximum amount
+ of free segment descriptors used can be retrieved from the
+ <c>erts_mmap</c> tuple part of the result from calling
+ <seealso marker="erts:erlang#system_info_allocator_tuple">
+ <c>erlang:system_info({allocator, mseg_alloc})</c></seealso>.</p>
+ </item>
+ <tag><marker id="MMscrpm"/><c><![CDATA[+MMscrpm true|false]]></c></tag>
+ <item>
+ <p>Sets <seealso marker="#MMscs">super carrier</seealso> reserve
+ physical memory flag. Defaults to <c>true</c>. When this flag is
+ <c>true</c>, physical memory is reserved for the whole super
+ carrier at once when it is created. The reservation is after that
+ left unchanged. When this flag is set to <c>false</c>, only virtual
+ address space is reserved for the super carrier upon creation.
+ The system attempts to reserve physical memory upon carrier
+ creations in the super carrier, and attempt to unreserve physical
+ memory upon carrier destructions in the super carrier.</p>
+ <note>
+ <p>What reservation of physical memory means, highly
+ depends on the operating system, and how it is configured. For
+ example, different memory overcommit settings on Linux drastically
+ change the behavior.</p>
+ <p>Setting this flag to <c>false</c> is possibly not supported on
+ all systems. The flag is then ignored.</p>
+ </note>
+ </item>
+ <tag><marker id="MMscs"/><c><![CDATA[+MMscs <size in MB>]]></c></tag>
+ <item>
+ <p>Sets super carrier size (in MB). Defaults to <c>0</c>, that is,
+ the super carrier is by default disabled. The super
+ carrier is a large continuous area in the virtual address space.
+ <c>mseg_alloc</c> always tries to create new carriers in the super
+ carrier if it exists. Notice that the <c>alloc_util</c> framework
+ can create <c>sys_alloc</c> carriers. For more information, see
+ <seealso marker="#MMsco"><c>+MMsco</c></seealso>.</p>
+ </item>
+ <tag><marker id="MMmcs"/><c><![CDATA[+MMmcs <amount>]]></c></tag>
+ <item>
+ <p>Maximum cached segments. The maximum number of memory segments
+ stored in the memory segment cache. Valid range is <c>[0, 30]</c>.
+ Defaults to <c>10</c>.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Flags for Configuration of sys_alloc</title>
+ <taglist>
+ <tag><marker id="MYe"/><c>+MYe true</c></tag>
+ <item>
+ <p>Enables <c>sys_alloc</c>.</p>
+ <note>
+ <p><c>sys_alloc</c> cannot be disabled.</p>
+ </note>
+ </item>
+ <tag><marker id="MYm"/><c>+MYm libc</c></tag>
+ <item>
+ <p><c>malloc</c> library to use. Only
+ <c>libc</c> is available. <c>libc</c> enables the standard
+ <c>libc</c> <c>malloc</c> implementation. By default <c>libc</c>
+ is used.</p>
+ </item>
+ <tag><marker id="MYtt"/><c><![CDATA[+MYtt <size>]]></c></tag>
+ <item>
+ <p>Trim threshold size (in kilobytes). This is the maximum amount
+ of free memory at the top of the heap (allocated by
+ <c>sbrk</c>) that is kept by <c>malloc</c> (not
+ released to the operating system). When the amount of free
+ memory at the top of the heap exceeds the trim threshold,
+ <c>malloc</c> releases it (by calling <c>sbrk</c>).
+ Trim threshold is specified in kilobytes.
+ Defaults to <c>128</c>.</p>
+ <note>
+ <p>This flag has effect only when the emulator is linked with
+ the GNU C library, and uses its <c>malloc</c> implementation.</p>
+ </note>
+ </item>
+ <tag><marker id="MYtp"/><c><![CDATA[+MYtp <size>]]></c></tag>
+ <item>
+ <p>Top pad size (in kilobytes). This is the amount of extra
+ memory that is allocated by <c>malloc</c> when
+ <c>sbrk</c> is called to get more memory from the operating
+ system. Defaults to <c>0</c>.</p>
+ <note>
+ <p>This flag has effect only when the emulator is linked with
+ the GNU C library, and uses its <c>malloc</c> implementation.</p>
+ </note>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Flags for Configuration of Allocators Based on alloc_util</title>
+ <p>If <c>u</c> is used as subsystem identifier (that is,
+ <c><![CDATA[<S> = u]]></c>), all allocators based on
+ <c>alloc_util</c> are effected. If <c>B</c>, <c>D</c>, <c>E</c>,
+ <c>F</c>, <c>H</c>, <c>L</c>, <c>R</c>, <c>S</c>, or <c>T</c> is used
+ as subsystem identifier, only the specific allocator identifier is
+ effected.</p>
+
+ <taglist>
+ <tag><marker id="M_acul"/><c><![CDATA[+M<S>acul <utilization>|de]]></c>
+ </tag>
+ <item>
+ <p>Abandon carrier utilization limit. A valid
+ <c><![CDATA[<utilization>]]></c> is an integer in the range
+ <c>[0, 100]</c> representing utilization in percent. When a
+ utilization value &gt; 0 is used, allocator instances
+ are allowed to abandon multiblock carriers. If <c>de</c> (default
+ enabled) is passed instead of a <c><![CDATA[<utilization>]]></c>,
+ a recomended non-zero utilization value is used. The value
+ chosen depends on the allocator type and can be changed between
+ ERTS versions. Defaults to <c>de</c>, but this
+ can be changed in the future.</p>
+ <p>Carriers are abandoned when
+ memory utilization in the allocator instance falls below the
+ utilization value used. Once a carrier is abandoned, no new
+ allocations are made in it. When an allocator instance gets an
+ increased multiblock carrier need, it first tries to fetch an
+ abandoned carrier from an allocator instance of the same
+ allocator type. If no abandoned carrier can be fetched, it
+ creates a new empty carrier. When an abandoned carrier has been
+ fetched, it will function as an ordinary carrier. This feature has
+ special requirements on the
+ <seealso marker="#M_as">allocation strategy</seealso> used. Only
+ the strategies <c>aoff</c>, <c>aoffcbf</c>, and <c>aoffcaobf</c>
+ support abandoned carriers.</p>
+ <p>This feature also requires
+ <seealso marker="#M_t">multiple thread specific instances</seealso>
+ to be enabled. When enabling this feature, multiple thread-specific
+ instances are enabled if not already enabled, and the
+ <c>aoffcbf</c> strategy is enabled if the current strategy does not
+ support abandoned carriers. This feature can be enabled on all
+ allocators based on the <c>alloc_util</c> framework, except
+ <c>temp_alloc</c> (which would be pointless).</p>
+ </item>
+ <tag><marker id="M_as"/>
+ <c><![CDATA[+M<S>as bf|aobf|aoff|aoffcbf|aoffcaobf|gf|af]]></c></tag>
+ <item>
+ <p>Allocation strategy. The following strategies are valid:</p>
+ <list type="bulleted">
+ <item><c>bf</c> (best fit)</item>
+ <item><c>aobf</c> (address order best fit)</item>
+ <item><c>aoff</c> (address order first fit)</item>
+ <item><c>aoffcbf</c> (address order first fit carrier best fit)
+ </item>
+ <item><c>aoffcaobf</c> (address order first fit carrier address
+ order best fit)</item>
+ <item><c>gf</c> (good fit)</item>
+ <item><c>af</c> (a fit)</item>
+ </list>
+ <p>See the description of allocation strategies in section
+ <seealso marker="#strategy">The alloc_util Framework</seealso>.</p>
+ </item>
+ <tag><marker id="M_asbcst"/><c><![CDATA[+M<S>asbcst <size>]]></c></tag>
+ <item>
+ <p>Absolute singleblock carrier shrink threshold (in
+ kilobytes). When a block located in an
+ <c>mseg_alloc</c> singleblock carrier is shrunk, the carrier
+ is left unchanged if the amount of unused memory is less
+ than this threshold, otherwise the carrier is shrunk.
+ See also <seealso marker="#M_rsbcst"><c>rsbcst</c></seealso>.</p>
+ </item>
+ <tag><marker id="M_e"/><c><![CDATA[+M<S>e true|false]]></c></tag>
+ <item>
+ <p>Enables allocator <c><![CDATA[<S>]]></c>.</p>
+ </item>
+ <tag><marker id="M_lmbcs"/><c><![CDATA[+M<S>lmbcs <size>]]></c></tag>
+ <item>
+ <p>Largest (<c>mseg_alloc</c>) multiblock carrier size (in kilobytes).
+ See the description on how sizes for <c>mseg_alloc</c> multiblock
+ carriers are decided in section
+ <seealso marker="#mseg_mbc_sizes">
+ The alloc_util Framework</seealso>. On
+ 32-bit Unix style OS this limit cannot be set &gt; 128 MB.</p>
+ </item>
+ <tag><marker id="M_mbcgs"/><c><![CDATA[+M<S>mbcgs <ratio>]]></c></tag>
+ <item>
+ <p>(<c>mseg_alloc</c>) multiblock carrier growth stages.
+ See the description on how sizes for <c>mseg_alloc</c> multiblock
+ carriers are decided in section
+ <seealso marker="#mseg_mbc_sizes">
+ The alloc_util Framework</seealso>.</p>
+ </item>
+ <tag><marker id="M_mbsd"/><c><![CDATA[+M<S>mbsd <depth>]]></c></tag>
+ <item>
+ <p>Maximum block search depth. This flag has effect only if the
+ good fit strategy is selected for allocator
+ <c><![CDATA[<S>]]></c>. When the good fit strategy is used, free
+ blocks are placed in segregated free-lists. Each free-list
+ contains blocks of sizes in a specific range. The maxiumum block
+ search depth sets a limit on the maximum number of blocks to
+ inspect in a free-list during a search for suitable block
+ satisfying the request.</p>
+ </item>
+ <tag><marker id="M_mmbcs"/><c><![CDATA[+M<S>mmbcs <size>]]></c></tag>
+ <item>
+ <p>Main multiblock carrier size. Sets the size of the main
+ multiblock carrier for allocator <c><![CDATA[<S>]]></c>. The main
+ multiblock carrier is allocated through <c><![CDATA[sys_alloc]]></c>
+ and is never deallocated.</p>
+ </item>
+ <tag><marker id="M_mmmbc"/><c><![CDATA[+M<S>mmmbc <amount>]]></c></tag>
+ <item>
+ <p>Maximum <c>mseg_alloc</c> multiblock carriers. Maximum number of
+ multiblock carriers allocated through <c>mseg_alloc</c> by
+ allocator <c><![CDATA[<S>]]></c>. When this limit is reached,
+ new multiblock carriers are allocated through
+ <c>sys_alloc</c>.</p>
+ </item>
+ <tag><marker id="M_mmsbc"/><c><![CDATA[+M<S>mmsbc <amount>]]></c></tag>
+ <item>
+ <p>Maximum <c>mseg_alloc</c> singleblock carriers. Maximum number of
+ singleblock carriers allocated through <c>mseg_alloc</c> by
+ allocator <c><![CDATA[<S>]]></c>. When this limit is reached,
+ new singleblock carriers are allocated through
+ <c>sys_alloc</c>.</p>
+ </item>
+ <tag><marker id="M_ramv"/><c><![CDATA[+M<S>ramv <bool>]]></c></tag>
+ <item>
+ <p>Realloc always moves. When enabled, reallocate operations are
+ more or less translated into an allocate, copy, free sequence.
+ This often reduces memory fragmentation, but costs performance.</p>
+ </item>
+ <tag><marker id="M_rmbcmt"/><c><![CDATA[+M<S>rmbcmt <ratio>]]></c></tag>
+ <item>
+ <p>Relative multiblock carrier move threshold (in percent). When
+ a block located in a multiblock carrier is shrunk,
+ the block is moved if the ratio of the size of the returned
+ memory compared to the previous size is more than this threshold,
+ otherwise the block is shrunk at the current location.</p>
+ </item>
+ <tag><marker id="M_rsbcmt"/><c><![CDATA[+M<S>rsbcmt <ratio>]]></c></tag>
+ <item>
+ <p>Relative singleblock carrier move threshold (in percent). When
+ a block located in a singleblock carrier is shrunk to
+ a size smaller than the value of parameter
+ <seealso marker="#M_sbct"><c>sbct</c></seealso>,
+ the block is left unchanged in the singleblock carrier if
+ the ratio of unused memory is less than this threshold,
+ otherwise it is moved into a multiblock carrier.</p>
+ </item>
+ <tag><marker id="M_rsbcst"/><c><![CDATA[+M<S>rsbcst <ratio>]]></c></tag>
+ <item>
+ <p>Relative singleblock carrier shrink threshold (in
+ percent). When a block located in an <c>mseg_alloc</c>
+ singleblock carrier is shrunk, the carrier is left
+ unchanged if the ratio of unused memory is less than this
+ threshold, otherwise the carrier is shrunk.
+ See also <seealso marker="#M_asbcst"><c>asbcst</c></seealso>.</p>
+ </item>
+ <tag><marker id="M_sbct"/><c><![CDATA[+M<S>sbct <size>]]></c></tag>
+ <item>
+ <p>Singleblock carrier threshold. Blocks larger than this
+ threshold are placed in singleblock carriers. Blocks
+ smaller than this threshold are placed in multiblock
+ carriers. On 32-bit Unix style OS this threshold cannot be set
+ &gt; 8 MB.</p>
+ </item>
+ <tag><marker id="M_smbcs"/><c><![CDATA[+M<S>smbcs <size>]]></c></tag>
+ <item>
+ <p>Smallest (<c>mseg_alloc</c>) multiblock carrier size (in
+ kilobytes). See the description on how sizes for <c>mseg_alloc</c>
+ multiblock carriers are decided in section
+ <seealso marker="#mseg_mbc_sizes">
+ The alloc_util Framework</seealso>.</p>
+ </item>
+ <tag><marker id="M_t"/><c><![CDATA[+M<S>t true|false]]></c></tag>
+ <item>
+ <p>Multiple, thread-specific instances of the allocator.
+ This option has only effect on the runtime system
+ with SMP support. Default behavior on the runtime system with
+ SMP support is <c>NoSchedulers+1</c> instances. Each scheduler
+ uses a lock-free instance of its own and other threads use
+ a common instance.</p>
+ <p>Before ERTS 5.9 it was possible to configure
+ a smaller number of thread-specific instances than schedulers.
+ This is, however, not possible anymore.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Flags for Configuration of alloc_util</title>
+ <p>All allocators based on <c>alloc_util</c> are effected.</p>
+
+ <taglist>
+ <tag><marker id="Muycs"/><c><![CDATA[+Muycs <size>]]></c></tag>
+ <item>
+ <p><c>sys_alloc</c> carrier size. Carriers allocated through
+ <c>sys_alloc</c> are allocated in sizes that are
+ multiples of the <c>sys_alloc</c> carrier size. This is not
+ true for main multiblock carriers and carriers allocated
+ during a memory shortage, though.</p>
+ </item>
+ <tag><marker id="Mummc"/><c><![CDATA[+Mummc <amount>]]></c></tag>
+ <item>
+ <p>Maximum <c>mseg_alloc</c> carriers. Maximum number of carriers
+ placed in separate memory segments. When this limit is
+ reached, new carriers are placed in memory retrieved from
+ <c>sys_alloc</c>.</p>
+ </item>
+ <tag><marker id="Musac"/><c><![CDATA[+Musac <bool>]]></c></tag>
+ <item>
+ <p>Allow <c>sys_alloc</c> carriers. Defaults to <c>true</c>.
+ If set to <c>false</c>, <c>sys_alloc</c> carriers are never
+ created by allocators using the <c>alloc_util</c> framework.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Special Flag for literal_alloc</title>
<taglist>
- <tag><c>min</c></tag>
+ <tag><marker id="MIscs"/><c><![CDATA[+MIscs <size in MB>]]></c></tag>
<item>
- Disables all allocators that can be disabled.
- </item>
+ <p><c>literal_alloc</c> super carrier size (in MB). The amount of
+ <em>virtual</em> address space reserved for literal terms in
+ Erlang code on 64-bit architectures. Defaults to <c>1024</c>
+ (that is, 1 GB), which is usually sufficient.
+ The flag is ignored on 32-bit architectures.</p>
+ </item>
+ </taglist>
+ </section>
- <tag><c>max</c></tag>
+ <section>
+ <title>Special Flag for exec_alloc</title>
+ <taglist>
+ <tag><marker id="MXscs"/><c><![CDATA[+MXscs <size in MB>]]></c></tag>
<item>
- Enables all allocators (currently default).
- </item>
+ <p><c>exec_alloc</c> super carrier size (in MB). The amount of
+ <em>virtual</em> address space reserved for native executable code
+ used by the <seealso marker="hipe:HiPE_app"><c>HiPE</c></seealso> application
+ on specific architectures (x86_64). Defaults to <c>512</c>.</p>
+ </item>
+ </taglist>
+ </section>
- <tag><c>r9c|r10b|r11b</c></tag>
+ <section>
+ <title>Instrumentation Flags</title>
+ <taglist>
+ <tag><marker id="Mim"/><c>+Mim true|false</c></tag>
<item>
- Configures all allocators as they were configured in respective
- OTP release. These will eventually be removed.
- </item>
+ <p>A map over current allocations is kept by the emulator.
+ The allocation map can be retrieved through module
+ <seealso marker="tools:instrument">
+ <c>instrument(3)</c></seealso>. <c>+Mim true</c>
+ implies <c>+Mis true</c>. <c>+Mim true</c> is the same as flag
+ <seealso marker="erl#instr"><c>-instr</c></seealso> in
+ <c>erl(1)</c>.</p>
+ </item>
+ <tag><marker id="Mis"/><c>+Mis true|false</c></tag>
+ <item>
+ <p>Status over allocated memory is kept by the emulator.
+ The allocation status can be retrieved through module
+ <seealso marker="tools:instrument">
+ <c>instrument(3)</c></seealso>.</p>
+ </item>
+ <tag><marker id="Mit"/><c>+Mit X</c></tag>
+ <item>
+ <p>Reserved for future use. Do <em>not</em> use this flag.</p>
+ </item>
+ </taglist>
- <tag><c>config</c></tag>
+ <note>
+ <p>When instrumentation of the emulator is enabled, the emulator
+ uses more memory and runs slower.</p>
+ </note>
+ </section>
+
+ <section>
+ <title>Other Flags</title>
+ <taglist>
+ <tag><marker id="Mea"/><c>+Mea min|max|r9c|r10b|r11b|config</c></tag>
+ <item>
+ <p>Options:</p>
+ <taglist>
+ <tag><c>min</c></tag>
+ <item>
+ <p>Disables all allocators that can be disabled.</p>
+ </item>
+ <tag><c>max</c></tag>
+ <item>
+ <p>Enables all allocators (default).</p>
+ </item>
+ <tag><c>r9c|r10b|r11b</c></tag>
+ <item>
+ <p>Configures all allocators as they were configured in respective
+ Erlang/OTP release. These will eventually be removed.</p>
+ </item>
+ <tag><c>config</c></tag>
+ <item>
+ <p>Disables features that cannot be enabled while creating an
+ allocator configuration with
+ <seealso marker="runtime_tools:erts_alloc_config">
+ <c>erts_alloc_config(3)</c></seealso>.</p>
+ <note>
+ <p>This option is to be used only while running
+ <c>erts_alloc_config(3)</c>, <em>not</em> when
+ using the created configuration.</p>
+ </note>
+ </item>
+ </taglist>
+ </item>
+ <tag><marker id="Mlpm"/><c>+Mlpm all|no</c></tag>
<item>
- Disables features that cannot be enabled while creating an
- allocator configuration with
- <seealso marker="runtime_tools:erts_alloc_config">erts_alloc_config(3)</seealso>.
- Note, this option should only be used while running
- <c>erts_alloc_config</c>, <em>not</em> when using the created
- configuration.
+ <p>Lock physical memory. Defaults to <c>no</c>, that is,
+ no physical memory is locked. If set to <c>all</c>, all
+ memory mappings made by the runtime system are locked into
+ physical memory. If set to <c>all</c>, the runtime system fails to
+ start if this feature is not supported, the user has not got enough
+ privileges, or the user is not allowed to lock enough physical
+ memory. The runtime system also fails with an out of memory
+ condition if the user limit on the amount of locked memory is
+ reached.</p>
</item>
</taglist>
- </item>
- <tag><marker id="Mlpm"/><c>+Mlpm all|no</c></tag>
- <item>Lock physical memory. The default value is <c>no</c>, i.e.,
- no physical memory will be locked. If set to <c>all</c>, all
- memory mappings made by the runtime system, will be locked into
- physical memory. If set to <c>all</c>, the runtime system will fail
- to start if this feature is not supported, the user has not got enough
- privileges, or the user is not allowed to lock enough physical memory.
- The runtime system will also fail with an out of memory condition
- if the user limit on the amount of locked memory is reached.
- </item>
- </taglist>
- <p>Only some default values have been presented
- here.
- <seealso marker="erts:erlang#system_info_allocator">erlang:system_info(allocator)</seealso>,
- and
- <seealso marker="erts:erlang#system_info_allocator_tuple">erlang:system_info({allocator, Alloc})</seealso>
- can be used in order to obtain currently used settings and current
- status of the allocators.</p>
+ </section>
+ </section>
+
+ <section>
+ <title>Notes</title>
+ <p>Only some default values have been presented here. For information
+ about the currently used settings and the current status of the
+ allocators, see
+ <seealso marker="erts:erlang#system_info_allocator">
+ <c>erlang:system_info(allocator)</c></seealso> and
+ <seealso marker="erts:erlang#system_info_allocator_tuple">
+ <c>erlang:system_info({allocator, Alloc})</c></seealso>.</p>
+
<note>
- <p>Most of these flags are highly implementation dependent, and they
- may be changed or removed without prior notice.</p>
+ <p>Most of these flags are highly implementation-dependent and
+ can be changed or removed without prior notice.</p>
<p><c>erts_alloc</c> is not obliged to strictly use the settings that
- have been passed to it (it may even ignore them).</p>
+ have been passed to it (it can even ignore them).</p>
</note>
- <p><seealso marker="runtime_tools:erts_alloc_config">erts_alloc_config(3)</seealso>
- is a tool that can be used to aid creation of an
+
+ <p>The <seealso marker="runtime_tools:erts_alloc_config">
+ <c>erts_alloc_config(3)</c></seealso>
+ tool can be used to aid creation of an
<c>erts_alloc</c> configuration that is suitable for a limited
number of runtime scenarios.</p>
</section>
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="runtime_tools:erts_alloc_config">erts_alloc_config(3)</seealso>,
- <seealso marker="erl">erl(1)</seealso>,
- <seealso marker="tools:instrument">instrument(3)</seealso>,
- <seealso marker="erts:erlang">erlang(3)</seealso></p>
+ <title>See Also</title>
+ <p><seealso marker="erl"><c>erl(1)</c></seealso>,
+ <seealso marker="erlang"><c>erlang(3)</c></seealso>,
+ <seealso marker="runtime_tools:erts_alloc_config">
+ <c>erts_alloc_config(3)</c></seealso>,
+ <seealso marker="tools:instrument">
+ <c>instrument(3)</c></seealso></p>
</section>
</cref>
diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml
index f12f76890c..1d5d280338 100644
--- a/erts/doc/src/escript.xml
+++ b/erts/doc/src/escript.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>2007</year><year>2015</year>
+ <year>2007</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,17 +33,18 @@
<comsummary>Erlang scripting support</comsummary>
<description>
<p><c>escript</c> provides support for running short Erlang programs
- without having to compile them first and an easy way to retrieve the
- command line arguments.</p>
+ without having to compile them first, and an easy way to retrieve the
+ command-line arguments.</p>
</description>
+
<funcs>
<func>
<name>script-name script-arg1 script-arg2...</name>
<name>escript escript-flags script-name script-arg1 script-arg2...</name>
- <fsummary>Run a script written in Erlang</fsummary>
+ <fsummary>Run a script written in Erlang.</fsummary>
<desc>
<p><c>escript</c> runs a script written in Erlang.</p>
- <p>Here follows an example.</p>
+ <p>Example:</p>
<pre>
$ <input>chmod u+x factorial</input>
$ <input>cat factorial</input>
@@ -73,195 +74,183 @@ factorial 5 = 120
$ <input>./factorial</input>
usage: factorial integer
$ <input>./factorial five</input>
-usage: factorial integer
- </pre>
+usage: factorial integer</pre>
<p>The header of the Erlang script in the example differs from
- a normal Erlang module. The first line is intended to be the
- interpreter line, which invokes <c>escript</c>. However if you
- invoke the <c>escript</c> like this</p>
+ a normal Erlang module. The first line is intended to be the
+ interpreter line, which invokes <c>escript</c>.</p>
+ <p>However, if you invoke the <c>escript</c> as follows,
+ the contents of the first line does not matter, but it
+ cannot contain Erlang code as it will be ignored:</p>
<pre>
-$ <input>escript factorial 5</input> </pre>
- <p>the contents of the first line does not matter, but it
- cannot contain Erlang code as it will be ignored.</p>
- <p>The second line in the example, contains an optional
- directive to the <c>Emacs</c> editor which causes it to
+$ <input>escript factorial 5</input></pre>
+ <p>The second line in the example contains an optional
+ directive to the <c>Emacs</c> editor, which causes it to
enter the major mode for editing Erlang source files. If the
- directive is present it must be located on the second
+ directive is present, it must be located on the second
line.</p>
-
- <p>If there is a comment selecting the <seealso
- marker="stdlib:epp#encoding">encoding</seealso> it can be
+ <p>If a comment selecting the <seealso
+ marker="stdlib:epp#encoding">encoding</seealso> exists, it can be
located on the second line.</p>
-
- <note><p>
- The encoding specified by the above mentioned comment
- applies to the script itself. The encoding of the
- I/O-server, however, has to be set explicitly like this:</p>
-<code>io:setopts([{encoding, unicode}])</code>
+ <note>
+ <p>The encoding specified by the above mentioned comment
+ applies to the script itself. The encoding of the
+ I/O-server, however, must be set explicitly as follows:</p>
+ <code>
+io:setopts([{encoding, unicode}])</code>
<p>The default encoding of the I/O-server for <c>standard_io</c>
- is <c>latin1</c>
- since the script runs in a non-interactive terminal
- (see <seealso marker="stdlib:unicode_usage#unicode_options_summary">
- Using Unicode in Erlang</seealso>).
- </p></note>
-
- <p>On the third line (or second line depending on the presence
- of the Emacs directive), it is possible to give arguments to
- the emulator, such as </p>
+ is <c>latin1</c>, as the script runs in a non-interactive terminal
+ (see section
+ <seealso marker="stdlib:unicode_usage#unicode_options_summary">
+ Summary of Options</seealso>) in the STDLIB User's Guide.</p>
+ </note>
+ <p>On the third line (or second line depending on the presence
+ of the Emacs directive), arguments can be specified to
+ the emulator, for example:</p>
<pre>
%%! -smp enable -sname factorial -mnesia debug verbose</pre>
<p>Such an argument line must start with <c>%%!</c> and the
- rest of the line will interpreted as arguments to the emulator.</p>
+ remaining line is interpreted as arguments to the emulator.</p>
<p>If you know the location of the <c>escript</c> executable, the first
- line can directly give the path to <c>escript</c>. For instance:</p>
+ line can directly give the path to <c>escript</c>, for example:</p>
<pre>
-#!/usr/local/bin/escript </pre>
- <p>As any other kind of scripts, Erlang scripts will not work on
+#!/usr/local/bin/escript</pre>
+ <p>As any other type of scripts, Erlang scripts do not work on
Unix platforms if the execution bit for the script file is not set.
- (Use <c>chmod +x script-name</c> to turn on the execution bit.)
- </p>
-
- <p>The rest of the Erlang script file may either contain
- Erlang <c>source code</c>, an <c>inlined beam file</c> or an
- <c>inlined archive file</c>.</p>
-
- <p>An Erlang script file must always contain the function
- <em>main/1</em>. When the script is run, the
- <c>main/1</c> function will be called with a list
- of strings representing the arguments given to the script (not
- changed or interpreted in any way).</p>
-
+ (To turn on the execution bit, use <c>chmod +x script-name</c>.)</p>
+ <p>The remaining Erlang script file can either contain
+ Erlang <em>source code</em>, an <em>inlined beam file</em>, or an
+ <em>inlined archive file</em>.</p>
+ <p>An Erlang script file must always contain the <c>main/1</c>
+ function. When the script is run, the
+ <c>main/1</c> function is called with a list
+ of strings representing the arguments specified to the script (not
+ changed or interpreted in any way).</p>
<p>If the <c>main/1</c> function in the script returns successfully,
- the exit status for the script will be 0. If an exception is generated
- during execution, a short message will be printed and the script terminated
- with exit status 127.</p>
-
- <p>To return your own non-zero exit code, call <c>halt(ExitCode)</c>;
- for instance:</p>
+ the exit status for the script is <c>0</c>. If an exception is
+ generated during execution, a short message is printed and the script
+ terminates with exit status <c>127</c>.</p>
+ <p>To return your own non-zero exit code, call <c>halt(ExitCode)</c>,
+ for example:</p>
<pre>
halt(1).</pre>
-
- <p>Call <seealso marker="#script_name_0">escript:script_name()</seealso>
- from your to script to retrieve the pathname of the script
- (the pathname is usually, but not always, absolute).</p>
-
+ <p>To retrieve the pathname of the script, call
+ <seealso marker="#script_name_0">
+ <c>escript:script_name()</c></seealso> from your script
+ (the pathname is usually, but not always, absolute).</p>
<p>If the file contains source code (as in the example above),
- it will be processed by the preprocessor <c>epp</c>. This
- means that you for example may use pre-defined macros (such as
- <c>?MODULE</c>) as well as include directives like
- the <c>-include_lib</c> directive. For instance, use</p>
+ it is processed by the
+ <seealso marker="stdlib:epp"><c>epp</c></seealso> preprocessor.
+ This means that you, for example, can use predefined macros
+ (such as <c>?MODULE</c>) and include directives like
+ the <c>-include_lib</c> directive. For example, use</p>
<pre>
-include_lib("kernel/include/file.hrl").</pre>
- <p>to include the record definitions for the records used by the
- <c>file:read_link_info/1</c> function. You can also select
- encoding by including a encoding comment here, but if there
- is a valid encoding comment on the second line it takes
+ <p>to include the record definitions for the records used by function
+ <seealso marker="kernel:file#read_link_info/1">
+ <c>file:read_link_info/1</c></seealso>. You can also select
+ encoding by including an encoding comment here, but if
+ a valid encoding comment exists on the second line, it takes
precedence.</p>
-
- <p>The script will be checked for syntactic and semantic
- correctness before being run. If there are warnings (such as
- unused variables), they will be printed and the script will
- still be run. If there are errors, they will be printed and
- the script will not be run and its exit status will be
- 127.</p>
-
+ <p>The script is checked for syntactic and semantic
+ correctness before it is run. If there are warnings (such as
+ unused variables), they are printed and the script will
+ still be run. If there are errors, they are printed and
+ the script will not be run and its exit status is
+ <c>127</c>.</p>
<p>Both the module declaration and the export declaration of
the <c>main/1</c> function are optional.</p>
-
<p>By default, the script will be interpreted. You can force
it to be compiled by including the following line somewhere
- in the script file:</p><pre>
+ in the script file:</p>
+ <pre>
-mode(compile).</pre>
-
<p>Execution of interpreted code is slower than compiled code.
- If much of the execution takes place in interpreted code it
- may be worthwhile to compile it, even though the compilation
- itself will take a little while. It is also possible to supply
- <c>native</c> instead of <c>compile</c>, this will compile the script
- using the native flag, again depending on the characteristics
- of the escript this could or could not be worth while.</p>
-
- <p>As mentioned earlier, it is possible to have a script which
+ If much of the execution takes place in interpreted code, it
+ can be worthwhile to compile it, although the compilation
+ itself takes a little while. Also, <c>native</c> can be supplied
+ instead of <c>compile</c>. This compiles the script
+ using the native flag and may or may not be worthwhile
+ depending on the escript characteristics.</p>
+ <p>As mentioned earlier, a script can
contains precompiled <c>beam</c> code. In a precompiled
- script, the interpretation of the script header is exactly
- the same as in a script containing source code. That means
+ script, the interpretation of the script header is
+ the same as in a script containing source code. This means
that you can make a <c>beam</c> file executable by
prepending the file with the lines starting with <c>#!</c>
and <c>%%!</c> mentioned above. In a precompiled script, the
- function
- <c>main/1</c> must be exported.</p>
-
- <p>As yet another option it is possible to have an entire
- Erlang archive in the script. In a archive script, the
- interpretation of the script header is exactly the same as
- in a script containing source code. That means that you can
+ <c>main/1</c> function must be exported.</p>
+ <p>Another option is to have an entire
+ Erlang archive in the script. In an archive script, the
+ interpretation of the script header is the same as
+ in a script containing source code. This means that you can
make an archive file executable by prepending the file with
the lines starting with <c>#!</c> and <c>%%!</c> mentioned
- above. In an archive script, the function <c>main/1</c> must
+ above. In an archive script, the <c>main/1</c> function must
be exported. By default the <c>main/1</c> function in the
module with the same name as the basename of the
- <c>escript</c> file will be invoked. This behavior can be
- overridden by setting the flag <c>-escript main Module</c>
- as one of the emulator flags. The <c>Module</c> must be the
- name of a module which has an exported <c>main/1</c>
- function. See <seealso marker="kernel:code">code(3)</seealso>
- for more information about archives and code loading.</p>
-
- <p>In many cases it is very convenient to have a header in
- the escript, especially on Unix platforms. But the header is
- in fact optional. This means that you directly can "execute"
- an Erlang module, beam file or archive file without adding
- any header to them. But then you have to invoke the script
- like this:</p>
- <pre>
+ <c>escript</c> file is invoked. This behavior can be
+ overridden by setting flag <c>-escript main Module</c>
+ as one of the emulator flags. <c>Module</c> must be the
+ name of a module that has an exported <c>main/1</c>
+ function. For more information about archives and code loading, see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
+ <p>It is often very convenient to have a header in
+ the escript, especially on Unix platforms. However, the header
+ is optional, so you directly can "execute"
+ an Erlang module, Beam file, or archive file without adding
+ any header to them. But then you have to invoke the script
+ as follows:</p>
+ <pre>
$ <input>escript factorial.erl 5</input>
factorial 5 = 120
$ <input>escript factorial.beam 5</input>
factorial 5 = 120
$ <input>escript factorial.zip 5</input>
-factorial 5 = 120
-</pre>
+factorial 5 = 120</pre>
</desc>
</func>
+
<func>
- <name>escript:create(FileOrBin, Sections) -> ok | {ok, binary()} | {error, term()}</name>
- <fsummary>Create an escript</fsummary>
+ <name>escript:create(FileOrBin, Sections) -> ok | {ok, binary()} |
+ {error, term()}</name>
+ <fsummary>Create an escript.</fsummary>
<type>
<v>FileOrBin = filename() | 'binary'</v>
<v>Sections = [Header] Body | Body</v>
<v>Header = shebang | {shebang, Shebang}
- | comment | {comment, Comment}
- | {emu_args, EmuArgs}</v>
+ &nbsp;&nbsp;&nbsp;| comment | {comment, Comment}
+ &nbsp;&nbsp;&nbsp;| {emu_args, EmuArgs}</v>
<v>Shebang = string() | 'default' | 'undefined'</v>
<v>Comment = string() | 'default' | 'undefined'</v>
<v>EmuArgs = string() | 'undefined'</v>
- <v>Body = {source, SourceCode}
- | {beam, BeamCode}
- | {archive, ZipArchive}
- | {archive, ZipFiles, ZipOptions}</v>
+ <v>Body = {source, SourceCode} | {beam, BeamCode}
+ &nbsp;&nbsp;&nbsp;| {archive, ZipArchive}
+ &nbsp;&nbsp;&nbsp;| {archive, ZipFiles, ZipOptions}</v>
<v>SourceCode = BeamCode = file:filename() | binary()</v>
- <v>ZipArchive = <seealso marker="stdlib:zip#type-filename">zip:filename()</seealso> | binary()</v>
+ <v>ZipArchive = <seealso marker="stdlib:zip#type-filename">
+ zip:filename()</seealso> | binary()</v>
<v>ZipFiles = [ZipFile]</v>
- <v>ZipFile = file:filename() | {file:filename(), binary()} | {file:filename(), binary(), file:file_info()}</v>
- <v>ZipOptions = [<seealso marker="stdlib:zip#type-create_option">zip:create_option()</seealso>]</v>
+ <v>ZipFile = file:filename()
+ &nbsp;&nbsp;&nbsp;| {file:filename(), binary()}
+ &nbsp;&nbsp;&nbsp;| {file:filename(), binary(), file:file_info()}</v>
+ <v>ZipOptions = [<seealso marker="stdlib:zip#type-create_option">
+ zip:create_option()</seealso>]</v>
</type>
<desc>
- <p>The <marker id="create_2"></marker> <c>create/2</c>
- function creates an escript from a list of sections. The
- sections can be given in any order. An escript begins with an
- optional <c>Header</c> followed by a mandatory <c>Body</c>. If
- the header is present, it does always begin with a
- <c>shebang</c>, possibly followed by a <c>comment</c> and
- <c>emu_args</c>. The <c>shebang</c> defaults to
- <c>"/usr/bin/env escript"</c>. The comment defaults to
- <c>"This is an -*- erlang -*- file"</c>. The created escript
- can either be returned as a binary or written to file.</p>
-
- <p>As an example of how the function can be used, we create an
- interpreted escript which uses <c>emu_args</c> to set some emulator
- flag. In this case it happens to disable the smp_support. We
- do also extract the different sections from the newly created
- script:</p>
+ <p><marker id="create_2"></marker>
+ Creates an escript from a list of sections. The
+ sections can be specified in any order. An escript begins with an
+ optional <c>Header</c> followed by a mandatory <c>Body</c>. If
+ the header is present, it does always begin with a
+ <c>shebang</c>, possibly followed by a <c>comment</c> and
+ <c>emu_args</c>. The <c>shebang</c> defaults to
+ <c>"/usr/bin/env escript"</c>. The <c>comment</c> defaults to
+ <c>"This is an -*- erlang -*- file"</c>. The created escript
+ can either be returned as a binary or written to file.</p>
+ <p>As an example of how the function can be used, we create an
+ interpreted escript that uses <c>emu_args</c> to set some emulator
+ flag. In this case, it happens to disable the <c>smp_support</c>. We
+ also extract the different sections from the newly created script:</p>
<pre>
&gt; <input>Source = "%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_support)).\n".</input>
"%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_support)).\n"
@@ -280,11 +269,9 @@ ok
"false"
&gt; <input>escript:extract("demo.escript", []).</input>
{ok,[{shebang,default}, {comment,default}, {emu_args,"-smp disable"},
- {source,&lt;&lt;"%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_su"...&gt;&gt;}]}
- </pre>
-
- <p>An escript without header can be created like this:</p>
-<pre>
+ {source,&lt;&lt;"%% Demo\nmain(_Args) ->\n io:format(erlang:system_info(smp_su"...&gt;&gt;}]}</pre>
+ <p>An escript without header can be created as follows:</p>
+ <pre>
&gt; <input>file:write_file("demo.erl",
["%% demo.erl\n-module(demo).\n-export([main/1]).\n\n", Source]).</input>
ok
@@ -299,14 +286,12 @@ ok
{beam,&lt;&lt;70,79,82,49,0,0,3,68,66,69,65,77,65,116,
111,109,0,0,0,83,0,0,0,9,...&gt;&gt;}]}
&gt; <input>os:cmd("escript demo.beam").</input>
-"true"
-</pre>
- <p>Here we create an archive script containing both Erlang
- code as well as beam code. Then we iterate over all files in
- the archive and collect their contents and some info about
- them.
- </p>
-<pre>
+"true"</pre>
+ <p>Here we create an archive script containing both Erlang
+ code and Beam code, then we iterate over all files in
+ the archive and collect their contents and some information about
+ them:</p>
+ <pre>
&gt; <input>{ok, SourceCode} = file:read_file("demo.erl").</input>
{ok,&lt;&lt;"%% demo.erl\n-module(demo).\n-export([main/1]).\n\n%% Demo\nmain(_Arg"...&gt;&gt;}
&gt; <input>escript:create("demo.escript",
@@ -339,43 +324,43 @@ ok
&lt;&lt;"%% demo.erl\n-module(demo).\n-export([main/1]).\n\n%% Demo\nmain(_Arg"...&gt;&gt;}]}</pre>
</desc>
</func>
+
<func>
- <name>escript:extract(File, Options) -> {ok, Sections} | {error, term()}</name>
- <fsummary>Parses an escript and extracts its sections</fsummary>
+ <name>escript:extract(File, Options) -> {ok, Sections} |
+ {error, term()}</name>
+ <fsummary>Parse an escript and extract its sections.</fsummary>
<type>
<v>File = filename()</v>
<v>Options = [] | [compile_source]</v>
<v>Sections = Headers Body</v>
<v>Headers = {shebang, Shebang}
- {comment, Comment}
- {emu_args, EmuArgs}</v>
+ {comment, Comment}
+ {emu_args, EmuArgs}</v>
<v>Shebang = string() | 'default' | 'undefined'</v>
<v>Comment = string() | 'default' | 'undefined'</v>
<v>EmuArgs = string() | 'undefined'</v>
<v>Body = {source, SourceCode}
- | {source, BeamCode}
- | {beam, BeamCode}
- | {archive, ZipArchive}</v>
+ &nbsp;&nbsp;&nbsp;| {source, BeamCode}
+ &nbsp;&nbsp;&nbsp;| {beam, BeamCode}
+ &nbsp;&nbsp;&nbsp;| {archive, ZipArchive}</v>
<v>SourceCode = BeamCode = ZipArchive = binary()</v>
</type>
<desc>
- <p>The <marker id="extract_2"></marker> <c>extract/2</c>
- function parses an escript and extracts its sections. This is
- the reverse of <c>create/2</c>.</p>
-
- <p>All sections are returned even if they do not exist in the
- escript. If a particular section happens to have the same
- value as the default value, the extracted value is set to the
- atom <c>default</c>. If a section is missing, the extracted
- value is set to the atom <c>undefined</c>. </p>
-
- <p>The <c>compile_source</c> option only affects the result if
- the escript contains <c>source</c> code. In that case the
- Erlang code is automatically compiled and <c>{source,
- BeamCode}</c> is returned instead of <c>{source,
- SourceCode}</c>.</p>
-
- <pre>
+ <p><marker id="extract_2"></marker>
+ Parses an escript and extracts its sections. This is the reverse
+ of <seealso marker="#create_2"><c>create/2</c></seealso>.</p>
+ <p>All sections are returned even if they do not exist in the
+ escript. If a particular section happens to have the same
+ value as the default value, the extracted value is set to the
+ atom <c>default</c>. If a section is missing, the extracted
+ value is set to the atom <c>undefined</c>.</p>
+ <p>Option <c>compile_source</c> only affects the result if
+ the escript contains <c>source</c> code. In this case the
+ Erlang code is automatically compiled and <c>{source,
+ BeamCode}</c> is returned instead of <c>{source,
+ SourceCode}</c>.</p>
+ <p>Example:</p>
+ <pre>
&gt; <input>escript:create("demo.escript",
[shebang, {archive, [{"demo.erl", SourceCode},
{"demo.beam", BeamCode}], []}]).</input>
@@ -385,52 +370,51 @@ ok
escript:extract("demo.escript", []).</input>
{ok,[{{archive,&lt;&lt;80,75,3,4,20,0,0,0,8,0,118,7,98,60,105,
152,61,93,107,0,0,0,118,0,...&gt;&gt;}
- {emu_args,undefined}]}
- </pre>
+ {emu_args,undefined}]}</pre>
</desc>
</func>
+
<func>
<name>escript:script_name() -> File</name>
- <fsummary>Returns the name of an escript</fsummary>
+ <fsummary>Return the name of an escript.</fsummary>
<type>
<v>File = filename()</v>
</type>
<desc>
- <p>The <marker id="script_name_0"></marker>
- <c>script_name/0</c> function returns the name of the escript
- being executed. If the function is invoked outside the context
- of an escript, the behavior is undefined.</p>
+ <p><marker id="script_name_0"></marker>
+ Returns the name of the escript that is executed.
+ If the function is invoked outside the context
+ of an escript, the behavior is undefined.</p>
</desc>
</func>
</funcs>
<section>
- <title>Options accepted by escript</title>
+ <title>Options Accepted By escript</title>
<taglist>
- <tag>-c</tag>
- <item>Compile the escript regardless of the value of the mode attribute.
+ <tag><c>-c</c></tag>
+ <item>Compiles the escript regardless of the value of the mode attribute.
</item>
-
- <tag>-d</tag>
- <item>Debug the escript. Starts the debugger, loads the module
- containing the <c>main/1</c> function into the debugger, sets a
- breakpoint in <c>main/1</c> and invokes <c>main/1</c>. If the
- module is precompiled, it must be explicitly compiled with the
- <c>debug_info</c> option.
+ <tag><c>-d</c></tag>
+ <item>Debugs the escript. Starts the debugger, loads the module
+ containing the <c>main/1</c> function into the debugger, sets a
+ breakpoint in <c>main/1</c>, and invokes <c>main/1</c>. If the
+ module is precompiled, it must be explicitly compiled with option
+ <c>debug_info</c>.
</item>
-
- <tag>-i</tag>
- <item>Interpret the escript regardless of the value of the mode attribute.
+ <tag><c>-i</c></tag>
+ <item>Interprets the escript regardless of the value of the mode
+ attribute.
+ </item>
+ <tag><c>-s</c></tag>
+ <item>Performs a syntactic and semantic check of the script file.
+ Warnings and errors (if any) are written to the standard output, but
+ the script will not be run. The exit status is <c>0</c> if any errors
+ are found, otherwise <c>127</c>.
+ </item>
+ <tag><c>-n</c></tag>
+ <item>Compiles the escript using flag <c>+native</c>.
</item>
-
- <tag>-s</tag>
- <item>Only perform a syntactic and semantic check of the script file.
- Warnings and errors (if any) are written to the standard output, but
- the script will not be run. The exit status will be 0 if there were
- no errors, and 127 otherwise.</item>
-
- <tag>-n</tag>
- <item>Compile the escript using the +native flag.</item>
</taglist>
</section>
</comref>
diff --git a/erts/doc/src/inet_cfg.xml b/erts/doc/src/inet_cfg.xml
index 027fe600d7..0cfcc7905d 100644
--- a/erts/doc/src/inet_cfg.xml
+++ b/erts/doc/src/inet_cfg.xml
@@ -22,7 +22,7 @@
</legalnotice>
- <title>Inet configuration</title>
+ <title>Inet Configuration</title>
<prepared>Peter Andersson</prepared>
<docno></docno>
<date>2004-03-02</date>
@@ -32,374 +32,341 @@
<section>
<title>Introduction</title>
- <p>This chapter tells you how the Erlang runtime system is configured
- for IP communication. It also explains how you may configure it
- for your own particular needs by means of a configuration file.
- The information here is mainly intended for users with special
- configuration needs or problems. There should normally be no need
- for specific settings for Erlang to function properly on a correctly
- IP configured platform. </p>
- <p>When Erlang starts up it will read the kernel variable
- <c><![CDATA[inetrc]]></c> which, if defined, should specify the location and
- name of a user configuration file. Example:</p>
- <p><c><![CDATA[% erl -kernel inetrc '"./cfg_files/erl_inetrc"']]></c></p>
- <p>Note that the usage of a <c><![CDATA[.inetrc]]></c> file, which was
- supported in earlier Erlang versions, is now obsolete.</p>
- <p>A second way to specify the configuration file is to set the
- environment variable <c><![CDATA[ERL_INETRC]]></c> to the full name of the file. Example (bash):</p>
- <p><c><![CDATA[% export ERL_INETRC=./cfg_files/erl_inetrc]]></c></p>
- <p>Note that the kernel variable <c><![CDATA[inetrc]]></c> overrides this environment variable.</p>
+ <p>This section describes how the Erlang runtime system is configured
+ for IP communication. It also explains how you can configure it
+ for your needs by a configuration file.
+ The information is primarily intended for users with special
+ configuration needs or problems. There is normally no need
+ for specific settings for Erlang to function properly on a correctly
+ IP-configured platform.</p>
+
+ <p>When Erlang starts up it reads the Kernel variable
+ <c><![CDATA[inetrc]]></c>, which, if defined, is to specify the location
+ and name of a user configuration file. Example:</p>
+
+ <code type="none"><![CDATA[
+% erl -kernel inetrc '"./cfg_files/erl_inetrc"']]></code>
+
+ <p>Notice that the use of an <c><![CDATA[.inetrc]]></c> file, which was
+ supported in earlier Erlang/OTP versions, is now obsolete.</p>
+
+ <p>A second way to specify the configuration file is to set
+ environment variable <c><![CDATA[ERL_INETRC]]></c> to the full name of
+ the file. Example (bash):</p>
+
+ <code type="none"><![CDATA[
+% export ERL_INETRC=./cfg_files/erl_inetrc]]></code>
+
+ <p>Notice that the Kernel variable <c><![CDATA[inetrc]]></c>
+ overrides this environment variable.</p>
+
<p>If no user configuration file is specified and Erlang is started
- in non-distributed or short name distributed mode, Erlang will use
- default configuration settings and a native lookup method that should
- work correctly under most circumstances. Erlang
- will not read any information from system inet configuration files
- (like /etc/host.conf, /etc/nsswitch.conf, etc) in these modes,
- except for /etc/resolv.conf and /etc/hosts that is read and monitored
- for changes on Unix platforms for the internal DNS client
- <seealso marker="kernel:inet_res">inet_res</seealso>.</p>
+ in non-distributed or short name distributed mode, Erlang uses
+ default configuration settings and a native lookup method that
+ works correctly under most circumstances. Erlang reads no
+ information from system <c>inet</c> configuration files (such as
+ <c>/etc/host.conf</c> and <c>/etc/nsswitch.conf</c>) in these modes,
+ except for <c>/etc/resolv.conf</c> and <c>/etc/hosts</c> that is read and
+ monitored for changes on Unix platforms for the internal DNS client
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.</p>
+
<p>If Erlang is started in long name distributed mode, it needs to
- get the domain name from somewhere and will read system inet
+ get the domain name from somewhere and reads system <c>inet</c>
configuration files for this information. Any hosts and resolver
- information found then is also recorded, but not
- used as long as Erlang is configured for native lookups. (The
+ information found is also recorded, but not
+ used as long as Erlang is configured for native lookups. The
information becomes useful if the lookup method is changed to
- <c><![CDATA['file']]></c> or <c><![CDATA['dns']]></c>, see below).</p>
- <p>Native lookup (system calls) is always the default resolver method. This
- is true for all platforms except VxWorks and OSE Delta where <c><![CDATA['file']]></c>
- or <c><![CDATA['dns']]></c> is used (in that order of priority).</p>
- <p>On Windows platforms, Erlang will search the system registry rather than
- look for configuration files when started in long name distributed mode. </p>
+ <c><![CDATA['file']]></c> or <c><![CDATA['dns']]></c>, see below.</p>
+
+ <p>Native lookup (system calls) is always the default resolver method.
+ This is true for all platforms, except VxWorks and OSE Delta where
+ <c><![CDATA['file']]></c> or <c><![CDATA['dns']]></c> is used (in that
+ priority order).</p>
+
+ <p>On Windows platforms, Erlang searches the system registry rather than
+ looks for configuration files when started in long name distributed
+ mode.</p>
</section>
<section>
<title>Configuration Data</title>
<p>Erlang records the following data in a local database if found in system
- inet configuration files (or system registry):</p>
- <list type="bulleted">
- <item>Host names and addresses</item>
+ <c>inet</c> configuration files (or system registry):</p>
+
+ <list type="bulleted">
+ <item>Hostnames and host addresses</item>
<item>Domain name</item>
<item>Nameservers</item>
<item>Search domains</item>
<item>Lookup method</item>
</list>
- <p>This data may also be specified explicitly in the user
- configuration file. The configuration file should contain lines
- of configuration parameters (each terminated with a full
- stop). Some parameters add data to the configuration (e.g. host
+
+ <p>This data can also be specified explicitly in the user
+ configuration file. This file is to contain lines
+ of configuration parameters (each terminated with a full stop).
+ Some parameters add data to the configuration (such as host
and nameserver), others overwrite any previous settings
- (e.g. domain and lookup). The user configuration file is always
+ (such as domain and lookup). The user configuration file is always
examined last in the configuration process, making it possible
for the user to override any default values or previously made
settings. Call <c><![CDATA[inet:get_rc()]]></c> to view the state of the
- inet configuration database.</p>
- <p>These are the valid configuration parameters:</p>
- <p></p>
+ <c>inet</c> configuration database.</p>
+
+ <p>The valid configuration parameters are as follows:</p>
+
<taglist>
- <tag><em><c><![CDATA[{file, Format, File}.]]></c></em></tag>
+ <tag><c><![CDATA[{file, Format, File}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Format = atom()]]></c></p>
<p><c><![CDATA[File = string()]]></c></p>
- <p></p>
- <p>Specify a system file that Erlang should read configuration
- data from. <c><![CDATA[Format]]></c> tells the parser how the file should be
- interpreted: <c><![CDATA[resolv]]></c> (Unix resolv.conf), <c><![CDATA[host_conf_freebsd]]></c>
- (FreeBSD host.conf), <c><![CDATA[host_conf_bsdos]]></c> (BSDOS host.conf),
- <c><![CDATA[host_conf_linux]]></c> (Linux host.conf), <c><![CDATA[nsswitch_conf]]></c>
- (Unix nsswitch.conf) or <c><![CDATA[hosts]]></c> (Unix hosts). <c><![CDATA[File]]></c> should
- specify the name of the file with full path.</p>
- <p></p>
+ <p>Specify a system file that Erlang is to read configuration data from.
+ <c><![CDATA[Format]]></c> tells the parser how the file is to be
+ interpreted:</p>
+ <list type="bulleted">
+ <item><c><![CDATA[resolv]]></c> (Unix resolv.conf)</item>
+ <item><c><![CDATA[host_conf_freebsd]]></c> (FreeBSD host.conf)</item>
+ <item><c><![CDATA[host_conf_bsdos]]></c> (BSDOS host.conf)</item>
+ <item><c><![CDATA[host_conf_linux]]></c> (Linux host.conf)</item>
+ <item><c><![CDATA[nsswitch_conf]]></c> (Unix nsswitch.conf)</item>
+ <item><c><![CDATA[hosts]]></c> (Unix hosts)</item>
+ </list>
+ <p><c><![CDATA[File]]></c> is to specify the filename with full
+ path.</p>
</item>
- <tag><em><c><![CDATA[{resolv_conf, File}.]]></c></em></tag>
+ <tag><c><![CDATA[{resolv_conf, File}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[File = string()]]></c></p>
- <p></p>
- <p>Specify a system file that Erlang should read resolver
+ <p>Specify a system file that Erlang is to read resolver
configuration from for the internal DNS client
- <seealso marker="kernel:inet_res">inet_res</seealso>,
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>,
and monitor for changes, even if it does not exist.
The path must be absolute.</p>
- <p>This may override the configuration parameters
+ <p>This can override the configuration parameters
<c><![CDATA[nameserver]]></c> and
<c><![CDATA[search]]></c> depending on the contents
- of the specified file. They may also change any time in the future
+ of the specified file. They can also change any time in the future
reflecting the file contents.</p>
- <p>If the file is specified as an empty string "",
- no file is read nor monitored in the future. This emulates
- the old behaviour of not configuring the DNS client when
+ <p>If the file is specified as an empty string <c>""</c>,
+ no file is read or monitored in the future. This emulates
+ the old behavior of not configuring the DNS client when
the node is started in short name distributed mode.</p>
- <p>If this parameter is not specified it defaults to
- <c><![CDATA[/etc/resolv.conf]]></c> unless the environment variable
- <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set which defines
+ <p>If this parameter is not specified, it defaults to
+ <c><![CDATA[/etc/resolv.conf]]></c> unless environment variable
+ <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set, which defines
the directory for this file to some maybe other than
<c><![CDATA[/etc]]></c>.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[{hosts_file, File}.]]></c></em></tag>
+ <tag><c><![CDATA[{hosts_file, File}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[File = string()]]></c></p>
- <p></p>
- <p>Specify a system file that Erlang should read resolver
- configuration from for the internal hosts file resolver
+ <p>Specify a system file that Erlang is to read resolver
+ configuration from for the internal hosts file resolver,
and monitor for changes, even if it does not exist.
The path must be absolute.</p>
<p>These host entries are searched after all added with
<c>{file, hosts, File}</c> above or
- <c>{host, IP, Aliases}</c> below when the lookup option
+ <c>{host, IP, Aliases}</c> below when lookup option
<c>file</c> is used.</p>
- <p>If the file is specified as an empty string "",
- no file is read nor monitored in the future. This emulates
- the old behaviour of not configuring the DNS client when
+ <p>If the file is specified as an empty string <c>""</c>,
+ no file is read or monitored in the future. This emulates
+ the old behavior of not configuring the DNS client when
the node is started in short name distributed mode.</p>
- <p>If this parameter is not specified it defaults to
- <c><![CDATA[/etc/hosts]]></c> unless the environment variable
- <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set which defines
+ <p>If this parameter is not specified, it defaults to
+ <c><![CDATA[/etc/hosts]]></c> unless environment variable
+ <c><![CDATA[ERL_INET_ETC_DIR]]></c> is set, which defines
the directory for this file to some maybe other than
<c><![CDATA[/etc]]></c>.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[{registry, Type}.]]></c></em></tag>
+ <tag><c><![CDATA[{registry, Type}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Type = atom()]]></c></p>
- <p></p>
- <p>Specify a system registry that Erlang should read configuration
- data from. Currently, <c><![CDATA[win32]]></c> is the only valid option.</p>
- <p></p>
+ <p>Specify a system registry that Erlang is to read configuration
+ data from. <c><![CDATA[win32]]></c> is the only valid option.</p>
</item>
- <tag><em><c><![CDATA[{host, IP, Aliases}.]]></c></em></tag>
+ <tag><c><![CDATA[{host, IP, Aliases}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[IP = tuple()]]></c></p>
<p><c><![CDATA[Aliases = [string()]]]></c></p>
- <p></p>
<p>Add host entry to the hosts table.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[{domain, Domain}.]]></c></em></tag>
+ <tag><c><![CDATA[{domain, Domain}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Domain = string()]]></c></p>
- <p></p>
<p>Set domain name.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[{nameserver, IP [,Port]}.]]></c></em></tag>
+ <tag><c><![CDATA[{nameserver, IP [,Port]}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[IP = tuple()]]></c></p>
<p><c><![CDATA[Port = integer()]]></c></p>
- <p></p>
- <p>Add address (and port, if other than default) of primary
+ <p>Add address (and port, if other than default) of the primary
nameserver to use for
- <seealso marker="kernel:inet_res">inet_res</seealso>.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
+ </p>
</item>
- <tag><em><c><![CDATA[{alt_nameserver, IP [,Port]}.]]></c></em></tag>
+ <tag><c><![CDATA[{alt_nameserver, IP [,Port]}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[IP = tuple()]]></c></p>
<p><c><![CDATA[Port = integer()]]></c></p>
- <p></p>
- <p>Add address (and port, if other than default) of secondary
+ <p>Add address (and port, if other than default) of the secondary
nameserver for
- <seealso marker="kernel:inet_res">inet_res</seealso>.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
+ </p>
</item>
- <tag><em><c><![CDATA[{search, Domains}.]]></c></em></tag>
+ <tag><c><![CDATA[{search, Domains}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Domains = [string()]]]></c></p>
- <p></p>
<p>Add search domains for
- <seealso marker="kernel:inet_res">inet_res</seealso>.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
+ </p>
</item>
- <tag><em><c><![CDATA[{lookup, Methods}.]]></c></em></tag>
+ <tag><c><![CDATA[{lookup, Methods}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Methods = [atom()]]]></c></p>
- <p></p>
- <p>Specify lookup methods and in which order to try them.
- The valid methods are: <c><![CDATA[native]]></c> (use system calls),
- <c><![CDATA[file]]></c> (use host data retrieved from
- system configuration files and/or
- the user configuration file) or <c><![CDATA[dns]]></c>
- (use the Erlang DNS client
- <seealso marker="kernel:inet_res">inet_res</seealso>
- for nameserver queries).</p>
+ <p>Specify lookup methods and in which order to try them.
+ The valid methods are as follows:</p>
+ <list type="bulleted">
+ <item><c><![CDATA[native]]></c> (use system calls)</item>
+ <item><c><![CDATA[file]]></c> (use host data retrieved from system
+ configuration files and/or the user configuration file)</item>
+ <item><c><![CDATA[dns]]></c> (use the Erlang DNS client
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ for nameserver queries)</item>
+ </list>
<p>The lookup method <c><![CDATA[string]]></c> tries to
- parse the hostname as a IPv4 or IPv6 string and return
+ parse the hostname as an IPv4 or IPv6 string and return
the resulting IP address. It is automatically tried
first when <c><![CDATA[native]]></c> is <em>not</em>
- in the <c><![CDATA[Methods]]></c> list. To skip it in this case
+ in the <c><![CDATA[Methods]]></c> list. To skip it in this case,
the pseudo lookup method <c><![CDATA[nostring]]></c> can be
inserted anywhere in the <c><![CDATA[Methods]]></c> list.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[{cache_size, Size}.]]></c></em></tag>
+ <tag><c><![CDATA[{cache_size, Size}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Size = integer()]]></c></p>
- <p></p>
- <p>Set size of resolver cache. Default is 100 DNS records.</p>
- <p></p>
+ <p>Set the resolver cache size. Defaults to 100 DNS records.</p>
</item>
- <tag><em><c><![CDATA[{cache_refresh, Time}.]]></c></em></tag>
- <item>
- <p></p>
+ <tag><c><![CDATA[{cache_refresh, Time}.]]></c></tag>
+ <item>
<p><c><![CDATA[Time = integer()]]></c></p>
- <p></p>
- <p>Set how often (in millisec)
- the resolver cache for
- <seealso marker="kernel:inet_res">inet_res</seealso>.
- is refreshed (i.e. expired DNS records are deleted).
- Default is 1 h.</p>
- <p></p>
+ <p>Set how often (in milliseconds) the resolver cache for
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ is refreshed (that is, expired DNS records are deleted).
+ Defaults to 1 hour.</p>
</item>
- <tag><em><c><![CDATA[{timeout, Time}.]]></c></em></tag>
+ <tag><c><![CDATA[{timeout, Time}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Time = integer()]]></c></p>
- <p></p>
- <p>Set the time to wait until retry (in millisec) for DNS queries
+ <p>Set the time to wait until retry (in milliseconds) for DNS queries
made by
- <seealso marker="kernel:inet_res">inet_res</seealso>.
- Default is 2 sec.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>.
+ Defaults to 2 seconds.</p>
</item>
- <tag><em><c><![CDATA[{retry, N}.]]></c></em></tag>
+ <tag><c><![CDATA[{retry, N}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[N = integer()]]></c></p>
- <p></p>
<p>Set the number of DNS queries
- <seealso marker="kernel:inet_res">inet_res</seealso>
- will try before giving up.
- Default is 3.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ will try before giving up. Defaults to 3.</p>
</item>
- <tag><em><c><![CDATA[{inet6, Bool}.]]></c></em></tag>
+ <tag><c><![CDATA[{inet6, Bool}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Bool = true | false]]></c></p>
- <p></p>
- <p>Tells the DNS client
- <seealso marker="kernel:inet_res">inet_res</seealso>
- to look up IPv6 addresses. Default is false.</p>
- <p></p>
+ <p>Tells the DNS client
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ to look up IPv6 addresses. Defaults to <c>false</c>.</p>
</item>
- <tag><em><c><![CDATA[{usevc, Bool}.]]></c></em></tag>
+ <tag><c><![CDATA[{usevc, Bool}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Bool = true | false]]></c></p>
- <p></p>
- <p>Tells the DNS client
- <seealso marker="kernel:inet_res">inet_res</seealso>
- to use TCP (Virtual Circuit) instead of UDP. Default is false.</p>
- <p></p>
+ <p>Tells the DNS client
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ to use TCP (Virtual Circuit) instead of UDP. Defaults to
+ <c>false</c>.</p>
</item>
- <tag><em><c><![CDATA[{edns, Version}.]]></c></em></tag>
+ <tag><c><![CDATA[{edns, Version}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Version = false | 0]]></c></p>
- <p></p>
<p>Sets the EDNS version that
- <seealso marker="kernel:inet_res">inet_res</seealso>
- will use. The only allowed is zero. Default is false
- which means to not use EDNS.</p>
- <p></p>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
+ will use. The only allowed version is zero. Defaults to <c>false</c>,
+ which means not to use EDNS.</p>
</item>
- <tag><em><c><![CDATA[{udp_payload_size, Size}.]]></c></em></tag>
+ <tag><c><![CDATA[{udp_payload_size, Size}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[N = integer()]]></c></p>
- <p></p>
<p>Sets the allowed UDP payload size
- <seealso marker="kernel:inet_res">inet_res</seealso>
+ <seealso marker="kernel:inet_res"><c>inet_res(3)</c></seealso>
will advertise in EDNS queries. Also sets the limit
when the DNS query will be deemed too large for UDP
- forcing a TCP query instead, which is not entirely
- correct since the advertised UDP payload size of the
- individual nameserver is what should be used,
+ forcing a TCP query instead; this is not entirely
+ correct, as the advertised UDP payload size of the
+ individual nameserver is what is to be used,
but this simple strategy will do until a more intelligent
- (probing, caching) algorithm need be implemented.
- The default is 1280 which stems from the
- standard Ethernet MTU size.</p>
- <p></p>
+ (probing, caching) algorithm needs to be implemented.
+ Default to 1280, which stems from the standard Ethernet MTU size.</p>
</item>
- <tag><em><c><![CDATA[{udp, Module}.]]></c></em></tag>
+ <tag><c><![CDATA[{udp, Module}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Module = atom()]]></c></p>
- <p></p>
- <p>Tell Erlang to use other primitive UDP module than inet_udp.</p>
- <p></p>
+ <p>Tell Erlang to use another primitive UDP module than
+ <c>inet_udp</c>.</p>
</item>
- <tag><em><c><![CDATA[{tcp, Module}.]]></c></em></tag>
+ <tag><c><![CDATA[{tcp, Module}.]]></c></tag>
<item>
- <p></p>
<p><c><![CDATA[Module = atom()]]></c></p>
- <p></p>
- <p>Tell Erlang to use other primitive TCP module than inet_tcp.</p>
- <p></p>
+ <p>Tell Erlang to use another primitive TCP module than
+ <c>inet_tcp</c>.</p>
</item>
- <tag><em><c><![CDATA[clear_hosts.]]></c></em></tag>
+ <tag><c><![CDATA[clear_hosts.]]></c></tag>
<item>
- <p></p>
<p>Clear the hosts table.</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[clear_ns.]]></c></em></tag>
+ <tag><c><![CDATA[clear_ns.]]></c></tag>
<item>
- <p></p>
<p>Clear the list of recorded nameservers (primary and secondary).</p>
- <p></p>
</item>
- <tag><em><c><![CDATA[clear_search.]]></c></em></tag>
+ <tag><c><![CDATA[clear_search.]]></c></tag>
<item>
- <p></p>
<p>Clear the list of search domains.</p>
- <p></p>
</item>
</taglist>
</section>
<section>
<title>User Configuration Example</title>
- <p>Here follows a user configuration example.</p>
- <p>Assume a user does not want Erlang to use the native lookup method,
- but wants Erlang to read all information necessary from start and use
- that for resolving names and addresses. In case lookup fails, Erlang
- should request the data from a nameserver (using the Erlang
+ <p>Assume that a user does not want Erlang to use the native lookup method,
+ but wants Erlang to read all information necessary from start and use
+ that for resolving names and addresses. If lookup fails, Erlang
+ is to request the data from a nameserver (using the Erlang
DNS client, set to use EDNS allowing larger responses).
- The resolver configuration will be updated when
- its configuration file changes, furthermore, DNS records
- should never be cached. The user configuration file
+ The resolver configuration is updated when
+ its configuration file changes. Also, DNS records
+ are never to be cached. The user configuration file
(in this example named <c><![CDATA[erl_inetrc]]></c>, stored
- in directory <c><![CDATA[./cfg_files]]></c>) could then look like this
+ in directory <c><![CDATA[./cfg_files]]></c>) can then look as follows
(Unix):</p>
+
<pre>
- %% -- ERLANG INET CONFIGURATION FILE --
- %% read the hosts file
- {file, hosts, "/etc/hosts"}.
- %% add a particular host
- {host, {134,138,177,105}, ["finwe"]}.
- %% do not monitor the hosts file
- {hosts_file, ""}.
- %% read and monitor nameserver config from here
- {resolv_conf, "/usr/local/etc/resolv.conf"}.
- %% enable EDNS
- {edns,0}.
- %% disable caching
- {cache_size, 0}.
- %% specify lookup method
- {lookup, [file, dns]}.</pre>
- <p>And Erlang could, for example, be started like this:</p>
- <p><c><![CDATA[% erl -sname my_node -kernel inetrc '"./cfg_files/erl_inetrc"']]></c></p>
+%% -- ERLANG INET CONFIGURATION FILE --
+%% read the hosts file
+{file, hosts, "/etc/hosts"}.
+%% add a particular host
+{host, {134,138,177,105}, ["finwe"]}.
+%% do not monitor the hosts file
+{hosts_file, ""}.
+%% read and monitor nameserver config from here
+{resolv_conf, "/usr/local/etc/resolv.conf"}.
+%% enable EDNS
+{edns,0}.
+%% disable caching
+{cache_size, 0}.
+%% specify lookup method
+{lookup, [file, dns]}.</pre>
+
+ <p>And Erlang can, for example, be started as follows:</p>
+
+ <code type="none"><![CDATA[
+% erl -sname my_node -kernel inetrc '"./cfg_files/erl_inetrc"']]></code>
</section>
</chapter>
diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml
index a88a815ef6..c14f0a558d 100644
--- a/erts/doc/src/init.xml
+++ b/erts/doc/src/init.xml
@@ -19,7 +19,7 @@
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>init</title>
@@ -30,128 +30,140 @@
<file>init.xml</file>
</header>
<module>init</module>
- <modulesummary>Coordination of System Startup</modulesummary>
+ <modulesummary>Coordination of system startup.</modulesummary>
<description>
- <p>The <c>init</c> module is pre-loaded and contains the code for
- the <c>init</c> system process which coordinates the start-up of
- the system. The first function evaluated at start-up is
- <c>boot(BootArgs)</c>, where <c>BootArgs</c> is a list of command
- line arguments supplied to the Erlang runtime system from
- the local operating system. See
- <seealso marker="erts:erl">erl(1)</seealso>.</p>
- <p><c>init</c> reads the boot script which contains instructions on
- how to initiate the system. See
- <seealso marker="sasl:script">script(4)</seealso> for more
- information about boot scripts.</p>
+ <p>This module is preloaded and contains the code for
+ the <c>init</c> system process that coordinates the startup of
+ the system. The first function evaluated at startup is
+ <c>boot(BootArgs)</c>, where <c>BootArgs</c> is a list of
+ command-line arguments supplied to the Erlang runtime system from
+ the local operating system; see
+ <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p>
+
+ <p><c>init</c> reads the boot script, which contains instructions on
+ how to initiate the system. For more information about boot scripts, see
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>.</p>
+
<p><c>init</c> also contains functions to restart, reboot, and stop
the system.</p>
</description>
+
<funcs>
<func>
<name name="boot" arity="1"/>
- <fsummary>Start the Erlang runtime system</fsummary>
+ <fsummary>Start the Erlang runtime system.</fsummary>
<desc>
<p>Starts the Erlang runtime system. This function is called
- when the emulator is started and coordinates system start-up.</p>
- <p><c><anno>BootArgs</anno></c> are all command line arguments except
- the emulator flags, that is, flags and plain arguments. See
- <seealso marker="erts:erl">erl(1)</seealso>.</p>
- <p><c>init</c> itself interprets some of the flags, see
- <seealso marker="#flags">Command Line Flags</seealso> below.
+ when the emulator is started and coordinates system startup.</p>
+ <p><c><anno>BootArgs</anno></c> are all command-line arguments except
+ the emulator flags, that is, flags and plain arguments; see
+ <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p>
+ <p><c>init</c> interprets some of the flags, see section
+ <seealso marker="#flags">Command-Line Flags</seealso> below.
The remaining flags ("user flags") and plain arguments are
passed to the <c>init</c> loop and can be retrieved by calling
- <c>get_arguments/0</c> and <c>get_plain_arguments/0</c>,
- respectively.</p>
+ <seealso marker="#get_arguments/0"><c>get_arguments/0</c></seealso>
+ and <seealso marker="#get_plain_arguments/0">
+ <c>get_plain_arguments/0</c></seealso>, respectively.</p>
</desc>
</func>
+
<func>
<name name="get_argument" arity="1"/>
- <fsummary>Get the values associated with a command line user flag</fsummary>
+ <fsummary>Get the values associated with a command-line user flag.
+ </fsummary>
<desc>
- <p>Returns all values associated with the command line user flag
- <c><anno>Flag</anno></c>. If <c><anno>Flag</anno></c> is provided several times, each
- <c><anno>Values</anno></c> is returned in preserved order.</p>
+ <p>Returns all values associated with the command-line user flag
+ <c><anno>Flag</anno></c>. If <c><anno>Flag</anno></c> is provided
+ several times, each <c><anno>Values</anno></c> is returned in
+ preserved order. Example:</p>
<pre>
% <input>erl -a b c -a d</input>
...
1> <input>init:get_argument(a).</input>
{ok,[["b","c"],["d"]]}</pre>
- <p>There are also a number of flags, which are defined
+ <p>The following flags are defined
automatically and can be retrieved using this function:</p>
<taglist>
<tag><c>root</c></tag>
<item>
- <p>The installation directory of Erlang/OTP, <c>$ROOT</c>.</p>
+ <p>The installation directory of Erlang/OTP, <c>$ROOT</c>:</p>
<pre>
2> <input>init:get_argument(root).</input>
{ok,[["/usr/local/otp/releases/otp_beam_solaris8_r10b_patched"]]}</pre>
</item>
<tag><c>progname</c></tag>
<item>
- <p>The name of the program which started Erlang.</p>
+ <p>The name of the program which started Erlang:</p>
<pre>
3> <input>init:get_argument(progname).</input>
{ok,[["erl"]]}</pre>
</item>
<tag><c>home</c></tag>
<item>
- <p>The home directory.</p>
+ <p>The home directory:</p>
<pre>
4> <input>init:get_argument(home).</input>
{ok,[["/home/harry"]]}</pre>
</item>
</taglist>
- <p>Returns <c>error</c> if there is no value associated with
- <c>Flag</c>.</p>
+ <p>Returns <c>error</c> if no value is associated with <c>Flag</c>.</p>
</desc>
</func>
+
<func>
<name name="get_arguments" arity="0"/>
- <fsummary>Get all command line user flags</fsummary>
+ <fsummary>Get all command-line user flags.</fsummary>
<desc>
- <p>Returns all command line flags, as well as the system
- defined flags, see <c>get_argument/1</c>.</p>
+ <p>Returns all command-line flags and the system-defined flags, see
+ <seealso marker="#get_argument/1"><c>get_argument/1</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="get_plain_arguments" arity="0"/>
- <fsummary>Get all non-flag command line arguments</fsummary>
+ <fsummary>Get all non-flag command-line arguments.</fsummary>
<desc>
- <p>Returns any plain command line arguments as a list of strings
+ <p>Returns any plain command-line arguments as a list of strings
(possibly empty).</p>
</desc>
</func>
+
<func>
<name name="get_status" arity="0"/>
- <fsummary>Get system status information</fsummary>
+ <fsummary>Get system status information.</fsummary>
<type name="internal_status"/>
<desc>
<p>The current status of the <c>init</c> process can be
inspected. During system startup (initialization),
<c><anno>InternalStatus</anno></c> is <c>starting</c>, and
- <c><anno>ProvidedStatus</anno></c> indicates how far the boot script has
- been interpreted. Each <c>{progress, Info}</c> term
- interpreted in the boot script affects <c><anno>ProvidedStatus</anno></c>,
- that is, <c><anno>ProvidedStatus</anno></c> gets the value of <c>Info</c>.</p>
+ <c><anno>ProvidedStatus</anno></c> indicates how far the boot
+ script has been interpreted. Each <c>{progress, Info}</c> term
+ interpreted in the boot script affects
+ <c><anno>ProvidedStatus</anno></c>, that is,
+ <c><anno>ProvidedStatus</anno></c> gets the value of <c>Info</c>.</p>
</desc>
</func>
+
<func>
<name name="reboot" arity="0"/>
- <fsummary>Take down and restart an Erlang node smoothly</fsummary>
+ <fsummary>Take down and restart an Erlang node smoothly.</fsummary>
<desc>
<p>All applications are taken down smoothly, all code is
unloaded, and all ports are closed before the system
- terminates. If the <c>-heart</c> command line flag was given,
- the <c>heart</c> program will try to reboot the system. Refer
- to <c>heart(3)</c> for more information.</p>
+ terminates. If command-line flag <c>-heart</c> was specified,
+ the <c>heart</c> program tries to reboot the system. For more
+ information, see
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso>.</p>
<p>To limit the shutdown time, the time <c>init</c> is allowed
- to spend taking down applications, the <c>-shutdown_time</c>
- command line flag should be used.</p>
+ to spend taking down applications, command-line flag
+ <c>-shutdown_time</c> is to be used.</p>
</desc>
</func>
+
<func>
<name name="restart" arity="0"/>
- <fsummary>Restart the running Erlang node</fsummary>
+ <fsummary>Restart the running Erlang node.</fsummary>
<desc>
<p>The system is restarted <em>inside</em> the running Erlang
node, which means that the emulator is not restarted. All
@@ -160,80 +172,91 @@
the same way as initially started. The same <c>BootArgs</c>
are used again.</p>
<p>To limit the shutdown time, the time <c>init</c> is allowed
- to spend taking down applications, the <c>-shutdown_time</c>
- command line flag should be used.</p>
+ to spend taking down applications, command-line flag
+ <c>-shutdown_time</c> is to be used.</p>
</desc>
</func>
+
<func>
<name name="script_id" arity="0"/>
- <fsummary>Get the identity of the used boot script</fsummary>
+ <fsummary>Get the identity of the used boot script.</fsummary>
<desc>
- <p>Get the identity of the boot script used to boot the system.
+ <p>Gets the identity of the boot script used to boot the system.
<c><anno>Id</anno></c> can be any Erlang term. In the delivered boot
- scripts, <c><anno>Id</anno></c> is <c>{Name, Vsn}</c>. <c>Name</c> and
- <c>Vsn</c> are strings.</p>
+ scripts, <c><anno>Id</anno></c> is <c>{Name, Vsn}</c>. <c>Name</c>
+ and <c>Vsn</c> are strings.</p>
</desc>
</func>
+
<func>
<name name="stop" arity="0"/>
- <fsummary>Take down an Erlang node smoothly</fsummary>
+ <fsummary>Take down an Erlang node smoothly.</fsummary>
<desc>
<p>The same as
<seealso marker="#stop/1"><c>stop(0)</c></seealso>.</p>
</desc>
</func>
+
<func>
<name name="stop" arity="1"/>
- <fsummary>Take down an Erlang node smoothly</fsummary>
+ <fsummary>Take down an Erlang node smoothly.</fsummary>
<desc>
<p>All applications are taken down smoothly, all code is
unloaded, and all ports are closed before the system
- terminates by calling <c>halt(<anno>Status</anno>)</c>. If the
- <c>-heart</c> command line flag was given, the <c>heart</c>
- program is terminated before the Erlang node
- terminates. Refer to <c>heart(3)</c> for more
- information.</p>
+ terminates by calling <c>halt(<anno>Status</anno>)</c>. If
+ command-line flag <c>-heart</c> was specified, the <c>heart</c>
+ program is terminated before the Erlang node terminates.
+ For more information, see
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso>.</p>
<p>To limit the shutdown time, the time <c>init</c> is allowed
- to spend taking down applications, the <c>-shutdown_time</c>
- command line flag should be used.</p>
+ to spend taking down applications, command-line flag
+ <c>-shutdown_time</c> is to be used.</p>
</desc>
</func>
</funcs>
<section>
<marker id="flags"></marker>
- <title>Command Line Flags</title>
- <warning><p>The support for loading of code from archive files is
- experimental. The sole purpose of releasing it before it is ready
- is to obtain early feedback. The file format, semantics,
- interfaces etc. may be changed in a future release. The
- <c>-code_path_choice</c> flag is also experimental.</p></warning>
+ <title>Command-Line Flags</title>
+ <warning>
+ <p>The support for loading of code from archive files is
+ experimental. The only purpose of releasing it before it is ready
+ is to obtain early feedback. The file format, semantics,
+ interfaces, and so on, can be changed in a future release. The
+ <c>-code_path_choice</c> flag is also experimental.</p>
+ </warning>
- <p>The <c>init</c> module interprets the following command line
- flags:</p>
+ <p>The <c>init</c> module interprets the following command-line flags:</p>
<taglist>
<tag><c>--</c></tag>
<item>
<p>Everything following <c>--</c> up to the next flag is
considered plain arguments and can be retrieved using
- <c>get_plain_arguments/0</c>.</p>
+ <seealso marker="#get_plain_arguments/0">
+ <c>get_plain_arguments/0</c></seealso>.</p>
</item>
<tag><c>-code_path_choice Choice</c></tag>
<item>
- <p>This flag can be set to <c>strict</c> or <c>relaxed</c>. It
- controls whether each directory in the code path should be
- interpreted strictly as it appears in the <c>boot script</c> or if
- <c>init</c> should be more relaxed and try to find a suitable
- directory if it can choose from a regular ebin directory and
- an ebin directory in an archive file. This flag is particular
- useful when you want to elaborate with code loading from
- archives without editing the <c>boot script</c>. See <seealso
- marker="sasl:script">script(4)</seealso> for more information
- about interpretation of boot scripts. The flag does also have
- a similar affect on how the code server works. See <seealso
- marker="kernel:code">code(3)</seealso>.</p>
-
+ <p>Can be set to <c>strict</c> or <c>relaxed</c>. It controls how each
+ directory in the code path is to be interpreted:</p>
+ <list type="bulleted">
+ <item>
+ <p>Strictly as it appears in the <c>boot script</c>, or</p>
+ </item>
+ <item>
+ <p><c>init</c> is to be more relaxed and try to find a suitable
+ directory if it can choose from a regular <c>ebin</c> directory
+ and an <c>ebin</c> directory in an archive file.</p>
+ </item>
+ </list>
+ <p>This flag is particular
+ useful when you want to elaborate with code loading from
+ archives without editing the <c>boot script</c>. For more
+ information about interpretation of boot scripts, see
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>.
+ The flag has also a similar effect on how the code server works; see
+ <seealso marker="kernel:code"><c>code(3)</c></seealso>.</p>
</item>
<tag><c>-epmd_module Module</c></tag>
<item>
@@ -242,11 +265,11 @@
</item>
<tag><c>-eval Expr</c></tag>
<item>
- <p>Scans, parses and evaluates an arbitrary expression
+ <p>Scans, parses, and evaluates an arbitrary expression
<c>Expr</c> during system initialization. If any of these
- steps fail (syntax error, parse error or exception during
- evaluation), Erlang stops with an error message. Here is an
- example that uses Erlang as a hexadecimal calculator:</p>
+ steps fail (syntax error, parse error, or exception during
+ evaluation), Erlang stops with an error message. In the following
+ example Erlang is used as a hexadecimal calculator:</p>
<pre>
% <input>erl -noshell -eval 'R = 16#1F+16#A0, io:format("~.16B~n", [R])' \\</input>
<input>-s erlang halt</input>
@@ -256,14 +279,15 @@ BF</pre>
<c>-eval</c> expressions are evaluated sequentially with
<c>-s</c> and <c>-run</c> function calls (this also in
the order specified). As with <c>-s</c> and <c>-run</c>, an
- evaluation that does not terminate, blocks the system
+ evaluation that does not terminate blocks the system
initialization process.</p>
</item>
<tag><c>-extra</c></tag>
<item>
<p>Everything following <c>-extra</c> is considered plain
arguments and can be retrieved using
- <c>get_plain_arguments/0</c>.</p>
+ <seealso marker="#get_plain_arguments/0">
+ <c>get_plain_arguments/0</c></seealso>.</p>
</item>
<tag><c>-run Mod [Func [Arg1, Arg2, ...]]</c></tag>
<item>
@@ -285,8 +309,8 @@ foo:bar()
foo:bar(["baz", "1", "2"]).</code>
<p>The functions are executed sequentially in an initialization
process, which then terminates normally and passes control to
- the user. This means that a <c>-run</c> call which does not
- return will block further processing; to avoid this, use
+ the user. This means that a <c>-run</c> call that does not
+ return blocks further processing; to avoid this, use
some variant of <c>spawn</c> in such cases.</p>
</item>
<tag><c>-s Mod [Func [Arg1, Arg2, ...]]</c></tag>
@@ -309,11 +333,11 @@ foo:bar()
foo:bar([baz, '1', '2']).</code>
<p>The functions are executed sequentially in an initialization
process, which then terminates normally and passes control to
- the user. This means that a <c>-s</c> call which does not
- return will block further processing; to avoid this, use
+ the user. This means that a <c>-s</c> call that does not
+ return blocks further processing; to avoid this, use
some variant of <c>spawn</c> in such cases.</p>
- <p>Due to the limited length of atoms, it is recommended that
- <c>-run</c> be used instead.</p>
+ <p>Because of the limited length of atoms, it is recommended to
+ use <c>-run</c> instead.</p>
</item>
</taglist>
</section>
@@ -335,9 +359,9 @@ error</pre>
</section>
<section>
- <title>SEE ALSO</title>
- <p><seealso marker="erl_prim_loader">erl_prim_loader(3)</seealso>,
- <seealso marker="kernel:heart">heart(3)</seealso></p>
+ <title>See Also</title>
+ <p><seealso marker="erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>,
+ <seealso marker="kernel:heart"><c>heart(3)</c></seealso></p>
</section>
</erlref>
diff --git a/erts/doc/src/introduction.xml b/erts/doc/src/introduction.xml
new file mode 100644
index 0000000000..790e24f9f3
--- /dev/null
+++ b/erts/doc/src/introduction.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2012</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>Introduction</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2016-05-22</date>
+ <rev>PA1</rev>
+ <file>introduction.xml</file>
+ </header>
+ <section>
+ <title>Scope</title>
+ <p>The Erlang Runtime System Application, ERTS, contains
+ functionality necessary to run the Erlang system.</p>
+ <note>
+ <p>By default, <c><![CDATA[ERTS]]></c> is only guaranteed to be
+ compatible with other Erlang/OTP components from the same release as
+ <c><![CDATA[ERTS]]></c> itself.</p>
+ <p>For information on how to communicate with Erlang/OTP components
+ from earlier releases, see the documentation of system flag
+ <seealso marker="erl#compat_rel"><c>+R</c></seealso> in <c>erl(1)</c>.
+ </p>
+ </note>
+ </section>
+
+ <section>
+ <title>Prerequisites</title>
+ <p>It is assumed that the reader is familiar with the Erlang programming
+ language.</p>
+ </section>
+</chapter>
+
diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml
index 7be3d15de6..2a14f1e47b 100644
--- a/erts/doc/src/match_spec.xml
+++ b/erts/doc/src/match_spec.xml
@@ -22,7 +22,7 @@
</legalnotice>
- <title>Match specifications in Erlang</title>
+ <title>Match Specifications in Erlang</title>
<prepared>Patrik Nyblom</prepared>
<responsible></responsible>
<docno></docno>
@@ -32,43 +32,54 @@
<rev>PA1</rev>
<file>match_spec.xml</file>
</header>
- <p>A "match specification" (match_spec) is an Erlang term describing a
- small "program" that will try to match something. It can be used
+ <p>A "match specification" (<c>match_spec</c>) is an Erlang term describing a
+ small "program" that tries to match something. It can be used
to either control tracing with
<seealso marker="erlang#trace_pattern/3">erlang:trace_pattern/3</seealso>
or to search for objects in an ETS table with for example
<seealso marker="stdlib:ets#select/2">ets:select/2</seealso>.
- The match_spec in many ways works like a small function in Erlang, but is
- interpreted/compiled by the Erlang runtime system to something much more
- efficient than calling an Erlang function. The match_spec is also
+ The match specification in many ways works like a small function in Erlang,
+ but is interpreted/compiled by the Erlang runtime system to something much more
+ efficient than calling an Erlang function. The match specification is also
very limited compared to the expressiveness of real Erlang functions.</p>
- <p>The most notable difference between a match_spec and an Erlang fun is
- of course the syntax. Match specifications are Erlang terms, not
- Erlang code. A match_spec also has a somewhat strange concept of
- exceptions. An exception (e.g., <c><![CDATA[badarg]]></c>) in the <c><![CDATA[MatchCondition]]></c>
- part,
- which resembles an Erlang guard, will generate immediate failure,
- while an exception in the <c><![CDATA[MatchBody]]></c> part, which resembles the body of an
- Erlang function, is implicitly caught and results in the single atom
- <c><![CDATA['EXIT']]></c>.
- </p>
+ <p>The most notable difference between a match specification and an Erlang
+ fun is the syntax. Match specifications are Erlang terms, not Erlang code.
+ Also, a match specification has a strange concept of exceptions:</p>
+
+ <list type="bulleted">
+ <item>
+ <p>An exception (such as <c><![CDATA[badarg]]></c>) in the
+ <c><![CDATA[MatchCondition]]></c> part, which resembles an Erlang guard,
+ generates immediate failure.</p>
+ </item>
+ <item>
+ <p>An exception in the <c><![CDATA[MatchBody]]></c> part, which resembles
+ the body of an Erlang function, is implicitly caught and results in the
+ single atom <c><![CDATA['EXIT']]></c>.</p>
+ </item>
+ </list>
<section>
<title>Grammar</title>
- <p>A match_spec used in tracing can be described in this <em>informal</em> grammar:</p>
+ <p>A match specification used in tracing can be described in the following
+ <em>informal</em> grammar:</p>
+
<list type="bulleted">
<item>MatchExpression ::= [ MatchFunction, ... ]
</item>
<item>MatchFunction ::= { MatchHead, MatchConditions, MatchBody }
</item>
- <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> | [ MatchHeadPart, ... ]
+ <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> |
+ [ MatchHeadPart, ... ]
+ </item>
+ <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c>
</item>
- <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c></item>
<item>MatchVariable ::= '$&lt;number&gt;'
</item>
- <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c></item>
- <item>MatchCondition ::= { GuardFunction } |
- { GuardFunction, ConditionExpression, ... }
+ <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c>
+ </item>
+ <item>MatchCondition ::= { GuardFunction } | { GuardFunction,
+ ConditionExpression, ... }
</item>
<item>BoolFunction ::= <c><![CDATA[is_atom]]></c> |
<c><![CDATA[is_float]]></c> | <c><![CDATA[is_integer]]></c> |
@@ -79,58 +90,77 @@
<c><![CDATA[is_function]]></c> | <c><![CDATA[is_record]]></c> |
<c><![CDATA[is_seq_trace]]></c> | <c><![CDATA['and']]></c> |
<c><![CDATA['or']]></c> | <c><![CDATA['not']]></c> |
- <c><![CDATA['xor']]></c> | <c><![CDATA[andalso]]></c> |
- <c><![CDATA[orelse]]></c></item>
+ <c><![CDATA['xor']]></c> | <c><![CDATA['andalso']]></c> |
+ <c><![CDATA['orelse']]></c>
+ </item>
<item>ConditionExpression ::= ExprMatchVariable | { GuardFunction } |
- { GuardFunction, ConditionExpression, ... } | TermConstruct
+ { GuardFunction, ConditionExpression, ... } | TermConstruct
+ </item>
+ <item>ExprMatchVariable ::= MatchVariable (bound in the MatchHead) |
+ <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c>
</item>
- <item>ExprMatchVariable ::= MatchVariable (bound in the MatchHead) |
- <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c></item>
<item>TermConstruct = {{}} | {{ ConditionExpression, ... }} |
<c><![CDATA[[]]]></c> | [ConditionExpression, ...] |
<c><![CDATA[#{}]]></c> | #{term() => ConditionExpression, ...} |
- NonCompositeTerm | Constant</item>
- <item>NonCompositeTerm ::= term() (not list or tuple or map)</item>
+ NonCompositeTerm | Constant
+ </item>
+ <item>NonCompositeTerm ::= term() (not list or tuple or map)
+ </item>
<item>Constant ::= {<c><![CDATA[const]]></c>, term()}
</item>
<item>GuardFunction ::= BoolFunction | <c><![CDATA[abs]]></c> |
- <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> | <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> |
- <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> | <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> |
- <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> | <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> |
- <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> | <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> |
- <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> | <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> |
- <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> | <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> |
- <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> | <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> |
- <c><![CDATA[get_tcw]]></c></item>
+ <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> |
+ <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> |
+ <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> |
+ <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> |
+ <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> |
+ <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> |
+ <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> |
+ <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> |
+ <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> |
+ <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> |
+ <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> |
+ <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> |
+ <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> |
+ <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> |
+ <c><![CDATA[get_tcw]]></c>
+ </item>
<item>MatchBody ::= [ ActionTerm ]
</item>
<item>ActionTerm ::= ConditionExpression | ActionCall
</item>
- <item>ActionCall ::= {ActionFunction} |
- {ActionFunction, ActionTerm, ...}
+ <item>ActionCall ::= {ActionFunction} | {ActionFunction, ActionTerm, ...}
</item>
<item>ActionFunction ::= <c><![CDATA[set_seq_token]]></c> |
- <c><![CDATA[get_seq_token]]></c> | <c><![CDATA[message]]></c> |
- <c><![CDATA[return_trace]]></c> | <c><![CDATA[exception_trace]]></c> | <c><![CDATA[process_dump]]></c> |
- <c><![CDATA[enable_trace]]></c> | <c><![CDATA[disable_trace]]></c> | <c><![CDATA[trace]]></c> |
- <c><![CDATA[display]]></c> | <c><![CDATA[caller]]></c> | <c><![CDATA[set_tcw]]></c> |
- <c><![CDATA[silent]]></c></item>
+ <c><![CDATA[get_seq_token]]></c> | <c><![CDATA[message]]></c> |
+ <c><![CDATA[return_trace]]></c> | <c><![CDATA[exception_trace]]></c> |
+ <c><![CDATA[process_dump]]></c> | <c><![CDATA[enable_trace]]></c> |
+ <c><![CDATA[disable_trace]]></c> | <c><![CDATA[trace]]></c> |
+ <c><![CDATA[display]]></c> | <c><![CDATA[caller]]></c> |
+ <c><![CDATA[set_tcw]]></c> | <c><![CDATA[silent]]></c>
+ </item>
</list>
- <p>A match_spec used in ets can be described in this <em>informal</em> grammar:</p>
+ <p>A match specification used in
+ <seealso marker="stdlib:ets"><c>ets(3)</c></seealso>
+ can be described in the following <em>informal</em> grammar:</p>
+
<list type="bulleted">
<item>MatchExpression ::= [ MatchFunction, ... ]
</item>
<item>MatchFunction ::= { MatchHead, MatchConditions, MatchBody }
</item>
- <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> | { MatchHeadPart, ... }
+ <item>MatchHead ::= MatchVariable | <c><![CDATA['_']]></c> |
+ { MatchHeadPart, ... }
+ </item>
+ <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c>
</item>
- <item>MatchHeadPart ::= term() | MatchVariable | <c><![CDATA['_']]></c></item>
<item>MatchVariable ::= '$&lt;number&gt;'
</item>
- <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c></item>
+ <item>MatchConditions ::= [ MatchCondition, ...] | <c><![CDATA[[]]]></c>
+ </item>
<item>MatchCondition ::= { GuardFunction } |
- { GuardFunction, ConditionExpression, ... }
+ { GuardFunction, ConditionExpression, ... }
</item>
<item>BoolFunction ::= <c><![CDATA[is_atom]]></c> |
<c><![CDATA[is_float]]></c> | <c><![CDATA[is_integer]]></c> |
@@ -141,243 +171,322 @@
<c><![CDATA[is_function]]></c> | <c><![CDATA[is_record]]></c> |
<c><![CDATA[is_seq_trace]]></c> | <c><![CDATA['and']]></c> |
<c><![CDATA['or']]></c> | <c><![CDATA['not']]></c> |
- <c><![CDATA['xor']]></c> | <c><![CDATA[andalso]]></c> |
- <c><![CDATA[orelse]]></c></item>
+ <c><![CDATA['xor']]></c> | <c><![CDATA['andalso']]></c> |
+ <c><![CDATA['orelse']]></c>
+ </item>
<item>ConditionExpression ::= ExprMatchVariable | { GuardFunction } |
- { GuardFunction, ConditionExpression, ... } | TermConstruct
+ { GuardFunction, ConditionExpression, ... } | TermConstruct
</item>
<item>ExprMatchVariable ::= MatchVariable (bound in the MatchHead) |
- <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c></item>
+ <c><![CDATA['$_']]></c> | <c><![CDATA['$$']]></c>
+ </item>
<item>TermConstruct = {{}} | {{ ConditionExpression, ... }} |
<c><![CDATA[[]]]></c> | [ConditionExpression, ...] | #{} |
- #{term() => ConditionExpression, ...} | NonCompositeTerm |
- Constant</item>
- <item>NonCompositeTerm ::= term() (not list or tuple or map)</item>
+ #{term() => ConditionExpression, ...} | NonCompositeTerm | Constant
+ </item>
+ <item>NonCompositeTerm ::= term() (not list or tuple or map)
+ </item>
<item>Constant ::= {<c><![CDATA[const]]></c>, term()}
</item>
<item>GuardFunction ::= BoolFunction | <c><![CDATA[abs]]></c> |
- <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> | <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> |
- <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> | <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> |
- <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> | <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> |
- <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> | <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> |
- <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> | <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> |
- <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> | <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> |
- <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> | <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> |
- <c><![CDATA[get_tcw]]></c></item>
- <item>MatchBody ::= [ ConditionExpression, ... ]</item>
+ <c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> |
+ <c><![CDATA[length]]></c> | <c><![CDATA[node]]></c> |
+ <c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> |
+ <c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> |
+ <c><![CDATA['+']]></c> | <c><![CDATA['-']]></c> |
+ <c><![CDATA['*']]></c> | <c><![CDATA['div']]></c> |
+ <c><![CDATA['rem']]></c> | <c><![CDATA['band']]></c> |
+ <c><![CDATA['bor']]></c> | <c><![CDATA['bxor']]></c> |
+ <c><![CDATA['bnot']]></c> | <c><![CDATA['bsl']]></c> |
+ <c><![CDATA['bsr']]></c> | <c><![CDATA['>']]></c> |
+ <c><![CDATA['>=']]></c> | <c><![CDATA['<']]></c> |
+ <c><![CDATA['=<']]></c> | <c><![CDATA['=:=']]></c> |
+ <c><![CDATA['==']]></c> | <c><![CDATA['=/=']]></c> |
+ <c><![CDATA['/=']]></c> | <c><![CDATA[self]]></c> |
+ <c><![CDATA[get_tcw]]></c>
+ </item>
+ <item>MatchBody ::= [ ConditionExpression, ... ]
+ </item>
</list>
</section>
<section>
- <title>Function descriptions</title>
-
+ <title>Function Descriptions</title>
<section>
- <title>Functions allowed in all types of match specifications</title>
- <p>The different functions allowed in <c><![CDATA[match_spec]]></c> work like this:
- </p>
- <p><em>is_atom, is_float, is_integer, is_list, is_number, is_pid, is_port,
- is_reference, is_tuple, is_map, is_binary, is_function:</em> Like the
- corresponding guard tests in Erlang, return <c><![CDATA[true]]></c> or
- <c><![CDATA[false]]></c>.</p>
- <p><em>is_record: </em>Takes an additional parameter, which SHALL
- be the result of <c><![CDATA[record_info(size, <record_type>)]]></c>,
- like in <c><![CDATA[{is_record, '$1', rectype, record_info(size, rectype)}]]></c>.
- </p>
- <p><em>'not': </em>Negates its single argument (anything other
- than <c><![CDATA[false]]></c> gives <c><![CDATA[false]]></c>).
- </p>
- <p><em>'and': </em>Returns <c><![CDATA[true]]></c> if all its arguments
- (variable length argument list) evaluate to <c><![CDATA[true]]></c>, else
- <c><![CDATA[false]]></c>. Evaluation order is undefined.
- </p>
- <p><em>'or': </em>Returns <c><![CDATA[true]]></c> if any of its arguments
- evaluates to <c><![CDATA[true]]></c>. Variable length argument
- list. Evaluation order is undefined.
- </p>
- <p><em>andalso: </em>Like <c><![CDATA['and']]></c>, but quits evaluating its
- arguments as soon as one argument evaluates to something else
- than true. Arguments are evaluated left to right.
- </p>
- <p><em>orelse: </em>Like <c><![CDATA['or']]></c>, but quits evaluating as soon
- as one of its arguments evaluates to <c><![CDATA[true]]></c>. Arguments are
- evaluated left to right.
- </p>
- <p><em>'xor': </em>Only two arguments, of which one has to be true
- and the other false to return <c><![CDATA[true]]></c>; otherwise
- <c><![CDATA['xor']]></c> returns false.
- </p>
- <p><em>abs, element, hd, length, node, round, size, tl, trunc, '+', '-', '*', 'div', 'rem', 'band', 'bor', 'bxor', 'bnot', 'bsl', 'bsr', '>', '>=', '&lt;', '=&lt;', '=:=', '==', '=/=', '/=', self: </em>Work as the corresponding Erlang bif's (or
- operators). In case of bad arguments, the result depends on
- the context. In the <c><![CDATA[MatchConditions]]></c> part of the
- expression, the test fails immediately (like in an Erlang
- guard), but in the <c><![CDATA[MatchBody]]></c>, exceptions are implicitly
- caught and the call results in the atom <c><![CDATA['EXIT']]></c>.</p>
+ <title>Functions Allowed in All Types of Match Specifications</title>
+ <p>The functions allowed in <c><![CDATA[match_spec]]></c> work as
+ follows:</p>
+
+ <taglist>
+ <tag><c>is_atom</c>, <c>is_float</c>, <c>is_integer</c>, <c>is_list</c>,
+ <c>is_number</c>, <c>is_pid</c>, <c>is_port</c>, <c>is_reference</c>,
+ <c>is_tuple</c>, <c>is_map</c>, <c>is_binary</c>, <c>is_function</c>
+ </tag>
+ <item>
+ <p>Same as the corresponding guard tests in Erlang, return
+ <c><![CDATA[true]]></c> or <c><![CDATA[false]]></c>.</p>
+ </item>
+ <tag><c>is_record</c></tag>
+ <item>
+ <p>Takes an additional parameter, which <em>must</em> be the result
+ of <c><![CDATA[record_info(size, <record_type>)]]></c>, like in
+ <c><![CDATA[{is_record, '$1', rectype, record_info(size,
+ rectype)}]]></c>.</p>
+ </item>
+ <tag><c>'not'</c></tag>
+ <item>
+ <p>Negates its single argument (anything other
+ than <c><![CDATA[false]]></c> gives <c><![CDATA[false]]></c>).</p>
+ </item>
+ <tag><c>'and'</c></tag>
+ <item>
+ <p>Returns <c><![CDATA[true]]></c> if all its arguments (variable
+ length argument list) evaluate to <c><![CDATA[true]]></c>, otherwise
+ <c><![CDATA[false]]></c>. Evaluation order is undefined.</p>
+ </item>
+ <tag><c>'or'</c></tag>
+ <item>
+ <p>Returns <c><![CDATA[true]]></c> if any of its arguments
+ evaluates to <c><![CDATA[true]]></c>. Variable length argument
+ list. Evaluation order is undefined.</p>
+ </item>
+ <tag><c>'andalso'</c></tag>
+ <item>
+ <p>Works as <c><![CDATA['and']]></c>, but quits evaluating its
+ arguments when one argument evaluates to something else
+ than <c>true</c>. Arguments are evaluated left to right.</p>
+ </item>
+ <tag><c>'orelse'</c></tag>
+ <item>
+ <p>Works as <c><![CDATA['or']]></c>, but quits evaluating as soon
+ as one of its arguments evaluates to <c><![CDATA[true]]></c>.
+ Arguments are evaluated left to right.</p>
+ </item>
+ <tag><c>'xor'</c></tag>
+ <item>
+ <p>Only two arguments, of which one must be <c>true</c> and the
+ other <c>false</c> to return <c><![CDATA[true]]></c>; otherwise
+ <c><![CDATA['xor']]></c> returns false.</p>
+ </item>
+ <tag><c>abs</c>, <c>element</c>, <c>hd</c>, <c>length</c>, <c>node</c>,
+ <c>round</c>, <c>size</c>, <c>tl</c>, <c>trunc</c>, <c>'+'</c>,
+ <c>'-'</c>, <c>'*'</c>, <c>'div'</c>, <c>'rem'</c>, <c>'band'</c>,
+ <c>'bor'</c>, <c>'bxor'</c>, <c>'bnot'</c>, <c>'bsl'</c>,
+ <c>'bsr'</c>, <c>'>'</c>, <c>'>='</c>, <c>'&lt;'</c>, <c>'=&lt;'</c>,
+ <c>'=:='</c>, <c>'=='</c>, <c>'=/='</c>, <c>'/='</c>,
+ <c>self</c></tag>
+ <item>
+ <p>Same as the corresponding Erlang BIFs (or operators). In case of
+ bad arguments, the result depends on the context. In the
+ <c><![CDATA[MatchConditions]]></c> part of the expression, the test
+ fails immediately (like in an Erlang guard). In the
+ <c><![CDATA[MatchBody]]></c> part, exceptions are implicitly caught
+ and the call results in the atom <c><![CDATA['EXIT']]></c>.</p>
+ </item>
+ </taglist>
</section>
<section>
- <title>Functions allowed only for tracing</title>
- <p><em>is_seq_trace: </em>Returns <c><![CDATA[true]]></c> if a sequential
- trace token is set for the current process, otherwise <c><![CDATA[false]]></c>.
- </p>
- <p><em>set_seq_token:</em> Works like
- <c><![CDATA[seq_trace:set_token/2]]></c>, but returns <c><![CDATA[true]]></c> on success
- and <c><![CDATA['EXIT']]></c> on error or bad argument. Only allowed in the
- <c><![CDATA[MatchBody]]></c> part and only allowed when tracing.
- </p>
- <p><em>get_seq_token:</em> Works just like
- <c><![CDATA[seq_trace:get_token/0]]></c>, and is only allowed in the
- <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p><em>message:</em> Sets an additional message appended to the
- trace message sent. One can only set one additional message in
- the body; subsequent calls will replace the appended message. As
- a special case, <c><![CDATA[{message, false}]]></c> disables sending of
- trace messages ('call' and 'return_to')
- for this function call, just like if the match_spec had not matched,
- which can be useful if only the side effects of
- the <c><![CDATA[MatchBody]]></c> are desired.
- Another special case is <c><![CDATA[{message, true}]]></c> which
- sets the default behavior, as if the function had no match_spec,
- trace message is sent with no extra
- information (if no other calls to <c><![CDATA[message]]></c> are placed
- before <c><![CDATA[{message, true}]]></c>, it is in fact a "noop").
- </p>
- <p>Takes one argument, the message. Returns <c><![CDATA[true]]></c> and can
- only be used in the <c><![CDATA[MatchBody]]></c> part and when tracing.
- </p>
- <p><em>return_trace:</em> Causes a <c><![CDATA[return_from]]></c> trace
- message to be sent upon return from the current function.
- Takes no arguments, returns <c><![CDATA[true]]></c> and can only be used
- in the <c><![CDATA[MatchBody]]></c> part when tracing.
- If the process trace flag <c><![CDATA[silent]]></c>
- is active the <c><![CDATA[return_from]]></c> trace message is inhibited.
- </p>
- <p>NOTE! If the traced function is tail recursive, this match
- spec function destroys that property.
- Hence, if a match spec executing this function is used on a
- perpetual server process, it may only be active for a limited
- time, or the emulator will eventually use all memory in the host
- machine and crash. If this match_spec function is inhibited
- using the <c><![CDATA[silent]]></c> process trace flag
- tail recursiveness still remains.
- </p>
- <p><em>exception_trace:</em> Same as <em>return_trace</em>,
- plus; if the traced function exits due to an exception,
- an <c><![CDATA[exception_from]]></c> trace message is generated,
- whether the exception is caught or not.
- </p>
- <p><em>process_dump:</em> Returns some textual information about
- the current process as a binary. Takes no arguments and is only
- allowed in the <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p><em>enable_trace:</em> With one parameter this function turns
- on tracing like the Erlang call <c><![CDATA[erlang:trace(self(), true, [P2])]]></c>, where <c><![CDATA[P2]]></c> is the parameter to
- <c><![CDATA[enable_trace]]></c>. With two parameters, the first parameter
- should be either a process identifier or the registered name of
- a process. In this case tracing is turned on for the designated
- process in the same way as in the Erlang call <c><![CDATA[erlang:trace(P1, true, [P2])]]></c>, where P1 is the first and P2 is the second
- argument. The process <c><![CDATA[P1]]></c> gets its trace messages sent to the same
- tracer as the process executing the statement uses. <c><![CDATA[P1]]></c>
- can <em>not</em> be one of the atoms <c><![CDATA[all]]></c>, <c><![CDATA[new]]></c> or
- <c><![CDATA[existing]]></c> (unless, of course, they are registered names).
- <c><![CDATA[P2]]></c> can <em>not</em> be <c><![CDATA[cpu_timestamp]]></c> nor
- <c><![CDATA[tracer]]></c>.
- Returns <c><![CDATA[true]]></c> and may only be used in
- the <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p><em>disable_trace:</em> With one parameter this function
- disables tracing like the Erlang call <c><![CDATA[erlang:trace(self(), false, [P2])]]></c>, where <c><![CDATA[P2]]></c> is the parameter to
- <c><![CDATA[disable_trace]]></c>. With two parameters it works like the
- Erlang call <c><![CDATA[erlang:trace(P1, false, [P2])]]></c>, where P1 can
- be either a process identifier or a registered name and is given
- as the first argument to the match_spec function.
- <c><![CDATA[P2]]></c> can <em>not</em> be <c><![CDATA[cpu_timestamp]]></c> nor
- <c><![CDATA[tracer]]></c>. Returns
- <c><![CDATA[true]]></c> and may only be used in the <c><![CDATA[MatchBody]]></c> part
- when tracing.
- </p>
- <p><em>trace:</em> With two parameters this function takes a list
- of trace flags to disable as first parameter and a list
- of trace flags to enable as second parameter. Logically, the
- disable list is applied first, but effectively all changes
- are applied atomically. The trace flags
- are the same as for <c><![CDATA[erlang:trace/3]]></c> not including
- <c><![CDATA[cpu_timestamp]]></c> but including <c><![CDATA[tracer]]></c>. If a
- tracer is specified in both lists, the tracer in the
- enable list takes precedence. If no tracer is specified the
- same tracer as the process executing the match spec is
- used. When using a <seealso marker="erl_tracer">tracer module</seealso>
- the module has to be loaded before the match specification is executed.
- If it is not loaded the match will fail.
- With three parameters to this function the first is
- either a process identifier or the registered name of a
- process to set trace flags on, the second is the disable
- list, and the third is the enable list. Returns
- <c><![CDATA[true]]></c> if any trace property was changed for the
- trace target process or <c><![CDATA[false]]></c> if not. It may only
- be used in the <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p><em>caller:</em>
- Returns the calling function as a tuple {Module,
- Function, Arity} or the atom <c><![CDATA[undefined]]></c> if the calling
- function cannot be determined. May only be used in the
- <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p>Note that if a "technically built in function" (i.e. a
- function not written in Erlang) is traced, the <c><![CDATA[caller]]></c>
- function will sometimes return the atom <c><![CDATA[undefined]]></c>. The calling
- Erlang function is not available during such calls.
- </p>
- <p><em>display:</em> For debugging purposes only; displays the
- single argument as an Erlang term on stdout, which is seldom
- what is wanted. Returns <c><![CDATA[true]]></c> and may only be used in the
- <c><![CDATA[MatchBody]]></c> part when tracing.
- </p>
- <p> <marker id="get_tcw"></marker>
-<em>get_tcw:</em>
- Takes no argument and returns the value of the node's trace
- control word. The same is done by
- <c><![CDATA[erlang:system_info(trace_control_word)]]></c>.
- </p>
- <p>The trace control word is a 32-bit unsigned integer intended for
- generic trace control. The trace control word can be tested and
- set both from within trace match specifications and with BIFs.
- This call is only allowed when tracing.
- </p>
- <p> <marker id="set_tcw"></marker>
-<em>set_tcw:</em>
- Takes one unsigned integer argument, sets the value of
- the node's trace control word to the value of the argument
- and returns the previous value. The same is done by
- <c><![CDATA[erlang:system_flag(trace_control_word, Value)]]></c>. It is only
- allowed to use <c><![CDATA[set_tcw]]></c> in the <c><![CDATA[MatchBody]]></c> part
- when tracing.
- </p>
- <p><em>silent:</em>
- Takes one argument. If the argument is <c><![CDATA[true]]></c>, the call
- trace message mode for the current process is set to silent
- for this call and all subsequent, i.e call trace messages
- are inhibited even if <c><![CDATA[{message, true}]]></c> is called in the
- <c><![CDATA[MatchBody]]></c> part for a traced function.
- </p>
- <p>This mode can also be activated with the <c><![CDATA[silent]]></c> flag
- to <c><![CDATA[erlang:trace/3]]></c>.
- </p>
- <p>If the argument is <c><![CDATA[false]]></c>, the call trace message mode
- for the current process is set to normal (non-silent) for
- this call and all subsequent.
- </p>
- <p>If the argument is neither <c><![CDATA[true]]></c> nor <c><![CDATA[false]]></c>,
- the call trace message mode is unaffected.</p>
+ <title>Functions Allowed Only for Tracing</title>
+ <p>The functions allowed only for tracing work as follows:</p>
+
+ <taglist>
+ <tag><c>is_seq_trace</c></tag>
+ <item>
+ <p>Returns <c><![CDATA[true]]></c> if a sequential trace token is set
+ for the current process, otherwise <c><![CDATA[false]]></c>.</p>
+ </item>
+ <tag><c>set_seq_token</c></tag>
+ <item>
+ <p>Works as <c><![CDATA[seq_trace:set_token/2]]></c>, but returns
+ <c><![CDATA[true]]></c> on success, and <c><![CDATA['EXIT']]></c>
+ on error or bad argument. Only allowed in the
+ <c><![CDATA[MatchBody]]></c> part and only allowed when tracing.</p>
+ </item>
+ <tag><c>get_seq_token</c></tag>
+ <item>
+ <p>Same as <c><![CDATA[seq_trace:get_token/0]]></c> and only
+ allowed in the <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><c>message</c></tag>
+ <item>
+ <p>Sets an additional message appended to the
+ trace message sent. One can only set one additional message in
+ the body. Later calls replace the appended message.</p>
+ <p>As a special case, <c><![CDATA[{message, false}]]></c> disables
+ sending of trace messages ('call' and 'return_to') for this function
+ call, just like if the match specification had not matched.
+ This can be useful if only the side effects of
+ the <c><![CDATA[MatchBody]]></c> part are desired.</p>
+ <p>Another special case is <c><![CDATA[{message, true}]]></c>, which
+ sets the default behavior, as if the function had no match
+ specification; trace message is sent with no extra information
+ (if no other calls to <c><![CDATA[message]]></c> are placed before
+ <c><![CDATA[{message, true}]]></c>, it is in fact a "noop").</p>
+ <p>Takes one argument: the message. Returns <c><![CDATA[true]]></c>
+ and can only be used in the <c><![CDATA[MatchBody]]></c> part and
+ when tracing.</p>
+ </item>
+ <tag><c>return_trace</c></tag>
+ <item>
+ <p>Causes a <c><![CDATA[return_from]]></c> trace message to be sent
+ upon return from the current function. Takes no arguments, returns
+ <c><![CDATA[true]]></c> and can only be used in the
+ <c><![CDATA[MatchBody]]></c> part when tracing.
+ If the process trace flag <c><![CDATA[silent]]></c> is active, the
+ <c><![CDATA[return_from]]></c> trace message is inhibited.</p>
+ <p><em>Warning:</em> If the traced function is tail-recursive, this
+ match specification function destroys that property. Hence, if a
+ match specification executing this function is used on a
+ perpetual server process, it can only be active for a limited
+ period of time, or the emulator will eventually use all memory in
+ the host machine and crash. If this match specification function is
+ inhibited using process trace flag <c><![CDATA[silent]]></c>,
+ tail-recursiveness still remains.</p>
+ </item>
+ <tag><c>exception_trace</c></tag>
+ <item>
+ <p>Works as <c>return_trace</c> plus; if the traced function exits
+ because of an exception,
+ an <c><![CDATA[exception_from]]></c> trace message is generated,
+ regardless of the exception is caught or not.</p>
+ </item>
+ <tag><c>process_dump</c></tag>
+ <item>
+ <p>Returns some textual information about
+ the current process as a binary. Takes no arguments and is only
+ allowed in the <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><c>enable_trace</c></tag>
+ <item>
+ <p>With one parameter this function turns on tracing like the Erlang
+ call <c><![CDATA[erlang:trace(self(), true, [P2])]]></c>, where
+ <c><![CDATA[P2]]></c> is the parameter to
+ <c><![CDATA[enable_trace]]></c>.</p>
+ <p>With two parameters, the first parameter is to be either a process
+ identifier or the registered name of a process. In this case tracing
+ is turned on for the designated process in the same way as in the
+ Erlang call <c><![CDATA[erlang:trace(P1, true, [P2])]]></c>, where
+ <c>P1</c> is the first and <c>P2</c> is the second argument. The
+ process <c><![CDATA[P1]]></c> gets its trace messages sent to the
+ same tracer as the process executing the statement uses.
+ <c><![CDATA[P1]]></c> <em>cannot</em> be one of the atoms
+ <c><![CDATA[all]]></c>, <c><![CDATA[new]]></c> or
+ <c><![CDATA[existing]]></c> (unless they are registered names).
+ <c><![CDATA[P2]]></c> <em>cannot</em> be
+ <c><![CDATA[cpu_timestamp]]></c> or <c><![CDATA[tracer]]></c>.</p>
+ <p>Returns <c><![CDATA[true]]></c> and can only be used in
+ the <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><c>disable_trace</c></tag>
+ <item>
+ <p>With one parameter this function disables tracing like the Erlang
+ call <c><![CDATA[erlang:trace(self(), false, [P2])]]></c>, where
+ <c><![CDATA[P2]]></c> is the parameter to
+ <c><![CDATA[disable_trace]]></c>.</p>
+ <p>With two parameters this function works as the Erlang call
+ <c><![CDATA[erlang:trace(P1, false, [P2])]]></c>, where <c>P1</c>
+ can be either a process identifier or a registered name and is
+ specified as the first argument to the match specification function.
+ <c><![CDATA[P2]]></c> <em>cannot</em> be
+ <c><![CDATA[cpu_timestamp]]></c> or <c><![CDATA[tracer]]></c>.</p>
+ <p>Returns <c><![CDATA[true]]></c> and can only be used in the
+ <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><c>trace</c></tag>
+ <item>
+ <p>With two parameters this function takes a list
+ of trace flags to disable as first parameter and a list
+ of trace flags to enable as second parameter. Logically, the
+ disable list is applied first, but effectively all changes
+ are applied atomically. The trace flags
+ are the same as for <c><![CDATA[erlang:trace/3]]></c>,
+ not including <c><![CDATA[cpu_timestamp]]></c>, but including
+ <c><![CDATA[tracer]]></c>.</p>
+ <p>If a tracer is specified in both lists, the tracer in the
+ enable list takes precedence. If no tracer is specified, the same
+ tracer as the process executing the match specification is used.</p>
+ <p>When using a <seealso marker="erl_tracer">tracer module</seealso>,
+ the module must be loaded before the match specification is
+ executed. If it is not loaded, the match fails.</p>
+ <p>With three parameters to this function, the first is
+ either a process identifier or the registered name of a
+ process to set trace flags on, the second is the disable
+ list, and the third is the enable list.</p>
+ <p>Returns <c><![CDATA[true]]></c> if any trace property was changed
+ for the trace target process, otherwise <c><![CDATA[false]]></c>.
+ Can only be used in the <c><![CDATA[MatchBody]]></c> part when
+ tracing.</p>
+ </item>
+ <tag><c>caller</c></tag>
+ <item>
+ <p>Returns the calling function as a tuple <c>{Module, Function,
+ Arity}</c> or the atom <c><![CDATA[undefined]]></c> if the calling
+ function cannot be determined. Can only be used in the
+ <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ <p>Notice that if a "technically built in function" (that is, a
+ function not written in Erlang) is traced, the
+ <c><![CDATA[caller]]></c> function sometimes returns the atom
+ <c><![CDATA[undefined]]></c>. The calling
+ Erlang function is not available during such calls.</p>
+ </item>
+ <tag><c>display</c></tag>
+ <item>
+ <p>For debugging purposes only. Displays the single argument as an
+ Erlang term on <c>stdout</c>, which is seldom what is wanted.
+ Returns <c><![CDATA[true]]></c> and can only be used in the
+ <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><marker id="get_tcw"/><c>get_tcw</c></tag>
+ <item>
+ <p>Takes no argument and returns the value of the node's trace
+ control word. The same is done by
+ <c><![CDATA[erlang:system_info(trace_control_word)]]></c>.</p>
+ <p>The trace control word is a 32-bit unsigned integer intended for
+ generic trace control. The trace control word can be tested and
+ set both from within trace match specifications and with BIFs.
+ This call is only allowed when tracing.</p>
+ </item>
+ <tag><marker id="set_tcw"/><c>set_tcw</c></tag>
+ <item>
+ <p>Takes one unsigned integer argument, sets the value of
+ the node's trace control word to the value of the argument,
+ and returns the previous value. The same is done by
+ <c><![CDATA[erlang:system_flag(trace_control_word, Value)]]></c>.
+ It is only allowed to use <c><![CDATA[set_tcw]]></c> in the
+ <c><![CDATA[MatchBody]]></c> part when tracing.</p>
+ </item>
+ <tag><c>silent</c></tag>
+ <item>
+ <p>Takes one argument. If the argument is <c><![CDATA[true]]></c>,
+ the call trace message mode for the current process is set to
+ silent for this call and all later calls, that is, call trace
+ messages are inhibited even if
+ <c><![CDATA[{message, true}]]></c> is called in the
+ <c><![CDATA[MatchBody]]></c> part for a traced function.</p>
+ <p>This mode can also be activated with flag
+ <c><![CDATA[silent]]></c> to
+ <c><![CDATA[erlang:trace/3]]></c>.</p>
+ <p>If the argument is <c><![CDATA[false]]></c>, the call trace
+ message mode for the current process is set to normal
+ (non-silent) for this call and all later calls.</p>
+ <p>If the argument is not <c><![CDATA[true]]></c> or
+ <c><![CDATA[false]]></c>, the call trace message mode is
+ unaffected.</p>
+ </item>
+ </taglist>
</section>
- <p><em>Note</em> that all "function calls" have to be tuples,
- even if they take no arguments. The value of <c><![CDATA[self]]></c> is
- the atom() <c><![CDATA[self]]></c>, but the value of <c><![CDATA[{self}]]></c> is
- the pid() of the current process.</p>
+
+ <note>
+ <p>All "function calls" must be tuples, even if they take no arguments.
+ The value of <c><![CDATA[self]]></c> is the atom()
+ <c><![CDATA[self]]></c>, but the value of <c><![CDATA[{self}]]></c> is
+ the pid() of the current process.</p>
+ </note>
</section>
- <marker id="match_target"></marker>
<section>
+ <marker id="match_target"/>
<title>Match target</title>
<p>Each execution of a match specification is done against
a match target term. The format and content of the target term
@@ -422,217 +531,268 @@
</section>
<section>
- <title>Variables and literals</title>
- <p>Variables take the form <c><![CDATA['$<number>']]></c> where
- <c><![CDATA[<number>]]></c> is an integer between 0 (zero) and
- 100000000 (1e+8), the behavior if the number is outside these
- limits is <em>undefined</em>. In the <c><![CDATA[MatchHead]]></c> part, the special
- variable <c><![CDATA['_']]></c> matches anything, and never gets bound (like
- <c><![CDATA[_]]></c> in Erlang). In the <c><![CDATA[MatchCondition/MatchBody]]></c>
- parts, no unbound variables are allowed, why <c><![CDATA['_']]></c> is
- interpreted as itself (an atom). Variables can only be bound in
- the <c><![CDATA[MatchHead]]></c> part. In the <c><![CDATA[MatchBody]]></c> and
- <c><![CDATA[MatchCondition]]></c> parts, only variables bound previously may
- be used. As a special case, in the
- <c><![CDATA[MatchCondition/MatchBody]]></c> parts, the variable <c><![CDATA['$_']]></c>
- expands to the whole <seealso marker="#match_target">match target</seealso>
- term and the variable <c><![CDATA['$$']]></c> expands to a list
- of the values of all bound variables in order
- (i.e. <c><![CDATA[['$1','$2', ...]]]></c>).
- </p>
- <p>In the <c><![CDATA[MatchHead]]></c> part, all literals (except the variables
- noted above) are interpreted as is. In the
- <c><![CDATA[MatchCondition/MatchBody]]></c> parts, however, the
- interpretation is in some ways different. Literals in the
- <c><![CDATA[MatchCondition/MatchBody]]></c> can either be written as is,
- which works for all literals except tuples, or by using the
- special form <c><![CDATA[{const, T}]]></c>, where <c><![CDATA[T]]></c> is any Erlang
- term. For tuple literals in the match_spec, one can also use
- double tuple parentheses, i.e., construct them as a tuple of
+ <title>Variables and Literals</title>
+ <p>Variables take the form <c><![CDATA['$<number>']]></c>, where
+ <c><![CDATA[<number>]]></c> is an integer between 0 and
+ 100,000,000 (1e+8). The behavior if the number is outside these limits
+ is <em>undefined</em>. In the <c><![CDATA[MatchHead]]></c> part, the
+ special variable <c><![CDATA['_']]></c> matches anything, and never gets
+ bound (like <c><![CDATA[_]]></c> in Erlang).</p>
+
+ <list type="bulleted">
+ <item>
+ <p>In the <c><![CDATA[MatchCondition/MatchBody]]></c> parts,
+ no unbound variables are allowed, so <c><![CDATA['_']]></c> is
+ interpreted as itself (an atom). Variables can only be bound in the
+ <c><![CDATA[MatchHead]]></c> part.</p>
+ </item>
+ <item>
+ <p>In the <c><![CDATA[MatchBody]]></c> and
+ <c><![CDATA[MatchCondition]]></c> parts, only variables bound
+ previously can be used.</p>
+ </item>
+ <item>
+ <p>As a special case, the following apply in the
+ <c><![CDATA[MatchCondition/MatchBody]]></c> parts:</p>
+ <list type="bulleted">
+ <item>
+ <p>The variable <c><![CDATA['$_']]></c> expands to the whole
+ <seealso marker="#match_target">match target</seealso> term.
+ </p>
+ </item>
+ <item>
+ <p>The variable <c><![CDATA['$$']]></c> expands to a list of the
+ values of all bound variables in order (that is,
+ <c><![CDATA[['$1','$2', ...]]]></c>).</p>
+ </item>
+ </list>
+ </item>
+ </list>
+
+ <p>In the <c><![CDATA[MatchHead]]></c> part, all literals (except the
+ variables above) are interpreted "as is".</p>
+
+ <p>In the <c><![CDATA[MatchCondition/MatchBody]]></c> parts, the
+ interpretation is in some ways different. Literals in these parts
+ can either be written "as is", which works for all literals except
+ tuples, or by using the special form <c><![CDATA[{const, T}]]></c>,
+ where <c><![CDATA[T]]></c> is any Erlang term.</p>
+
+ <p>For tuple literals in the match specification, double tuple parentheses
+ can also be used, that is, construct them as a tuple of
arity one containing a single tuple, which is the one to be
constructed. The "double tuple parenthesis" syntax is useful to
construct tuples from already bound variables, like in
- <c><![CDATA[{{'$1', [a,b,'$2']}}]]></c>. Some examples may be needed:
- </p>
+ <c><![CDATA[{{'$1', [a,b,'$2']}}]]></c>. Examples:</p>
+
<table>
<row>
- <cell align="left" valign="middle">Expression </cell>
- <cell align="left" valign="middle">Variable bindings </cell>
- <cell align="left" valign="middle">Result </cell>
+ <cell align="left" valign="middle"><em>Expression</em></cell>
+ <cell align="left" valign="middle"><em>Variable Bindings</em></cell>
+ <cell align="left" valign="middle"><em>Result</em></cell>
</row>
<row>
- <cell align="left" valign="middle">{{'$1','$2'}} </cell>
+ <cell align="left" valign="middle">{{'$1','$2'}}</cell>
<cell align="left" valign="middle">'$1' = a, '$2' = b</cell>
<cell align="left" valign="middle">{a,b}</cell>
</row>
<row>
- <cell align="left" valign="middle">{const, {'$1', '$2'}} </cell>
- <cell align="left" valign="middle">doesn't matter</cell>
+ <cell align="left" valign="middle">{const, {'$1', '$2'}}</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
<cell align="left" valign="middle">{'$1', '$2'}</cell>
</row>
<row>
- <cell align="left" valign="middle">a </cell>
- <cell align="left" valign="middle">doesn't matter </cell>
+ <cell align="left" valign="middle">a</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
<cell align="left" valign="middle">a</cell>
</row>
<row>
- <cell align="left" valign="middle">'$1' </cell>
- <cell align="left" valign="middle">'$1' = [] </cell>
+ <cell align="left" valign="middle">'$1'</cell>
+ <cell align="left" valign="middle">'$1' = []</cell>
<cell align="left" valign="middle">[]</cell>
</row>
<row>
- <cell align="left" valign="middle">['$1'] </cell>
- <cell align="left" valign="middle">'$1' = [] </cell>
+ <cell align="left" valign="middle">['$1']</cell>
+ <cell align="left" valign="middle">'$1' = []</cell>
<cell align="left" valign="middle">[[]]</cell>
</row>
<row>
- <cell align="left" valign="middle">[{{a}}] </cell>
- <cell align="left" valign="middle">doesn't matter</cell>
+ <cell align="left" valign="middle">[{{a}}]</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
<cell align="left" valign="middle">[{a}]</cell>
</row>
<row>
- <cell align="left" valign="middle">42 </cell>
- <cell align="left" valign="middle">doesn't matter</cell>
+ <cell align="left" valign="middle">42</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
<cell align="left" valign="middle">42</cell>
</row>
<row>
- <cell align="left" valign="middle">"hello" </cell>
- <cell align="left" valign="middle">doesn't matter</cell>
+ <cell align="left" valign="middle">"hello"</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
<cell align="left" valign="middle">"hello"</cell>
</row>
<row>
- <cell align="left" valign="middle">$1 </cell>
- <cell align="left" valign="middle">doesn't matter</cell>
- <cell align="left" valign="middle">49 (the ASCII value for the character '1')</cell>
+ <cell align="left" valign="middle">$1</cell>
+ <cell align="left" valign="middle">Irrelevant</cell>
+ <cell align="left" valign="middle">49 (the ASCII value for
+ character '1')</cell>
</row>
- <tcaption>Literals in the MatchCondition/MatchBody parts of a match_spec</tcaption>
+ <tcaption>Literals in MatchCondition/MatchBody Parts of a Match
+ Specification</tcaption>
</table>
</section>
<section>
- <title>Execution of the match</title>
+ <title>Execution of the Match</title>
<p>The execution of the match expression, when the runtime system
- decides whether a trace message should be sent, goes as follows:
- </p>
- <p>For each tuple in the <c><![CDATA[MatchExpression]]></c> list and while no
- match has succeeded:</p>
- <list type="bulleted">
- <item>Match the <c><![CDATA[MatchHead]]></c> part against the
- match target term,
- binding the <c><![CDATA['$<number>']]></c> variables (much like in
- <c><![CDATA[ets:match/2]]></c>).
- If the <c><![CDATA[MatchHead]]></c> cannot match the arguments, the match fails.
- </item>
- <item>Evaluate each <c><![CDATA[MatchCondition]]></c> (where only
- <c><![CDATA['$<number>']]></c> variables previously bound in the
- <c><![CDATA[MatchHead]]></c> can occur) and expect it to return the atom
- <c><![CDATA[true]]></c>. As soon as a condition does not evaluate to
- <c><![CDATA[true]]></c>, the match fails. If any BIF call generates an
- exception, also fail.
+ decides whether a trace message is to be sent, is as follows:</p>
+
+ <p>For each tuple in the <c><![CDATA[MatchExpression]]></c> list and while
+ no match has succeeded:</p>
+
+ <list type="ordered">
+ <item>
+ <p>Match the <c><![CDATA[MatchHead]]></c> part against the match target
+ term, binding the <c><![CDATA['$<number>']]></c> variables
+ (much like in <c><![CDATA[ets:match/2]]></c>). If the
+ <c><![CDATA[MatchHead]]></c> part cannot match the arguments, the
+ match fails.</p>
</item>
<item>
+ <p>Evaluate each <c><![CDATA[MatchCondition]]></c> (where only
+ <c><![CDATA['$<number>']]></c> variables previously bound in the
+ <c><![CDATA[MatchHead]]></c> part can occur) and expect it to return
+ the atom <c><![CDATA[true]]></c>. When a condition does not evaluate
+ to <c><![CDATA[true]]></c>, the match fails. If any BIF call
+ generates an exception, the match also fails.</p>
+ </item>
+ <item>
+ <p>Two cases can occur:</p>
<list type="bulleted">
- <item><em>If the match_spec is executing when tracing:</em><br></br>
- Evaluate each <c><![CDATA[ActionTerm]]></c> in the same way as the
- <c><![CDATA[MatchConditions]]></c>, but completely ignore the return
- values. Regardless of what happens in this part, the match has
- succeeded.</item>
- <item><em>If the match_spec is executed when selecting objects from an ETS table:</em><br></br>
- Evaluate the expressions in order and return the value of
- the last expression (typically there is only one expression
- in this context)</item>
+ <item>
+ <p>If the match specification is executing when tracing:</p>
+ <p>Evaluate each <c><![CDATA[ActionTerm]]></c> in the same way as
+ the <c><![CDATA[MatchConditions]]></c>, but ignore the return
+ values. Regardless of what happens in this part, the match has
+ succeeded.</p>
+ </item>
+ <item>
+ <p>If the match specification is executed when selecting objects
+ from an ETS table:</p>
+ <p>Evaluate the expressions in order and return the value of
+ the last expression (typically there is only one expression
+ in this context).</p>
+ </item>
</list>
</item>
</list>
</section>
<section>
- <title>Differences between match specifications in ETS and tracing</title>
- <p>ETS match specifications are there to produce a return
- value. Usually the <c><![CDATA[MatchBody]]></c> contains one single
- <c><![CDATA[ConditionExpression]]></c> which defines the return value without having
- any side effects. Calls with side effects are not allowed in the
- ETS context.</p>
+ <marker id="differences_ets_tracing"/>
+ <title>Differences between Match Specifications in ETS and Tracing</title>
+ <p>ETS match specifications produce a return value.
+ Usually the <c><![CDATA[MatchBody]]></c> contains one single
+ <c><![CDATA[ConditionExpression]]></c> that defines the return value
+ without any side effects. Calls with side effects are not allowed in
+ the ETS context.</p>
+
<p>When tracing there is no return value to produce, the
- match specification either matches or doesn't. The effect when the
- expression matches is a trace message rather then a returned
- term. The <c><![CDATA[ActionTerm]]></c>'s are executed as in an imperative
- language, i.e. for their side effects. Functions with side effects
+ match specification either matches or does not. The effect when the
+ expression matches is a trace message rather than a returned
+ term. The <c><![CDATA[ActionTerm]]></c>s are executed as in an imperative
+ language, that is, for their side effects. Functions with side effects
are also allowed when tracing.</p>
</section>
<section>
<title>Tracing Examples</title>
- <p>Match an argument list of three where the first and third arguments
+ <p>Match an argument list of three, where the first and third arguments
are equal:</p>
+
<code type="none"><![CDATA[
[{['$1', '_', '$1'],
[],
[]}]
]]></code>
- <p>Match an argument list of three where the second argument is
- a number greater than three:</p>
+
+ <p>Match an argument list of three, where the second argument is
+ a number &gt; 3:</p>
+
<code type="none"><![CDATA[
[{['_', '$1', '_'],
[{ '>', '$1', 3}],
[]}]
]]></code>
- <p>Match an argument list of three, where the third argument
- is a tuple containing argument one and two <em>or</em> a list
- beginning with argument one and two (i. e. <c><![CDATA[[a,b,[a,b,c]]]]></c> or
- <c><![CDATA[[a,b,{a,b}]]]></c>):
- </p>
+
+ <p>Match an argument list of three, where the third argument is
+ either a tuple containing argument one and two, <em>or</em> a list
+ beginning with argument one and two (that is,
+ <c><![CDATA[[a,b,[a,b,c]]]]></c> or <c><![CDATA[[a,b,{a,b}]]]></c>):</p>
+
<code type="none"><![CDATA[
[{['$1', '$2', '$3'],
- [{orelse,
+ [{'orelse',
{'=:=', '$3', {{'$1','$2'}}},
{'and',
{'=:=', '$1', {hd, '$3'}},
{'=:=', '$2', {hd, {tl, '$3'}}}}}],
[]}]
]]></code>
- <p>The above problem may also be solved like this:</p>
+
+ <p>The above problem can also be solved as follows:</p>
+
<code type="none"><![CDATA[
[{['$1', '$2', {'$1', '$2}], [], []},
{['$1', '$2', ['$1', '$2' | '_']], [], []}]
]]></code>
- <p>Match two arguments where the first is a tuple beginning with
- a list which in turn begins with the second argument times
- two (i. e. [{[4,x],y},2] or [{[8], y, z},4])</p>
+
+ <p>Match two arguments, where the first is a tuple beginning with
+ a list that in turn begins with the second argument times
+ two (that is, <c>[{[4,x],y},2]</c> or <c>[{[8], y, z},4])</c>:</p>
+
<code type="none"><![CDATA[
[{['$1', '$2'],[{'=:=', {'*', 2, '$2'}, {hd, {element, 1, '$1'}}}],
[]}]
]]></code>
+
<p>Match three arguments. When all three are equal and are
- numbers, append the process dump to the trace message, else
- let the trace message be as is, but set the sequential trace
- token label to 4711.</p>
+ numbers, append the process dump to the trace message, otherwise
+ let the trace message be "as is", but set the sequential trace
+ token label to 4711:</p>
+
<code type="none"><![CDATA[
[{['$1', '$1', '$1'],
[{is_number, '$1'}],
[{message, {process_dump}}]},
{'_', [], [{set_seq_token, label, 4711}]}]
]]></code>
- <p>As can be noted above, the parameter list can be matched
- against a single <c><![CDATA[MatchVariable]]></c> or an <c><![CDATA['_']]></c>. To replace the
- whole
- parameter list with a single variable is a special case. In all
- other cases the <c><![CDATA[MatchHead]]></c> has to be a <em>proper</em> list.
- </p>
- <p>Only generate trace message if trace control word is set to 1:</p>
+
+ <p>As can be noted above, the parameter list can be matched against a
+ single <c><![CDATA[MatchVariable]]></c> or an <c><![CDATA['_']]></c>.
+ To replace the whole parameter list with a single variable is a special
+ case. In all other cases the <c><![CDATA[MatchHead]]></c> must be a
+ <em>proper</em> list.</p>
+
+ <p>Generate a trace message only if the trace control word is set to 1:</p>
+
<code type="none"><![CDATA[
[{'_',
[{'==',{get_tcw},{const, 1}}],
[]}]
]]></code>
- <p>Only generate trace message if there is a seq trace token:</p>
+
+ <p>Generate a trace message only if there is a <c>seq_trace</c> token:</p>
+
<code type="none"><![CDATA[
[{'_',
[{'==',{is_seq_trace},{const, 1}}],
[]}]
]]></code>
- <p>Remove 'silent' trace flag when first argument is 'verbose'
- and add it when it is 'silent':</p>
+
+ <p>Remove the <c>'silent'</c> trace flag when the first argument is
+ <c>'verbose'</c>, and add it when it is <c>'silent':</c></p>
+
<code type="none"><![CDATA[
[{'$1',
[{'==',{hd, '$1'},verbose}],
@@ -641,14 +801,19 @@
[{'==',{hd, '$1'},silent}],
[{trace, [],[silent]}]}]
]]></code>
- <p>Add return_trace message if function is of arity 3:</p>
+
+ <p>Add a <c>return_trace</c> message if the function is of arity 3:</p>
+
<code type="none"><![CDATA[
[{'$1',
[{'==',{length, '$1'},3}],
[{return_trace}]},
{'_',[],[]}]
]]></code>
- <p>Only generate trace message if function is of arity 3 and first argument is 'trace':</p>
+
+ <p>Generate a trace message only if the function is of arity 3 and the
+ first argument is <c>'trace'</c>:</p>
+
<code type="none"><![CDATA[
[{['trace','$2','$3'],
[],
@@ -659,29 +824,35 @@
<section>
<title>ETS Examples</title>
- <p>Match all objects in an ets table where the first element is
- the atom 'strider' and the tuple arity is 3 and return the whole
- object.</p>
+ <p>Match all objects in an ETS table, where the first element is
+ the atom <c>'strider'</c> and the tuple arity is 3, and return the whole
+ object:</p>
+
<code type="none"><![CDATA[
[{{strider,'_','_'},
[],
['$_']}]
]]></code>
- <p>Match all objects in an ets table with arity &gt; 1 and the first
- element is 'gandalf', return element 2.</p>
+
+ <p>Match all objects in an ETS table with arity &gt; 1 and the first
+ element is 'gandalf', and return element 2:</p>
+
<code type="none"><![CDATA[
[{'$1',
[{'==', gandalf, {element, 1, '$1'}},{'>=',{size, '$1'},2}],
[{element,2,'$1'}]}]
]]></code>
- <p>In the above example, if the first element had been the key,
- it's much more efficient to match that key in the <c><![CDATA[MatchHead]]></c>
- part than in the <c><![CDATA[MatchConditions]]></c> part. The search space of
- the tables is restricted with regards to the <c><![CDATA[MatchHead]]></c> so
- that only objects with the matching key are searched.
- </p>
- <p>Match tuples of 3 elements where the second element is either
- 'merry' or 'pippin', return the whole objects.</p>
+
+ <p>In this example, if the first element had been the key, it is
+ much more efficient to match that key in the <c><![CDATA[MatchHead]]></c>
+ part than in the <c><![CDATA[MatchConditions]]></c> part.
+ The search space of the tables is restricted with regards to the
+ <c><![CDATA[MatchHead]]></c> so
+ that only objects with the matching key are searched.</p>
+
+ <p>Match tuples of three elements, where the second element is either
+ <c>'merry'</c> or <c>'pippin'</c>, and return the whole objects:</p>
+
<code type="none"><![CDATA[
[{{'_',merry,'_'},
[],
@@ -690,8 +861,9 @@
[],
['$_']}]
]]></code>
- <p>The function <c><![CDATA[ets:test_ms/2]]></c> can be useful for testing
- complicated ets matches.</p>
+
+ <p>Function <seealso marker="stdlib:ets#test_ms/2"><c>ets:test_ms/2></c></seealso>
+ can be useful for testing complicated ETS matches.</p>
</section>
</chapter>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 3c3129d543..7368d3e174 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2016</year>
+ <year>2004</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,1172 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 8.3.5.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed small memory leak that could occur when sending to
+ a terminating port.</p>
+ <p>
+ Own Id: OTP-14609 Aux Id: ERIERL-238 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.5.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a race condition in the inet driver that could
+ cause receive to hang when the emulator was compiled with
+ gcc 8.</p>
+ <p>
+ Own Id: OTP-15158 Aux Id: ERL-654 </p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug causing some Erlang references to be
+ inconsistently ordered. This could for example cause
+ failure to look up certain elements with references as
+ keys in search data structures. This bug was introduced
+ in R13B02.</p>
+ <p>
+ Thanks to Simon Cornish for finding the bug and supplying
+ a fix.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-15225</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.5.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a bug in file closure on Unix; close(2) was
+ retried on EINTR which could cause a different (recently
+ opened) file to be closed as well.</p>
+ <p>
+ Own Id: OTP-14775</p>
+ </item>
+ <item>
+ <p>
+ A race-condition when tearing down a connection with
+ active node monitors could cause the runtime system to
+ crash.</p>
+ <p>
+ This bug was introduced in ERTS version 8.0 (OTP 19.0).</p>
+ <p>
+ Own Id: OTP-14781 Aux Id: OTP-13047 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.5.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A timer internal bit-field used for storing scheduler id
+ was too small. As a result, VM internal timer data
+ structures could become inconsistent when using 1024
+ schedulers on the system. Note that systems with less
+ than 1024 schedulers are not effected by this bug.</p>
+ <p>
+ This bug was introduced in ERTS version 7.0 (OTP 18.0).</p>
+ <p>
+ Own Id: OTP-14548 Aux Id: OTP-11997, ERL-468 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>binary_to_term</c> and
+ <c>binary_to_atom</c> that could cause VM crash.
+ Typically happens when the last character of an UTF8
+ string is in the range 128 to 255, but truncated to only
+ one byte. Bug exists in <c>binary_to_term</c> since ERTS
+ version 5.10.2 (OTP_R16B01) and <c>binary_to_atom</c>
+ since ERTS version 9.0 (OTP-20.0).</p>
+ <p>
+ Own Id: OTP-14590 Aux Id: ERL-474 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug causing VM crash when a module with
+ <c>-on_load</c> directive is loaded while
+ <c>erlang:trace(on_load, ...)</c> is enabled.</p>
+ <p>
+ Own Id: OTP-14612</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug that could cause a VM crash when a corrupt
+ message is received on distribution channel from other
+ node.</p>
+ <p>
+ Own Id: OTP-14661 Aux Id: ERIERL-80 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix performance bug in pre-allocators that could cause
+ them to permanently fall back on normal more expensive
+ memory allocation. Pre-allocators are used for quick
+ allocation of short lived meta data used by messages and
+ other scheduled tasks. Bug exists since OTP_R15B02.</p>
+ <p>
+ Own Id: OTP-14491</p>
+ </item>
+ <item>
+ <p>Fixed a bug that prevented TCP sockets from being
+ closed properly on send timeouts.</p>
+ <p>
+ Own Id: OTP-14509</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in operator <c>bxor</c> causing erroneuos
+ result when one operand is a big <em>negative</em>
+ integer with the lowest <c>N*W</c> bits as zero and the
+ other operand not larger than <c>N*W</c> bits. <c>N</c>
+ is an integer of 1 or larger and <c>W</c> is 32 or 64
+ depending on word size.</p>
+ <p>
+ Own Id: OTP-14514</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.5.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a bug in gen_tcp:send where it never returned when
+ repeatedly called on a remotely closed TCP socket.</p>
+ <p>
+ Own Id: OTP-13939 Aux Id: ERL-193 </p>
+ </item>
+ <item>
+ <p>
+ Fixed segfault that could happen during cleanup of
+ aborted erlang:port_command/3 calls. A port_command is
+ aborted if the port is closed at the same time as the
+ port_command was issued. This bug was introduced in
+ erts-8.0.</p>
+ <p>
+ Own Id: OTP-14481</p>
+ </item>
+ <item>
+ <p>
+ Fixed implementation of <c>statistics(wall_clock)</c> and
+ <c>statistics(runtime)</c> so that values do not
+ unnecessarily wrap due to the emulator. Note that the
+ values returned by <c>statistics(runtime)</c> may still
+ wrap due to limitations in the underlying functionality
+ provided by the operating system.</p>
+ <p>
+ Own Id: OTP-14484</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Active-mode TCP sockets are now cleaned up properly on
+ send/shutdown errors.</p>
+ <p>
+ Own Id: OTP-14441 Aux Id: ERL-430 </p>
+ </item>
+ <item>
+ <p>
+ A code purge operation could under certain circumstances
+ expand the size of hibernated processes.</p>
+ <p>
+ Own Id: OTP-14444 Aux Id: ERIERL-24 </p>
+ </item>
+ <item>
+ <p>
+ Fix so that the ERL_ZZ_SIGTERM_KILL introduced in
+ erts-8.3.4 works.</p>
+ <p>
+ Own Id: OTP-14451</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add option to make SIGTERM trigger the OS default
+ behaviour instead of doing a gracefull shutdown. To
+ activate this bahviour the environment variable
+ ERL_ZZ_SIGTERM_KILL should be set to "true". This option
+ only works in OTP 19 as OTP 20 will have a different way
+ to deal with SIGTERM.</p>
+ <p>
+ Own Id: OTP-14418 Aux Id: ERIERL-15 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed memory segment cache used for multiblock carriers.
+ Huge (> 2GB) memory segments could cause a VM crash.
+ Creation of such huge memory segments used for multiblock
+ carriers is however very uncommon.</p>
+ <p>
+ Own Id: OTP-14360 Aux Id: ERL-401, PR-1417 </p>
+ </item>
+ <item>
+ <p>
+ Fix release note for OTP-14290 in ERTS version 8.3.1. It
+ was erroneously placed under "Known Bugs and Problems".</p>
+ <p>
+ Own Id: OTP-14363 Aux Id: OTP-14290 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The <c>+Bi</c> command line argument of <c>erl</c>
+ erroneously caused <c>SIGTERM</c> to be ignored by the VM
+ as well as of all its child processes. This bug was
+ introduced in erts version 8.3.</p>
+ <p>
+ Own Id: OTP-14358 Aux Id: OTP-14085 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Invoking <c>init:stop/0</c> via the SIGTERM signal, in a
+ non-SMP BEAM, could cause BEAM to terminate with fatal
+ error. This has now been fixed and the BEAM will
+ terminate normally when SIGTERM is received.</p>
+ <p>
+ Own Id: OTP-14290</p>
+ </item>
+ <item>
+ <p>
+ Trying to open a directory with file:read_file/1 on Unix
+ leaked a file descriptor. This bug has now been fixed.</p>
+ <p>
+ Own Id: OTP-14308 Aux Id: ERL-383 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a number of bugs that caused faulty stack-traces
+ to be generated. The faulty stack traces were generated
+ either when applying the following functions or tracing
+ the following functions:</p> <list>
+ <item><c>erlang:error/1</c></item>
+ <item><c>erlang:error/2</c></item>
+ <item><c>erlang:exit/1</c></item>
+ <item><c>erlang:throw/1</c></item> </list>
+ <p>
+ Own Id: OTP-14055</p>
+ </item>
+ <item>
+ <p>
+ Corrected documentation about memory footprint for maps.</p>
+ <p>
+ Own Id: OTP-14118</p>
+ </item>
+ <item>
+ <p>
+ Fix <c>process_info(Pid, current_stacktrace)</c> to use
+ stack depth limit set by
+ <c>system_flag(backtrace_depth)</c>. The old behavior was
+ a hard coded depth limit of 8.</p>
+ <p>
+ Own Id: OTP-14119 Aux Id: PR-1263 </p>
+ </item>
+ <item>
+ <p>
+ A process calling <seealso
+ marker="erlang#system_flag_multi_scheduling"><c>erlang:system_flag(multi_scheduling,
+ block)</c></seealso> could end up hanging forever in the
+ call.</p>
+ <p>
+ Own Id: OTP-14121</p>
+ </item>
+ <item>
+ <p>Dirty scheduler bug fixes:</p> <list> <item><p>Fixed
+ call time tracing of process being scheduled on dirty
+ scheduler.</p></item> <item><p>GC info from dirty
+ schedulers.</p></item> <item><p>Multi scheduling block
+ with dirty schedulers could crash the runtime
+ system.</p></item> <item><p>Process structures could be
+ removed prematurely.</p></item> <item><p>GC on dirty
+ scheduler could crash the runtime system.</p></item>
+ <item><p>Termination of a process executing on a dirty
+ scheduler could cause a runtime system crash.</p></item>
+ </list>
+ <p>
+ Own Id: OTP-14122</p>
+ </item>
+ <item>
+ <p>
+ Fixed crash that occurred when writing timer data to a
+ crash dump.</p>
+ <p>
+ Own Id: OTP-14133</p>
+ </item>
+ <item>
+ <p>
+ A literal area could be removed while still referred from
+ processes.</p>
+ <p>
+ Own Id: OTP-14134</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug in the garbage collector that could crash the
+ runtime system.</p>
+ <p>
+ Own Id: OTP-14135</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug in call-time trace for NIFs which caused
+ tracing to erroneously be started multiple times for one
+ call.</p>
+ <p>
+ Own Id: OTP-14136</p>
+ </item>
+ <item>
+ <p>
+ Remove a debug printout and an unnecessary garbage
+ collection when handling exceptions in hipe compiled
+ code.</p>
+ <p>
+ Own Id: OTP-14153</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in tracing of garbage collection that could cause
+ VM crash. Bug exists since OTP 19.0.</p>
+ <p>
+ Own Id: OTP-14154</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in <c>binary_to_term</c> for binaries created by
+ <c>term_to_binary </c> with option <c>compressed</c>. The
+ bug can cause <c>badarg</c> exception for a valid binary
+ when Erlang VM is linked against a <c>zlib</c> library of
+ version 1.2.9 or newer. Bug exists since OTP 17.0.</p>
+ <p>
+ Own Id: OTP-14159 Aux Id: ERL-340 </p>
+ </item>
+ <item>
+ <p>
+ Fix suspension of schedulers when generating a crashdump.</p>
+ <p>
+ Own Id: OTP-14164</p>
+ </item>
+ <item>
+ <p>NIF resources was not handled in a thread-safe manner
+ in the runtime system without SMP support.</p>
+ <p>As a consequence of this fix, the following driver
+ functions are now thread-safe also in the runtime system
+ without SMP support:</p> <list>
+ <item><p><c>driver_free_binary()</c></p></item>
+ <item><p><c>driver_realloc_binary()</c></p></item>
+ <item><p><c>driver_binary_get_refc()</c></p></item>
+ <item><p><c>driver_binary_inc_refc()</c></p></item>
+ <item><p><c>driver_binary_dec_refc()</c></p></item>
+ </list>
+ <p>
+ Own Id: OTP-14202</p>
+ </item>
+ <item>
+ <p>
+ Fix <c>erlang:round/1</c> for large floating point
+ numbers with an odd absolute value between <c>(1 bsl
+ 52)</c> and <c>(1 bsl 53)</c>. The result was falsely
+ calculated as the next higher even number even though all
+ integer values up to <c>(1 bsl 53)</c> can be represented
+ as floats with full precision.</p>
+ <p>
+ Own Id: OTP-14227</p>
+ </item>
+ <item>
+ <p>
+ Add size of literals to module code size in crash dump
+ and <c>(l)oaded</c> command in break menu like it used to
+ be before OTP-19.0.</p>
+ <p>
+ Own Id: OTP-14228</p>
+ </item>
+ <item>
+ <p>
+ Fix potential bug in <c>enif_send</c> when called without
+ a process context and with argument <c>msg_env</c> as
+ <c>NULL</c>.</p>
+ <p>
+ Own Id: OTP-14229</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where passing an appendable binary to
+ <c>erlang:port_control()</c> could crash the emulator.</p>
+ <p>
+ Own Id: OTP-14231</p>
+ </item>
+ <item>
+ <p>
+ Receive expressions with timeout in the Erlang shell
+ could cause a VM crash.</p>
+ <p>
+ Own Id: OTP-14241 Aux Id: ERL-365 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ A received SIGTERM signal to beam will generate a
+ <c>'stop'</c> message to the <c>init</c> process and
+ terminate the Erlang VM nicely. This is equivalent to
+ calling <c>init:stop/0</c>.</p>
+ <p>
+ Own Id: OTP-14085</p>
+ </item>
+ <item>
+ <p>
+ Workaround for buggy Android implementation of
+ <c>PTHREAD_STACK_MIN</c> causing build of runtime system
+ to crash on undeclared <c>PAGE_SIZE</c>.</p>
+ <p>
+ Own Id: OTP-14165 Aux Id: ERL-319 </p>
+ </item>
+ <item>
+ <p>
+ Add configure option --without-thread-names that removes
+ the naming of individual emulator threads.</p>
+ <p>
+ Own Id: OTP-14234</p>
+ </item>
+ <item>
+ <p>
+ Add warning in documentation of <c>zlib:deflateInit/6</c>
+ about option <c>WindowsBits</c> values 8 and -8.</p>
+ <p>
+ Own Id: OTP-14254 Aux Id: ERL-362 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug in <c>binary_to_term</c> for binaries created by
+ <c>term_to_binary </c> with option <c>compressed</c>. The
+ bug can cause <c>badarg</c> exception for a valid binary
+ when Erlang VM is linked against a <c>zlib</c> library of
+ version 1.2.9 or newer. Bug exists since OTP 17.0.</p>
+ <p>
+ Own Id: OTP-14159 Aux Id: ERL-340 </p>
+ </item>
+ <item>
+ <p>
+ The driver efile_drv when opening a file now use fstat()
+ on the open file instead of stat() before opening, if
+ fstat() exists. This avoids a race when the file happens
+ to change between stat() and open().</p>
+ <p>
+ Own Id: OTP-14184 Aux Id: seq-13266 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.2.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a quite rare bug causing VM crash during code loading
+ and the use of export funs (fun M:F/A) of not yet loaded
+ modules. Requires a very specfic timing of concurrent
+ scheduler threads. Has been seen on ARM but can probably
+ also occure on other architectures. Bug has existed since
+ OTP R16.</p>
+ <p>
+ Own Id: OTP-14144 Aux Id: seq13242 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed <c>configure</c> failures on MacOSX. Most important
+ <c>clock_gettime()</c> was detected when building for
+ MacOSX - El Capitan using XCode 8 despite it is not
+ available until MacOSX - Sierra.</p>
+ <p>
+ Own Id: OTP-13904 Aux Id: ERL-256 </p>
+ </item>
+ <item>
+ <p>
+ <c>code:add_pathsa/1</c> and command line option
+ <c>-pa</c> both revert the given list of directories when
+ adding it at the beginning of the code path. This is now
+ documented.</p>
+ <p>
+ Own Id: OTP-13920 Aux Id: ERL-267 </p>
+ </item>
+ <item>
+ <p>
+ Fix a compilation error of erts in OpenBSD related to the
+ usage of the __errno variable.</p>
+ <p>
+ Own Id: OTP-13927</p>
+ </item>
+ <item>
+ <p>
+ Fixed so that when enabling tracing on a process that had
+ an invalid tracer associated with it, the new tracer
+ overwrites the old tracer. Before this fix, calling
+ erlang:trace/3 would behave as if the tracer was still
+ alive and not apply the new trace.</p>
+ <p>
+ This fault was introduced in ERTS 8.0.</p>
+ <p>
+ Own Id: OTP-13928</p>
+ </item>
+ <item>
+ <p>
+ Fix parsing of <c>-profile_boot 'true' | 'false'</c></p>
+ <p>
+ Own Id: OTP-13955 Aux Id: ERL-280 </p>
+ </item>
+ <item>
+ <p>
+ A slight improvement of <c>erlang:get_stacktrace/0</c>
+ for exceptions raised in hipe compiled code. Beam
+ compiled functions in such stack trace was earlier
+ replaced by some unrelated function. They are now instead
+ omitted. This is an attempt to reduce the confusion in
+ the absence of a complete and correct stack trace for
+ mixed beam and hipe functions.</p>
+ <p>
+ Own Id: OTP-13992</p>
+ </item>
+ <item>
+ <p> Correct type declaration of match specification head.
+ </p>
+ <p>
+ Own Id: OTP-13996</p>
+ </item>
+ <item>
+ <p>
+ HiPE code loading failed for x86_64 if gcc was configured
+ with <c>--enable-default-pie</c>. Fixed by disabling PIE,
+ if needed for HiPE, when building the VM.</p>
+ <p>
+ Own Id: OTP-14031 Aux Id: ERL-294, PR-1239 </p>
+ </item>
+ <item>
+ <p>
+ Faulty arguments could be presented on exception from a
+ NIF that had rescheduled itself using
+ <c>enif_schedule_nif()</c>.</p>
+ <p>
+ Own Id: OTP-14048</p>
+ </item>
+ <item>
+ <p>
+ The runtime system could crash if a garbage collection on
+ a process was performed immediately after a NIF had been
+ rescheduled using <c>enif_schedule_nif()</c>.</p>
+ <p>
+ Own Id: OTP-14049</p>
+ </item>
+ <item>
+ <p>
+ A reference to purged code could be left undetected by
+ the purge operation if a process just had rescheduled a
+ NIF call using <c>enif_schedule_nif()</c> when the
+ process was checked. This could cause a runtime system
+ crash.</p>
+ <p>
+ Own Id: OTP-14050</p>
+ </item>
+ <item>
+ <p>Fixed a number of dirty scheduler related bugs:</p>
+ <list> <item><p>Process priority was not handled correct
+ when scheduling on a dirty scheduler.</p></item>
+ <item><p>The runtime system could crash when an exit
+ signal with a compound exit reason was sent to a process
+ executing on a dirty scheduler.</p></item> <item><p>The
+ runtime system crashed when call tracing a process
+ executing on a dirty scheduler.</p></item> <item><p>A
+ code purge operation could end up hanging forever when a
+ process executed on a dirty scheduler</p></item> </list>
+ <p>
+ Own Id: OTP-14051</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix minor soft purge race bug that could incorrectly
+ trigger code_server to load new code for the module if
+ the soft purge failed and no current version of the
+ module existed.</p>
+ <p>
+ Own Id: OTP-13925</p>
+ </item>
+ <item>
+ <p>
+ To ease troubleshooting, <c>erlang:load_nif/2</c> now
+ includes the return value from a failed call to
+ load/reload/upgrade in the text part of the error tuple.
+ The <c>crypto</c> NIF makes use of this feature by
+ returning the source line where/if the initialization
+ fails.</p>
+ <p>
+ Own Id: OTP-13951</p>
+ </item>
+ <item>
+ <p>
+ New environment variable <c>ERL_CRASH_DUMP_BYTES</c> can
+ be used to limit the size of crash dumps. If the limit is
+ reached, crash dump generation is aborted and the
+ generated file will be truncated.</p>
+ <p>
+ Own Id: OTP-14046</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.1.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A code purge operation could under certain circumstances
+ expand the size of hibernated processes.</p>
+ <p>
+ Own Id: OTP-14444 Aux Id: ERIERL-24 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The emulator got a dynamic library dependency towards
+ libsctp, which on Linux was not intended since the
+ emulator there loads and resolves the needed sctp
+ functions in runtime. This has been fixed and a configure
+ switch --enable-sctp=lib has been added for those who
+ want such a library dependency.</p>
+ <p>
+ Own Id: OTP-13956 Aux Id: ERL-262, ERL-133 </p>
+ </item>
+ <item>
+ <p>
+ Fix SIGUSR1 crashdump generation</p>
+ <p>
+ Do not generate a core when a crashdump is asked for.</p>
+ <p>
+ Own Id: OTP-13997</p>
+ </item>
+ <item>
+ <p>The new functions in <c>code</c> that allows loading
+ of many modules at once had a performance problem. While
+ executing a helper function in the <c>erl_prim_loader</c>
+ process, garbage messages were produced. The garbages
+ messages were ignored and ultimately discarded, but there
+ would be a negative impact on performance and memory
+ usage. The number of garbage message depended on both the
+ number of modules to be loaded and the length of the code
+ path.</p>
+ <p>The functions affected of this problem were:
+ <c>atomic_load/1</c>, <c>ensure_modules_loaded/1</c>, and
+ <c>prepare_loading/1</c>.</p>
+ <p>
+ Own Id: OTP-14009</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug for calls from hipe code to BIFs that disable GC
+ while yielding. Has been causing Dialyzer crashes on ARM
+ (and presumably all other non-intel platforms).</p>
+ <p>
+ Own Id: OTP-13724 Aux Id: PR-1116 </p>
+ </item>
+ <item>
+ <p>
+ Fix a bug where changing the current working directory of
+ the VM would not change the current working directory of
+ programs spawned using <c>erlang:open_port({spawn,""},
+ ...)</c>.</p>
+ <p>
+ Own Id: OTP-13733 Aux Id: ERL-175 </p>
+ </item>
+ <item>
+ <p>
+ Fix a bug where disabling tracing from a process that had
+ return_to tracing enabled and was tracing on
+ <c>erlang:trace/3</c> would cause a segmentation fault.</p>
+ <p>
+ Own Id: OTP-13734</p>
+ </item>
+ <item>
+ <p>
+ Update all erts documentation to use simpler English, use
+ consistent terminology and be easier to navigate.</p>
+ <p>
+ Own Id: OTP-13740</p>
+ </item>
+ <item>
+ <p>
+ Add dirty schedulers to the microstate accounting
+ statistics.</p>
+ <p>
+ Own Id: OTP-13744</p>
+ </item>
+ <item>
+ <p>
+ Fixed dirty scheduler build support on 32-bit windows.</p>
+ <p>
+ Own Id: OTP-13759</p>
+ </item>
+ <item>
+ <p>
+ inet:getstat(Socket) on an SCTP socket returned 0 for
+ send stats. This bug has now been corrected. Reported by
+ systra as issue ERL-102 on bugs.erlang.org.</p>
+ <p>
+ Own Id: OTP-13773 Aux Id: ERL-102 </p>
+ </item>
+ <item>
+ <p>
+ AF_UNSPEC and unknown address families were misread by
+ UDP receive in prim_inet resulting in an exception. This
+ bug has now been corrected.</p>
+ <p>
+ Own Id: OTP-13775</p>
+ </item>
+ <item>
+ <p>
+ Sweep HiPE stack for literals during code purge.</p>
+ <p>
+ Own Id: OTP-13777 Aux Id: PR-1122 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug in run_erl for OpenBSD that could cause it on
+ rare occations to exit without starting the program (erl)
+ at all.</p>
+ <p>
+ Own Id: OTP-13795</p>
+ </item>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug where init:stop could deadlock if a process
+ with infinite shutdown timeout (e.g. a supervisor)
+ attempted to load code while terminating.</p>
+ <p>
+ Own Id: OTP-13802</p>
+ </item>
+ <item>
+ <p>
+ Fixed a segmentation fault on sparc CPUs when free'ing a
+ tracer module's state.</p>
+ <p>
+ Own Id: OTP-13803</p>
+ </item>
+ <item>
+ <p>
+ <c>fun</c>s was not properly handled during purge of a
+ module. This could cause a crash of the VM after a purge
+ of a module.</p>
+ <p>
+ Own Id: OTP-13809</p>
+ </item>
+ <item>
+ <p>
+ Fixed a memory leak when the process monitoring a port
+ crashed.</p>
+ <p>
+ Own Id: OTP-13818</p>
+ </item>
+ <item>
+ <p>
+ Fixed multiple dirty scheduler related tracing bugs.</p>
+ <p>
+ Own Id: OTP-13822</p>
+ </item>
+ <item>
+ <p>
+ Fix error handling in beam code runtime loader for a
+ number of cases when index and size fields got corrupted
+ (negative) values.</p>
+ <p>
+ Own Id: OTP-13848 Aux Id: ERL-216 </p>
+ </item>
+ <item>
+ <p>
+ Minor fix of dirty scheduler implementation.</p>
+ <p>
+ Own Id: OTP-13852</p>
+ </item>
+ <item>
+ <p>
+ Calls to <c>erl_drv_send_term()</c> or
+ <c>erl_drv_output_term()</c> from a non-scheduler thread
+ while the corresponding port was invalid caused the
+ emulator to enter an inconsistent state which eventually
+ caused an emulator crash.</p>
+ <p>
+ Own Id: OTP-13866</p>
+ </item>
+ <item>
+ <p>
+ Fix a rare race condition in <c>erlang:open_port({spawn,
+ ""}, ...)</c> that would result in the erl_child_setup
+ program aborting and cause the emulator to exit.</p>
+ <p>
+ Own Id: OTP-13868</p>
+ </item>
+ <item>
+ <p>Driver and NIF operations accessing processes or ports
+ could cause an emulator crash when used from
+ non-scheduler threads. Those operations are:</p> <list>
+ <item><c>erl_drv_send_term()</c></item>
+ <item><c>driver_send_term()</c></item>
+ <item><c>erl_drv_output_term()</c></item>
+ <item><c>driver_output_term()</c></item>
+ <item><c>enif_send()</c></item>
+ <item><c>enif_port_command()</c></item> </list>
+ <p>
+ Own Id: OTP-13869</p>
+ </item>
+ <item>
+ <p>
+ Fix start scripts generation dependency in Makefile</p>
+ <p>
+ Own Id: OTP-13871 Aux Id: ERL-241 </p>
+ </item>
+ <item>
+ <p>
+ The VM could crash if <c>erlang:get_stacktrace()</c> was
+ called after a rescheduled NIF had thrown an exception.</p>
+ <p>
+ Own Id: OTP-13877</p>
+ </item>
+ <item>
+ <p>Calling <c>code:delete/1</c> before a loading a module
+ with an on_load function, the old code would be
+ overwritten, causing a memory or a crash if NIFs were
+ involved. (Thanks to vans163 for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-13893 Aux Id: ERL-240 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved accuracy of timeouts on MacOS X. This by setting
+ premature timeouts followed by a short actual timeout
+ during scheduler wait.</p>
+ <p>
+ Own Id: OTP-13698</p>
+ </item>
+ <item>
+ <p>Added the following symbolic time unit representations
+ to the <seealso
+ marker="erlang#type-time_unit"><c>erlang:time_unit()</c></seealso>
+ type:</p> <list> <item><c>second</c></item>
+ <item><c>millisecond</c></item>
+ <item><c>microsecond</c></item>
+ <item><c>nanosecond</c></item> </list> <p>The following
+ symbolic time unit representations are now
+ <em>deprecated</em>, but still part of the
+ <c>erlang:time_unit()</c> type:</p> <list>
+ <item><c>seconds</c></item>
+ <item><c>milli_seconds</c></item>
+ <item><c>micro_seconds</c></item>
+ <item><c>nano_seconds</c></item> </list>
+ <p>
+ Own Id: OTP-13735</p>
+ </item>
+ <item>
+ <p>
+ Fix maps hashing entropy of maps with maps keys.</p>
+ <p>
+ Own Id: OTP-13763 Aux Id: ERL-199 </p>
+ </item>
+ <item>
+ <p>
+ Improved dirty scheduler support. A purge of a module can
+ now be performed without having to wait for completion of
+ all ongoing dirty NIF calls.</p>
+ <p>
+ Note that when enabling support for dirty schedulers, a
+ new purge strategy will as of ERTS version 8.1 be
+ enabled. This new strategy is not fully backwards
+ compatible with the strategy used by default. For more
+ information see the documentation of <seealso
+ marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>.</p>
+ <p>
+ Own Id: OTP-13808 Aux Id: OTP-13833 </p>
+ </item>
+ <item>
+ <p>
+ A new purge strategy has been introduced. The new
+ strategy will by default be disabled during the OTP 19
+ release, but will be the only strategy available as of
+ the OTP 20 release.</p>
+ <p>
+ The new strategy is slightly incompatible with the
+ strategy being used by default in OTP 19. Using the
+ default strategy, processes holding <c>fun</c>s that
+ refer to the module being purged either fail a soft
+ purge, or will be killed during a hard purge. The new
+ strategy completely ignores <c>fun</c>s. If <c>fun</c>s
+ referring to the code being purged exist, and are used
+ after a purge, an exception will be raised upon usage.
+ That is, the behavior will be exactly the same as the
+ case when a <c>fun</c> is received by a process after the
+ purge.</p>
+ <p>
+ The new strategy can optionally be enabled when building
+ OTP during OTP 19, and will automatically be enabled if
+ the runtime system is built with support for dirty
+ schedulers.</p>
+ <p>
+ For more information see the documentation of <seealso
+ marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>.</p>
+ <p>
+ Own Id: OTP-13833</p>
+ </item>
+ <item>
+ <p>
+ Fixed unnecessary overestimation of heap size need during
+ garbage collection.</p>
+ <p>
+ Own Id: OTP-13851</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a VM crash that occured in a garbage collection of
+ a process when it had received binaries. This bug was
+ introduced in ERTS version 8.0 (OTP 19.0).</p>
+ <p>
+ Own Id: OTP-13890</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a VM crash that occured in garbage collection of a
+ process when it had received maps over the distribution.
+ This bug was introduced in ERTS version 8.0 (OTP 19.0).</p>
+ <p>
+ Own Id: OTP-13889</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a race that could cause a lost wakeup of a process
+ that timed out in a <c>receive ... after</c>. This bug
+ was introduced in ERTS version 7.0.</p>
+ <p>
+ Own Id: OTP-13798 Aux Id: OTP-11997 </p>
+ </item>
+ <item>
+ <p>
+ Fixed segfault after writing an erl crash dump.</p>
+ <p>
+ Own Id: OTP-13799</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix scheduler deadlock bug in <c>ets:update_counter/4</c>
+ when key is not found and inserting the default object
+ causes the table to grow.</p>
+ <p>
+ Own Id: OTP-13731 Aux Id: ERL-188 </p>
+ </item>
+ <item>
+ <p>
+ Fix VM abort "Overrun stack and heap" in garbage
+ collection triggered by a <c>bsl</c> operation and some
+ very specific heap conditions.</p>
+ <p>
+ Own Id: OTP-13732 Aux Id: seq13142 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 8.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A memory allocation bug in <c>group_leader/2</c> could
+ cause an emulator crash when garbage collecting a process
+ that had been assigned a remote group leader. This bug
+ was introduced in ERTS version 8.0.</p>
+ <p>
+ Own Id: OTP-13716</p>
+ </item>
+ </list>
+ </section>
+
+</section>
<section><title>Erts 8.0</title>
@@ -523,9 +1689,7 @@
Own Id: OTP-13440</p>
</item>
<item>
- <p>
- Add the following NIF API functions:</p>
- <p>
+ <p>Add the following NIF API functions:</p>
<list> <item><seealso
marker="erl_nif#enif_cpu_time"><c>enif_cpu_time</c></seealso></item>
<item><seealso
@@ -542,9 +1706,9 @@
marker="erl_nif#enif_binary_to_term"><c>enif_binary_to_term</c></seealso></item>
<item><seealso
marker="erl_nif#enif_port_command"><c>enif_port_command</c></seealso></item>
- </list></p>
+ </list>
<p>
- for details of what each function does, see the erl_nif
+ For details of what each function does, see the erl_nif
documentation.</p>
<p>
Own Id: OTP-13442</p>
@@ -9316,9 +10480,9 @@
dynamically linking against <c>libssl.so</c> and
<c>libcrypto.so</c>. The runtime library search path has
also been extended. </item><item> The <c>configure</c>
- scripts of <c>erl_interface</c> and <c>odbc</c> now
+ scripts of Erl_interface and ODBC now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
@@ -10434,7 +11598,7 @@
</item>
<item>
<p>
- A corrected bug in <c>ets</c> for <c>bag</c> and
+ A corrected bug in ETS for <c>bag</c> and
<c>duplicate_bag</c>. A <c>delete/2</c> or
<c>lookup_element/3</c> could miss objects in a fixed
table if one or more objects with the same key had
@@ -10876,7 +12040,7 @@
<list>
<item>
<p>
- A corrected bug in <c>ets</c> for <c>bag</c> and
+ A corrected bug in ETS for <c>bag</c> and
<c>duplicate_bag</c>. A <c>delete/2</c> or
<c>lookup_element/3</c> could miss objects in a fixed
table if one or more objects with the same key had
diff --git a/erts/doc/src/part.xml b/erts/doc/src/part.xml
index b2abfc62ca..d583b873a0 100644
--- a/erts/doc/src/part.xml
+++ b/erts/doc/src/part.xml
@@ -30,8 +30,8 @@
<file>part.xml</file>
</header>
<description>
- <p>The Erlang Runtime System Application <em>ERTS</em>.</p>
</description>
+ <xi:include href="introduction.xml"/>
<xi:include href="communication.xml"/>
<xi:include href="time_correction.xml"/>
<xi:include href="match_spec.xml"/>
diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml
index e45402a397..0617463a7b 100644
--- a/erts/doc/src/ref_man.xml
+++ b/erts/doc/src/ref_man.xml
@@ -30,14 +30,6 @@
<file>application.xml</file>
</header>
<description>
- <p>The Erlang Runtime System Application <em>ERTS</em>.</p>
- <note>
- <p>By default, the <c><![CDATA[erts]]></c> is only guaranteed to be compatible
- with other Erlang/OTP components from the same release as
- the <c><![CDATA[erts]]></c> itself. See the documentation of the system flag
- <seealso marker="erl#compat_rel">+R</seealso> on how to communicate
- with Erlang/OTP components from earlier releases.</p>
- </note>
</description>
<xi:include href="erl_prim_loader.xml"/>
<xi:include href="erlang.xml"/>
diff --git a/erts/doc/src/run_erl.xml b/erts/doc/src/run_erl.xml
index 6b0fef7c0a..a9b6a7e2c6 100644
--- a/erts/doc/src/run_erl.xml
+++ b/erts/doc/src/run_erl.xml
@@ -28,137 +28,183 @@
<docno></docno>
<approved></approved>
<checked></checked>
- <date>99-12-15</date>
+ <date>1999-12-15</date>
<rev></rev>
<file>run_erl.xml</file>
</header>
<com>run_erl</com>
- <comsummary>Redirect Erlang input and output streams on Solaris&reg;</comsummary>
+ <comsummary>Redirect Erlang input and output streams on Unix systems.</comsummary>
<description>
- <p>This describes the <c><![CDATA[run_erl]]></c> program specific to
- Solaris/Linux. This program redirect the standard input and standard
- output streams so that all output can be logged. It also let the
- program <c><![CDATA[to_erl]]></c> connect to the Erlang console making it
- possible to monitor and debug an embedded system remotely.</p>
- <p>You can read more about the use in the <c><![CDATA[Embedded System User's Guide]]></c>.</p>
+ <p>The <c><![CDATA[run_erl]]></c> program is specific to Unix systems.
+ This program redirects the standard input and standard
+ output streams so that all output can be logged. It also lets the
+ program <c><![CDATA[to_erl]]></c> connect to the Erlang console, making
+ it possible to monitor and debug an embedded system remotely.</p>
+
+ <p>For more information about the use, see the
+ <seealso marker="doc/embedded:embedded_solaris">
+ Embedded System User's Guide</seealso> in System Documentation.</p>
</description>
+
<funcs>
<func>
- <name>run_erl [-daemon] pipe_dir/ log_dir "exec command [command_arguments]"</name>
- <fsummary>Start the Erlang emulator without attached terminal</fsummary>
+ <name>run_erl [-daemon] pipe_dir/ log_dir "exec command
+ arg1 arg2 ..."</name>
+ <fsummary>Start the Erlang emulator without attached terminal.</fsummary>
<desc>
- <p>The <c><![CDATA[run_erl]]></c> program arguments are:</p>
+ <p>Arguments:</p>
<taglist>
- <tag>-daemon</tag>
- <item>This option is highly recommended. It makes run_erl run in
- the background completely detached from any controlling
- terminal and the command returns to the caller immediately.
- Without this option, run_erl must be started using several
- tricks in the shell to detach it completely from the
- terminal in use when starting it. The option must be the
- first argument to run_erl on the command line.</item>
- <tag>pipe_dir</tag>
- <item>This is where to put the named pipe, usually
- <c><![CDATA[/tmp/]]></c>. It shall be suffixed by a <c><![CDATA[/]]></c> (slash),
- i.e. not <c><![CDATA[/tmp/epipies]]></c>, but <c><![CDATA[/tmp/epipes/]]></c>. </item>
- <tag>log_dir</tag>
- <item>This is where the log files are written. There will be one
- log file, <c><![CDATA[run_erl.log]]></c> that log progress and
- warnings from the <c><![CDATA[run_erl]]></c> program itself and there
- will be up to five log files at maximum 100KB each (both
- number of logs and sizes can be
- changed by environment variables, see below) with
- the content of the standard streams from and to the
- command. When the logs are full <c><![CDATA[run_erl]]></c> will delete
- and reuse the oldest log file.</item>
- <tag>"exec command [command_arguments]"</tag>
- <item>In the third argument <c><![CDATA[command]]></c> is the to execute
- where everything written to stdin and stdout is logged to
- <c><![CDATA[log_dir]]></c>.</item>
+ <tag><c>-daemon</c></tag>
+ <item>
+ <p>This option is highly recommended. It makes <c>run_erl</c> run
+ in the background completely detached from any controlling
+ terminal and the command returns to the caller immediately.
+ Without this option, <c>run_erl</c> must be started using several
+ tricks in the shell to detach it completely from the
+ terminal in use when starting it. The option must be the
+ first argument to <c>run_erl</c> on the command line.</p>
+ </item>
+ <tag><c>pipe_dir</c></tag>
+ <item>
+ <p>The named pipe, usually <c><![CDATA[/tmp/]]></c>. It must be
+ suffixed by a <c><![CDATA[/]]></c> (slash), that is,
+ <c><![CDATA[/tmp/epipes/]]></c>, not
+ <c><![CDATA[/tmp/epipes]]></c>.</p>
+ </item>
+ <tag><c>log_dir</c></tag>
+ <item>
+ <p>The log files, that is:</p>
+ <list type="bulleted">
+ <item>
+ <p>One log file, <c><![CDATA[run_erl.log]]></c>, which logs
+ progress and warnings from the <c><![CDATA[run_erl]]></c>
+ program itself.</p>
+ </item>
+ <item>
+ <p>Up to five log files at maximum 100 KB each with the content
+ of the standard streams from and to the command. (Both the
+ number of logs and sizes can be changed by environment
+ variables, see section <seealso
+ marker="#environment_variables">Environment Variables</seealso>
+ below.)</p>
+ <p>When the logs are full, <c><![CDATA[run_erl]]></c> deletes
+ and reuses the oldest log file.</p>
+ </item>
+ </list>
+ </item>
+ <tag><c>"exec command arg1 arg2 ..."</c></tag>
+ <item>
+ <p>A space-separated string specifying the program to be executed.
+ The second field is typically a command name such as <c>erl</c>.</p>
+ </item>
</taglist>
</desc>
</func>
</funcs>
<section>
- <title>Notes concerning the log files</title>
- <p>While running, run_erl (as stated earlier) sends all output,
- uninterpreted, to a log file. The file is called
- <c><![CDATA[erlang.log.N]]></c>, where N is a number. When the log is "full",
- default after 100KB, run_erl starts to log in file
- <c><![CDATA[erlang.log.(N+1)]]></c>, until N reaches a certain number (default
- 5), where after N starts at 1 again and the oldest files start
- getting overwritten. If no output comes from the erlang shell, but
- the erlang machine still seems to be alive, an "ALIVE" message is
- written to the log, it is a timestamp and is written, by default,
- after 15 minutes of inactivity. Also, if output from erlang is
- logged but it's been more than 5 minutes (default) since last time
- we got anything from erlang, a timestamp is written in the
- log. The "ALIVE" messages look like this:</p>
+ <title>Notes concerning the Log Files</title>
+ <p>While running, <c>run_erl</c> sends all output,
+ uninterpreted, to a log file. The file is named
+ <c><![CDATA[erlang.log.N]]></c>, where <c>N</c> is an integer. When the
+ log is "full" (default log size is 100 KB), <c>run_erl</c> starts to log
+ in file <c><![CDATA[erlang.log.(N+1)]]></c>, until <c>N</c> reaches a
+ certain number (default 5), whereupon <c>N</c> starts at 1 again and
+ the oldest files start getting overwritten.</p>
+
+ <p>If no output comes from the Erlang shell, but
+ the Erlang machine still seems to be alive, an "ALIVE" message is
+ written to the log; it is a time stamp and is written, by default,
+ after 15 minutes of inactivity. Also, if output from Erlang is
+ logged, but more than 5 minutes (default) has passed since last time
+ we got anything from Erlang, a time stamp is written in the
+ log. The "ALIVE" messages look as follows:</p>
+
<code type="none"><![CDATA[
- ===== ALIVE <date-time-string>
- ]]></code>
- <p>while the other timestamps look like this:</p>
+===== ALIVE <date-time-string> ]]></code>
+
+ <p>The other time stamps look as follows:</p>
+
<code type="none"><![CDATA[
- ===== <date-time-string>
- ]]></code>
- <p>The <c><![CDATA[date-time-string]]></c> is the date and time the message is
- written, default in local time (can be changed to GMT if one wants
- to) and is formatted with the ANSI-C function <c><![CDATA[strftime]]></c>
- using the format string <c><![CDATA[%a %b %e %T %Z %Y]]></c>, which produces
- messages on the line of <c><![CDATA[===== ALIVE Thu May 15 10:13:36 MEST 2003]]></c>, this can be changed, see below.</p>
+===== <date-time-string> ]]></code>
+
+ <p><c><![CDATA[date-time-string]]></c> is the date and time the message is
+ written, default in local time (can be changed to UTC if needed).
+ It is formatted with the ANSI-C function <c><![CDATA[strftime]]></c>
+ using the format string <c><![CDATA[%a %b %e %T %Z %Y]]></c>, which
+ produces messages like
+ <c><![CDATA[===== ALIVE Thu May 15 10:13:36 MEST 2003]]></c>; this can
+ be changed, see the next section.</p>
</section>
<section>
- <title>Environment variables</title>
- <p>The following environment variables are recognized by run_erl
- and change the logging behavior. Also see the notes above to get
- more info on how the log behaves.</p>
+ <marker id="environment_variables"/>
+ <title>Environment Variables</title>
+ <p>The following environment variables are recognized by <c>run_erl</c>
+ and change the logging behavior. For more information, see the previous
+ section.</p>
+
<taglist>
- <tag>RUN_ERL_LOG_ALIVE_MINUTES</tag>
- <item>How long to wait for output (in minutes) before writing an
- "ALIVE" message to the log. Default is 15, can never be less
- than 1.</item>
- <tag>RUN_ERL_LOG_ACTIVITY_MINUTES</tag>
- <item>How long erlang need to be inactive before output will be
- preceded with a timestamp. Default is
- RUN_ERL_LOG_ALIVE_MINUTES div 3, but never less than 1.</item>
- <tag>RUN_ERL_LOG_ALIVE_FORMAT</tag>
- <item>Specifies another format string to be used in the strftime
- C library call. i.e specifying this to <c><![CDATA["%e-%b-%Y, %T %Z"]]></c>
- will give log messages with timestamps looking like
- <c><![CDATA[15-May-2003, 10:23:04 MET]]></c> etc. See the documentation
- for the C library function strftime for more
- information. Default is <c><![CDATA["%a %b %e %T %Z %Y"]]></c>.</item>
- <tag>RUN_ERL_LOG_ALIVE_IN_UTC</tag>
- <item>If set to anything else than "0", it will make all
- times displayed by run_erl to be in UTC (GMT,CET,MET, without
- DST), rather than
- in local time. This does not affect data coming from erlang,
- only the logs output directly by run_erl. The application
- <c><![CDATA[sasl]]></c> can be modified accordingly by setting the erlang
- application variable <c><![CDATA[utc_log]]></c> to <c><![CDATA[true]]></c>.</item>
- <tag>RUN_ERL_LOG_GENERATIONS</tag>
- <item>Controls the number of log files written before older
- files are being reused. Default is 5, minimum is 2, maximum is 1000.</item>
- <tag>RUN_ERL_LOG_MAXSIZE</tag>
- <item>The size (in bytes) of a log file before switching to a
- new log file. Default is 100000, minimum is 1000 and maximum is
- approximately 2^30.</item>
- <tag>RUN_ERL_DISABLE_FLOWCNTRL</tag>
- <item>If defined, disables input and output flow control for the pty opend by run_erl.
- Useful if you want to remove any risk of accidentally blocking the flow control
- by hit Ctrl-S (instead of Ctrl-D to detach).
- Which may result in blocking of the entire beam process
- and in the case of running heart as supervisor
- even the heart process will be blocked when writing log message to terminal.
- Leaving the heart process unable to do its work.</item>
+ <tag><c>RUN_ERL_LOG_ALIVE_MINUTES</c></tag>
+ <item>
+ <p>How long to wait for output (in minutes) before writing an
+ "ALIVE" message to the log. Defaults to 15, minimum is 1.</p>
+ </item>
+ <tag><c>RUN_ERL_LOG_ACTIVITY_MINUTES</c></tag>
+ <item>
+ <p>How long Erlang needs to be inactive before output is
+ preceded with a time stamp. Defaults to
+ <c>RUN_ERL_LOG_ALIVE_MINUTES div 3</c>, minimum is 1.</p>
+ </item>
+ <tag><c>RUN_ERL_LOG_ALIVE_FORMAT</c></tag>
+ <item>
+ <p>Specifies another format string to be used in the <c>strftime</c>
+ C library call. That is, specifying this to
+ <c><![CDATA["%e-%b-%Y, %T %Z"]]></c> gives
+ log messages with time stamps like
+ <c><![CDATA[15-May-2003, 10:23:04 MET]]></c>. For more information,
+ see the documentation for the C library function <c>strftime</c>.
+ Defaults to <c><![CDATA["%a %b %e %T %Z %Y"]]></c>.</p>
+ </item>
+ <tag><c>RUN_ERL_LOG_ALIVE_IN_UTC</c></tag>
+ <item>
+ <p>If set to anything else than <c>0</c>, it makes all
+ times displayed by <c>run_erl</c> to be in UTC (GMT, CET, MET,
+ without Daylight Saving Time), rather than in local time.
+ This does not affect data coming from Erlang,
+ only the logs output directly by <c>run_erl</c>. Application
+ SASL can be modified accordingly by setting the Erlang
+ application variable <c><![CDATA[utc_log]]></c> to
+ <c><![CDATA[true]]></c>.</p>
+ </item>
+ <tag><c>RUN_ERL_LOG_GENERATIONS</c></tag>
+ <item>
+ <p>Controls the number of log files written before older
+ files are reused. Defaults to 5, minimum is 2, maximum is 1000.</p>
+ </item>
+ <tag><c>RUN_ERL_LOG_MAXSIZE</c></tag>
+ <item>
+ <p>The size, in bytes, of a log file before switching to a
+ new log file. Defaults to 100000, minimum is 1000, maximum is
+ about 2^30.</p>
+ </item>
+ <tag><c>RUN_ERL_DISABLE_FLOWCNTRL</c></tag>
+ <item>
+ <p>If defined, disables input and output flow control for the pty
+ opend by <c>run_erl</c>. Useful if you want to remove any risk of
+ accidentally blocking the flow control by using Ctrl-S (instead of
+ Ctrl-D to detach), which can result in blocking of the entire Beam
+ process, and in the case of running heart as supervisor even the
+ heart process becomes blocked when writing log message to terminal,
+ leaving the heart process unable to do its work.</p>
+ </item>
</taglist>
</section>
<section>
- <title>SEE ALSO</title>
- <p>start(1), start_erl(1)</p>
+ <title>See Also</title>
+ <p><seealso marker="start"><c>start(1)</c></seealso>,
+ <seealso marker="start_erl"><c>start_erl(1)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/start.xml b/erts/doc/src/start.xml
index adacf5b98d..6eac47fe94 100644
--- a/erts/doc/src/start.xml
+++ b/erts/doc/src/start.xml
@@ -28,38 +28,46 @@
<docno></docno>
<approved></approved>
<checked></checked>
- <date>99-12-15</date>
+ <date>1999-12-15</date>
<rev></rev>
<file>start.xml</file>
</header>
<com>start</com>
- <comsummary>OTP start script example for Unix</comsummary>
+ <comsummary>OTP start script example for Unix.</comsummary>
<description>
- <p>This describes the <c><![CDATA[start]]></c> script that is an example script on
- how to startup the Erlang system in embedded mode on Unix.</p>
- <p>You can read more about the use in the <c><![CDATA[Embedded System User's Guide]]></c>.</p>
+ <p>The <c><![CDATA[start]]></c> script is an example script on
+ how to start up the Erlang system in embedded mode on Unix.</p>
+
+ <p>For more information about the use, see the
+ <seealso marker="doc/embedded:embedded_solaris">
+ Embedded System User's Guide</seealso> in System Documentation.</p>
</description>
+
<funcs>
<func>
<name>start [ data_file ]</name>
- <fsummary>This is an example script on how to startup the Erlang system in embedded mode on Unix.</fsummary>
+ <fsummary>Example script on how to start up the Erlang system in embedded
+ mode on Unix.</fsummary>
<desc>
- <p>In the example there is one argument</p>
+ <p>Argument:</p>
<taglist>
- <tag>data_file</tag>
- <item>Optional, specifies what <c><![CDATA[start_erl.data]]></c> file
- to use.</item>
+ <tag><c>data_file</c></tag>
+ <item>
+ <p>Optional. Specifies what <c><![CDATA[start_erl.data]]></c> file
+ to use.</p>
+ </item>
</taglist>
- <p>There is also an environment variable <c><![CDATA[RELDIR]]></c> that can
- be set prior to calling this example that set the directory
+ <p>Environment variable <c><![CDATA[RELDIR]]></c> can
+ be set before calling this example, which sets the directory
where to find the release files.</p>
</desc>
</func>
</funcs>
<section>
- <title>SEE ALSO</title>
- <p>run_erl(1), start_erl(1)</p>
+ <title>See Also</title>
+ <p><seealso marker="run_erl"><c>run_erl(1)</c></seealso>,
+ <seealso marker="start_erl"><c>start_erl(1)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/start_erl.xml b/erts/doc/src/start_erl.xml
index 243aeaa717..4887d4606e 100644
--- a/erts/doc/src/start_erl.xml
+++ b/erts/doc/src/start_erl.xml
@@ -28,117 +28,142 @@
<docno></docno>
<approved></approved>
<checked></checked>
- <date>98-08-05</date>
+ <date>1998-08-05</date>
<rev></rev>
<file>start_erl.xml</file>
</header>
<com>start_erl</com>
- <comsummary>Start Erlang for embedded systems on Windows NT&reg;</comsummary>
+ <comsummary>Start Erlang for embedded systems on Windows systems.</comsummary>
<description>
- <p>This describes the <c><![CDATA[start_erl]]></c> program specific to Windows
- NT. Although there exists programs with the same name on other
- platforms, their functionality is not the same.</p>
- <p>The <c><![CDATA[start_erl]]></c> program is distributed both in compiled
+ <p>The <c><![CDATA[start_erl]]></c> program is specific to
+ Windows NT/2000/XP (and later versions of Windows).
+ Although there are programs with the same name on other
+ platforms, their functionality is different.</p>
+
+ <p>This program is distributed both in compiled
form (under &lt;Erlang root&gt;\\erts-&lt;version&gt;\\bin) and
- in source form (under &lt;Erlang
- root&gt;\\erts-&lt;version&gt;\\src).
- The purpose of the source code is to make it possible to easily
- customize the program for local needs, such as cyclic restart
- detection etc. There is also a "make"-file, written for the
- <c><![CDATA[nmake]]></c> program distributed with Microsoft&reg; Visual
- C++&reg;. The program can however be compiled with
- any Win32 C compiler (possibly with slight modifications).</p>
- <p>The purpose of the program is to aid release handling on
- Windows NT&reg;. The program should be called by the
+ in source form (under &lt;Erlang root&gt;\\erts-&lt;version&gt;\\src).
+ The purpose of the source code is to ease customization of the
+ program for local needs, such as cyclic restart
+ detection. There is also a "make"-file, written for the
+ <c><![CDATA[nmake]]></c> program distributed with Microsoft Visual
+ C++. This program can, however, be compiled with
+ any Win32 C compiler (possibly with minor modifications).</p>
+
+ <p>This program aids release handling on Windows systems.
+ The program is to be called by the
<c><![CDATA[erlsrv]]></c> program, read up the release data file
- start_erl.data and start Erlang. Certain options to start_erl
- are added and removed by the release handler during upgrade with
- emulator restart (more specifically the <c><![CDATA[-data]]></c> option).</p>
+ <c>start_erl.data</c>, and start Erlang. Some options to
+ <c>start_erl</c> are added and removed by the release handler
+ during upgrade with emulator restart (more specifically option
+ <c><![CDATA[-data]]></c>).</p>
</description>
+
<funcs>
<func>
<name>start_erl [&lt;erl options>] ++ [&lt;start_erl options>]</name>
- <fsummary>Start the Erlang emulator with the correct release data</fsummary>
+ <fsummary>Start the Erlang emulator with the correct release data.
+ </fsummary>
<desc>
- <p>The <c><![CDATA[start_erl]]></c> program in its original form
+ <p>The <c><![CDATA[start_erl]]></c> program in its original form
recognizes the following options:</p>
<taglist>
- <tag>++</tag>
- <item>Mandatory, delimits start_erl options from normal Erlang
- options. Everything on the command line <em>before</em> the
- <c><![CDATA[++]]></c> is interpreted as options to be sent to the
- <c><![CDATA[erl]]></c> program. Everything <em>after</em><c><![CDATA[++]]></c> is
- interpreted as options to <c><![CDATA[start_erl]]></c> itself.</item>
- <tag>-reldir &lt;release root&gt;</tag>
-
- <item>Mandatory if the environment variable
- <c><![CDATA[RELDIR]]></c> is not specified and no
- <c>-rootdir</c> option is given. Tells start_erl where the
- root of the release tree is placed in the file-system (typically
- &lt;Erlang root&gt;\\releases). The
- <c><![CDATA[start_erl.data]]></c> file is expected to be
- placed in this directory (if not otherwise specified). If
- only the <c>-rootdir</c> option is given, the directory is
- assumed to be &lt;Erlang root&gt;\\releases.</item>
-
- <tag>-rootdir &lt;Erlang root directory&gt;</tag>
-
- <item>Mandatory if <c>-reldir</c> is not given and there is
- no <c><![CDATA[RELDIR]]></c> in the environment. This
- specifies the Erlang installation root directory (under
- which the <c>lib</c>, <c>releases</c> and
- <c>erts-&lt;Version&gt;</c> directories are placed). If only
- <c>-reldir</c> (or the environment variable
- <c><![CDATA[RELDIR]]></c>) is given, the Erlang root is assumed to
- be the directory exactly one level above the release
- directory.</item>
-
- <tag>-data &lt;data file name&gt;</tag>
- <item>Optional, specifies another data file than start_erl.data
- in the &lt;release root&gt;. It is specified relative to the
- &lt;release root&gt; or absolute (including drive letter
- etc.). This option is used by the release handler during
- upgrade and should not be used during normal
- operation. The release data file should not normally be
- named differently.</item>
- <tag>-bootflags &lt;boot flags file name&gt;</tag>
- <item>Optional, specifies a file name relative to actual release
- directory (that is the subdirectory of &lt;release
- root&gt; where the <c><![CDATA[.boot]]></c> file etc. are placed).
- The contents of this file is appended to the command line
- when Erlang is started. This makes it easy to start the
- emulator with different options for different releases.</item>
+ <tag><c>++</c></tag>
+ <item>
+ <p>Mandatory. Delimits <c>start_erl</c> options from normal Erlang
+ options. Everything on the command line <em>before</em>
+ <c><![CDATA[++]]></c> is interpreted as options to be sent to the
+ <c><![CDATA[erl]]></c> program. Everything <em>after</em>
+ <c><![CDATA[++]]></c> is interpreted as options to
+ <c><![CDATA[start_erl]]></c> itself.</p>
+ </item>
+ <tag><c>-reldir &lt;release root&gt;</c></tag>
+ <item>
+ <p>Mandatory if environment variable
+ <c><![CDATA[RELDIR]]></c> is not specified and no
+ <c>-rootdir</c> option is specified. Tells <c>start_erl</c> where
+ the root of the release tree is located in the file system
+ (typically &lt;Erlang root&gt;\\releases). The
+ <c><![CDATA[start_erl.data]]></c> file is expected to be
+ located in this directory (unless otherwise specified). If
+ only option <c>-rootdir</c> is specified, the directory is
+ assumed to be &lt;Erlang root&gt;\\releases.</p>
+ </item>
+ <tag><c>-rootdir &lt;Erlang root directory&gt;</c></tag>
+ <item>
+ <p>Mandatory if <c>-reldir</c> is not specified and no
+ <c><![CDATA[RELDIR]]></c> exists in the environment. This
+ specifies the Erlang installation root directory (under
+ which the <c>lib</c>, <c>releases</c>, and
+ <c>erts-&lt;Version&gt;</c> directories are located). If only
+ <c>-reldir</c> (or environment variable <c><![CDATA[RELDIR]]></c>)
+ is specified, the Erlang root is assumed to
+ be the directory exactly one level above the release
+ directory.</p>
+ </item>
+ <tag><c>-data &lt;data file name&gt;</c></tag>
+ <item>
+ <p>Optional. Specifies another data file than <c>start_erl.data</c>
+ in the &lt;release root&gt;. It is specified relative to the
+ &lt;release root&gt; or absolute (including drive letter, and so
+ on). This option is used by the release handler during
+ upgrade and is not to be used during normal
+ operation. Normally the release data file is not to be
+ named differently.</p>
+ </item>
+ <tag><c>-bootflags &lt;boot flags file name&gt;</c></tag>
+ <item>
+ <p>Optional. Specifies a file name relative to the release
+ directory (that is, the subdirectory of &lt;release root&gt;
+ where the <c><![CDATA[.boot]]></c> file and others are located).
+ The contents of this file is appended to the command line
+ when Erlang is started. This makes it easy to start the
+ emulator with different options for different releases.</p>
+ </item>
</taglist>
</desc>
</func>
</funcs>
<section>
- <title>NOTES</title>
- <p>As the source code is distributed, it can easily be modified to
- accept other options. The program must still accept the
- <c><![CDATA[-data]]></c> option with the semantics described above for the
- release handler to work correctly.</p>
- <p>The Erlang emulator is found by examining the registry keys for
- the emulator version specified in the release data file. The new
- emulator needs to be properly installed before the upgrade for
- this to work.</p>
- <p>Although the program is located together with files specific to
- emulator version, it is not expected to be specific to the
- emulator version. The release handler does <em>not</em> change the
- <c><![CDATA[-machine]]></c> option to <c><![CDATA[erlsrv]]></c> during emulator restart.
- Place the (possibly customized) <c><![CDATA[start_erl]]></c> program so that
- it is not overwritten during upgrade. </p>
- <p>The <c><![CDATA[erlsrv]]></c> program's default options are not
- sufficient for release handling. The machine <c><![CDATA[erlsrv]]></c>
- starts should be specified as the <c><![CDATA[start_erl]]></c> program and
- the arguments should contain the <c><![CDATA[++]]></c> followed by desired
- options.</p>
+ <title>Notes</title>
+ <list type="bulleted">
+ <item>
+ <p>As the source code is distributed, it can easily be modified to
+ accept other options. The program must still accept option
+ <c><![CDATA[-data]]></c> with the semantics described above for the
+ release handler to work correctly.</p>
+ </item>
+ <item>
+ <p>The Erlang emulator is found by examining the registry keys for
+ the emulator version specified in the release data file. The new
+ emulator must be properly installed before the upgrade for
+ this to work.</p>
+ </item>
+ <item>
+ <p>Although the program is located together with files specific to the
+ emulator version, it is not expected to be specific to the
+ emulator version. The release handler does <em>not</em> change option
+ <c><![CDATA[-machine]]></c> to <c><![CDATA[erlsrv]]></c> during
+ emulator restart. Locate the (possibly customized)
+ <c><![CDATA[start_erl]]></c> program so that it is not overwritten
+ during upgrade.</p>
+ </item>
+ <item>
+ <p>The default options of the <c><![CDATA[erlsrv]]></c> program are not
+ sufficient for release handling. The machine started by
+ <c><![CDATA[erlsrv]]></c> is be specified as the
+ <c><![CDATA[start_erl]]></c> program and the arguments are to contain
+ <c><![CDATA[++]]></c> followed by the desired options.</p>
+ </item>
+ </list>
</section>
<section>
- <title>SEE ALSO</title>
- <p>erlsrv(1), release_handler(3)</p>
+ <title>See Also</title>
+ <p><seealso marker="erlsrv"><c>erlsrv(1)</c></seealso>,
+ <seealso marker="sasl:release_handler">
+ <c>release_handler(3)</c></seealso></p>
</section>
</comref>
diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml
index 236fe679cb..77e7a40529 100644
--- a/erts/doc/src/time_correction.xml
+++ b/erts/doc/src/time_correction.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2015</year>
+ <year>1999</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -35,12 +35,12 @@
<section>
<title>New Extended Time Functionality</title>
- <note><p>As of OTP 18 (<c>ERTS</c> version 7.0) the time functionality of
- Erlang has been extended. This includes a
+ <note><p>As from Erlang/OTP 18 (ERTS 7.0) the time functionality
+ has been extended. This includes a
<seealso marker="#The_New_Time_API">new API</seealso>
for time and
<seealso marker="#Time_Warp_Modes">time warp
- modes</seealso> that alter the system behavior when
+ modes</seealso> that change the system behavior when
system time changes.</p>
<p>The <seealso marker="#No_Time_Warp_Mode">default
@@ -106,14 +106,18 @@
<section>
<title>POSIX Time</title>
<p>Time since
- <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_17">Epoch</url>.
+ <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_17">
+ Epoch</url>.
Epoch is defined to be 00:00:00 <seealso marker="#UTC">UTC</seealso>,
1970-01-01.
- <url href="http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap04.html#tag_04_14">A day in POSIX time</url>
- is defined to be exactly 86400 seconds long. Strangely enough
+ <url href="http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap04.html#tag_04_14">
+ A day in POSIX time</url>
+ is defined to be exactly 86400 seconds long. Strangely enough,
Epoch is defined to be a time in UTC, and UTC has another
definition of how long a day is. Quoting the Open Group
- <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_15">"POSIX time is therefore not necessarily UTC, despite its appearance"</url>.
+ <url href="http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_15">
+ "POSIX time is therefore not necessarily UTC, despite its
+ appearance"</url>.
The effect of this is that when an UTC leap second is
inserted, POSIX time either stops for a second, or repeats the
last second. If an UTC leap second would be deleted (which has not
@@ -157,7 +161,8 @@
<p>The operating systems view of
<seealso marker="#POSIX_Time">POSIX time</seealso>. To
retrieve it, call
- <seealso marker="kernel:os#system_time/0"><c>os:system_time()</c></seealso>.
+ <seealso marker="kernel:os#system_time/0">
+ <c>os:system_time()</c></seealso>.
This may or may not be an accurate view of POSIX time. This time
may typically be adjusted both backwards and forwards without
limitation. That is, <seealso marker="#Time_Warp">time warps</seealso>
@@ -165,25 +170,26 @@
<p>To get information about the Erlang runtime
system's source of OS system time, call
- <seealso marker="erlang#system_info_os_system_time_source"><c>erlang:system_info(os_system_time_source)</c></seealso>.</p>
+ <seealso marker="erlang#system_info_os_system_time_source">
+ <c>erlang:system_info(os_system_time_source)</c></seealso>.</p>
</section>
<marker id="OS_Monotonic_Time"/>
<section>
<title>OS Monotonic Time</title>
- <p>A monotonically increasing time provided by the operating
- system. This time does not leap and has a relatively steady
+ <p>A monotonically increasing time provided by the OS.
+ This time does not leap and has a relatively steady
frequency although not completely correct. However, it is not
uncommon that OS monotonic time stops if the system is
suspended. This time typically increases since some
unspecified point in time that is not connected to
<seealso marker="#OS_System_Time">OS system time</seealso>.
- This type of time is not necessarily provided by all
- operating systems.</p>
+ This type of time is not necessarily provided by all OSs.</p>
<p>To get information about the Erlang
runtime system's source of OS monotonic time, call
- <seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso>.</p>
+ <seealso marker="erlang#system_info_os_monotonic_time_source">
+ <c>erlang:system_info(os_monotonic_time_source)</c></seealso>.</p>
</section>
<marker id="Erlang_System_Time"/>
@@ -192,7 +198,8 @@
<p>The Erlang runtime systems view of
<seealso marker="#POSIX_Time">POSIX time</seealso>. To
retrieve it, call
- <seealso marker="erlang#system_time/0"><c>erlang:system_time()</c></seealso>.</p>
+ <seealso marker="erlang#system_time/0">
+ <c>erlang:system_time()</c></seealso>.</p>
<p>This time may or may not be an accurate view of POSIX time,
and may
@@ -211,8 +218,8 @@
<p>A monotonically increasing time provided by the
Erlang runtime system. Erlang monotonic time increases since
some unspecified point in time. To retrieve it, call
- <seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso>.
- </p>
+ <seealso marker="erlang#monotonic_time/0">
+ <c>erlang:monotonic_time()</c></seealso>.</p>
<p>The <seealso marker="#Time_Accuracy">accuracy</seealso> and
<seealso marker="#Time_Precision">precision</seealso> of Erlang
@@ -225,7 +232,8 @@
<item>Accuracy and precision of
<seealso marker="#OS_System_Time">OS system time</seealso>
</item>
- <item><seealso marker="#Time_Warp_Modes">time warp mode</seealso> used
+ <item><seealso marker="#Time_Warp_Modes">
+ time warp mode</seealso> used
</item>
</list>
@@ -238,16 +246,17 @@
time is the "time engine" that is used for more or less
everything that has anything to do with time. All timers,
regardless of it is a <c>receive ... after</c> timer, BIF timer,
- or a timer in the <c>timer</c> module, are triggered
- relative Erlang monotonic time. Even
+ or a timer in the
+ <seealso marker="stdlib:timer"><c>timer(3)</c></seealso>
+ module, are triggered relative Erlang monotonic time. Even
<seealso marker="#Erlang_System_Time">Erlang system
time</seealso> is based on Erlang monotonic time.
By adding current Erlang monotonic time with current time
offset, you get current Erlang system time.</p>
- <p>To retrieve current time offset, call
- <seealso marker="erlang#time_offset/0"><c>erlang:time_offset/0</c></seealso>.
- </p>
+ <p>To retrieve the current time offset, call
+ <seealso marker="erlang#time_offset/0">
+ <c>erlang:time_offset/0</c></seealso>.</p>
</section>
</section>
@@ -278,7 +287,7 @@
less potent time-keeper than an atomic clock is used.</p>
<p>However, NTP is not fail-safe. The NTP server can be unavailable,
- <c>ntp.conf</c> can be wrongly configured, or your computer may
+ <c>ntp.conf</c> can be wrongly configured, or your computer can
sometimes be disconnected from Internet. Furthermore, you can have a
user (or even system administrator) who thinks the correct
way to handle Daylight Saving Time is to adjust the clock one
@@ -327,16 +336,18 @@
implementation in the Erlang runtime system using
OS monotonic time. To check if your system has support
for OS monotonic time, call
- <seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso>.
+ <seealso marker="erlang#system_info_os_monotonic_time_source">
+ <c>erlang:system_info(os_monotonic_time_source)</c></seealso>.
To check if time correction is enabled on your system, call
- <seealso marker="erlang#system_info_time_correction"><c>erlang:system_info(time_correction)</c></seealso>.</p>
+ <seealso marker="erlang#system_info_time_correction">
+ <c>erlang:system_info(time_correction)</c></seealso>.</p>
<p>To enable or disable time correction, pass command-line argument
- <seealso marker="erl#+c"><c>+c [true|false]</c></seealso>
- to <c>erl</c>.</p>
+ <seealso marker="erl#+c"><c>+c [true|false]</c></seealso> to
+ <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
<p>If time correction is disabled, Erlang monotonic time
- may warp forwards or stop, or even freeze for extended
+ can warp forwards or stop, or even freeze for extended
periods of time. There are then no guarantees that the frequency
of the Erlang monotonic clock is accurate or stable.</p>
@@ -369,7 +380,7 @@
<c>erlang:now/0</c> are suboptimal</em> from a performance
and scalability perspective. So you really want to replace
the use of it with other functionality. For examples
- of how to replace the use of <c>erlang:now/0</c>, see Section
+ of how to replace the use of <c>erlang:now/0</c>, see section
<seealso marker="#Dos_and_Donts">How to Work with the New
API</seealso>.</p>
</section>
@@ -378,7 +389,7 @@
<title>Time Warp Modes</title>
<marker id="Time_Warp_Modes"/>
<p>Current <seealso marker="#Erlang_System_Time">Erlang system
- time</seealso> is determined by adding current
+ time</seealso> is determined by adding the current
<seealso marker="erlang#monotonic_time/0">Erlang monotonic time</seealso>
with current
<seealso marker="erlang#time_offset/0">time offset</seealso>. The
@@ -387,8 +398,8 @@
<p>To set the time warp mode, pass command-line argument
<seealso marker="erl#+C_"><c>+C
- [no_time_warp|single_time_warp|multi_time_warp]</c></seealso>
- to <c>erl</c>.</p>
+ [no_time_warp|single_time_warp|multi_time_warp]</c></seealso> to
+ <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
<marker id="No_Time_Warp_Mode"/>
<section>
@@ -397,8 +408,8 @@
and does not change later. This is the default behavior, but
not because it is the best mode (which it is not). It is
default <em>only</em> because this is how the runtime system
- behaved until <c>ERTS</c> 7.0.
- Ensure that your Erlang code that may execute during a time
+ behaved until ERTS 7.0.
+ Ensure that your Erlang code that can execute during a time
warp is <seealso marker="#Time_Warp_Safe_Code">time warp
safe</seealso> before enabling other modes.</p>
@@ -422,8 +433,8 @@
<marker id="Single_Time_Warp_Mode"/>
<section>
<title>Single Time Warp Mode</title>
- <p>This mode is more or less a backwards compatibility mode
- as of its introduction.</p>
+ <p>This mode is more or less a backward compatibility mode
+ as from its introduction.</p>
<p>On an embedded system it is not uncommon that the system
has no power supply, not even a battery, when it is
@@ -475,11 +486,12 @@
<item>
<p>This phase begins when the user finalizes the time
offset by calling
- <seealso marker="erlang#system_flag_time_offset"><c>erlang:system_flag(time_offset, finalize)</c></seealso>.
+ <seealso marker="erlang#system_flag_time_offset">
+ <c>erlang:system_flag(time_offset, finalize)</c></seealso>.
The finalization can only be performed once.</p>
<p>During finalization, the time offset is adjusted and
- fixated so that current Erlang system time aligns with
+ fixed so that current Erlang system time aligns with the
current OS system time. As the time offset can
change during the finalization, Erlang system time
can do a time warp at this point. The time offset is
@@ -488,7 +500,7 @@
correction from now on also makes adjustments
to align Erlang system time with OS system
time. When the system is in the final phase, it behaves
- exactly as in the <seealso marker="#No_Time_Warp_Mode">no
+ exactly as in <seealso marker="#No_Time_Warp_Mode">no
time warp mode</seealso>.</p>
</item>
</taglist>
@@ -520,7 +532,7 @@
may behave very bad.</p>
<p>Assuming that these requirements are fulfilled,
- time correction is enabled, and that OS system time
+ time correction is enabled, and OS system time
is adjusted using a time adjustment protocol such as NTP,
only small adjustments of Erlang monotonic
time are needed to keep system times
@@ -529,9 +541,10 @@
inserted (or deleted) leap seconds.</p>
<warning><p>To use this mode, ensure that
- all Erlang code that will execute in both phases are
+ all Erlang code that will execute in both phases is
<seealso marker="#Time_Warp_Safe_Code">time warp
safe</seealso>.</p>
+
<p>Code executing only in the final phase does not have
to be able to cope with the time warp.</p></warning>
</section>
@@ -542,13 +555,13 @@
<p><em>Multi-time warp mode in combination with time
correction is the preferred configuration</em>. This as
the Erlang runtime system have better performance, scale
- better, and behave better on almost all platforms. In
- addition, the accuracy and precision of time measurements
+ better, and behave better on almost all platforms.
+ Also, the accuracy and precision of time measurements
are better. Only Erlang runtime systems executing on
ancient platforms benefit from another configuration.</p>
- <p>The time offset may change at any time without limitations.
- That is, Erlang system time may perform time warps both
+ <p>The time offset can change at any time without limitations.
+ That is, Erlang system time can perform time warps both
forwards and backwards at <em>any</em> time. As we align
Erlang system time with OS system time by changing
the time offset, we can enable a time correction that tries
@@ -582,8 +595,8 @@
such issues. To improve this, the new API spreads different
functionality over multiple functions.</p>
- <p>To be backwards compatible, <c>erlang:now/0</c>
- remains as is, but <em>you are strongly discouraged from using
+ <p>To be backward compatible, <c>erlang:now/0</c>
+ remains "as is", but <em>you are strongly discouraged from using
it</em>. Many use cases of <c>erlang:now/0</c>
prevents you from using the new
<seealso marker="#Multi_Time_Warp_Mode">multi-time warp
@@ -597,38 +610,60 @@
<p>The new API consists of the following new BIFs:</p>
<list>
- <item><p><seealso marker="erlang#convert_time_unit/3"><c>erlang:convert_time_unit/3</c></seealso></p></item>
- <item><p><seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time/0</c></seealso></p></item>
- <item><p><seealso marker="erlang#monotonic_time/1"><c>erlang:monotonic_time/1</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_time/0"><c>erlang:system_time/0</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_time/1"><c>erlang:system_time/1</c></seealso></p></item>
- <item><p><seealso marker="erlang#time_offset/0"><c>erlang:time_offset/0</c></seealso></p></item>
- <item><p><seealso marker="erlang#time_offset/1"><c>erlang:time_offset/1</c></seealso></p></item>
- <item><p><seealso marker="erlang#timestamp/0"><c>erlang:timestamp/0</c></seealso></p></item>
- <item><p><seealso marker="erlang#unique_integer/0"><c>erlang:unique_integer/0</c></seealso></p></item>
- <item><p><seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer/1</c></seealso></p></item>
- <item><p><seealso marker="kernel:os#system_time/0"><c>os:system_time/0</c></seealso></p></item>
- <item><p><seealso marker="kernel:os#system_time/1"><c>os:system_time/1</c></seealso></p></item>
+ <item><p><seealso marker="erlang#convert_time_unit/3">
+ <c>erlang:convert_time_unit/3</c></seealso></p></item>
+ <item><p><seealso marker="erlang#monotonic_time/0">
+ <c>erlang:monotonic_time/0</c></seealso></p></item>
+ <item><p><seealso marker="erlang#monotonic_time/1">
+ <c>erlang:monotonic_time/1</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_time/0">
+ <c>erlang:system_time/0</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_time/1">
+ <c>erlang:system_time/1</c></seealso></p></item>
+ <item><p><seealso marker="erlang#time_offset/0">
+ <c>erlang:time_offset/0</c></seealso></p></item>
+ <item><p><seealso marker="erlang#time_offset/1">
+ <c>erlang:time_offset/1</c></seealso></p></item>
+ <item><p><seealso marker="erlang#timestamp/0">
+ <c>erlang:timestamp/0</c></seealso></p></item>
+ <item><p><seealso marker="erlang#unique_integer/0">
+ <c>erlang:unique_integer/0</c></seealso></p></item>
+ <item><p><seealso marker="erlang#unique_integer/1">
+ <c>erlang:unique_integer/1</c></seealso></p></item>
+ <item><p><seealso marker="kernel:os#system_time/0">
+ <c>os:system_time/0</c></seealso></p></item>
+ <item><p><seealso marker="kernel:os#system_time/1">
+ <c>os:system_time/1</c></seealso></p></item>
</list>
- <p>The new API also consists of extensions of the following existing BIFs:</p>
+ <p>The new API also consists of extensions of the following existing BIFs:
+ </p>
<list>
- <item><p><seealso marker="erlang#monitor/2"><c>erlang:monitor(time_offset, clock_service)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_flag_time_offset"><c>erlang:system_flag(time_offset, finalize)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_os_system_time_source"><c>erlang:system_info(os_system_time_source)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_time_offset"><c>erlang:system_info(time_offset)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_time_warp_mode"><c>erlang:system_info(time_warp_mode)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_time_correction"><c>erlang:system_info(time_correction)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso></p></item>
- <item><p><seealso marker="erlang#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#monitor/2">
+ <c>erlang:monitor(time_offset, clock_service)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_flag_time_offset">
+ <c>erlang:system_flag(time_offset, finalize)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_os_monotonic_time_source">
+ <c>erlang:system_info(os_monotonic_time_source)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_os_system_time_source">
+ <c>erlang:system_info(os_system_time_source)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_time_offset">
+ <c>erlang:system_info(time_offset)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_time_warp_mode">
+ <c>erlang:system_info(time_warp_mode)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_time_correction">
+ <c>erlang:system_info(time_correction)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_start_time">
+ <c>erlang:system_info(start_time)</c></seealso></p></item>
+ <item><p><seealso marker="erlang#system_info_end_time">
+ <c>erlang:system_info(end_time)</c></seealso></p></item>
</list>
<marker id="The_New_Erlang_Monotonic_Time"/>
<section>
<title>New Erlang Monotonic Time</title>
- <p>Erlang monotonic time as such is new as of <c>ERTS</c> 7.0.
+ <p>Erlang monotonic time as such is new as from ERTS 7.0.
It is introduced to detach time measurements, such as elapsed
time from calendar time. In many use cases there is a need to
measure elapsed time or specify a time relative to another point
@@ -649,7 +684,7 @@
modes, and only fully separated in the
<seealso marker="#Multi_Time_Warp_Mode">multi-time
warp mode</seealso>. All other modes than the
- multi-time warp mode are for backwards
+ multi-time warp mode are for backward
compatibility reasons. When using these modes, the
accuracy of Erlang monotonic time suffer, as
the adjustments of Erlang monotonic time in these
@@ -672,15 +707,17 @@
<p>To be able to react to a change in Erlang
system time, you must be able to detect that it
- happened. The change in Erlang system time occurs when
+ happened. The change in Erlang system time occurs when the
current time offset is changed. We have therefore
introduced the possibility to monitor the time offset using
- <seealso marker="erlang#monitor/2"><c>erlang:monitor(time_offset, clock_service)</c></seealso>.
+ <seealso marker="erlang#monitor/2">
+ <c>erlang:monitor(time_offset, clock_service)</c></seealso>.
A process monitoring the time
offset is sent a message on the following format
when the time offset is changed:</p>
- <code type="none">{'CHANGE', MonitorReference, time_offset, clock_service, NewTimeOffset}</code>
+ <code type="none">
+{'CHANGE', MonitorReference, time_offset, clock_service, NewTimeOffset}</code>
</section>
<marker id="Unique_Values"/>
@@ -690,8 +727,8 @@
produces unique and strictly monotonically increasing
values. To detach this functionality from
time measurements, we have introduced
- <seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer()</c></seealso>.
- </p>
+ <seealso marker="erlang#unique_integer/1">
+ <c>erlang:unique_integer()</c></seealso>.</p>
</section>
<marker id="Dos_and_Donts"/>
@@ -699,27 +736,28 @@
<title>How to Work with the New API</title>
<p>Previously <c>erlang:now/0</c> was the only option for doing
many things. This section deals with some things that
- <c>erlang:now/0</c> can be used for, and how you are to
- these using the new API.</p>
+ <c>erlang:now/0</c> can be used for, and how you use the new API.</p>
<marker id="Dos_and_Donts_Retrieve_Erlang_System_Time"/>
<section>
<title>Retrieve Erlang System Time</title>
<dont>
<p>
- Use <c>erlang:now/0</c> to retrieve current Erlang system time.
+ Use <c>erlang:now/0</c> to retrieve the current Erlang system time.
</p>
</dont>
<do>
<p>
Use
- <seealso marker="erlang#system_time/1"><c>erlang:system_time/1</c></seealso>
- to retrieve current Erlang system time on the
+ <seealso marker="erlang#system_time/1">
+ <c>erlang:system_time/1</c></seealso>
+ to retrieve the current Erlang system time on the
<seealso marker="erlang#type_time_unit">time unit</seealso>
of your choice.</p>
- <p>If you want the same format as returned by <c>erlang:now/0</c>, use
- <seealso marker="erlang#timestamp/0"><c>erlang:timestamp/0</c></seealso>.
- </p>
+ <p>If you want the same format as returned by <c>erlang:now/0</c>,
+ use <seealso marker="erlang#timestamp/0">
+ <c>erlang:timestamp/0</c></seealso>.
+ </p>
</do>
</section>
@@ -728,28 +766,31 @@
<title>Measure Elapsed Time</title>
<dont>
<p>
- Take timestamps with <c>erlang:now/0</c> and calculate
+ Take time stamps with <c>erlang:now/0</c> and calculate
the difference in time with
- <seealso marker="stdlib:timer#now_diff/2"><c>timer:now_diff/2</c></seealso>.
+ <seealso marker="stdlib:timer#now_diff/2">
+ <c>timer:now_diff/2</c></seealso>.
</p>
</dont>
<do>
<p>
- Take timestamps with
- <seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time/0</c></seealso>
+ Take time stamps with
+ <seealso marker="erlang#monotonic_time/0">
+ <c>erlang:monotonic_time/0</c></seealso>
and calculate the time difference using ordinary subtraction.
- The result will be in <c>native</c>
+ The result is in <c>native</c>
<seealso marker="erlang#type_time_unit">time unit</seealso>.
If you want to convert the
result to another time unit, you can use
- <seealso marker="erlang#convert_time_unit/3"><c>erlang:convert_time_unit/3</c></seealso>.
- </p>
-
- <p>An easier way to do this is to use
- <seealso marker="erlang#monotonic_time/1"><c>erlang:monotonic_time/1</c></seealso>
+ <seealso marker="erlang#convert_time_unit/3">
+ <c>erlang:convert_time_unit/3</c></seealso>.
+ </p>
+ <p>An easier way to do this is to use
+ <seealso marker="erlang#monotonic_time/1">
+ <c>erlang:monotonic_time/1</c></seealso>
with the desired time unit. However, you can then lose accuracy
and precision.
- </p>
+ </p>
</do>
</section>
@@ -758,7 +799,7 @@
<title>Determine Order of Events</title>
<dont>
<p>
- Determine the order of events by saving a timestamp
+ Determine the order of events by saving a time stamp
with <c>erlang:now/0</c> when the event occurs.
</p>
</dont>
@@ -766,8 +807,9 @@
<p>
Determine the order of events by saving the integer
returned by
- <seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer([monotonic])</c></seealso>
- when the event occurs. These integers will be strictly
+ <seealso marker="erlang#unique_integer/1">
+ <c>erlang:unique_integer([monotonic])</c></seealso>
+ when the event occurs. These integers are strictly
monotonically ordered on current runtime system instance
corresponding to creation time.
</p>
@@ -779,7 +821,7 @@
<title>Determine Order of Events with Time of the Event</title>
<dont>
<p>
- Determine the order of events by saving a timestamp
+ Determine the order of events by saving a time stamp
with <c>erlang:now/0</c> when the event occurs.
</p>
</dont>
@@ -795,18 +837,19 @@ Time = erlang:monotonic_time(),
UMI = erlang:unique_integer([monotonic]),
EventTag = {Time, UMI}</code>
- <p>These tuples will be strictly monotonically ordered
- on current runtime system instance according to
+ <p>These tuples are strictly monotonically ordered
+ on the current runtime system instance according to
creation time. It is important that the
monotonic time is in the first element (the most
- significant element when comparing 2-tuples). Using
+ significant element when comparing two-tuples). Using
the monotonic time in the tuples, you can calculate time
between events.</p>
<p>If you are interested in Erlang system time at the
time when the event occurred, you can also save the time
offset before or after saving the events using
- <seealso marker="erlang#time_offset/0"><c>erlang:time_offset/0</c></seealso>.
+ <seealso marker="erlang#time_offset/0">
+ <c>erlang:time_offset/0</c></seealso>.
Erlang monotonic time added with the time
offset corresponds to Erlang system time.</p>
@@ -814,7 +857,7 @@ EventTag = {Time, UMI}</code>
can change, and you want to get the actual
Erlang system time when the event occurred, you can
save the time offset as a third element in the tuple
- (the least significant element when comparing 3-tuples).</p>
+ (the least significant element when comparing three-tuples).</p>
</do>
</section>
@@ -830,10 +873,12 @@ EventTag = {Time, UMI}</code>
<do>
<p>
Use the value returned from
- <seealso marker="erlang#unique_integer/0"><c>erlang:unique_integer/0</c></seealso>
+ <seealso marker="erlang#unique_integer/0">
+ <c>erlang:unique_integer/0</c></seealso>
to create a name unique on the current runtime system
instance. If you only want positive integers, you can use
- <seealso marker="erlang#unique_integer/1"><c>erlang:unique_integer([positive])</c></seealso>.
+ <seealso marker="erlang#unique_integer/1">
+ <c>erlang:unique_integer([positive])</c></seealso>.
</p>
</do>
</section>
@@ -849,12 +894,15 @@ EventTag = {Time, UMI}</code>
<do>
<p>
Seed random number generation using a combination of
- <seealso marker="erlang#monotonic_time/0"><c>erlang:monotonic_time()</c></seealso>,
- <seealso marker="erlang#time_offset/0"><c>erlang:time_offset()</c></seealso>,
- <seealso marker="erlang#unique_integer/0"><c>erlang:unique_integer()</c></seealso>,
+ <seealso marker="erlang#monotonic_time/0">
+ <c>erlang:monotonic_time()</c></seealso>,
+ <seealso marker="erlang#time_offset/0">
+ <c>erlang:time_offset()</c></seealso>,
+ <seealso marker="erlang#unique_integer/0">
+ <c>erlang:unique_integer()</c></seealso>,
and other functionality.
</p>
- </do>
+ </do>
</section>
<p>To sum up this section: <em>Do not use <c>erlang:now/0</c>.</em></p>
@@ -867,9 +915,9 @@ EventTag = {Time, UMI}</code>
<p>It can be required that your code must run on a variety
of OTP installations of different OTP releases. If so, you
cannot use the new API out of the box, as it will
- not be available on old pre OTP 18 releases. The solution
+ not be available on releases before OTP 18. The solution
is <em>not</em> to avoid using the new API, as your
- code then would not benefit from the scalability
+ code would then not benefit from the scalability
and accuracy improvements made. Instead, use the
new API when available, and fall back on <c>erlang:now/0</c>
when the new API is unavailable.</p>
@@ -879,16 +927,20 @@ EventTag = {Time, UMI}</code>
<list type="bulleted">
<item>
- <seealso marker="erlang#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso>
+ <seealso marker="erlang#system_info_start_time">
+ <c>erlang:system_info(start_time)</c></seealso>
</item>
<item>
- <seealso marker="erlang#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso>
+ <seealso marker="erlang#system_info_end_time">
+ <c>erlang:system_info(end_time)</c></seealso>
</item>
<item>
- <seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso>
+ <seealso marker="erlang#system_info_os_monotonic_time_source">
+ <c>erlang:system_info(os_monotonic_time_source)</c></seealso>
</item>
<item>
- <seealso marker="erlang#system_info_os_system_time_source"><c>erlang:system_info(os_system_time_source)</c></seealso>)
+ <seealso marker="erlang#system_info_os_system_time_source">
+ <c>erlang:system_info(os_system_time_source)</c></seealso>)
</item>
</list>
diff --git a/erts/doc/src/tty.xml b/erts/doc/src/tty.xml
index b2866c82cf..51db1ba8e2 100644
--- a/erts/doc/src/tty.xml
+++ b/erts/doc/src/tty.xml
@@ -22,7 +22,7 @@
</legalnotice>
- <title>tty - A command line interface</title>
+ <title>tty - A Command-Line Interface</title>
<prepared>ETX/B/SFP C. Granbom</prepared>
<responsible></responsible>
<docno></docno>
@@ -32,24 +32,48 @@
<rev>A</rev>
<file>tty.xml</file>
</header>
- <p><c><![CDATA[tty]]></c> is a simple command line interface program where keystrokes are collected and interpreted. Completed lines are sent to the shell for interpretation. There is a simple history mechanism, which saves previous lines. These can be edited before sending them to the shell.
- <c><![CDATA[tty]]></c> is started when Erlang is started with the command:<br></br></p>
- <p><em>erl</em></p>
- <p><c><![CDATA[tty]]></c> operates in one of two modes:<br></br></p>
+ <p><c><![CDATA[tty]]></c> is a simple command-line interface program where
+ keystrokes are collected and interpreted. Completed lines are sent to the
+ shell for interpretation. A simple history mechanism saves previous lines,
+ which can be edited before sending them to the shell. <c><![CDATA[tty]]></c>
+ is started when Erlang is started with the following command:</p>
+
+ <pre>
+erl</pre>
+
+ <p><c><![CDATA[tty]]></c> operates in one of two modes:</p>
+
<list type="bulleted">
<item>
- <p><em>normal mode</em>, in which lines of text can be edited and sent to the shell.</p>
+ <p>Normal mode, in which text lines can be edited and sent to the
+ shell.</p>
</item>
<item>
- <p><em>shell break</em> mode, which allows the user to kill the current shell, start multiple shells etc. Shell break mode is started by typing <em>Control G</em>.</p>
+ <p>Shell break mode, which allows the user to kill the current shell,
+ start multiple shells, and so on.</p>
</item>
</list>
<section>
<title>Normal Mode</title>
- <p>In normal mode keystrokes from the user are collected and interpreted by <c><![CDATA[tty]]></c>. Most of the <em>emacs</em> line editing commands are supported. The following is a complete list of the supported line editing commands.<br></br></p>
- <p><em>Note:</em> The notation <c><![CDATA[C-a]]></c> means pressing the control key and the letter <c><![CDATA[a]]></c> simultaneously. <c><![CDATA[M-f]]></c> means pressing the <c><![CDATA[ESC]]></c> key followed by the letter <c><![CDATA[f]]></c>. <c><![CDATA[Home]]></c> and <c><![CDATA[End]]></c> represent the keys with the same name on the keyboard, whereas <c><![CDATA[Left]]></c> and <c><![CDATA[Right]]></c> represent the corresponding arrow keys.
- </p>
+ <p>In normal mode keystrokes from the user are collected and interpreted by
+ <c><![CDATA[tty]]></c>. Most of the <em>Emacs</em> line-editing commands
+ are supported. The following is a complete list of the supported
+ line-editing commands.</p>
+
+ <p>Typographic conventions:</p>
+
+ <list type="bulleted">
+ <item><c>C-a</c> means pressing the <em>Ctrl</em> key and the letter
+ <c>a</c> simultaneously.</item>
+ <item><c>M-f</c> means pressing the <em>Esc</em> key and the letter
+ <c>f</c> in sequence.</item>
+ <item><c>Home</c> and <c>End</c> represent the keys with the same
+ name on the keyboard.</item>
+ <item><c>Left</c> and <c>Right</c> represent the corresponding arrow
+ keys.</item>
+ </list>
+
<table>
<row>
<cell align="left" valign="middle"><em>Key Sequence</em></cell>
@@ -121,11 +145,13 @@
</row>
<row>
<cell align="left" valign="middle">C-n</cell>
- <cell align="left" valign="middle">Fetch next line from the history buffer</cell>
+ <cell align="left" valign="middle">Fetch next line from the history
+ buffer</cell>
</row>
<row>
<cell align="left" valign="middle">C-p</cell>
- <cell align="left" valign="middle">Fetch previous line from the history buffer</cell>
+ <cell align="left" valign="middle">Fetch previous line from the history
+ buffer</cell>
</row>
<row>
<cell align="left" valign="middle">C-t</cell>
@@ -139,23 +165,18 @@
<cell align="left" valign="middle">C-y</cell>
<cell align="left" valign="middle">Insert previously killed text</cell>
</row>
- <tcaption>tty text editing</tcaption>
+ <tcaption>tty Text Editing</tcaption>
</table>
</section>
<section>
<title>Shell Break Mode</title>
- <p><em>tty</em> enters <em>shell</em> break mode when you type <em>Control G</em>. In this mode you can:<br></br></p>
+ <p>In this mode the following can be done:</p>
+
<list type="bulleted">
- <item>
- <p>Kill or suspend the current shell</p>
- </item>
- <item>
- <p>Connect to a suspended shell</p>
- </item>
- <item>
- <p>Start a new shell</p>
- </item>
+ <item>Kill or suspend the current shell</item>
+ <item>Connect to a suspended shell</item>
+ <item>Start a new shell</item>
</list>
</section>
</chapter>
diff --git a/erts/doc/src/werl.xml b/erts/doc/src/werl.xml
index 1a3cb6f502..792fe204e8 100644
--- a/erts/doc/src/werl.xml
+++ b/erts/doc/src/werl.xml
@@ -28,62 +28,91 @@
<docno>1</docno>
<approved>Bjarne D&auml;cker</approved>
<checked></checked>
- <date>98-01-26</date>
+ <date>1998-01-26</date>
<rev>A</rev>
<file>werl.xml</file>
</header>
<com>werl</com>
<comsummary>The Erlang Emulator</comsummary>
<description>
- <p>On Windows, the preferred way to start the Erlang system for interactive use is:</p>
+ <p>On Windows, the preferred way to start the Erlang system for interactive
+ use is as follows:</p>
+
<p><c><![CDATA[werl <arguments>]]></c></p>
- <p>This will start Erlang in its own window, with fully
- functioning command-line editing and scrollbars. All flags
+ <p>This starts Erlang in its own window, with fully
+ functioning command-line editing and scrollbars. All flags
except <c><![CDATA[-oldshell]]></c> work as they do for
- the <seealso marker="erl">erl</seealso> command.</p>
+ <seealso marker="erl"><c>erl(1)</c></seealso>.</p>
+
+ <list type="bulleted">
+ <item>
+ <p>To copy text to the clipboard, use <c>Ctrl-C</c>.</p>
+ </item>
+ <item>
+ <p>To paste text, use <c>Ctrl-V</c>.</p>
+ </item>
+ <item>
+ <p>To interrupt the runtime system or the shell process (depending
+ on what has been specified with system flag <c>+B</c>), use
+ <c>Ctrl-Break</c>.</p>
+ </item>
+ </list>
- <p>Ctrl-C is reserved for copying text to the clipboard (Ctrl-V to paste).
- To interrupt the runtime system or the shell process (depending on what
- has been specified with the +B system flag), you should use Ctrl-Break.</p>
<p>In cases where you want to redirect standard input and/or
- standard output or use Erlang in a pipeline, the <c>werl</c> is
- not suitable, and the <c>erl</c> program should be used instead.</p>
+ standard output or use Erlang in a pipeline, <c>werl</c> is
+ not suitable, and the <c>erl</c> program is to be used instead.</p>
- <p>The <c>werl</c> window is in many ways modelled after the <c>xterm</c>
+ <p>The <c>werl</c> window is in many ways modeled after the <c>xterm</c>
window present on other platforms, as the <c>xterm</c> model
- fits well with line oriented command based interaction. This
- means that selecting text is line oriented rather than rectangle
- oriented.</p>
-
- <p>To select text in the <c>werl</c> window , simply press and hold
- the left mouse button and drag the mouse over the text you want
- to select. If the selection crosses line boundaries, the
- selected text will consist of complete lines where applicable
- (just like in a word processor). To select more text than fits
- in the window, start by selecting a small portion in the
- beginning of the text you want, then use the scrollbar
- to view the end of the desired selection, point to it and press
- the <em>right</em> mouse-button. The whole area between your
- first selection and the point where you right-clicked will be
- included in the selection.</p>
+ fits well with line-oriented command-based interaction. This
+ means that selecting text is line-oriented rather than
+ rectangle-oriented.</p>
- <p>The selected text is copied to the clipboard by either
- pressing <c>Ctrl-C</c>, using the menu or pressing the copy
- button in the toolbar.</p>
+ <list type="bulleted">
+ <item>
+ <p>To select text in the <c>werl</c> window, press and hold
+ the left mouse button and drag the mouse over the text you want
+ to select. If the selection crosses line boundaries, the
+ selected text consists of complete lines where applicable
+ (just like in a word processor).</p>
+ </item>
+ <item>
+ <p>To select more text than fits
+ in the window, start by selecting a small part in the
+ beginning of the text you want, then use the scrollbar
+ to view the end of the desired selection, point to it, and press
+ the <em>right</em> mouse button. The whole area between your
+ first selection and the point where you right-clicked is
+ included in the selection.</p>
+ </item>
+ <item>
+ <p>To copy the selected text to the clipboard, either
+ use <c>Ctrl-C</c>, use the menu, or press the copy
+ button in the toolbar.</p>
+ </item>
+ </list>
- <p>Pasted text is always inserted at the current prompt position
- and will be interpreted by Erlang as usual keyboard input.</p>
+ <p>Pasted text is inserted at the current prompt position
+ and is interpreted by Erlang as usual keyboard input.</p>
- <p>Previous command lines can be retrieved by pressing the <c>Up
- arrow</c> or by pressing <c>Ctrl-P</c>. There is also a drop
- down box in the toolbar containing the command
- history. Selecting a command in the drop down box will insert it
- at the prompt, just as if you used the keyboard to retrieve the
+ <list type="bulleted">
+ <item>
+ <p>To retrieve previous command lines, press the <c>Up arrow</c> or
+ use <c>Ctrl-P</c>.</p>
+ </item>
+ </list>
+
+ <p>A drop-down box in the toolbar contains the command
+ history. Selecting a command in the drop-down box inserts the command
+ at the prompt, as if you used the keyboard to retrieve the
command.</p>
-
- <p>Closing the <c>werl</c> window will stop the Erlang emulator.</p>
+ <list type="bulleted">
+ <item>
+ <p>To stop the Erlang emulator, close the <c>werl</c> window.</p>
+ </item>
+ </list>
</description>
</comref>
diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml
index 861661043f..f8140544b7 100644
--- a/erts/doc/src/zlib.xml
+++ b/erts/doc/src/zlib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2005</year><year>2016</year>
+ <year>2005</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,13 +30,18 @@
<file>zlib.xml</file>
</header>
<module>zlib</module>
- <modulesummary>Zlib Compression interface.</modulesummary>
+ <modulesummary>zlib compression interface.</modulesummary>
<description>
- <p>The zlib module provides an API for the zlib library
- (http://www.zlib.org).
- It is used to compress and decompress data. The
- data format is described by RFCs 1950 to 1952.</p>
- <p>A typical (compress) usage looks like:</p>
+ <p>This module provides an API for the zlib library
+ (<url href="http://www.zlib.net">www.zlib.net</url>).
+ It is used to compress and decompress data.
+ The data format is described by
+ <url href="https://www.ietf.org/rfc/rfc1950.txt">RFC 1950</url>,
+ <url href="https://www.ietf.org/rfc/rfc1951.txt">RFC 1951</url>, and
+ <url href="https://www.ietf.org/rfc/rfc1952.txt">RFC 1952</url>.</p>
+
+ <p>A typical (compress) usage is as follows:</p>
+
<pre>
Z = zlib:open(),
ok = zlib:deflateInit(Z,default),
@@ -50,29 +55,25 @@ Last = zlib:deflate(Z, [], finish),
ok = zlib:deflateEnd(Z),
zlib:close(Z),
list_to_binary([Compressed|Last])</pre>
+
<p>In all functions errors, <c>{'EXIT',{Reason,Backtrace}}</c>,
- might be thrown, where <c>Reason</c> describes the
- error. Typical reasons are:</p>
+ can be thrown, where <c>Reason</c> describes the error.</p>
+
+ <p>Typical <c>Reasons</c>s:</p>
+
<taglist>
<tag><c>badarg</c></tag>
- <item>
- <p>Bad argument</p>
+ <item>Bad argument.
</item>
<tag><c>data_error</c></tag>
- <item>
- <p>The data contains errors</p>
+ <item>The data contains errors.
</item>
<tag><c>stream_error</c></tag>
- <item>
- <p>Inconsistent stream state</p>
- </item>
+ <item>Inconsistent stream state.</item>
<tag><c>einval</c></tag>
- <item>
- <p>Bad value or wrong function called</p>
- </item>
+ <item>Bad value or wrong function called.</item>
<tag><c>{need_dictionary,Adler32}</c></tag>
- <item>
- <p>See <c>inflate/2</c></p>
+ <item>See <seealso marker="#inflate/2"><c>inflate/2</c></seealso>.
</item>
</taglist>
</description>
@@ -81,7 +82,7 @@ list_to_binary([Compressed|Last])</pre>
<datatype>
<name name="zstream"/>
<desc>
- <p>A zlib stream, see <seealso marker="#open/0">open/0</seealso>.
+ <p>A zlib stream, see <seealso marker="#open/0"><c>open/0</c></seealso>.
</p>
</desc>
</datatype>
@@ -104,116 +105,144 @@ list_to_binary([Compressed|Last])</pre>
</desc>
</datatype>
</datatypes>
+
<funcs>
<func>
- <name name="open" arity="0"/>
- <fsummary>Open a stream and return a stream reference</fsummary>
+ <name name="adler32" arity="2"/>
+ <fsummary>Calculate the Adler checksum.</fsummary>
+ <desc>
+ <p>Calculates the Adler-32 checksum for <c><anno>Data</anno></c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="adler32" arity="3"/>
+ <fsummary>Calculate the Adler checksum.</fsummary>
+ <desc>
+ <p>Updates a running Adler-32 checksum for <c><anno>Data</anno></c>.
+ If <c><anno>Data</anno></c> is the empty binary or the empty iolist,
+ this function returns the required initial value for the checksum.</p>
+ <p>Example:</p>
+ <pre>
+Crc = lists:foldl(fun(Data,Crc0) ->
+ zlib:adler32(Z, Crc0, Data),
+ end, zlib:adler32(Z,&lt;&lt; &gt;&gt;), Datas)</pre>
+ </desc>
+ </func>
+
+ <func>
+ <name name="adler32_combine" arity="4"/>
+ <fsummary>Combine two Adler-32 checksums.</fsummary>
<desc>
- <p>Open a zlib stream.</p>
+ <p>Combines two Adler-32 checksums into one. For two binaries or
+ iolists, <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c>
+ and <c><anno>Size2</anno></c>, with Adler-32 checksums
+ <c><anno>Adler1</anno></c> and <c><anno>Adler2</anno></c>.</p>
+ <p>This function returns the <c><anno>Adler</anno></c> checksum of
+ <c>[Data1,Data2]</c>, requiring only <c><anno>Adler1</anno></c>,
+ <c><anno>Adler2</anno></c>, and <c><anno>Size2</anno></c>.</p>
</desc>
</func>
+
<func>
<name name="close" arity="1"/>
- <fsummary>Close a stream</fsummary>
+ <fsummary>Close a stream.</fsummary>
<desc>
<p>Closes the stream referenced by <c><anno>Z</anno></c>.</p>
</desc>
</func>
+
<func>
- <name name="deflateInit" arity="1"/>
- <fsummary>Initialize a session for compression</fsummary>
+ <name name="compress" arity="1"/>
+ <fsummary>Compress data with standard zlib functionality.</fsummary>
<desc>
- <p>Same as <c>zlib:deflateInit(<anno>Z</anno>, default)</c>.</p>
+ <p>Compresses data with zlib headers and checksum.</p>
</desc>
</func>
+
<func>
- <name name="deflateInit" arity="2"/>
- <fsummary>Initialize a session for compression</fsummary>
+ <name name="crc32" arity="1"/>
+ <fsummary>Get current CRC.</fsummary>
<desc>
- <p>Initialize a zlib stream for compression.</p>
- <p><c><anno>Level</anno></c> decides the compression level to be used, 0
- (<c>none</c>), gives no compression at all, 1
- (<c>best_speed</c>) gives best speed and 9
- (<c>best_compression</c>) gives best compression.</p>
+ <p>Gets the current calculated CRC checksum.</p>
</desc>
</func>
+
<func>
- <name name="deflateInit" arity="6"/>
- <fsummary>Initialize a session for compression</fsummary>
+ <name name="crc32" arity="2"/>
+ <fsummary>Calculate CRC.</fsummary>
<desc>
- <p>Initiates a zlib stream for compression.</p>
- <p>The <c><anno>Level</anno></c> parameter decides the compression level to be
- used, 0 (<c>none</c>), gives no compression at all, 1
- (<c>best_speed</c>) gives best speed and 9
- (<c>best_compression</c>) gives best compression.</p>
- <p>The <c><anno>Method</anno></c> parameter decides which compression method to use,
- currently the only supported method is <c>deflated</c>.</p>
- <p>The <c><anno>WindowBits</anno></c> parameter is the base two logarithm
- of the window size (the size of the history buffer). It
- should be in the range 8 through 15. Larger values
- of this parameter result in better compression at the
- expense of memory usage. The default value is 15 if
- <c>deflateInit/2</c>. A negative <c><anno>WindowBits</anno></c>
- value suppresses the zlib header (and checksum) from the
- stream. Note that the zlib source mentions this only as a
- undocumented feature.</p>
- <p>The <c><anno>MemLevel</anno></c> parameter specifies how much memory
- should be allocated for the internal compression
- state. <c><anno>MemLevel</anno></c>=1 uses minimum memory but is slow and
- reduces compression ratio; <c><anno>MemLevel</anno></c>=9 uses maximum
- memory for optimal speed. The default value is 8.</p>
- <p>The <c><anno>Strategy</anno></c> parameter is used to tune
- the compression algorithm. Use the value <c>default</c> for
- normal data, <c>filtered</c> for data produced by a filter (or
- predictor), <c>huffman_only</c> to force Huffman encoding
- only (no string match), or <c>rle</c> to limit match
- distances to one (run-length encoding). Filtered data
- consists mostly of small values with a somewhat random
- distribution. In this case, the compression algorithm is tuned
- to compress them better. The effect of <c>filtered</c>is to
- force more Huffman coding and less string matching; it is
- somewhat intermediate between <c>default</c> and
- <c>huffman_only</c>. <c>rle</c> is designed to be almost as
- fast as <c>huffman_only</c>, but give better compression for PNG
- image data. The <c><anno>Strategy</anno></c> parameter only
- affects the compression ratio but not the correctness of the
- compressed output even if it is not set appropriately.</p>
+ <p>Calculates the CRC checksum for <c><anno>Data</anno></c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="crc32" arity="3"/>
+ <fsummary>Calculate CRC.</fsummary>
+ <desc>
+ <p>Updates a running CRC checksum for <c><anno>Data</anno></c>.
+ If <c><anno>Data</anno></c> is the empty binary or the empty iolist,
+ this function returns the required initial value for the CRC.</p>
+ <p>Example:</p>
+ <pre>
+Crc = lists:foldl(fun(Data,Crc0) ->
+ zlib:crc32(Z, Crc0, Data),
+ end, zlib:crc32(Z,&lt;&lt; &gt;&gt;), Datas)</pre>
+ </desc>
+ </func>
+
+ <func>
+ <name name="crc32_combine" arity="4"/>
+ <fsummary>Combine two CRCs.</fsummary>
+ <desc>
+ <p>Combines two CRC checksums into one. For two binaries or iolists,
+ <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c> and
+ <c><anno>Size2</anno></c>, with CRC checksums <c><anno>CRC1</anno></c>
+ and <c><anno>CRC2</anno></c>.</p>
+ <p>This function returns the <c><anno>CRC</anno></c> checksum of
+ <c>[Data1,Data2]</c>, requiring only <c><anno>CRC1</anno></c>,
+ <c><anno>CRC2</anno></c>, and <c><anno>Size2</anno></c>.</p>
</desc>
</func>
+
<func>
<name name="deflate" arity="2"/>
- <fsummary>Compress data</fsummary>
+ <fsummary>Compress data.</fsummary>
<desc>
<p>Same as <c>deflate(<anno>Z</anno>, <anno>Data</anno>, none)</c>.</p>
</desc>
</func>
+
<func>
<name name="deflate" arity="3"/>
- <fsummary>Compress data</fsummary>
+ <fsummary>Compress data.</fsummary>
<desc>
- <p><c>deflate/3</c> compresses as much data as possible, and
- stops when the input buffer becomes empty. It may introduce
+ <p>Compresses as much data as possible, and
+ stops when the input buffer becomes empty. It can introduce
some output latency (reading input without producing any
output) except when forced to flush.</p>
- <p>If the parameter <c><anno>Flush</anno></c> is set to <c>sync</c>, all
+ <p>If <c><anno>Flush</anno></c> is set to <c>sync</c>, all
pending output is flushed to the output buffer and the
output is aligned on a byte boundary, so that the
decompressor can get all input data available so far.
- Flushing may degrade compression for some compression algorithms and so
- it should be used only when necessary.</p>
- <p>If <c><anno>Flush</anno></c> is set to <c>full</c>, all output is flushed as with
- <c>sync</c>, and the compression state is reset so that decompression can
- restart from this point if previous compressed data has been damaged or if
- random access is desired. Using <c>full</c> too often can seriously degrade
- the compression.</p>
- <p>If the parameter <c><anno>Flush</anno></c> is set to <c>finish</c>,
- pending input is processed, pending output is flushed and
- <c>deflate/3</c> returns. Afterwards the only possible
- operations on the stream are <c>deflateReset/1</c> or <c>deflateEnd/1</c>.</p>
- <p><c><anno>Flush</anno></c> can be set to <c>finish</c> immediately after
- <c>deflateInit</c> if all compression is to be done in one step.</p>
+ Flushing can degrade compression for some compression algorithms;
+ thus, use it only when necessary.</p>
+ <p>If <c><anno>Flush</anno></c> is set to <c>full</c>, all output is
+ flushed as with <c>sync</c>, and the compression state is reset so
+ that decompression can restart from this point if previous compressed
+ data has been damaged or if random access is desired. Using
+ <c>full</c> too often can seriously degrade the compression.</p>
+ <p>If <c><anno>Flush</anno></c> is set to <c>finish</c>,
+ pending input is processed, pending output is flushed, and
+ <c>deflate/3</c> returns. Afterwards the only possible operations
+ on the stream are
+ <seealso marker="#deflateReset/1"><c>deflateReset/1</c></seealso> or
+ <seealso marker="#deflateEnd/1"><c>deflateEnd/1</c></seealso>.</p>
+ <p><c><anno>Flush</anno></c> can be set to <c>finish</c> immediately
+ after <seealso marker="#deflateInit/1"><c>deflateInit</c></seealso>
+ if all compression is to be done in one step.</p>
+ <p>Example:</p>
<pre>
-
zlib:deflateInit(Z),
B1 = zlib:deflate(Z,Data),
B2 = zlib:deflate(Z,&lt;&lt; &gt;&gt;,finish),
@@ -221,116 +250,243 @@ zlib:deflateEnd(Z),
list_to_binary([B1,B2])</pre>
</desc>
</func>
+
<func>
- <name name="deflateSetDictionary" arity="2"/>
- <fsummary>Initialize the compression dictionary</fsummary>
+ <name name="deflateEnd" arity="1"/>
+ <fsummary>End deflate session.</fsummary>
<desc>
- <p>Initializes the compression dictionary from the given byte
- sequence without producing any compressed output. This
- function must be called immediately after
- <c>deflateInit/[1|2|6]</c> or <c>deflateReset/1</c>, before
- any call of <c>deflate/3</c>. The compressor and
- decompressor must use exactly the same dictionary (see
- <c>inflateSetDictionary/2</c>). The adler checksum of the
- dictionary is returned.</p>
+ <p>Ends the deflate session and cleans all data used. Notice that this
+ function throws a <c>data_error</c> exception if the last call to
+ <seealso marker="#deflate/3"><c>deflate/3</c></seealso>
+ was not called with <c>Flush</c> set to <c>finish</c>.</p>
</desc>
</func>
+
<func>
- <name name="deflateReset" arity="1"/>
- <fsummary>Reset the deflate session</fsummary>
+ <name name="deflateInit" arity="1"/>
+ <fsummary>Initialize a session for compression.</fsummary>
<desc>
- <p>This function is equivalent to <c>deflateEnd/1</c>
- followed by <c>deflateInit/[1|2|6]</c>, but does not free
- and reallocate all the internal compression state. The
- stream will keep the same compression level and any other
- attributes.</p>
+ <p>Same as <c>zlib:deflateInit(<anno>Z</anno>, default)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="deflateInit" arity="2"/>
+ <fsummary>Initialize a session for compression.</fsummary>
+ <desc>
+ <p>Initializes a zlib stream for compression.</p>
+ <p><c><anno>Level</anno></c> decides the compression level to be
+ used:</p>
+ <list type="bulleted">
+ <item>0 (<c>none</c>), gives no compression</item>
+ <item>1 (<c>best_speed</c>) gives best speed</item>
+ <item>9 (<c>best_compression</c>) gives best compression</item>
+ </list>
+ </desc>
+ </func>
+
+ <func>
+ <name name="deflateInit" arity="6"/>
+ <fsummary>Initialize a session for compression.</fsummary>
+ <desc>
+ <p>Initiates a zlib stream for compression.</p>
+ <taglist>
+ <tag><c><anno>Level</anno></c></tag>
+ <item>
+ <p>Compression level to use:</p>
+ <list type="bulleted">
+ <item>0 (<c>none</c>), gives no compression</item>
+ <item>1 (<c>best_speed</c>) gives best speed</item>
+ <item>9 (<c>best_compression</c>) gives best compression</item>
+ </list>
+ </item>
+ <tag><c><anno>Method</anno></c></tag>
+ <item>
+ <p>Compression method to use, currently the only supported method
+ is <c>deflated</c>.</p>
+ </item>
+ <tag><c><anno>WindowBits</anno></c></tag>
+ <item>
+ <p>The base two logarithm of the window size (the size of the
+ history buffer). It is to be in the range 8 through 15. Larger
+ values result in better compression at the expense of memory
+ usage. Defaults to 15 if <seealso marker="#deflateInit/2">
+ <c>deflateInit/2</c></seealso> is used. A negative
+ <c><anno>WindowBits</anno></c> value suppresses the zlib header
+ (and checksum) from the stream. Notice that the zlib source
+ mentions this only as a undocumented feature.</p>
+ <warning>
+ <p>Due to a known bug in the underlying zlib library, <c>WindowBits</c> values 8 and -8
+ do not work as expected. In zlib versions before 1.2.9 values
+ 8 and -8 are automatically changed to 9 and -9. <em>From zlib version 1.2.9
+ value -8 is rejected</em> causing <c>zlib:deflateInit/6</c> to fail
+ (8 is still changed to 9). It also seem possible that future versions
+ of zlib may fix this bug and start accepting 8 and -8 as is.</p>
+ <p>Conclusion: Avoid values 8 and -8 unless you know your zlib version supports them.</p>
+ </warning>
+ </item>
+ <tag><c><anno>MemLevel</anno></c></tag>
+ <item>
+ <p>Specifies how much memory is to be allocated for the internal
+ compression state: <c><anno>MemLevel</anno></c>=1 uses minimum
+ memory but is slow and reduces compression ratio;
+ <c><anno>MemLevel</anno></c>=9 uses maximum memory for optimal
+ speed. Defaults to 8.</p>
+ </item>
+ <tag><c><anno>Strategy</anno></c></tag>
+ <item>
+ <p>Tunes the compression algorithm. Use the following values:</p>
+ <list type="bulleted">
+ <item><c>default</c> for normal data</item>
+ <item><c>filtered</c> for data produced by a filter (or
+ predictor)</item>
+ <item><c>huffman_only</c> to force Huffman encoding only
+ (no string match)</item>
+ <item><c>rle</c> to limit match distances to one (run-length
+ encoding)</item>
+ </list>
+ <p>Filtered data consists mostly of small values with a somewhat
+ random distribution. In this case, the compression algorithm is
+ tuned to compress them better. The effect of <c>filtered</c> is to
+ force more Huffman coding and less string matching; it is somewhat
+ intermediate between <c>default</c> and <c>huffman_only</c>.
+ <c>rle</c> is designed to be almost as fast as
+ <c>huffman_only</c>, but gives better compression for PNG image
+ data.</p>
+ <p><c><anno>Strategy</anno></c> affects only the compression ratio,
+ but not the correctness of the compressed output even if it is not
+ set appropriately.</p>
+ </item>
+ </taglist>
</desc>
</func>
+
<func>
<name name="deflateParams" arity="3"/>
- <fsummary>Dynamicly update deflate parameters</fsummary>
+ <fsummary>Dynamicly update deflate parameters.</fsummary>
<desc>
- <p>Dynamically update the compression level and compression
- strategy. The interpretation of <c><anno>Level</anno></c> and
- <c><anno>Strategy</anno></c> is as in <c>deflateInit/6</c>. This can be
+ <p>Dynamically updates the compression level and compression
+ strategy. The interpretation of <c><anno>Level</anno></c> and
+ <c><anno>Strategy</anno></c> is as in
+ <seealso marker="#deflateInit/6"><c>deflateInit/6</c></seealso>.
+ This can be
used to switch between compression and straight copy of the
input data, or to switch to a different kind of input data
requiring a different strategy. If the compression level is
changed, the input available so far is compressed with the
- old level (and may be flushed); the new level will take
- effect only at the next call of <c>deflate/3</c>.</p>
- <p>Before the call of <c>deflateParams</c>, the stream state must be set as for
- a call of <c>deflate/3</c>, since the currently available input may have to
- be compressed and flushed.</p>
+ old level (and can be flushed); the new level takes
+ effect only at the next call of
+ <seealso marker="#deflate/3"><c>deflate/3</c></seealso>.</p>
+ <p>Before the call of <c>deflateParams</c>, the stream state must be
+ set as for a call of <c>deflate/3</c>, as the currently available
+ input may have to be compressed and flushed.</p>
</desc>
</func>
+
<func>
- <name name="deflateEnd" arity="1"/>
- <fsummary>End deflate session</fsummary>
+ <name name="deflateReset" arity="1"/>
+ <fsummary>Reset the deflate session.</fsummary>
+ <desc>
+ <p>Equivalent to
+ <seealso marker="#deflateEnd/1"><c>deflateEnd/1</c></seealso>
+ followed by
+ <seealso marker="#deflateInit/1"><c>deflateInit/1,2,6</c></seealso>,
+ but does not free and reallocate all the internal compression state.
+ The stream keeps the same compression level and any other
+ attributes.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="deflateSetDictionary" arity="2"/>
+ <fsummary>Initialize the compression dictionary.</fsummary>
<desc>
- <p>End the deflate session and cleans all data used.
- Note that this function will throw an <c>data_error</c>
- exception if the last call to
- <c>deflate/3</c> was not called with <c>Flush</c> set to
- <c>finish</c>.</p>
+ <p>Initializes the compression dictionary from the specified byte
+ sequence without producing any compressed output.</p>
+ <p>This function must be called immediately after
+ <seealso marker="#deflateInit/1"><c>deflateInit/1,2,6</c></seealso> or
+ <seealso marker="#deflateReset/1"><c>deflateReset/1</c></seealso>,
+ before any call of
+ <seealso marker="#deflate/3"><c>deflate/3</c></seealso>.
+ The compressor and decompressor must use the same dictionary (see
+ <seealso marker="#inflateSetDictionary/2">
+ <c>inflateSetDictionary/2</c></seealso>).</p>
+ <p>The Adler checksum of the dictionary is returned.</p>
</desc>
</func>
+
<func>
- <name name="inflateInit" arity="1"/>
- <fsummary>Initialize a session for decompression</fsummary>
+ <name name="getBufSize" arity="1"/>
+ <fsummary>Get buffer size.</fsummary>
<desc>
- <p>Initialize a zlib stream for decompression.</p>
+ <p>Gets the size of the intermediate buffer.</p>
</desc>
</func>
+
<func>
- <name name="inflateInit" arity="2"/>
- <fsummary>Initialize a session for decompression</fsummary>
+ <name name="gunzip" arity="1"/>
+ <fsummary>Uncompress data with gz header.</fsummary>
<desc>
- <p>Initialize decompression session on zlib stream.</p>
- <p>The <c><anno>WindowBits</anno></c> parameter is the base two logarithm
- of the maximum window size (the size of the history buffer).
- It should be in the range 8 through 15.
- The default value is 15 if <c>inflateInit/1</c> is used.
- If a compressed stream with a larger window size is
- given as input, inflate() will throw the <c>data_error</c>
- exception. A negative <c><anno>WindowBits</anno></c> value makes zlib ignore the
- zlib header (and checksum) from the stream. Note that the zlib
- source mentions this only as a undocumented feature.</p>
+ <p>Uncompresses data with gz headers and checksum.</p>
</desc>
</func>
+
+ <func>
+ <name name="gzip" arity="1"/>
+ <fsummary>Compress data with gz header.</fsummary>
+ <desc>
+ <p>Compresses data with gz headers and checksum.</p>
+ </desc>
+ </func>
+
<func>
<name name="inflate" arity="2"/>
- <fsummary>Decompress data</fsummary>
+ <fsummary>Decompress data.</fsummary>
<desc>
- <p><c>inflate/2</c> decompresses as much data as possible.
- It may introduce some output latency (reading
+ <p>Decompresses as much data as possible.
+ It can introduce some output latency (reading
input without producing any output).</p>
<p>If a preset dictionary is needed at this point (see
- <c>inflateSetDictionary</c> below), <c>inflate/2</c> throws a
- <c>{need_dictionary,Adler}</c> exception where <c>Adler</c> is
- the adler32 checksum of the dictionary chosen by the
- compressor.</p>
+ <seealso marker="#inflateSetDictionary/2">
+ <c>inflateSetDictionary/2</c></seealso>), <c>inflate/2</c> throws a
+ <c>{need_dictionary,Adler}</c> exception, where <c>Adler</c> is
+ the Adler-32 checksum of the dictionary chosen by the compressor.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="inflateChunk" arity="1"/>
+ <fsummary>Read next uncompressed chunk.</fsummary>
+ <desc>
+ <p>Reads the next chunk of uncompressed data, initialized by
+ <seealso marker="#inflateChunk/2"><c>inflateChunk/2</c></seealso>.</p>
+ <p>This function is to be repeatedly called, while it returns
+ <c>{more, Decompressed}</c>.</p>
</desc>
</func>
+
<func>
<name name="inflateChunk" arity="2"/>
- <fsummary>Decompress data with limited output size</fsummary>
+ <fsummary>Decompress data with limited output size.</fsummary>
<desc>
- <p>Like <c>inflate/2</c>, but decompress no more data than
- will fit in the buffer configured via <c>setBufSize/2</c>.
+ <p>Like <seealso marker="#inflate/2"><c>inflate/2</c></seealso>,
+ but decompresses no more data than will fit in the buffer configured
+ through <seealso marker="#setBufSize/2"><c>setBufSize/2</c></seealso>.
Is is useful when decompressing a stream with a high compression
- ratio such that a small amount of compressed input may expand up to
- 1000 times.
- It returns <c>{more, Decompressed}</c>, when there is more output
- available, and <c>inflateChunk/1</c> should be used to read it.
- It may introduce some output latency (reading
+ ratio, such that a small amount of compressed input can expand up to
+ 1000 times.</p>
+ <p>This function returns <c>{more, Decompressed}</c>, when there is
+ more output available, and
+ <seealso marker="#inflateChunk/1"><c>inflateChunk/1</c></seealso>
+ is to be used to read it.</p>
+ <p>This function can introduce some output latency (reading
input without producing any output).</p>
<p>If a preset dictionary is needed at this point (see
- <c>inflateSetDictionary</c> below), <c>inflateChunk/2</c> throws a
- <c>{need_dictionary,Adler}</c> exception where <c>Adler</c> is
- the adler32 checksum of the dictionary chosen by the
- compressor.</p>
-
+ <seealso marker="#inflateSetDictionary/2">
+ <c>inflateSetDictionary/2</c></seealso>), this function throws a
+ <c>{need_dictionary,Adler}</c> exception, where <c>Adler</c> is
+ the Adler-32 checksum of the dictionary chosen by the compressor.</p>
+ <p>Example:</p>
<pre>
walk(Compressed, Handler) ->
Z = zlib:open(),
@@ -345,191 +501,126 @@ loop(Z, Handler, {more, Uncompressed}) ->
Handler(Uncompressed),
loop(Z, Handler, zlib:inflateChunk(Z));
loop(Z, Handler, Uncompressed) ->
- Handler(Uncompressed).
- </pre>
- </desc>
- </func>
- <func>
- <name name="inflateChunk" arity="1"/>
- <fsummary>Read next uncompressed chunk</fsummary>
- <desc>
- <p>Read next chunk of uncompressed data, initialized by
- <c>inflateChunk/2</c>.</p>
- <p>This function should be repeatedly called, while it returns
- <c>{more, Decompressed}</c>.</p>
- </desc>
- </func>
- <func>
- <name name="inflateSetDictionary" arity="2"/>
- <fsummary>Initialize the decompression dictionary</fsummary>
- <desc>
- <p>Initializes the decompression dictionary from the given
- uncompressed byte sequence. This function must be called
- immediately after a call of <c>inflate/2</c> if this call
- threw a <c>{need_dictionary,Adler}</c> exception.
- The dictionary chosen by the
- compressor can be determined from the Adler value thrown
- by the call to <c>inflate/2</c>. The compressor and decompressor
- must use exactly the same dictionary (see <c>deflateSetDictionary/2</c>).</p>
- <p>Example:</p>
- <pre>
-unpack(Z, Compressed, Dict) ->
- case catch zlib:inflate(Z, Compressed) of
- {'EXIT',{{need_dictionary,DictID},_}} ->
- zlib:inflateSetDictionary(Z, Dict),
- Uncompressed = zlib:inflate(Z, []);
- Uncompressed ->
- Uncompressed
- end.</pre>
- </desc>
- </func>
- <func>
- <name name="inflateReset" arity="1"/>
- <fsummary>>Reset the inflate session</fsummary>
- <desc>
- <p>This function is equivalent to <c>inflateEnd/1</c> followed
- by <c>inflateInit/1</c>, but does not free and reallocate all
- the internal decompression state. The stream will keep
- attributes that may have been set by <c>inflateInit/[1|2]</c>.</p>
+ Handler(Uncompressed).</pre>
</desc>
</func>
+
<func>
<name name="inflateEnd" arity="1"/>
- <fsummary>End inflate session</fsummary>
+ <fsummary>End inflate session.</fsummary>
<desc>
- <p>End the inflate session and cleans all data used. Note
- that this function will throw a <c>data_error</c> exception
+ <p>Ends the inflate session and cleans all data used. Notice
+ that this function throws a <c>data_error</c> exception
if no end of stream was found (meaning that not all data
has been uncompressed).</p>
</desc>
</func>
+
<func>
- <name name="setBufSize" arity="2"/>
- <fsummary>Set buffer size</fsummary>
- <desc>
- <p>Sets the intermediate buffer size.</p>
- </desc>
- </func>
- <func>
- <name name="getBufSize" arity="1"/>
- <fsummary>Get buffer size</fsummary>
- <desc>
- <p>Get the size of intermediate buffer.</p>
- </desc>
- </func>
- <func>
- <name name="crc32" arity="1"/>
- <fsummary>Get current CRC</fsummary>
- <desc>
- <p>Get the current calculated CRC checksum.</p>
- </desc>
- </func>
- <func>
- <name name="crc32" arity="2"/>
- <fsummary>Calculate CRC</fsummary>
- <desc>
- <p>Calculate the CRC checksum for <c><anno>Data</anno></c>.</p>
- </desc>
- </func>
- <func>
- <name name="crc32" arity="3"/>
- <fsummary>Calculate CRC</fsummary>
+ <name name="inflateInit" arity="1"/>
+ <fsummary>Initialize a session for decompression.</fsummary>
<desc>
- <p>Update a running CRC checksum for <c><anno>Data</anno></c>.
- If <c><anno>Data</anno></c> is the empty binary or the empty iolist, this function returns
- the required initial value for the crc.</p>
- <pre>
-Crc = lists:foldl(fun(Data,Crc0) ->
- zlib:crc32(Z, Crc0, Data),
- end, zlib:crc32(Z,&lt;&lt; &gt;&gt;), Datas)</pre>
+ <p>Initializes a zlib stream for decompression.</p>
</desc>
</func>
+
<func>
- <name name="crc32_combine" arity="4"/>
- <fsummary>Combine two CRC's</fsummary>
+ <name name="inflateInit" arity="2"/>
+ <fsummary>Initialize a session for decompression.</fsummary>
<desc>
- <p>Combine two CRC checksums into one. For two binaries or iolists,
- <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c> and
- <c><anno>Size2</anno></c>, with CRC checksums <c><anno>CRC1</anno></c> and
- <c><anno>CRC2</anno></c>. <c>crc32_combine/4</c> returns the <c><anno>CRC</anno></c>
- checksum of <c>[Data1,Data2]</c>, requiring
- only <c><anno>CRC1</anno></c>, <c><anno>CRC2</anno></c>, and <c><anno>Size2</anno></c>.
- </p>
+ <p>Initializes a decompression session on zlib stream.</p>
+ <p><c><anno>WindowBits</anno></c> is the base two logarithm
+ of the maximum window size (the size of the history buffer).
+ It is to be in the range 8 through 15. Default to 15 if
+ <seealso marker="#inflateInit/1"><c>inflateInit/1</c></seealso>
+ is used.</p>
+ <p>If a compressed stream with a larger window size is specified as
+ input, <seealso marker="#inflate/2"><c>inflate/2</c></seealso>
+ throws the <c>data_error</c> exception.</p>
+ <p>A negative <c><anno>WindowBits</anno></c> value makes zlib
+ ignore the zlib header (and checksum) from the stream. Notice that
+ the zlib source mentions this only as a undocumented feature.</p>
</desc>
</func>
+
<func>
- <name name="adler32" arity="2"/>
- <fsummary>Calculate the adler checksum</fsummary>
+ <name name="inflateReset" arity="1"/>
+ <fsummary>>Reset the inflate session.</fsummary>
<desc>
- <p>Calculate the Adler-32 checksum for <c><anno>Data</anno></c>.</p>
+ <p>Equivalent to
+ <seealso marker="#inflateEnd/1"><c>inflateEnd/1</c></seealso>
+ followed by
+ <seealso marker="#inflateInit/1"><c>inflateInit/1</c></seealso>,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that could have been set by
+ <c>inflateInit/1,2</c>.</p>
</desc>
</func>
+
<func>
- <name name="adler32" arity="3"/>
- <fsummary>Calculate the adler checksum</fsummary>
+ <name name="inflateSetDictionary" arity="2"/>
+ <fsummary>Initialize the decompression dictionary.</fsummary>
<desc>
- <p>Update a running Adler-32 checksum for <c><anno>Data</anno></c>.
- If <c><anno>Data</anno></c> is the empty binary or the empty iolist, this function returns
- the required initial value for the checksum.</p>
+ <p>Initializes the decompression dictionary from the specified
+ uncompressed byte sequence. This function must be called
+ immediately after a call of
+ <seealso marker="#inflate/2"><c>inflate/2</c></seealso>
+ if this call threw a <c>{need_dictionary,Adler}</c> exception.
+ The dictionary chosen by the compressor can be determined from the
+ Adler value thrown by the call to <c>inflate/2</c>.
+ The compressor and decompressor must use the same dictionary (see
+ <seealso marker="#deflateSetDictionary/2">
+ <c>deflateSetDictionary/2</c></seealso>).</p>
+ <p>Example:</p>
<pre>
-Crc = lists:foldl(fun(Data,Crc0) ->
- zlib:adler32(Z, Crc0, Data),
- end, zlib:adler32(Z,&lt;&lt; &gt;&gt;), Datas)</pre>
+unpack(Z, Compressed, Dict) ->
+ case catch zlib:inflate(Z, Compressed) of
+ {'EXIT',{{need_dictionary,DictID},_}} ->
+ zlib:inflateSetDictionary(Z, Dict),
+ Uncompressed = zlib:inflate(Z, []);
+ Uncompressed ->
+ Uncompressed
+ end.</pre>
</desc>
</func>
+
<func>
- <name name="adler32_combine" arity="4"/>
- <fsummary>Combine two Adler-32 checksums</fsummary>
+ <name name="open" arity="0"/>
+ <fsummary>Open a stream and return a stream reference.</fsummary>
<desc>
- <p>Combine two Adler-32 checksums into one. For two binaries or iolists,
- <c>Data1</c> and <c>Data2</c> with sizes of <c>Size1</c> and
- <c><anno>Size2</anno></c>, with Adler-32 checksums <c><anno>Adler1</anno></c> and
- <c><anno>Adler2</anno></c>. <c>adler32_combine/4</c> returns the <c><anno>Adler</anno></c>
- checksum of <c>[Data1,Data2]</c>, requiring
- only <c><anno>Adler1</anno></c>, <c><anno>Adler2</anno></c>, and <c><anno>Size2</anno></c>.
- </p>
+ <p>Opens a zlib stream.</p>
</desc>
</func>
+
<func>
- <name name="compress" arity="1"/>
- <fsummary>Compress data with standard zlib functionality</fsummary>
+ <name name="setBufSize" arity="2"/>
+ <fsummary>Set buffer size.</fsummary>
<desc>
- <p>Compress data (with zlib headers and checksum).</p>
+ <p>Sets the intermediate buffer size.</p>
</desc>
</func>
+
<func>
<name name="uncompress" arity="1"/>
- <fsummary>Uncompress data with standard zlib functionality</fsummary>
+ <fsummary>Uncompress data with standard zlib functionality.</fsummary>
<desc>
- <p>Uncompress data (with zlib headers and checksum).</p>
- </desc>
- </func>
- <func>
- <name name="zip" arity="1"/>
- <fsummary>Compress data without the zlib headers</fsummary>
- <desc>
- <p>Compress data (without zlib headers and checksum).</p>
+ <p>Uncompresses data with zlib headers and checksum.</p>
</desc>
</func>
+
<func>
<name name="unzip" arity="1"/>
- <fsummary>Uncompress data without the zlib headers</fsummary>
+ <fsummary>Uncompress data without the zlib headers.</fsummary>
<desc>
- <p>Uncompress data (without zlib headers and checksum).</p>
- </desc>
- </func>
- <func>
- <name name="gzip" arity="1"/>
- <fsummary>Compress data with gz header</fsummary>
- <desc>
- <p>Compress data (with gz headers and checksum).</p>
+ <p>Uncompresses data without zlib headers and checksum.</p>
</desc>
</func>
+
<func>
- <name name="gunzip" arity="1"/>
- <fsummary>Uncompress data with gz header</fsummary>
+ <name name="zip" arity="1"/>
+ <fsummary>Compress data without the zlib headers.</fsummary>
<desc>
- <p>Uncompress data (with gz headers and checksum).</p>
+ <p>Compresses data without zlib headers and checksum.</p>
</desc>
</func>
</funcs>
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 2212aed5e0..8772befe27 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -49,8 +49,10 @@ CREATE_DIRS=
LDFLAGS=@LDFLAGS@
ARFLAGS=rc
OMIT_OMIT_FP=no
+TYPE_LIBS=
DIRTY_SCHEDULER_SUPPORT=@DIRTY_SCHEDULER_SUPPORT@
+NEW_PURGE_STRATEGY=@NEW_PURGE_STRATEGY@
ifeq ($(TYPE),debug)
PURIFY =
@@ -89,7 +91,7 @@ PURIFY =
TYPEMARKER = .gcov
TYPE_FLAGS = $(DEBUG_CFLAGS) -DERTS_GCOV -DNO_JUMP_TABLE -fprofile-arcs -ftest-coverage -O0 -DERTS_CAN_INLINE=0 -DERTS_INLINE=
ifneq ($(findstring solaris,$(TARGET)),solaris)
-LIBS += -lgcov
+TYPE_LIBS = -lgcov
endif
ENABLE_ALLOC_TYPE_VARS += debug
else
@@ -145,6 +147,8 @@ endif
endif
endif
+LIBS += $(TYPE_LIBS)
+
comma:=,
space:=
space+=
@@ -591,11 +595,8 @@ GENERATE += $(TTF_DIR)/driver_tab.c
#
# This list must be consistent with PRE_LOADED_MODULES in
# erts/preloaded/src/Makefile.
-ifeq ($(TARGET),win32)
-# On windows the preloaded objects are in a resource object.
-PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT)
-PRELOAD_SRC = $(TARGET)/beams.rc
-$(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
+
+PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
$(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \
$(ERL_TOP)/erts/preloaded/ebin/init.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \
@@ -606,23 +607,20 @@ $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
$(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \
$(ERL_TOP)/erts/preloaded/ebin/erlang.beam \
$(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam
+ $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/erts_literal_area_collector.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
+
+ifeq ($(TARGET),win32)
+# On windows the preloaded objects are in a resource object.
+PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT)
+PRELOAD_SRC = $(TARGET)/beams.rc
+$(PRELOAD_SRC): $(PRELOAD_BEAM)
$(gen_verbose)LANG=C $(PERL) utils/make_preload $(MAKE_PRELOAD_EXTRA) -rc $^ > $@
else
PRELOAD_OBJ = $(OBJDIR)/preload.o
PRELOAD_SRC = $(TARGET)/preload.c
-$(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \
- $(ERL_TOP)/erts/preloaded/ebin/init.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_inet.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_file.beam \
- $(ERL_TOP)/erts/preloaded/ebin/zlib.beam \
- $(ERL_TOP)/erts/preloaded/ebin/prim_zip.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \
- $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam
+$(PRELOAD_SRC): $(PRELOAD_BEAM)
$(gen_verbose)LANG=C $(PERL) utils/make_preload -old $^ > $@
endif
@@ -744,7 +742,7 @@ EMU_OBJS = \
$(OBJDIR)/beam_ranges.o
RUN_OBJS = \
- $(OBJDIR)/erl_pbifs.o $(OBJDIR)/benchmark.o \
+ $(OBJDIR)/erl_pbifs.o \
$(OBJDIR)/erl_alloc.o $(OBJDIR)/erl_mtrace.o \
$(OBJDIR)/erl_alloc_util.o $(OBJDIR)/erl_goodfit_alloc.o \
$(OBJDIR)/erl_bestfit_alloc.o $(OBJDIR)/erl_afit_alloc.o \
@@ -936,7 +934,7 @@ $(OBJDIR)/%.o: hipe/%.c
$(V_CC) $(subst O2,O3, $(CFLAGS)) $(INCLUDES) -c $< -o $@
$(BINDIR)/hipe_mkliterals$(TF_MARKER): $(OBJDIR)/hipe_mkliterals.o
- $(ld_verbose)$(CC) $(CFLAGS) $(INCLUDES) -o $@ $<
+ $(ld_verbose)$(CC) $(LDFLAGS) -o $@ $< $(TYPE_LIBS)
$(OBJDIR)/hipe_mkliterals.o: $(HIPE_ASM) $(TTF_DIR)/erl_alloc_types.h $(DTRACE_HEADERS) \
$(TTF_DIR)/OPCODES-GENERATED $(TARGET)/TABLES-GENERATED
diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c
index a5e778e4aa..e7a655b60f 100644
--- a/erts/emulator/beam/atom.c
+++ b/erts/emulator/beam/atom.c
@@ -68,7 +68,7 @@ static Uint atom_space; /* Amount of atom text space used */
/*
* Print info about atom tables
*/
-void atom_info(int to, void *to_arg)
+void atom_info(fmtfn_t to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
@@ -138,7 +138,7 @@ atom_hash(Atom* obj)
while(len--) {
v = *p++;
/* latin1 clutch for r16 */
- if ((v & 0xFE) == 0xC2 && (*p & 0xC0) == 0x80) {
+ if (len && (v & 0xFE) == 0xC2 && (*p & 0xC0) == 0x80) {
v = (v << 6) | (*p & 0x3F);
p++; len--;
}
@@ -470,7 +470,7 @@ init_atom_table(void)
}
void
-dump_atoms(int to, void *to_arg)
+dump_atoms(fmtfn_t to, void *to_arg)
{
int i = erts_atom_table.entries;
diff --git a/erts/emulator/beam/atom.h b/erts/emulator/beam/atom.h
index fbd0528009..a82efabb9f 100644
--- a/erts/emulator/beam/atom.h
+++ b/erts/emulator/beam/atom.h
@@ -21,10 +21,7 @@
#ifndef __ATOM_H__
#define __ATOM_H__
-#ifndef __INDEX_H__
#include "index.h"
-#endif
-
#include "erl_atom_table.h"
#define MAX_ATOM_CHARACTERS 255
@@ -139,8 +136,8 @@ Eterm erts_atom_put(const byte *name, int len, ErtsAtomEncoding enc, int trunc);
int atom_erase(byte*, int);
int atom_static_put(byte*, int);
void init_atom_table(void);
-void atom_info(int, void *);
-void dump_atoms(int, void *);
+void atom_info(fmtfn_t, void *);
+void dump_atoms(fmtfn_t, void *);
int erts_atom_get(const char* name, int len, Eterm* ap, ErtsAtomEncoding enc);
void erts_atom_get_text_space_sizes(Uint *reserved, Uint *used);
#endif
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index badd69856e..2b66bf6f4e 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -65,6 +65,7 @@ atom undefined_lambda
atom DOWN='DOWN'
atom UP='UP'
atom EXIT='EXIT'
+atom abort
atom aborted
atom abs_path
atom absoluteURI
@@ -165,6 +166,7 @@ atom commandv
atom compact
atom compat_rel
atom compile
+atom complete
atom compressed
atom config_h
atom convert_time_unit
@@ -176,6 +178,7 @@ atom const
atom context_switches
atom control
atom copy
+atom copy_literals
atom counters
atom cpu
atom cpu_timestamp
@@ -195,6 +198,7 @@ atom dgroup_leader
atom dictionary
atom dirty_cpu
atom dirty_cpu_schedulers_online
+atom dirty_execution
atom dirty_io
atom disable_trace
atom disabled
@@ -235,6 +239,7 @@ atom erlang
atom ERROR='ERROR'
atom error_handler
atom error_logger
+atom erts_code_purger
atom erts_internal
atom ets
atom ETS_TRANSFER='ETS-TRANSFER'
@@ -384,8 +389,10 @@ atom merge_trap
atom meta
atom meta_match_spec
atom micro_seconds
+atom microsecond
atom microstate_accounting
atom milli_seconds
+atom millisecond
atom min_heap_size
atom min_bin_vheap_size
atom minor_version
@@ -402,11 +409,13 @@ atom more
atom multi_scheduling
atom multiline
atom nano_seconds
+atom nanosecond
atom name
atom named_table
atom namelist
atom native
atom native_addresses
+atom need_gc
atom Neq='=/='
atom Neqeq='/='
atom net_kernel
@@ -485,6 +494,7 @@ atom pause
atom pending
atom pending_driver
atom pending_process
+atom pending_purge_lambda
atom pending_reload
atom permanent
atom pid
@@ -494,6 +504,8 @@ atom port_count
atom port_limit
atom port_op
atom positive
+atom prepare
+atom prepare_on_load
atom print
atom priority
atom private
@@ -554,6 +566,7 @@ atom schedulers_online
atom scheme
atom scientific
atom scope
+atom second
atom seconds
atom send_to_non_existing_process
atom sensitive
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 15e878ba65..8fc8613c66 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2017. 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.
@@ -37,13 +37,93 @@
#include "erl_bits.h"
#include "erl_thr_progress.h"
+#ifdef HIPE
+# include "hipe_stack.h"
+#endif
+
+static struct {
+ Eterm module;
+ erts_smp_mtx_t mtx;
+ Export *pending_purge_lambda;
+ Eterm *sprocs;
+ Eterm def_sprocs[10];
+ Uint sp_size;
+ Uint sp_ix;
+ ErlFunEntry **funs;
+ ErlFunEntry *def_funs[10];
+ Uint fe_size;
+ Uint fe_ix;
+ struct erl_module_instance saved_old;
+} purge_state;
+
+Process *erts_code_purger = NULL;
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+Process *erts_dirty_process_code_checker;
+#endif
+erts_smp_atomic_t erts_copy_literal_area__;
+#define ERTS_SET_COPY_LITERAL_AREA(LA) \
+ erts_smp_atomic_set_nob(&erts_copy_literal_area__, \
+ (erts_aint_t) (LA))
+#ifdef ERTS_NEW_PURGE_STRATEGY
+Process *erts_literal_area_collector = NULL;
+
+typedef struct ErtsLiteralAreaRef_ ErtsLiteralAreaRef;
+struct ErtsLiteralAreaRef_ {
+ ErtsLiteralAreaRef *next;
+ ErtsLiteralArea *literal_area;
+};
+
+struct {
+ erts_smp_mtx_t mtx;
+ ErtsLiteralAreaRef *first;
+ ErtsLiteralAreaRef *last;
+} release_literal_areas;
+#endif
+
static void set_default_trace_pattern(Eterm module);
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);
static int any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size);
+static void
+init_purge_state(void)
+{
+ purge_state.module = THE_NON_VALUE;
+
+ erts_smp_mtx_init(&purge_state.mtx, "purge_state");
+
+ purge_state.pending_purge_lambda =
+ erts_export_put(am_erts_code_purger, am_pending_purge_lambda, 3);
+
+ purge_state.sprocs = &purge_state.def_sprocs[0];
+ purge_state.sp_size = sizeof(purge_state.def_sprocs);
+ purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]);
+ purge_state.sp_ix = 0;
+
+ purge_state.funs = &purge_state.def_funs[0];
+ purge_state.fe_size = sizeof(purge_state.def_funs);
+ purge_state.fe_size /= sizeof(purge_state.def_funs[0]);
+ purge_state.fe_ix = 0;
+
+ purge_state.saved_old.code_hdr = 0;
+}
+
+void
+erts_beam_bif_load_init(void)
+{
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ erts_smp_mtx_init(&release_literal_areas.mtx, "release_literal_areas");
+ release_literal_areas.first = NULL;
+ release_literal_areas.last = NULL;
+#endif
+ erts_smp_atomic_init_nob(&erts_copy_literal_area__,
+ (erts_aint_t) NULL);
+
+ init_purge_state();
+}
+
BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1)
{
Module* modp;
@@ -162,7 +242,7 @@ struct m {
Binary* code;
Eterm module;
Module* modp;
- Uint exception;
+ Eterm exception;
};
static Eterm staging_epilogue(Process* c_p, int, Eterm res, int, struct m*, int);
@@ -183,7 +263,7 @@ exception_list(Process* p, Eterm tag, struct m* mp, Sint exceptions)
Eterm res = NIL;
while (exceptions > 0) {
- if (mp->exception) {
+ if (is_value(mp->exception)) {
res = CONS(hp, mp->module, res);
hp += 2;
exceptions--;
@@ -286,9 +366,9 @@ finish_loading_1(BIF_ALIST_1)
exceptions = 0;
for (i = 0; i < n; i++) {
- p[i].exception = 0;
+ p[i].exception = THE_NON_VALUE;
if (p[i].modp->seen) {
- p[i].exception = 1;
+ p[i].exception = am_duplicated;
exceptions++;
}
p[i].modp->seen = 1;
@@ -321,9 +401,9 @@ finish_loading_1(BIF_ALIST_1)
exceptions = 0;
for (i = 0; i < n; i++) {
- p[i].exception = 0;
+ p[i].exception = THE_NON_VALUE;
if (p[i].modp->curr.code_hdr && p[i].modp->old.code_hdr) {
- p[i].exception = 1;
+ p[i].exception = am_not_purged;
exceptions++;
}
}
@@ -344,7 +424,7 @@ finish_loading_1(BIF_ALIST_1)
retval = erts_finish_loading(p[i].code, BIF_P, 0, &mod);
ASSERT(retval == NIL || retval == am_on_load);
if (retval == am_on_load) {
- p[i].exception = 1;
+ p[i].exception = am_on_load;
exceptions++;
}
}
@@ -376,8 +456,9 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking,
erts_commit_staging_code_ix();
if (loaded) {
int i;
- for (i=0; i < nloaded; i++) {
- set_default_trace_pattern(loaded[i].module);
+ for (i=0; i < nloaded; i++) {
+ if (loaded[i].exception != am_on_load)
+ set_default_trace_pattern(loaded[i].module);
}
}
}
@@ -516,6 +597,43 @@ badarg:
BIF_ERROR(BIF_P, BADARG);
}
+BIF_RETTYPE erts_internal_check_dirty_process_code_2(BIF_ALIST_2)
+{
+#if !defined(ERTS_DIRTY_SCHEDULERS)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+#else
+ Process *rp;
+ int reds = 0;
+ Eterm res;
+
+ if (BIF_P != erts_dirty_process_code_checker)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+
+ if (is_not_internal_pid(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+
+ if (is_not_atom(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+
+ 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_YIELD2(bif_export[BIF_erts_internal_check_dirty_process_code_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ if (!rp)
+ BIF_RET(am_false);
+
+ res = erts_check_process_code(rp, BIF_ARG_2, 0, &reds, BIF_P->fcalls);
+
+ if (BIF_P != rp)
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+
+ ASSERT(is_value(res));
+
+ BIF_RET2(res, reds);
+#endif
+}
+
BIF_RETTYPE delete_module_1(BIF_ALIST_1)
{
ErtsCodeIndex code_ix;
@@ -541,7 +659,7 @@ BIF_RETTYPE delete_module_1(BIF_ALIST_1)
}
else if (modp->old.code_hdr) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp, "Module %T must be purged before loading\n",
+ erts_dsprintf(dsbufp, "Module %T must be purged before deleting\n",
BIF_ARG_1);
erts_send_error_to_logger(BIF_P->group_leader, dsbufp);
ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
@@ -625,10 +743,13 @@ 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->old.code_hdr) {
- BIF_TRAP_CODE_PTR_0(BIF_P, modp->old.code_hdr->on_load_function_ptr);
+ if (!modp || !modp->on_load) {
+ BIF_ERROR(BIF_P, BADARG);
}
- else {
+ if (modp->on_load->code_hdr) {
+ BIF_TRAP_CODE_PTR_0(BIF_P,
+ modp->on_load->code_hdr->on_load_function_ptr);
+ } else {
BIF_ERROR(BIF_P, BADARG);
}
}
@@ -651,14 +772,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->old.code_hdr) {
+ if (!modp || !modp->on_load || !modp->on_load->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->old.code_hdr->on_load_function_ptr == NULL) {
+ if (modp->on_load->code_hdr->on_load_function_ptr == NULL) {
goto error;
}
if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) {
@@ -666,20 +787,24 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
}
if (BIF_ARG_2 == am_true) {
- int i;
- struct erl_module_instance t;
+ int i, num_exps;
/*
- * Swap old and new code.
+ * Make the code with the on_load function current.
*/
- t = modp->curr;
- modp->curr = modp->old;
- modp->old = t;
+
+ if (modp->curr.code_hdr) {
+ modp->old = modp->curr;
+ }
+ modp->curr = *modp->on_load;
+ erts_free(ERTS_ALC_T_PREPARED_CODE, modp->on_load);
+ modp->on_load = 0;
/*
* The on_load function succeded. Fix up export entries.
*/
- for (i = 0; i < export_list_size(code_ix); i++) {
+ num_exps = export_list_size(code_ix);
+ for (i = 0; i < num_exps; i++) {
Export *ep = export_list(i,code_ix);
if (ep == NULL || ep->code[0] != BIF_ARG_1) {
continue;
@@ -699,14 +824,15 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
modp->curr.code_hdr->on_load_function_ptr = NULL;
set_default_trace_pattern(BIF_ARG_1);
} else if (BIF_ARG_2 == am_false) {
- int i;
+ int i, num_exps;
/*
* The on_load function failed. Remove references to the
* code that is about to be purged from the export entries.
*/
- for (i = 0; i < export_list_size(code_ix); i++) {
+ num_exps = export_list_size(code_ix);
+ for (i = 0; i < num_exps; i++) {
Export *ep = export_list(i,code_ix);
if (ep == NULL || ep->code[0] != BIF_ARG_1) {
continue;
@@ -748,6 +874,8 @@ set_default_trace_pattern(Eterm module)
}
}
+#ifndef ERTS_NEW_PURGE_STRATEGY
+
static ERTS_INLINE int
check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size)
{
@@ -762,12 +890,269 @@ check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size)
return 0;
}
+#endif
+
static Uint hfrag_literal_size(Eterm* start, Eterm* end,
char* lit_start, Uint lit_size);
static void hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
Eterm *start, Eterm *end,
char *lit_start, Uint lit_size);
+#ifdef ERTS_NEW_PURGE_STRATEGY
+
+Eterm
+erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed)
+{
+ ErtsLiteralArea *la;
+ ErtsMessage *msgp;
+ struct erl_off_heap_header* oh;
+ char *literals;
+ Uint lit_bsize;
+ ErlHeapFragment *hfrag;
+
+ la = ERTS_COPY_LITERAL_AREA();
+ if (!la)
+ return am_ok;
+
+ oh = la->off_heap;
+ literals = (char *) &la->start[0];
+ lit_bsize = (char *) la->end - literals;
+
+ /*
+ * If a literal is in the message queue we make an explicit copy of
+ * it and attach it to the heap fragment. Each message needs to be
+ * self contained, we cannot save the literal in the old_heap or
+ * any other heap than the message it self.
+ */
+
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ);
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ);
+
+ for (msgp = c_p->msg.first; msgp; msgp = msgp->next) {
+ ErlHeapFragment *hf;
+ Uint lit_sz = 0;
+
+ *redsp += 1;
+
+ if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
+ hfrag = &msgp->hfrag;
+ else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag)
+ hfrag = msgp->data.heap_frag;
+ else
+ continue; /* Content on heap or in external term format... */
+
+ for (hf = hfrag; hf; hf = hf->next) {
+ lit_sz += hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ *redsp += 1;
+ }
+
+ *redsp += lit_sz / 16; /* Better value needed... */
+ if (lit_sz > 0) {
+ ErlHeapFragment *bp = new_message_buffer(lit_sz);
+ Eterm *hp = bp->mem;
+
+ for (hf = hfrag; hf; hf = hf->next) {
+ hfrag_literal_copy(&hp, &bp->off_heap,
+ &hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ hfrag = hf;
+ }
+
+ /* link new hfrag last */
+ ASSERT(hfrag->next == NULL);
+ hfrag->next = bp;
+ bp->next = NULL;
+ }
+ }
+
+ if (gc_allowed) {
+ /*
+ * Current implementation first tests without
+ * allowing GC, and then restarts the operation
+ * allowing GC if it is needed. It is therfore
+ * very likely that we will need the GC (although
+ * this is not completely certain). We go for
+ * the GC directly instead of scanning everything
+ * one more time...
+ */
+ goto literal_gc;
+ }
+
+ *redsp += 2;
+ if (any_heap_ref_ptrs(&c_p->fvalue, &c_p->fvalue+1, literals, lit_bsize)) {
+ c_p->freason = EXC_NULL;
+ c_p->fvalue = NIL;
+ c_p->ftrace = NIL;
+ }
+
+ if (any_heap_ref_ptrs(c_p->stop, c_p->hend, literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+#ifdef HIPE
+ if (nstack_any_heap_ref_ptrs(c_p, literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+#endif
+ if (any_heap_refs(c_p->heap, c_p->htop, literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+ if (c_p->abandoned_heap) {
+ if (any_heap_refs(c_p->abandoned_heap, c_p->abandoned_heap + c_p->heap_sz,
+ literals, lit_bsize))
+ goto literal_gc;
+ *redsp += 1;
+ }
+ if (any_heap_refs(c_p->old_heap, c_p->old_htop, literals, lit_bsize))
+ goto literal_gc;
+
+ /* Check dictionary */
+ *redsp += 1;
+ if (c_p->dictionary) {
+ Eterm* start = ERTS_PD_START(c_p->dictionary);
+ Eterm* end = start + ERTS_PD_SIZE(c_p->dictionary);
+
+ if (any_heap_ref_ptrs(start, end, literals, lit_bsize))
+ goto literal_gc;
+ }
+
+ /* Check heap fragments */
+ for (hfrag = c_p->mbuf; hfrag; hfrag = hfrag->next) {
+ Eterm *hp, *hp_end;
+
+ *redsp += 1;
+
+ hp = &hfrag->mem[0];
+ hp_end = &hfrag->mem[hfrag->used_size];
+ if (any_heap_refs(hp, hp_end, literals, lit_bsize))
+ goto literal_gc;
+ }
+
+ /*
+ * Message buffer fragments (matched messages)
+ * - off heap lists should already have been moved into
+ * process off heap structure.
+ * - Check for literals
+ */
+ for (msgp = c_p->msg_frag; msgp; msgp = msgp->next) {
+ hfrag = erts_message_to_heap_frag(msgp);
+ for (; hfrag; hfrag = hfrag->next) {
+ Eterm *hp, *hp_end;
+
+ *redsp += 1;
+
+ hp = &hfrag->mem[0];
+ hp_end = &hfrag->mem[hfrag->used_size];
+
+ if (any_heap_refs(hp, hp_end, literals, lit_bsize))
+ goto literal_gc;
+ }
+ }
+
+ return am_ok;
+
+literal_gc: {
+ int hibernated = !!(c_p->flags & F_HIBERNATED);
+ int gc_cost;
+
+ if (!gc_allowed)
+ return am_need_gc;
+
+ if (c_p->flags & F_DISABLE_GC)
+ return THE_NON_VALUE;
+
+ FLAGS(c_p) |= F_NEED_FULLSWEEP;
+
+ gc_cost = erts_garbage_collect_nobump(c_p, 0, c_p->arg_reg, c_p->arity, fcalls);
+ *redsp += gc_cost;
+
+ erts_garbage_collect_literals(c_p, (Eterm *) literals, lit_bsize, oh);
+
+ *redsp += lit_bsize / 64; /* Need, better value... */
+
+ if (hibernated) {
+ erts_garbage_collect_hibernate(c_p);
+ *redsp += gc_cost;
+ }
+
+ return am_ok;
+ }
+}
+
+static Eterm
+check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls)
+{
+ BeamInstr* start;
+ char* mod_start;
+ Uint mod_size;
+ Eterm* sp;
+
+ *redsp += 1;
+
+ /*
+ * Pick up limits for the module.
+ */
+ start = (BeamInstr*) modp->old.code_hdr;
+ mod_start = (char *) start;
+ mod_size = modp->old.code_length;
+
+ /*
+ * Check if current instruction or continuation pointer points into module.
+ */
+ if (ErtsInArea(rp->i, mod_start, mod_size)
+ || ErtsInArea(rp->cp, mod_start, mod_size)) {
+ return am_true;
+ }
+
+ *redsp += 1;
+
+ if (erts_check_nif_export_in_area(rp, mod_start, mod_size))
+ return am_true;
+
+ *redsp += (STACK_START(rp) - rp->stop) / 32;
+
+ /*
+ * Check all continuation pointers stored on the stack.
+ */
+ for (sp = rp->stop; sp < STACK_START(rp); sp++) {
+ if (is_CP(*sp) && ErtsInArea(cp_val(*sp), mod_start, mod_size)) {
+ return am_true;
+ }
+ }
+
+ /*
+ * Check all continuation pointers stored in stackdump
+ * and clear exception stackdump if there is a pointer
+ * to the module.
+ */
+ if (rp->ftrace != NIL) {
+ struct StackTrace *s;
+ ASSERT(is_list(rp->ftrace));
+ s = (struct StackTrace *) big_val(CDR(list_val(rp->ftrace)));
+ if ((s->pc && ErtsInArea(s->pc, mod_start, mod_size)) ||
+ (s->current && ErtsInArea(s->current, mod_start, mod_size))) {
+ rp->freason = EXC_NULL;
+ rp->fvalue = NIL;
+ rp->ftrace = NIL;
+ } else {
+ int i;
+ for (i = 0; i < s->depth; i++) {
+ if (ErtsInArea(s->trace[i], mod_start, mod_size)) {
+ rp->freason = EXC_NULL;
+ rp->fvalue = NIL;
+ rp->ftrace = NIL;
+ break;
+ }
+ }
+ }
+ }
+
+ return am_false;
+}
+
+#else /* !ERTS_NEW_PURGE_STRATEGY, i.e, old style purge... */
+
static Eterm
check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls)
{
@@ -779,6 +1164,8 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
Eterm* sp;
int done_gc = 0;
int need_gc = 0;
+ int hibernated = !!(rp->flags & F_HIBERNATED);
+ int gc_cost = 0;
ErtsMessage *msgp;
ErlHeapFragment *hfrag;
@@ -799,6 +1186,12 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
|| ErtsInArea(rp->cp, mod_start, mod_size)) {
return am_true;
}
+
+ *redsp += 1;
+
+ if (erts_check_nif_export_in_area(rp, mod_start, mod_size))
+ return am_true;
+
/*
* Check all continuation pointers stored on the stack.
@@ -861,8 +1254,14 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
- literals = (char*) modp->old.code_hdr->literals_start;
- lit_bsize = (char*) modp->old.code_hdr->literals_end - literals;
+ if (modp->old.code_hdr->literal_area) {
+ literals = (char*) modp->old.code_hdr->literal_area->start;
+ lit_bsize = (char*) modp->old.code_hdr->literal_area->end - literals;
+ }
+ else {
+ literals = NULL;
+ lit_bsize = 0;
+ }
for (msgp = rp->msg.first; msgp; msgp = msgp->next) {
if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
@@ -901,14 +1300,13 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
while (1) {
/* Check heap, stack etc... */
- if (check_mod_funs(rp, &rp->off_heap, mod_start, mod_size))
+ if (check_mod_funs(rp, &rp->off_heap, mod_start, mod_size)) {
+ if (hibernated) {
+ /* GC wont help; everything on heap is live... */
+ return am_true;
+ }
goto try_gc;
- if (!(flags & ERTS_CPC_COPY_LITERALS)) {
- /* Process ok. May contain old literals but we will be called
- * again before module is purged.
- */
- return am_false;
- }
+ }
if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, literals, lit_bsize)) {
rp->freason = EXC_NULL;
rp->fvalue = NIL;
@@ -916,8 +1314,17 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
}
if (any_heap_ref_ptrs(rp->stop, rp->hend, literals, lit_bsize))
goto try_literal_gc;
+#ifdef HIPE
+ if (nstack_any_heap_ref_ptrs(rp, literals, lit_bsize))
+ goto try_literal_gc;
+#endif
if (any_heap_refs(rp->heap, rp->htop, literals, lit_bsize))
goto try_literal_gc;
+ if (rp->abandoned_heap) {
+ if (any_heap_refs(rp->abandoned_heap, rp->abandoned_heap + rp->heap_sz,
+ literals, lit_bsize))
+ goto try_literal_gc;
+ }
if (any_heap_refs(rp->old_heap, rp->old_htop, literals, lit_bsize))
goto try_literal_gc;
@@ -989,15 +1396,21 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
if (need_gc & ERTS_ORDINARY_GC__) {
FLAGS(rp) |= F_NEED_FULLSWEEP;
- *redsp += erts_garbage_collect_nobump(rp, 0, rp->arg_reg, rp->arity, fcalls);
+ gc_cost = erts_garbage_collect_nobump(rp, 0, rp->arg_reg, rp->arity, fcalls);
+ ASSERT(!hibernated || (need_gc & ERTS_LITERAL_GC__));
+ *redsp += gc_cost;
done_gc |= ERTS_ORDINARY_GC__;
}
if (need_gc & ERTS_LITERAL_GC__) {
struct erl_off_heap_header* oh;
- oh = modp->old.code_hdr->literals_off_heap;
+ oh = modp->old.code_hdr->literal_area->off_heap;
*redsp += lit_bsize / 64; /* Need, better value... */
erts_garbage_collect_literals(rp, (Eterm*)literals, lit_bsize, oh);
done_gc |= ERTS_LITERAL_GC__;
+ if (hibernated) {
+ erts_garbage_collect_hibernate(rp);
+ *redsp += gc_cost;
+ }
}
need_gc = 0;
}
@@ -1007,6 +1420,8 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
}
+#endif /* !ERTS_NEW_PURGE_STRATEGY */
+
static int
any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size)
{
@@ -1135,200 +1550,502 @@ hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
}
}
-#undef in_area
+#ifdef ERTS_NEW_PURGE_STRATEGY
#ifdef ERTS_SMP
-static void copy_literals_commit(void*);
-#endif
-copy_literals_t erts_clrange = {NULL, 0, THE_NON_VALUE};
+ErtsThrPrgrLaterOp later_literal_area_switch;
-/* copy literals
- *
- * copy_literals.ptr = LitPtr
- * copy_literals.sz = LitSz
- * ------ THR PROG COMMIT -----
- *
- * - check process code
- * - check process code
- * ...
- * copy_literals.ptr = NULL
- * copy_literals.sz = 0
- * ------ THR PROG COMMIT -----
- * ...
- */
+typedef struct {
+ ErtsThrPrgrLaterOp lop;
+ ErtsLiteralArea *la;
+} ErtsLaterReleasLiteralArea;
+static void
+later_release_literal_area(void *vlrlap)
+{
+ ErtsLaterReleasLiteralArea *lrlap;
+ lrlap = (ErtsLaterReleasLiteralArea *) vlrlap;
+ erts_release_literal_area(lrlap->la);
+ erts_free(ERTS_ALC_T_RELEASE_LAREA, vlrlap);
+}
-BIF_RETTYPE erts_internal_copy_literals_2(BIF_ALIST_2)
+static void
+complete_literal_area_switch(void *literal_area)
{
- ErtsCodeIndex code_ix;
- Eterm res = am_true;
+ Process *p = erts_literal_area_collector;
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ if (literal_area)
+ erts_release_literal_area((ErtsLiteralArea *) literal_area);
+}
+#endif
- if (is_not_atom(BIF_ARG_1) || (am_true != BIF_ARG_2 && am_false != BIF_ARG_2)) {
- BIF_ERROR(BIF_P, BADARG);
- }
+#endif /* ERTS_NEW_PURGE_STRATEGY */
- if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_copy_literals_2],
- BIF_P, BIF_ARG_1, BIF_ARG_2);
+BIF_RETTYPE erts_internal_release_literal_area_switch_0(BIF_ALIST_0)
+{
+#ifndef ERTS_NEW_PURGE_STRATEGY
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+#else
+ ErtsLiteralArea *unused_la;
+ ErtsLiteralAreaRef *la_ref;
+
+ if (BIF_P != erts_literal_area_collector)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+
+ erts_smp_mtx_lock(&release_literal_areas.mtx);
+
+ la_ref = release_literal_areas.first;
+ if (la_ref) {
+ release_literal_areas.first = la_ref->next;
+ if (!release_literal_areas.first)
+ release_literal_areas.last = NULL;
}
- code_ix = erts_active_code_ix();
+ erts_smp_mtx_unlock(&release_literal_areas.mtx);
- if (BIF_ARG_2 == am_true) {
- Module* modp = erts_get_module(BIF_ARG_1, code_ix);
- if (!modp || !modp->old.code_hdr) {
- res = am_false;
- goto done;
- }
- if (erts_clrange.ptr != NULL
- && !(BIF_P->static_flags & ERTS_STC_FLG_SYSTEM_PROC)) {
- res = am_aborted;
- goto done;
- }
- erts_clrange.ptr = modp->old.code_hdr->literals_start;
- erts_clrange.sz = modp->old.code_hdr->literals_end - erts_clrange.ptr;
- erts_clrange.pid = BIF_P->common.id;
- } else if (BIF_ARG_2 == am_false) {
- if (erts_clrange.pid != BIF_P->common.id) {
- res = am_false;
- goto done;
- }
- erts_clrange.ptr = NULL;
- erts_clrange.sz = 0;
- erts_clrange.pid = THE_NON_VALUE;
+ unused_la = ERTS_COPY_LITERAL_AREA();
+
+ if (!la_ref) {
+ ERTS_SET_COPY_LITERAL_AREA(NULL);
+ if (unused_la) {
+#ifdef ERTS_SMP
+ ErtsLaterReleasLiteralArea *lrlap;
+ lrlap = erts_alloc(ERTS_ALC_T_RELEASE_LAREA,
+ sizeof(ErtsLaterReleasLiteralArea));
+ lrlap->la = unused_la;
+ erts_schedule_thr_prgr_later_cleanup_op(
+ later_release_literal_area,
+ (void *) lrlap,
+ &lrlap->lop,
+ (sizeof(ErtsLaterReleasLiteralArea)
+ + sizeof(ErtsLiteralArea)
+ + ((unused_la->end
+ - &unused_la->start[0])
+ - 1)*(sizeof(Eterm))));
+#else
+ erts_release_literal_area(unused_la);
+#endif
+ }
+ BIF_RET(am_false);
}
+ ERTS_SET_COPY_LITERAL_AREA(la_ref->literal_area);
+
+ erts_free(ERTS_ALC_T_LITERAL_REF, la_ref);
+
#ifdef ERTS_SMP
- ASSERT(committer_state.stager == NULL);
- committer_state.stager = BIF_P;
- erts_schedule_thr_prgr_later_op(copy_literals_commit, NULL, &committer_state.lop);
- erts_proc_inc_refc(BIF_P);
+ erts_schedule_thr_prgr_later_op(complete_literal_area_switch,
+ unused_la,
+ &later_literal_area_switch);
erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
+#else
+ erts_release_literal_area(unused_la);
+ BIF_RET(am_true);
#endif
-done:
- erts_release_code_write_permission();
- BIF_RET(res);
+
+#endif /* ERTS_NEW_PURGE_STRATEGY */
+}
+
+void
+erts_purge_state_add_fun(ErlFunEntry *fe)
+{
+ ASSERT(is_value(purge_state.module));
+ if (purge_state.fe_ix >= purge_state.fe_size) {
+ ErlFunEntry **funs;
+ purge_state.fe_size += 100;
+ funs = erts_alloc(ERTS_ALC_T_PURGE_DATA,
+ sizeof(ErlFunEntry *)*purge_state.fe_size);
+ sys_memcpy((void *) funs,
+ (void *) purge_state.funs,
+ purge_state.fe_ix*sizeof(ErlFunEntry *));
+ if (purge_state.funs != &purge_state.def_funs[0])
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.funs);
+ purge_state.funs = funs;
+ }
+ purge_state.funs[purge_state.fe_ix++] = fe;
+}
+
+Export *
+erts_suspend_process_on_pending_purge_lambda(Process *c_p, ErlFunEntry* fe)
+{
+ erts_smp_mtx_lock(&purge_state.mtx);
+ if (purge_state.module == fe->module) {
+ /*
+ * The process c_p is about to call a fun in the code
+ * that we are trying to purge. Suspend it and call
+ * erts_code_purger:pending_purge_lambda/3. The process
+ * will be resumed when the purge completes or aborts,
+ * and will then try to do the call again.
+ */
+ if (purge_state.sp_ix >= purge_state.sp_size) {
+ Eterm *sprocs;
+ purge_state.sp_size += 100;
+ sprocs = erts_alloc(ERTS_ALC_T_PURGE_DATA,
+ (sizeof(ErlFunEntry *)
+ * purge_state.sp_size));
+ sys_memcpy((void *) sprocs,
+ (void *) purge_state.sprocs,
+ purge_state.sp_ix*sizeof(ErlFunEntry *));
+ if (purge_state.sprocs != &purge_state.def_sprocs[0])
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs);
+ purge_state.sprocs = sprocs;
+ }
+ purge_state.sprocs[purge_state.sp_ix++] = c_p->common.id;
+ erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_VBUMP_ALL_REDS(c_p);
+ }
+ erts_smp_mtx_unlock(&purge_state.mtx);
+ return purge_state.pending_purge_lambda;
+}
+
+static void
+finalize_purge_operation(Process *c_p, int succeded)
+{
+ Uint ix;
+
+ if (c_p)
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ erts_smp_mtx_lock(&purge_state.mtx);
+
+ ASSERT(purge_state.module != THE_NON_VALUE);
+
+ purge_state.module = THE_NON_VALUE;
+
+ /*
+ * Resume all processes that have tried to call
+ * funs in this code.
+ */
+ for (ix = 0; ix < purge_state.sp_ix; ix++) {
+ Process *rp = erts_pid2proc(NULL, 0,
+ purge_state.sprocs[ix],
+ ERTS_PROC_LOCK_STATUS);
+ if (rp) {
+ erts_resume(rp, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+ }
+ }
+
+ erts_smp_mtx_unlock(&purge_state.mtx);
+
+ if (c_p)
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ if (purge_state.sprocs != &purge_state.def_sprocs[0]) {
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs);
+ purge_state.sprocs = &purge_state.def_sprocs[0];
+ purge_state.sp_size = sizeof(purge_state.def_sprocs);
+ purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]);
+ }
+ purge_state.sp_ix = 0;
+
+ if (purge_state.funs != &purge_state.def_funs[0]) {
+ erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.funs);
+ purge_state.funs = &purge_state.def_funs[0];
+ purge_state.fe_size = sizeof(purge_state.def_funs);
+ purge_state.fe_size /= sizeof(purge_state.def_funs[0]);
+ }
+ purge_state.fe_ix = 0;
}
#ifdef ERTS_SMP
-static void copy_literals_commit(void* null) {
- Process* p = committer_state.stager;
-#ifdef DEBUG
- committer_state.stager = NULL;
-#endif
- erts_release_code_write_permission();
+
+static ErtsThrPrgrLaterOp purger_lop_data;
+
+static void
+resume_purger(void *unused)
+{
+ Process *p = erts_code_purger;
erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- if (!ERTS_PROC_IS_EXITING(p)) {
- erts_resume(p, ERTS_PROC_LOCK_STATUS);
- }
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
- erts_proc_dec_refc(p);
}
-#endif /* ERTS_SMP */
+static void
+finalize_purge_abort(void *unused)
+{
+ erts_fun_purge_abort_finalize(purge_state.funs, purge_state.fe_ix);
+
+ finalize_purge_operation(NULL, 0);
-/* Do the actualy module purging and return:
- * true for success
- * false if no such old module
- * BADARG if not an atom
- */
-BIF_RETTYPE erts_internal_purge_module_1(BIF_ALIST_1)
+ resume_purger(NULL);
+}
+
+#endif /* ERTS_SMP */
+
+BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
{
- ErtsCodeIndex code_ix;
- BeamInstr* code;
- BeamInstr* end;
- Module* modp;
- int is_blocking = 0;
- Eterm ret;
+ if (BIF_P != erts_code_purger)
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
- if (is_not_atom(BIF_ARG_1)) {
+ if (is_not_atom(BIF_ARG_1))
BIF_ERROR(BIF_P, BADARG);
- }
- if (!erts_try_seize_code_write_permission(BIF_P)) {
- ERTS_BIF_YIELD1(bif_export[BIF_erts_internal_purge_module_1],
- BIF_P, BIF_ARG_1);
- }
+ switch (BIF_ARG_2) {
- code_ix = erts_active_code_ix();
+ case am_prepare:
+ case am_prepare_on_load: {
+ /*
+ * Prepare for purge by marking all fun
+ * entries referring to the code to purge
+ * with "pending purge" markers.
+ */
+ ErtsCodeIndex code_ix;
+ Module* modp;
+ Eterm res;
- /*
- * Correct module?
- */
+ if (is_value(purge_state.module))
+ BIF_ERROR(BIF_P, BADARG);
- if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
- ERTS_BIF_PREP_RET(ret, am_false);
- }
- else {
- erts_rwlock_old_code(code_ix);
+ code_ix = erts_active_code_ix();
/*
- * Any code to purge?
+ * Correct module?
*/
- if (!modp->old.code_hdr) {
- ERTS_BIF_PREP_RET(ret, am_false);
- }
+ modp = erts_get_module(BIF_ARG_1, code_ix);
+ if (!modp)
+ res = am_false;
else {
/*
- * Unload any NIF library
+ * Any code to purge?
*/
- if (modp->old.nif != NULL) {
- /* ToDo: Do unload nif without blocking */
- erts_rwunlock_old_code(code_ix);
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
- is_blocking = 1;
- erts_rwlock_old_code(code_ix);
- erts_unload_nif(modp->old.nif);
- modp->old.nif = NULL;
+
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ erts_rwlock_old_code(code_ix);
+ } else {
+ erts_rlock_old_code(code_ix);
}
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ ASSERT(modp->on_load);
+ ASSERT(modp->on_load->code_hdr);
+ purge_state.saved_old = modp->old;
+ modp->old = *modp->on_load;
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) modp->on_load);
+ modp->on_load = 0;
+ }
+
+ if (!modp->old.code_hdr)
+ res = am_false;
+ else {
+ BeamInstr* code;
+ BeamInstr* end;
+ erts_smp_mtx_lock(&purge_state.mtx);
+ purge_state.module = BIF_ARG_1;
+ erts_smp_mtx_unlock(&purge_state.mtx);
+ res = am_true;
+ code = (BeamInstr*) modp->old.code_hdr;
+ end = (BeamInstr *)((char *)code + modp->old.code_length);
+ erts_fun_purge_prepare(code, end);
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ ASSERT(!ERTS_COPY_LITERAL_AREA());
+ ERTS_SET_COPY_LITERAL_AREA(modp->old.code_hdr->literal_area);
+#endif
+ }
+
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ erts_rwunlock_old_code(code_ix);
+ } else {
+ erts_runlock_old_code(code_ix);
+ }
+ }
+
+#ifndef ERTS_SMP
+ BIF_RET(res);
+#else
+ if (res != am_true)
+ BIF_RET(res);
+ else {
/*
- * Remove the old code.
+ * We'll be resumed when all schedulers are guaranteed
+ * to see the "pending purge" markers that we've made on
+ * all fun entries of the code that we are about to purge.
+ * Processes trying to call these funs will be suspended
+ * before calling the funs. That is we are guaranteed not
+ * to get any more direct references into the code while
+ * checking for such references...
*/
- ASSERT(erts_total_code_size >= modp->old.code_length);
- erts_total_code_size -= modp->old.code_length;
- code = (BeamInstr*) modp->old.code_hdr;
- end = (BeamInstr *)((char *)code + modp->old.code_length);
- erts_cleanup_funs_on_purge(code, end);
- beam_catches_delmod(modp->old.catches, code, modp->old.code_length,
- code_ix);
- decrement_refc(modp->old.code_hdr);
- if (modp->old.code_hdr->literals_start) {
- erts_free(ERTS_ALC_T_LITERAL, modp->old.code_hdr->literals_start);
- }
- erts_free(ERTS_ALC_T_CODE, (void *) code);
- modp->old.code_hdr = NULL;
- modp->old.code_length = 0;
- modp->old.catches = BEAM_CATCHES_NIL;
- erts_remove_from_ranges(code);
- ERTS_BIF_PREP_RET(ret, am_true);
+ erts_schedule_thr_prgr_later_op(resume_purger,
+ NULL,
+ &purger_lop_data);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
}
- erts_rwunlock_old_code(code_ix);
+#endif
}
- if (is_blocking) {
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+
+ case am_abort: {
+ /*
+ * Soft purge that detected direct references into the code
+ * we set out to purge. Abort the purge.
+ */
+
+ if (purge_state.module != BIF_ARG_1)
+ BIF_ERROR(BIF_P, BADARG);
+
+ erts_fun_purge_abort_prepare(purge_state.funs, purge_state.fe_ix);
+
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ ASSERT(ERTS_COPY_LITERAL_AREA());
+ ERTS_SET_COPY_LITERAL_AREA(NULL);
+#endif
+#ifndef ERTS_SMP
+ erts_fun_purge_abort_finalize(purge_state.funs, purge_state.fe_ix);
+ finalize_purge_operation(BIF_P, 0);
+ BIF_RET(am_false);
+#else
+ /*
+ * We need to restore the code addresses of the funs in
+ * two stages in order to ensure that we do not get any
+ * stale suspended processes due to the purge abort.
+ * Restore address pointer (erts_fun_purge_abort_prepare);
+ * wait for thread progress; clear pending purge address
+ * pointer (erts_fun_purge_abort_finalize), and then
+ * resume processes that got suspended
+ * (finalize_purge_operation).
+ */
+ erts_schedule_thr_prgr_later_op(finalize_purge_abort,
+ NULL,
+ &purger_lop_data);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_YIELD_RETURN(BIF_P, am_false);
+#endif
}
- erts_release_code_write_permission();
- return ret;
-}
-static void
-decrement_refc(BeamCodeHeader* code_hdr)
-{
- struct erl_off_heap_header* oh = code_hdr->literals_off_heap;
-
- while (oh) {
- Binary* bptr;
- ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG);
- bptr = ((ProcBin*)oh)->val;
- if (erts_refc_dectest(&bptr->refc, 0) == 0) {
- erts_bin_free(bptr);
+ case am_complete: {
+ ErtsCodeIndex code_ix;
+ BeamInstr* code;
+ Module* modp;
+ int is_blocking = 0;
+ Eterm ret;
+ ErtsLiteralArea *literals = NULL;
+
+
+ /*
+ * We have no direct references into the code.
+ * Complete to purge.
+ */
+
+ if (purge_state.module != BIF_ARG_1)
+ BIF_ERROR(BIF_P, BADARG);
+
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_purge_module_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ }
+
+ code_ix = erts_active_code_ix();
+
+ /*
+ * Correct module?
+ */
+
+ if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
+ ERTS_BIF_PREP_RET(ret, am_false);
+ }
+ else {
+
+ erts_rwlock_old_code(code_ix);
+
+ /*
+ * Any code to purge?
+ */
+ if (!modp->old.code_hdr) {
+ ERTS_BIF_PREP_RET(ret, am_false);
+ }
+ else {
+ /*
+ * Unload any NIF library
+ */
+ if (modp->old.nif != NULL) {
+ /* ToDo: Do unload nif without blocking */
+ erts_rwunlock_old_code(code_ix);
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+ is_blocking = 1;
+ erts_rwlock_old_code(code_ix);
+ erts_unload_nif(modp->old.nif);
+ modp->old.nif = NULL;
+ }
+
+ /*
+ * Remove the old code.
+ */
+ ASSERT(erts_total_code_size >= modp->old.code_length);
+ erts_total_code_size -= modp->old.code_length;
+ code = (BeamInstr*) modp->old.code_hdr;
+ erts_fun_purge_complete(purge_state.funs, purge_state.fe_ix);
+ beam_catches_delmod(modp->old.catches, code, modp->old.code_length,
+ code_ix);
+ literals = modp->old.code_hdr->literal_area;
+ modp->old.code_hdr->literal_area = NULL;
+ erts_free(ERTS_ALC_T_CODE, (void *) code);
+ modp->old.code_hdr = NULL;
+ modp->old.code_length = 0;
+ modp->old.catches = BEAM_CATCHES_NIL;
+ erts_remove_from_ranges(code);
+
+ ERTS_BIF_PREP_RET(ret, am_true);
+ }
+
+ if (purge_state.saved_old.code_hdr) {
+ modp->old = purge_state.saved_old;
+ purge_state.saved_old.code_hdr = 0;
+ }
+ erts_rwunlock_old_code(code_ix);
+ }
+ if (is_blocking) {
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ }
+
+ erts_release_code_write_permission();
+
+ finalize_purge_operation(BIF_P, ret == am_true);
+
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+
+ ASSERT(ERTS_COPY_LITERAL_AREA() == literals);
+ ERTS_SET_COPY_LITERAL_AREA(NULL);
+ erts_release_literal_area(literals);
+
+#else /* ERTS_NEW_PURGE_STRATEGY */
+
+ if (literals) {
+ ErtsLiteralAreaRef *ref;
+ ref = erts_alloc(ERTS_ALC_T_LITERAL_REF,
+ sizeof(ErtsLiteralAreaRef));
+ ref->literal_area = literals;
+ ref->next = NULL;
+ erts_smp_mtx_lock(&release_literal_areas.mtx);
+ if (release_literal_areas.last) {
+ release_literal_areas.last->next = ref;
+ release_literal_areas.last = ref;
+ }
+ else {
+ release_literal_areas.first = ref;
+ release_literal_areas.last = ref;
+ }
+ erts_smp_mtx_unlock(&release_literal_areas.mtx);
+ erts_queue_message(erts_literal_area_collector,
+ 0,
+ erts_alloc_message(0, NULL),
+ am_copy_literals,
+ BIF_P->common.id);
}
- oh = oh->next;
+
+#endif /* ERTS_NEW_PURGE_STRATEGY */
+
+ return ret;
+ }
+
+ default:
+ BIF_ERROR(BIF_P, BADARG);
+
}
}
@@ -1341,9 +2058,9 @@ delete_code(Module* modp)
{
ErtsCodeIndex code_ix = erts_staging_code_ix();
Eterm module = make_atom(modp->module);
- int i;
+ int i, num_exps = export_list_size(code_ix);
- for (i = 0; i < export_list_size(code_ix); i++) {
+ for (i = 0; i < num_exps; i++) {
Export *ep = export_list(i, code_ix);
if (ep != NULL && (ep->code[0] == module)) {
if (ep->addressv[code_ix] == ep->code+3) {
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index 8489897d3a..6d723a4b92 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2017. 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.
@@ -74,6 +74,9 @@ extern BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */
erts_smp_atomic32_t erts_active_bp_index;
erts_smp_atomic32_t erts_staging_bp_index;
+#ifdef ERTS_DIRTY_SCHEDULERS
+erts_smp_mtx_t erts_dirty_bp_ix_mtx;
+#endif
/*
* Inlined helpers
@@ -85,6 +88,31 @@ get_mtime(Process *c_p)
return erts_get_monotonic_time(erts_proc_sched_data(c_p));
}
+static ERTS_INLINE Uint32
+acquire_bp_sched_ix(Process *c_p)
+{
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
+ ASSERT(esdp);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ erts_smp_mtx_lock(&erts_dirty_bp_ix_mtx);
+ return (Uint32) erts_no_schedulers;
+ }
+#endif
+ return (Uint32) esdp->no - 1;
+}
+
+static ERTS_INLINE void
+release_bp_sched_ix(Uint32 ix)
+{
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (ix == (Uint32) erts_no_schedulers)
+ erts_smp_mtx_unlock(&erts_dirty_bp_ix_mtx);
+#endif
+}
+
+
+
/* *************************************************************************
** Local prototypes
*/
@@ -135,6 +163,9 @@ void
erts_bp_init(void) {
erts_smp_atomic32_init_nob(&erts_active_bp_index, 0);
erts_smp_atomic32_init_nob(&erts_staging_bp_index, 1);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_smp_mtx_init(&erts_dirty_bp_ix_mtx, "dirty_break_point_index");
+#endif
}
@@ -347,17 +378,17 @@ consolidate_bp_data(Module* modp, BeamInstr* pc, int local)
}
if (flags & ERTS_BPF_META_TRACE) {
dst->meta_tracer = src->meta_tracer;
- erts_refc_inc(&dst->meta_tracer->refc, 1);
+ erts_smp_refc_inc(&dst->meta_tracer->refc, 1);
dst->meta_ms = src->meta_ms;
MatchSetRef(dst->meta_ms);
}
if (flags & ERTS_BPF_COUNT) {
dst->count = src->count;
- erts_refc_inc(&dst->count->refc, 1);
+ erts_smp_refc_inc(&dst->count->refc, 1);
}
if (flags & ERTS_BPF_TIME_TRACE) {
dst->time = src->time;
- erts_refc_inc(&dst->time->refc, 1);
+ erts_smp_refc_inc(&dst->time->refc, 1);
ASSERT(dst->time->hash);
}
}
@@ -858,7 +889,8 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
if (flags & MATCH_SET_RX_TRACE) {
erts_trace_return(p, ep->code, result, &ERTS_TRACER(p));
}
- if (flags & MATCH_SET_RETURN_TO_TRACE) {
+ if (flags & MATCH_SET_RETURN_TO_TRACE &&
+ IS_TRACED_FL(p, F_TRACE_RETURN_TO)) {
/* can only happen if(local)*/
if (applying) {
/* Apply of BIF, cp is in calling function */
@@ -972,6 +1004,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
bp_data_time_item_t sitem, *item = NULL;
bp_time_hash_t *h = NULL;
BpDataTime *pbdt = NULL;
+ Uint32 six = acquire_bp_sched_ix(c_p);
ASSERT(c_p);
ASSERT(erts_smp_atomic32_read_acqb(&c_p->state) & (ERTS_PSFLG_RUNNING
@@ -979,7 +1012,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
/* get previous timestamp and breakpoint
* from the process psd */
-
+
pbt = ERTS_PROC_GET_CALL_TIME(c_p);
time = get_mtime(c_p);
@@ -1005,7 +1038,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
/* if null then the breakpoint was removed */
if (pbdt) {
- h = &(pbdt->hash[bp_sched2ix_proc(c_p)]);
+ h = &(pbdt->hash[six]);
ASSERT(h);
ASSERT(h->item);
@@ -1026,7 +1059,7 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
/* this breakpoint */
ASSERT(bdt);
- h = &(bdt->hash[bp_sched2ix_proc(c_p)]);
+ h = &(bdt->hash[six]);
ASSERT(h);
ASSERT(h->item);
@@ -1040,6 +1073,8 @@ erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
pbt->pc = I;
pbt->time = time;
+
+ release_bp_sched_ix(six);
}
void
@@ -1050,6 +1085,7 @@ erts_trace_time_return(Process *p, BeamInstr *pc)
bp_data_time_item_t sitem, *item = NULL;
bp_time_hash_t *h = NULL;
BpDataTime *pbdt = NULL;
+ Uint32 six = acquire_bp_sched_ix(p);
ASSERT(p);
ASSERT(erts_smp_atomic32_read_acqb(&p->state) & (ERTS_PSFLG_RUNNING
@@ -1070,6 +1106,7 @@ erts_trace_time_return(Process *p, BeamInstr *pc)
*/
if (pbt) {
+
/* might have been removed due to
* trace_pattern(false)
*/
@@ -1084,7 +1121,8 @@ erts_trace_time_return(Process *p, BeamInstr *pc)
/* beware, the trace_pattern might have been removed */
if (pbdt) {
- h = &(pbdt->hash[bp_sched2ix_proc(p)]);
+
+ h = &(pbdt->hash[six]);
ASSERT(h);
ASSERT(h->item);
@@ -1095,11 +1133,15 @@ erts_trace_time_return(Process *p, BeamInstr *pc)
} else {
BP_TIME_ADD(item, &sitem);
}
+
}
pbt->pc = pc;
pbt->time = time;
+
}
+
+ release_bp_sched_ix(six);
}
int
@@ -1352,6 +1394,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
bp_data_time_item_t sitem, *item = NULL;
bp_time_hash_t *h = NULL;
BpDataTime *pbdt = NULL;
+ Uint32 six = acquire_bp_sched_ix(p);
ASSERT(p);
@@ -1374,7 +1417,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
sitem.pid = p->common.id;
sitem.count = 0;
- h = &(pbdt->hash[bp_sched2ix_proc(p)]);
+ h = &(pbdt->hash[six]);
ASSERT(h);
ASSERT(h->item);
@@ -1400,6 +1443,8 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
break;
}
} /* pbt */
+
+ release_bp_sched_ix(six);
}
/* *************************************************************************
@@ -1497,7 +1542,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
MatchSetRef(match_spec);
bp->meta_ms = match_spec;
bmt = Alloc(sizeof(BpMetaTracer));
- erts_refc_init(&bmt->refc, 1);
+ erts_smp_refc_init(&bmt->refc, 1);
erts_tracer_update(&meta_tracer, tracer); /* copy tracer */
erts_smp_atomic_init_nob(&bmt->tracer, (erts_aint_t)meta_tracer);
bp->meta_tracer = bmt;
@@ -1506,7 +1551,7 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
ASSERT((bp->flags & ERTS_BPF_COUNT) == 0);
bcp = Alloc(sizeof(BpCount));
- erts_refc_init(&bcp->refc, 1);
+ erts_smp_refc_init(&bcp->refc, 1);
erts_smp_atomic_init_nob(&bcp->acount, 0);
bp->count = bcp;
} else if (break_flags & ERTS_BPF_TIME_TRACE) {
@@ -1515,8 +1560,12 @@ set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
ASSERT((bp->flags & ERTS_BPF_TIME_TRACE) == 0);
bdt = Alloc(sizeof(BpDataTime));
- erts_refc_init(&bdt->refc, 1);
+ erts_smp_refc_init(&bdt->refc, 1);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ bdt->n = erts_no_schedulers + 1;
+#else
bdt->n = erts_no_schedulers;
+#endif
bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n));
for (i = 0; i < bdt->n; i++) {
bp_hash_init(&(bdt->hash[i]), 32);
@@ -1582,7 +1631,7 @@ clear_function_break(BeamInstr *pc, Uint break_flags)
static void
bp_meta_unref(BpMetaTracer* bmt)
{
- if (erts_refc_dectest(&bmt->refc, 0) <= 0) {
+ if (erts_smp_refc_dectest(&bmt->refc, 0) <= 0) {
ErtsTracer trc = erts_smp_atomic_read_nob(&bmt->tracer);
ERTS_TRACER_CLEAR(&trc);
Free(bmt);
@@ -1592,7 +1641,7 @@ bp_meta_unref(BpMetaTracer* bmt)
static void
bp_count_unref(BpCount* bcp)
{
- if (erts_refc_dectest(&bcp->refc, 0) <= 0) {
+ if (erts_smp_refc_dectest(&bcp->refc, 0) <= 0) {
Free(bcp);
}
}
@@ -1600,7 +1649,7 @@ bp_count_unref(BpCount* bcp)
static void
bp_time_unref(BpDataTime* bdt)
{
- if (erts_refc_dectest(&bdt->refc, 0) <= 0) {
+ if (erts_smp_refc_dectest(&bdt->refc, 0) <= 0) {
Uint i = 0;
Uint j = 0;
Process *h_p = NULL;
diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h
index 541af77211..7372b9b258 100644
--- a/erts/emulator/beam/beam_bp.h
+++ b/erts/emulator/beam/beam_bp.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2017. 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.
@@ -41,7 +41,7 @@ typedef struct {
typedef struct bp_data_time { /* Call time */
Uint n;
bp_time_hash_t *hash;
- erts_refc_t refc;
+ erts_smp_refc_t refc;
} BpDataTime;
typedef struct {
@@ -51,12 +51,12 @@ typedef struct {
typedef struct {
erts_smp_atomic_t acount;
- erts_refc_t refc;
+ erts_smp_refc_t refc;
} BpCount;
typedef struct {
erts_smp_atomic_t tracer;
- erts_refc_t refc;
+ erts_smp_refc_t refc;
} BpMetaTracer;
typedef struct generic_bp_data {
@@ -79,10 +79,8 @@ typedef struct generic_bp {
#define ERTS_BP_CALL_TIME_SCHEDULE_OUT (1)
#define ERTS_BP_CALL_TIME_SCHEDULE_EXITING (2)
-#ifdef ERTS_SMP
-#define bp_sched2ix_proc(p) (erts_proc_sched_data(p)->no - 1)
-#else
-#define bp_sched2ix_proc(p) (0)
+#ifdef ERTS_DIRTY_SCHEDULERS
+extern erts_smp_mtx_t erts_dirty_bp_ix_mtx;
#endif
enum erts_break_op{
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index a4ad3e7886..21d336049f 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -50,7 +50,7 @@
void dbg_bt(Process* p, Eterm* sp);
void dbg_where(BeamInstr* addr, Eterm x0, Eterm* reg);
-static int print_op(int to, void *to_arg, int op, int size, BeamInstr* addr);
+static int print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr);
BIF_RETTYPE
erts_debug_same_2(BIF_ALIST_2)
@@ -377,7 +377,7 @@ dbg_where(BeamInstr* addr, Eterm x0, Eterm* reg)
}
static int
-print_op(int to, void *to_arg, int op, int size, BeamInstr* addr)
+print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
{
int i;
BeamInstr tag;
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 4716460a6b..1ad13c32e3 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -1047,9 +1047,11 @@ static BeamInstr* handle_error(Process* c_p, BeamInstr* pc,
Eterm* reg, BifFunction bf) NOINLINE;
static BeamInstr* call_error_handler(Process* p, BeamInstr* ip,
Eterm* reg, Eterm func) NOINLINE;
-static BeamInstr* fixed_apply(Process* p, Eterm* reg, Uint arity) NOINLINE;
+static BeamInstr* fixed_apply(Process* p, Eterm* reg, Uint arity,
+ BeamInstr *I, Uint offs) NOINLINE;
static BeamInstr* apply(Process* p, Eterm module, Eterm function,
- Eterm args, Eterm* reg) NOINLINE;
+ Eterm args, Eterm* reg,
+ BeamInstr *I, Uint offs) NOINLINE;
static BeamInstr* call_fun(Process* p, int arity,
Eterm* reg, Eterm args) NOINLINE;
static BeamInstr* apply_fun(Process* p, Eterm fun,
@@ -1960,6 +1962,8 @@ void process_main(void)
ErtsMessage* msgp;
PROCESS_MAIN_CHK_LOCKS(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
+
PreFetch(0, next);
msgp = PEEK_MESSAGE(c_p);
@@ -2047,6 +2051,7 @@ void process_main(void)
}
ERTS_DBG_CHK_REDS(c_p, FCALLS);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2161,7 +2166,10 @@ void process_main(void)
c_p->i = (BeamInstr *) Arg(0); /* L1 */
SWAPOUT;
c_p->arity = 0;
- erts_smp_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE);
+
+ if (!ERTS_PTMR_IS_TIMED_OUT(c_p))
+ erts_smp_atomic32_read_band_relb(&c_p->state,
+ ~ERTS_PSFLG_ACTIVE);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
c_p->current = NULL;
@@ -2580,7 +2588,9 @@ do { \
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2611,7 +2621,9 @@ do { \
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2641,7 +2653,9 @@ do { \
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, live);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2682,7 +2696,9 @@ do { \
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, live);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2725,7 +2741,9 @@ do { \
SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, live);
+ ERTS_CHK_MBUF_SZ(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2763,7 +2781,9 @@ do { \
c_p->fcalls = FCALLS;
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2790,7 +2810,9 @@ do { \
bf = (BifFunction) Arg(0);
PROCESS_MAIN_CHK_LOCKS(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, tmp_reg);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -2826,13 +2848,7 @@ do { \
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);
- } else {
- ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF);
- }
- }
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(Arg(0)), GET_BIF_ADDRESS(Arg(0)));
bf = GET_BIF_ADDRESS(Arg(0));
@@ -2846,7 +2862,9 @@ do { \
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
live_hf_end = c_p->mbuf;
+ ERTS_CHK_MBUF_SZ(c_p);
result = (*bf)(c_p, reg, I);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
@@ -3032,6 +3050,7 @@ do { \
if (i == 0) {
StoreBifResult(4, Op1);
}
+ ires = big_size(Op1);
goto big_shift;
}
} else if (is_big(Op2)) {
@@ -3047,7 +3066,6 @@ do { \
OpCase(i_bsl_jIssd):
GetArg2(2, Op1, Op2);
-
do_bsl:
if (is_small(Op2)) {
i = signed_val(Op2);
@@ -3073,16 +3091,12 @@ do { \
StoreBifResult(4, Op1);
}
}
- Op1 = small_to_big(ires, tmp_big);
-#ifdef TAG_LITERAL_PTR
- Op1 |= TAG_LITERAL_PTR;
-#endif
+ ires = 1; /* big_size(small_to_big(Op1)) */
big_shift:
if (i > 0) { /* Left shift. */
- ires = big_size(Op1) + (i / D_EXP);
+ ires += (i / D_EXP);
} else { /* Right shift. */
- ires = big_size(Op1);
if (ires <= (-i / D_EXP))
ires = 3; /* ??? */
else
@@ -3101,6 +3115,9 @@ do { \
goto lb_Cl_error;
}
TestHeapPreserve(ires+1, Arg(1), Op1);
+ if (is_small(Op1)) {
+ Op1 = small_to_big(signed_val(Op1), tmp_big);
+ }
bigp = HTOP;
Op1 = big_lshift(Op1, i, bigp);
if (is_big(Op1)) {
@@ -3123,6 +3140,7 @@ do { \
if (i == 0) {
StoreBifResult(4, Op1);
}
+ ires = big_size(Op1);
goto big_shift;
}
} else if (is_big(Op2)) {
@@ -3178,7 +3196,7 @@ do { \
OpCase(i_apply): {
BeamInstr *next;
HEAVY_SWAPOUT;
- next = apply(c_p, r(0), x(1), x(2), reg);
+ next = apply(c_p, r(0), x(1), x(2), reg, NULL, 0);
HEAVY_SWAPIN;
if (next != NULL) {
SET_CP(c_p, I+1);
@@ -3192,7 +3210,7 @@ do { \
OpCase(i_apply_last_P): {
BeamInstr *next;
HEAVY_SWAPOUT;
- next = apply(c_p, r(0), x(1), x(2), reg);
+ next = apply(c_p, r(0), x(1), x(2), reg, I, Arg(0));
HEAVY_SWAPIN;
if (next != NULL) {
SET_CP(c_p, (BeamInstr *) E[0]);
@@ -3207,7 +3225,7 @@ do { \
OpCase(i_apply_only): {
BeamInstr *next;
HEAVY_SWAPOUT;
- next = apply(c_p, r(0), x(1), x(2), reg);
+ next = apply(c_p, r(0), x(1), x(2), reg, I, 0);
HEAVY_SWAPIN;
if (next != NULL) {
SET_I(next);
@@ -3221,7 +3239,7 @@ do { \
BeamInstr *next;
HEAVY_SWAPOUT;
- next = fixed_apply(c_p, reg, Arg(0));
+ next = fixed_apply(c_p, reg, Arg(0), NULL, 0);
HEAVY_SWAPIN;
if (next != NULL) {
SET_CP(c_p, I+2);
@@ -3236,7 +3254,7 @@ do { \
BeamInstr *next;
HEAVY_SWAPOUT;
- next = fixed_apply(c_p, reg, Arg(0));
+ next = fixed_apply(c_p, reg, Arg(0), I, Arg(1));
HEAVY_SWAPIN;
if (next != NULL) {
SET_CP(c_p, (BeamInstr *) E[0]);
@@ -3534,18 +3552,11 @@ 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]);
c_p->current = I-3; /* current and vbf set to please handle_error */
- SWAPOUT;
- c_p->fcalls = FCALLS - 1;
+ HEAVY_SWAPOUT;
PROCESS_MAIN_CHK_LOCKS(c_p);
bif_nif_arity = I[-1];
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
@@ -3559,11 +3570,13 @@ do { \
ASSERT(c_p->scheduler_data);
#endif
live_hf_end = c_p->mbuf;
+ ERTS_CHK_MBUF_SZ(c_p);
erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL);
nif_bif_result = (*fp)(&env, bif_nif_arity, reg);
if (env.exception_thrown)
nif_bif_result = THE_NON_VALUE;
erts_post_nif(&env);
+ ERTS_CHK_MBUF_SZ(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
@@ -3593,13 +3606,7 @@ do { \
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);
- } else {
- ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF);
- }
- }
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X((Eterm)I[-3], (BifFunction)Arg(0));
c_p->current = I-3; /* In case we apply process_info/1,2 or load_nif/1 */
c_p->i = I; /* In case we apply check_process_code/2. */
@@ -3621,7 +3628,9 @@ do { \
Eterm (*bf)(Process*, Eterm*, BeamInstr*) = vbf;
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
live_hf_end = c_p->mbuf;
+ ERTS_CHK_MBUF_SZ(c_p);
nif_bif_result = (*bf)(c_p, reg, I);
+ ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) ||
is_non_value(nif_bif_result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
@@ -5261,19 +5270,14 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
else {
/*
* Dirty CPU scheduler:
- * Currently two reductions consumed per
- * micro second spent in the dirty NIF.
+ * Reductions based on time consumed by
+ * the dirty NIF.
*/
- ErtsMonotonicTime time;
- time = erts_get_monotonic_time(esdp);
- time -= start_time;
- time = ERTS_MONOTONIC_TO_USEC(time);
- time *= (CONTEXT_REDS-1)/1000 + 1;
- ASSERT(time >= 0);
- if (time == 0)
- time = 1; /* At least one reduction */
- time += esdp->virtual_reds;
- reds_used = time > INT_MAX ? INT_MAX : (int) time;
+ Sint64 treds;
+ treds = erts_time2reds(start_time,
+ erts_get_monotonic_time(esdp));
+ treds += esdp->virtual_reds;
+ reds_used = treds > INT_MAX ? INT_MAX : (int) treds;
}
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -5316,7 +5320,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
I = c_p->i;
- ASSERT(BeamOp(op_call_nif) == (BeamInstr *) *I);
+ ASSERT(em_call_nif == (BeamInstr *) *I);
/*
* Set fcalls even though we ignore it, so we don't
@@ -5391,7 +5395,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
ASSERT(!c_p->scheduler_data);
erts_pre_dirty_nif(esdp, &env, c_p,
- (struct erl_module_nif*)I[2], NULL);
+ (struct erl_module_nif*)I[2]);
#ifdef DEBUG
result =
@@ -5400,7 +5404,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
#endif
(*fp)(&env, arity, reg);
- erts_post_nif(&env);
+ erts_post_dirty_nif(&env);
ASSERT(!is_value(result));
ASSERT(c_p->freason == TRAP);
@@ -5835,12 +5839,13 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, BifFunction bf,
s->depth = 0;
/*
- * If the failure was in a BIF other than 'error', 'exit' or
- * 'throw', find the bif-table index and save the argument
- * registers by consing up an arglist.
+ * If the failure was in a BIF other than 'error/1', 'error/2',
+ * 'exit/1' or 'throw/1', find the bif-table index and save the
+ * argument registers by consing up an arglist.
*/
- if (bf != NULL && bf != error_1 && bf != error_2 &&
- bf != exit_1 && bf != throw_1) {
+ if (bf != NULL && bf != error_1 && bf != error_2 && bf != exit_1
+ && bf != throw_1 && bf != wrap_error_1 && bf != wrap_error_2
+ && bf != wrap_exit_1 && bf != wrap_throw_1) {
int i;
int a = 0;
for (i = 0; i < BIF_SIZE; i++) {
@@ -5936,12 +5941,30 @@ erts_save_stacktrace(Process* p, struct StackTrace* s, int depth)
p->cp) {
/* Cannot follow cp here - code may be unloaded */
BeamInstr *cpp = p->cp;
+ int trace_cp;
if (cpp == beam_exception_trace || cpp == beam_return_trace) {
/* Skip return_trace parameters */
ptr += 2;
+ trace_cp = 1;
} else if (cpp == beam_return_to_trace) {
/* Skip return_to_trace parameters */
ptr += 1;
+ trace_cp = 1;
+ }
+ else {
+ trace_cp = 0;
+ }
+ if (trace_cp && s->pc == cpp) {
+ /*
+ * If process 'cp' points to a return/exception trace
+ * instruction and 'cp' has been saved as 'pc' in
+ * stacktrace, we need to update 'pc' in stacktrace
+ * with the actual 'cp' located on the top of the
+ * stack; otherwise, we will lose the top stackframe
+ * when building the stack trace.
+ */
+ ASSERT(is_CP(p->stop[0]));
+ s->pc = cp_val(p->stop[0]);
}
}
while (ptr < STACK_START(p) && depth > 0) {
@@ -6061,12 +6084,15 @@ build_stacktrace(Process* c_p, Eterm exc) {
erts_set_current_function(&fi, s->current);
}
+ depth = s->depth;
/*
- * If fi.current is still NULL, default to the initial function
+ * If fi.current is still NULL, and we have no
+ * stack at all, default to the initial function
* (e.g. spawn_link(erlang, abs, [1])).
*/
if (fi.current == NULL) {
- erts_set_current_function(&fi, c_p->u.initial);
+ if (depth <= 0)
+ erts_set_current_function(&fi, c_p->u.initial);
args = am_true; /* Just in case */
} else {
args = get_args_from_exc(exc);
@@ -6076,10 +6102,9 @@ build_stacktrace(Process* c_p, Eterm exc) {
* Look up all saved continuation pointers and calculate
* needed heap space.
*/
- depth = s->depth;
stk = stkp = (FunctionInfo *) erts_alloc(ERTS_ALC_T_TMP,
depth*sizeof(FunctionInfo));
- heap_size = fi.needed + 2;
+ heap_size = fi.current ? fi.needed + 2 : 0;
for (i = 0; i < depth; i++) {
erts_lookup_function_info(stkp, s->trace[i], 1);
if (stkp->current) {
@@ -6098,8 +6123,10 @@ build_stacktrace(Process* c_p, Eterm exc) {
res = CONS(hp, mfa, res);
hp += 2;
}
- hp = erts_build_mfa_item(&fi, hp, args, &mfa);
- res = CONS(hp, mfa, res);
+ if (fi.current) {
+ hp = erts_build_mfa_item(&fi, hp, args, &mfa);
+ res = CONS(hp, mfa, res);
+ }
erts_free(ERTS_ALC_T_TMP, (void *) stk);
return res;
@@ -6196,8 +6223,107 @@ apply_setup_error_handler(Process* p, Eterm module, Eterm function, Uint arity,
return ep;
}
+static ERTS_INLINE void
+apply_bif_error_adjustment(Process *p, Export *ep,
+ Eterm *reg, Uint arity,
+ BeamInstr *I, Uint stack_offset)
+{
+ /*
+ * I is only set when the apply is a tail call, i.e.,
+ * from the instructions i_apply_only, i_apply_last_P,
+ * and apply_last_IP.
+ */
+ if (I
+ && ep->code[3] == (BeamInstr) em_apply_bif
+ && (ep == bif_export[BIF_error_1]
+ || ep == bif_export[BIF_error_2]
+ || ep == bif_export[BIF_exit_1]
+ || ep == bif_export[BIF_throw_1])) {
+ /*
+ * We are about to tail apply one of the BIFs
+ * erlang:error/1, erlang:error/2, erlang:exit/1,
+ * or erlang:throw/1. Error handling of these BIFs is
+ * special!
+ *
+ * We need 'p->cp' to point into the calling
+ * function when handling the error after the BIF has
+ * been applied. This in order to get the topmost
+ * stackframe correct. Without the following adjustment,
+ * 'p->cp' will point into the function that called
+ * current function when handling the error. We add a
+ * dummy stackframe in order to achive this.
+ *
+ * Note that these BIFs unconditionally will cause
+ * an exception to be raised. That is, our modifications
+ * of 'p->cp' as well as the stack will be corrected by
+ * the error handling code.
+ *
+ * If we find an exception/return-to trace continuation
+ * pointer as the topmost continuation pointer, we do not
+ * need to do anything since the information already will
+ * be available for generation of the stacktrace.
+ */
+ int apply_only = stack_offset == 0;
+ BeamInstr *cpp;
+
+ if (apply_only) {
+ ASSERT(p->cp != NULL);
+ cpp = p->cp;
+ }
+ else {
+ ASSERT(is_CP(p->stop[0]));
+ cpp = cp_val(p->stop[0]);
+ }
+
+ if (cpp != beam_exception_trace
+ && cpp != beam_return_trace
+ && cpp != beam_return_to_trace) {
+ Uint need = stack_offset /* bytes */ / sizeof(Eterm);
+ if (need == 0)
+ need = 1; /* i_apply_only */
+ if (p->stop - p->htop < need)
+ erts_garbage_collect(p, (int) need, reg, arity+1);
+ p->stop -= need;
+
+ if (apply_only) {
+ /*
+ * Called from the i_apply_only instruction.
+ *
+ * 'p->cp' contains continuation pointer pointing
+ * into the function that called current function.
+ * We push that continuation pointer onto the stack,
+ * and set 'p->cp' to point into current function.
+ */
+
+ p->stop[0] = make_cp(p->cp);
+ p->cp = I;
+ }
+ else {
+ /*
+ * Called from an i_apply_last_p, or apply_last_IP,
+ * instruction.
+ *
+ * Calling instruction will after we return read
+ * a continuation pointer from the stack and write
+ * it to 'p->cp', and then remove the topmost
+ * stackframe of size 'stack_offset'.
+ *
+ * We have sized the dummy-stackframe so that it
+ * will be removed by the instruction we currently
+ * are executing, and leave the stackframe that
+ * normally would have been removed intact.
+ *
+ */
+ p->stop[0] = make_cp(I);
+ }
+ }
+ }
+}
+
static BeamInstr*
-apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
+apply(
+Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg,
+BeamInstr *I, Uint stack_offset)
{
int arity;
Export* ep;
@@ -6222,21 +6348,54 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
return 0;
}
- /* The module argument may be either an atom or an abstract module
- * (currently implemented using tuples, but this might change).
- */
- this = THE_NON_VALUE;
- if (is_not_atom(module)) {
- Eterm* tp;
+ while (1) {
+ Eterm m, f, a;
+ /* The module argument may be either an atom or an abstract module
+ * (currently implemented using tuples, but this might change).
+ */
+ this = THE_NON_VALUE;
+ if (is_not_atom(module)) {
+ Eterm* tp;
+
+ if (is_not_tuple(module)) goto error;
+ tp = tuple_val(module);
+ if (arityval(tp[0]) < 1) goto error;
+ this = module;
+ module = tp[1];
+ if (is_not_atom(module)) goto error;
+ }
- if (is_not_tuple(module)) goto error;
- tp = tuple_val(module);
- if (arityval(tp[0]) < 1) goto error;
- this = module;
- module = tp[1];
- if (is_not_atom(module)) goto error;
+ if (module != am_erlang || function != am_apply)
+ break;
+
+ /* Adjust for multiple apply of apply/3... */
+
+ a = args;
+ if (is_list(a)) {
+ Eterm *consp = list_val(a);
+ m = CAR(consp);
+ a = CDR(consp);
+ if (is_list(a)) {
+ consp = list_val(a);
+ f = CAR(consp);
+ a = CDR(consp);
+ if (is_list(a)) {
+ consp = list_val(a);
+ a = CAR(consp);
+ if (is_nil(CDR(consp))) {
+ /* erlang:apply/3 */
+ module = m;
+ function = f;
+ args = a;
+ if (is_not_atom(f))
+ goto error;
+ continue;
+ }
+ }
+ }
+ }
+ break; /* != erlang:apply/3 */
}
-
/*
* Walk down the 3rd parameter of apply (the argument list) and copy
* the parameters to the x registers (reg[]). If the module argument
@@ -6274,12 +6433,14 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
} else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
save_calls(p, ep);
}
+ apply_bif_error_adjustment(p, ep, reg, arity, I, stack_offset);
DTRACE_GLOBAL_CALL_FROM_EXPORT(p, ep);
return ep->addressv[erts_active_code_ix()];
}
static BeamInstr*
-fixed_apply(Process* p, Eterm* reg, Uint arity)
+fixed_apply(Process* p, Eterm* reg, Uint arity,
+ BeamInstr *I, Uint stack_offset)
{
Export* ep;
Eterm module;
@@ -6309,6 +6470,10 @@ fixed_apply(Process* p, Eterm* reg, Uint arity)
if (is_not_atom(module)) goto error;
++arity;
}
+
+ /* Handle apply of apply/3... */
+ if (module == am_erlang && function == am_apply && arity == 3)
+ return apply(p, reg[0], reg[1], reg[2], reg, I, stack_offset);
/*
* Get the index into the export table, or failing that the export
@@ -6323,6 +6488,7 @@ fixed_apply(Process* p, Eterm* reg, Uint arity)
} else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
save_calls(p, ep);
}
+ apply_bif_error_adjustment(p, ep, reg, arity, I, stack_offset);
DTRACE_GLOBAL_CALL_FROM_EXPORT(p, ep);
return ep->addressv[erts_active_code_ix()];
}
@@ -6535,34 +6701,49 @@ call_fun(Process* p, /* Current process. */
* representation (the module has never been loaded),
* or the module defining the fun has been unloaded.
*/
+
module = fe->module;
- if ((modp = erts_get_module(module, code_ix)) != NULL
- && modp->curr.code_hdr != NULL) {
+
+ ERTS_SMP_READ_MEMORY_BARRIER;
+ if (fe->pend_purge_address) {
/*
- * There is a module loaded, but obviously the fun is not
- * defined in it. We must not call the error_handler
- * (or we will get into an infinite loop).
+ * The system is currently trying to purge the
+ * module containing this fun. Suspend the process
+ * and let it try again when the purge operation is
+ * done (may succeed or not).
*/
- goto badfun;
+ ep = erts_suspend_process_on_pending_purge_lambda(p, fe);
+ ASSERT(ep);
}
+ else {
+ if ((modp = erts_get_module(module, code_ix)) != NULL
+ && modp->curr.code_hdr != NULL) {
+ /*
+ * There is a module loaded, but obviously the fun is not
+ * defined in it. We must not call the error_handler
+ * (or we will get into an infinite loop).
+ */
+ goto badfun;
+ }
- /*
- * No current code for this module. Call the error_handler module
- * to attempt loading the module.
- */
+ /*
+ * No current code for this module. Call the error_handler module
+ * to attempt loading the module.
+ */
- ep = erts_find_function(erts_proc_get_error_handler(p),
- am_undefined_lambda, 3, code_ix);
- if (ep == NULL) { /* No error handler */
- p->current = NULL;
- p->freason = EXC_UNDEF;
- return NULL;
+ ep = erts_find_function(erts_proc_get_error_handler(p),
+ am_undefined_lambda, 3, code_ix);
+ if (ep == NULL) { /* No error handler */
+ p->current = NULL;
+ p->freason = EXC_UNDEF;
+ return NULL;
+ }
}
reg[0] = module;
reg[1] = fun;
reg[2] = args;
reg[3] = NIL;
- return ep->addressv[erts_active_code_ix()];
+ return ep->addressv[code_ix];
}
}
} else if (is_export_header(hdr)) {
@@ -6654,7 +6835,7 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free)
p->htop = hp + needed;
funp = (ErlFunThing *) hp;
hp = funp->env;
- erts_refc_inc(&fe->refc, 2);
+ erts_smp_refc_inc(&fe->refc, 2);
funp->thing_word = HEADER_FUN;
funp->next = MSO(p).first;
MSO(p).first = (struct erl_off_heap_header*) funp;
@@ -7017,7 +7198,11 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I)
/* The expensive case, need to build a hashmap */
if (n > MAP_SMALL_MAP_LIMIT) {
- res = erts_hashmap_from_ks_and_vs(p,flatmap_get_keys(mp),flatmap_get_values(mp),n);
+ ErtsHeapFactory factory;
+ erts_factory_proc_init(&factory, p);
+ res = erts_hashmap_from_ks_and_vs(&factory,flatmap_get_keys(mp),
+ flatmap_get_values(mp),n);
+ erts_factory_close(&factory);
}
return res;
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 0c2743beb2..2bad3ab4c5 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -105,7 +105,7 @@ typedef struct {
*/
typedef struct genop {
- int op; /* Opcode. */
+ unsigned int op; /* Opcode. */
int arity; /* Number of arguments. */
GenOpArg def_args[MAX_OPARGS]; /* Default buffer for arguments. */
GenOpArg* a; /* The arguments. */
@@ -283,8 +283,8 @@ typedef struct LoaderState {
byte* code_start; /* Start of code file. */
unsigned code_size; /* Size of code file. */
int specific_op; /* Specific opcode (-1 if not found). */
- int num_functions; /* Number of functions in module. */
- int num_labels; /* Number of labels. */
+ unsigned int num_functions; /* Number of functions in module. */
+ unsigned int num_labels; /* Number of labels. */
BeamCodeHeader* hdr; /* Loaded code header */
BeamInstr* codev; /* Loaded code buffer */
int codev_size; /* Size of code buffer in words. */
@@ -303,13 +303,13 @@ typedef struct LoaderState {
* Atom table.
*/
- int num_atoms; /* Number of atoms in atom table. */
+ unsigned int num_atoms; /* Number of atoms in atom table. */
Eterm* atom; /* Atom table. */
- int num_exps; /* Number of exports. */
+ unsigned int num_exps; /* Number of exports. */
ExportEntry* export; /* Pointer to export table. */
- int num_imports; /* Number of imports. */
+ unsigned int num_imports; /* Number of imports. */
ImportEntry* import; /* Import entry (translated information). */
/*
@@ -323,8 +323,8 @@ typedef struct LoaderState {
* Lambda table.
*/
- int num_lambdas; /* Number of lambdas in table. */
- int lambdas_allocated; /* Size of allocated lambda table. */
+ unsigned int num_lambdas; /* Number of lambdas in table. */
+ unsigned int lambdas_allocated; /* Size of allocated lambda table. */
Lambda* lambdas; /* Pointer to lambdas. */
Lambda def_lambdas[16]; /* Default storage for lambda table. */
char* lambda_error; /* Delayed missing 'FunT' error. */
@@ -333,8 +333,8 @@ typedef struct LoaderState {
* Literals (constant pool).
*/
- int num_literals; /* Number of literals in table. */
- int allocated_literals; /* Number of literal entries allocated. */
+ unsigned int num_literals; /* Number of literals in table. */
+ unsigned int allocated_literals; /* Number of literal entries allocated. */
Literal* literals; /* Array of literals. */
LiteralPatch* literal_patches; /* Operands that need to be patched. */
Uint total_literal_size; /* Total heap size for all literals. */
@@ -343,13 +343,13 @@ typedef struct LoaderState {
* Line table.
*/
BeamInstr* line_item; /* Line items from the BEAM file. */
- int num_line_items; /* Number of line items. */
+ unsigned int num_line_items;/* Number of line items. */
LineInstr* line_instr; /* Line instructions */
- int num_line_instrs; /* Maximum number of line instructions */
- int current_li; /* Current line instruction */
- int* func_line; /* Mapping from function to first line instr */
+ unsigned int num_line_instrs; /* Maximum number of line instructions */
+ unsigned int current_li; /* Current line instruction */
+ unsigned int* func_line; /* Mapping from function to first line instr */
Eterm* fname; /* List of file names */
- int num_fnames; /* Number of filenames in fname table */
+ unsigned int num_fnames; /* Number of filenames in fname table */
int loc_size; /* Size of location info in bytes (2/4) */
} LoaderState;
@@ -663,7 +663,7 @@ erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader,
stp->hdr->compile_ptr = NULL;
stp->hdr->compile_size = 0;
stp->hdr->compile_size_on_heap = 0;
- stp->hdr->literals_start = NULL;
+ stp->hdr->literal_area = NULL;
stp->hdr->md5_ptr = NULL;
/*
@@ -797,14 +797,14 @@ erts_finish_loading(Binary* magic, Process* c_p,
} else {
ErtsCodeIndex code_ix = erts_staging_code_ix();
Eterm module = stp->module;
- int i;
+ int i, num_exps;
/*
* 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++) {
+ num_exps = export_list_size(code_ix);
+ for (i = 0; i < num_exps; i++) {
Export *ep = export_list(i, code_ix);
if (ep == NULL || ep->code[0] != module) {
continue;
@@ -833,11 +833,20 @@ erts_finish_loading(Binary* magic, Process* c_p,
size = stp->loaded_size;
erts_total_code_size += size;
- if (stp->on_load) {
- inst_p = &mod_tab_p->old;
- } else {
+
+ if (!stp->on_load) {
inst_p = &mod_tab_p->curr;
+ } else {
+ mod_tab_p->on_load =
+ (struct erl_module_instance *)
+ erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ sizeof(struct erl_module_instance));
+ inst_p = mod_tab_p->on_load;
+ inst_p->nif = 0;
+ inst_p->num_breakpoints = 0;
+ inst_p->num_traced_exports = 0;
}
+
inst_p->code_hdr = stp->hdr;
inst_p->code_length = size;
@@ -1005,8 +1014,9 @@ loader_state_dtor(Binary* magic)
stp->bin = 0;
}
if (stp->hdr != 0) {
- if (stp->hdr->literals_start) {
- erts_free(ERTS_ALC_T_LITERAL, stp->hdr->literals_start);
+ if (stp->hdr->literal_area) {
+ erts_release_literal_area(stp->hdr->literal_area);
+ stp->hdr->literal_area = NULL;
}
erts_free(ERTS_ALC_T_CODE, stp->hdr);
stp->hdr = 0;
@@ -1330,7 +1340,7 @@ verify_chunks(LoaderState* stp)
static int
load_atom_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_atoms);
stp->num_atoms++;
@@ -1375,13 +1385,13 @@ load_atom_table(LoaderState* stp)
static int
load_import_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_imports);
stp->import = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_imports * sizeof(ImportEntry));
for (i = 0; i < stp->num_imports; i++) {
- int n;
+ unsigned int n;
Eterm mod;
Eterm func;
Uint arity;
@@ -1389,17 +1399,17 @@ load_import_table(LoaderState* stp)
GetInt(stp, 4, n);
if (n >= stp->num_atoms) {
- LoadError2(stp, "import entry %d: invalid atom number %d", i, n);
+ LoadError2(stp, "import entry %u: invalid atom number %u", i, n);
}
mod = stp->import[i].module = stp->atom[n];
GetInt(stp, 4, n);
if (n >= stp->num_atoms) {
- LoadError2(stp, "import entry %d: invalid atom number %d", i, n);
+ LoadError2(stp, "import entry %u: invalid atom number %u", i, n);
}
func = stp->import[i].function = stp->atom[n];
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "import entry %d: invalid arity %d", i, arity);
+ LoadError2(stp, "import entry %u: invalid arity %d", i, arity);
}
stp->import[i].arity = arity;
stp->import[i].patches = 0;
@@ -1427,12 +1437,12 @@ load_import_table(LoaderState* stp)
static int
read_export_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
BeamInstr* address;
GetInt(stp, 4, stp->num_exps);
if (stp->num_exps > stp->num_functions) {
- LoadError2(stp, "%d functions exported; only %d functions defined",
+ LoadError2(stp, "%u functions exported; only %u functions defined",
stp->num_exps, stp->num_functions);
}
stp->export
@@ -1450,16 +1460,16 @@ read_export_table(LoaderState* stp)
stp->export[i].function = func;
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "export table entry %d: absurdly high arity %d", i, arity);
+ LoadError2(stp, "export table entry %u: absurdly high arity %u", i, arity);
}
stp->export[i].arity = arity;
GetInt(stp, 4, n);
if (n >= stp->num_labels) {
- LoadError3(stp, "export table entry %d: invalid label %d (highest defined label is %d)", i, n, stp->num_labels);
+ LoadError3(stp, "export table entry %u: invalid label %u (highest defined label is %u)", i, n, stp->num_labels);
}
value = stp->labels[n].value;
if (value == 0) {
- LoadError2(stp, "export table entry %d: label %d not resolved", i, n);
+ LoadError2(stp, "export table entry %u: label %u not resolved", i, n);
}
stp->export[i].address = address = stp->codev + value;
@@ -1520,7 +1530,7 @@ is_bif(Eterm mod, Eterm func, unsigned arity)
static int
read_lambda_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_lambdas);
if (stp->num_lambdas > stp->lambdas_allocated) {
@@ -1540,12 +1550,12 @@ read_lambda_table(LoaderState* stp)
GetAtom(stp, n, stp->lambdas[i].function);
GetInt(stp, 4, arity);
if (arity > MAX_REG) {
- LoadError2(stp, "lambda entry %d: absurdly high arity %d", i, arity);
+ LoadError2(stp, "lambda entry %u: absurdly high arity %u", i, arity);
}
stp->lambdas[i].arity = arity;
GetInt(stp, 4, n);
if (n >= stp->num_labels) {
- LoadError3(stp, "lambda entry %d: invalid label %d (highest defined label is %d)",
+ LoadError3(stp, "lambda entry %u: invalid label %u (highest defined label is %u)",
i, n, stp->num_labels);
}
stp->lambdas[i].label = n;
@@ -1566,7 +1576,7 @@ read_lambda_table(LoaderState* stp)
static int
read_literal_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
uLongf uncompressed_sz;
byte* uncompressed = 0;
@@ -1588,7 +1598,7 @@ read_literal_table(LoaderState* stp)
}
for (i = 0; i < stp->num_literals; i++) {
- int sz;
+ Uint sz;
Sint heap_size;
byte* p;
Eterm val;
@@ -1597,7 +1607,7 @@ read_literal_table(LoaderState* stp)
GetInt(stp, 4, sz); /* Size of external term format. */
GetString(stp, p, sz);
if ((heap_size = erts_decode_ext_size(p, sz)) < 0) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
if (heap_size > 0) {
@@ -1607,7 +1617,7 @@ read_literal_table(LoaderState* stp)
val = erts_decode_ext(&factory, &p, 0);
if (is_non_value(val)) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
erts_factory_close(&factory);
stp->literals[i].heap_frags = factory.heap_frags;
@@ -1617,7 +1627,7 @@ read_literal_table(LoaderState* stp)
erts_factory_dummy_init(&factory);
val = erts_decode_ext(&factory, &p, 0);
if (is_non_value(val)) {
- LoadError1(stp, "literal %d: bad external format", i);
+ LoadError1(stp, "literal %u: bad external format", i);
}
ASSERT(is_immed(val));
stp->literals[i].heap_frags = NULL;
@@ -1640,9 +1650,9 @@ read_line_table(LoaderState* stp)
{
unsigned version;
ERTS_DECLARE_DUMMY(unsigned flags);
- int num_line_items;
+ unsigned int num_line_items;
BeamInstr* lp;
- int i;
+ unsigned int i;
BeamInstr fname_index;
BeamInstr tag;
@@ -1721,7 +1731,7 @@ read_line_table(LoaderState* stp)
}
} else if (tag == TAG_a) {
if (val > stp->num_fnames) {
- LoadError2(stp, "file index overflow (%d/%d)",
+ LoadError2(stp, "file index overflow (%u/%u)",
val, stp->num_fnames);
}
fname_index = val;
@@ -1757,9 +1767,9 @@ read_line_table(LoaderState* stp)
stp->num_line_instrs *
sizeof(LineInstr));
stp->current_li = 0;
- stp->func_line = (int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
- stp->num_functions *
- sizeof(int));
+ stp->func_line = (unsigned int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ stp->num_functions *
+ sizeof(int));
return 1;
@@ -1783,6 +1793,10 @@ read_code_header(LoaderState* stp)
*/
GetInt(stp, 4, head_size);
+ if (head_size > stp->file_left) {
+ LoadError2(stp, "invalid code header size %u; bytes left %u",
+ head_size, stp->file_left);
+ }
stp->code_start = stp->file_p + head_size;
stp->code_size = stp->file_left - head_size;
stp->file_left = head_size;
@@ -1887,7 +1901,7 @@ load_code(LoaderState* stp)
ci = stp->ci;
for (;;) {
- int new_op;
+ unsigned int new_op;
GenOp* tmp_op;
ASSERT(ci <= codev_size);
@@ -1895,10 +1909,10 @@ load_code(LoaderState* stp)
get_next_instr:
GetByte(stp, new_op);
if (new_op >= NUM_GENERIC_OPS) {
- LoadError1(stp, "invalid opcode %d", new_op);
+ LoadError1(stp, "invalid opcode %u", new_op);
}
if (gen_opc[new_op].name[0] == '\0') {
- LoadError1(stp, "invalid opcode %d", new_op);
+ LoadError1(stp, "invalid opcode %u", new_op);
}
@@ -2368,7 +2382,7 @@ load_code(LoaderState* stp)
VerifyTag(stp, tag, TAG_u);
last_label = tmp_op->a[arg].val;
if (!(0 < last_label && last_label < stp->num_labels)) {
- LoadError2(stp, "invalid label num %d (0 < label < %d)",
+ LoadError2(stp, "invalid label num %u (0 < label < %u)",
tmp_op->a[arg].val, stp->num_labels);
}
if (stp->labels[last_label].value != 0) {
@@ -2512,7 +2526,7 @@ load_code(LoaderState* stp)
{
Sint offset;
if (function_number >= stp->num_functions) {
- LoadError1(stp, "too many functions in module (header said %d)",
+ LoadError1(stp, "too many functions in module (header said %u)",
stp->num_functions);
}
@@ -2591,14 +2605,14 @@ load_code(LoaderState* stp)
if (stp->line_item) {
BeamInstr item = code[ci-1];
BeamInstr loc;
- int li;
+ unsigned int li;
if (item >= stp->num_line_items) {
- LoadError2(stp, "line instruction index overflow (%d/%d)",
+ LoadError2(stp, "line instruction index overflow (%u/%u)",
item, stp->num_line_items);
}
li = stp->current_li;
if (li >= stp->num_line_instrs) {
- LoadError2(stp, "line instruction table overflow (%d/%d)",
+ LoadError2(stp, "line instruction table overflow (%u/%u)",
li, stp->num_line_instrs);
}
loc = stp->line_item[item];
@@ -2630,6 +2644,10 @@ load_code(LoaderState* stp)
* End of code found.
*/
case op_int_code_end:
+ if (function_number != stp->num_functions) {
+ LoadError2(stp, "too few functions (%u) in module (header said %u)",
+ function_number, stp->num_functions);
+ }
stp->codev_size = codev_size;
stp->ci = ci;
stp->function = THE_NON_VALUE;
@@ -4560,13 +4578,16 @@ freeze_code(LoaderState* stp)
Eterm* ptr;
LiteralPatch* lp;
ErlOffHeap code_off_heap;
+ ErtsLiteralArea *literal_area;
+ Uint lit_asize;
ERTS_INIT_OFF_HEAP(&code_off_heap);
- ptr = (Eterm*)erts_alloc(ERTS_ALC_T_LITERAL,
- stp->total_literal_size*sizeof(Eterm));
- code_hdr->literals_start = ptr;
- code_hdr->literals_end = ptr + stp->total_literal_size;
+ lit_asize = ERTS_LITERAL_AREA_ALLOC_SIZE(stp->total_literal_size);
+ literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_asize);
+ ptr = &literal_area->start[0];
+ literal_area->end = ptr + stp->total_literal_size;
+
for (i = 0; i < stp->num_literals; i++) {
if (is_not_immed(stp->literals[i].term)) {
erts_move_multi_frags(&ptr, &code_off_heap,
@@ -4576,7 +4597,7 @@ freeze_code(LoaderState* stp)
ptr_val(stp->literals[i].term)));
}
}
- code_hdr->literals_off_heap = code_off_heap.first;
+ literal_area->off_heap = code_off_heap.first;
lp = stp->literal_patches;
while (lp != 0) {
BeamInstr* op_ptr;
@@ -4587,6 +4608,7 @@ freeze_code(LoaderState* stp)
op_ptr[0] = lit->term;
lp = lp->next;
}
+ code_hdr->literal_area = literal_area;
}
CHKBLK(ERTS_ALC_T_CODE,code);
@@ -4598,8 +4620,8 @@ freeze_code(LoaderState* stp)
str_table = (byte *) (codev + stp->ci);
} else {
BeamCodeLineTab* const line_tab = (BeamCodeLineTab *) (codev+stp->ci);
- const int ftab_size = stp->num_functions;
- const int num_instrs = stp->current_li;
+ const unsigned int ftab_size = stp->num_functions;
+ const unsigned int num_instrs = stp->current_li;
const BeamInstr** const line_items =
(const BeamInstr**) &line_tab->func_tab[ftab_size + 1];
@@ -4759,7 +4781,7 @@ freeze_code(LoaderState* stp)
static void
final_touch(LoaderState* stp, struct erl_module_instance* inst_p)
{
- int i;
+ unsigned int i;
int on_load = stp->on_load;
unsigned catches;
Uint index;
@@ -4845,7 +4867,7 @@ final_touch(LoaderState* stp, struct erl_module_instance* inst_p)
/*
* We are hiding a pointer into older code.
*/
- erts_refc_dec(&fe->refc, 1);
+ erts_smp_refc_dec(&fe->refc, 1);
}
fe->address = code_ptr;
#ifdef HIPE
@@ -5431,7 +5453,7 @@ new_genop(LoaderState* stp)
static int
new_label(LoaderState* stp)
{
- int num = stp->num_labels;
+ unsigned int num = stp->num_labels;
stp->num_labels++;
stp->labels = (Label *) erts_realloc(ERTS_ALC_T_PREPARED_CODE,
@@ -5642,6 +5664,28 @@ has_native(BeamCodeHeader *code_hdr)
return result;
}
+void
+erts_release_literal_area(ErtsLiteralArea* literal_area)
+{
+ struct erl_off_heap_header* oh;
+
+ if (!literal_area)
+ return;
+
+ oh = literal_area->off_heap;
+
+ while (oh) {
+ Binary* bptr;
+ ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG);
+ bptr = ((ProcBin*)oh)->val;
+ if (erts_refc_dectest(&bptr->refc, 0) == 0) {
+ erts_bin_free(bptr);
+ }
+ oh = oh->next;
+ }
+ erts_free(ERTS_ALC_T_LITERAL, literal_area);
+}
+
int
erts_is_module_native(BeamCodeHeader* code_hdr)
{
@@ -5710,12 +5754,13 @@ exported_from_module(Process* p, /* Process whose heap to use. */
ErtsCodeIndex code_ix,
Eterm mod) /* Tagged atom for module. */
{
- int i;
+ int i, num_exps;
Eterm* hp = NULL;
Eterm* hend = NULL;
Eterm result = NIL;
- for (i = 0; i < export_list_size(code_ix); i++) {
+ num_exps = export_list_size(code_ix);
+ for (i = 0; i < num_exps; i++) {
Export* ep = export_list(i,code_ix);
if (ep->code[0] == mod) {
@@ -6029,11 +6074,11 @@ stub_copy_info(LoaderState* stp,
static int
stub_read_export_table(LoaderState* stp)
{
- int i;
+ unsigned int i;
GetInt(stp, 4, stp->num_exps);
if (stp->num_exps > stp->num_functions) {
- LoadError2(stp, "%d functions exported; only %d functions defined",
+ LoadError2(stp, "%u functions exported; only %u functions defined",
stp->num_exps, stp->num_functions);
}
stp->export
@@ -6047,7 +6092,7 @@ stub_read_export_table(LoaderState* stp)
GetAtom(stp, n, stp->export[i].function);
GetInt(stp, 4, n);
if (n > MAX_REG) {
- LoadError2(stp, "export table entry %d: absurdly high arity %d", i, n);
+ LoadError2(stp, "export table entry %u: absurdly high arity %u", i, n);
}
stp->export[i].arity = n;
GetInt(stp, 4, n); /* Ignore label */
@@ -6061,8 +6106,8 @@ stub_read_export_table(LoaderState* stp)
static void
stub_final_touch(LoaderState* stp, BeamInstr* fp)
{
- int i;
- int n = stp->num_exps;
+ unsigned int i;
+ unsigned int n = stp->num_exps;
Eterm mod = fp[2];
Eterm function = fp[3];
int arity = fp[4];
@@ -6242,7 +6287,7 @@ patch_funentries(Eterm Patchlist)
*
* Reproduced on a debug emulator with stdlib_test/qlc_SUITE:join_merge
*
- * erts_refc_dec(&fe->refc, 1);
+ * erts_smp_refc_dec(&fe->refc, 1);
*/
if (!patch(Addresses, (Uint) fe))
@@ -6373,9 +6418,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
code_hdr->compile_ptr = NULL;
code_hdr->compile_size = 0;
code_hdr->compile_size_on_heap = 0;
- code_hdr->literals_start = NULL;
- code_hdr->literals_end = NULL;
- code_hdr->literals_off_heap = 0;
+ code_hdr->literal_area = NULL;
code_hdr->on_load_function_ptr = NULL;
code_hdr->line_table = NULL;
code_hdr->md5_ptr = NULL;
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index fd2dd97fee..9be5e14e40 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -50,6 +50,7 @@ extern BeamInstr* em_call_error_handler;
extern BeamInstr* em_apply_bif;
extern BeamInstr* em_call_nif;
+struct ErtsLiteralArea_;
/*
* The following variables keep a sorted list of address ranges for
@@ -89,9 +90,7 @@ typedef struct beam_code_header {
/*
* Literal area (constant pool).
*/
- Eterm* literals_start;
- Eterm* literals_end;
- struct erl_off_heap_header* literals_off_heap;
+ struct ErtsLiteralArea_ *literal_area;
/*
* Pointer to the on_load function (or NULL if none).
@@ -120,7 +119,13 @@ typedef struct beam_code_header {
}BeamCodeHeader;
+void erts_release_literal_area(struct ErtsLiteralArea_* literal_area);
int erts_is_module_native(BeamCodeHeader* code);
+void erts_beam_bif_load_init(void);
+struct erl_fun_entry;
+void erts_purge_state_add_fun(struct erl_fun_entry *fe);
+Export *erts_suspend_process_on_pending_purge_lambda(Process *c_p,
+ struct erl_fun_entry*);
/*
* Layout of the line table.
diff --git a/erts/emulator/beam/benchmark.c b/erts/emulator/beam/benchmark.c
deleted file mode 100644
index c8409784ef..0000000000
--- a/erts/emulator/beam/benchmark.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2002-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "sys.h"
-#include "global.h"
-#include "benchmark.h"
-
-#ifdef BM_COUNTERS
-unsigned long long processes_busy;
-unsigned long long processes_spawned;
-unsigned long long messages_sent;
-unsigned long long messages_copied;
-unsigned long long messages_ego;
-unsigned long long minor_gc;
-unsigned long long major_gc;
-#endif /* BM_COUNTERS */
-
-#ifdef BM_TIMERS
-
-/* assuming Solaris */
-#include <time.h>
-BM_TIMER_T system_clock;
-
-unsigned long local_pause_times[MAX_PAUSE_TIME];
-unsigned long pause_times[MAX_PAUSE_TIME];
-unsigned long pause_times_old[MAX_PAUSE_TIME];
-
-BM_TIMER_T mmu;
-BM_TIMER_T mmu_counter;
-
-BM_NEW_TIMER(timer);
-BM_NEW_TIMER(system);
-BM_NEW_TIMER(gc);
-BM_NEW_TIMER(minor_gc);
-BM_NEW_TIMER(major_gc);
-BM_NEW_TIMER(minor_global_gc);
-BM_NEW_TIMER(major_global_gc);
-BM_NEW_TIMER(send);
-BM_NEW_TIMER(copy);
-BM_NEW_TIMER(size);
-BM_NEW_TIMER(max_minor);
-BM_NEW_TIMER(max_major);
-BM_NEW_TIMER(max_global_minor);
-BM_NEW_TIMER(max_global_major);
-BM_NEW_TIMER(misc0);
-BM_NEW_TIMER(misc1);
-BM_NEW_TIMER(misc2);
-#endif /* BM_TIMERS */
-
-#ifdef BM_HEAP_SIZES
-unsigned long long max_used_heap;
-unsigned long long max_allocated_heap;
-unsigned long long max_used_global_heap;
-unsigned long long max_allocated_global_heap;
-#endif /* BM_HEAP_SIZES */
-
-#ifdef BM_MESSAGE_SIZES
-unsigned long long words_sent;
-unsigned long long words_copied;
-unsigned long long words_prealloc;
-unsigned long long message_sizes[1000];
-#endif /* BM_MESSAGE_SIZES */
-
-/*****
- * The following functions have to be defined, but they only have contents
- * if certain keywords are defined.
- */
-
-void init_benchmarking()
-{
-#ifdef BM_TIMERS
- int i;
- for (i = 0; i < 1000; i++)
- {
- BM_START_TIMER(system);
- BM_STOP_TIMER(system);
- }
- timer_time = system_time / 1000;
-
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- local_pause_times[i] = 0;
- pause_times[i] = 0;
- pause_times_old[i] = 0;
- }
-
- mmu = 0;
- mmu_counter = 0;
-
- BM_MMU_INIT();
-#endif /* BM_TIMERS */
-
-#ifdef BM_COUNTERS
- processes_busy = 0;
- processes_spawned = 0;
- messages_sent = 0;
- messages_copied = 0;
- messages_ego = 0;
- minor_gc = 0;
- major_gc = 0;
-#endif /* BM_COUNTERS */
-
-#ifdef BM_HEAP_SIZES
- max_used_heap = 0;
- max_allocated_heap = 0;
- max_used_global_heap = 0;
- max_allocated_global_heap = 0;
-#endif /* BM_HEAP_SIZES */
-
-#ifdef BM_MESSAGE_SIZES
- words_sent = 0;
- words_copied = 0;
- words_prealloc = 0;
- {
- int i;
- for (i = 0; i < 1000; i++)
- message_sizes[i] = 0;
- }
-#endif /* BM_MESSAGE_SIZES */
-}
-
-void save_statistics()
-{
-#ifdef BM_STATISTICS
- FILE *file = fopen(BM_STATISTICS_FILE,"a");
- long i = 0;
-
- if (file)
- {
- erts_fprintf(file,"-------------------------------------------------------------------------\n");
- erts_fprintf(file,"The counters are reset at system start and are sums over the entire node.\n");
- erts_fprintf(file,"You may reset them manually using the BIFs in the module hipe_bifs.\n");
- erts_fprintf(file,"All times are given in milliseconds.\n");
- erts_fprintf(file,"-------------------------------------------------------------------------\n");
-
- erts_fprintf(file,"Node: %T\n",erts_this_node->sysname);
-
-#ifdef BM_COUNTERS
- erts_fprintf(file,"Number of processes spawned: %lld\n",processes_spawned);
- erts_fprintf(file,"Number of local minor GCs: %lld\n",minor_gc);
- erts_fprintf(file,"Number of local major GCs: %lld\n",major_gc);
- erts_fprintf(file,"Number of messages sent: %lld\n",messages_sent);
- erts_fprintf(file,"Number of messages copied: %lld\n",messages_copied);
- erts_fprintf(file,"Number of messages sent to self: %lld\n",messages_ego);
-#endif /* BM_COUNTERS */
-
-#ifdef BM_MESSAGE_SIZES
- erts_fprintf(file,"Number of words sent: %lld\n",words_sent);
- erts_fprintf(file,"Number of words copied: %lld\n",words_copied);
- erts_fprintf(file,"Number of words preallocated: %lld\n",words_prealloc);
-#endif /* BM_MESSAGE_SIZES */
-
-#ifdef BM_HEAP_SIZES
- erts_fprintf(file,"Biggest local heap used (in words): %lld\n",max_used_heap);
- erts_fprintf(file,"Biggest local heap allocated (in words): %lld\n",max_allocated_heap);
- erts_fprintf(file,"Biggest global heap used (in words): %lld\n",max_used_global_heap);
- erts_fprintf(file,"Biggest global heap allocated (in words): %lld\n",max_allocated_global_heap);
-#endif /* BM_HEAP_SIZES */
-
-#ifdef BM_TIMERS
- erts_fprintf(file,"--- The total active system time is the sum of all times below ---\n");
- BM_TIME_PRINTER("Mutator time",system_time);
- BM_TIME_PRINTER("Time spent in send (excluding size & copy)",send_time);
- BM_TIME_PRINTER("Time spent in size",size_time);
- BM_TIME_PRINTER("Time spent in copy",copy_time);
- BM_TIME_PRINTER("Time spent in local minor GC",minor_gc_time);
- BM_TIME_PRINTER("Time spent in local major GC",major_gc_time);
- BM_TIME_PRINTER("Time spent in global minor GC",minor_global_gc_time);
- BM_TIME_PRINTER("Time spent in global major GC",major_global_gc_time);
- erts_fprintf(file,"---\n");
- BM_TIME_PRINTER("Maximum time spent in one separate local minor GC",max_minor_time);
- BM_TIME_PRINTER("Maximum time spent in one separate local major GC",max_major_time);
- BM_TIME_PRINTER("Maximum time spent in one separate global minor GC",max_global_minor_time);
- BM_TIME_PRINTER("Maximum time spent in one separate global major GC",max_global_major_time);
-#endif /* BM_TIMERS */
-
-#if 0
- /* Save a log file for import into excel */
-
- long long total_time, n;
- long left, right, mid;
-
-#ifdef BM_COUNTERS
- erts_fprintf(file,"Spawns\tLocalGC\tMAGC\tMessages\tMutator_t\tLocalGC_t\tMAGC_t\tLocMaxP\tLocMeanP\tLocGeoMP\tMAMaxP\tMAMeanP\tMAGeoMP\t\tCMAGC\tCMAGC_t\n");
- erts_fprintf(file,"%lld\t%lld\t%lld\t%lld\t",
- processes_spawned,
- minor_garbage_cols + major_garbage_cols,
- minor_global_garbage_cols + major_global_garbage_cols,
- messages_sent);
-#endif /* BM_COUNTERS */
-
-#ifdef BM_TIMERS
- erts_fprintf(file,"%lld\t%lld\t%lld\t",
- (long long)(system_time + send_time + size_time + copy_time),
- (long long)(minor_gc_time + major_gc_time),
- (long long)(minor_global_gc_time + major_global_gc_time));
-
- total_time = 0; n = 0;
- left = 0; right = 0; mid = 0;
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- total_time += local_pause_times[i] * i;
- n += local_pause_times[i];
- if (i > mid)
- right += local_pause_times[i];
- while(right > left) {
- left += local_pause_times[mid++];
- right -= local_pause_times[mid];
- }
- }
- erts_fprintf(file,"%lld\t%lld\t%ld\t",
- (long long)((max_minor_time > max_major_time ?
- max_minor_time :
- max_major_time)*1000),
- total_time / n,
- mid);
-
- total_time = 0; n = 0;
- left = 0; right = 0; mid = 0;
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (pause_times[i] > 0) {
- total_time += pause_times[i] * i;
- n += pause_times[i];
- if (i > mid)
- right += pause_times[i];
- while(right > left) {
- left += pause_times[mid++];
- right -= pause_times[mid];
- }
- }
- }
- erts_fprintf(file,"%lld\t%lld\t%ld\t",
- (long long)((max_global_minor_time > max_global_major_time ?
- max_global_minor_time :
- max_global_major_time)*1000),
- (n > 0 ? total_time / n : 0),
- mid);
-
- erts_fprintf(file,"\t%lld\t%lld\n",n,total_time);
-
- erts_fprintf(file,"\nMinor:\n");
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (i < 1000 || pause_times[i] > 0) {
- erts_fprintf(file,"%d\t%ld\n",i,pause_times[i]);
- }
- }
-
- fprintf(file,"Major:\n");
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (pause_times_old[i] > 0) {
- fprintf(file,"%d\t%ld\n",i,pause_times_old[i]);
- }
- }
-#endif /* BM_TIMERS */
-
-#ifdef BM_TIMERS
- total_time = 0; n = 0;
- left = 0; right = 0; mid = 0;
- fprintf(file,"\nLocal:\n");
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (local_pause_times[i] > 0) {
- erts_fprintf(file,"%d\t%ld\n",i,local_pause_times[i]);
- total_time += local_pause_times[i] * i;
- n += local_pause_times[i];
- if (i > mid)
- right += local_pause_times[i];
- while(right > left) {
- left += local_pause_times[mid++];
- right -= local_pause_times[mid];
- }
- }
- }
- erts_fprintf(file,"Mid: %ld Mean: %ld\n",(long)mid,
- (long)(n > 0 ? total_time / n : 0));
-#endif
-#endif /* 0 */
- fclose(file);
- }
- else
- fprintf(stderr,"Sorry... Can not write to %s!\n\r",BM_STATISTICS_FILE);
-#endif /* BM_STATISTICS */
-}
diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h
deleted file mode 100644
index 0272896f4f..0000000000
--- a/erts/emulator/beam/benchmark.h
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2002-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
-#ifndef __BENCHMARK_H__
-#define __BENCHMARK_H__
-
-/* The define __BENCHMARK__ is the master switch to turn on and off
- * benchmarking. This will enable the benchmark-BIFs in hipe_bif1.c.
- * Documentation for the BIFs is in hipe_bif1.c, and that is where you
- * will find the information about how to accually get some data out
- * from these timers and counters.
- */
-/* #define __BENCHMARK__ */
-
-#ifdef __BENCHMARK__
-/*
- * The defines below enables different parts of the benchmaring.
- * Counters and timers that are disabled, always report zero in
- * the BIFs.
- */
-
-/* BM_TIMERS keeps track of the time spent in diferent parts of the
- * system. It only measures accual active time, not time spent in idle
- * mode. Currently, the Solaris hrtime_t will be used.
- * To add new timers look below.
- */
-#define BM_TIMERS
-
-/* BM_COUNTERS count all kinds of events that occurs in the system.
- * Among other things it counts the number of messages, then number of
- * garbage collections, the number of processes spawned etc.
- * To add new counters look below.
- */
-#define BM_COUNTERS
-
-/* BM_MESSAGE_SIZES keeps a log of the size of all messages sent in
- * the system. This introduce an overhead in time for the shared heap
- * system since all message sizes have to be calculated at send.
- */
-/* #define BM_MESSAGE_SIZES */
-
-/* BM_HEAP_SIZES goes through all processes at garbage collection time
- * to sum their allocated and used heap sizes. In anything else than a
- * shared heap system, this will cost.
- */
-/* #define BM_HEAP_SIZES */
-
-/* BM_STATISTICS saves an entry in the file BM_STATISTICS_FILE. This
- * is done for each erlang node at exit time.
- */
-/* #define BM_STATISTICS */
-
-#endif /* __BENCHMARK__ */
-
-
-#ifdef BM_STATISTICS
-# define BM_STATISTICS_FILE "/tmp/erlang_statistics.joppe.log"
-#endif /* BM_STATISTICS */
-
-
-/************ There are no more settings below this line *************/
-
-/*
- * Maintenance and how to add new stuff is documented by the code
- * below ;-)
- */
-
-#ifdef BM_COUNTERS
-/*********************************************************************
- * To add new counters:
- *
- * Add the variable here AND in benchmark.c. Use the macro
- * BM_COUNT(var) in the code where you want to increase it.
- *
- */
-extern unsigned long long processes_busy;
-extern unsigned long long processes_spawned;
-extern unsigned long long messages_sent;
-extern unsigned long long messages_copied;
-extern unsigned long long messages_ego;
-extern unsigned long long minor_gc;
-extern unsigned long long major_gc;
-
-#define BM_COUNT(var) (var)++;
-
-#define BM_EGO_COUNT(send,rec) { \
- if ((send) == (rec)) \
- BM_COUNT(messages_ego); }
-
-#define BM_LAZY_COPY_START long long gcs = minor_global_gc + major_global_gc;
-#define BM_LAZY_COPY_STOP { gcs = (minor_global_gc + major_global_gc) - gcs; \
- if (gcs > gc_in_copy) gc_in_copy = gcs; }
-
-#else /* !BM_COUNTERS */
-# define BM_COUNT(var)
-# define BM_EGO_COUNT(send,rec)
-# define BM_LAZY_COPY_START
-# define BM_LAZY_COPY_STOP
-#endif /* BM_COUNTERS */
-
-
-#ifdef BM_TIMERS
-/*********************************************************************
- * To add new timers:
- *
- * Add the variable below using the form extern BM_TIMER_T blah_time.
- * Also add them in benchmark.c using the macro NEW_TIMER(blah). Use
- * the macro BM_SWAP_TIMER(from,blah) ... BM_SWAP_TIMER(blah,to) to
- * start and stop the new timer. Note, that you have to know what
- * timer is running at the place where you want to insert your new
- * timer to be able to stop and start (from,to) it.
- *
- * You can use the macros BM_STOP_TIMER(blah) and BM_START_TIMER(blah)
- * around code that should not be timed at all. As above, you have to
- * know what timer to start and stop. The system timer is running at
- * most places in the emulator. Only the garbage collector and the
- * message sending has its own timers at the moment.
- *
- * The timer_time used when stopping timers is the time it takes to
- * start and stop the timers, calculated in init_benchmarking(). If it
- * is not there, the time it takes to do this will accually be
- * substantial compared to some small times in the system we want to
- * meassure (send time in shared heap for instance).
- */
-
-/* (Assuming Solaris) */
-
-#define BM_TIMER_T ErtsMonotonicTime
-#define BM_START_TIMER(t) system_clock = ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time())
-#define BM_STOP_TIMER(t) do { \
- BM_TIMER_T tmp = (ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time()) - system_clock) - timer_time; \
- t##_time += (tmp > 0 ? tmp : 0); \
-} while(0)
-
-#define BM_TIME_PRINTER(str,time) do { \
- int min,sec,milli,micro; \
- BM_TIMER_T tmp; \
- tmp = (time) / 1000; \
- micro = tmp % 1000; \
- tmp /= 1000; \
- milli = tmp % 1000; \
- tmp /= 1000; \
- sec = tmp % 60; \
- min = tmp / 60; \
- erts_fprintf(file,str": %d:%02d.%03d %03d\n",min,sec,milli,micro); \
-} while(0)
-
-extern BM_TIMER_T system_clock;
-
-extern BM_TIMER_T timer_time;
-extern BM_TIMER_T system_time;
-extern BM_TIMER_T gc_time;
-extern BM_TIMER_T minor_gc_time;
-extern BM_TIMER_T major_gc_time;
-extern BM_TIMER_T minor_global_gc_time;
-extern BM_TIMER_T major_global_gc_time;
-extern BM_TIMER_T send_time;
-extern BM_TIMER_T copy_time;
-extern BM_TIMER_T size_time;
-extern BM_TIMER_T max_minor_time;
-extern BM_TIMER_T max_major_time;
-extern BM_TIMER_T max_global_minor_time;
-extern BM_TIMER_T max_global_major_time;
-extern BM_TIMER_T misc0_time;
-extern BM_TIMER_T misc1_time;
-extern BM_TIMER_T misc2_time;
-
-#define MAX_PAUSE_TIME 500000
-extern unsigned long local_pause_times[MAX_PAUSE_TIME];
-extern unsigned long pause_times[MAX_PAUSE_TIME];
-extern unsigned long pause_times_old[MAX_PAUSE_TIME];
-
-#define MMU_INTERVAL 5 /* milli seconds */
-extern BM_TIMER_T mmu_counter;
-extern BM_TIMER_T mmu;
-
-#define BM_NEW_TIMER(t) BM_TIMER_T t##_time = 0;
-#define BM_RESET_TIMER(t) t##_time = 0;
-#define BM_SWAP_TIMER(t1,t2) do { BM_STOP_TIMER(t1); BM_START_TIMER(t2); } while(0)
-#define BM_MMU_INIT() do { \
- BM_TIMER_T gc = gc_time; \
- while (gc > 0) { \
- if (gc > MMU_INTERVAL) { \
- gc -= MMU_INTERVAL - mmu_counter; \
- erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \
- mmu_counter = 0; mmu = 0; \
- } else { \
- mmu_counter += gc; \
- if (mmu_counter >= MMU_INTERVAL) { \
- mmu_counter -= MMU_INTERVAL; \
- erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \
- mmu = 0; \
- } \
- gc = 0; \
- } \
- } \
- BM_RESET_TIMER(system); \
- BM_RESET_TIMER(send); \
- BM_RESET_TIMER(copy); \
- BM_RESET_TIMER(size); \
-} while(0)
-
-#define BM_MMU_READ() do { \
- BM_TIMER_T mut = system_time + send_time + copy_time + size_time; \
- while (mut > 0) { \
- if (mut > MMU_INTERVAL) { \
- BM_TIMER_T tmp = MMU_INTERVAL - mmu_counter; \
- mmu += tmp; mut -= tmp; \
- erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \
- mmu_counter = 0; mmu = 0; \
- } else { \
- mmu_counter += mut; mmu += mut; \
- if (mmu_counter >= MMU_INTERVAL) { \
- mmu_counter -= MMU_INTERVAL; \
- mmu -= mmu_counter; \
- erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \
- mmu = mmu_counter; \
- } \
- mut = 0; \
- } \
- } \
-} while(0)
-
-#else /* !BM_TIMERS */
-# define BM_NEW_TIMER(t)
-# define BM_START_TIMER(t)
-# define BM_STOP_TIMER(t)
-# define BM_RESET_TIMER(t)
-# define BM_SWAP_TIMER(t1,t2)
-# define BM_TIME_PRINTER(str,time)
-# define BM_MMU_INIT()
-# define BM_MMU_READ()
-#endif /* BM_TIMERS */
-
-#ifdef BM_HEAP_SIZES
-extern unsigned long long max_used_heap;
-extern unsigned long long max_allocated_heap;
-extern unsigned long long max_used_global_heap;
-extern unsigned long long max_allocated_global_heap;
-#endif /* BM_HEAP_SIZES */
-
-#ifdef BM_MESSAGE_SIZES
-extern unsigned long long words_sent;
-extern unsigned long long words_copied;
-extern unsigned long long words_prealloc;
-extern unsigned long long message_sizes[1000];
-
-#define BM_MESSAGE_COPIED(size) { \
- words_copied += size; \
- BM_COUNT(messages_copied); }
-
-#define BM_PREALLOC_DATA(size) { \
- words_prealloc += size; }
-
-#define BM_MESSAGE(mess,send,rec) { \
- Uint msize = size_object(mess); \
- words_sent += msize; \
- if (msize < 1000) \
- message_sizes[msize]++; \
- else \
- message_sizes[999]++; \
- BM_EGO_COUNT(send,rec); \
- BM_COUNT(messages_sent); }
-
-#else /* !BM_MESSAGE_SIZES */
-
-#define BM_MESSAGE_COPIED(size) BM_COUNT(messages_copied);
-#define BM_PREALLOC_DATA(size)
-#define BM_MESSAGE(mess,send,rec) { \
- BM_EGO_COUNT(send,rec); \
- BM_COUNT(messages_sent); }
-
-#endif /* BM_MESSAGE_SIZES */
-
-void init_benchmarking(void);
-void save_statistics(void);
-
-#endif /* _BENCHMARK_H_ */
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index fc14061a44..d9048065c8 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -4305,8 +4305,9 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2)
else {
locks &= ~ERTS_PROC_LOCK_STATUS;
erts_smp_proc_unlock(new_member, ERTS_PROC_LOCK_STATUS);
- if (erts_smp_atomic32_read_nob(&new_member->state)
- & !(ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ if (new_member == BIF_P
+ || !(erts_smp_atomic32_read_nob(&new_member->state)
+ & (ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS))) {
new_member->group_leader = STORE_NC_IN_PROC(new_member,
BIF_ARG_1);
}
@@ -4326,6 +4327,7 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2)
BIF_ARG_1);
bp->next = new_member->mbuf;
new_member->mbuf = bp;
+ new_member->mbuf_sz += bp->used_size;
}
}
}
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 065018514a..80db4eb6ff 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -161,6 +161,7 @@ bif erts_internal:port_close/1
bif erts_internal:port_connect/2
bif erts_internal:request_system_task/3
+bif erts_internal:request_system_task/4
bif erts_internal:check_process_code/2
bif erts_internal:map_to_tuple_keys/1
@@ -174,6 +175,8 @@ bif erts_internal:is_system_process/1
bif erts_internal:system_check/1
+bif erts_internal:release_literal_area_switch/0
+
# inet_db support
bif erlang:port_set_data/2
bif erlang:port_get_data/1
@@ -642,8 +645,9 @@ bif erts_debug:map_info/1
# New in 19.0
#
-bif erts_internal:copy_literals/2
-bif erts_internal:purge_module/1
+bif erts_internal:is_process_executing_dirty/1
+bif erts_internal:check_dirty_process_code/2
+bif erts_internal:purge_module/2
bif binary:split/2
bif binary:split/3
bif erts_debug:size_shared/1
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 4baee7900b..27064a4fd6 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -1293,8 +1293,11 @@ static dsize_t I_bxor(ErtsDigit* x, dsize_t xl, short xsgn,
*r++ = ~c ^ *y++;
x++;
}
- while(xl--)
- *r++ = ~*x++;
+ while(xl--) {
+ DSUBb(*x,0,b,c);
+ *r++ = ~c;
+ x++;
+ }
}
else {
ErtsDigit b1, b2;
@@ -1312,7 +1315,9 @@ static dsize_t I_bxor(ErtsDigit* x, dsize_t xl, short xsgn,
x++; y++;
}
while(xl--) {
- *r++ = *x++;
+ DSUBb(*x,0,b1,c1);
+ *r++ = c1;
+ x++;
}
}
}
diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h
index 464acd67f6..4a96d971c3 100644
--- a/erts/emulator/beam/big.h
+++ b/erts/emulator/beam/big.h
@@ -21,17 +21,8 @@
#ifndef __BIG_H__
#define __BIG_H__
-#ifndef __SYS_H__
#include "sys.h"
-#endif
-
-#ifndef __CONFIG_H__
-#include "erl_vm.h"
-#endif
-
-#ifndef __GLOBAL_H__
#include "global.h"
-#endif
typedef Uint ErtsDigit;
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 3c19e82b66..74a8a3b852 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -44,23 +44,22 @@
static void process_killer(void);
void do_break(void);
void erl_crash_dump_v(char *file, int line, char* fmt, va_list args);
-void erl_crash_dump(char* file, int line, char* fmt, ...);
#ifdef DEBUG
static void bin_check(void);
#endif
-static void print_garb_info(int to, void *to_arg, Process* p);
+static void print_garb_info(fmtfn_t to, void *to_arg, Process* p);
#ifdef OPPROF
static void dump_frequencies(void);
#endif
-static void dump_attributes(int to, void *to_arg, byte* ptr, int size);
+static void dump_attributes(fmtfn_t to, void *to_arg, byte* ptr, int size);
extern char* erts_system_version[];
static void
-port_info(int to, void *to_arg)
+port_info(fmtfn_t to, void *to_arg)
{
int i, max = erts_ptab_max(&erts_port);
for (i = 0; i < max; i++) {
@@ -71,7 +70,7 @@ port_info(int to, void *to_arg)
}
void
-process_info(int to, void *to_arg)
+process_info(fmtfn_t to, void *to_arg)
{
int i, max = erts_ptab_max(&erts_proc);
for (i = 0; i < max; i++) {
@@ -148,14 +147,14 @@ process_killer(void)
typedef struct {
int is_first;
- int to;
+ fmtfn_t to;
void *to_arg;
} PrintMonitorContext;
static void doit_print_link(ErtsLink *lnk, void *vpcontext)
{
PrintMonitorContext *pcontext = vpcontext;
- int to = pcontext->to;
+ fmtfn_t to = pcontext->to;
void *to_arg = pcontext->to_arg;
if (pcontext->is_first) {
@@ -170,7 +169,7 @@ static void doit_print_link(ErtsLink *lnk, void *vpcontext)
static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
{
PrintMonitorContext *pcontext = vpcontext;
- int to = pcontext->to;
+ fmtfn_t to = pcontext->to;
void *to_arg = pcontext->to_arg;
char *prefix = ", ";
@@ -197,7 +196,7 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
/* Display info about an individual Erlang process */
void
-print_process_info(int to, void *to_arg, Process *p)
+print_process_info(fmtfn_t to, void *to_arg, Process *p)
{
time_t approx_started;
int garbing = 0;
@@ -300,7 +299,7 @@ print_process_info(int to, void *to_arg, Process *p)
/* display the links only if there are any*/
if (ERTS_P_LINKS(p) || ERTS_P_MONITORS(p)) {
- PrintMonitorContext context = {1,to};
+ PrintMonitorContext context = {1, to, to_arg};
erts_print(to, to_arg,"Link list: [");
erts_doforall_links(ERTS_P_LINKS(p), &doit_print_link, &context);
erts_doforall_monitors(ERTS_P_MONITORS(p), &doit_print_monitor, &context);
@@ -348,7 +347,7 @@ print_process_info(int to, void *to_arg, Process *p)
}
static void
-print_garb_info(int to, void *to_arg, Process* p)
+print_garb_info(fmtfn_t to, void *to_arg, Process* p)
{
#ifdef ERTS_SMP
/* ERTS_SMP: A scheduler is probably concurrently doing gc... */
@@ -365,7 +364,7 @@ print_garb_info(int to, void *to_arg, Process* p)
}
void
-info(int to, void *to_arg)
+info(fmtfn_t to, void *to_arg)
{
erts_memory(&to, to_arg, NULL, THE_NON_VALUE);
atom_info(to, to_arg);
@@ -380,8 +379,20 @@ info(int to, void *to_arg)
}
+static int code_size(struct erl_module_instance* modi)
+{
+ int size = modi->code_length;
+
+ if (modi->code_hdr) {
+ ErtsLiteralArea* lit = modi->code_hdr->literal_area;
+ if (lit)
+ size += (lit->end - lit->start) * sizeof(Eterm);
+ }
+ return size;
+}
+
void
-loaded(int to, void *to_arg)
+loaded(fmtfn_t to, void *to_arg)
{
int i;
int old = 0;
@@ -397,13 +408,9 @@ loaded(int to, void *to_arg)
* Calculate and print totals.
*/
for (i = 0; i < module_code_size(code_ix); i++) {
- if ((modp = module_code(i, code_ix)) != NULL &&
- ((modp->curr.code_length != 0) ||
- (modp->old.code_length != 0))) {
- cur += modp->curr.code_length;
- if (modp->old.code_length != 0) {
- old += modp->old.code_length;
- }
+ if ((modp = module_code(i, code_ix)) != NULL) {
+ cur += code_size(&modp->curr);
+ old += code_size(&modp->old);
}
}
erts_print(to, to_arg, "Current code: %d\n", cur);
@@ -419,31 +426,25 @@ loaded(int to, void *to_arg)
/*
* Interactive dump; keep it brief.
*/
- if (modp != NULL &&
- ((modp->curr.code_length != 0) ||
- (modp->old.code_length != 0))) {
- erts_print(to, to_arg, "%T", make_atom(modp->module));
- cur += modp->curr.code_length;
- erts_print(to, to_arg, " %d", modp->curr.code_length );
- if (modp->old.code_length != 0) {
- erts_print(to, to_arg, " (%d old)",
- modp->old.code_length );
- old += modp->old.code_length;
- }
+ if (modp != NULL && ((modp->curr.code_length != 0) ||
+ (modp->old.code_length != 0))) {
+ erts_print(to, to_arg, "%T %d", make_atom(modp->module),
+ code_size(&modp->curr));
+ if (modp->old.code_length != 0)
+ erts_print(to, to_arg, " (%d old)", code_size(&modp->old));
erts_print(to, to_arg, "\n");
}
} else {
/*
* To crash dump; make it parseable.
*/
- if (modp != NULL &&
- ((modp->curr.code_length != 0) ||
- (modp->old.code_length != 0))) {
+ if (modp != NULL && ((modp->curr.code_length != 0) ||
+ (modp->old.code_length != 0))) {
erts_print(to, to_arg, "=mod:");
erts_print(to, to_arg, "%T", make_atom(modp->module));
erts_print(to, to_arg, "\n");
erts_print(to, to_arg, "Current size: %d\n",
- modp->curr.code_length);
+ code_size(&modp->curr));
code = modp->curr.code_hdr;
if (code != NULL && code->attr_ptr) {
erts_print(to, to_arg, "Current attributes: ");
@@ -457,7 +458,7 @@ loaded(int to, void *to_arg)
}
if (modp->old.code_length != 0) {
- erts_print(to, to_arg, "Old size: %d\n", modp->old.code_length);
+ erts_print(to, to_arg, "Old size: %d\n", code_size(&modp->old));
code = modp->old.code_hdr;
if (code->attr_ptr) {
erts_print(to, to_arg, "Old attributes: ");
@@ -478,7 +479,7 @@ loaded(int to, void *to_arg)
static void
-dump_attributes(int to, void *to_arg, byte* ptr, int size)
+dump_attributes(fmtfn_t to, void *to_arg, byte* ptr, int size)
{
while (size-- > 0) {
erts_print(to, to_arg, "%02X", *ptr++);
@@ -648,7 +649,7 @@ bin_check(void)
erts_printf("%p orig_size: %bpd, norefs = %bpd\n",
bp->val,
bp->val->orig_size,
- erts_smp_atomic_read_nob(&bp->val->refc));
+ erts_refc_read(&bp->val->refc, 1));
}
}
if (printed) {
@@ -662,6 +663,26 @@ bin_check(void)
#endif
+static Sint64 crash_dump_limit = ERTS_SINT64_MAX;
+static Sint64 crash_dump_written = 0;
+
+static int crash_dump_limited_writer(void* vfdp, char* buf, size_t len)
+{
+ const char stop_msg[] = "\n=abort:CRASH DUMP SIZE LIMIT REACHED\n";
+
+ crash_dump_written += len;
+ if (crash_dump_written <= crash_dump_limit) {
+ return erts_write_fd(vfdp, buf, len);
+ }
+
+ len -= (crash_dump_written - crash_dump_limit);
+ erts_write_fd(vfdp, buf, len);
+ erts_write_fd(vfdp, (char*)stop_msg, sizeof(stop_msg)-1);
+
+ /* We assume that crash dump was called from erts_exit_vv() */
+ erts_exit_epilogue();
+}
+
/* XXX THIS SHOULD BE IN SYSTEM !!!! */
void
erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
@@ -679,6 +700,8 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
int secs;
int env_erl_crash_dump_seconds_set = 1;
int i;
+ fmtfn_t to = &erts_write_fd;
+ void* to_arg;
if (ERTS_SOMEONE_IS_CRASH_DUMPING)
return;
@@ -696,6 +719,8 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
* We have to be very very careful when doing this as the schedulers
* could be anywhere.
*/
+ sys_init_suspend_handler();
+
for (i = 0; i < erts_no_schedulers; i++) {
erts_tid_t tid = ERTS_SCHEDULER_IX(i)->tid;
if (!erts_equal_tids(tid,erts_thr_self()))
@@ -759,6 +784,21 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
return;
}
+ crash_dump_limit = ERTS_SINT64_MAX;
+ envsz = sizeof(env);
+ if (erts_sys_getenv__("ERL_CRASH_DUMP_BYTES", env, &envsz) == 0) {
+ Sint64 limit;
+ char* endptr;
+ errno = 0;
+ limit = ErtsStrToSint64(env, &endptr, 10);
+ if (errno == 0 && limit >= 0 && endptr != env && *endptr == 0) {
+ if (limit == 0)
+ return;
+ crash_dump_limit = limit;
+ to = &crash_dump_limited_writer;
+ }
+ }
+
if (erts_sys_getenv__("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0)
dumpname = "erl_crash.dump";
else
@@ -769,39 +809,40 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
fd = open(dumpname,O_WRONLY | O_CREAT | O_TRUNC,0640);
if (fd < 0)
return; /* Can't create the crash dump, skip it */
+ to_arg = (void*)&fd;
time(&now);
- erts_fdprintf(fd, "=erl_crash_dump:0.3\n%s", ctime(&now));
+ erts_cbprintf(to, to_arg, "=erl_crash_dump:0.3\n%s", ctime(&now));
if (file != NULL)
- erts_fdprintf(fd, "The error occurred in file %s, line %d\n", file, line);
+ erts_cbprintf(to, to_arg, "The error occurred in file %s, line %d\n", file, line);
if (fmt != NULL && *fmt != '\0') {
- erts_fdprintf(fd, "Slogan: ");
- erts_vfdprintf(fd, fmt, args);
+ erts_cbprintf(to, to_arg, "Slogan: ");
+ erts_vcbprintf(to, to_arg, fmt, args);
}
- erts_fdprintf(fd, "System version: ");
- erts_print_system_version(fd, NULL, NULL);
+ erts_cbprintf(to, to_arg, "System version: ");
+ erts_print_system_version(to, to_arg, NULL);
#if ERTS_SAVED_COMPILE_TIME
- erts_fdprintf(fd, "%s\n", "Compiled: " ERLANG_COMPILE_DATE);
+ erts_cbprintf(to, to_arg, "%s\n", "Compiled: " ERLANG_COMPILE_DATE);
#endif
- erts_fdprintf(fd, "Taints: ");
- erts_print_nif_taints(fd, NULL);
- erts_fdprintf(fd, "Atoms: %d\n", atom_table_size());
+ erts_cbprintf(to, to_arg, "Taints: ");
+ erts_print_nif_taints(to, to_arg);
+ erts_cbprintf(to, to_arg, "Atoms: %d\n", atom_table_size());
#ifdef USE_THREADS
/* We want to note which thread it was that called erts_exit */
if (erts_get_scheduler_data()) {
- erts_fdprintf(fd, "Calling Thread: scheduler:%d\n",
+ erts_cbprintf(to, to_arg, "Calling Thread: scheduler:%d\n",
erts_get_scheduler_data()->no);
} else {
if (!erts_thr_getname(erts_thr_self(), dumpnamebuf, MAXPATHLEN))
- erts_fdprintf(fd, "Calling Thread: %s\n", dumpnamebuf);
+ erts_cbprintf(to, to_arg, "Calling Thread: %s\n", dumpnamebuf);
else
- erts_fdprintf(fd, "Calling Thread: %p\n", erts_thr_self());
+ erts_cbprintf(to, to_arg, "Calling Thread: %p\n", erts_thr_self());
}
#else
- erts_fdprintf(fd, "Calling Thread: scheduler:1\n");
+ erts_cbprintf(to, to_arg, "Calling Thread: scheduler:1\n");
#endif
#if defined(ERTS_HAVE_TRY_CATCH)
@@ -816,8 +857,8 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
*/
for (i = 0; i < erts_no_schedulers; i++) {
ERTS_SYS_TRY_CATCH(
- erts_print_scheduler_info(fd, NULL, ERTS_SCHEDULER_IX(i)),
- erts_fdprintf(fd, "** crashed **\n"));
+ erts_print_scheduler_info(to, to_arg, ERTS_SCHEDULER_IX(i)),
+ erts_cbprintf(to, to_arg, "** crashed **\n"));
}
#endif
@@ -848,48 +889,39 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
#ifndef ERTS_HAVE_TRY_CATCH
/* This is safe to call here, as all schedulers are blocked */
for (i = 0; i < erts_no_schedulers; i++) {
- erts_print_scheduler_info(fd, NULL, ERTS_SCHEDULER_IX(i));
+ erts_print_scheduler_info(to, to_arg, ERTS_SCHEDULER_IX(i));
}
#endif
- info(fd, NULL); /* General system info */
+ info(to, to_arg); /* General system info */
if (erts_ptab_initialized(&erts_proc))
- process_info(fd, NULL); /* Info about each process and port */
- db_info(fd, NULL, 0);
- erts_print_bif_timer_info(fd, NULL);
- distribution_info(fd, NULL);
- erts_fdprintf(fd, "=loaded_modules\n");
- loaded(fd, NULL);
- erts_dump_fun_entries(fd, NULL);
- erts_deep_process_dump(fd, NULL);
- erts_fdprintf(fd, "=atoms\n");
- dump_atoms(fd, NULL);
+ process_info(to, to_arg); /* Info about each process and port */
+ db_info(to, to_arg, 0);
+ erts_print_bif_timer_info(to, to_arg);
+ distribution_info(to, to_arg);
+ erts_cbprintf(to, to_arg, "=loaded_modules\n");
+ loaded(to, to_arg);
+ erts_dump_fun_entries(to, to_arg);
+ erts_deep_process_dump(to, to_arg);
+ erts_cbprintf(to, to_arg, "=atoms\n");
+ dump_atoms(to, to_arg);
/* Keep the instrumentation data at the end of the dump */
if (erts_instr_memory_map || erts_instr_stat) {
- erts_fdprintf(fd, "=instr_data\n");
+ erts_cbprintf(to, to_arg, "=instr_data\n");
if (erts_instr_stat) {
- erts_fdprintf(fd, "=memory_status\n");
- erts_instr_dump_stat_to_fd(fd, 0);
+ erts_cbprintf(to, to_arg, "=memory_status\n");
+ erts_instr_dump_stat_to(to, to_arg, 0);
}
if (erts_instr_memory_map) {
- erts_fdprintf(fd, "=memory_map\n");
- erts_instr_dump_memory_map_to_fd(fd);
+ erts_cbprintf(to, to_arg, "=memory_map\n");
+ erts_instr_dump_memory_map_to(to, to_arg);
}
}
- erts_fdprintf(fd, "=end\n");
+ erts_cbprintf(to, to_arg, "=end\n");
close(fd);
erts_fprintf(stderr,"done\n");
}
-void
-erl_crash_dump(char* file, int line, char* fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- erl_crash_dump_v(file, line, fmt, args);
- va_end(args);
-}
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index ccc4cbad43..de68adbeb6 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -826,7 +826,7 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
funp = (ErlFunThing *) tp;
funp->next = off_heap->first;
off_heap->first = (struct erl_off_heap_header*) funp;
- erts_refc_inc(&funp->fe->refc, 2);
+ erts_smp_refc_inc(&funp->fe->refc, 2);
*argp = make_fun(tp);
}
break;
@@ -845,7 +845,7 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
etp->next = off_heap->first;
off_heap->first = (struct erl_off_heap_header*)etp;
- erts_refc_inc(&etp->node->refc, 2);
+ erts_smp_refc_inc(&etp->node->refc, 2);
*argp = make_external(tp);
}
@@ -1491,7 +1491,7 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
}
funp->next = off_heap->first;
off_heap->first = (struct erl_off_heap_header*) funp;
- erts_refc_inc(&funp->fe->refc, 2);
+ erts_smp_refc_inc(&funp->fe->refc, 2);
goto cleanup_next;
}
case MAP_SUBTAG:
@@ -1626,7 +1626,7 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
}
etp->next = off_heap->first;
off_heap->first = (struct erl_off_heap_header*) etp;
- erts_refc_inc(&etp->node->refc, 2);
+ erts_smp_refc_inc(&etp->node->refc, 2);
goto cleanup_next;
}
default:
@@ -1802,7 +1802,7 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
case FUN_SUBTAG:
{
ErlFunThing* funp = (ErlFunThing *) (tp-1);
- erts_refc_inc(&funp->fe->refc, 2);
+ erts_smp_refc_inc(&funp->fe->refc, 2);
}
goto off_heap_common;
case EXTERNAL_PID_SUBTAG:
@@ -1810,7 +1810,7 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
case EXTERNAL_REF_SUBTAG:
{
ExternalThing* etp = (ExternalThing *) (tp-1);
- erts_refc_inc(&etp->node->refc, 2);
+ erts_smp_refc_inc(&etp->node->refc, 2);
}
off_heap_common:
{
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 09c83f1117..5566055de0 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -383,22 +383,24 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp)
if (!rp)
goto done;
erts_smp_proc_lock(rp, rp_locks);
- rlnk = erts_remove_link(&ERTS_P_LINKS(rp), name);
- if (rlnk != NULL) {
- ASSERT(is_atom(rlnk->pid) && (rlnk->type == LINK_NODE));
- erts_destroy_link(rlnk);
- }
- n = ERTS_LINK_REFC(lnk);
- for (i = 0; i < n; ++i) {
- Eterm tup;
- Eterm *hp;
- ErtsMessage *msgp;
-
- 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, am_system);
- }
+ if (!ERTS_PROC_IS_EXITING(rp)) {
+ rlnk = erts_remove_link(&ERTS_P_LINKS(rp), name);
+ if (rlnk != NULL) {
+ ASSERT(is_atom(rlnk->pid) && (rlnk->type == LINK_NODE));
+ erts_destroy_link(rlnk);
+ }
+ n = ERTS_LINK_REFC(lnk);
+ for (i = 0; i < n; ++i) {
+ Eterm tup;
+ Eterm *hp;
+ ErtsMessage *msgp;
+
+ 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, am_system);
+ }
+ }
erts_smp_proc_unlock(rp, rp_locks);
}
done:
@@ -2081,7 +2083,7 @@ erts_dist_command(Port *prt, int reds_limit)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- erts_refc_inc(&dep->refc, 1); /* Otherwise dist_entry might be
+ erts_smp_refc_inc(&dep->refc, 1); /* Otherwise dist_entry might be
removed if port command fails */
erts_smp_atomic_set_mb(&dep->dist_cmd_scheduled, 0);
@@ -2402,13 +2404,13 @@ erts_kill_dist_connection(DistEntry *dep, Uint32 connection_id)
}
struct print_to_data {
- int to;
+ fmtfn_t to;
void *arg;
};
static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
{
- int to = ((struct print_to_data *) vptdp)->to;
+ fmtfn_t to = ((struct print_to_data *) vptdp)->to;
void *arg = ((struct print_to_data *) vptdp)->arg;
Process *rp;
ErtsMonitor *rmon;
@@ -2431,7 +2433,7 @@ static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
}
}
-static void print_monitor_info(int to, void *arg, ErtsMonitor *mon)
+static void print_monitor_info(fmtfn_t to, void *arg, ErtsMonitor *mon)
{
struct print_to_data ptd = {to, arg};
erts_doforall_monitors(mon,&doit_print_monitor_info,&ptd);
@@ -2457,7 +2459,7 @@ static void doit_print_link_info(ErtsLink *lnk, void *vptdp)
}
}
-static void print_link_info(int to, void *arg, ErtsLink *lnk)
+static void print_link_info(fmtfn_t to, void *arg, ErtsLink *lnk)
{
struct print_to_data ptd = {to, arg};
erts_doforall_links(lnk, &doit_print_link_info, (void *) &ptd);
@@ -2478,7 +2480,7 @@ static void doit_print_nodelink_info(ErtsLink *lnk, void *vpcontext)
"Remote monitoring: %T %T\n", lnk->pid, pcontext->sysname);
}
-static void print_nodelink_info(int to, void *arg, ErtsLink *lnk, Eterm sysname)
+static void print_nodelink_info(fmtfn_t to, void *arg, ErtsLink *lnk, Eterm sysname)
{
PrintNodeLinkContext context = {{to, arg}, sysname};
erts_doforall_links(lnk, &doit_print_nodelink_info, &context);
@@ -2486,7 +2488,7 @@ static void print_nodelink_info(int to, void *arg, ErtsLink *lnk, Eterm sysname)
static int
-info_dist_entry(int to, void *arg, DistEntry *dep, int visible, int connected)
+info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connected)
{
if (visible && connected) {
@@ -2514,7 +2516,7 @@ info_dist_entry(int to, void *arg, DistEntry *dep, int visible, int connected)
erts_print(to, arg, "Name: %T", dep->sysname);
#ifdef DEBUG
- erts_print(to, arg, " (refc=%d)", erts_refc_read(&dep->refc, 0));
+ erts_print(to, arg, " (refc=%d)", erts_smp_refc_read(&dep->refc, 0));
#endif
erts_print(to, arg, "\n");
if (!connected && is_nil(dep->cid)) {
@@ -2535,7 +2537,7 @@ info_dist_entry(int to, void *arg, DistEntry *dep, int visible, int connected)
return 0;
}
-int distribution_info(int to, void *arg) /* Called by break handler */
+int distribution_info(fmtfn_t to, void *arg) /* Called by break handler */
{
DistEntry *dep;
diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c
index eda3ad870a..4ebe37ee1d 100644
--- a/erts/emulator/beam/erl_afit_alloc.c
+++ b/erts/emulator/beam/erl_afit_alloc.c
@@ -54,7 +54,7 @@ static void link_free_block (Allctr_t *, Block_t *);
static void unlink_free_block (Allctr_t *, Block_t *);
-static Eterm info_options (Allctr_t *, char *, int *,
+static Eterm info_options (Allctr_t *, char *, fmtfn_t *,
void *arg, Uint **, Uint *);
static void init_atoms (void);
@@ -227,7 +227,7 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
static Eterm
info_options(Allctr_t *allctr,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index 3c2c9def3b..214fb1f2af 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -2113,7 +2113,7 @@ add_fix_values(UWord *ap, UWord *up, ErtsAlcUFixInfo_t *fi, ErtsAlcType_t type)
}
Eterm
-erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
+erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
{
/*
* NOTE! When updating this function, make sure to also update
@@ -2476,7 +2476,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
if (print_to_p) {
int i;
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
/* Print result... */
@@ -2530,7 +2530,7 @@ struct aa_values {
};
Eterm
-erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
+erts_allocated_areas(fmtfn_t *print_to_p, void *print_to_arg, void *proc)
{
#define MAX_AA_VALUES (24)
struct aa_values values[MAX_AA_VALUES];
@@ -2665,7 +2665,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
if (print_to_p) {
/* Print result... */
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to, arg, "=allocated_areas\n");
@@ -2779,7 +2779,7 @@ erts_alloc_util_allocators(void *proc)
}
void
-erts_allocator_info(int to, void *arg)
+erts_allocator_info(fmtfn_t to, void *arg)
{
ErtsAlcType_t a;
@@ -3110,7 +3110,7 @@ reply_alloc_info(void *vair)
Eterm (*info_func)(Allctr_t *,
int,
int,
- int *,
+ fmtfn_t *,
void *,
Uint **,
Uint *) = (air->only_sz
diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h
index 925a081a02..56a3b73bf9 100644
--- a/erts/emulator/beam/erl_alloc.h
+++ b/erts/emulator/beam/erl_alloc.h
@@ -69,11 +69,11 @@ void *erts_sys_aligned_realloc(UWord alignment, void *ptr, UWord size, UWord old
void erts_sys_aligned_free(UWord alignment, void *ptr);
#endif
-Eterm erts_memory(int *, void *, void *, Eterm);
-Eterm erts_allocated_areas(int *, void *, void *);
+Eterm erts_memory(fmtfn_t *, void *, void *, Eterm);
+Eterm erts_allocated_areas(fmtfn_t *, void *, void *);
Eterm erts_alloc_util_allocators(void *proc);
-void erts_allocator_info(int, void *);
+void erts_allocator_info(fmtfn_t, void *);
Eterm erts_allocator_options(void *proc);
struct process;
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 227fedfb69..6e8710eb8a 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -279,6 +279,7 @@ type TRACER_NIF LONG_LIVED SYSTEM tracer_nif
type TRACE_MSG_QUEUE SHORT_LIVED SYSTEM trace_message_queue
type SCHED_ASYNC_JOB SHORT_LIVED SYSTEM async_calls
type DIRTY_START STANDARD PROCESSES dirty_start
+type DIRTY_SL SHORT_LIVED SYSTEM dirty_short_lived
+if threads_no_smp
# Need thread safe allocs, but std_alloc and fix_alloc are not;
@@ -313,6 +314,7 @@ type RUNQ_BLNS LONG_LIVED SYSTEM run_queue_balancing
type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data
type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data
type T_THR_PRGR_DATA SHORT_LIVED SYSTEM temp_thr_prgr_data
+type RELEASE_LAREA SHORT_LIVED SYSTEM release_literal_area
+endif
#
@@ -367,6 +369,8 @@ type MONITOR_LH STANDARD PROCESSES monitor_lh
type NLINK_LH STANDARD PROCESSES nlink_lh
type CODE LONG_LIVED CODE code
type LITERAL LITERAL CODE literal
+type LITERAL_REF SHORT_LIVED CODE literal_area_ref
+type PURGE_DATA SHORT_LIVED CODE purge_data
type DB_HEIR_DATA STANDARD ETS db_heir_data
type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc
type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 2995f2f822..230ca6ccbb 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -4153,9 +4153,9 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp)
ASSERT(IS_LAST_BLK(blk));
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- (*allctr->link_free_block)(allctr, blk, 0);
+ (*allctr->link_free_block)(allctr, blk);
HARD_CHECK_BLK_CARRIER(allctr, blk);
- (*allctr->unlink_free_block)(allctr, blk, 0);
+ (*allctr->unlink_free_block)(allctr, blk);
#endif
}
#endif
@@ -4473,7 +4473,7 @@ add_fix_types(Allctr_t *allctr, int internal, Uint **hpp, Uint *szp,
static Eterm
sz_info_fix(Allctr_t *allctr,
int internal,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4494,7 +4494,7 @@ sz_info_fix(Allctr_t *allctr,
UWord used = fix->type_size * fix->u.cpool.used;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to,
arg,
@@ -4522,7 +4522,7 @@ sz_info_fix(Allctr_t *allctr,
UWord used = fix->type_size*fix->u.nocpool.used;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to,
arg,
@@ -4548,7 +4548,7 @@ static Eterm
sz_info_carriers(Allctr_t *allctr,
CarriersStats_t *cs,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4557,7 +4557,7 @@ sz_info_carriers(Allctr_t *allctr,
UWord curr_size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to,
arg,
@@ -4598,7 +4598,7 @@ static Eterm
info_cpool(Allctr_t *allctr,
int sz_only,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4615,7 +4615,7 @@ info_cpool(Allctr_t *allctr,
}
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
if (!sz_only)
erts_print(to, arg, "%sblocks: %bpu\n", prefix, nob);
@@ -4652,7 +4652,7 @@ static Eterm
info_carriers(Allctr_t *allctr,
CarriersStats_t *cs,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4664,7 +4664,7 @@ info_carriers(Allctr_t *allctr,
curr_size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to,
arg,
@@ -4790,7 +4790,7 @@ make_name_atoms(Allctr_t *allctr)
static Eterm
info_calls(Allctr_t *allctr,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -4807,7 +4807,7 @@ info_calls(Allctr_t *allctr,
erts_print(TO, TOA, "%s%s calls: %b64u\n",PRFX,NAME,CC)
char *prefix = allctr->name_prefix;
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
PRINT_CC_5(to, arg, prefix, "alloc", allctr->calls.this_alloc);
@@ -4883,7 +4883,7 @@ info_calls(Allctr_t *allctr,
static Eterm
info_options(Allctr_t *allctr,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -5035,7 +5035,7 @@ reset_max_values(CarriersStats_t *cs)
\* */
Eterm
-erts_alcu_au_info_options(int *print_to_p, void *print_to_arg,
+erts_alcu_au_info_options(fmtfn_t *print_to_p, void *print_to_arg,
Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
@@ -5078,7 +5078,7 @@ erts_alcu_au_info_options(int *print_to_p, void *print_to_arg,
Eterm
erts_alcu_info_options(Allctr_t *allctr,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -5110,7 +5110,7 @@ Eterm
erts_alcu_sz_info(Allctr_t *allctr,
int internal,
int begin_max_period,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -5196,7 +5196,7 @@ Eterm
erts_alcu_info(Allctr_t *allctr,
int internal,
int begin_max_period,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -6440,11 +6440,6 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
ASSERT(SBC2BLK(allctr, sbc) == iblk);
ASSERT(CARRIER_SZ(sbc) - SBC_HEADER_SIZE >= SBC_BLK_SZ(iblk));
-#if HAVE_ERTS_MSEG
- if (IS_MSEG_CARRIER(sbc)) {
- ASSERT(CARRIER_SZ(sbc) % ERTS_SACRR_UNIT_SZ == 0);
- }
-#endif
crr = sbc;
cl = &allctr->sbc_list;
}
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index f50f09907a..81180382af 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -178,10 +178,10 @@ void * erts_alcu_realloc_mv_thr_pref(ErtsAlcType_t, void *, void *, Uint);
void erts_alcu_free_thr_pref(ErtsAlcType_t, void *, void *);
#endif
#endif
-Eterm erts_alcu_au_info_options(int *, void *, Uint **, Uint *);
-Eterm erts_alcu_info_options(Allctr_t *, int *, void *, Uint **, Uint *);
-Eterm erts_alcu_sz_info(Allctr_t *, int, int, int *, void *, Uint **, Uint *);
-Eterm erts_alcu_info(Allctr_t *, int, int, int *, void *, Uint **, Uint *);
+Eterm erts_alcu_au_info_options(fmtfn_t *, void *, Uint **, Uint *);
+Eterm erts_alcu_info_options(Allctr_t *, fmtfn_t *, void *, Uint **, Uint *);
+Eterm erts_alcu_sz_info(Allctr_t *, int, int, fmtfn_t *, void *, Uint **, Uint *);
+Eterm erts_alcu_info(Allctr_t *, int, int, fmtfn_t *, void *, Uint **, Uint *);
void erts_alcu_init(AlcUInit_t *);
void erts_alcu_current_size(Allctr_t *, AllctrSize_t *,
ErtsAlcUFixInfo_t *, int);
@@ -586,7 +586,7 @@ struct Allctr_t_ {
Block_t *, Uint);
void (*link_free_block) (Allctr_t *, Block_t *);
void (*unlink_free_block) (Allctr_t *, Block_t *);
- Eterm (*info_options) (Allctr_t *, char *, int *,
+ Eterm (*info_options) (Allctr_t *, char *, fmtfn_t *,
void *, Uint **, Uint *);
Uint (*get_next_mbc_size) (Allctr_t *);
diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c
index 7e239d1f5d..05ba1f9891 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.c
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c
@@ -224,7 +224,7 @@ static AOFF_RBTree_t* rbt_search(AOFF_RBTree_t* root, Uint size);
static int rbt_assert_is_member(AOFF_RBTree_t* root, AOFF_RBTree_t* node);
#endif
-static Eterm info_options(Allctr_t *, char *, int *, void *, Uint **, Uint *);
+static Eterm info_options(Allctr_t *, char *, fmtfn_t *, void *, Uint **, Uint *);
static void init_atoms(void);
@@ -1014,7 +1014,7 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
static Eterm
info_options(Allctr_t *allctr,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c
index 379cee39a1..6173c408e1 100644
--- a/erts/emulator/beam/erl_bestfit_alloc.c
+++ b/erts/emulator/beam/erl_bestfit_alloc.c
@@ -104,7 +104,7 @@ static void bf_link_free_block (Allctr_t *, Block_t *);
static ERTS_INLINE void bf_unlink_free_block (Allctr_t *, Block_t *);
-static Eterm info_options (Allctr_t *, char *, int *,
+static Eterm info_options (Allctr_t *, char *, fmtfn_t *,
void *, Uint **, Uint *);
static void init_atoms (void);
@@ -921,7 +921,7 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
static Eterm
info_options(Allctr_t *allctr,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index ef77201544..4948975851 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2017. 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.
@@ -1109,25 +1109,25 @@ void erts_ddll_decrement_port_count(DE_Handle *dh)
static void first_ddll_reference(DE_Handle *dh)
{
assert_drv_list_rwlocked();
- erts_refc_init(&(dh->refc),1);
+ erts_smp_refc_init(&(dh->refc),1);
}
void erts_ddll_reference_driver(DE_Handle *dh)
{
assert_drv_list_locked();
- if (erts_refc_inctest(&(dh->refc),1) == 1) {
- erts_refc_inc(&(dh->refc),2); /* add a reference for the scheduled operation */
+ if (erts_smp_refc_inctest(&(dh->refc),1) == 1) {
+ erts_smp_refc_inc(&(dh->refc),2); /* add a reference for the scheduled operation */
}
}
void erts_ddll_reference_referenced_driver(DE_Handle *dh)
{
- erts_refc_inc(&(dh->refc),2);
+ erts_smp_refc_inc(&(dh->refc),2);
}
void erts_ddll_dereference_driver(DE_Handle *dh)
{
- if (erts_refc_dectest(&(dh->refc),0) == 0) {
+ if (erts_smp_refc_dectest(&(dh->refc),0) == 0) {
/* No lock here, but if the driver is referenced again,
the scheduled deletion is added as a reference too, see above */
erts_schedule_misc_op(ddll_no_more_references, (void *) dh);
@@ -1150,11 +1150,11 @@ static void restore_process_references(DE_Handle *dh)
{
DE_ProcEntry *p;
assert_drv_list_rwlocked();
- ASSERT(erts_refc_read(&(dh->refc),0) == 0);
+ ASSERT(erts_smp_refc_read(&(dh->refc),0) == 0);
for(p = dh->procs;p != NULL; p = p->next) {
if (p->awaiting_status == ERL_DE_PROC_LOADED) {
ASSERT(p->flags & ERL_DE_FL_DEREFERENCED);
- erts_refc_inc(&(dh->refc),1);
+ erts_smp_refc_inc(&(dh->refc),1);
p->flags &= ~ERL_DE_FL_DEREFERENCED;
}
}
@@ -1176,9 +1176,9 @@ static void ddll_no_more_references(void *vdh)
lock_drv_list();
- x = erts_refc_read(&(dh->refc),0);
+ x = erts_smp_refc_read(&(dh->refc),0);
if (x > 0) {
- x = erts_refc_dectest(&(dh->refc),0); /* delete the reference added for me */
+ x = erts_smp_refc_dectest(&(dh->refc),0); /* delete the reference added for me */
}
@@ -1643,7 +1643,7 @@ static int load_driver_entry(DE_Handle **dhp, char *path, char *name)
dh->handle = NULL;
dh->procs = NULL;
erts_smp_atomic32_init_nob(&dh->port_count, 0);
- erts_refc_init(&(dh->refc), (erts_aint_t) 0);
+ erts_smp_refc_init(&(dh->refc), (erts_aint_t) 0);
dh->status = -1;
dh->reload_full_path = NULL;
dh->reload_driver_name = NULL;
@@ -1681,7 +1681,7 @@ static int reload_driver_entry(DE_Handle *dh)
dh->reload_full_path = NULL;
dh->reload_driver_name = NULL;
- ASSERT(erts_refc_read(&(dh->refc),0) == 0);
+ ASSERT(erts_smp_refc_read(&(dh->refc),0) == 0);
ASSERT(dh->full_path != NULL);
erts_free(ERTS_ALC_T_DDLL_HANDLE, (void *) dh->full_path);
dh->full_path = NULL;
diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c
index b42d2dc28b..ea508bd1c4 100644
--- a/erts/emulator/beam/erl_bif_guard.c
+++ b/erts/emulator/beam/erl_bif_guard.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2017. 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.
@@ -157,7 +157,7 @@ BIF_RETTYPE round_1(BIF_ALIST_1)
GET_DOUBLE(BIF_ARG_1, f);
/* round it and return the resultant integer */
- res = double_to_integer(BIF_P, (f.fd > 0.0) ? f.fd + 0.5 : f.fd - 0.5);
+ res = double_to_integer(BIF_P, round(f.fd));
BIF_RET(res);
}
@@ -597,8 +597,7 @@ Eterm erts_gc_round_1(Process* p, Eterm* reg, Uint live)
}
GET_DOUBLE(arg, f);
- return gc_double_to_integer(p, (f.fd > 0.0) ? f.fd + 0.5 : f.fd - 0.5,
- reg, live);
+ return gc_double_to_integer(p, round(f.fd), reg, live);
}
Eterm erts_gc_trunc_1(Process* p, Eterm* reg, Uint live)
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 3fb866733c..7791231d56 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2017. 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.
@@ -174,7 +174,7 @@ bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh)
if (szp)
*szp += 4+2;
if (hpp) {
- Uint refc = (Uint) erts_smp_atomic_read_nob(&pb->val->refc);
+ Uint refc = (Uint) erts_refc_read(&pb->val->refc, 1);
tuple = TUPLE3(*hpp, val, orig_size, make_small(refc));
res = CONS(*hpp + 4, tuple, res);
*hpp += 4+2;
@@ -317,7 +317,7 @@ make_link_list(Process *p, ErtsLink *root, Eterm tail)
}
int
-erts_print_system_version(int to, void *arg, Process *c_p)
+erts_print_system_version(fmtfn_t to, void *arg, Process *c_p)
{
int i, rc = -1;
char *rc_str = "";
@@ -1672,11 +1672,11 @@ current_stacktrace(Process* p, Process* rp, Eterm** hpp)
Eterm mfa;
Eterm res = NIL;
- depth = 8;
+ depth = erts_backtrace_depth;
sz = offsetof(struct StackTrace, trace) + sizeof(BeamInstr *)*depth;
s = (struct StackTrace *) erts_alloc(ERTS_ALC_T_TMP, sz);
s->depth = 0;
- if (rp->i) {
+ if (depth > 0 && rp->i) {
s->trace[s->depth++] = rp->i;
depth--;
}
@@ -2284,9 +2284,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
} else if (ERTS_IS_ATOM_STR("dist_ctrl", BIF_ARG_1)) {
DistEntry *dep;
i = 0;
- /* Need to be the only thread running... */
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx);
for (dep = erts_visible_dist_entries; dep; dep = dep->next)
++i;
for (dep = erts_hidden_dist_entries; dep; dep = dep->next)
@@ -2309,8 +2307,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
res = CONS(hp, tpl, res);
hp += 2;
}
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx);
BIF_RET(res);
} else if (BIF_ARG_1 == am_system_version) {
erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
@@ -2886,6 +2883,27 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_RET(AM_tag);
#endif
}
+ else if (ERTS_IS_ATOM_STR("check_process_code",BIF_ARG_1)) {
+ Eterm terms[3];
+ Sint length = 1;
+ Uint sz = 0;
+ Eterm *hp, res;
+ DECL_AM(direct_references);
+
+ terms[0] = AM_direct_references;
+#if !defined(ERTS_NEW_PURGE_STRATEGY)
+ {
+ DECL_AM(indirect_references);
+ terms[1] = AM_indirect_references;
+ terms[2] = am_copy_literals;
+ length = 3;
+ }
+#endif
+ erts_bld_list(NULL, &sz, length, terms);
+ hp = HAlloc(BIF_P, sz);
+ res = erts_bld_list(&hp, NULL, length, terms);
+ BIF_RET(res);
+ }
BIF_ERROR(BIF_P, BADARG);
}
@@ -3450,24 +3468,32 @@ BIF_RETTYPE statistics_1(BIF_ALIST_1)
res = TUPLE2(hp, b1, b2);
BIF_RET(res);
} else if (BIF_ARG_1 == am_runtime) {
- UWord u1, u2, dummy;
+ ErtsMonotonicTime u1, u2;
Eterm b1, b2;
- elapsed_time_both(&u1,&dummy,&u2,&dummy);
- b1 = erts_make_integer(u1,BIF_P);
- b2 = erts_make_integer(u2,BIF_P);
- hp = HAlloc(BIF_P,3);
+ Uint hsz;
+ elapsed_time_both(&u1, NULL, &u2, NULL);
+ hsz = 3; /* 2-tuple */
+ (void) erts_bld_monotonic_time(NULL, &hsz, u1);
+ (void) erts_bld_monotonic_time(NULL, &hsz, u2);
+ hp = HAlloc(BIF_P, hsz);
+ b1 = erts_bld_monotonic_time(&hp, NULL, u1);
+ b2 = erts_bld_monotonic_time(&hp, NULL, u2);
res = TUPLE2(hp, b1, b2);
BIF_RET(res);
} else if (BIF_ARG_1 == am_run_queue) {
res = erts_run_queues_len(NULL, 1, 0);
BIF_RET(make_small(res));
} else if (BIF_ARG_1 == am_wall_clock) {
- UWord w1, w2;
+ ErtsMonotonicTime w1, w2;
Eterm b1, b2;
+ Uint hsz;
wall_clock_elapsed_time_both(&w1, &w2);
- b1 = erts_make_integer((Uint) w1,BIF_P);
- b2 = erts_make_integer((Uint) w2,BIF_P);
- hp = HAlloc(BIF_P,3);
+ hsz = 3; /* 2-tuple */
+ (void) erts_bld_monotonic_time(NULL, &hsz, w1);
+ (void) erts_bld_monotonic_time(NULL, &hsz, w2);
+ hp = HAlloc(BIF_P, hsz);
+ b1 = erts_bld_monotonic_time(&hp, NULL, w1);
+ b2 = erts_bld_monotonic_time(&hp, NULL, w2);
res = TUPLE2(hp, b1, b2);
BIF_RET(res);
} else if (BIF_ARG_1 == am_io) {
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 66e5146da0..4fed09f085 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2017. 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.
@@ -512,7 +512,7 @@ start_trace(Process *c_p, ErtsTracer tracer,
&& !ERTS_TRACER_COMPARE(ERTS_TRACER(port), tracer)) {
/* This tracee is already being traced, and not by the
* tracer to be */
- if (erts_is_tracer_enabled(tracer, common)) {
+ if (erts_is_tracer_enabled(ERTS_TRACER(port), common)) {
/* The tracer is still in use */
return 1;
}
@@ -715,8 +715,8 @@ Eterm erts_internal_trace_3(BIF_ALIST_3)
Process* tracee_p = erts_pix2proc(i);
if (! tracee_p)
continue;
- start_trace(p, tracer, &tracee_p->common, on, mask);
- matches++;
+ if (!start_trace(p, tracer, &tracee_p->common, on, mask))
+ matches++;
}
}
if (ports || mods) {
@@ -730,8 +730,8 @@ Eterm erts_internal_trace_3(BIF_ALIST_3)
state = erts_atomic32_read_nob(&tracee_port->state);
if (state & ERTS_PORT_SFLGS_DEAD)
continue;
- start_trace(p, tracer, &tracee_port->common, on, mask);
- matches++;
+ if (!start_trace(p, tracer, &tracee_port->common, on, mask))
+ matches++;
}
}
}
@@ -1062,9 +1062,16 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
erts_smp_thr_progress_block();
}
#endif
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_smp_mtx_lock(&erts_dirty_bp_ix_mtx);
+#endif
+
r = function_is_traced(p, mfa, &ms, &ms_meta, &meta, &count, &call_time);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_smp_mtx_unlock(&erts_dirty_bp_ix_mtx);
+#endif
#ifdef ERTS_SMP
if ( (key == am_call_time) || (key == am_all)) {
erts_smp_thr_progress_unblock();
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index bad34211a5..23c8a5bae9 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -201,7 +201,7 @@ static int free_table_cont(Process *p,
DbTable *tb,
int first,
int clean_meta_tab);
-static void print_table(int to, void *to_arg, int show, DbTable* tb);
+static void print_table(fmtfn_t to, void *to_arg, int show, DbTable* tb);
static BIF_RETTYPE ets_select_delete_1(BIF_ALIST_1);
static BIF_RETTYPE ets_select_count_1(BIF_ALIST_1);
static BIF_RETTYPE ets_select_trap_1(BIF_ALIST_1);
@@ -266,7 +266,7 @@ static void schedule_free_dbtable(DbTable* tb)
* Caller is *not* allowed to access the specialized part
* (hash or tree) of *tb after this function has returned.
*/
- ASSERT(erts_refc_read(&tb->common.ref, 0) == 0);
+ ASSERT(erts_smp_refc_read(&tb->common.ref, 0) == 0);
erts_schedule_thr_prgr_later_cleanup_op(free_dbtable,
(void *) tb,
&tb->release.data,
@@ -600,11 +600,11 @@ done:
*/
static ERTS_INLINE void local_fix_table(DbTable* tb)
{
- erts_refc_inc(&tb->common.ref, 1);
+ erts_smp_refc_inc(&tb->common.ref, 1);
}
static ERTS_INLINE void local_unfix_table(DbTable* tb)
{
- if (erts_refc_dectest(&tb->common.ref, 0) == 0) {
+ if (erts_smp_refc_dectest(&tb->common.ref, 0) == 0) {
ASSERT(IS_HASH_TABLE(tb->common.status));
db_unfix_table_hash(&(tb->hash));
}
@@ -1094,7 +1094,7 @@ BIF_RETTYPE ets_insert_2(BIF_ALIST_2)
CHECK_TABLES();
- /* Write lock table if more than one object to keep atomicy */
+ /* Write lock table if more than one object to keep atomicity */
kind = ((is_list(BIF_ARG_2) && CDR(list_val(BIF_ARG_2)) != NIL)
? LCK_WRITE : LCK_WRITE_REC);
@@ -1164,7 +1164,7 @@ BIF_RETTYPE ets_insert_new_2(BIF_ALIST_2)
Eterm lookup_ret;
DbTableMethod* meth;
- /* More than one object, use LCK_WRITE to keep atomicy */
+ /* More than one object, use LCK_WRITE to keep atomicity */
kind = LCK_WRITE;
tb = db_get_table(BIF_P, BIF_ARG_1, DB_WRITE, kind);
if (tb == NULL) {
@@ -1487,7 +1487,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
tb->common.type = status & ERTS_ETS_TABLE_TYPES;
/* Note, 'type' is *read only* from now on... */
#endif
- erts_refc_init(&tb->common.ref, 0);
+ erts_smp_refc_init(&tb->common.ref, 0);
db_init_lock(tb, status & (DB_FINE_LOCKED|DB_FREQ_READ),
"db_tab", "db_tab_fix");
tb->common.keypos = keypos;
@@ -2990,7 +2990,7 @@ void init_db(ErtsDbSpinCount db_spin_count)
meta_pid_to_tab->common.meth = &db_hash;
meta_pid_to_tab->common.compress = 0;
- erts_refc_init(&meta_pid_to_tab->common.ref, 0);
+ erts_smp_refc_init(&meta_pid_to_tab->common.ref, 0);
/* Neither rwlock or fixlock used
db_init_lock(meta_pid_to_tab, "meta_pid_to_tab", "meta_pid_to_tab_FIX");*/
@@ -3021,7 +3021,7 @@ void init_db(ErtsDbSpinCount db_spin_count)
meta_pid_to_fixed_tab->common.meth = &db_hash;
meta_pid_to_fixed_tab->common.compress = 0;
- erts_refc_init(&meta_pid_to_fixed_tab->common.ref, 0);
+ erts_smp_refc_init(&meta_pid_to_fixed_tab->common.ref, 0);
/* Neither rwlock or fixlock used
db_init_lock(meta_pid_to_fixed_tab, "meta_pid_to_fixed_tab", "meta_pid_to_fixed_tab_FIX");*/
@@ -3382,7 +3382,7 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
if ((*pp)->pid == pid) {
DbFixation* fix = *pp;
erts_aint_t diff = -((erts_aint_t) fix->counter);
- erts_refc_add(&tb->common.ref,diff,0);
+ erts_smp_refc_add(&tb->common.ref,diff,0);
*pp = fix->next;
erts_db_free(ERTS_ALC_T_DB_FIXATION,
tb, fix, sizeof(DbFixation));
@@ -3458,7 +3458,7 @@ static void fix_table_locked(Process* p, DbTable* tb)
#ifdef ERTS_SMP
erts_smp_mtx_lock(&tb->common.fixlock);
#endif
- erts_refc_inc(&tb->common.ref,1);
+ erts_smp_refc_inc(&tb->common.ref,1);
fix = tb->common.fixations;
if (fix == NULL) {
tb->common.time.monotonic
@@ -3514,7 +3514,7 @@ static void unfix_table_locked(Process* p, DbTable* tb,
for (pp = &tb->common.fixations; *pp != NULL; pp = &(*pp)->next) {
if ((*pp)->pid == p->common.id) {
DbFixation* fix = *pp;
- erts_refc_dec(&tb->common.ref,0);
+ erts_smp_refc_dec(&tb->common.ref,0);
--(fix->counter);
ASSERT(fix->counter >= 0);
if (fix->counter > 0) {
@@ -3563,7 +3563,7 @@ static void free_fixations_locked(DbTable *tb)
fix = tb->common.fixations;
while (fix != NULL) {
erts_aint_t diff = -((erts_aint_t) fix->counter);
- erts_refc_add(&tb->common.ref,diff,0);
+ erts_smp_refc_add(&tb->common.ref,diff,0);
next_fix = fix->next;
db_meta_lock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
db_erase_bag_exact2(meta_pid_to_fixed_tab,
@@ -3871,7 +3871,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
return ret;
}
-static void print_table(int to, void *to_arg, int show, DbTable* tb)
+static void print_table(fmtfn_t to, void *to_arg, int show, DbTable* tb)
{
erts_print(to, to_arg, "Table: %T\n", tb->common.id);
erts_print(to, to_arg, "Name: %T\n", tb->common.the_name);
@@ -3891,7 +3891,7 @@ static void print_table(int to, void *to_arg, int show, DbTable* tb)
erts_print(to, to_arg, "Read Concurrency: %T\n", table_info(NULL, tb, am_read_concurrency));
}
-void db_info(int to, void *to_arg, int show) /* Called by break handler */
+void db_info(fmtfn_t to, void *to_arg, int show) /* Called by break handler */
{
int i;
for (i=0; i < db_max_tabs; i++)
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index 1d26c49652..b0508f2e74 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -74,7 +74,7 @@ typedef enum {
void init_db(ErtsDbSpinCount);
int erts_db_process_exiting(Process *, ErtsProcLocks);
-void db_info(int, void *, int);
+void db_info(fmtfn_t, void *, int);
void erts_db_foreach_table(void (*)(DbTable *, void *), void *);
void erts_db_foreach_offheap(DbTable *,
void (*func)(ErlOffHeap *, void *),
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index 74979f984a..390369fdb9 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -40,7 +40,7 @@
** DB_FINE_LOCKED set. The table variable is_thread_safe will then indicate
** if operations need to obtain fine grained locks or not. Some operations
** will for example always use exclusive table lock to guarantee
-** a higher level of atomicy.
+** a higher level of atomicity.
*/
/* FIXATION:
@@ -437,7 +437,7 @@ static int db_select_count_continue_hash(Process *p, DbTable *tbl,
static int db_select_delete_continue_hash(Process *p, DbTable *tbl,
Eterm continuation, Eterm *ret);
static int db_take_hash(Process *, DbTable *, Eterm, Eterm *);
-static void db_print_hash(int to,
+static void db_print_hash(fmtfn_t to,
void *to_arg,
int show,
DbTable *tbl);
@@ -2155,7 +2155,7 @@ int db_mark_all_deleted_hash(DbTable *tbl)
/* Display hash table contents (for dump) */
-static void db_print_hash(int to, void *to_arg, int show, DbTable *tbl)
+static void db_print_hash(fmtfn_t to, void *to_arg, int show, DbTable *tbl)
{
DbTableHash *tb = &tbl->hash;
DbHashStats stats;
@@ -2866,15 +2866,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
q->hvalue = hval;
q->next = NULL;
*bp = b = q;
-
- {
- int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems);
- int nactive = NACTIVE(tb);
-
- if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) {
- grow(tb, nactive);
- }
- }
+ flags |= DB_INC_TRY_GROW;
} else {
HashDbTerm *q, *next = b->next;
@@ -2910,6 +2902,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
HashDbTerm **bp = (HashDbTerm **) handle->bp;
HashDbTerm *b = *bp;
erts_smp_rwmtx_t* lck = (erts_smp_rwmtx_t*) handle->lck;
+ HashDbTerm* free_me = NULL;
ERTS_SMP_LC_ASSERT(IS_HASH_WLOCKED(tb, lck)); /* locked by db_lookup_dbterm_hash */
@@ -2921,21 +2914,34 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
b->hvalue = INVALID_HASH;
} else {
*bp = b->next;
- free_term(tb, b);
+ free_me = b;
}
WUNLOCK_HASH(lck);
erts_smp_atomic_dec_nob(&tb->common.nitems);
try_shrink(tb);
- } else if (handle->flags & DB_MUST_RESIZE) {
- db_finalize_resize(handle, offsetof(HashDbTerm,dbterm));
- WUNLOCK_HASH(lck);
-
- free_term(tb, b);
- }
- else {
- WUNLOCK_HASH(lck);
+ } else {
+ if (handle->flags & DB_MUST_RESIZE) {
+ db_finalize_resize(handle, offsetof(HashDbTerm,dbterm));
+ free_me = b;
+ }
+ if (handle->flags & DB_INC_TRY_GROW) {
+ int nactive;
+ int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems);
+ WUNLOCK_HASH(lck);
+ nactive = NACTIVE(tb);
+
+ if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) {
+ grow(tb, nactive);
+ }
+ } else {
+ WUNLOCK_HASH(lck);
+ }
}
+
+ if (free_me)
+ free_term(tb, free_me);
+
#ifdef DEBUG
handle->dbterm = 0;
#endif
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 02d211a4bb..dd9403e132 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -385,7 +385,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl,
static int db_select_delete_continue_tree(Process *p, DbTable *tbl,
Eterm continuation, Eterm *ret);
static int db_take_tree(Process *, DbTable *, Eterm, Eterm *);
-static void db_print_tree(int to, void *to_arg,
+static void db_print_tree(fmtfn_t to, void *to_arg,
int show, DbTable *tbl);
static int db_free_table_tree(DbTable *tbl);
@@ -1740,7 +1740,7 @@ static int db_take_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
/* Display tree contents (for dump) */
-static void db_print_tree(int to, void *to_arg,
+static void db_print_tree(fmtfn_t to, void *to_arg,
int show,
DbTable *tbl)
{
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 6732b708a8..3c5bfb2552 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2017. 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.
@@ -3107,7 +3107,7 @@ void db_cleanup_offheap_comp(DbTerm* obj)
break;
case FUN_SUBTAG:
ASSERT(u.pb != &tmp);
- if (erts_refc_dectest(&u.fun->fe->refc, 0) == 0) {
+ if (erts_smp_refc_dectest(&u.fun->fe->refc, 0) == 0) {
erts_erase_fun_entry(u.fun->fe);
}
break;
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 60f7067d70..b30f0580dd 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2017. 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.
@@ -79,6 +79,7 @@ typedef union db_table DbTable;
#define DB_MUST_RESIZE 1
#define DB_NEW_OBJECT 2
+#define DB_INC_TRY_GROW 4
/* Info about a database entry while it's being updated
* (by update_counter or update_element)
@@ -174,7 +175,7 @@ typedef struct db_table_method
int (*db_free_table)(DbTable* db /* [in out] */ );
int (*db_free_table_continue)(DbTable* db); /* [in out] */
- void (*db_print)(int to,
+ void (*db_print)(fmtfn_t to,
void* to_arg,
int show,
DbTable* tb /* [in out] */ );
@@ -210,7 +211,7 @@ typedef struct db_fixation {
*/
typedef struct db_table_common {
- erts_refc_t ref; /* fixation counter */
+ erts_smp_refc_t ref; /* fixation counter */
#ifdef ERTS_SMP
erts_smp_rwmtx_t rwlock; /* rw lock on table */
erts_smp_mtx_t fixlock; /* Protects fixations,megasec,sec,microsec */
@@ -259,7 +260,7 @@ typedef struct db_table_common {
(DB_BAG | DB_SET | DB_DUPLICATE_BAG)))
#define IS_TREE_TABLE(Status) (!!((Status) & \
DB_ORDERED_SET))
-#define NFIXED(T) (erts_refc_read(&(T)->common.ref,0))
+#define NFIXED(T) (erts_smp_refc_read(&(T)->common.ref,0))
#define IS_FIXED(T) (NFIXED(T) != 0)
/*
diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c
index 3e3bfa03a2..3526bb684d 100644
--- a/erts/emulator/beam/erl_debug.c
+++ b/erts/emulator/beam/erl_debug.c
@@ -60,10 +60,10 @@ static const char dashes[PTR_SIZE+3] = {
void pps(Process*, Eterm*);
void ptd(Process*, Eterm);
-void paranoid_display(int, void*, Process*, Eterm);
+void paranoid_display(fmtfn_t, void*, Process*, Eterm);
static int dcount;
-static int pdisplay1(int to, void *to_arg, Process* p, Eterm obj);
+static int pdisplay1(fmtfn_t to, void *to_arg, Process* p, Eterm obj);
void ptd(Process* p, Eterm x)
{
@@ -77,14 +77,14 @@ void ptd(Process* p, Eterm x)
*/
void
-paranoid_display(int to, void *to_arg, Process* p, Eterm obj)
+paranoid_display(fmtfn_t to, void *to_arg, Process* p, Eterm obj)
{
dcount = 100000;
pdisplay1(to, to_arg, p, obj);
}
static int
-pdisplay1(int to, void *to_arg, Process* p, Eterm obj)
+pdisplay1(fmtfn_t to, void *to_arg, Process* p, Eterm obj)
{
int i, k;
Eterm* nobj;
@@ -201,7 +201,7 @@ pdisplay1(int to, void *to_arg, Process* p, Eterm obj)
void
pps(Process* p, Eterm* stop)
{
- int to = ERTS_PRINT_STDOUT;
+ fmtfn_t to = ERTS_PRINT_STDOUT;
void *to_arg = NULL;
Eterm* sp = STACK_START(p) - 1;
diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c
index 6ce1376c81..53a1801b72 100644
--- a/erts/emulator/beam/erl_fun.c
+++ b/erts/emulator/beam/erl_fun.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2017. 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.
@@ -74,7 +74,7 @@ erts_init_fun_table(void)
}
void
-erts_fun_info(int to, void *to_arg)
+erts_fun_info(fmtfn_t to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
@@ -110,9 +110,9 @@ erts_put_fun_entry(Eterm mod, int uniq, int index)
fe = (ErlFunEntry *) hash_put(&erts_fun_table, (void*) &template);
sys_memset(fe->uniq, 0, sizeof(fe->uniq));
fe->index = 0;
- refc = erts_refc_inctest(&fe->refc, 0);
+ refc = erts_smp_refc_inctest(&fe->refc, 0);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&fe->refc, 1);
+ erts_smp_refc_inc(&fe->refc, 1);
erts_fun_write_unlock();
return fe;
}
@@ -134,9 +134,9 @@ erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
sys_memcpy(fe->uniq, uniq, sizeof(fe->uniq));
fe->index = index;
fe->arity = arity;
- refc = erts_refc_inctest(&fe->refc, 0);
+ refc = erts_smp_refc_inctest(&fe->refc, 0);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&fe->refc, 1);
+ erts_smp_refc_inc(&fe->refc, 1);
erts_fun_write_unlock();
return fe;
}
@@ -161,9 +161,9 @@ erts_get_fun_entry(Eterm mod, int uniq, int index)
erts_fun_read_lock();
ret = (ErlFunEntry *) hash_get(&erts_fun_table, (void*) &template);
if (ret) {
- erts_aint_t refc = erts_refc_inctest(&ret->refc, 1);
+ erts_aint_t refc = erts_smp_refc_inctest(&ret->refc, 1);
if (refc < 2) /* Pending delete */
- erts_refc_inc(&ret->refc, 1);
+ erts_smp_refc_inc(&ret->refc, 1);
}
erts_fun_read_unlock();
return ret;
@@ -184,7 +184,7 @@ erts_erase_fun_entry(ErlFunEntry* fe)
* We have to check refc again since someone might have looked up
* the fun entry and incremented refc after last check.
*/
- if (erts_refc_dectest(&fe->refc, -1) <= 0)
+ if (erts_smp_refc_dectest(&fe->refc, -1) <= 0)
#endif
{
if (fe->address != unloaded_fun)
@@ -199,14 +199,13 @@ erts_erase_fun_entry(ErlFunEntry* fe)
}
void
-erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end)
+erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end)
{
int limit;
HashBucket** bucket;
- ErlFunEntry* to_delete = NULL;
int i;
- erts_fun_write_lock();
+ erts_fun_read_lock();
limit = erts_fun_table.size;
bucket = erts_fun_table.bucket;
for (i = 0; i < limit; i++) {
@@ -217,26 +216,54 @@ erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end)
BeamInstr* addr = fe->address;
if (start <= addr && addr < end) {
+ fe->pend_purge_address = addr;
+ ERTS_SMP_WRITE_MEMORY_BARRIER;
fe->address = unloaded_fun;
- if (erts_refc_dectest(&fe->refc, 0) == 0) {
- fe->address = (void *) to_delete;
- to_delete = fe;
- }
+ erts_purge_state_add_fun(fe);
}
b = b->next;
}
}
+ erts_fun_read_unlock();
+}
+
+void
+erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no)
+{
+ Uint ix;
- while (to_delete != NULL) {
- ErlFunEntry* next = (ErlFunEntry *) to_delete->address;
- erts_erase_fun_entry_unlocked(to_delete);
- to_delete = next;
+ for (ix = 0; ix < no; ix++) {
+ ErlFunEntry *fe = funs[ix];
+ if (fe->address == unloaded_fun)
+ fe->address = fe->pend_purge_address;
}
- erts_fun_write_unlock();
}
void
-erts_dump_fun_entries(int to, void *to_arg)
+erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no)
+{
+ Uint ix;
+
+ for (ix = 0; ix < no; ix++)
+ funs[ix]->pend_purge_address = NULL;
+}
+
+void
+erts_fun_purge_complete(ErlFunEntry **funs, Uint no)
+{
+ Uint ix;
+
+ for (ix = 0; ix < no; ix++) {
+ ErlFunEntry *fe = funs[ix];
+ fe->pend_purge_address = NULL;
+ if (erts_smp_refc_dectest(&fe->refc, 0) == 0)
+ erts_erase_fun_entry(fe);
+ }
+ ERTS_SMP_WRITE_MEMORY_BARRIER;
+}
+
+void
+erts_dump_fun_entries(fmtfn_t to, void *to_arg)
{
int limit;
HashBucket** bucket;
@@ -261,7 +288,7 @@ erts_dump_fun_entries(int to, void *to_arg)
#ifdef HIPE
erts_print(to, to_arg, "Native_address: %p\n", fe->native_address);
#endif
- erts_print(to, to_arg, "Refc: %ld\n", erts_refc_read(&fe->refc, 1));
+ erts_print(to, to_arg, "Refc: %ld\n", erts_smp_refc_read(&fe->refc, 1));
b = b->next;
}
}
@@ -292,8 +319,9 @@ fun_alloc(ErlFunEntry* template)
obj->old_uniq = template->old_uniq;
obj->old_index = template->old_index;
obj->module = template->module;
- erts_refc_init(&obj->refc, -1);
+ erts_smp_refc_init(&obj->refc, -1);
obj->address = unloaded_fun;
+ obj->pend_purge_address = NULL;
#ifdef HIPE
obj->native_address = NULL;
#endif
diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h
index 8c4deea7a0..b1d5cddb34 100644
--- a/erts/emulator/beam/erl_fun.h
+++ b/erts/emulator/beam/erl_fun.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2017. 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.
@@ -42,8 +42,9 @@ typedef struct erl_fun_entry {
Uint arity; /* The arity of the fun. */
Eterm module; /* Tagged atom for module. */
- erts_refc_t refc; /* Reference count: One for code + one for each
+ erts_smp_refc_t refc; /* Reference count: One for code + one for each
fun object in each process. */
+ BeamInstr *pend_purge_address; /* address stored during a pending purge */
} ErlFunEntry;
/*
@@ -70,7 +71,7 @@ typedef struct erl_fun_thing {
#define ERL_FUN_SIZE ((sizeof(ErlFunThing)/sizeof(Eterm))-1)
void erts_init_fun_table(void);
-void erts_fun_info(int, void *);
+void erts_fun_info(fmtfn_t, void *);
int erts_fun_table_sz(void);
ErlFunEntry* erts_put_fun_entry(Eterm mod, int uniq, int index);
@@ -81,7 +82,10 @@ ErlFunEntry* erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
void erts_erase_fun_entry(ErlFunEntry* fe);
void erts_cleanup_funs(ErlFunThing* funp);
-void erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end);
-void erts_dump_fun_entries(int, void *);
+void erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end);
+void erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no);
+void erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no);
+void erts_fun_purge_complete(ErlFunEntry **funs, Uint no);
+void erts_dump_fun_entries(fmtfn_t, void *);
#endif
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index d0d74bbf44..d2560cfb49 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2017. 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.
@@ -59,6 +59,10 @@
# define ERTS_GC_ASSERT(B) ((void) 1)
#endif
+#if defined(DEBUG) && 0
+# define HARDDEBUG 1
+#endif
+
/*
* Returns number of elements in an array.
*/
@@ -110,7 +114,6 @@ typedef struct {
static Uint setup_rootset(Process*, Eterm*, int, Rootset*);
static void cleanup_rootset(Rootset *rootset);
-static void remove_message_buffers(Process* p);
static Eterm *full_sweep_heaps(Process *p,
int hibernate,
Eterm *n_heap, Eterm* n_htop,
@@ -150,9 +153,10 @@ 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);
+static Uint64 next_vheap_size(Process* p, Uint64 vheap, Uint64 vheap_sz);
#ifdef HARDDEBUG
-static void disallow_heap_frag_ref_in_heap(Process* p);
+static void disallow_heap_frag_ref_in_heap(Process *p, Eterm *heap, Eterm *htop);
static void disallow_heap_frag_ref_in_old_heap(Process* p);
#endif
@@ -175,6 +179,13 @@ typedef struct {
erts_smp_atomic32_t refc;
} ErtsGCInfoReq;
+#ifdef ERTS_DIRTY_SCHEDULERS
+static struct {
+ erts_mtx_t mtx;
+ ErtsGCInfo info;
+} dirty_gc;
+#endif
+
static ERTS_INLINE int
gc_cost(Uint gc_moved_live_words, Uint resize_moved_words)
{
@@ -258,6 +269,11 @@ erts_init_gc(void)
init_gc_info(&esdp->gc_info);
}
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_smp_mtx_init(&dirty_gc.mtx, "dirty_gc_info");
+ init_gc_info(&dirty_gc.info);
+#endif
+
init_gcireq_alloc();
}
@@ -387,6 +403,11 @@ erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end,
return result;
}
+ if (!p->mbuf) {
+ /* Must have GC:d in BIF call... invalidate live_hf_end */
+ live_hf_end = ERTS_INVALID_HFRAG_PTR;
+ }
+
if (is_non_value(result)) {
if (p->freason == TRAP) {
#if HIPE
@@ -471,24 +492,26 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need, int
hsz = ssz + need + ERTS_DELAY_GC_EXTRA_FREE;
hfrag = new_message_buffer(hsz);
- hfrag->next = p->mbuf;
- p->mbuf = hfrag;
- p->mbuf_sz += hsz;
p->heap = p->htop = &hfrag->mem[0];
p->hend = hend = &hfrag->mem[hsz];
p->stop = stop = hend - ssz;
sys_memcpy((void *) stop, (void *) orig_stop, ssz * sizeof(Eterm));
if (p->abandoned_heap) {
- /* Active heap already in a fragment; adjust it... */
- ErlHeapFragment *hfrag = ((ErlHeapFragment *)
- (((char *) orig_heap)
- - offsetof(ErlHeapFragment, mem)));
- Uint unused = orig_hend - orig_htop;
- ASSERT(hfrag->used_size == hfrag->alloc_size);
- ASSERT(hfrag->used_size >= unused);
- hfrag->used_size -= unused;
- p->mbuf_sz -= unused;
+ /*
+ * Active heap already in a fragment; adjust it and
+ * save it into mbuf list...
+ */
+ ErlHeapFragment *hfrag = ((ErlHeapFragment *)
+ (((char *) orig_heap)
+ - offsetof(ErlHeapFragment, mem)));
+ Uint used = orig_htop - orig_heap;
+ hfrag->used_size = used;
+ p->mbuf_sz += used;
+ ASSERT(hfrag->used_size <= hfrag->alloc_size);
+ ASSERT(!hfrag->off_heap.first && !hfrag->off_heap.overhead);
+ hfrag->next = p->mbuf;
+ p->mbuf = hfrag;
}
else {
/* Do not leave a hole in the abandoned heap... */
@@ -517,6 +540,8 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need, int
erts_proc_sched_data((p))->virtual_reds += vreds;
}
+ ERTS_CHK_MBUF_SZ(p);
+
ASSERT(CONTEXT_REDS >= erts_proc_sched_data(p)->virtual_reds);
return reds_left;
}
@@ -527,26 +552,35 @@ young_gen_usage(Process *p)
Uint hsz;
Eterm *aheap;
+ ERTS_CHK_MBUF_SZ(p);
+
hsz = p->mbuf_sz;
if (p->flags & F_ON_HEAP_MSGQ) {
ErtsMessage *mp;
- for (mp = p->msg.first; mp; mp = mp->next)
+ for (mp = p->msg.first; mp; mp = mp->next) {
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding. However, we use their estimated
+ * size when calculating need, and by this making
+ * it more likely that they will fit on the heap
+ * when actually decoded.
+ */
if (mp->data.attached)
hsz += erts_msg_attached_data_size(mp);
+ }
}
+ hsz += p->htop - p->heap;
aheap = p->abandoned_heap;
- if (!aheap)
- hsz += p->htop - p->heap;
- else {
+ if (aheap) {
/* used in orig heap */
if (p->flags & F_ABANDONED_HEAP_USE)
hsz += aheap[p->heap_sz-1];
else
hsz += p->heap_sz;
- /* Remove unused part in latest fragment */
- hsz -= p->hend - p->htop;
}
return hsz;
}
@@ -582,7 +616,7 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
Uint reclaimed_now = 0;
Eterm gc_trace_end_tag;
int reds;
- ErtsMonotonicTime start_time = 0; /* Shut up faulty warning... */
+ ErtsMonotonicTime start_time;
ErtsSchedulerData *esdp;
erts_aint32_t state;
ERTS_MSACC_PUSH_STATE_M();
@@ -590,6 +624,9 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
#endif
+ ERTS_UNDEF(start_time, 0);
+ ERTS_CHK_MBUF_SZ(p);
+
ASSERT(CONTEXT_REDS - ERTS_REDS_LEFT(p, fcalls)
>= erts_proc_sched_data(p)->virtual_reds);
@@ -672,6 +709,7 @@ do_major_collection:
killed before a GC could be done. */
if (reds == -2) {
ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL;
+ int res;
erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
erts_send_exit_signal(p, p->common.id, p, &locks,
@@ -683,7 +721,9 @@ do_major_collection:
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);
+ res = delay_garbage_collection(p, live_hf_end, need, fcalls);
+ ERTS_MSACC_POP_STATE_M();
+ return res;
}
erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
@@ -710,10 +750,21 @@ do_major_collection:
monitor_large_heap(p);
}
- esdp->gc_info.garbage_cols++;
- esdp->gc_info.reclaimed += reclaimed_now;
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ erts_mtx_lock(&dirty_gc.mtx);
+ dirty_gc.info.garbage_cols++;
+ dirty_gc.info.reclaimed += reclaimed_now;
+ erts_mtx_unlock(&dirty_gc.mtx);
+ }
+ else
+#endif
+ {
+ esdp->gc_info.garbage_cols++;
+ esdp->gc_info.reclaimed += reclaimed_now;
+ }
- FLAGS(p) &= ~F_FORCE_GC;
+ FLAGS(p) &= ~(F_FORCE_GC|F_HIBERNATED);
p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
ERTS_MSACC_POP_STATE_M();
@@ -738,6 +789,10 @@ do_major_collection:
p->last_old_htop = p->old_htop;
#endif
+ ASSERT(!p->mbuf);
+ ASSERT(!ERTS_IS_GC_DESIRED(p));
+ ASSERT(need <= HEAP_LIMIT(p) - HEAP_TOP(p));
+
return reds;
}
@@ -761,6 +816,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
>= erts_proc_sched_data(p)->virtual_reds);
}
+
/*
* Place all living data on a the new heap; deallocate any old heap.
* Meant to be used by hibernate/3.
@@ -807,11 +863,11 @@ erts_garbage_collect_hibernate(Process* p)
p->arg_reg,
p->arity);
- ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
- (p->abandoned_heap
- ? p->abandoned_heap
- : p->heap),
- p->heap_sz * sizeof(Eterm));
+#ifdef HARDDEBUG
+ disallow_heap_frag_ref_in_heap(p, heap, htop);
+#endif
+
+ erts_deallocate_young_generation(p);
p->heap = heap;
p->high_water = htop;
@@ -846,8 +902,6 @@ erts_garbage_collect_hibernate(Process* p)
sys_memcpy((void *) heap, (void *) p->heap, actual_size*sizeof(Eterm));
ERTS_HEAP_FREE(ERTS_ALC_T_TMP_HEAP, p->heap, p->heap_sz*sizeof(Eterm));
- remove_message_buffers(p);
-
p->stop = p->hend = heap + heap_size;
offs = heap - p->heap;
@@ -875,6 +929,8 @@ erts_garbage_collect_hibernate(Process* p)
ErtsGcQuickSanityCheck(p);
+ p->flags |= F_HIBERNATED;
+
erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
reds = gc_cost(actual_size, actual_size);
@@ -882,6 +938,58 @@ erts_garbage_collect_hibernate(Process* p)
}
+/*
+ * HiPE native code stack scanning procedures:
+ * - fullsweep_nstack()
+ * - gensweep_nstack()
+ * - offset_nstack()
+ * - sweep_literals_nstack()
+ */
+#if defined(HIPE)
+
+#define GENSWEEP_NSTACK(p,old_htop,n_htop) \
+ do { \
+ Eterm *tmp_old_htop = old_htop; \
+ Eterm *tmp_n_htop = n_htop; \
+ gensweep_nstack((p), &tmp_old_htop, &tmp_n_htop); \
+ old_htop = tmp_old_htop; \
+ n_htop = tmp_n_htop; \
+ } while(0)
+
+/*
+ * offset_nstack() can ignore the descriptor-based traversal the other
+ * nstack procedures use and simply call offset_heap_ptr() instead.
+ * This relies on two facts:
+ * 1. The only live non-Erlang terms on an nstack are return addresses,
+ * and they will be skipped thanks to the low/high range check.
+ * 2. Dead values, even if mistaken for pointers into the low/high area,
+ * can be offset safely since they won't be dereferenced.
+ *
+ * XXX: WARNING: If HiPE starts storing other non-Erlang values on the
+ * nstack, such as floats, then this will have to be changed.
+ */
+static ERTS_INLINE void offset_nstack(Process* p, Sint offs,
+ char* area, Uint area_size)
+{
+ if (p->hipe.nstack) {
+ ASSERT(p->hipe.nsp && p->hipe.nstend);
+ offset_heap_ptr(hipe_nstack_start(p), hipe_nstack_used(p),
+ offs, area, area_size);
+ }
+ else {
+ ASSERT(!p->hipe.nsp && !p->hipe.nstend);
+ }
+}
+
+#else /* !HIPE */
+
+#define fullsweep_nstack(p,n_htop) (n_htop)
+#define GENSWEEP_NSTACK(p,old_htop,n_htop) do{}while(0)
+#define offset_nstack(p,offs,area,area_size) do{}while(0)
+#define sweep_literals_nstack(p,old_htop,area,area_size) (old_htop)
+
+#endif /* HIPE */
+
void
erts_garbage_collect_literals(Process* p, Eterm* literals,
Uint byte_lit_size,
@@ -944,7 +1052,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
area_size = byte_lit_size;
n = setup_rootset(p, p->arg_reg, p->arity, &rootset);
roots = rootset.roots;
- old_htop = p->old_htop;
+ old_htop = sweep_literals_nstack(p, p->old_htop, area, area_size);
while (n--) {
Eterm* g_ptr = roots->v;
Uint g_sz = roots->sz;
@@ -1052,6 +1160,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
/*
* Restore status.
*/
+ p->flags &= ~F_HIBERNATED;
erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
}
@@ -1211,56 +1320,6 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end,
return -1;
}
-/*
- * HiPE native code stack scanning procedures:
- * - fullsweep_nstack()
- * - gensweep_nstack()
- * - offset_nstack()
- */
-#if defined(HIPE)
-
-#define GENSWEEP_NSTACK(p,old_htop,n_htop) \
- do { \
- Eterm *tmp_old_htop = old_htop; \
- Eterm *tmp_n_htop = n_htop; \
- gensweep_nstack((p), &tmp_old_htop, &tmp_n_htop); \
- old_htop = tmp_old_htop; \
- n_htop = tmp_n_htop; \
- } while(0)
-
-/*
- * offset_nstack() can ignore the descriptor-based traversal the other
- * nstack procedures use and simply call offset_heap_ptr() instead.
- * This relies on two facts:
- * 1. The only live non-Erlang terms on an nstack are return addresses,
- * and they will be skipped thanks to the low/high range check.
- * 2. Dead values, even if mistaken for pointers into the low/high area,
- * can be offset safely since they won't be dereferenced.
- *
- * XXX: WARNING: If HiPE starts storing other non-Erlang values on the
- * nstack, such as floats, then this will have to be changed.
- */
-static ERTS_INLINE void offset_nstack(Process* p, Sint offs,
- char* area, Uint area_size)
-{
- if (p->hipe.nstack) {
- ASSERT(p->hipe.nsp && p->hipe.nstend);
- offset_heap_ptr(hipe_nstack_start(p), hipe_nstack_used(p),
- offs, area, area_size);
- }
- else {
- ASSERT(!p->hipe.nsp && !p->hipe.nstend);
- }
-}
-
-#else /* !HIPE */
-
-#define fullsweep_nstack(p,n_htop) (n_htop)
-#define GENSWEEP_NSTACK(p,old_htop,n_htop) do{}while(0)
-#define offset_nstack(p,offs,area,area_size) do{}while(0)
-
-#endif /* HIPE */
-
static void
do_minor(Process *p, ErlHeapFragment *live_hf_end,
char *mature, Uint mature_size,
@@ -1464,22 +1523,16 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
}
#endif
- ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
- (p->abandoned_heap
- ? p->abandoned_heap
- : HEAP_START(p)),
- HEAP_SIZE(p) * sizeof(Eterm));
- p->abandoned_heap = NULL;
- p->flags &= ~F_ABANDONED_HEAP_USE;
+#ifdef HARDDEBUG
+ disallow_heap_frag_ref_in_heap(p, n_heap, n_htop);
+#endif
+
+ erts_deallocate_young_generation(p);
+
HEAP_START(p) = n_heap;
HEAP_TOP(p) = n_htop;
HEAP_SIZE(p) = new_sz;
HEAP_END(p) = n_heap + new_sz;
-
-#ifdef HARDDEBUG
- disallow_heap_frag_ref_in_heap(p);
-#endif
- remove_message_buffers(p);
}
/*
@@ -1568,13 +1621,12 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
}
#endif
- ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
- (p->abandoned_heap
- ? p->abandoned_heap
- : HEAP_START(p)),
- p->heap_sz * sizeof(Eterm));
- p->abandoned_heap = NULL;
- p->flags &= ~F_ABANDONED_HEAP_USE;
+#ifdef HARDDEBUG
+ disallow_heap_frag_ref_in_heap(p, n_heap, n_htop);
+#endif
+
+ erts_deallocate_young_generation(p);
+
HEAP_START(p) = n_heap;
HEAP_TOP(p) = n_htop;
HEAP_SIZE(p) = new_sz;
@@ -1583,11 +1635,6 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
HIGH_WATER(p) = HEAP_TOP(p);
-#ifdef HARDDEBUG
- disallow_heap_frag_ref_in_heap(p);
-#endif
- remove_message_buffers(p);
-
if (p->flags & F_ON_HEAP_MSGQ)
move_msgq_to_heap(p);
@@ -1740,22 +1787,56 @@ adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj)
return adjusted;
}
-/*
- * Remove all message buffers.
- */
-static void
-remove_message_buffers(Process* p)
+void
+erts_deallocate_young_generation(Process *c_p)
{
- if (MBUF(p) != NULL) {
- free_message_buffer(MBUF(p));
- MBUF(p) = NULL;
+ Eterm *orig_heap;
+
+ if (!c_p->abandoned_heap) {
+ orig_heap = c_p->heap;
+ ASSERT(!(c_p->flags & F_ABANDONED_HEAP_USE));
+ }
+ else {
+ ErlHeapFragment *hfrag;
+
+ orig_heap = c_p->abandoned_heap;
+ c_p->abandoned_heap = NULL;
+ c_p->flags &= ~F_ABANDONED_HEAP_USE;
+
+ /*
+ * Temporary heap located in heap fragment
+ * only referred to by 'c_p->heap'. Add it to
+ * 'c_p->mbuf' list and deallocate it as any
+ * other heap fragment...
+ */
+ hfrag = ((ErlHeapFragment *)
+ (((char *) c_p->heap)
+ - offsetof(ErlHeapFragment, mem)));
+
+ ASSERT(!hfrag->off_heap.first);
+ ASSERT(!hfrag->off_heap.overhead);
+ ASSERT(!hfrag->next);
+ ASSERT(c_p->htop - c_p->heap <= hfrag->alloc_size);
+
+ hfrag->next = c_p->mbuf;
+ c_p->mbuf = hfrag;
+ }
+
+ if (c_p->mbuf) {
+ free_message_buffer(c_p->mbuf);
+ c_p->mbuf = NULL;
}
- if (p->msg_frag) {
- erts_cleanup_messages(p->msg_frag);
- p->msg_frag = NULL;
+ if (c_p->msg_frag) {
+ erts_cleanup_messages(c_p->msg_frag);
+ c_p->msg_frag = NULL;
}
- MBUF_SIZE(p) = 0;
+ c_p->mbuf_sz = 0;
+
+ ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
+ orig_heap,
+ c_p->heap_sz * sizeof(Eterm));
}
+
#ifdef HARDDEBUG
/*
@@ -1767,19 +1848,15 @@ remove_message_buffers(Process* p)
*/
static void
-disallow_heap_frag_ref_in_heap(Process* p)
+disallow_heap_frag_ref_in_heap(Process *p, Eterm *heap, Eterm *htop)
{
Eterm* hp;
- Eterm* htop;
- Eterm* heap;
Uint heap_size;
if (p->mbuf == 0) {
return;
}
- htop = p->htop;
- heap = p->heap;
heap_size = (htop - heap)*sizeof(Eterm);
hp = heap;
@@ -2240,44 +2317,31 @@ copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
static void
move_msgq_to_heap(Process *p)
{
+
ErtsMessage **mpp = &p->msg.first;
+ Uint64 pre_oh = MSO(p).overhead;
while (*mpp) {
ErtsMessage *mp = *mpp;
if (mp->data.attached) {
ErlHeapFragment *bp;
- ErtsHeapFactory factory;
-
- erts_factory_proc_prealloc_init(&factory, p,
- erts_msg_attached_data_size(mp));
-
- if (is_non_value(ERL_MESSAGE_TERM(mp))) {
- if (mp->data.dist_ext) {
- ASSERT(mp->data.dist_ext->heap_size >= 0);
- if (is_not_nil(ERL_MESSAGE_TOKEN(mp))) {
- bp = erts_dist_ext_trailer(mp->data.dist_ext);
- ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp),
- bp->used_size,
- &factory.hp,
- factory.off_heap);
- erts_cleanup_offheap(&bp->off_heap);
- }
- ERL_MESSAGE_TERM(mp) = erts_decode_dist_ext(&factory,
- mp->data.dist_ext);
- erts_free_dist_ext_copy(mp->data.dist_ext);
- mp->data.dist_ext = NULL;
- }
- }
- else {
+
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding...
+ */
+ if (is_value(ERL_MESSAGE_TERM(mp))) {
bp = erts_message_to_heap_frag(mp);
if (bp->next)
- erts_move_multi_frags(&factory.hp, factory.off_heap, bp,
+ erts_move_multi_frags(&p->htop, &p->off_heap, bp,
mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0);
else
- copy_one_frag(&factory.hp, factory.off_heap, bp,
+ copy_one_frag(&p->htop, &p->off_heap, bp,
mp->m, ERL_MESSAGE_REF_ARRAY_SZ);
if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
@@ -2294,17 +2358,25 @@ move_msgq_to_heap(Process *p)
mp = new_mp;
}
}
-
- erts_factory_close(&factory);
}
mpp = &(*mpp)->next;
}
+
+ if (pre_oh != MSO(p).overhead) {
+ /* Got new binaries; update vheap size... */
+ BIN_VHEAP_SZ(p) = next_vheap_size(p, MSO(p).overhead, BIN_VHEAP_SZ(p));
+ }
}
static Uint
setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
{
+ /*
+ * NOTE!
+ * Remember to update offset_rootset() when changing
+ * this function.
+ */
Roots* roots;
Uint n;
@@ -2693,7 +2765,7 @@ sweep_off_heap(Process *p, int fullsweep)
case FUN_SUBTAG:
{
ErlFunEntry* fe = ((ErlFunThing*)ptr)->fe;
- if (erts_refc_dectest(&fe->refc, 0) == 0) {
+ if (erts_smp_refc_dectest(&fe->refc, 0) == 0) {
erts_erase_fun_entry(fe);
}
break;
@@ -2949,6 +3021,12 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size,
offset_heap_ptr(objv, nobj, offs, area, area_size);
}
offset_off_heap(p, offs, area, area_size);
+ if (ERTS_PROC_GET_NIF_TRAP_EXPORT(p)) {
+ Eterm* argv;
+ int argc;
+ if (erts_setup_nif_gc(p, &argv, &argc))
+ offset_heap_ptr(argv, argc, offs, area, area_size);
+ }
}
static void
@@ -2986,6 +3064,18 @@ reply_gc_info(void *vgcirp)
reclaimed = esdp->gc_info.reclaimed;
garbage_cols = esdp->gc_info.garbage_cols;
+#ifdef ERTS_DIRTY_SCHEDULERS
+ /*
+ * Add dirty schedulers info on requesting
+ * schedulers info
+ */
+ if (gcirp->req_sched == esdp->no) {
+ erts_mtx_lock(&dirty_gc.mtx);
+ reclaimed += dirty_gc.info.reclaimed;
+ garbage_cols += dirty_gc.info.garbage_cols;
+ erts_mtx_unlock(&dirty_gc.mtx);
+ }
+#endif
sz = 0;
hpp = NULL;
@@ -3266,13 +3356,15 @@ within2(Eterm *ptr, Process *p, Eterm *real_htop)
ErtsMessage* mp;
Eterm *htop, *heap;
- if (p->abandoned_heap)
+ if (p->abandoned_heap) {
ERTS_GET_ORIG_HEAP(p, heap, htop);
- else {
- heap = p->heap;
- htop = real_htop ? real_htop : HEAP_TOP(p);
+ if (heap <= ptr && ptr < htop)
+ return 1;
}
+ heap = p->heap;
+ htop = real_htop ? real_htop : HEAP_TOP(p);
+
if (OLD_HEAP(p) && (OLD_HEAP(p) <= ptr && ptr < OLD_HEND(p))) {
return 1;
}
@@ -3343,12 +3435,12 @@ erts_check_off_heap2(Process *p, Eterm *htop)
refc = erts_refc_read(&u.pb->val->refc, 1);
break;
case FUN_SUBTAG:
- refc = erts_refc_read(&u.fun->fe->refc, 1);
+ refc = erts_smp_refc_read(&u.fun->fe->refc, 1);
break;
case EXTERNAL_PID_SUBTAG:
case EXTERNAL_PORT_SUBTAG:
case EXTERNAL_REF_SUBTAG:
- refc = erts_refc_read(&u.ext->node->refc, 1);
+ refc = erts_smp_refc_read(&u.ext->node->refc, 1);
break;
default:
ASSERT(!"erts_check_off_heap2: Invalid thing_word");
diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h
index 54ea9ca3c0..76dd74c866 100644
--- a/erts/emulator/beam/erl_gc.h
+++ b/erts/emulator/beam/erl_gc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2007-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2007-2017. 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.
@@ -27,10 +27,6 @@
#include "erl_map.h"
-#if defined(DEBUG) && !ERTS_GLB_INLINE_INCL_FUNC_DEF
-# define HARDDEBUG 1
-#endif
-
#define IS_MOVED_BOXED(x) (!is_header((x)))
#define IS_MOVED_CONS(x) (is_non_value((x)))
@@ -157,5 +153,6 @@ 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 *);
+void erts_deallocate_young_generation(Process *c_p);
#endif /* __ERL_GC_H__ */
diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c
index 223ba193da..50aa41b4d2 100644
--- a/erts/emulator/beam/erl_goodfit_alloc.c
+++ b/erts/emulator/beam/erl_goodfit_alloc.c
@@ -168,7 +168,7 @@ static Block_t * get_free_block (Allctr_t *, Uint,
static void link_free_block (Allctr_t *, Block_t *);
static void unlink_free_block (Allctr_t *, Block_t *);
static void update_last_aux_mbc (Allctr_t *, Carrier_t *);
-static Eterm info_options (Allctr_t *, char *, int *,
+static Eterm info_options (Allctr_t *, char *, fmtfn_t *,
void *, Uint **, Uint *);
static void init_atoms (void);
@@ -551,7 +551,7 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
static Eterm
info_options(Allctr_t *allctr,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
index ebeff51aac..e9e1e502aa 100644
--- a/erts/emulator/beam/erl_hl_timer.c
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2015-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2015-2017. 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.
@@ -98,16 +98,16 @@ typedef enum {
# define ERTS_HLT_SMP_MEMBAR_LoadLoad_LoadStore
#endif
-/* Bit 0 to 9 contains scheduler id (see mask below) */
-#define ERTS_TMR_ROFLG_HLT (((Uint32) 1) << 10)
-#define ERTS_TMR_ROFLG_BIF_TMR (((Uint32) 1) << 11)
-#define ERTS_TMR_ROFLG_PRE_ALC (((Uint32) 1) << 12)
-#define ERTS_TMR_ROFLG_REG_NAME (((Uint32) 1) << 13)
-#define ERTS_TMR_ROFLG_PROC (((Uint32) 1) << 14)
-#define ERTS_TMR_ROFLG_PORT (((Uint32) 1) << 15)
-#define ERTS_TMR_ROFLG_CALLBACK (((Uint32) 1) << 16)
+/* Bit 0 to 10 contains scheduler id (see mask below) */
+#define ERTS_TMR_ROFLG_HLT (((Uint32) 1) << 11)
+#define ERTS_TMR_ROFLG_BIF_TMR (((Uint32) 1) << 12)
+#define ERTS_TMR_ROFLG_PRE_ALC (((Uint32) 1) << 13)
+#define ERTS_TMR_ROFLG_REG_NAME (((Uint32) 1) << 14)
+#define ERTS_TMR_ROFLG_PROC (((Uint32) 1) << 15)
+#define ERTS_TMR_ROFLG_PORT (((Uint32) 1) << 16)
+#define ERTS_TMR_ROFLG_CALLBACK (((Uint32) 1) << 17)
#ifdef ERTS_BTM_ACCESSOR_SUPPORT
-#define ERTS_TMR_ROFLG_ABIF_TMR (((Uint32) 1) << 17)
+#define ERTS_TMR_ROFLG_ABIF_TMR (((Uint32) 1) << 18)
#endif
#define ERTS_TMR_ROFLG_SID_MASK \
@@ -735,7 +735,10 @@ proc_timeout_common(Process *proc, void *tmr)
if (tmr == (void *) erts_smp_atomic_cmpxchg_mb(&proc->common.timer,
ERTS_PTMR_TIMEDOUT,
(erts_aint_t) tmr)) {
- erts_aint32_t state = erts_smp_atomic32_read_acqb(&proc->state);
+ erts_aint32_t state;
+ erts_smp_proc_lock(proc, ERTS_PROC_LOCKS_MSG_RECEIVE);
+ state = erts_smp_atomic32_read_acqb(&proc->state);
+ erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_RECEIVE);
if (!(state & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_EXITING)))
erts_schedule_process(proc, state, 0);
return 1;
@@ -2663,7 +2666,7 @@ erts_start_timer_callback(ErtsMonotonicTime tmo,
tmo);
twt = tmo < ERTS_TIMER_WHEEL_MSEC;
- if (esdp)
+ if (esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp))
start_callback_timer(esdp,
twt,
timeout_pos,
@@ -2848,7 +2851,7 @@ erts_read_port_timer(Port *c_prt)
*/
typedef struct {
- int to;
+ fmtfn_t to;
void *to_arg;
ErtsMonotonicTime now;
} ErtsBTMPrint;
@@ -2862,7 +2865,8 @@ btm_print(ErtsHLTimer *tmr, void *vbtmp)
if (tmr->timeout <= btmp->now)
left = 0;
- left = ERTS_CLKTCKS_TO_MSEC(tmr->timeout - btmp->now);
+ else
+ left = ERTS_CLKTCKS_TO_MSEC(tmr->timeout - btmp->now);
receiver = ((tmr->head.roflgs & ERTS_TMR_ROFLG_REG_NAME)
? tmr->receiver.name
@@ -2878,7 +2882,7 @@ btm_print(ErtsHLTimer *tmr, void *vbtmp)
}
void
-erts_print_bif_timer_info(int to, void *to_arg)
+erts_print_bif_timer_info(fmtfn_t to, void *to_arg)
{
ErtsBTMPrint btmp;
int six;
diff --git a/erts/emulator/beam/erl_hl_timer.h b/erts/emulator/beam/erl_hl_timer.h
index 0931bb8965..9cdcd581a0 100644
--- a/erts/emulator/beam/erl_hl_timer.h
+++ b/erts/emulator/beam/erl_hl_timer.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2015. All Rights Reserved.
+ * Copyright Ericsson AB 2015-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.
@@ -72,7 +72,7 @@ erts_handle_canceled_timers(void *vesdp,
#endif
Uint erts_bif_timer_memory_size(void);
-void erts_print_bif_timer_info(int to, void *to_arg);
+void erts_print_bif_timer_info(fmtfn_t to, void *to_arg);
void erts_debug_bif_timer_foreach(void (*func)(Eterm,
Eterm,
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index fbdafec4ef..e8b8739852 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2017. 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.
@@ -117,6 +117,11 @@ const int etp_big_endian = 1;
const int etp_big_endian = 0;
#endif
const Eterm etp_the_non_value = THE_NON_VALUE;
+#ifdef ERTS_HOLE_MARKER
+const Eterm etp_hole_marker = ERTS_HOLE_MARKER;
+#else
+const Eterm etp_hole_marker = 0;
+#endif
/*
* Note about VxWorks: All variables must be initialized by executable code,
@@ -332,8 +337,6 @@ erl_init(int ncpu,
int node_tab_delete_delay,
ErtsDbSpinCount db_spin_count)
{
- init_benchmarking();
-
erts_bif_unique_init();
erts_init_monitors();
erts_init_time(time_correction, time_warp_mode);
@@ -384,6 +387,7 @@ erl_init(int ncpu,
erts_init_unicode(); /* after RE to get access to PCRE unicode */
erts_init_external();
erts_init_map();
+ erts_beam_bif_load_init();
erts_delay_trap = erts_export_put(am_erlang, am_delay_trap, 2);
erts_late_init_process();
#if HAVE_ERTS_MSEG
@@ -796,9 +800,6 @@ early_init(int *argc, char **argv) /*
erts_thr_progress_pre_init();
#endif
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_lc_init();
-#endif
#ifdef ERTS_SMP
erts_smp_atomic32_init_nob(&erts_writing_erl_crash_dump, 0L);
erts_tsd_key_create(&erts_is_crash_dumping_key,"erts_is_crash_dumping_key");
@@ -2224,7 +2225,6 @@ erl_start(int argc, char **argv)
init_break_handler();
if (replace_intr)
erts_replace_intr();
- sys_init_suspend_handler();
#endif
boot_argc = argc - i; /* Number of arguments to init */
@@ -2250,7 +2250,42 @@ erl_start(int argc, char **argv)
otp_ring0_pid = erl_first_process_otp("otp_ring0", NULL, 0,
boot_argc, boot_argv);
- (void) erl_system_process_otp(otp_ring0_pid, "erts_code_purger");
+ {
+ /*
+ * The erts_code_purger and the erts_literal_area_collector
+ * system processes are *always* alive. If they terminate
+ * they bring the whole VM down.
+ */
+ Eterm pid;
+
+ pid = erl_system_process_otp(otp_ring0_pid, "erts_code_purger");
+ erts_code_purger
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_code_purger && erts_code_purger->common.id == pid);
+ erts_proc_inc_refc(erts_code_purger);
+
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ pid = erl_system_process_otp(otp_ring0_pid, "erts_literal_area_collector");
+ erts_literal_area_collector
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_literal_area_collector
+ && erts_literal_area_collector->common.id == pid);
+ erts_proc_inc_refc(erts_literal_area_collector);
+#endif
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ pid = erl_system_process_otp(otp_ring0_pid, "erts_dirty_process_code_checker");
+ erts_dirty_process_code_checker
+ = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ ASSERT(erts_dirty_process_code_checker
+ && erts_dirty_process_code_checker->common.id == pid);
+ erts_proc_inc_refc(erts_dirty_process_code_checker);
+#endif
+
+ }
#ifdef ERTS_SMP
erts_start_schedulers();
@@ -2340,24 +2375,34 @@ system_cleanup(int flush_async)
erts_exit_flush_async();
}
+static int erts_exit_code;
+
static __decl_noreturn void __noreturn
erts_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2)
{
system_cleanup(flush_async);
- save_statistics();
-
if (erts_mtrace_enabled)
erts_mtrace_exit((Uint32) n);
- /* Produce an Erlang core dump if error */
+ if (fmt != NULL && *fmt != '\0')
+ erl_error(fmt, args2); /* Print error message. */
+
+ erts_exit_code = n;
+
+ /* Produce an Erlang crash dump if error */
if (((n == ERTS_ERROR_EXIT && erts_no_crash_dump == 0) || n == ERTS_DUMP_EXIT)
&& erts_initialized) {
erl_crash_dump_v((char*) NULL, 0, fmt, args1);
}
- if (fmt != NULL && *fmt != '\0')
- erl_error(fmt, args2); /* Print error message. */
+ erts_exit_epilogue();
+}
+
+__decl_noreturn void __noreturn erts_exit_epilogue(void)
+{
+ int n = erts_exit_code;
+
sys_tty_reset(n);
if (n == ERTS_INTR_EXIT)
diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c
index f84c63e7a4..4d4defd8b5 100644
--- a/erts/emulator/beam/erl_instrument.c
+++ b/erts/emulator/beam/erl_instrument.c
@@ -539,7 +539,7 @@ map_stat_free(ErtsAlcType_t n, void *extra, void *ptr)
}
-static void dump_memory_map_to_stream(FILE *fp)
+static void dump_memory_map_to_stream(fmtfn_t to, void* to_arg)
{
ErtsAlcType_t n;
MapStatBlock_t *bp;
@@ -551,7 +551,7 @@ static void dump_memory_map_to_stream(FILE *fp)
/* Write header */
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"{instr_hdr,\n"
" %lu,\n"
" %lu,\n"
@@ -574,7 +574,7 @@ static void dump_memory_map_to_stream(FILE *fp)
else
astr = ERTS_ALC_A2AD(ERTS_ALC_A_SYSTEM);
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"%s{%s,%s,%s}%s",
(n == ERTS_ALC_N_MIN) ? "" : " ",
ERTS_ALC_N2TD(n),
@@ -583,12 +583,12 @@ static void dump_memory_map_to_stream(FILE *fp)
(n == ERTS_ALC_N_MAX) ? "" : ",\n");
}
- fprintf(fp, "}}.\n");
+ erts_cbprintf(to, to_arg, "}}.\n");
/* Write memory data */
for (bp = mem_anchor; bp; bp = bp->next) {
if (is_internal_pid(bp->pid))
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"{%lu, %lu, %lu, {%lu,%lu,%lu}}.\n",
(UWord) bp->type_no,
(UWord) bp->mem,
@@ -597,7 +597,7 @@ static void dump_memory_map_to_stream(FILE *fp)
(UWord) pid_number(bp->pid),
(UWord) pid_serial(bp->pid));
else
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"{%lu, %lu, %lu, undefined}.\n",
(UWord) bp->type_no,
(UWord) bp->mem,
@@ -608,40 +608,29 @@ static void dump_memory_map_to_stream(FILE *fp)
erts_mtx_unlock(&instr_mutex);
}
-int erts_instr_dump_memory_map_to_fd(int fd)
+int erts_instr_dump_memory_map_to(fmtfn_t to, void* to_arg)
{
- char buf[BUFSIZ];
- FILE *f;
-
if (!erts_instr_memory_map)
return 0;
- f = fdopen(fd, "w");
- if (f == NULL)
- return 0;
-
- /* Avoid allocating memory; we may have run out of it at this point. */
- setbuf(f, buf);
-
- dump_memory_map_to_stream(f);
- fflush(f);
+ dump_memory_map_to_stream(to, to_arg);
return 1;
}
int erts_instr_dump_memory_map(const char *name)
{
- FILE *f;
+ int fd;
if (!erts_instr_memory_map)
return 0;
- f = fopen(name, "w");
- if (f == NULL)
+ fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+ if (fd < 0)
return 0;
- dump_memory_map_to_stream(f);
+ dump_memory_map_to_stream(erts_write_fd, (void*)&fd);
- fclose(f);
+ close(fd);
return 1;
}
@@ -998,19 +987,19 @@ erts_instr_get_stat(Process *proc, Eterm what, int begin_max_period)
}
static void
-dump_stat_to_stream(FILE *fp, int begin_max_period)
+dump_stat_to_stream(fmtfn_t to, void* to_arg, int begin_max_period)
{
ErtsAlcType_t i, a_max, a_min;
erts_mtx_lock(&instr_mutex);
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"{instr_vsn,%lu}.\n",
(unsigned long) ERTS_INSTR_VSN);
update_max_ever_values(&stats->tot, 0, 0);
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"{total,[{total,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}]}.\n",
(UWord) stats->tot.size,
(UWord) stats->tot.max_size,
@@ -1038,7 +1027,7 @@ dump_stat_to_stream(FILE *fp, int begin_max_period)
for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
if (erts_allctrs_info[i].enabled) {
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"%s{%s,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}%s",
i == a_min ? "{allocators,\n [" : " ",
ERTS_ALC_A2AD(i),
@@ -1055,7 +1044,7 @@ dump_stat_to_stream(FILE *fp, int begin_max_period)
update_max_ever_values(stats->c, ERTS_ALC_C_MIN, ERTS_ALC_C_MAX);
for (i = ERTS_ALC_C_MIN; i <= ERTS_ALC_C_MAX; i++) {
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"%s{%s,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}%s",
i == ERTS_ALC_C_MIN ? "{classes,\n [" : " ",
ERTS_ALC_C2CD(i),
@@ -1071,7 +1060,7 @@ dump_stat_to_stream(FILE *fp, int begin_max_period)
update_max_ever_values(stats->n, ERTS_ALC_N_MIN, ERTS_ALC_N_MAX);
for (i = ERTS_ALC_N_MIN; i <= ERTS_ALC_N_MAX; i++) {
- fprintf(fp,
+ erts_cbprintf(to, to_arg,
"%s{%s,[{sizes,%lu,%lu,%lu},{blocks,%lu,%lu,%lu}]}%s",
i == ERTS_ALC_N_MIN ? "{types,\n [" : " ",
ERTS_ALC_N2TD(i),
@@ -1095,40 +1084,29 @@ dump_stat_to_stream(FILE *fp, int begin_max_period)
}
-int erts_instr_dump_stat_to_fd(int fd, int begin_max_period)
+int erts_instr_dump_stat_to(fmtfn_t to, void* to_arg, int begin_max_period)
{
- char buf[BUFSIZ];
- FILE *fp;
-
if (!erts_instr_stat)
return 0;
- fp = fdopen(fd, "w");
- if (fp == NULL)
- return 0;
-
- /* Avoid allocating memory; we may have run out of it at this point. */
- setbuf(fp, buf);
-
- dump_stat_to_stream(fp, begin_max_period);
- fflush(fp);
+ dump_stat_to_stream(to, to_arg, begin_max_period);
return 1;
}
int erts_instr_dump_stat(const char *name, int begin_max_period)
{
- FILE *file;
+ int fd;
if (!erts_instr_stat)
return 0;
- file = fopen(name, "w");
- if (file == NULL)
+ fd = open(name, O_WRONLY | O_CREAT | O_TRUNC,0640);
+ if (fd < 0)
return 0;
- dump_stat_to_stream(file, begin_max_period);
+ dump_stat_to_stream(erts_write_fd, (void*)&fd, begin_max_period);
- fclose(file);
+ close(fd);
return 1;
}
diff --git a/erts/emulator/beam/erl_instrument.h b/erts/emulator/beam/erl_instrument.h
index 1f04c91d5e..351172b2fa 100644
--- a/erts/emulator/beam/erl_instrument.h
+++ b/erts/emulator/beam/erl_instrument.h
@@ -29,10 +29,10 @@ extern int erts_instr_memory_map;
extern int erts_instr_stat;
Uint erts_instr_init(int stat, int map_stat);
-int erts_instr_dump_memory_map_to_fd(int fd);
+int erts_instr_dump_memory_map_to(fmtfn_t to, void* to_arg);
int erts_instr_dump_memory_map(const char *name);
Eterm erts_instr_get_memory_map(Process *process);
-int erts_instr_dump_stat_to_fd(int fd, int begin_max_period);
+int erts_instr_dump_stat_to(fmtfn_t to, void* to_arg, int begin_max_period);
int erts_instr_dump_stat(const char *name, int begin_max_period);
Eterm erts_instr_get_stat(Process *proc, Eterm what, int begin_max_period);
Eterm erts_instr_get_type_info(Process *proc);
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 39c0617143..c53bdc685b 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2005-2017. 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.
@@ -96,6 +96,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "dist_entry", "address" },
{ "dist_entry_links", "address" },
{ "code_write_permission", NULL },
+ { "purge_state", NULL },
{ "proc_status", "pid" },
{ "proc_trace", "pid" },
{ "ports_snapshot", NULL },
@@ -112,6 +113,9 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "export_tab", NULL },
{ "fun_tab", NULL },
{ "environ", NULL },
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ { "release_literal_areas", NULL },
+#endif
#endif
{ "efile_drv", "address" },
{ "drv_ev_state_grow", NULL, },
@@ -125,6 +129,8 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "run_queue", "address" },
#ifdef ERTS_DIRTY_SCHEDULERS
{ "dirty_run_queue_sleep_list", "address" },
+ { "dirty_gc_info", NULL },
+ { "dirty_break_point_index", NULL },
#endif
{ "process_table", NULL },
{ "cpu_info", NULL },
diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c
index a00e0f0fff..481e92b2cd 100644
--- a/erts/emulator/beam/erl_lock_count.c
+++ b/erts/emulator/beam/erl_lock_count.c
@@ -274,11 +274,11 @@ void erts_lcnt_init() {
lcnt_unlock();
- /* set start timer and zero statistics */
- erts_lcnt_clear_counters();
}
void erts_lcnt_late_init() {
+ /* set start timer and zero statistics */
+ erts_lcnt_clear_counters();
erts_thr_install_exit_handler(erts_lcnt_thread_exit_handler);
}
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 8efc983f04..979a0040b0 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -497,18 +497,50 @@ Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n,
return res;
}
+Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0, Uint n)
+{
+ if (n < MAP_SMALL_MAP_LIMIT) {
+ Eterm *ks, *vs, *hp;
+ flatmap_t *mp;
+ Eterm keys;
-Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n,
+ hp = erts_produce_heap(factory, 3 + 1 + (2 * n), 0);
+ keys = make_tuple(hp);
+ *hp++ = make_arityval(n);
+ ks = hp;
+ hp += n;
+ mp = (flatmap_t*)hp;
+ hp += MAP_HEADER_FLATMAP_SZ;
+ vs = hp;
+
+ mp->thing_word = MAP_HEADER_FLATMAP;
+ mp->size = n;
+ mp->keys = keys;
+
+ sys_memcpy(ks, ks0, n * sizeof(Eterm));
+ sys_memcpy(vs, vs0, n * sizeof(Eterm));
+
+ erts_validate_and_sort_flatmap(mp);
+
+ return make_flatmap(mp);
+ } else {
+ return erts_hashmap_from_ks_and_vs(factory, ks0, vs0, n);
+ }
+ return THE_NON_VALUE;
+}
+
+
+Eterm erts_hashmap_from_ks_and_vs_extra(ErtsHeapFactory *factory,
+ Eterm *ks, Eterm *vs, Uint n,
Eterm key, Eterm value) {
Uint32 sw, hx;
Uint i,sz;
hxnode_t *hxns;
- ErtsHeapFactory factory;
Eterm *hp, res;
sz = (key == THE_NON_VALUE) ? n : (n + 1);
ASSERT(sz > MAP_SMALL_MAP_LIMIT);
- hp = HAlloc(p, 2 * sz);
+ hp = erts_produce_heap(factory, 2 * sz, 0);
/* create tmp hx values and leaf ptrs */
hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, sz * sizeof(hxnode_t));
@@ -531,12 +563,9 @@ Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n
hxns[i].i = i;
}
- erts_factory_proc_init(&factory, p);
- res = hashmap_from_unsorted_array(&factory, hxns, sz, 0);
- erts_factory_close(&factory);
+ res = hashmap_from_unsorted_array(factory, hxns, sz, 0);
erts_free(ERTS_ALC_T_TMP, (void *) hxns);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(p);
return res;
}
@@ -1780,11 +1809,14 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) {
/* the map will grow */
if (n >= MAP_SMALL_MAP_LIMIT) {
+ ErtsHeapFactory factory;
HRelease(p, shp + MAP_HEADER_FLATMAP_SZ + n, shp);
ks = flatmap_get_keys(mp);
vs = flatmap_get_values(mp);
- res = erts_hashmap_from_ks_and_vs_extra(p,ks,vs,n,key,value);
+ erts_factory_proc_init(&factory, p);
+ res = erts_hashmap_from_ks_and_vs_extra(&factory,ks,vs,n,key,value);
+ erts_factory_close(&factory);
return res;
}
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 8b5c9582ba..61a841f7f0 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -98,10 +98,12 @@ Eterm* hashmap_iterator_prev(struct ErtsWStack_* s);
int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp);
Eterm erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n, int reject_dupkeys);
-#define erts_hashmap_from_ks_and_vs(P, KS, VS, N) \
- erts_hashmap_from_ks_and_vs_extra((P), (KS), (VS), (N), THE_NON_VALUE, THE_NON_VALUE);
+#define erts_hashmap_from_ks_and_vs(F, KS, VS, N) \
+ erts_hashmap_from_ks_and_vs_extra((F), (KS), (VS), (N), THE_NON_VALUE, THE_NON_VALUE);
-Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n,
+Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Uint n);
+Eterm erts_hashmap_from_ks_and_vs_extra(ErtsHeapFactory *factory,
+ Eterm *ks, Eterm *vs, Uint n,
Eterm k, Eterm v);
const Eterm *erts_maps_get(Eterm key, Eterm map);
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 71ab92937d..de9f16d088 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2017. 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.
@@ -172,7 +172,7 @@ erts_cleanup_offheap(ErlOffHeap *offheap)
}
break;
case FUN_SUBTAG:
- if (erts_refc_dectest(&u.fun->fe->refc, 0) == 0) {
+ if (erts_smp_refc_dectest(&u.fun->fe->refc, 0) == 0) {
erts_erase_fun_entry(u.fun->fe);
}
break;
@@ -193,7 +193,7 @@ free_message_buffer(ErlHeapFragment* bp)
erts_cleanup_offheap(&bp->off_heap);
ERTS_HEAP_FREE(ERTS_ALC_T_HEAP_FRAG, (void *) bp,
- ERTS_HEAP_FRAG_SIZE(bp->size));
+ ERTS_HEAP_FRAG_SIZE(bp->alloc_size));
bp = next_bp;
}while (bp != NULL);
}
@@ -564,14 +564,11 @@ erts_msg_attached_data_size_aux(ErtsMessage *msg)
sz = erts_decode_dist_ext_size(msg->data.dist_ext);
if (sz < 0) {
- /* Bad external; remove it */
- if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) {
- ErlHeapFragment *heap_frag;
- heap_frag = erts_dist_ext_trailer(msg->data.dist_ext);
- erts_cleanup_offheap(&heap_frag->off_heap);
- }
- erts_free_dist_ext_copy(msg->data.dist_ext);
- msg->data.dist_ext = NULL;
+ /* Bad external
+ * We leave the message intact in this case as it's not worth the trouble
+ * to make all callers remove it from queue. It will be detected again
+ * and removed from message queue later anyway.
+ */
return 0;
}
@@ -697,9 +694,6 @@ erts_send_message(Process* sender,
#ifdef SHCOPY_SEND
erts_shcopy_t info;
#endif
- BM_STOP_TIMER(system);
- BM_MESSAGE(message,sender,receiver);
- BM_START_TIMER(send);
#ifdef USE_VM_PROBES
*sender_name = *receiver_name = '\0';
@@ -720,7 +714,6 @@ erts_send_message(Process* sender,
#ifdef USE_VM_PROBES
Uint dt_utag_size = 0;
#endif
- BM_SWAP_TIMER(send,size);
/* SHCOPY corrupts the heap between
* copy_shared_calculate, and
@@ -747,8 +740,6 @@ erts_send_message(Process* sender,
#else
msize = size_object(message);
#endif
- BM_SWAP_TIMER(size,send);
-
mp = erts_alloc_message_heap_state(receiver,
&receiver_state,
receiver_locks,
@@ -760,8 +751,6 @@ erts_send_message(Process* sender,
&hp,
&ohp);
- BM_SWAP_TIMER(send,copy);
-
#ifdef SHCOPY_SEND
if (is_not_immed(message))
message = copy_shared_perform(message, msize, &info, &hp, ohp);
@@ -792,9 +781,6 @@ erts_send_message(Process* sender,
msize, tok_label, tok_lastcnt, tok_serial);
}
#endif
- BM_MESSAGE_COPIED(msize);
- BM_SWAP_TIMER(copy,send);
-
} else {
Eterm *hp;
@@ -803,22 +789,18 @@ erts_send_message(Process* sender,
msize = 0;
}
else {
- BM_SWAP_TIMER(send,size);
#ifdef SHCOPY_SEND
INITIALIZE_SHCOPY(info);
msize = copy_shared_calculate(message, &info);
#else
msize = size_object(message);
#endif
- BM_SWAP_TIMER(size,send);
-
mp = erts_alloc_message_heap_state(receiver,
&receiver_state,
receiver_locks,
msize,
&hp,
&ohp);
- BM_SWAP_TIMER(send,copy);
#ifdef SHCOPY_SEND
if (is_not_immed(message))
message = copy_shared_perform(message, msize, &info, &hp, ohp);
@@ -827,8 +809,6 @@ erts_send_message(Process* sender,
if (is_not_immed(message))
message = copy_struct(message, msize, &hp, ohp);
#endif
- BM_MESSAGE_COPIED(msz);
- BM_SWAP_TIMER(copy,send);
}
#ifdef USE_VM_PROBES
DTRACE6(message_send, sender_name, receiver_name,
@@ -846,8 +826,6 @@ erts_send_message(Process* sender,
mp, message,
sender->common.id);
- BM_SWAP_TIMER(send,system);
-
return res;
}
@@ -1861,3 +1839,20 @@ void erts_factory_undo(ErtsHeapFactory* factory)
factory->heap_frags = NULL;
#endif
}
+
+Uint
+erts_mbuf_size(Process *p)
+{
+ Uint sz = 0;
+ ErlHeapFragment* bp;
+ ErtsMessage* mp;
+
+ for (bp = p->mbuf; bp; bp = bp->next)
+ sz += bp->used_size;
+
+ for (mp = p->msg_frag; mp; mp = mp->next)
+ for (bp = erts_message_to_heap_frag(mp); bp; bp = bp->next)
+ sz += bp->used_size;
+
+ return sz;
+}
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 6df969367b..42ed14e69c 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -499,4 +499,15 @@ erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, ErtsMessage *newp, ErtsMessage
#endif
+Uint erts_mbuf_size(Process *p);
+#if defined(DEBUG) || 0
+# define ERTS_CHK_MBUF_SZ(P) \
+ do { \
+ Uint actual_mbuf_sz__ = erts_mbuf_size((P)); \
+ ERTS_ASSERT((P)->mbuf_sz >= actual_mbuf_sz__); \
+ } while (0)
+#else
+# define ERTS_CHK_MBUF_SZ(P) ((void) 1)
+#endif
+
#endif
diff --git a/erts/emulator/beam/erl_monitors.c b/erts/emulator/beam/erl_monitors.c
index 910598690d..7244ae144f 100644
--- a/erts/emulator/beam/erl_monitors.c
+++ b/erts/emulator/beam/erl_monitors.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2017. 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.
@@ -104,7 +104,7 @@ do { \
(*((Hp)++)) = boxed_val((From))[i__]; \
if (is_external((To))) { \
external_thing_ptr((To))->next = NULL; \
- erts_refc_inc(&(external_thing_ptr((To))->node->refc), 2);\
+ erts_smp_refc_inc(&(external_thing_ptr((To))->node->refc), 2);\
} \
} \
} while (0)
diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c
index 544bc8b983..7ddf49937f 100644
--- a/erts/emulator/beam/erl_msacc.c
+++ b/erts/emulator/beam/erl_msacc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2014-2015. All Rights Reserved.
+ * Copyright Ericsson AB 2014-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.
@@ -40,10 +40,11 @@
#include "erl_bif_unique.h"
#include "erl_map.h"
#include "erl_msacc.h"
+#include "erl_bif_table.h"
#if ERTS_ENABLE_MSACC
-static Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, Eterm **hpp, Uint *szp);
+static Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, ErtsHeapFactory *factory);
static void erts_msacc_reset(ErtsMsAcc *msacc);
static ErtsMsAcc* get_msacc(void);
@@ -52,7 +53,9 @@ erts_tsd_key_t ERTS_WRITE_UNLIKELY(erts_msacc_key);
#else
ErtsMsAcc *ERTS_WRITE_UNLIKELY(erts_msacc) = NULL;
#endif
+#ifndef ERTS_MSACC_ALWAYS_ON
int ERTS_WRITE_UNLIKELY(erts_msacc_enabled);
+#endif
static Eterm *erts_msacc_state_atoms = NULL;
static erts_rwmtx_t msacc_mutex;
@@ -62,6 +65,12 @@ static ErtsMsAcc *msacc_unmanaged = NULL;
static Uint msacc_unmanaged_count = 0;
#endif
+#if ERTS_MSACC_STATE_COUNT < MAP_SMALL_MAP_LIMIT
+#define DEFAULT_MSACC_MSG_SIZE (3 + 1 + ERTS_MSACC_STATE_COUNT * 2 + 3 + REF_THING_SIZE)
+#else
+#define DEFAULT_MSACC_MSG_SIZE (3 + ERTS_MSACC_STATE_COUNT * 3 + 3 + REF_THING_SIZE)
+#endif
+
/* we have to split initiation as atoms are not inited in early init */
void erts_msacc_early_init(void) {
#ifndef ERTS_MSACC_ALWAYS_ON
@@ -88,7 +97,8 @@ void erts_msacc_init(void) {
void erts_msacc_init_thread(char *type, int id, int managed) {
ErtsMsAcc *msacc;
- msacc = erts_alloc(ERTS_ALC_T_MSACC, sizeof(ErtsMsAcc));
+ msacc = erts_alloc(ERTS_ALC_T_MSACC, sizeof(ErtsMsAcc) +
+ sizeof(ErtsMsAccPerfCntr) * ERTS_MSACC_STATE_COUNT);
msacc->type = strdup(type);
msacc->id = make_small(id);
@@ -122,79 +132,80 @@ void erts_msacc_init_thread(char *type, int id, int managed) {
#endif
}
+#ifdef ERTS_MSACC_EXTENDED_STATES
+
+void erts_msacc_set_bif_state(ErtsMsAcc *__erts_msacc_cache, Eterm mod, void *fn) {
+
+#ifdef ERTS_MSACC_EXTENDED_BIFS
+#define BIF_LIST(Mod,Func,Arity,FuncAddr,Num) \
+ if (fn == &FuncAddr) { \
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATIC_STATE_COUNT + Num); \
+ } else
+#include "erl_bif_list.h"
+#undef BIF_LIST
+ { /* The last else in the macro expansion,
+ this happens for internal bifs, i.e. traps etc */
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF);
+ }
+#else
+ if (mod == am_ets) {
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_ETS);
+ } else {
+ ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_BIF);
+ }
+#endif
+}
+
+#endif
+
/*
* Creates a structure looking like this
* #{ type => scheduler, id => 1, counters => #{ State1 => Counter1 ... StateN => CounterN}}
*/
static
-Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, Eterm **hpp, Uint *szp) {
+Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, ErtsHeapFactory *factory) {
+ Uint sz = 0;
+ Eterm *hp, cvs[ERTS_MSACC_STATE_COUNT];
+ Eterm key, state_map;
int i;
- Eterm *hp;
- Eterm key, state_key, state_map;
- Eterm res = THE_NON_VALUE;
- flatmap_t *map;
-
- if (szp) {
- *szp += MAP_HEADER_FLATMAP_SZ + 1 + 2*(3);
- *szp += MAP_HEADER_FLATMAP_SZ + 1 + 2*(ERTS_MSACC_STATE_COUNT);
- for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
- (void)erts_bld_sint64(NULL,szp,(Sint64)msacc->perf_counters[i]);
+ flatmap_t *map;
+
+ hp = erts_produce_heap(factory, 4, 0);
+ key = TUPLE3(hp,am_counters,am_id,am_type);
+
+ for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
+ cvs[i] = erts_bld_sint64(NULL, &sz,(Sint64)msacc->counters[i].pc);
#ifdef ERTS_MSACC_STATE_COUNTERS
- (void)erts_bld_uint64(NULL,szp,msacc->state_counters[i]);
- *szp += 3; /* tuple to put state+perf counter in */
+ erts_bld_uint64(NULL,&sz,msacc->counters[i].sc);
+ sz += 3;
#endif
- }
}
- if (hpp) {
- Eterm counters[ERTS_MSACC_STATE_COUNT];
- hp = *hpp;
- for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
- Eterm counter = erts_bld_sint64(&hp,NULL,(Sint64)msacc->perf_counters[i]);
+ hp = erts_produce_heap(factory, sz, 0);
+
+ for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
+ cvs[i] = erts_bld_sint64(&hp,NULL,(Sint64)msacc->counters[i].pc);
#ifdef ERTS_MSACC_STATE_COUNTERS
- Eterm counter__ = erts_bld_uint64(&hp,NULL,msacc->state_counters[i]);
- counters[i] = TUPLE2(hp,counter,counter__);
- hp += 3;
-#else
- counters[i] = counter;
+ Eterm counter__ = erts_bld_uint64(&hp,NULL,msacc->counters[i].sc);
+ cvs[i] = TUPLE2(hp,cvs[i],counter__);
+ hp += 3;
#endif
- }
-
- key = TUPLE3(hp,am_counters,am_id,am_type);
- hp += 4;
-
- state_key = make_tuple(hp);
- hp[0] = make_arityval(ERTS_MSACC_STATE_COUNT);
-
- for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++)
- hp[1+i] = erts_msacc_state_atoms[i];
- hp += 1 + ERTS_MSACC_STATE_COUNT;
-
- map = (flatmap_t*)hp;
- hp += MAP_HEADER_FLATMAP_SZ;
- map->thing_word = MAP_HEADER_FLATMAP;
- map->size = ERTS_MSACC_STATE_COUNT;
- map->keys = state_key;
- for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++)
- hp[i] = counters[i];
- hp += ERTS_MSACC_STATE_COUNT;
- state_map = make_flatmap(map);
-
- map = (flatmap_t*)hp;
- hp += MAP_HEADER_FLATMAP_SZ;
- map->thing_word = MAP_HEADER_FLATMAP;
- map->size = 3;
- map->keys = key;
- hp[0] = state_map;
- hp[1] = msacc->id;
- hp[2] = am_atom_put(msacc->type,strlen(msacc->type));
- hp += 3;
-
- *hpp = hp;
- res = make_flatmap(map);
}
- return res;
+ state_map = erts_map_from_ks_and_vs(factory, erts_msacc_state_atoms, cvs,
+ ERTS_MSACC_STATE_COUNT);
+
+ hp = erts_produce_heap(factory, MAP_HEADER_FLATMAP_SZ + 3, 0);
+ map = (flatmap_t*)hp;
+ hp += MAP_HEADER_FLATMAP_SZ;
+ map->thing_word = MAP_HEADER_FLATMAP;
+ map->size = 3;
+ map->keys = key;
+ hp[0] = state_map;
+ hp[1] = msacc->id;
+ hp[2] = am_atom_put(msacc->type,strlen(msacc->type));
+
+ return make_flatmap(map);
}
typedef struct {
@@ -222,40 +233,31 @@ static void send_reply(ErtsMsAcc *msacc, ErtsMSAccReq *msaccrp) {
ErtsSchedulerData *esdp = erts_get_scheduler_data();
Process *rp = msaccrp->proc;
ErtsMessage *msgp = NULL;
- Eterm **hpp, *hp;
+ Eterm *hp;
Eterm ref_copy = NIL, msg;
- Uint sz, *szp;
- ErlOffHeap *ohp = NULL;
ErtsProcLocks rp_locks = (esdp && msaccrp->req_sched == esdp->no
? ERTS_PROC_LOCK_MAIN : 0);
+ ErtsHeapFactory factory;
- sz = 0;
- hpp = NULL;
- szp = &sz;
-
- if (msacc->unmanaged) erts_mtx_lock(&msacc->mtx);
-
- while (1) {
- if (hpp)
- ref_copy = STORE_NC(hpp, ohp, msaccrp->ref);
- else
- *szp += REF_THING_SIZE;
-
- if (msaccrp->action != ERTS_MSACC_GATHER)
- msg = ref_copy;
- else {
- msg = erts_msacc_gather_stats(msacc, hpp, szp);
- msg = erts_bld_tuple(hpp, szp, 2, ref_copy, msg);
- }
- if (hpp)
- break;
-
- msgp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
- hpp = &hp;
- szp = NULL;
- }
+ if (msaccrp->action == ERTS_MSACC_GATHER) {
+
+ msgp = erts_factory_message_create(&factory, rp, &rp_locks, DEFAULT_MSACC_MSG_SIZE);
+
+ if (msacc->unmanaged) erts_mtx_lock(&msacc->mtx);
- if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx);
+ hp = erts_produce_heap(&factory, REF_THING_SIZE + 3 /* tuple */, 0);
+ ref_copy = STORE_NC(&hp, &msgp->hfrag.off_heap, msaccrp->ref);
+ msg = erts_msacc_gather_stats(msacc, &factory);
+ msg = TUPLE2(hp, ref_copy, msg);
+
+ if (msacc->unmanaged) erts_mtx_unlock(&msacc->mtx);
+
+ erts_factory_close(&factory);
+ } else {
+ ErlOffHeap *ohp = NULL;
+ msgp = erts_alloc_message_heap(rp, &rp_locks, REF_THING_SIZE, &hp, &ohp);
+ msg = STORE_NC(&hp, &msgp->hfrag.off_heap, msaccrp->ref);
+ }
erts_queue_message(rp, rp_locks, msgp, msg, am_system);
@@ -308,9 +310,9 @@ static void erts_msacc_reset(ErtsMsAcc *msacc) {
if (msacc->unmanaged) erts_mtx_lock(&msacc->mtx);
for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) {
- msacc->perf_counters[i] = 0;
+ msacc->counters[i].pc = 0;
#ifdef ERTS_MSACC_STATE_COUNTERS
- msacc->state_counters[i] = 0;
+ msacc->counters[i].sc = 0;
#endif
}
@@ -415,7 +417,7 @@ erts_msacc_request(Process *c_p, int action, Eterm *threads)
ErtsSysPerfCounter perf_counter;
/* if enabled update stats */
perf_counter = erts_sys_perf_counter();
- unmanaged[i]->perf_counters[unmanaged[i]->state] +=
+ unmanaged[i]->counters[unmanaged[i]->state].pc +=
perf_counter - unmanaged[i]->perf_counter;
unmanaged[i]->perf_counter = perf_counter;
}
@@ -454,7 +456,7 @@ erts_msacc_request(Process *c_p, int action, Eterm *threads)
for (msacc = msacc_unmanaged; msacc != NULL; msacc = msacc->next) {
erts_mtx_lock(&msacc->mtx);
perf_counter = erts_sys_perf_counter();
- msacc->perf_counters[msacc->state] += perf_counter - msacc->perf_counter;
+ msacc->counters[msacc->state].pc += perf_counter - msacc->perf_counter;
msacc->perf_counter = 0;
erts_mtx_unlock(&msacc->mtx);
}
diff --git a/erts/emulator/beam/erl_msacc.h b/erts/emulator/beam/erl_msacc.h
index 284388f7aa..4c8e1c8e22 100644
--- a/erts/emulator/beam/erl_msacc.h
+++ b/erts/emulator/beam/erl_msacc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2014-2015. All Rights Reserved.
+ * Copyright Ericsson AB 2014-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.
@@ -35,6 +35,10 @@
this reduces overhead a little bit when profiling */
/* #define ERTS_MSACC_ALWAYS_ON 1 */
+/* Uncomment this to keep individual stats for all
+ of the bifs when extended states is enabled */
+/* #define ERTS_MSACC_EXTENDED_BIFS 1 */
+
#define ERTS_MSACC_DISABLE 0
#define ERTS_MSACC_ENABLE 1
#define ERTS_MSACC_RESET 2
@@ -92,7 +96,13 @@ static char *erts_msacc_states[] = {
#define ERTS_MSACC_STATE_SLEEP 13
#define ERTS_MSACC_STATE_TIMERS 14
-#define ERTS_MSACC_STATE_COUNT 15
+#define ERTS_MSACC_STATIC_STATE_COUNT 15
+
+#ifdef ERTS_MSACC_EXTENDED_BIFS
+#define ERTS_MSACC_STATE_COUNT (ERTS_MSACC_STATIC_STATE_COUNT + BIF_SIZE)
+#else
+#define ERTS_MSACC_STATE_COUNT ERTS_MSACC_STATIC_STATE_COUNT
+#endif
#if ERTS_MSACC_STATE_STRINGS
static char *erts_msacc_states[] = {
@@ -111,22 +121,26 @@ static char *erts_msacc_states[] = {
"send",
"sleep",
"timers"
+#ifdef ERTS_MSACC_EXTENDED_BIFS
+#define BIF_LIST(Mod,Func,Arity,FuncAddr,Num) \
+ ,"bif_" #Mod "_" #Func "_" #Arity
+#include "erl_bif_list.h"
+#undef BIF_LIST
+#endif
};
#endif
#endif
typedef struct erl_msacc_t_ ErtsMsAcc;
-
-struct erl_msacc_t_ {
-
- /* the the values below are protected by mtx iff unmanaged = 1 */
- ErtsSysPerfCounter perf_counter;
- ErtsSysPerfCounter perf_counters[ERTS_MSACC_STATE_COUNT];
+typedef struct erl_msacc_p_cnt_t_ {
+ ErtsSysPerfCounter pc;
#ifdef ERTS_MSACC_STATE_COUNTERS
- Uint64 state_counters[ERTS_MSACC_STATE_COUNT];
+ Uint64 sc;
#endif
- Uint state;
+} ErtsMsAccPerfCntr;
+
+struct erl_msacc_t_ {
/* protected by msacc_mutex in erl_msacc.c, and should be constant */
int unmanaged;
@@ -135,12 +149,16 @@ struct erl_msacc_t_ {
erts_tid_t tid;
Eterm id;
char *type;
+
+ /* the the values below are protected by mtx iff unmanaged = 1 */
+ ErtsSysPerfCounter perf_counter;
+ Uint state;
+ ErtsMsAccPerfCntr counters[];
+
};
#if ERTS_ENABLE_MSACC
-#define ERTS_MSACC_INLINE ERTS_GLB_INLINE
-
#ifdef USE_THREADS
extern erts_tsd_key_t erts_msacc_key;
#else
@@ -276,20 +294,20 @@ void erts_msacc_init_thread(char *type, int id, int liberty);
#define ERTS_MSACC_PUSH_AND_SET_STATE_M(state) \
ERTS_MSACC_PUSH_STATE_M(); ERTS_MSACC_SET_STATE_CACHED_M(state)
-ERTS_MSACC_INLINE
+ERTS_GLB_INLINE
void erts_msacc_set_state_um__(ErtsMsAcc *msacc,Uint state,int increment);
-ERTS_MSACC_INLINE
+ERTS_GLB_INLINE
void erts_msacc_set_state_m__(ErtsMsAcc *msacc,Uint state,int increment);
-ERTS_MSACC_INLINE
+ERTS_GLB_INLINE
Uint erts_msacc_get_state_um__(ErtsMsAcc *msacc);
-ERTS_MSACC_INLINE
+ERTS_GLB_INLINE
Uint erts_msacc_get_state_m__(ErtsMsAcc *msacc);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_MSACC_INLINE
+ERTS_GLB_INLINE
Uint erts_msacc_get_state_um__(ErtsMsAcc *msacc) {
Uint state;
if (msacc->unmanaged)
@@ -300,12 +318,12 @@ Uint erts_msacc_get_state_um__(ErtsMsAcc *msacc) {
return state;
}
-ERTS_MSACC_INLINE
+ERTS_GLB_INLINE
Uint erts_msacc_get_state_m__(ErtsMsAcc *msacc) {
return msacc->state;
}
-ERTS_MSACC_INLINE
+ERTS_GLB_INLINE
void erts_msacc_set_state_um__(ErtsMsAcc *msacc, Uint new_state, int increment) {
if (ERTS_UNLIKELY(msacc->unmanaged)) {
erts_mtx_lock(&msacc->mtx);
@@ -322,7 +340,7 @@ void erts_msacc_set_state_um__(ErtsMsAcc *msacc, Uint new_state, int increment)
erts_mtx_unlock(&msacc->mtx);
}
-ERTS_MSACC_INLINE
+ERTS_GLB_INLINE
void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) {
ErtsSysPerfCounter prev_perf_counter;
Sint64 diff;
@@ -334,9 +352,9 @@ void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) {
msacc->perf_counter = erts_sys_perf_counter();
diff = msacc->perf_counter - prev_perf_counter;
ASSERT(diff >= 0);
- msacc->perf_counters[msacc->state] += diff;
+ msacc->counters[msacc->state].pc += diff;
#ifdef ERTS_MSACC_STATE_COUNTERS
- msacc->state_counters[new_state] += increment;
+ msacc->counters[new_state].sc += increment;
#endif
msacc->state = new_state;
}
@@ -364,7 +382,7 @@ void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) {
#define ERTS_MSACC_SET_STATE_CACHED_M(state)
#define ERTS_MSACC_POP_STATE_M()
#define ERTS_MSACC_PUSH_AND_SET_STATE_M(state)
-
+#define ERTS_MSACC_SET_BIF_STATE_CACHED_X(Mod,Addr)
#endif /* ERTS_ENABLE_MSACC */
@@ -385,9 +403,13 @@ void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) {
#define ERTS_MSACC_SET_STATE_CACHED_M_X(state)
#define ERTS_MSACC_POP_STATE_M_X()
#define ERTS_MSACC_PUSH_AND_SET_STATE_M_X(state)
+#define ERTS_MSACC_PUSH_AND_SET_STATE_CACHED_M_X(state)
+#define ERTS_MSACC_SET_BIF_STATE_CACHED_X(Mod,Addr)
#else
+void erts_msacc_set_bif_state(ErtsMsAcc *msacc, Eterm mod, void *addr);
+
#define ERTS_MSACC_PUSH_STATE_X() ERTS_MSACC_PUSH_STATE()
#define ERTS_MSACC_POP_STATE_X() ERTS_MSACC_POP_STATE()
#define ERTS_MSACC_SET_STATE_X(state) ERTS_MSACC_SET_STATE(state)
@@ -403,6 +425,9 @@ void erts_msacc_set_state_m__(ErtsMsAcc *msacc, Uint new_state, int increment) {
#define ERTS_MSACC_SET_STATE_CACHED_M_X(state) ERTS_MSACC_SET_STATE_CACHED_M(state)
#define ERTS_MSACC_POP_STATE_M_X() ERTS_MSACC_POP_STATE_M()
#define ERTS_MSACC_PUSH_AND_SET_STATE_M_X(state) ERTS_MSACC_PUSH_AND_SET_STATE_M(state)
+#define ERTS_MSACC_SET_BIF_STATE_CACHED_X(Mod,Addr) \
+ if (ERTS_MSACC_IS_ENABLED_CACHED_X()) \
+ erts_msacc_set_bif_state(__erts_msacc_cache, Mod, Addr)
#endif /* !ERTS_MSACC_EXTENDED_STATES */
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 23931f0e54..479a2f4809 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2009-2017. 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.
@@ -120,8 +120,10 @@ execution_state(ErlNifEnv *env, Process **c_pp, int *schedp)
else {
Process *c_p = env->proc;
- if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC))
- ASSERT(is_scheduler() > 0);
+ if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)) {
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
+ }
else {
c_p = env->proc->next;
ASSERT(is_scheduler() < 0);
@@ -154,7 +156,9 @@ static Eterm* alloc_heap_heavy(ErlNifEnv* env, size_t need, Eterm* hp)
HEAP_TOP(env->proc) = env->hp;
}
else {
- env->heap_frag->used_size = hp - env->heap_frag->mem;
+ Uint usz = env->hp - env->heap_frag->mem;
+ env->proc->mbuf_sz += usz - env->heap_frag->used_size;
+ env->heap_frag->used_size = usz;
ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
}
hp = erts_heap_alloc(env->proc, need, MIN_HEAP_FRAG_SZ);
@@ -208,11 +212,16 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif,
#endif
}
+static void full_cache_env(ErlNifEnv *env);
+static void cache_env(ErlNifEnv* env);
+static void full_flush_env(ErlNifEnv *env);
+static void flush_env(ErlNifEnv* env);
+
+#ifdef ERTS_DIRTY_SCHEDULERS
void erts_pre_dirty_nif(ErtsSchedulerData *esdp,
- ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif,
- Process* tracee)
+ ErlNifEnv* env, Process* p,
+ struct erl_module_nif* mod_nif)
{
-#ifdef ERTS_DIRTY_SCHEDULERS
Process *sproc;
#ifdef DEBUG
erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state);
@@ -223,7 +232,7 @@ void erts_pre_dirty_nif(ErtsSchedulerData *esdp,
ASSERT(esdp);
#endif
- erts_pre_nif(env, p, mod_nif, tracee);
+ erts_pre_nif(env, p, mod_nif, NULL);
sproc = esdp->dirty_shadow_process;
ASSERT(sproc);
@@ -235,22 +244,10 @@ void erts_pre_dirty_nif(ErtsSchedulerData *esdp,
sproc->next = p;
sproc->common.id = p->common.id;
- sproc->htop = p->htop;
- sproc->stop = p->stop;
- sproc->hend = p->hend;
- sproc->heap = p->heap;
- sproc->abandoned_heap = p->abandoned_heap;
- sproc->heap_sz = p->heap_sz;
- sproc->high_water = p->high_water;
- sproc->old_hend = p->old_hend;
- sproc->old_htop = p->old_htop;
- sproc->old_heap = p->old_heap;
- sproc->mbuf = NULL;
- sproc->mbuf_sz = 0;
- ERTS_INIT_OFF_HEAP(&sproc->off_heap);
env->proc = sproc;
-#endif
+ full_cache_env(env);
}
+#endif
/* Temporary object header, auto-deallocated when NIF returns
* or when independent environment is cleared.
@@ -274,32 +271,37 @@ static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env)
void erts_post_nif(ErlNifEnv* env)
{
erts_unblock_fpe(env->fpe_was_unmasked);
+ full_flush_env(env);
+ free_tmp_objs(env);
+ env->exiting = ERTS_PROC_IS_EXITING(env->proc);
+}
#ifdef ERTS_DIRTY_SCHEDULERS
- if (!(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+void erts_post_dirty_nif(ErlNifEnv* env)
+{
+ Process *c_p;
+ ASSERT(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC);
+ ASSERT(env->proc->next);
+ erts_unblock_fpe(env->fpe_was_unmasked);
+ full_flush_env(env);
+ free_tmp_objs(env);
+ c_p = env->proc->next;
+ env->exiting = ERTS_PROC_IS_EXITING(c_p);
+ ERTS_VBUMP_ALL_REDS(c_p);
+}
#endif
- {
- ASSERT(is_scheduler() > 0);
- if (env->heap_frag == NULL) {
- ASSERT(env->hp_end == HEAP_LIMIT(env->proc));
- ASSERT(env->hp >= HEAP_TOP(env->proc));
- ASSERT(env->hp <= HEAP_LIMIT(env->proc));
- HEAP_TOP(env->proc) = env->hp;
- }
- else {
- ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
- ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
- env->heap_frag->used_size = env->hp - env->heap_frag->mem;
- ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
- }
- env->exiting = ERTS_PROC_IS_EXITING(env->proc);
- }
+
+static void full_flush_env(ErlNifEnv* env)
+{
#ifdef ERTS_DIRTY_SCHEDULERS
- else { /* Dirty nif call using shadow process struct */
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ /* Dirty nif call using shadow process struct */
Process *c_p = env->proc->next;
ASSERT(is_scheduler() < 0);
ASSERT(env->proc->common.id == c_p->common.id);
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
if (!env->heap_frag) {
ASSERT(env->hp_end == HEAP_LIMIT(c_p));
@@ -308,11 +310,14 @@ void erts_post_nif(ErlNifEnv* env)
HEAP_TOP(c_p) = env->hp;
}
else {
+ Uint usz;
ASSERT(env->hp_end != HEAP_LIMIT(c_p));
ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
HEAP_TOP(c_p) = HEAP_TOP(env->proc);
- env->heap_frag->used_size = env->hp - env->heap_frag->mem;
+ usz = env->hp - env->heap_frag->mem;
+ env->proc->mbuf_sz += usz - env->heap_frag->used_size;
+ env->heap_frag->used_size = usz;
ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
@@ -339,11 +344,48 @@ void erts_post_nif(ErlNifEnv* env)
}
c_p->off_heap.overhead += env->proc->off_heap.overhead;
- env->exiting = ERTS_PROC_IS_EXITING(c_p);
- BUMP_ALL_REDS(c_p);
+ return;
}
#endif
- free_tmp_objs(env);
+
+ flush_env(env);
+}
+
+static void full_cache_env(ErlNifEnv* env)
+{
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ /* Dirty nif call using shadow process struct */
+ Process *sproc = env->proc;
+ Process *c_p = sproc->next;
+ ASSERT(c_p);
+ ASSERT(is_scheduler() < 0);
+ ASSERT(env->proc->common.id == c_p->common.id);
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
+
+ sproc->htop = c_p->htop;
+ sproc->stop = c_p->stop;
+ sproc->hend = c_p->hend;
+ sproc->heap = c_p->heap;
+ sproc->abandoned_heap = c_p->abandoned_heap;
+ sproc->heap_sz = c_p->heap_sz;
+ sproc->high_water = c_p->high_water;
+ sproc->old_hend = c_p->old_hend;
+ sproc->old_htop = c_p->old_htop;
+ sproc->old_heap = c_p->old_heap;
+ sproc->mbuf = NULL;
+ sproc->mbuf_sz = 0;
+ ERTS_INIT_OFF_HEAP(&sproc->off_heap);
+
+ env->hp_end = HEAP_LIMIT(c_p);
+ env->hp = HEAP_TOP(c_p);
+ env->heap_frag = NULL;
+ return;
+ }
+#endif
+
+ cache_env(env);
}
/* Flush out our cached heap pointers to allow an ordinary HAlloc
@@ -357,9 +399,12 @@ static void flush_env(ErlNifEnv* env)
HEAP_TOP(env->proc) = env->hp;
}
else {
+ Uint usz;
ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
- env->heap_frag->used_size = env->hp - env->heap_frag->mem;
+ usz = env->hp - env->heap_frag->mem;
+ env->proc->mbuf_sz += usz - env->heap_frag->used_size;
+ env->heap_frag->used_size = usz;
ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
}
}
@@ -600,17 +645,32 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (scheduler > 0) { /* Normal scheduler */
rp = erts_proc_lookup(receiver);
- if (c_p == rp)
- rp_locks = ERTS_PROC_LOCK_MAIN;
+ if (!rp)
+ return 0;
}
else {
- if (c_p && ERTS_PROC_IS_EXITING(c_p))
- return 0;
- rp = erts_pid2proc_opt(c_p, 0, receiver, rp_locks,
+ if (c_p) {
+ ASSERT(scheduler < 0); /* Dirty scheduler */
+ if (ERTS_PROC_IS_EXITING(c_p))
+ return 0;
+
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ }
+
+ rp = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
+ receiver, rp_locks,
ERTS_P2P_FLG_INC_REFC);
+ if (!rp) {
+ if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ return 0;
+ }
}
- if (rp == NULL)
- return 0;
+
+ if (c_p == rp)
+ rp_locks = ERTS_PROC_LOCK_MAIN;
if (menv) {
flush_env(msg_env);
@@ -630,10 +690,10 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
Uint sz = size_object(msg);
ErlOffHeap *ohp;
Eterm *hp;
- if (env && !env->tracee) {
- flush_env(env);
+ if (c_p && !env->tracee) {
+ full_flush_env(env);
mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
- cache_env(env);
+ full_cache_env(env);
}
else {
erts_aint_t state = erts_smp_atomic32_read_nob(&rp->state);
@@ -656,8 +716,11 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (!env || !env->tracee) {
- if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND))
+ if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) {
+ full_flush_env(env);
trace_send(c_p, receiver, msg);
+ full_cache_env(env);
+ }
}
#ifdef ERTS_SMP
else {
@@ -690,10 +753,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
erts_smp_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
if (!msgq) {
-#ifdef ERTS_SMP
- ErtsThrPrgrDelayHandle dhndl;
-#endif
-
msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE,
sizeof(ErlTraceMessageQueue));
msgq->receiver = receiver;
@@ -707,15 +766,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE);
-#ifdef ERTS_SMP
- if (!scheduler)
- dhndl = erts_thr_progress_unmanaged_delay();
-#endif
- erts_schedule_flush_trace_messages(t_p->common.id);
-#ifdef ERTS_SMP
- if (!scheduler)
- erts_thr_progress_unmanaged_continue(dhndl);
-#endif
+ erts_schedule_flush_trace_messages(t_p, 0);
} else {
msgq->len++;
*msgq->last = mp;
@@ -740,6 +791,8 @@ done:
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
if (rp_locks & ~lc_locks)
erts_smp_proc_unlock(rp, rp_locks & ~lc_locks);
+ if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
#endif
if (scheduler <= 0)
erts_proc_dec_refc(rp);
@@ -2215,7 +2268,7 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent)
* NIF exports need a few more items than the Export struct provides,
* including the erl_module_nif* and a NIF function pointer, so the
* NifExport below adds those. The Export member must be first in the
- * struct. The saved_mfa, exception_thrown, saved_argc, rootset_extra, and
+ * struct. The saved_current, exception_thrown, saved_argc, rootset_extra, and
* rootset members are used to track the MFA, any pending exception, and
* arguments of the top NIF in case a chain of one or more
* enif_schedule_nif() calls results in an exception, since in that case
@@ -2229,7 +2282,7 @@ typedef struct {
Export exp;
struct erl_module_nif* m;
NativeFunPtr fp;
- Eterm saved_mfa[3];
+ BeamInstr *saved_current;
int exception_thrown;
int saved_argc;
int rootset_extra;
@@ -2256,6 +2309,17 @@ erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj)
return gc;
}
+int
+erts_check_nif_export_in_area(Process *p, char *start, Uint size)
+{
+ NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(p);
+ if (!nep || !nep->saved_current)
+ return 0;
+ if (ErtsInArea(nep->saved_current, start, size))
+ return 1;
+ return 0;
+}
+
/*
* Allocate a NifExport and set it in proc specific data
*/
@@ -2307,6 +2371,7 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec
Eterm* reg;
NifExport* ep;
int i, scheduler;
+ int orig_argc;
execution_state(env, &proc, &scheduler);
@@ -2317,11 +2382,14 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec
reg = erts_proc_sched_data(proc)->x_reg_array;
+ ASSERT(!need_save || proc->current);
+ orig_argc = need_save ? (int) proc->current[2] : 0;
+
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
if (!ep)
- ep = allocate_nif_sched_data(proc, argc);
- else if (need_save && ep->rootset_extra < argc) {
- NifExport* new_ep = allocate_nif_sched_data(proc, argc);
+ ep = allocate_nif_sched_data(proc, orig_argc);
+ else if (need_save && ep->rootset_extra < orig_argc) {
+ NifExport* new_ep = allocate_nif_sched_data(proc, orig_argc);
destroy_nif_export(ep);
ep = new_ep;
}
@@ -2334,17 +2402,14 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec
}
if (scheduler > 0)
ERTS_VBUMP_ALL_REDS(proc);
- for (i = 0; i < argc; i++) {
- if (need_save)
- ep->rootset[i+1] = reg[i];
- reg[i] = (Eterm) argv[i];
- }
if (need_save) {
- ep->saved_mfa[0] = proc->current[0];
- ep->saved_mfa[1] = proc->current[1];
- ep->saved_mfa[2] = proc->current[2];
- ep->saved_argc = argc;
+ ep->saved_current = proc->current;
+ ep->saved_argc = orig_argc;
+ for (i = 0; i < orig_argc; i++)
+ ep->rootset[i+1] = reg[i];
}
+ for (i = 0; i < argc; i++)
+ reg[i] = (Eterm) argv[i];
proc->i = (BeamInstr*) ep->exp.addressv[0];
ep->exp.code[0] = (BeamInstr) proc->current[0];
ep->exp.code[1] = (BeamInstr) proc->current[1];
@@ -2365,21 +2430,21 @@ static void
restore_nif_mfa(Process* proc, NifExport* ep, int exception)
{
int i;
- Eterm* reg = erts_proc_sched_data(proc)->x_reg_array;
ERTS_SMP_LC_ASSERT(!(proc->static_flags
& ERTS_STC_FLG_SHADOW_PROC));
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(proc)
& ERTS_PROC_LOCK_MAIN);
- proc->current[0] = ep->saved_mfa[0];
- proc->current[1] = ep->saved_mfa[1];
- proc->current[2] = ep->saved_mfa[2];
- if (exception)
+ ASSERT(ep->saved_current != &ep->exp.code[0]);
+ proc->current = ep->saved_current;
+ ep->saved_current = NULL;
+ if (exception) {
+ Eterm* reg = erts_proc_sched_data(proc)->x_reg_array;
for (i = 0; i < ep->saved_argc; i++)
reg[i] = ep->rootset[i+1];
+ }
ep->saved_argc = 0;
- ep->saved_mfa[0] = THE_NON_VALUE;
}
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -2454,6 +2519,7 @@ execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
*/
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
ASSERT(ep && fp);
+
ep->fp = NULL;
erts_smp_atomic32_read_band_mb(&proc->state, ~(ERTS_PSFLG_DIRTY_CPU_PROC
| ERTS_PSFLG_DIRTY_IO_PROC));
@@ -2538,7 +2604,7 @@ schedule_dirty_nif(ErlNifEnv* env, int flags, int argc, const ERL_NIF_TERM argv[
}
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
- need_save = (ep == NULL || is_non_value(ep->saved_mfa[0]));
+ need_save = (ep == NULL || !ep->saved_current);
result = init_nif_sched_data(env, execute_dirty_nif, fp, need_save, argc, argv);
if (scheduler <= 0)
erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
@@ -2616,7 +2682,7 @@ enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags,
}
ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
- need_save = (ep == NULL || is_non_value(ep->saved_mfa[0]));
+ need_save = (ep == NULL || !ep->saved_current);
if (flags) {
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -3015,16 +3081,16 @@ Eterm erts_nif_taints(Process* p)
return list;
}
-void erts_print_nif_taints(int to, void* to_arg)
+void erts_print_nif_taints(fmtfn_t to, void* to_arg)
{
struct tainted_module_t* t;
const char* delim = "";
for (t=first_tainted_module ; t!=NULL; t=t->next) {
const Atom* atom = atom_tab(atom_val(t->module_atom));
- erts_print(to,to_arg,"%s%.*s", delim, atom->len, atom->name);
+ erts_cbprintf(to,to_arg,"%s%.*s", delim, atom->len, atom->name);
delim = ",";
}
- erts_print(to,to_arg,"\n");
+ erts_cbprintf(to,to_arg,"\n");
}
@@ -3150,18 +3216,20 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
if (init_func != NULL)
handle = init_func;
+ this_mi = &module_p->curr;
+ prev_mi = &module_p->old;
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;
+ ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
+ "module '%T' not allowed", mod_atom);
+ goto error;
+ } else if (module_p->on_load) {
+ ASSERT(module_p->on_load->code_hdr->on_load_function_ptr);
+ if (module_p->curr.code_hdr) {
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;
+ prev_mi = &module_p->old;
}
- } else {
- this_mi = &module_p->curr;
- prev_mi = &module_p->old;
+ this_mi = module_p->on_load;
}
if (init_func == NULL &&
@@ -3305,7 +3373,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
veto = entry->reload(&env, &lib->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
- ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful.");
+ ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful (%d).", veto);
}
else {
commit_opened_resource_types(lib);
@@ -3327,7 +3395,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
erts_post_nif(&env);
if (veto) {
prev_mi->nif->priv_data = prev_old_data;
- ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful.");
+ ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful (%d).", veto);
}
else
commit_opened_resource_types(lib);
@@ -3337,7 +3405,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
veto = entry->load(&env, &lib->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
- ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful.");
+ ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful (%d).", veto);
}
else
commit_opened_resource_types(lib);
@@ -3493,8 +3561,8 @@ Eterm erts_nif_call_function(Process *p, Process *tracee,
#endif
if (p) {
/* This is almost a normal nif call like in beam_emu,
- except that any heap fragment created in the nif will be
- discarded without checking if anything in it is live.
+ except that any heap consumed by the nif will be
+ released without checking if anything in it is live.
This is because we cannot do a GC here as we don't know
the number of live registers that have to be preserved.
This means that any heap part of the returned term may
@@ -3502,6 +3570,7 @@ Eterm erts_nif_call_function(Process *p, Process *tracee,
struct enif_environment_t env;
ErlHeapFragment *orig_hf = MBUF(p);
ErlOffHeap orig_oh = MSO(p);
+ Eterm *orig_htop = HEAP_TOP(p);
ASSERT(is_internal_pid(p->common.id));
MBUF(p) = NULL;
clear_offheap(&MSO(p));
@@ -3523,6 +3592,7 @@ Eterm erts_nif_call_function(Process *p, Process *tracee,
/* restore original heap fragment list */
MBUF(p) = orig_hf;
MSO(p) = orig_oh;
+ HEAP_TOP(p) = orig_htop;
} else {
/* Nif call was done without a process context,
so we create a phony one. */
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 646f786651..f37393bce4 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2017. 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.
@@ -98,7 +98,7 @@ dist_table_alloc(void *dep_tmpl)
dist_entries++;
dep->prev = NULL;
- erts_refc_init(&dep->refc, -1);
+ erts_smp_refc_init(&dep->refc, -1);
erts_smp_rwmtx_init_opt_x(&dep->rwmtx, &rwmtx_opt, "dist_entry", chnl_nr);
dep->sysname = sysname;
dep->cid = NIL;
@@ -188,7 +188,7 @@ dist_table_free(void *vdep)
void
-erts_dist_table_info(int to, void *to_arg)
+erts_dist_table_info(fmtfn_t to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
@@ -208,7 +208,7 @@ erts_channel_no_to_dist_entry(Uint cno)
* to the node name is used as channel no.
*/
if(cno == ERST_INTERNAL_CHANNEL_NO) {
- erts_refc_inc(&erts_this_dist_entry->refc, 2);
+ erts_smp_refc_inc(&erts_this_dist_entry->refc, 2);
return erts_this_dist_entry;
}
@@ -231,16 +231,16 @@ erts_sysname_to_connected_dist_entry(Eterm sysname)
de.sysname = sysname;
if(erts_this_dist_entry->sysname == sysname) {
- erts_refc_inc(&erts_this_dist_entry->refc, 2);
+ erts_smp_refc_inc(&erts_this_dist_entry->refc, 2);
return erts_this_dist_entry;
}
erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx);
res_dep = (DistEntry *) hash_get(&erts_dist_table, (void *) &de);
if (res_dep) {
- erts_aint_t refc = erts_refc_inctest(&res_dep->refc, 1);
+ erts_aint_t refc = erts_smp_refc_inctest(&res_dep->refc, 1);
if (refc < 2) /* Pending delete */
- erts_refc_inc(&res_dep->refc, 1);
+ erts_smp_refc_inc(&res_dep->refc, 1);
}
erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx);
if (res_dep) {
@@ -267,9 +267,9 @@ DistEntry *erts_find_or_insert_dist_entry(Eterm sysname)
de.sysname = sysname;
erts_smp_rwmtx_rwlock(&erts_dist_table_rwmtx);
res = hash_put(&erts_dist_table, (void *) &de);
- refc = erts_refc_inctest(&res->refc, 0);
+ refc = erts_smp_refc_inctest(&res->refc, 0);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&res->refc, 1);
+ erts_smp_refc_inc(&res->refc, 1);
erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx);
return res;
}
@@ -282,9 +282,9 @@ DistEntry *erts_find_dist_entry(Eterm sysname)
erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx);
res = hash_get(&erts_dist_table, (void *) &de);
if (res) {
- erts_aint_t refc = erts_refc_inctest(&res->refc, 1);
+ erts_aint_t refc = erts_smp_refc_inctest(&res->refc, 1);
if (refc < 2) /* Pending delete */
- erts_refc_inc(&res->refc, 1);
+ erts_smp_refc_inc(&res->refc, 1);
}
erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx);
return res;
@@ -311,7 +311,7 @@ static void try_delete_dist_entry(void *vdep)
*
* If refc > 0, the entry is in use. Keep the entry.
*/
- refc = erts_refc_dectest(&dep->refc, -1);
+ refc = erts_smp_refc_dectest(&dep->refc, -1);
if (refc == -1)
(void) hash_erase(&erts_dist_table, (void *) dep);
erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx);
@@ -518,7 +518,7 @@ node_table_alloc(void *venp_tmpl)
node_entries++;
- erts_refc_init(&enp->refc, -1);
+ erts_smp_refc_init(&enp->refc, -1);
enp->creation = ((ErlNode *) venp_tmpl)->creation;
enp->sysname = ((ErlNode *) venp_tmpl)->sysname;
enp->dist_entry = erts_find_or_insert_dist_entry(((ErlNode *) venp_tmpl)->sysname);
@@ -564,7 +564,7 @@ erts_node_table_size(void)
}
void
-erts_node_table_info(int to, void *to_arg)
+erts_node_table_info(fmtfn_t to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
@@ -585,9 +585,9 @@ ErlNode *erts_find_or_insert_node(Eterm sysname, Uint32 creation)
erts_smp_rwmtx_rlock(&erts_node_table_rwmtx);
res = hash_get(&erts_node_table, (void *) &ne);
if (res && res != erts_this_node) {
- erts_aint_t refc = erts_refc_inctest(&res->refc, 0);
+ erts_aint_t refc = erts_smp_refc_inctest(&res->refc, 0);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&res->refc, 1);
+ erts_smp_refc_inc(&res->refc, 1);
}
erts_smp_rwmtx_runlock(&erts_node_table_rwmtx);
if (res)
@@ -597,9 +597,9 @@ ErlNode *erts_find_or_insert_node(Eterm sysname, Uint32 creation)
res = hash_put(&erts_node_table, (void *) &ne);
ASSERT(res);
if (res != erts_this_node) {
- erts_aint_t refc = erts_refc_inctest(&res->refc, 0);
+ erts_aint_t refc = erts_smp_refc_inctest(&res->refc, 0);
if (refc < 2) /* New or pending delete */
- erts_refc_inc(&res->refc, 1);
+ erts_smp_refc_inc(&res->refc, 1);
}
erts_smp_rwmtx_rwunlock(&erts_node_table_rwmtx);
return res;
@@ -626,7 +626,7 @@ static void try_delete_node(void *venp)
*
* If refc > 0, the entry is in use. Keep the entry.
*/
- refc = erts_refc_dectest(&enp->refc, -1);
+ refc = erts_smp_refc_dectest(&enp->refc, -1);
if (refc == -1)
(void) hash_erase(&erts_node_table, (void *) enp);
erts_smp_rwmtx_rwunlock(&erts_node_table_rwmtx);
@@ -649,7 +649,7 @@ void erts_schedule_delete_node(ErlNode *enp)
}
struct pn_data {
- int to;
+ fmtfn_t to;
void *to_arg;
Eterm sysname;
int no_sysname;
@@ -672,14 +672,14 @@ static void print_node(void *venp, void *vpndp)
erts_print(pndp->to, pndp->to_arg, " %d", enp->creation);
#ifdef DEBUG
erts_print(pndp->to, pndp->to_arg, " (refc=%ld)",
- erts_refc_read(&enp->refc, 0));
+ erts_smp_refc_read(&enp->refc, 0));
#endif
pndp->no_sysname++;
}
pndp->no_total++;
}
-void erts_print_node_info(int to,
+void erts_print_node_info(fmtfn_t to,
void *to_arg,
Eterm sysname,
int *no_sysname,
@@ -715,19 +715,19 @@ void
erts_set_this_node(Eterm sysname, Uint creation)
{
ERTS_SMP_LC_ASSERT(erts_thr_progress_is_blocking());
- ASSERT(erts_refc_read(&erts_this_dist_entry->refc, 2));
+ ASSERT(erts_smp_refc_read(&erts_this_dist_entry->refc, 2));
- if (erts_refc_dectest(&erts_this_node->refc, 0) == 0)
+ if (erts_smp_refc_dectest(&erts_this_node->refc, 0) == 0)
try_delete_node(erts_this_node);
- if (erts_refc_dectest(&erts_this_dist_entry->refc, 0) == 0)
+ if (erts_smp_refc_dectest(&erts_this_dist_entry->refc, 0) == 0)
try_delete_dist_entry(erts_this_dist_entry);
erts_this_node = NULL; /* to make sure refc is bumped for this node */
erts_this_node = erts_find_or_insert_node(sysname, creation);
erts_this_dist_entry = erts_this_node->dist_entry;
- erts_refc_inc(&erts_this_dist_entry->refc, 2);
+ erts_smp_refc_inc(&erts_this_dist_entry->refc, 2);
erts_this_node_sysname = erts_this_node_sysname_BUFFER;
erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname_BUFFER),
@@ -789,13 +789,13 @@ void erts_init_node_tables(int dd_sec)
node_tmpl.creation = 0;
erts_this_node = hash_put(&erts_node_table, &node_tmpl);
/* +1 for erts_this_node */
- erts_refc_init(&erts_this_node->refc, 1);
+ erts_smp_refc_init(&erts_this_node->refc, 1);
ASSERT(erts_this_node->dist_entry != NULL);
erts_this_dist_entry = erts_this_node->dist_entry;
/* +1 for erts_this_dist_entry */
/* +1 for erts_this_node->dist_entry */
- erts_refc_init(&erts_this_dist_entry->refc, 2);
+ erts_smp_refc_init(&erts_this_dist_entry->refc, 2);
erts_this_node_sysname = erts_this_node_sysname_BUFFER;
@@ -1623,7 +1623,7 @@ reference_table_term(Uint **hpp, Uint *szp)
tup = MK_2TUP(referred_nodes[i].node->sysname,
MK_UINT(referred_nodes[i].node->creation));
- tup = MK_3TUP(tup, MK_UINT(erts_refc_read(&referred_nodes[i].node->refc, 0)), nril);
+ tup = MK_3TUP(tup, MK_UINT(erts_smp_refc_read(&referred_nodes[i].node->refc, 0)), nril);
nl = MK_CONS(tup, nl);
}
@@ -1684,7 +1684,7 @@ reference_table_term(Uint **hpp, Uint *szp)
/* DistList = [{Dist, Refc, ReferenceIdList}] */
tup = MK_3TUP(referred_dists[i].dist->sysname,
- MK_UINT(erts_refc_read(&referred_dists[i].dist->refc, 0)),
+ MK_UINT(erts_smp_refc_read(&referred_dists[i].dist->refc, 0)),
dril);
dl = MK_CONS(tup, dl);
}
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index 7a4434acbf..489da1ba17 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2017. 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.
@@ -107,7 +107,7 @@ typedef struct dist_entry_ {
HashBucket hash_bucket; /* Hash bucket */
struct dist_entry_ *next; /* Next entry in dist_table (not sorted) */
struct dist_entry_ *prev; /* Previous entry in dist_table (not sorted) */
- erts_refc_t refc; /* Reference count */
+ erts_smp_refc_t refc; /* Reference count */
erts_smp_rwmtx_t rwmtx; /* Protects all fields below until lck_mtx. */
Eterm sysname; /* name@host atom for efficiency */
@@ -149,7 +149,7 @@ typedef struct dist_entry_ {
typedef struct erl_node_ {
HashBucket hash_bucket; /* Hash bucket */
- erts_refc_t refc; /* Reference count */
+ erts_smp_refc_t refc; /* Reference count */
Eterm sysname; /* name@host atom for efficiency */
Uint32 creation; /* Creation */
DistEntry *dist_entry; /* Corresponding dist entry */
@@ -179,7 +179,7 @@ DistEntry *erts_find_or_insert_dist_entry(Eterm);
DistEntry *erts_find_dist_entry(Eterm);
void erts_schedule_delete_dist_entry(DistEntry *);
Uint erts_dist_table_size(void);
-void erts_dist_table_info(int, void *);
+void erts_dist_table_info(fmtfn_t, void *);
void erts_set_dist_entry_not_connected(DistEntry *);
void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint);
ErlNode *erts_find_or_insert_node(Eterm, Uint32);
@@ -187,8 +187,8 @@ void erts_schedule_delete_node(ErlNode *);
void erts_set_this_node(Eterm, Uint);
Uint erts_node_table_size(void);
void erts_init_node_tables(int);
-void erts_node_table_info(int, void *);
-void erts_print_node_info(int, void *, Eterm, int*, int*);
+void erts_node_table_info(fmtfn_t, void *);
+void erts_print_node_info(fmtfn_t, void *, Eterm, int*, int*);
Eterm erts_get_node_and_dist_references(struct process *);
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
int erts_lc_is_de_rwlocked(DistEntry *);
@@ -210,7 +210,7 @@ ERTS_GLB_INLINE void
erts_deref_dist_entry(DistEntry *dep)
{
ASSERT(dep);
- if (erts_refc_dectest(&dep->refc, 0) == 0)
+ if (erts_smp_refc_dectest(&dep->refc, 0) == 0)
erts_schedule_delete_dist_entry(dep);
}
@@ -218,7 +218,7 @@ ERTS_GLB_INLINE void
erts_deref_node_entry(ErlNode *np)
{
ASSERT(np);
- if (erts_refc_dectest(&np->refc, 0) == 0)
+ if (erts_smp_refc_dectest(&np->refc, 0) == 0)
erts_schedule_delete_node(np);
}
diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h
index f90844ccc8..c59b42cdae 100644
--- a/erts/emulator/beam/erl_port.h
+++ b/erts/emulator/beam/erl_port.h
@@ -369,7 +369,7 @@ Eterm erts_request_io_bytes(Process *c_p);
#define ERTS_PORT_REDS_INFO (CONTEXT_REDS/100)
#define ERTS_PORT_REDS_TERMINATE (CONTEXT_REDS/50)
-void print_port_info(Port *, int, void *);
+void print_port_info(Port *, fmtfn_t, void *);
void erts_port_free(Port *);
#ifndef ERTS_SMP
void erts_port_cleanup(Port *);
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index 3102e44c11..ab4dd5f5d3 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -852,10 +852,11 @@ schedule_port_task_handle_list_free(ErtsPortTaskHandleList *pthlp)
}
static ERTS_INLINE void
-abort_nosuspend_task(Port *pp,
- ErtsPortTaskType type,
- ErtsPortTaskTypeData *tdp,
- int bpq_data)
+abort_signal_task(Port *pp,
+ int abort_type,
+ ErtsPortTaskType type,
+ ErtsPortTaskTypeData *tdp,
+ int bpq_data)
{
ASSERT(type == ERTS_PORT_TASK_PROC_SIG);
@@ -863,18 +864,28 @@ abort_nosuspend_task(Port *pp,
if (!bpq_data)
tdp->psig.callback(NULL,
ERTS_PORT_SFLG_INVALID,
- ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND,
+ abort_type,
&tdp->psig.data);
else {
ErlDrvSizeT size = erts_proc2port_sig_command_data_size(&tdp->psig.data);
tdp->psig.callback(NULL,
ERTS_PORT_SFLG_INVALID,
- ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND,
+ abort_type,
&tdp->psig.data);
aborted_proc2port_data(pp, size);
}
}
+
+static ERTS_INLINE void
+abort_nosuspend_task(Port *pp,
+ ErtsPortTaskType type,
+ ErtsPortTaskTypeData *tdp,
+ int bpq_data)
+{
+ abort_signal_task(pp, ERTS_PROC2PORT_SIG_ABORT_NOSUSPEND, type, tdp, bpq_data);
+}
+
static ErtsPortTaskHandleList *
get_free_nosuspend_handles(Port *pp)
{
@@ -1463,10 +1474,10 @@ erts_port_task_schedule(Eterm id,
}
#endif
- if (!pp)
- goto fail;
-
if (type != ERTS_PORT_TASK_PROC_SIG) {
+ if (!pp)
+ goto fail;
+
ptp = port_task_alloc();
ptp->type = type;
@@ -1504,6 +1515,9 @@ erts_port_task_schedule(Eterm id,
ptp->u.alive.td.psig.callback = va_arg(argp, ErtsProc2PortSigCallback);
ptp->u.alive.flags |= va_arg(argp, int);
va_end(argp);
+ if (!pp)
+ goto fail;
+
if (!(ptp->u.alive.flags & ERTS_PT_FLG_NOSUSPEND))
set_tmp_handle(ptp, pthp);
else {
@@ -1613,8 +1627,9 @@ abort_nosuspend:
ASSERT(ns_pthlp);
erts_free(ERTS_ALC_T_PT_HNDL_LIST, ns_pthlp);
- if (ptp)
- port_task_free(ptp);
+
+ ASSERT(ptp);
+ port_task_free(ptp);
return 0;
@@ -1625,12 +1640,15 @@ fail:
erts_port_dec_refc(pp);
#endif
+ if (ptp) {
+ abort_signal_task(pp, ERTS_PROC2PORT_SIG_ABORT,
+ ptp->type, &ptp->u.alive.td, 0);
+ port_task_free(ptp);
+ }
+
if (ns_pthlp)
erts_free(ERTS_ALC_T_PT_HNDL_LIST, ns_pthlp);
- if (ptp)
- port_task_free(ptp);
-
return -1;
}
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 66f22979ad..54724fd62d 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -154,6 +154,7 @@ int ERTS_WRITE_UNLIKELY(erts_eager_check_io) = 1;
int ERTS_WRITE_UNLIKELY(erts_sched_compact_load);
int ERTS_WRITE_UNLIKELY(erts_sched_balance_util) = 0;
Uint ERTS_WRITE_UNLIKELY(erts_no_schedulers);
+Uint ERTS_WRITE_UNLIKELY(erts_no_total_schedulers);
Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_cpu_schedulers) = 0;
Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_io_schedulers) = 0;
@@ -448,6 +449,9 @@ int erts_system_profile_ts_type = ERTS_TRACE_FLG_NOW_TIMESTAMP;
typedef enum {
ERTS_PSTT_GC, /* Garbage Collect */
ERTS_PSTT_CPC, /* Check Process Code */
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ ERTS_PSTT_CLA, /* Copy Literal Area */
+#endif
ERTS_PSTT_COHMQ, /* Change off heap message queue */
ERTS_PSTT_FTMQ /* Flush trace msg queue */
} ErtsProcSysTaskType;
@@ -554,8 +558,8 @@ do { \
*/
static void exec_misc_ops(ErtsRunQueue *);
-static void print_function_from_pc(int to, void *to_arg, BeamInstr* x);
-static int stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg);
+static void print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x);
+static int stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg);
static void aux_work_timeout(void *unused);
static void aux_work_timeout_early_init(int no_schedulers);
@@ -2056,6 +2060,7 @@ handle_thr_prgr_later_op(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int wait
#endif
for (lops = 0; lops < ERTS_MAX_THR_PRGR_LATER_OPS; lops++) {
ErtsThrPrgrLaterOp *lop = awdp->later_op.first;
+
if (!erts_thr_progress_has_reached_this(current, lop->later))
return aux_work & ~ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP;
awdp->later_op.first = lop->next;
@@ -5905,9 +5910,12 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online
n = (int) no_schedulers;
erts_no_schedulers = n;
+ erts_no_total_schedulers = n;
#ifdef ERTS_DIRTY_SCHEDULERS
erts_no_dirty_cpu_schedulers = no_dirty_cpu_schedulers;
+ erts_no_total_schedulers += no_dirty_cpu_schedulers;
erts_no_dirty_io_schedulers = no_dirty_io_schedulers;
+ erts_no_total_schedulers += no_dirty_io_schedulers;
#endif
/* Create and initialize scheduler sleep info */
@@ -6227,6 +6235,12 @@ check_dirty_enqueue_in_prio_queue(Process *c_p,
int queue;
erts_aint32_t dact, max_qbit;
+ /* Do not enqueue free process... */
+ if (actual & ERTS_PSFLG_FREE) {
+ *newp &= ~ERTS_PSFLGS_DIRTY_WORK;
+ return ERTS_ENQUEUE_NOT;
+ }
+
/* Termination should be done on an ordinary scheduler */
if ((*newp) & ERTS_PSFLG_EXITING) {
*newp &= ~ERTS_PSFLGS_DIRTY_WORK;
@@ -6268,7 +6282,11 @@ check_dirty_enqueue_in_prio_queue(Process *c_p,
return -1*queue;
}
- *newp |= ERTS_PSFLG_IN_RUNQ;
+ /*
+ * Enqueue using process struct.
+ */
+ *newp &= ~ERTS_PSFLGS_PRQ_PRIO_MASK;
+ *newp |= ERTS_PSFLG_IN_RUNQ | (aprio << ERTS_PSFLGS_PRQ_PRIO_OFFSET);
return queue;
}
@@ -6415,6 +6433,9 @@ select_enqueue_run_queue(int enqueue, int enq_prio, Process *p, erts_aint32_t st
/*
* schedule_out_process() return with c_rq locked.
+ *
+ * Return non-zero value if caller should decrease
+ * reference count on the process when done with it...
*/
static ERTS_INLINE int
schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
@@ -6469,12 +6490,18 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
erts_smp_runq_lock(c_rq);
- return 0;
-
+#if !defined(ERTS_SMP)
+ /* Decrement refc if process struct is free... */
+ return !!(n & ERTS_PSFLG_FREE);
+#else
+ /* Decrement refc if scheduled out from dirty scheduler... */
+ return !is_normal_sched;
+#endif
}
else {
Process* sched_p;
+ ASSERT(!(n & ERTS_PSFLG_FREE));
ASSERT(!(n & ERTS_PSFLG_SUSPENDED) || (n & (ERTS_PSFLG_ACTIVE_SYS
| ERTS_PSFLG_DIRTY_ACTIVE_SYS)));
@@ -6490,11 +6517,14 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
erts_smp_runq_lock(runq);
+ if (is_normal_sched && sched_p == p && ERTS_RUNQ_IX_IS_DIRTY(runq->ix))
+ erts_proc_inc_refc(p); /* Needs to be done before enqueue_process() */
+
/* Enqueue the process */
enqueue_process(runq, (int) enq_prio, sched_p);
if (runq == c_rq)
- return 1;
+ return 0;
erts_smp_runq_unlock(runq);
@@ -6502,9 +6532,15 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
erts_smp_runq_lock(c_rq);
- return 1;
+ /*
+ * Decrement refc if process is scheduled out by a
+ * dirty scheduler, and we have not just scheduled
+ * the process using the ordinary process struct
+ * on a dirty run-queue again...
+ */
+ return !is_normal_sched && (sched_p != p
+ || !ERTS_RUNQ_IX_IS_DIRTY(runq->ix));
}
-
}
static ERTS_INLINE void
@@ -6519,8 +6555,17 @@ add2runq(int enqueue, erts_aint32_t prio,
if (runq) {
Process *sched_p;
- if (enqueue > 0)
+ if (enqueue > 0) {
sched_p = proc;
+ /*
+ * Refc on process struct (i.e. true struct,
+ * not proxy-struct) increased while in a
+ * dirty run-queue or executing on a dirty
+ * scheduler.
+ */
+ if (ERTS_RUNQ_IX_IS_DIRTY(runq->ix))
+ erts_proc_inc_refc(proc);
+ }
else {
Process *pxy;
@@ -6671,15 +6716,18 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks)
}
static int
-schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st)
+schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st,
+ erts_aint32_t *fail_state_p)
{
int res;
int locked;
ErtsProcSysTaskQs *stqs, *free_stqs;
- erts_aint32_t state, a, n, enq_prio;
+ erts_aint32_t fail_state, state, a, n, enq_prio;
int enqueue; /* < 0 -> use proxy */
unsigned int prof_runnable_procs;
+ fail_state = *fail_state_p;
+
res = 1; /* prepare for success */
st->next = st->prev = st; /* Prep for empty prio queue */
state = erts_smp_atomic32_read_nob(&p->state);
@@ -6705,7 +6753,8 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st)
erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
state = erts_smp_atomic32_read_nob(&p->state);
- if (state & ERTS_PSFLG_EXITING) {
+ if (state & fail_state) {
+ *fail_state_p = (state & fail_state);
free_stqs = stqs;
res = 0;
goto cleanup;
@@ -7040,12 +7089,18 @@ typedef struct {
} ErtsSchdlrSspndResume;
static void
-schdlr_sspnd_resume_proc(Eterm pid)
+schdlr_sspnd_resume_proc(ErtsSchedType sched_type, Eterm pid)
{
- Process *p = erts_pid2proc(NULL, 0, pid, ERTS_PROC_LOCK_STATUS);
+ Process *p;
+ p = erts_pid2proc_opt(NULL, 0, pid, ERTS_PROC_LOCK_STATUS,
+ (sched_type != ERTS_SCHED_NORMAL
+ ? ERTS_P2P_FLG_INC_REFC
+ : 0));
if (p) {
resume_process(p, ERTS_PROC_LOCK_STATUS);
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ if (sched_type != ERTS_SCHED_NORMAL)
+ erts_proc_dec_refc(p);
}
}
@@ -7054,17 +7109,20 @@ schdlr_sspnd_resume_procs(ErtsSchedType sched_type,
ErtsSchdlrSspndResume *resume)
{
if (is_internal_pid(resume->onln.chngr)) {
- schdlr_sspnd_resume_proc(resume->onln.chngr);
+ schdlr_sspnd_resume_proc(sched_type,
+ resume->onln.chngr);
resume->onln.chngr = NIL;
}
if (is_internal_pid(resume->onln.nxt)) {
- schdlr_sspnd_resume_proc(resume->onln.nxt);
+ schdlr_sspnd_resume_proc(sched_type,
+ resume->onln.nxt);
resume->onln.nxt = NIL;
}
while (resume->msb.chngrs) {
ErtsProcList *plp = resume->msb.chngrs;
resume->msb.chngrs = plp->next;
- schdlr_sspnd_resume_proc(plp->pid);
+ schdlr_sspnd_resume_proc(sched_type,
+ plp->pid);
proclist_destroy(plp);
}
}
@@ -7116,16 +7174,8 @@ suspend_scheduler(ErtsSchedulerData *esdp)
ASSERT(sched_type != ERTS_SCHED_NORMAL || no != 1);
- if (sched_type != ERTS_SCHED_NORMAL) {
- if (erts_smp_mtx_trylock(&schdlr_sspnd.mtx) == EBUSY) {
- erts_smp_runq_unlock(esdp->run_queue);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- erts_smp_runq_lock(esdp->run_queue);
- }
- if (schdlr_sspnd.msb.ongoing)
- evacuate_run_queue(esdp->run_queue, &sbp);
+ if (sched_type != ERTS_SCHED_NORMAL)
erts_smp_runq_unlock(esdp->run_queue);
- }
else {
evacuate_run_queue(esdp->run_queue, &sbp);
@@ -7138,8 +7188,8 @@ suspend_scheduler(ErtsSchedulerData *esdp)
sched_wall_time_change(esdp, 0);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
}
+ erts_smp_mtx_lock(&schdlr_sspnd.mtx);
flgs = sched_prep_spin_suspended(ssi, ERTS_SSI_FLG_SUSPENDED);
if (flgs & ERTS_SSI_FLG_SUSPENDED) {
@@ -7183,15 +7233,18 @@ suspend_scheduler(ErtsSchedulerData *esdp)
(void) erts_proclist_fetch(&msb[i]->chngq, &end_plp);
/* resume processes that initiated the multi scheduling block... */
plp = msb[i]->chngq;
- while (plp) {
- erts_proclist_store_last(&msb[i]->blckrs,
- proclist_copy(plp));
- plp = plp->next;
- }
- if (end_plp)
+ if (plp) {
+ ASSERT(end_plp);
+ ASSERT(msb[i]->ongoing);
+ do {
+ erts_proclist_store_last(&msb[i]->blckrs,
+ proclist_copy(plp));
+ plp = plp->next;
+ } while (plp);
end_plp->next = resume.msb.chngrs;
- resume.msb.chngrs = msb[i]->chngq;
- msb[i]->chngq = NULL;
+ resume.msb.chngrs = msb[i]->chngq;
+ msb[i]->chngq = NULL;
+ }
}
}
}
@@ -7255,24 +7308,14 @@ suspend_scheduler(ErtsSchedulerData *esdp)
while (1) {
ErtsMonotonicTime current_time;
- erts_aint32_t qmask;
erts_aint32_t flgs;
- qmask = (ERTS_RUNQ_FLGS_GET(esdp->run_queue)
- & ERTS_RUNQ_FLGS_QMASK);
-
- if (sched_type != ERTS_SCHED_NORMAL) {
- if (qmask) {
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
- erts_smp_runq_lock(esdp->run_queue);
- if (schdlr_sspnd.msb.ongoing)
- evacuate_run_queue(esdp->run_queue, &sbp);
- erts_smp_runq_unlock(esdp->run_queue);
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- }
+ if (sched_type != ERTS_SCHED_NORMAL)
aux_work = 0;
- }
else {
+ erts_aint32_t qmask;
+ qmask = (ERTS_RUNQ_FLGS_GET(esdp->run_queue)
+ & ERTS_RUNQ_FLGS_QMASK);
aux_work = erts_atomic32_read_acqb(&ssi->aux_work);
@@ -7400,12 +7443,23 @@ suspend_scheduler(ErtsSchedulerData *esdp)
schdlr_sspnd_inc_nscheds(&schdlr_sspnd.active, sched_type);
changing = erts_smp_atomic32_read_nob(&schdlr_sspnd.changing);
- if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB)
- && schdlr_sspnd.online == schdlr_sspnd.active) {
- erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
- ~ERTS_SCHDLR_SSPND_CHNG_MSB);
- }
-
+ if (changing) {
+ if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB)
+ && !schdlr_sspnd.msb.ongoing
+ && schdlr_sspnd.online == schdlr_sspnd.active) {
+ erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
+ ~ERTS_SCHDLR_SSPND_CHNG_MSB);
+ }
+ if ((changing & ERTS_SCHDLR_SSPND_CHNG_NMSB)
+ && !schdlr_sspnd.nmsb.ongoing
+ && (schdlr_sspnd_get_nscheds(&schdlr_sspnd.online,
+ ERTS_SCHED_NORMAL)
+ == schdlr_sspnd_get_nscheds(&schdlr_sspnd.active,
+ ERTS_SCHED_NORMAL))) {
+ erts_smp_atomic32_read_band_nob(&schdlr_sspnd.changing,
+ ~ERTS_SCHDLR_SSPND_CHNG_NMSB);
+ }
+ }
ASSERT(no <= schdlr_sspnd_get_nscheds(&schdlr_sspnd.online, sched_type));
ASSERT((sched_type == ERTS_SCHED_NORMAL
? !(schdlr_sspnd.msb.ongoing|schdlr_sspnd.nmsb.ongoing)
@@ -7538,7 +7592,7 @@ abort_sched_onln_chng_waitq(Process *p)
erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
if (is_internal_pid(resume))
- schdlr_sspnd_resume_proc(resume);
+ schdlr_sspnd_resume_proc(ERTS_SCHED_NORMAL, resume);
}
ErtsSchedSuspendResult
@@ -7936,21 +7990,20 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int normal
}
else { /* ------ UNBLOCK ------ */
if (p->flags & have_blckd_flg) {
- ErtsProcList *plps[2];
+ ErtsProcList **plpps[3] = {0};
ErtsProcList *plp;
- int limit = 0;
- plps[limit++] = erts_proclist_peek_first(msbp->blckrs);
- if (all)
- plps[limit++] = erts_proclist_peek_first(msbp->chngq);
+ plpps[0] = &msbp->blckrs;
+ if (all)
+ plpps[1] = &msbp->chngq;
- for (ix = 0; ix < limit; ix++) {
- plp = plps[ix];
+ for (ix = 0; plpps[ix]; ix++) {
+ plp = erts_proclist_peek_first(*plpps[ix]);
while (plp) {
ErtsProcList *tmp_plp = plp;
- plp = erts_proclist_peek_next(msbp->blckrs, plp);
+ plp = erts_proclist_peek_next(*plpps[ix], plp);
if (erts_proclist_same(tmp_plp, p)) {
- erts_proclist_remove(&msbp->blckrs, tmp_plp);
+ erts_proclist_remove(plpps[ix], tmp_plp);
proclist_destroy(tmp_plp);
if (!all)
break;
@@ -8177,6 +8230,8 @@ sched_dirty_cpu_thread_func(void *vesdp)
esdp->thr_id += erts_no_schedulers;
+ erts_msacc_init_thread("dirty_cpu_scheduler", no, 0);
+
erts_thr_progress_register_unmanaged_thread(&callbacks);
#ifdef ERTS_ENABLE_LOCK_CHECK
{
@@ -8222,6 +8277,8 @@ sched_dirty_io_thread_func(void *vesdp)
esdp->thr_id += erts_no_schedulers + erts_no_dirty_cpu_schedulers;
+ erts_msacc_init_thread("dirty_io_scheduler", no, 0);
+
erts_thr_progress_register_unmanaged_thread(&callbacks);
#ifdef ERTS_ENABLE_LOCK_CHECK
{
@@ -8585,8 +8642,15 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
* from being selected for normal execution regardless
* of locks held or not held on it...
*/
- ASSERT(!((ERTS_PSFLG_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS)
- & erts_smp_atomic32_read_nob(&rp->state)));
+#ifdef DEBUG
+ {
+ erts_aint32_t state;
+ state = erts_smp_atomic32_read_nob(&rp->state);
+ ASSERT((state & ERTS_PSFLG_PENDING_EXIT)
+ || !(state & (ERTS_PSFLG_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)));
+ }
+#endif
if (!suspend)
resume_process(rp, pid_locks|ERTS_PROC_LOCK_STATUS);
@@ -9088,6 +9152,27 @@ resume_process_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
+BIF_RETTYPE
+erts_internal_is_process_executing_dirty_1(BIF_ALIST_1)
+{
+ if (is_not_internal_pid(BIF_ARG_1))
+ BIF_ERROR(BIF_P, BADARG);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ else {
+ Process *rp = erts_proc_lookup(BIF_ARG_1);
+ if (rp) {
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ |ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ BIF_RET(am_true);
+ }
+ }
+ }
+#endif
+ BIF_RET(am_false);
+}
+
+
Uint
erts_run_queues_len(Uint *qlen, int atomic_queues_read, int incl_active_sched)
{
@@ -9364,6 +9449,14 @@ erts_set_process_priority(Process *p, Eterm value)
}
}
+#ifdef __WIN32__
+Sint64
+erts_time2reds(ErtsMonotonicTime start, ErtsMonotonicTime end)
+{
+ return ERTS_TIME2REDS_IMPL__(start, end);
+}
+#endif
+
static int
scheduler_gc_proc(Process *c_p, int reds_left)
{
@@ -9494,15 +9587,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
p->reds += actual_reds;
-#ifdef ERTS_SMP
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_TRACE);
- if (p->trace_msg_q) {
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE);
- erts_schedule_flush_trace_messages(p->common.id);
- } else
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE);
-#endif
-
state = erts_smp_atomic32_read_nob(&p->state);
if (IS_TRACED(p)) {
@@ -9522,7 +9606,15 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
}
}
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+
+#ifdef ERTS_SMP
+ if (p->trace_msg_q) {
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_schedule_flush_trace_messages(p, 1);
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ }
+#endif
/* have to re-read state after taking lock */
state = erts_smp_atomic32_read_nob(&p->state);
@@ -9530,46 +9622,56 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
#ifdef ERTS_SMP
if (is_normal_sched && (state & ERTS_PSFLG_PENDING_EXIT))
erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_TRACE
| ERTS_PROC_LOCK_STATUS));
if (p->pending_suspenders)
handle_pending_suspend(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_TRACE
| ERTS_PROC_LOCK_STATUS));
#endif
esdp->reductions += reds;
- /* schedule_out_process() returns with rq locked! */
- schedule_out_process(rq, state, p, proxy_p, is_normal_sched);
- proxy_p = NULL;
+ {
+ int dec_refc;
- ERTS_PROC_REDUCTIONS_EXECUTED(esdp, rq,
- (int) ERTS_PSFLGS_GET_USR_PRIO(state),
- reds,
- actual_reds);
+ /* schedule_out_process() returns with rq locked! */
+ dec_refc = schedule_out_process(rq, state, p,
+ proxy_p, is_normal_sched);
+ proxy_p = NULL;
- esdp->current_process = NULL;
+ ERTS_PROC_REDUCTIONS_EXECUTED(esdp, rq,
+ (int) ERTS_PSFLGS_GET_USR_PRIO(state),
+ reds,
+ actual_reds);
+
+ esdp->current_process = NULL;
#ifdef ERTS_SMP
- p->scheduler_data = NULL;
+ if (is_normal_sched)
+ p->scheduler_data = NULL;
#endif
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_STATUS
+ | ERTS_PROC_LOCK_TRACE));
- ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER);
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER);
- if (state & ERTS_PSFLG_FREE) {
- if (!is_normal_sched) {
- ASSERT(p->flags & F_DELAYED_DEL_PROC);
- erts_proc_dec_refc(p);
- }
- else {
#ifdef ERTS_SMP
- ASSERT(esdp->free_process == p);
- esdp->free_process = NULL;
-#else
- erts_proc_dec_refc(p);
+ if (state & ERTS_PSFLG_FREE) {
+ if (!is_normal_sched) {
+ ASSERT(p->flags & F_DELAYED_DEL_PROC);
+ }
+ else {
+ ASSERT(esdp->free_process == p);
+ esdp->free_process = NULL;
+ }
+ }
#endif
- }
- }
+
+ if (dec_refc)
+ erts_proc_dec_refc(p);
+ }
#ifdef ERTS_SMP
ASSERT(!esdp->free_process);
@@ -9588,8 +9690,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
erts_smp_runq_lock(rq);
}
}
- BM_STOP_TIMER(system);
-
}
ERTS_SMP_LC_ASSERT(!is_normal_sched || !erts_thr_progress_is_blocking());
@@ -9825,10 +9925,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
goto check_activities_to_run;
}
- ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_EMULATOR);
-
- BM_START_TIMER(system);
-
/*
* Take the chosen process out of the queue.
*/
@@ -9851,8 +9947,12 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
if (!(state & ERTS_PSFLG_PROXY))
psflg_band_mask &= ~ERTS_PSFLG_IN_RUNQ;
else {
+ Eterm pid = p->common.id;
proxy_p = p;
- p = erts_proc_lookup_raw(proxy_p->common.id);
+ p = (is_normal_sched
+ ? erts_proc_lookup_raw(pid)
+ : erts_pid2proc_opt(NULL, 0, pid, 0,
+ ERTS_P2P_FLG_INC_REFC));
if (!p) {
free_proxy_proc(proxy_p);
proxy_p = NULL;
@@ -9884,6 +9984,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
| ERTS_PSFLG_FREE)))
#ifdef ERTS_DIRTY_SCHEDULERS
| (((state & (ERTS_PSFLG_RUNNING
+
| ERTS_PSFLG_FREE
| ERTS_PSFLG_RUNNING_SYS
| ERTS_PSFLG_EXITING))
@@ -9916,6 +10017,8 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
/* free and not queued by proxy */
erts_proc_dec_refc(p);
}
+ if (!is_normal_sched)
+ erts_proc_dec_refc(p);
goto pick_next_process;
}
state = new;
@@ -9934,6 +10037,8 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
}
+ ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_EMULATOR);
+
#ifdef ERTS_SMP
if (flags & ERTS_RUNQ_FLG_PROTECTED)
@@ -9966,8 +10071,8 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
state = erts_smp_atomic32_read_nob(&p->state);
- ASSERT(!p->scheduler_data);
#ifndef ERTS_DIRTY_SCHEDULERS
+ ASSERT(!p->scheduler_data);
p->scheduler_data = esdp;
#else /* ERTS_DIRTY_SCHEDULERS */
if (is_normal_sched) {
@@ -9978,12 +10083,18 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
goto sched_out_proc;
}
+ ASSERT(!p->scheduler_data);
p->scheduler_data = esdp;
}
else {
if (state & (ERTS_PSFLG_ACTIVE_SYS
| ERTS_PSFLG_PENDING_EXIT
| ERTS_PSFLG_EXITING)) {
+ /*
+ * IMPORTANT! We need to take care of
+ * scheduled check-process-code requests
+ * before continuing with dirty execution!
+ */
/* Migrate to normal scheduler... */
goto sunlock_sched_out_proc;
}
@@ -10365,7 +10476,8 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
reds--;
}
else {
- if (!garbage_collected) {
+ if (!garbage_collected
+ && !(c_p->flags & F_HIBERNATED)) {
FLAGS(c_p) |= F_NEED_FULLSWEEP;
reds -= scheduler_gc_proc(c_p, reds);
garbage_collected = 1;
@@ -10393,6 +10505,27 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
}
break;
}
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ case ERTS_PSTT_CLA: {
+ int fcalls;
+ int cla_reds = 0;
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p))
+ fcalls = reds;
+ else
+ fcalls = reds - CONTEXT_REDS;
+ st_res = erts_proc_copy_literal_area(c_p,
+ &cla_reds,
+ fcalls,
+ st->arg[0] == am_true);
+ reds -= cla_reds;
+ if (is_non_value(st_res)) {
+ /* Needed gc, but gc was disabled */
+ save_gc_task(c_p, st, st_prio);
+ st = NULL;
+ }
+ break;
+ }
+#endif
case ERTS_PSTT_COHMQ:
reds -= erts_complete_off_heap_message_queue_change(c_p);
st_res = am_true;
@@ -10443,14 +10576,15 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
switch (st->type) {
case ERTS_PSTT_GC:
- st_res = am_false;
- break;
case ERTS_PSTT_CPC:
- st_res = am_false;
- break;
case ERTS_PSTT_COHMQ:
st_res = am_false;
break;
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ case ERTS_PSTT_CLA:
+ st_res = am_ok;
+ break;
+#endif
#ifdef ERTS_SMP
case ERTS_PSTT_FTMQ:
reds -= erts_flush_trace_messages(c_p, ERTS_PROC_LOCK_MAIN);
@@ -10471,22 +10605,83 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
return reds;
}
-BIF_RETTYPE
-erts_internal_request_system_task_3(BIF_ALIST_3)
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+static BIF_RETTYPE
+dispatch_system_task(Process *c_p, erts_aint_t fail_state,
+ ErtsProcSysTask *st, Eterm target,
+ Eterm prio, Eterm operation)
+{
+ Process *rp;
+ ErtsProcLocks rp_locks = 0;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+ Eterm *hp, msg;
+ Uint hsz, osz;
+ BIF_RETTYPE ret;
+
+ switch (st->type) {
+ case ERTS_PSTT_CPC:
+ rp = erts_dirty_process_code_checker;
+ ASSERT(fail_state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS));
+ if (c_p == rp) {
+ ERTS_BIF_PREP_RET(ret, am_dirty_execution);
+ return ret;
+ }
+ break;
+ default:
+ rp = NULL;
+ ERTS_INTERNAL_ERROR("Non-dispatchable system task");
+ break;
+ }
+
+ ERTS_BIF_PREP_RET(ret, am_ok);
+
+ /*
+ * Send message on the form: {Requester, Target, Operation}
+ */
+
+ ASSERT(is_immed(st->requester));
+ ASSERT(is_immed(target));
+ ASSERT(is_immed(prio));
+
+ osz = size_object(operation);
+ hsz = 5 /* 4-tuple */ + osz;
+
+ mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp);
+
+ msg = copy_struct(operation, osz, &hp, ohp);
+ msg = TUPLE4(hp, st->requester, target, prio, msg);
+
+ erts_queue_message(rp, rp_locks, mp, msg, st->requester);
+
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+
+ return ret;
+}
+
+#endif
+
+static BIF_RETTYPE
+request_system_task(Process *c_p, Eterm requester, Eterm target,
+ Eterm priority, Eterm operation)
{
- Process *rp = erts_proc_lookup(BIF_ARG_1);
+ BIF_RETTYPE ret;
+ Process *rp = erts_proc_lookup(target);
ErtsProcSysTask *st = NULL;
- erts_aint32_t prio;
+ erts_aint32_t prio, fail_state = ERTS_PSFLG_EXITING;
Eterm noproc_res, req_type;
- if (!rp && !is_internal_pid(BIF_ARG_1)) {
- if (!is_external_pid(BIF_ARG_1))
+ if (!rp && !is_internal_pid(target)) {
+ if (!is_external_pid(target))
goto badarg;
- if (external_pid_dist_entry(BIF_ARG_1) != erts_this_dist_entry)
+ if (external_pid_dist_entry(target) != erts_this_dist_entry)
goto badarg;
}
- switch (BIF_ARG_2) {
+ switch (priority) {
case am_max: prio = PRIORITY_MAX; break;
case am_high: prio = PRIORITY_HIGH; break;
case am_normal: prio = PRIORITY_NORMAL; break;
@@ -10494,11 +10689,11 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
default: goto badarg;
}
- if (is_not_tuple(BIF_ARG_3))
+ if (is_not_tuple(operation))
goto badarg;
else {
int i;
- Eterm *tp = tuple_val(BIF_ARG_3);
+ Eterm *tp = tuple_val(operation);
Uint arity = arityval(*tp);
Eterm req_id;
Uint req_id_sz;
@@ -10536,7 +10731,7 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
ERTS_INIT_OFF_HEAP(&st->off_heap);
hp = &st->heap[0];
- st->requester = BIF_P->common.id;
+ st->requester = requester;
st->reply_tag = req_type;
st->req_id_sz = req_id_sz;
st->req_id = req_id_sz == 0 ? req_id : copy_struct(req_id,
@@ -10570,26 +10765,85 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
st->type = ERTS_PSTT_CPC;
if (!rp)
goto noproc;
+#ifdef ERTS_DIRTY_SCHEDULERS
+ /*
+ * If the process should start executing dirty
+ * code it is important that this task is
+ * aborted. Therefore this strict fail state...
+ */
+ fail_state |= (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS);
+#endif
break;
+#ifdef ERTS_NEW_PURGE_STRATEGY
+ case am_copy_literals:
+ if (st->arg[0] != am_true && st->arg[0] != am_false)
+ goto badarg;
+ st->type = ERTS_PSTT_CLA;
+ noproc_res = am_ok;
+ if (!rp)
+ goto noproc;
+ break;
+#endif
+
default:
goto badarg;
}
- if (!schedule_process_sys_task(rp, prio, st)) {
- noproc:
- notify_sys_task_executed(BIF_P, st, noproc_res);
+ if (!schedule_process_sys_task(rp, prio, st, &fail_state)) {
+ Eterm failure;
+ if (fail_state & ERTS_PSFLG_EXITING) {
+ noproc:
+ failure = noproc_res;
+ }
+#ifdef ERTS_DIRTY_SCHEDULERS
+ else if (fail_state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ ret = dispatch_system_task(c_p, fail_state, st,
+ target, priority, operation);
+ goto cleanup_return;
+ }
+#endif
+ else {
+ ERTS_INTERNAL_ERROR("Unknown failure schedule_process_sys_task()");
+ failure = am_internal_error;
+ }
+ notify_sys_task_executed(c_p, st, failure);
}
- BIF_RET(am_ok);
+ ERTS_BIF_PREP_RET(ret, am_ok);
+
+ return ret;
badarg:
+ ERTS_BIF_PREP_ERROR(ret, c_p, BADARG);
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+cleanup_return:
+#endif
+
if (st) {
erts_cleanup_offheap(&st->off_heap);
erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
}
- BIF_ERROR(BIF_P, BADARG);
+
+ return ret;
+}
+
+BIF_RETTYPE
+erts_internal_request_system_task_3(BIF_ALIST_3)
+{
+ return request_system_task(BIF_P, BIF_P->common.id,
+ BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+}
+
+BIF_RETTYPE
+erts_internal_request_system_task_4(BIF_ALIST_4)
+{
+ return request_system_task(BIF_P, BIF_ARG_1,
+ BIF_ARG_2, BIF_ARG_3, BIF_ARG_4);
}
static void
@@ -10598,7 +10852,7 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
Process *rp = erts_proc_lookup(pid);
if (rp) {
ErtsProcSysTask *st;
- erts_aint32_t state;
+ erts_aint32_t state, fail_state;
int i;
st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK,
@@ -10613,21 +10867,103 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
ERTS_INIT_OFF_HEAP(&st->off_heap);
state = erts_smp_atomic32_read_nob(&rp->state);
- if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state), st))
+ fail_state = ERTS_PSFLG_EXITING;
+
+ if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state),
+ st, &fail_state))
erts_free(ERTS_ALC_T_PROC_SYS_TSK, st);
}
}
+
void
erts_schedule_complete_off_heap_message_queue_change(Eterm pid)
{
erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ);
}
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+static void
+flush_dirty_trace_messages(void *vpid)
+{
+ Process *proc;
+ Eterm pid;
+#ifdef ARCH_64
+ pid = (Eterm) vpid;
+#else
+ pid = *((Eterm *) vpid);
+ erts_free(ERTS_ALC_T_DIRTY_SL, vpid);
+#endif
+
+ proc = erts_proc_lookup(pid);
+ if (proc)
+ (void) erts_flush_trace_messages(proc, 0);
+}
+
+#endif /* ERTS_DIRTY_SCHEDULERS */
+
void
-erts_schedule_flush_trace_messages(Eterm pid)
+erts_schedule_flush_trace_messages(Process *proc, int force_on_proc)
{
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl;
+#endif
+ Eterm pid = proc->common.id;
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_aint32_t state;
+
+ if (!force_on_proc) {
+ state = erts_smp_atomic32_read_nob(&proc->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ goto sched_flush_dirty;
+ }
+ }
+#endif
+
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
+
erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ);
+
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (!force_on_proc) {
+ state = erts_smp_atomic32_read_mb(&proc->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ void *vargp;
+
+ sched_flush_dirty:
+ /*
+ * We traced 'proc' from another thread than
+ * it is executing on, and it is executing
+ * on a dirty scheduler. It might take a
+ * significant amount of time before it is
+ * scheduled out (where it gets opportunity
+ * to flush messages). We therefore schedule
+ * the flush on the first ordinary scheduler.
+ */
+
+#ifdef ARCH_64
+ vargp = (void *) pid;
+#else
+ {
+ Eterm *argp = erts_alloc(ERTS_ALC_T_DIRTY_SL, sizeof(Eterm));
+ *argp = pid;
+ vargp = (void *) argp;
+ }
+#endif
+ erts_schedule_misc_aux_work(1, flush_dirty_trace_messages, vargp);
+ }
+ }
+#endif
}
static void
@@ -11154,18 +11490,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
|| (erts_smp_atomic32_read_nob(&p->state)
& ERTS_PSFLG_OFF_HEAP_MSGQ));
-#ifdef BM_COUNTERS
- processes_busy++;
-#endif
- BM_COUNT(processes_spawned);
-
- BM_SWAP_TIMER(system,size);
#ifdef SHCOPY_SPAWN
arg_size = copy_shared_calculate(args, &info);
#else
arg_size = size_object(args);
#endif
- BM_SWAP_TIMER(size,system);
heap_need = arg_size;
p->flags = flags;
@@ -11242,18 +11571,12 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->max_arg_reg = sizeof(p->def_arg_reg)/sizeof(p->def_arg_reg[0]);
p->arg_reg[0] = mod;
p->arg_reg[1] = func;
- BM_STOP_TIMER(system);
- BM_MESSAGE(args,p,parent);
- BM_START_TIMER(system);
- BM_SWAP_TIMER(system,copy);
#ifdef SHCOPY_SPAWN
p->arg_reg[2] = copy_shared_perform(args, arg_size, &info, &p->htop, &p->off_heap);
DESTROY_SHCOPY(info);
#else
p->arg_reg[2] = copy_struct(args, arg_size, &p->htop, &p->off_heap);
#endif
- BM_MESSAGE_COPIED(arg_size);
- BM_SWAP_TIMER(copy,system);
p->arity = 3;
p->fvalue = NIL;
@@ -11649,7 +11972,6 @@ erts_cleanup_empty_process(Process* p)
static void
delete_process(Process* p)
{
- Eterm *heap;
ErtsPSD *psd;
struct saved_calls *scb;
process_breakpoint_time_t *pbt;
@@ -11704,13 +12026,8 @@ delete_process(Process* p)
hipe_delete_process(&p->hipe);
#endif
- heap = p->abandoned_heap ? p->abandoned_heap : p->heap;
-
-#ifdef DEBUG
- sys_memset(heap, DEBUG_BAD_BYTE, p->heap_sz*sizeof(Eterm));
-#endif
+ erts_deallocate_young_generation(p);
- ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, (void*) heap, p->heap_sz*sizeof(Eterm));
if (p->old_heap != NULL) {
#ifdef DEBUG
@@ -11722,16 +12039,6 @@ delete_process(Process* p)
(p->old_hend-p->old_heap)*sizeof(Eterm));
}
- /*
- * Free all pending message buffers.
- */
- if (p->mbuf != NULL) {
- free_message_buffer(p->mbuf);
- }
-
- if (p->msg_frag)
- erts_cleanup_messages(p->msg_frag);
-
erts_erase_dicts(p);
/* free all pending messages */
@@ -12134,6 +12441,8 @@ send_exit_signal(Process *c_p, /* current process if and only
else if (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS))) {
/* Process not running ... */
ErtsProcLocks need_locks = ~(*rp_locks) & ERTS_PROC_LOCKS_ALL;
+ ErlHeapFragment *bp = NULL;
+ Eterm rsn_cpy;
if (need_locks
&& erts_smp_proc_trylock(rp, need_locks) == EBUSY) {
/* ... but we havn't got all locks on it ... */
@@ -12146,12 +12455,32 @@ send_exit_signal(Process *c_p, /* current process if and only
}
/* ...and we have all locks on it... */
*rp_locks = ERTS_PROC_LOCKS_ALL;
- set_proc_exiting(rp,
- state,
- (is_immed(rsn)
- ? rsn
- : copy_object(rsn, rp)),
- NULL);
+
+ state = erts_smp_atomic32_read_nob(&rp->state);
+
+ if (is_immed(rsn))
+ rsn_cpy = rsn;
+ else {
+ Eterm *hp;
+ ErlOffHeap *ohp;
+ Uint rsn_sz = size_object(rsn);
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ bp = new_message_buffer(rsn_sz);
+ ohp = &bp->off_heap;
+ hp = &bp->mem[0];
+ }
+ else
+#endif
+ {
+ hp = HAlloc(rp, rsn_sz);
+ ohp = &rp->off_heap;
+ }
+ rsn_cpy = copy_struct(rsn, rsn_sz, &hp, ohp);
+ }
+
+ set_proc_exiting(rp, state, rsn_cpy, bp);
}
else { /* Process running... */
@@ -12289,7 +12618,6 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext)
erts_port_demonitor(pcontext->p,
ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED,
prt, mon->ref, NULL);
- return; /* let erts_port_demonitor do the deletion */
} else { /* remote by pid */
ASSERT(is_external_pid(mon->pid));
dep = external_pid_dist_entry(mon->pid);
@@ -12807,15 +13135,14 @@ erts_continue_exit_process(Process *p)
}
#ifdef ERTS_DIRTY_SCHEDULERS
- if (a & (ERTS_PSFLG_DIRTY_RUNNING
- | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ if (a & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
p->flags |= F_DELAYED_DEL_PROC;
delay_del_proc = 1;
/*
- * The dirty scheduler will also decrease
- * refc when done...
+ * The dirty scheduler decrease refc
+ * when done with the process...
*/
- erts_proc_inc_refc(p);
}
#endif
@@ -12826,9 +13153,6 @@ erts_continue_exit_process(Process *p)
dep = (p->flags & F_DISTRIBUTION) ? erts_this_dist_entry : NULL;
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
-#ifdef BM_COUNTERS
- processes_busy--;
-#endif
if (dep) {
erts_do_net_exits(dep, reason);
@@ -12908,7 +13232,7 @@ erts_continue_exit_process(Process *p)
*/
void
-erts_stack_dump(int to, void *to_arg, Process *p)
+erts_stack_dump(fmtfn_t to, void *to_arg, Process *p)
{
Eterm* sp;
int yreg = -1;
@@ -12923,7 +13247,7 @@ erts_stack_dump(int to, void *to_arg, Process *p)
}
void
-erts_program_counter_info(int to, void *to_arg, Process *p)
+erts_program_counter_info(fmtfn_t to, void *to_arg, Process *p)
{
erts_aint32_t state;
int i;
@@ -12953,7 +13277,7 @@ erts_program_counter_info(int to, void *to_arg, Process *p)
}
static void
-print_function_from_pc(int to, void *to_arg, BeamInstr* x)
+print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x)
{
BeamInstr* addr = find_function_from_pc(x);
if (addr == NULL) {
@@ -12975,7 +13299,7 @@ print_function_from_pc(int to, void *to_arg, BeamInstr* x)
}
static int
-stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg)
+stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg)
{
Eterm x = *sp;
@@ -13007,7 +13331,7 @@ stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg)
* Print scheduler information
*/
void
-erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp) {
+erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
int i;
erts_aint32_t flg;
Process *p;
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 7c98b60647..b8755fe948 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -111,6 +111,7 @@ extern int erts_eager_check_io;
extern int erts_sched_compact_load;
extern int erts_sched_balance_util;
extern Uint erts_no_schedulers;
+extern Uint erts_no_total_schedulers;
#ifdef ERTS_DIRTY_SCHEDULERS
extern Uint erts_no_dirty_cpu_schedulers;
extern Uint erts_no_dirty_io_schedulers;
@@ -989,8 +990,7 @@ struct process {
Uint32 rcount; /* suspend count */
int schedule_count; /* Times left to reschedule a low prio process */
Uint reds; /* No of reductions for this process */
- Eterm group_leader; /* Pid in charge
- (can be boxed) */
+ Eterm group_leader; /* Pid in charge (can be boxed) */
Uint flags; /* Trap exit, etc (no trace flags anymore) */
Eterm fvalue; /* Exit & Throw value (failure reason) */
Uint freason; /* Reason for detected failure */
@@ -1329,10 +1329,13 @@ ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp);
ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp)
{
ErlHeapFragment* hf = MBUF(p);
+ Uint sz;
ASSERT(hf!=NULL && (hp - hf->mem < hf->alloc_size));
- hf->used_size = hp - hf->mem;
+ sz = hp - hf->mem;
+ p->mbuf_sz -= hf->used_size - sz;
+ hf->used_size = sz;
}
#endif /* inline */
@@ -1392,6 +1395,7 @@ extern int erts_system_profile_ts_type;
#define F_HAVE_BLCKD_NMSCHED (1 << 18) /* Process has blocked normal multi-scheduling */
#define F_HIPE_MODE (1 << 19)
#define F_DELAYED_DEL_PROC (1 << 20) /* Delay delete process (dirty proc exit case) */
+#define F_HIBERNATED (1 << 21) /* Hibernated */
/*
* F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent
@@ -1550,9 +1554,9 @@ extern int erts_system_profile_ts_type;
#define ERTS_SCHEDULER_IS_DIRTY(ESDP) \
((ESDP)->dirty_no.s.num != 0)
#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) \
- ((ESDP)->dirty_no.s.type == 0)
+ (ERTS_SCHEDULER_IS_DIRTY((ESDP)) & ((ESDP)->dirty_no.s.type == 0))
#define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) \
- ((ESDP)->dirty_no.s.type == 1)
+ (ERTS_SCHEDULER_IS_DIRTY((ESDP)) & ((ESDP)->dirty_no.s.type == 1))
#else
#define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0
#define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) 0
@@ -1584,6 +1588,7 @@ Uint64 erts_step_proc_interval(void);
int erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj); /* see erl_nif.c */
void erts_destroy_nif_export(void *); /* see erl_nif.c */
+int erts_check_nif_export_in_area(Process *p, char *start, Uint size);
ErtsProcList *erts_proclist_create(Process *);
ErtsProcList *erts_proclist_copy(ErtsProcList *);
@@ -1680,7 +1685,7 @@ ERTS_GLB_INLINE ErtsProcList *erts_proclist_fetch_first(ErtsProcList **list)
return NULL;
else {
ErtsProcList *res = *list;
- if (res == *list)
+ if (res->next == *list)
*list = NULL;
else
*list = res->next;
@@ -1768,7 +1773,7 @@ void erts_schedule_thr_prgr_later_cleanup_op(void (*)(void *),
ErtsThrPrgrLaterOp *,
UWord);
void erts_schedule_complete_off_heap_message_queue_change(Eterm pid);
-void erts_schedule_flush_trace_messages(Eterm pid);
+void erts_schedule_flush_trace_messages(Process *proc, int force_on_proc);
int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks);
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
@@ -1835,12 +1840,12 @@ void erts_cleanup_empty_process(Process* p);
#ifdef DEBUG
void erts_debug_verify_clean_empty_process(Process* p);
#endif
-void erts_stack_dump(int to, void *to_arg, Process *);
-void erts_limited_stack_trace(int to, void *to_arg, Process *);
-void erts_program_counter_info(int to, void *to_arg, Process *);
-void erts_print_scheduler_info(int to, void *to_arg, ErtsSchedulerData *esdp);
-void erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg);
-void erts_dump_process_state(int to, void *to_arg, erts_aint32_t psflg);
+void erts_stack_dump(fmtfn_t to, void *to_arg, Process *);
+void erts_limited_stack_trace(fmtfn_t to, void *to_arg, Process *);
+void erts_program_counter_info(fmtfn_t to, void *to_arg, Process *);
+void erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp);
+void erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
+void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
Eterm erts_get_process_priority(Process *p);
Eterm erts_set_process_priority(Process *p, Eterm prio);
@@ -1876,7 +1881,7 @@ void erts_handle_pending_exit(Process *, ErtsProcLocks);
#define ERTS_PROC_PENDING_EXIT(P) 0
#endif
-void erts_deep_process_dump(int, void *);
+void erts_deep_process_dump(fmtfn_t, void *);
Eterm erts_get_reader_groups_map(Process *c_p);
Eterm erts_debug_reader_groups_map(Process *c_p, int groups);
@@ -2474,6 +2479,35 @@ erts_get_atom_cache_map(Process *c_p)
}
#endif
+#ifdef __WIN32__
+/*
+ * Don't want erts_time2reds() inlined in beam_emu.c on windows since
+ * it is compiled with gcc which fails on it. Implementation is in
+ * erl_process.c on windows.
+ */
+# define ERTS_TIME2REDS_IMPL__ erts_time2reds__
+#else
+# define ERTS_TIME2REDS_IMPL__ erts_time2reds
+#endif
+
+ERTS_GLB_INLINE Sint64 ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start,
+ ErtsMonotonicTime end);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE Sint64
+ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, ErtsMonotonicTime end)
+{
+ ErtsMonotonicTime time = end - start;
+ ASSERT(time >= 0);
+ time = ERTS_MONOTONIC_TO_USEC(time);
+ if (time == 0)
+ return (Sint64) 1; /* At least one reduction */
+ /* Currently two reductions per micro second */
+ time *= (CONTEXT_REDS-1)/1000 + 1;
+ return (Sint64) time;
+}
+#endif
+
Process *erts_pid2proc_suspend(Process *,
ErtsProcLocks,
Eterm,
diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c
index d8c2eaba94..42654604cb 100644
--- a/erts/emulator/beam/erl_process_dict.c
+++ b/erts/emulator/beam/erl_process_dict.c
@@ -156,7 +156,7 @@ erts_pd_set_initial_size(int size)
* Called from break handler
*/
void
-erts_dictionary_dump(int to, void *to_arg, ProcDict *pd)
+erts_dictionary_dump(fmtfn_t to, void *to_arg, ProcDict *pd)
{
unsigned int i;
#ifdef DEBUG
@@ -196,8 +196,8 @@ erts_dictionary_dump(int to, void *to_arg, ProcDict *pd)
}
void
-erts_deep_dictionary_dump(int to, void *to_arg,
- ProcDict* pd, void (*cb)(int, void *, Eterm))
+erts_deep_dictionary_dump(fmtfn_t to, void *to_arg,
+ ProcDict* pd, void (*cb)(fmtfn_t, void *, Eterm))
{
unsigned int i;
Eterm t;
diff --git a/erts/emulator/beam/erl_process_dict.h b/erts/emulator/beam/erl_process_dict.h
index 387562058c..b50a2af72c 100644
--- a/erts/emulator/beam/erl_process_dict.h
+++ b/erts/emulator/beam/erl_process_dict.h
@@ -37,9 +37,9 @@ typedef struct proc_dict {
int erts_pd_set_initial_size(int size);
Uint erts_dicts_mem_size(struct process *p);
void erts_erase_dicts(struct process *p);
-void erts_dictionary_dump(int to, void *to_arg, ProcDict *pd);
-void erts_deep_dictionary_dump(int to, void *to_arg,
- ProcDict* pd, void (*cb)(int, void *, Eterm obj));
+void erts_dictionary_dump(fmtfn_t to, void *to_arg, ProcDict *pd);
+void erts_deep_dictionary_dump(fmtfn_t to, void *to_arg,
+ ProcDict* pd, void (*cb)(fmtfn_t, void *, Eterm obj));
Eterm erts_dictionary_copy(struct process *p, ProcDict *pd);
Eterm erts_pd_hash_get(struct process *p, Eterm id);
diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c
index a70dfb8e73..8d66391d55 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2003-2017. 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.
@@ -40,17 +40,17 @@
#define OUR_NIL _make_header(0,_TAG_HEADER_FLOAT)
-static void dump_process_info(int to, void *to_arg, Process *p);
-static void dump_element(int to, void *to_arg, Eterm x);
-static void dump_dist_ext(int to, void *to_arg, ErtsDistExternal *edep);
-static void dump_element_nl(int to, void *to_arg, Eterm x);
-static int stack_element_dump(int to, void *to_arg, Eterm* sp,
+static void dump_process_info(fmtfn_t to, void *to_arg, Process *p);
+static void dump_element(fmtfn_t to, void *to_arg, Eterm x);
+static void dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep);
+static void dump_element_nl(fmtfn_t to, void *to_arg, Eterm x);
+static int stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp,
int yreg);
-static void stack_trace_dump(int to, void *to_arg, Eterm* sp);
-static void print_function_from_pc(int to, void *to_arg, BeamInstr* x);
-static void heap_dump(int to, void *to_arg, Eterm x);
-static void dump_binaries(int to, void *to_arg, Binary* root);
-static void dump_externally(int to, void *to_arg, Eterm term);
+static void stack_trace_dump(fmtfn_t to, void *to_arg, Eterm* sp);
+static void print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x);
+static void heap_dump(fmtfn_t to, void *to_arg, Eterm x);
+static void dump_binaries(fmtfn_t to, void *to_arg, Binary* root);
+static void dump_externally(fmtfn_t to, void *to_arg, Eterm term);
static Binary* all_binaries;
@@ -60,7 +60,7 @@ extern BeamInstr beam_continue_exit[];
void
-erts_deep_process_dump(int to, void *to_arg)
+erts_deep_process_dump(fmtfn_t to, void *to_arg)
{
int i, max = erts_ptab_max(&erts_proc);
@@ -90,9 +90,12 @@ Uint erts_process_memory(Process *p, int incl_msg_inq) {
erts_doforall_links(ERTS_P_LINKS(p), &erts_one_link_size, &size);
erts_doforall_monitors(ERTS_P_MONITORS(p), &erts_one_mon_size, &size);
size += (p->heap_sz + p->mbuf_sz) * sizeof(Eterm);
+ if (p->abandoned_heap)
+ size += (p->hend - p->heap) * sizeof(Eterm);
if (p->old_hend && p->old_heap)
size += (p->old_hend - p->old_heap) * sizeof(Eterm);
+
size += p->msg.len * sizeof(ErtsMessage);
for (mp = p->msg.first; mp; mp = mp->next)
@@ -117,7 +120,7 @@ Uint erts_process_memory(Process *p, int incl_msg_inq) {
}
static void
-dump_process_info(int to, void *to_arg, Process *p)
+dump_process_info(fmtfn_t to, void *to_arg, Process *p)
{
Eterm* sp;
ErtsMessage* mp;
@@ -176,7 +179,7 @@ dump_process_info(int to, void *to_arg, Process *p)
}
static void
-dump_dist_ext(int to, void *to_arg, ErtsDistExternal *edep)
+dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep)
{
if (!edep)
erts_print(to, to_arg, "D0:E0:");
@@ -210,7 +213,7 @@ dump_dist_ext(int to, void *to_arg, ErtsDistExternal *edep)
}
static void
-dump_element(int to, void *to_arg, Eterm x)
+dump_element(fmtfn_t to, void *to_arg, Eterm x)
{
if (is_list(x)) {
erts_print(to, to_arg, "H" PTR_FMT, list_val(x));
@@ -240,14 +243,14 @@ dump_element(int to, void *to_arg, Eterm x)
}
static void
-dump_element_nl(int to, void *to_arg, Eterm x)
+dump_element_nl(fmtfn_t to, void *to_arg, Eterm x)
{
dump_element(to, to_arg, x);
erts_putc(to, to_arg, '\n');
}
static void
-stack_trace_dump(int to, void *to_arg, Eterm *sp) {
+stack_trace_dump(fmtfn_t to, void *to_arg, Eterm *sp) {
Eterm x = *sp;
if (is_CP(x)) {
erts_print(to, to_arg, "%p:", sp);
@@ -258,7 +261,7 @@ stack_trace_dump(int to, void *to_arg, Eterm *sp) {
}
void
-erts_limited_stack_trace(int to, void *to_arg, Process *p)
+erts_limited_stack_trace(fmtfn_t to, void *to_arg, Process *p)
{
Eterm* sp;
@@ -304,7 +307,7 @@ erts_limited_stack_trace(int to, void *to_arg, Process *p)
}
static int
-stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg)
+stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg)
{
Eterm x = *sp;
@@ -332,7 +335,7 @@ stack_element_dump(int to, void *to_arg, Eterm* sp, int yreg)
}
static void
-print_function_from_pc(int to, void *to_arg, BeamInstr* x)
+print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x)
{
BeamInstr* addr = find_function_from_pc(x);
if (addr == NULL) {
@@ -352,7 +355,7 @@ print_function_from_pc(int to, void *to_arg, BeamInstr* x)
}
static void
-heap_dump(int to, void *to_arg, Eterm x)
+heap_dump(fmtfn_t to, void *to_arg, Eterm x)
{
DeclareTmpHeapNoproc(last,1);
Eterm* next = last;
@@ -443,7 +446,7 @@ heap_dump(int to, void *to_arg, Eterm x)
ProcBin* pb = (ProcBin *) binary_val(x);
Binary* val = pb->val;
- if (erts_smp_atomic_xchg_nob(&val->refc, 0) != 0) {
+ if (erts_atomic_xchg_nob(&val->refc, 0) != 0) {
val->flags = (UWord) all_binaries;
all_binaries = val;
}
@@ -512,7 +515,7 @@ heap_dump(int to, void *to_arg, Eterm x)
}
static void
-dump_binaries(int to, void *to_arg, Binary* current)
+dump_binaries(fmtfn_t to, void *to_arg, Binary* current)
{
while (current) {
long i;
@@ -530,7 +533,7 @@ dump_binaries(int to, void *to_arg, Binary* current)
}
static void
-dump_externally(int to, void *to_arg, Eterm term)
+dump_externally(fmtfn_t to, void *to_arg, Eterm term)
{
byte sbuf[1024]; /* encode and hope for the best ... */
byte* s;
@@ -573,7 +576,7 @@ dump_externally(int to, void *to_arg, Eterm term)
}
}
-void erts_dump_process_state(int to, void *to_arg, erts_aint32_t psflg)
+void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
{
char *s;
switch (erts_process_state2status(psflg)) {
@@ -591,7 +594,7 @@ void erts_dump_process_state(int to, void *to_arg, erts_aint32_t psflg)
}
void
-erts_dump_extended_process_state(int to, void *to_arg, erts_aint32_t psflg) {
+erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg) {
int i;
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
index cab4bd73db..96238318c9 100644
--- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c
+++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c
@@ -161,7 +161,7 @@ enqueue_remote_managed_thread(erts_sspa_chunk_header_t *chdr,
if ((i & 1) == 0)
itmp = itmp2;
else {
- enq = (erts_sspa_blk_t *) itmp;
+ enq = (erts_sspa_blk_t *) itmp2;
itmp = erts_atomic_read_acqb(&enq->next_atmc);
ASSERT(itmp != ERTS_AINT_NULL);
}
diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c
index 21938e7684..700ed90def 100644
--- a/erts/emulator/beam/erl_thr_progress.c
+++ b/erts/emulator/beam/erl_thr_progress.c
@@ -700,6 +700,7 @@ leader_update(ErtsThrPrgrData *tpd)
tpd->leader_state.chk_next_ix = no_managed;
erts_atomic32_set_nob(&intrnl->misc.data.umrefc_ix.current,
(erts_aint32_t) new_umrefc_ix);
+ tpd->leader_state.umrefc_ix.current = new_umrefc_ix;
ETHR_MEMBAR(ETHR_StoreLoad);
refc = erts_atomic_read_nob(&intrnl->umrefc[umrefc_ix].refc);
ASSERT(refc >= 0);
@@ -969,8 +970,10 @@ erts_thr_progress_unmanaged_continue__(ErtsThrPrgrDelayHandle handle)
#ifdef ERTS_ENABLE_LOCK_CHECK
ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(NULL);
ERTS_LC_ASSERT(tpd && tpd->is_delaying);
- tpd->is_delaying = 0;
- return_tmp_thr_prgr_data(tpd);
+ tpd->is_delaying--;
+ ASSERT(tpd->is_delaying >= 0);
+ if (!tpd->is_delaying)
+ return_tmp_thr_prgr_data(tpd);
#endif
ASSERT(!erts_thr_progress_is_managed_thread());
@@ -995,7 +998,7 @@ erts_thr_progress_unmanaged_delay__(void)
#ifdef ERTS_ENABLE_LOCK_CHECK
{
ErtsThrPrgrData *tpd = tmp_thr_prgr_data(NULL);
- tpd->is_delaying = 1;
+ tpd->is_delaying++;
}
#endif
return (ErtsThrPrgrDelayHandle) umrefc_ix;
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 9e37106b88..1ef8c1b73a 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -1289,56 +1289,62 @@ erts_finalize_time_offset(void)
/* info functions */
void
-elapsed_time_both(UWord *ms_user, UWord *ms_sys,
- UWord *ms_user_diff, UWord *ms_sys_diff)
+elapsed_time_both(ErtsMonotonicTime *ms_user, ErtsMonotonicTime *ms_sys,
+ ErtsMonotonicTime *ms_user_diff, ErtsMonotonicTime *ms_sys_diff)
{
- UWord prev_total_user, prev_total_sys;
- UWord total_user, total_sys;
+ ErtsMonotonicTime prev_total_user, prev_total_sys;
+ ErtsMonotonicTime total_user, total_sys;
SysTimes now;
sys_times(&now);
- total_user = (now.tms_utime * 1000) / SYS_CLK_TCK;
- total_sys = (now.tms_stime * 1000) / SYS_CLK_TCK;
+ total_user = (ErtsMonotonicTime) ((now.tms_utime * 1000) / SYS_CLK_TCK);
+ total_sys = (ErtsMonotonicTime) ((now.tms_stime * 1000) / SYS_CLK_TCK);
if (ms_user != NULL)
*ms_user = total_user;
if (ms_sys != NULL)
*ms_sys = total_sys;
- erts_smp_mtx_lock(&erts_timeofday_mtx);
+ if (ms_user_diff || ms_sys_diff) {
+ erts_smp_mtx_lock(&erts_timeofday_mtx);
- prev_total_user = (t_start.tms_utime * 1000) / SYS_CLK_TCK;
- prev_total_sys = (t_start.tms_stime * 1000) / SYS_CLK_TCK;
- t_start = now;
+ prev_total_user = (ErtsMonotonicTime) ((t_start.tms_utime * 1000) / SYS_CLK_TCK);
+ prev_total_sys = (ErtsMonotonicTime) ((t_start.tms_stime * 1000) / SYS_CLK_TCK);
+ t_start = now;
- erts_smp_mtx_unlock(&erts_timeofday_mtx);
+ erts_smp_mtx_unlock(&erts_timeofday_mtx);
- if (ms_user_diff != NULL)
- *ms_user_diff = total_user - prev_total_user;
+ if (ms_user_diff != NULL)
+ *ms_user_diff = total_user - prev_total_user;
- if (ms_sys_diff != NULL)
- *ms_sys_diff = total_sys - prev_total_sys;
+ if (ms_sys_diff != NULL)
+ *ms_sys_diff = total_sys - prev_total_sys;
+ }
}
/* wall clock routines */
void
-wall_clock_elapsed_time_both(UWord *ms_total, UWord *ms_diff)
+wall_clock_elapsed_time_both(ErtsMonotonicTime *ms_total, ErtsMonotonicTime *ms_diff)
{
ErtsMonotonicTime now, elapsed;
- erts_smp_mtx_lock(&erts_timeofday_mtx);
-
now = time_sup.r.o.get_time();
update_last_mtime(NULL, now);
elapsed = ERTS_MONOTONIC_TO_MSEC(now);
- *ms_total = (UWord) elapsed;
- *ms_diff = (UWord) (elapsed - prev_wall_clock_elapsed);
- prev_wall_clock_elapsed = elapsed;
- erts_smp_mtx_unlock(&erts_timeofday_mtx);
+ *ms_total = elapsed;
+
+ if (ms_diff) {
+ erts_smp_mtx_lock(&erts_timeofday_mtx);
+
+ *ms_diff = elapsed - prev_wall_clock_elapsed;
+ prev_wall_clock_elapsed = elapsed;
+
+ erts_smp_mtx_unlock(&erts_timeofday_mtx);
+ }
}
/* get current time */
@@ -2133,22 +2139,26 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto
/* Convert to common user specified time units */
switch (term) {
+ case am_second:
case am_seconds:
case make_small(1):
result = ERTS_MONOTONIC_TO_SEC(val) + muloff*ERTS_MONOTONIC_OFFSET_SEC;
ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result));
break;
+ case am_millisecond:
case am_milli_seconds:
case make_small(1000):
result = ERTS_MONOTONIC_TO_MSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_MSEC;
ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result));
break;
+ case am_microsecond:
case am_micro_seconds:
case make_small(1000*1000):
result = ERTS_MONOTONIC_TO_USEC(val) + muloff*ERTS_MONOTONIC_OFFSET_USEC;
ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result));
break;
#ifdef ARCH_64
+ case am_nanosecond:
case am_nano_seconds:
case make_small(1000*1000*1000):
result = ERTS_MONOTONIC_TO_NSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_NSEC;
@@ -2159,7 +2169,7 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto
Eterm value, native_res;
#ifndef ARCH_64
Sint user_res;
- if (term == am_nano_seconds)
+ if (term == am_nanosecond || term == am_nano_seconds)
goto to_nano_seconds;
if (term_to_Sint(term, &user_res)) {
if (user_res == 1000*1000*1000) {
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 4cf38bf894..00bb114c26 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2017. 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.
@@ -493,8 +493,8 @@ erts_get_system_seq_tracer(void)
if (st != erts_tracer_nil &&
call_enabled_tracer(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;
+ st = erts_set_system_seq_tracer(NULL, 0, erts_tracer_nil);
+ ERTS_TRACER_CLEAR(&st);
}
return st;
@@ -813,6 +813,9 @@ trace_send(Process *p, Eterm to, Eterm msg)
ErtsTracerNif *tnif = NULL;
ErtsTracingEvent* te;
Eterm pam_result;
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl;
+#endif
ASSERT(ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND));
@@ -837,6 +840,10 @@ trace_send(Process *p, Eterm to, Eterm msg)
} else
pam_result = am_true;
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
+
if (is_internal_pid(to)) {
if (!erts_proc_lookup(to))
goto send_to_non_existing_process;
@@ -852,6 +859,11 @@ trace_send(Process *p, Eterm to, Eterm msg)
send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND,
operation, msg, to, pam_result);
}
+
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+
erts_match_set_release_result_trace(p, pam_result);
}
@@ -1167,6 +1179,8 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
Eterm transformed_args[MAX_ARG];
ErtsTracer pre_ms_tracer = erts_tracer_nil;
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) & ERTS_PROC_LOCK_MAIN);
+
ASSERT(tracer);
if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) {
/* Breakpoint trace enabled without specifying tracer =>
@@ -1422,6 +1436,7 @@ void
trace_gc(Process *p, Eterm what, Uint size, Eterm msg)
{
ErtsTracerNif *tnif = NULL;
+ Eterm* o_hp = NULL;
Eterm* hp;
Uint sz = 0;
Eterm tup;
@@ -1432,7 +1447,7 @@ trace_gc(Process *p, Eterm what, Uint size, Eterm msg)
if (is_non_value(msg)) {
(void) erts_process_gc_info(p, &sz, NULL, 0, 0);
- hp = HAlloc(p, sz + 3 + 2);
+ o_hp = hp = erts_alloc(ERTS_ALC_T_TMP, (sz + 3 + 2) * sizeof(Eterm));
msg = erts_process_gc_info(p, NULL, &hp, 0, 0);
tup = TUPLE2(hp, am_wordsize, make_small(size)); hp += 3;
@@ -1441,6 +1456,8 @@ trace_gc(Process *p, Eterm what, Uint size, Eterm msg)
send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_GC,
what, msg, THE_NON_VALUE, am_true);
+ if (o_hp)
+ erts_free(ERTS_ALC_T_TMP, o_hp);
}
}
@@ -3108,6 +3125,7 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer)
if (is_not_nil(*tracer)) {
Uint offs = 2;
UWord size = 2 * sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp);
+ ErtsThrPrgrLaterOp *lop;
ASSERT(is_list(*tracer));
if (is_not_immed(ERTS_TRACER_STATE(*tracer))) {
hf = (void*)(((char*)(ptr_val(*tracer)) - offsetof(ErlHeapFragment, mem)));
@@ -3115,6 +3133,16 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer)
size = hf->alloc_size * sizeof(Eterm) + sizeof(ErlHeapFragment);
ASSERT(offs == size_object(*tracer));
}
+
+ /* sparc assumes that all structs are double word aligned, so we
+ have to align the ErtsThrPrgrLaterOp struct otherwise it may
+ segfault.*/
+ if ((UWord)(ptr_val(*tracer) + offs) % (sizeof(UWord)*2) == sizeof(UWord))
+ offs += 1;
+
+ lop = (ErtsThrPrgrLaterOp*)(ptr_val(*tracer) + offs);
+ ASSERT((UWord)lop % (sizeof(UWord)*2) == 0);
+
/* We schedule the free:ing of the tracer until after a thread progress
has been made so that we know that no schedulers have any references
to it. Because we do this, it is possible to release all locks of a
@@ -3122,9 +3150,7 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer)
without having to worry if it is free'd.
*/
erts_schedule_thr_prgr_later_cleanup_op(
- free_tracer, (void*)(*tracer),
- (ErtsThrPrgrLaterOp*)(ptr_val(*tracer) + offs),
- size);
+ free_tracer, (void*)(*tracer), lop, size);
}
if (is_nil(new_tracer)) {
@@ -3134,16 +3160,17 @@ erts_tracer_update(ErtsTracer *tracer, const ErtsTracer new_tracer)
Not sure if it is worth it, we save 4 words (sizeof(ErlHeapFragment))
per tracer. */
Eterm *hp = erts_alloc(ERTS_ALC_T_HEAP_FRAG,
- 2*sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp));
+ 3*sizeof(Eterm) + sizeof(ErtsThrPrgrLaterOp));
*tracer = CONS(hp, ERTS_TRACER_MODULE(new_tracer),
ERTS_TRACER_STATE(new_tracer));
} else {
Eterm *hp, tracer_state = ERTS_TRACER_STATE(new_tracer),
tracer_module = ERTS_TRACER_MODULE(new_tracer);
Uint sz = size_object(tracer_state);
- hf = new_message_buffer(sz + 2 /* cons cell */ + (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm));
+ hf = new_message_buffer(sz + 2 /* cons cell */ +
+ (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm) + 1);
hp = hf->mem + 2;
- hf->used_size -= (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm);
+ hf->used_size -= (sizeof(ErtsThrPrgrLaterOp)+sizeof(Eterm)-1)/sizeof(Eterm) + 1;
*tracer = copy_struct(tracer_state, sz, &hp, &hf->off_heap);
*tracer = CONS(hf->mem, tracer_module, *tracer);
ASSERT((void*)(((char*)(ptr_val(*tracer)) - offsetof(ErlHeapFragment, mem))) == hf);
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index 81800752f0..b0912a346d 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -132,6 +132,7 @@ Eterm erts_bld_uint(Uint **hpp, Uint *szp, Uint ui);
Eterm erts_bld_uword(Uint **hpp, Uint *szp, UWord uw);
Eterm erts_bld_uint64(Uint **hpp, Uint *szp, Uint64 ui64);
Eterm erts_bld_sint64(Uint **hpp, Uint *szp, Sint64 si64);
+#define erts_bld_monotonic_time erts_bld_sint64
Eterm erts_bld_cons(Uint **hpp, Uint *szp, Eterm car, Eterm cdr);
Eterm erts_bld_tuple(Uint **hpp, Uint *szp, Uint arity, ...);
#define erts_bld_tuple2(H,S,E1,E2) erts_bld_tuple(H,S,2,E1,E2)
diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h
index f97716d030..3d8c647386 100644
--- a/erts/emulator/beam/erl_vm.h
+++ b/erts/emulator/beam/erl_vm.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -110,8 +110,21 @@
#define HeapWordsLeft(p) (HEAP_LIMIT(p) - HEAP_TOP(p))
#if defined(DEBUG) || defined(CHECK_FOR_HOLES)
-# define ERTS_HOLE_MARKER (((0xdeadbeef << 24) << 8) | 0xdeadbeef)
-#endif /* egil: 32-bit ? */
+
+/*
+ * ERTS_HOLE_MARKER must *not* be mistaken for a valid term
+ * on the heap...
+ */
+# ifdef ARCH_64
+# define ERTS_HOLE_MARKER \
+ make_catch(UWORD_CONSTANT(0xdeadbeaf00000000) >> _TAG_IMMED2_SIZE)
+/* Will (at the time of writing) appear as 0xdeadbeaf0000001b */
+# else
+# define ERTS_HOLE_MARKER \
+ make_catch(UWORD_CONSTANT(0xdead0000) >> _TAG_IMMED2_SIZE)
+/* Will (at the time of writing) appear as 0xdead001b */
+# endif
+#endif
/*
* Allocate heap memory on the ordinary heap, NEVER in a heap
diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c
index 2a19211987..6cf6fed31a 100644
--- a/erts/emulator/beam/export.c
+++ b/erts/emulator/beam/export.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -83,7 +83,7 @@ static struct export_blob* entry_to_blob(struct export_entry* ee)
}
void
-export_info(int to, void *to_arg)
+export_info(fmtfn_t to, void *to_arg)
{
#ifdef ERTS_SMP
int lock = !ERTS_IS_CRASH_DUMPING;
@@ -348,7 +348,7 @@ Export *export_list(int i, ErtsCodeIndex code_ix)
int export_list_size(ErtsCodeIndex code_ix)
{
- return export_tables[code_ix].entries;
+ return erts_index_num_entries(&export_tables[code_ix]);
}
int export_table_sz(void)
diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h
index 8c81cbd410..17fc4828ca 100644
--- a/erts/emulator/beam/export.h
+++ b/erts/emulator/beam/export.h
@@ -21,14 +21,8 @@
#ifndef __EXPORT_H__
#define __EXPORT_H__
-#ifndef __SYS_H__
#include "sys.h"
-#endif
-
-#ifndef __INDEX_H__
#include "index.h"
-#endif
-
#include "code_ix.h"
/*
@@ -59,7 +53,7 @@ typedef struct export
void init_export_table(void);
-void export_info(int, void *);
+void export_info(fmtfn_t, void *);
ERTS_GLB_INLINE Export* erts_active_export_entry(Eterm m, Eterm f, unsigned a);
Export* erts_export_put(Eterm mod, Eterm func, unsigned int arity);
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index beed847578..9436259011 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -616,7 +616,7 @@ erts_make_dist_ext_copy(ErtsDistExternal *edep, Uint xsize)
sys_memcpy((void *) ep, (void *) edep, dist_ext_sz);
ep += dist_ext_sz;
if (new_edep->dep)
- erts_refc_inc(&new_edep->dep->refc, 1);
+ erts_smp_refc_inc(&new_edep->dep->refc, 1);
new_edep->extp = ep;
new_edep->ext_endp = ep + ext_sz;
new_edep->heap_size = -1;
@@ -1222,6 +1222,7 @@ typedef struct B2TContext_t {
} u;
} B2TContext;
+static B2TContext* b2t_export_context(Process*, B2TContext* src);
static uLongf binary2term_uncomp_size(byte* data, Sint size)
{
@@ -1254,7 +1255,7 @@ static uLongf binary2term_uncomp_size(byte* data, Sint size)
static ERTS_INLINE int
binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size,
- B2TContext* ctx)
+ B2TContext** ctxp, Process* p)
{
byte *bytes = data;
Sint size = data_size;
@@ -1268,8 +1269,8 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size,
size--;
if (size < 5 || *bytes != COMPRESSED) {
state->extp = bytes;
- if (ctx)
- ctx->state = B2TSizeInit;
+ if (ctxp)
+ (*ctxp)->state = B2TSizeInit;
}
else {
uLongf dest_len = (Uint32) get_int32(bytes+1);
@@ -1286,16 +1287,26 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size,
return -1;
}
state->extp = erts_alloc(ERTS_ALC_T_EXT_TERM_DATA, dest_len);
- ctx->reds -= dest_len;
+ if (ctxp)
+ (*ctxp)->reds -= dest_len;
}
state->exttmp = 1;
- if (ctx) {
+ if (ctxp) {
+ /*
+ * Start decompression by exporting trap context
+ * so we don't have to deal with deep-copying z_stream.
+ */
+ B2TContext* ctx = b2t_export_context(p, *ctxp);
+ ASSERT(state = &(*ctxp)->b2ts);
+ state = &ctx->b2ts;
+
if (erl_zlib_inflate_start(&ctx->u.uc.stream, bytes, size) != Z_OK)
return -1;
ctx->u.uc.dbytes = state->extp;
ctx->u.uc.dleft = dest_len;
ctx->state = B2TUncompressChunk;
+ *ctxp = ctx;
}
else {
uLongf dlen = dest_len;
@@ -1339,7 +1350,7 @@ erts_binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size
{
Sint res;
- if (binary2term_prepare(state, data, data_size, NULL) < 0 ||
+ if (binary2term_prepare(state, data, data_size, NULL, NULL) < 0 ||
(res=decoded_size(state->extp, state->extp + state->extsize, 0, NULL)) < 0) {
if (state->exttmp)
@@ -1485,7 +1496,7 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar
if (ctx->aligned_alloc) {
ctx->reds -= bin_size / 8;
}
- if (binary2term_prepare(&ctx->b2ts, bytes, bin_size, ctx) < 0) {
+ if (binary2term_prepare(&ctx->b2ts, bytes, bin_size, &ctx, p) < 0) {
ctx->state = B2TBadArg;
}
break;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index f3d4ac56cd..19286e1310 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -35,7 +35,6 @@
#include "register.h"
#include "erl_fun.h"
#include "erl_node_tables.h"
-#include "benchmark.h"
#include "erl_process.h"
#include "erl_sys_driver.h"
#include "erl_debug.h"
@@ -62,11 +61,14 @@ struct enif_environment_t /* ErlNifEnv */
extern void erts_pre_nif(struct enif_environment_t*, Process*,
struct erl_module_nif*, Process* tracee);
extern void erts_post_nif(struct enif_environment_t* env);
+#ifdef ERTS_DIRTY_SCHEDULERS
extern void erts_pre_dirty_nif(ErtsSchedulerData *,
struct enif_environment_t*, Process*,
- struct erl_module_nif*, Process* tracee);
+ struct erl_module_nif*);
+extern void erts_post_dirty_nif(struct enif_environment_t* env);
+#endif
extern Eterm erts_nif_taints(Process* p);
-extern void erts_print_nif_taints(int to, void* to_arg);
+extern void erts_print_nif_taints(fmtfn_t to, void* to_arg);
void erts_unload_nif(struct erl_module_nif* nif);
extern void erl_nif_init(void);
extern int erts_nif_get_funcs(struct erl_module_nif*,
@@ -125,7 +127,7 @@ typedef struct {
void *handle; /* Handle for DLL or SO (for dyn. drivers). */
DE_ProcEntry *procs; /* List of pids that have loaded this driver,
or that wait for it to change state */
- erts_refc_t refc; /* Number of ports/processes having
+ erts_smp_refc_t refc; /* Number of ports/processes having
references to the driver */
erts_smp_atomic32_t port_count; /* Number of ports using the driver */
Uint flags; /* ERL_DE_FL_KILL_PORTS */
@@ -999,17 +1001,33 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2);
/* beam_bif_load.c */
#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)
+#define ERTS_CPC_ALL ERTS_CPC_ALLOW_GC
Eterm erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp, int fcalls);
+#ifdef ERTS_NEW_PURGE_STRATEGY
+Eterm erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed);
+#endif
-typedef struct {
- Eterm *ptr;
- Uint sz;
- Eterm pid;
-} copy_literals_t;
+typedef struct ErtsLiteralArea_ {
+ struct erl_off_heap_header *off_heap;
+ Eterm *end;
+ Eterm start[1]; /* beginning of area */
+} ErtsLiteralArea;
+
+#define ERTS_LITERAL_AREA_ALLOC_SIZE(N) \
+ (sizeof(ErtsLiteralArea) + sizeof(Eterm)*((N) - 1))
+
+extern erts_smp_atomic_t erts_copy_literal_area__;
+#define ERTS_COPY_LITERAL_AREA() \
+ ((ErtsLiteralArea *) erts_smp_atomic_read_nob(&erts_copy_literal_area__))
-extern copy_literals_t erts_clrange;
+#ifdef ERTS_NEW_PURGE_STRATEGY
+extern Process *erts_literal_area_collector;
+#endif
+#ifdef ERTS_DIRTY_SCHEDULERS
+extern Process *erts_dirty_process_code_checker;
+#endif
+
+extern Process *erts_code_purger;
/* beam_load.c */
typedef struct {
@@ -1051,16 +1069,17 @@ void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info);
void init_break_handler(void);
void erts_set_ignore_break(void);
void erts_replace_intr(void);
-void process_info(int, void *);
-void print_process_info(int, void *, Process*);
-void info(int, void *);
-void loaded(int, void *);
+void process_info(fmtfn_t, void *);
+void print_process_info(fmtfn_t, void *, Process*);
+void info(fmtfn_t, void *);
+void loaded(fmtfn_t, void *);
/* erl_arith.c */
double erts_get_positive_zero_float(void);
/* config.c */
+__decl_noreturn void __noreturn erts_exit_epilogue(void);
__decl_noreturn void __noreturn erts_exit(int n, char*, ...);
__decl_noreturn void __noreturn erts_flush_async_exit(int n, char*, ...);
void erl_error(char*, va_list);
@@ -1092,12 +1111,19 @@ typedef struct {
#define INITIALIZE_SHCOPY(info) \
do { \
+ ErtsLiteralArea *larea__ = ERTS_COPY_LITERAL_AREA();\
info.queue_start = info.queue_default; \
info.bitstore_start = info.bitstore_default; \
info.shtable_start = info.shtable_default; \
info.literal_size = 0; \
- info.range_ptr = erts_clrange.ptr; \
- info.range_sz = erts_clrange.sz; \
+ if (larea__) { \
+ info.range_ptr = &larea__->start[0]; \
+ info.range_sz = larea__->end - info.range_ptr; \
+ } \
+ else { \
+ info.range_ptr = NULL; \
+ info.range_sz = 0; \
+ } \
} while(0)
#define DESTROY_SHCOPY(info) \
@@ -1136,7 +1162,7 @@ extern void erts_delete_nodes_monitors(Process *, ErtsProcLocks);
extern Eterm erts_monitor_nodes(Process *, Eterm, Eterm);
extern Eterm erts_processes_monitoring_nodes(Process *);
extern int erts_do_net_exits(DistEntry*, Eterm);
-extern int distribution_info(int, void *);
+extern int distribution_info(fmtfn_t, void *);
extern int is_node_name_atom(Eterm a);
extern int erts_net_message(Port *, DistEntry *,
@@ -1325,7 +1351,7 @@ int erts_utf8_to_latin1(byte* dest, const byte* source, int slen);
#define ERTS_UTF8_ANALYZE_MORE 3
#define ERTS_UTF8_OK_MAX_CHARS 4
-void bin_write(int, void*, byte*, size_t);
+void bin_write(fmtfn_t, void*, byte*, size_t);
Sint intlist_to_buf(Eterm, char*, Sint); /* most callers pass plain char*'s */
struct Sint_buf {
@@ -1441,7 +1467,7 @@ Eterm erts_gc_binary_part_2(Process* p, Eterm* reg, Uint live);
Uint erts_current_reductions(Process* current, Process *p);
-int erts_print_system_version(int to, void *arg, Process *c_p);
+int erts_print_system_version(fmtfn_t to, void *arg, Process *c_p);
int erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* reg);
diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c
index e255b961f1..8548e30e8b 100644
--- a/erts/emulator/beam/hash.c
+++ b/erts/emulator/beam/hash.c
@@ -35,9 +35,9 @@
static const int h_size_table[] = {
2, 5, 11, 23, 47, 97, 197, 397, 797, /* double upto here */
1201, 1597,
- 2411, 3203,
+ 2411, 3203,
4813, 6421,
- 9643, 12853,
+ 9643, 12853,
19289, 25717,
51437,
102877,
@@ -49,8 +49,8 @@ static const int h_size_table[] = {
6584983,
13169977,
26339969,
- 52679969,
- -1
+ 52679969,
+ -1
};
/*
@@ -69,7 +69,7 @@ void hash_get_info(HashInfo *hi, Hash *h)
for (i = 0; i < size; i++) {
int depth = 0;
HashBucket* b = h->bucket[i];
-
+
while (b != (HashBucket*) 0) {
objects++;
depth++;
@@ -95,7 +95,7 @@ void hash_get_info(HashInfo *hi, Hash *h)
**
*/
-void hash_info(int to, void *arg, Hash* h)
+void hash_info(fmtfn_t to, void *arg, Hash* h)
{
HashInfo hi;
@@ -112,7 +112,7 @@ void hash_info(int to, void *arg, Hash* h)
/*
* Returns size of table in bytes. Stored objects not included.
*/
-int
+int
hash_table_sz(Hash *h)
{
int i;
@@ -190,7 +190,7 @@ void hash_delete(Hash* h)
HashBucket* b = h->bucket[i];
while (b != (HashBucket*) 0) {
HashBucket* b_next = b->next;
-
+
h->fun.free((void*) b);
b = b_next;
}
@@ -250,7 +250,7 @@ void* hash_get(Hash* h, void* tmpl)
HashValue hval = h->fun.hash(tmpl);
int ix = hval % h->size;
HashBucket* b = h->bucket[ix];
-
+
while(b != (HashBucket*) 0) {
if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0))
return (void*) b;
@@ -294,7 +294,7 @@ void* hash_erase(Hash* h, void* tmpl)
int ix = hval % h->size;
HashBucket* b = h->bucket[ix];
HashBucket* prev = 0;
-
+
while(b != 0) {
if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0)) {
if (prev != 0)
@@ -326,7 +326,7 @@ hash_remove(Hash *h, void *tmpl)
int ix = hval % h->size;
HashBucket *b = h->bucket[ix];
HashBucket *prev = NULL;
-
+
while (b) {
if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0)) {
if (prev)
@@ -355,4 +355,3 @@ void hash_foreach(Hash* h, void (*func)(void *, void *), void *func_arg2)
}
}
}
-
diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h
index 9f773d8faa..d319aaca83 100644
--- a/erts/emulator/beam/hash.h
+++ b/erts/emulator/beam/hash.h
@@ -25,9 +25,7 @@
#ifndef __HASH_H__
#define __HASH_H__
-#ifndef __SYS_H__
#include "sys.h"
-#endif
typedef unsigned long HashValue;
typedef struct hash Hash;
@@ -39,7 +37,7 @@ typedef void (*HFREE_FUN)(void*);
/* Meta functions */
typedef void* (*HMALLOC_FUN)(int,size_t);
typedef void (*HMFREE_FUN)(int,void*);
-typedef int (*HMPRINT_FUN)(int,void*,char*, ...);
+typedef int (*HMPRINT_FUN)(fmtfn_t,void*,char*, ...);
/*
** This bucket must be placed in top of
@@ -91,7 +89,7 @@ Hash* hash_init(int, Hash*, char*, int, HashFunctions);
void hash_delete(Hash*);
void hash_get_info(HashInfo*, Hash*);
-void hash_info(int, void *, Hash*);
+void hash_info(fmtfn_t, void *, Hash*);
int hash_table_sz(Hash *);
void* hash_get(Hash*, void*);
diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c
index 26d6c04ea0..a1f6f54543 100644
--- a/erts/emulator/beam/index.c
+++ b/erts/emulator/beam/index.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -27,7 +27,7 @@
#include "global.h"
#include "index.h"
-void index_info(int to, void *arg, IndexTable *t)
+void index_info(fmtfn_t to, void *arg, IndexTable *t)
{
hash_info(to, arg, &t->htable);
erts_print(to, arg, "=index_table:%s\n", t->htable.name);
@@ -91,9 +91,16 @@ index_put_entry(IndexTable* t, void* tmpl)
t->seg_table[ix>>INDEX_PAGE_SHIFT] = erts_alloc(t->type, sz);
t->size += INDEX_PAGE_SIZE;
}
- t->entries++;
p->index = ix;
t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
+
+ /*
+ * Do a write barrier here to allow readers to do lock free iteration.
+ * erts_index_num_entries() does matching read barrier.
+ */
+ ERTS_SMP_WRITE_MEMORY_BARRIER;
+ t->entries++;
+
return p;
}
diff --git a/erts/emulator/beam/index.h b/erts/emulator/beam/index.h
index 218779c33b..6c07571df6 100644
--- a/erts/emulator/beam/index.h
+++ b/erts/emulator/beam/index.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -26,13 +26,8 @@
#ifndef __INDEX_H__
#define __INDEX_H__
-#ifndef __HASH_H__
#include "hash.h"
-#endif
-
-#ifndef ERL_ALLOC_H__
#include "erl_alloc.h"
-#endif
typedef struct index_slot
{
@@ -56,7 +51,7 @@ typedef struct index_table
#define INDEX_PAGE_MASK ((1 << INDEX_PAGE_SHIFT)-1)
IndexTable *erts_index_init(ErtsAlcType_t,IndexTable*,char*,int,int,HashFunctions);
-void index_info(int, void *, IndexTable*);
+void index_info(fmtfn_t, void *, IndexTable*);
int index_table_sz(IndexTable *);
int index_get(IndexTable*, void*);
@@ -70,6 +65,7 @@ void index_erase_latest_from(IndexTable*, Uint ix);
ERTS_GLB_INLINE int index_put(IndexTable*, void*);
ERTS_GLB_INLINE IndexSlot* erts_index_lookup(IndexTable*, Uint);
+ERTS_GLB_INLINE int erts_index_num_entries(IndexTable* t);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
@@ -83,6 +79,19 @@ erts_index_lookup(IndexTable* t, Uint ix)
{
return t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK];
}
+
+ERTS_GLB_INLINE int erts_index_num_entries(IndexTable* t)
+{
+ int ret = t->entries;
+ /*
+ * Do a read barrier here to allow lock free iteration
+ * on tables where entries are never erased.
+ * index_put_entry() does matching write barrier.
+ */
+ ERTS_SMP_READ_MEMORY_BARRIER;
+ return ret;
+}
+
#endif
#endif
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index cb8792dffa..caf8bfc294 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -1968,7 +1968,6 @@ int
erts_port_output_async(Port *prt, Eterm from, Eterm list)
{
- ErtsPortOpResult res;
ErtsProc2PortSigData *sigdp;
erts_driver_t *drv = prt->drv_ptr;
size_t size;
@@ -2102,26 +2101,18 @@ erts_port_output_async(Port *prt, Eterm from, Eterm list)
sigdp->u.output.size = size;
port_sig_callback = port_sig_output;
}
- sigdp->flags = 0;
ns_pthp = NULL;
task_flags = 0;
- res = erts_schedule_proc2port_signal(NULL,
- prt,
- ERTS_INVALID_PID,
- NULL,
- sigdp,
- task_flags,
- ns_pthp,
- port_sig_callback);
+ erts_schedule_proc2port_signal(NULL,
+ prt,
+ ERTS_INVALID_PID,
+ NULL,
+ sigdp,
+ task_flags,
+ ns_pthp,
+ port_sig_callback);
- if (res != ERTS_PORT_OP_SCHEDULED) {
- if (drv->outputv)
- cleanup_scheduled_outputv(evp, cbin);
- else
- cleanup_scheduled_output(buf);
- return 1;
- }
return 1;
bad_value:
@@ -2554,10 +2545,6 @@ erts_port_output(Process *c_p,
port_sig_callback);
if (res != ERTS_PORT_OP_SCHEDULED) {
- if (drv->outputv)
- cleanup_scheduled_outputv(evp, cbin);
- else
- cleanup_scheduled_output(buf);
return res;
}
@@ -2736,21 +2723,14 @@ erts_port_exit(Process *c_p,
&bp->off_heap);
}
- res = erts_schedule_proc2port_signal(c_p,
- prt,
- c_p ? c_p->common.id : from,
- refp,
- sigdp,
- 0,
- NULL,
- port_sig_exit);
-
- if (res == ERTS_PORT_OP_DROPPED) {
- if (bp)
- free_message_buffer(bp);
- }
-
- return res;
+ return erts_schedule_proc2port_signal(c_p,
+ prt,
+ c_p ? c_p->common.id : from,
+ refp,
+ sigdp,
+ 0,
+ NULL,
+ port_sig_exit);
}
static ErtsPortOpResult
@@ -4884,10 +4864,24 @@ erts_port_control(Process* c_p,
ASSERT(!tmp_alloced);
if (*ebinp == HEADER_SUB_BIN)
ebinp = binary_val(((ErlSubBin *) ebinp)->orig);
+
if (*ebinp != HEADER_PROC_BIN)
copy = 1;
else {
- binp = ((ProcBin *) ebinp)->val;
+ ProcBin *pb = (ProcBin *) ebinp;
+ int offset = bufp - pb->val->orig_bytes;
+
+ ASSERT(pb->val->orig_bytes <= bufp
+ && bufp + size <= pb->val->orig_bytes + pb->val->orig_size);
+
+ if (pb->flags) {
+ erts_emasculate_writable_binary(pb);
+
+ /* The procbin may have been reallocated, so update bufp */
+ bufp = pb->val->orig_bytes + offset;
+ }
+
+ binp = pb->val;
ASSERT(bufp <= bufp + size);
ASSERT(binp->orig_bytes <= bufp
&& bufp + size <= binp->orig_bytes + binp->orig_size);
@@ -4918,10 +4912,9 @@ erts_port_control(Process* c_p,
0,
NULL,
port_sig_control);
- if (res != ERTS_PORT_OP_SCHEDULED) {
- cleanup_scheduled_control(binp, bufp);
+ if (res != ERTS_PORT_OP_SCHEDULED)
return ERTS_PORT_OP_BADARG;
- }
+
return res;
}
@@ -5211,10 +5204,9 @@ erts_port_call(Process* c_p,
0,
NULL,
port_sig_call);
- if (res != ERTS_PORT_OP_SCHEDULED) {
- cleanup_scheduled_call(bufp);
+ if (res != ERTS_PORT_OP_SCHEDULED)
return ERTS_PORT_OP_BADARG;
- }
+
return res;
}
@@ -5480,7 +5472,7 @@ erts_request_io_bytes(Process *c_p)
typedef struct {
- int to;
+ fmtfn_t to;
void *arg;
} prt_one_lnk_data;
@@ -5497,7 +5489,7 @@ static void prt_one_lnk(ErtsLink *lnk, void *vprtd)
}
void
-print_port_info(Port *p, int to, void *arg)
+print_port_info(Port *p, fmtfn_t to, void *arg)
{
erts_aint32_t state = erts_atomic32_read_nob(&p->state);
@@ -6591,16 +6583,20 @@ deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p,
ErtsThrPrgrDelayHandle dhndl = erts_thr_progress_unmanaged_delay();
#endif
erts_aint32_t state;
+ int res = 1;
Port *prt = erts_port_lookup_raw((Eterm) port_id);
- if (!prt)
- return -1;
+ if (!prt) {
+ res = -1;
+ goto done;
+ }
state = erts_atomic32_read_nob(&prt->state);
if (state & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
| ERTS_PORT_SFLG_CLOSING)) {
if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
- return -1;
+ res = -1;
else
- return 0;
+ res = 0;
+ goto done;
}
if (connected_p) {
#ifdef ERTS_SMP
@@ -6609,25 +6605,27 @@ deliver_term_check_port(ErlDrvTermData port_id, Eterm *connected_p,
#endif
*connected_p = ERTS_PORT_GET_CONNECTED(prt);
}
+
+done:
+
#ifdef ERTS_SMP
if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) {
+ ERTS_SMP_LC_ASSERT(!prt || !erts_lc_is_port_locked(prt));
erts_thr_progress_unmanaged_continue(dhndl);
ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
} else
#endif
- {
+ if (res == 1) {
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
*trace_prt = prt;
}
- ERTS_SMP_LC_ASSERT(dhndl == ERTS_THR_PRGR_DHANDLE_MANAGED
- ? erts_lc_is_port_locked(prt)
- : !erts_lc_is_port_locked(prt));
- return 1;
+ return res;
}
int erl_drv_output_term(ErlDrvTermData port_id, ErlDrvTermData* data, int len)
{
/* May be called from arbitrary thread */
- Eterm connected;
+ Eterm connected = NIL; /* Shut up faulty warning... */
Port *prt = NULL;
int res = deliver_term_check_port(port_id, &connected, &prt);
if (res <= 0)
@@ -6884,12 +6882,6 @@ ErlDrvSizeT driver_vec_to_buf(ErlIOVec *vec, char *buf, ErlDrvSizeT len)
return (orig_len - len);
}
-
-/*
- * - driver_alloc_binary() is thread safe (efile driver depend on it).
- * - driver_realloc_binary(), and driver_free_binary() are *not* thread safe.
- */
-
/*
* reference count on driver binaries...
*/
@@ -6932,26 +6924,15 @@ driver_alloc_binary(ErlDrvSizeT size)
return Binary2ErlDrvBinary(bin);
}
-/* Reallocate space hold by binary */
+/* Reallocate space held by binary */
ErlDrvBinary* driver_realloc_binary(ErlDrvBinary* bin, ErlDrvSizeT size)
{
Binary* oldbin;
Binary* newbin;
- if (!bin) {
- erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp,
- "Bad use of driver_realloc_binary(%p, %lu): "
- "called with ",
- bin, (unsigned long)size);
- if (!bin) {
- erts_dsprintf(dsbufp, "NULL pointer as first argument");
- }
- erts_send_warning_to_logger_nogl(dsbufp);
- if (!bin)
- return driver_alloc_binary(size);
- }
+ if (!bin)
+ return driver_alloc_binary(size);
oldbin = ErlDrvBinary2Binary(bin);
newbin = (Binary *) erts_bin_realloc_fnf(oldbin, size);
@@ -6965,14 +6946,8 @@ ErlDrvBinary* driver_realloc_binary(ErlDrvBinary* bin, ErlDrvSizeT size)
void driver_free_binary(ErlDrvBinary* dbin)
{
Binary *bin;
- if (!dbin) {
- erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
- erts_dsprintf(dsbufp,
- "Bad use of driver_free_binary(%p): called with "
- "NULL pointer as argument", dbin);
- erts_send_warning_to_logger_nogl(dsbufp);
+ if (!dbin)
return;
- }
bin = ErlDrvBinary2Binary(dbin);
if (erts_refc_dectest(&bin->refc, 0) == 0)
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index 3eb11f1693..5978478323 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -50,7 +50,7 @@ static erts_smp_atomic_t tot_module_bytes;
#include "erl_smp.h"
-void module_info(int to, void *to_arg)
+void module_info(fmtfn_t to, void *to_arg)
{
index_info(to, to_arg, &module_tables[erts_active_code_ix()]);
}
@@ -85,6 +85,7 @@ static Module* module_alloc(Module* tmpl)
obj->old.num_breakpoints = 0;
obj->curr.num_traced_exports = 0;
obj->old.num_traced_exports = 0;
+ obj->on_load = 0;
return obj;
}
@@ -201,6 +202,7 @@ void module_start_staging(void)
dst_mod->curr = src_mod->curr;
dst_mod->old = src_mod->old;
+ dst_mod->on_load = src_mod->on_load;
}
/*
@@ -214,6 +216,7 @@ void module_start_staging(void)
dst_mod->curr = src_mod->curr;
dst_mod->old = src_mod->old;
+ dst_mod->on_load = src_mod->on_load;
}
newsz = index_table_sz(dst);
erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz));
diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h
index 4e12731d85..f105b3f401 100644
--- a/erts/emulator/beam/module.h
+++ b/erts/emulator/beam/module.h
@@ -21,9 +21,7 @@
#ifndef __MODULE_H__
#define __MODULE_H__
-#ifndef __INDEX_H__
#include "index.h"
-#endif
struct erl_module_instance {
BeamCodeHeader* code_hdr;
@@ -41,6 +39,7 @@ typedef struct erl_module {
struct erl_module_instance curr;
struct erl_module_instance old; /* protected by "old_code" rwlock */
+ struct erl_module_instance* on_load;
} Module;
Module* erts_get_module(Eterm mod, ErtsCodeIndex code_ix);
@@ -49,7 +48,7 @@ Module* erts_put_module(Eterm mod);
void init_module_table(void);
void module_start_staging(void);
void module_end_staging(int commit);
-void module_info(int, void *);
+void module_info(fmtfn_t, void *);
Module *module_code(int, ErtsCodeIndex);
int module_code_size(ErtsCodeIndex);
diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c
index ac7096745e..acda51c9fc 100644
--- a/erts/emulator/beam/register.c
+++ b/erts/emulator/beam/register.c
@@ -102,7 +102,7 @@ is_proc_alive(Process *p)
return !ERTS_PROC_IS_EXITING(p);
}
-void register_info(int to, void *to_arg)
+void register_info(fmtfn_t to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h
index d839f55d6b..27a314ca78 100644
--- a/erts/emulator/beam/register.h
+++ b/erts/emulator/beam/register.h
@@ -44,7 +44,7 @@ typedef struct reg_proc
int process_reg_size(void);
int process_reg_sz(void);
void init_register_table(void);
-void register_info(int, void *);
+void register_info(fmtfn_t, void *);
int erts_register_name(Process *, Eterm, Eterm);
Eterm erts_whereis_name_to_id(Process *, Eterm);
void erts_whereis_name(Process *, ErtsProcLocks,
diff --git a/erts/emulator/beam/safe_hash.h b/erts/emulator/beam/safe_hash.h
index 6910b33004..285103cb17 100644
--- a/erts/emulator/beam/safe_hash.h
+++ b/erts/emulator/beam/safe_hash.h
@@ -26,14 +26,9 @@
#ifndef __SAFE_HASH_H__
#define __SAFE_HASH_H__
-
-#ifndef __SYS_H__
#include "sys.h"
-#endif
-
#include "erl_alloc.h"
-
typedef unsigned long SafeHashValue;
typedef int (*SHCMP_FUN)(void*, void*);
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index dfe82cab44..950583bbfa 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -378,6 +378,7 @@ typedef long Sint64;
# ifdef LONG_MIN
# define ERTS_SINT64_MIN LONG_MIN
# endif
+# define ErtsStrToSint64 strtol
# elif SIZEOF_LONG_LONG == 8
# define HAVE_INT64 1
typedef unsigned long long Uint64;
@@ -391,6 +392,7 @@ typedef long long Sint64;
# ifdef LLONG_MIN
# define ERTS_SINT64_MIN LLONG_MIN
# endif
+# define ErtsStrToSint64 strtoll
# else
# error "No 64-bit integer type found"
# endif
@@ -479,6 +481,12 @@ extern volatile int erts_break_requested;
void erts_do_break_handling(void);
#endif
+#if !defined(ERTS_SMP) && !defined(__WIN32__)
+extern volatile Uint erts_signal_sigterm;
+#define ERTS_SIGNAL_SIGTERM erts_signal_sigterm
+void erts_handle_signal_sigterm(void);
+#endif
+
#ifdef ERTS_WANT_GOT_SIGUSR1
# ifndef UNIX
# define ERTS_GOT_SIGUSR1 0
@@ -610,22 +618,21 @@ Uint erts_sys_misc_mem_sz(void);
#include "erl_printf.h"
/* Io constants to erts_print and erts_putc */
-#define ERTS_PRINT_STDERR (2)
-#define ERTS_PRINT_STDOUT (1)
-#define ERTS_PRINT_FILE (-1)
-#define ERTS_PRINT_SBUF (-2)
-#define ERTS_PRINT_SNBUF (-3)
-#define ERTS_PRINT_DSBUF (-4)
-
-#define ERTS_PRINT_MIN ERTS_PRINT_DSBUF
+#define ERTS_PRINT_STDERR ((fmtfn_t)0)
+#define ERTS_PRINT_STDOUT ((fmtfn_t)1)
+#define ERTS_PRINT_FILE ((fmtfn_t)2)
+#define ERTS_PRINT_SBUF ((fmtfn_t)3)
+#define ERTS_PRINT_SNBUF ((fmtfn_t)4)
+#define ERTS_PRINT_DSBUF ((fmtfn_t)5)
+#define ERTS_PRINT_FD ((fmtfn_t)6)
typedef struct {
char *buf;
size_t size;
} erts_print_sn_buf;
-int erts_print(int to, void *arg, char *format, ...); /* in utils.c */
-int erts_putc(int to, void *arg, char); /* in utils.c */
+int erts_print(fmtfn_t to, void *arg, char *format, ...); /* in utils.c */
+int erts_putc(fmtfn_t to, void *arg, char); /* in utils.c */
/* logger stuff is declared here instead of in global.h, so sys files
won't have to include global.h */
@@ -783,10 +790,10 @@ Preload* sys_preloaded(void);
unsigned char* sys_preload_begin(Preload*);
void sys_preload_end(Preload*);
int sys_get_key(int);
-void elapsed_time_both(UWord *ms_user, UWord *ms_sys,
- UWord *ms_user_diff, UWord *ms_sys_diff);
-void wall_clock_elapsed_time_both(UWord *ms_total,
- UWord *ms_diff);
+void elapsed_time_both(ErtsMonotonicTime *ms_user, ErtsMonotonicTime *ms_sys,
+ ErtsMonotonicTime *ms_user_diff, ErtsMonotonicTime *ms_sys_diff);
+void wall_clock_elapsed_time_both(ErtsMonotonicTime *ms_total,
+ ErtsMonotonicTime *ms_diff);
void get_time(int *hour, int *minute, int *second);
void get_date(int *year, int *month, int *day);
void get_localtime(int *year, int *month, int *day,
@@ -858,9 +865,12 @@ int erts_sys_unsetenv(char *key);
char *erts_read_env(char *key);
void erts_free_read_env(void *value);
+#if defined(ERTS_SMP)
#if defined(ERTS_THR_HAVE_SIG_FUNCS) && !defined(ETHR_UNUSABLE_SIGUSRX)
extern void sys_thr_resume(erts_tid_t tid);
extern void sys_thr_suspend(erts_tid_t tid);
+#define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2
+#endif
#endif
/* utils.c */
@@ -893,7 +903,7 @@ void sys_alloc_stat(SysAllocStat *);
#define ERTS_REFC_DEBUG
#endif
-typedef erts_smp_atomic_t erts_refc_t;
+typedef erts_atomic_t erts_refc_t;
ERTS_GLB_INLINE void erts_refc_init(erts_refc_t *refcp, erts_aint_t val);
ERTS_GLB_INLINE void erts_refc_inc(erts_refc_t *refcp, erts_aint_t min_val);
@@ -912,27 +922,27 @@ ERTS_GLB_INLINE erts_aint_t erts_refc_read(erts_refc_t *refcp,
ERTS_GLB_INLINE void
erts_refc_init(erts_refc_t *refcp, erts_aint_t val)
{
- erts_smp_atomic_init_nob((erts_smp_atomic_t *) refcp, val);
+ erts_atomic_init_nob((erts_atomic_t *) refcp, val);
}
ERTS_GLB_INLINE void
erts_refc_inc(erts_refc_t *refcp, erts_aint_t min_val)
{
#ifdef ERTS_REFC_DEBUG
- erts_aint_t val = erts_smp_atomic_inc_read_nob((erts_smp_atomic_t *) refcp);
+ erts_aint_t val = erts_atomic_inc_read_nob((erts_atomic_t *) refcp);
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
"erts_refc_inc(): Bad refc found (refc=%ld < %ld)!\n",
val, min_val);
#else
- erts_smp_atomic_inc_nob((erts_smp_atomic_t *) refcp);
+ erts_atomic_inc_nob((erts_atomic_t *) refcp);
#endif
}
ERTS_GLB_INLINE erts_aint_t
erts_refc_inctest(erts_refc_t *refcp, erts_aint_t min_val)
{
- erts_aint_t val = erts_smp_atomic_inc_read_nob((erts_smp_atomic_t *) refcp);
+ erts_aint_t val = erts_atomic_inc_read_nob((erts_atomic_t *) refcp);
#ifdef ERTS_REFC_DEBUG
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
@@ -946,20 +956,20 @@ ERTS_GLB_INLINE void
erts_refc_dec(erts_refc_t *refcp, erts_aint_t min_val)
{
#ifdef ERTS_REFC_DEBUG
- erts_aint_t val = erts_smp_atomic_dec_read_nob((erts_smp_atomic_t *) refcp);
+ erts_aint_t val = erts_atomic_dec_read_nob((erts_atomic_t *) refcp);
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
"erts_refc_dec(): Bad refc found (refc=%ld < %ld)!\n",
val, min_val);
#else
- erts_smp_atomic_dec_nob((erts_smp_atomic_t *) refcp);
+ erts_atomic_dec_nob((erts_atomic_t *) refcp);
#endif
}
ERTS_GLB_INLINE erts_aint_t
erts_refc_dectest(erts_refc_t *refcp, erts_aint_t min_val)
{
- erts_aint_t val = erts_smp_atomic_dec_read_nob((erts_smp_atomic_t *) refcp);
+ erts_aint_t val = erts_atomic_dec_read_nob((erts_atomic_t *) refcp);
#ifdef ERTS_REFC_DEBUG
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
@@ -973,20 +983,20 @@ ERTS_GLB_INLINE void
erts_refc_add(erts_refc_t *refcp, erts_aint_t diff, erts_aint_t min_val)
{
#ifdef ERTS_REFC_DEBUG
- erts_aint_t val = erts_smp_atomic_add_read_nob((erts_smp_atomic_t *) refcp, diff);
+ erts_aint_t val = erts_atomic_add_read_nob((erts_atomic_t *) refcp, diff);
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
"erts_refc_add(%ld): Bad refc found (refc=%ld < %ld)!\n",
diff, val, min_val);
#else
- erts_smp_atomic_add_nob((erts_smp_atomic_t *) refcp, diff);
+ erts_atomic_add_nob((erts_atomic_t *) refcp, diff);
#endif
}
ERTS_GLB_INLINE erts_aint_t
erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val)
{
- erts_aint_t val = erts_smp_atomic_read_nob((erts_smp_atomic_t *) refcp);
+ erts_aint_t val = erts_atomic_read_nob((erts_atomic_t *) refcp);
#ifdef ERTS_REFC_DEBUG
if (val < min_val)
erts_exit(ERTS_ABORT_EXIT,
@@ -998,6 +1008,112 @@ erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val)
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+typedef erts_smp_atomic_t erts_smp_refc_t;
+
+ERTS_GLB_INLINE void erts_smp_refc_init(erts_smp_refc_t *refcp, erts_aint_t val);
+ERTS_GLB_INLINE void erts_smp_refc_inc(erts_smp_refc_t *refcp, erts_aint_t min_val);
+ERTS_GLB_INLINE erts_aint_t erts_smp_refc_inctest(erts_smp_refc_t *refcp,
+ erts_aint_t min_val);
+ERTS_GLB_INLINE void erts_smp_refc_dec(erts_smp_refc_t *refcp, erts_aint_t min_val);
+ERTS_GLB_INLINE erts_aint_t erts_smp_refc_dectest(erts_smp_refc_t *refcp,
+ erts_aint_t min_val);
+ERTS_GLB_INLINE void erts_smp_refc_add(erts_smp_refc_t *refcp, erts_aint_t diff,
+ erts_aint_t min_val);
+ERTS_GLB_INLINE erts_aint_t erts_smp_refc_read(erts_smp_refc_t *refcp,
+ erts_aint_t min_val);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void
+erts_smp_refc_init(erts_smp_refc_t *refcp, erts_aint_t val)
+{
+ erts_smp_atomic_init_nob((erts_smp_atomic_t *) refcp, val);
+}
+
+ERTS_GLB_INLINE void
+erts_smp_refc_inc(erts_smp_refc_t *refcp, erts_aint_t min_val)
+{
+#ifdef ERTS_REFC_DEBUG
+ erts_aint_t val = erts_smp_atomic_inc_read_nob((erts_smp_atomic_t *) refcp);
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_inc(): Bad refc found (refc=%ld < %ld)!\n",
+ val, min_val);
+#else
+ erts_smp_atomic_inc_nob((erts_smp_atomic_t *) refcp);
+#endif
+}
+
+ERTS_GLB_INLINE erts_aint_t
+erts_smp_refc_inctest(erts_smp_refc_t *refcp, erts_aint_t min_val)
+{
+ erts_aint_t val = erts_smp_atomic_inc_read_nob((erts_smp_atomic_t *) refcp);
+#ifdef ERTS_REFC_DEBUG
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_inctest(): Bad refc found (refc=%ld < %ld)!\n",
+ val, min_val);
+#endif
+ return val;
+}
+
+ERTS_GLB_INLINE void
+erts_smp_refc_dec(erts_smp_refc_t *refcp, erts_aint_t min_val)
+{
+#ifdef ERTS_REFC_DEBUG
+ erts_aint_t val = erts_smp_atomic_dec_read_nob((erts_smp_atomic_t *) refcp);
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_dec(): Bad refc found (refc=%ld < %ld)!\n",
+ val, min_val);
+#else
+ erts_smp_atomic_dec_nob((erts_smp_atomic_t *) refcp);
+#endif
+}
+
+ERTS_GLB_INLINE erts_aint_t
+erts_smp_refc_dectest(erts_smp_refc_t *refcp, erts_aint_t min_val)
+{
+ erts_aint_t val = erts_smp_atomic_dec_read_nob((erts_smp_atomic_t *) refcp);
+#ifdef ERTS_REFC_DEBUG
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_dectest(): Bad refc found (refc=%ld < %ld)!\n",
+ val, min_val);
+#endif
+ return val;
+}
+
+ERTS_GLB_INLINE void
+erts_smp_refc_add(erts_smp_refc_t *refcp, erts_aint_t diff, erts_aint_t min_val)
+{
+#ifdef ERTS_REFC_DEBUG
+ erts_aint_t val = erts_smp_atomic_add_read_nob((erts_smp_atomic_t *) refcp, diff);
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_add(%ld): Bad refc found (refc=%ld < %ld)!\n",
+ diff, val, min_val);
+#else
+ erts_smp_atomic_add_nob((erts_smp_atomic_t *) refcp, diff);
+#endif
+}
+
+ERTS_GLB_INLINE erts_aint_t
+erts_smp_refc_read(erts_smp_refc_t *refcp, erts_aint_t min_val)
+{
+ erts_aint_t val = erts_smp_atomic_read_nob((erts_smp_atomic_t *) refcp);
+#ifdef ERTS_REFC_DEBUG
+ if (val < min_val)
+ erts_exit(ERTS_ABORT_EXIT,
+ "erts_smp_refc_read(): Bad refc found (refc=%ld < %ld)!\n",
+ val, min_val);
+#endif
+ return val;
+}
+
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+
#ifdef ERTS_ENABLE_KERNEL_POLL
extern int erts_use_kernel_poll;
#endif
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 675fafa726..a9447e3ce3 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -85,7 +85,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra)
&& HEAP_TOP(p) >= p->space_verified_from
&& HEAP_TOP(p) + need <= p->space_verified_from + p->space_verified
&& HEAP_LIMIT(p) - HEAP_TOP(p) >= need) {
-
+
Uint consumed = need + (HEAP_TOP(p) - p->space_verified_from);
ASSERT(consumed <= p->space_verified);
p->space_verified -= consumed;
@@ -102,6 +102,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra)
if (bp != NULL && need <= (bp->alloc_size - bp->used_size)) {
Eterm* ret = bp->mem + bp->used_size;
bp->used_size += need;
+ p->mbuf_sz += need;
return ret;
}
#ifdef DEBUG
@@ -124,7 +125,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra)
MBUF(p) = bp;
bp->alloc_size = n;
bp->used_size = need;
- MBUF_SIZE(p) += n;
+ MBUF_SIZE(p) += need;
bp->off_heap.first = NULL;
bp->off_heap.overhead = 0;
return bp->mem;
@@ -347,40 +348,41 @@ int erts_fit_in_bits_uint(Uint value)
}
int
-erts_print(int to, void *arg, char *format, ...)
+erts_print(fmtfn_t to, void *arg, char *format, ...)
{
int res;
va_list arg_list;
va_start(arg_list, format);
- if (to < ERTS_PRINT_MIN)
- res = -EINVAL;
- else {
- switch (to) {
- case ERTS_PRINT_STDOUT:
+ {
+ switch ((UWord)to) {
+ case (UWord)ERTS_PRINT_STDOUT:
res = erts_vprintf(format, arg_list);
break;
- case ERTS_PRINT_STDERR:
+ case (UWord)ERTS_PRINT_STDERR:
res = erts_vfprintf(stderr, format, arg_list);
break;
- case ERTS_PRINT_FILE:
+ case (UWord)ERTS_PRINT_FILE:
res = erts_vfprintf((FILE *) arg, format, arg_list);
break;
- case ERTS_PRINT_SBUF:
+ case (UWord)ERTS_PRINT_SBUF:
res = erts_vsprintf((char *) arg, format, arg_list);
break;
- case ERTS_PRINT_SNBUF:
+ case (UWord)ERTS_PRINT_SNBUF:
res = erts_vsnprintf(((erts_print_sn_buf *) arg)->buf,
((erts_print_sn_buf *) arg)->size,
format,
arg_list);
break;
- case ERTS_PRINT_DSBUF:
+ case (UWord)ERTS_PRINT_DSBUF:
res = erts_vdsprintf((erts_dsprintf_buf_t *) arg, format, arg_list);
break;
- default:
- res = erts_vfdprintf((int) to, format, arg_list);
+ case (UWord)ERTS_PRINT_FD:
+ res = erts_vfdprintf((int)(SWord) arg, format, arg_list);
break;
+ default:
+ res = erts_vcbprintf(to, arg, format, arg_list);
+ break;
}
}
@@ -389,7 +391,7 @@ erts_print(int to, void *arg, char *format, ...)
}
int
-erts_putc(int to, void *arg, char c)
+erts_putc(fmtfn_t to, void *arg, char c)
{
return erts_print(to, arg, "%c", c);
}
@@ -638,7 +640,7 @@ erts_bld_atom_uword_2tup_list(Uint **hpp, Uint *szp,
ui = uint_to_big(uints[i], *hpp);
*hpp += BIG_UINT_HEAP_SIZE;
}
-
+
res = CONS(*hpp+3, TUPLE2(*hpp, atoms[i], ui), res);
*hpp += 5;
}
@@ -676,14 +678,14 @@ erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length,
ui1 = uint_to_big(uints1[i], *hpp);
*hpp += BIG_UINT_HEAP_SIZE;
}
-
+
if (IS_USMALL(0, uints2[i]))
ui2 = make_small(uints2[i]);
else {
ui2 = uint_to_big(uints2[i], *hpp);
*hpp += BIG_UINT_HEAP_SIZE;
}
-
+
res = CONS(*hpp+4, TUPLE3(*hpp, atoms[i], ui1, ui2), res);
*hpp += 6;
}
@@ -794,7 +796,7 @@ hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash)
Uint b;
Uint lshift = bitoffs;
Uint rshift = 8 - lshift;
-
+
while (sz--) {
b = (previous << lshift) & 0xFF;
previous = *ptr++;
@@ -805,7 +807,7 @@ hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash)
b = (previous << lshift) & 0xFF;
previous = *ptr++;
b |= previous >> rshift;
-
+
b >>= 8 - bitsize;
hash = (hash*FUNNY_NUMBER1 + b) * FUNNY_NUMBER12 + bitsize;
}
@@ -835,21 +837,21 @@ Uint32 make_hash(Eterm term_arg)
do { \
Uint32 x = (Uint32) (Expr); \
hash = \
- (((((hash)*(Prime1) + (x & 0xFF)) * (Prime1) + \
- ((x >> 8) & 0xFF)) * (Prime1) + \
- ((x >> 16) & 0xFF)) * (Prime1) + \
+ (((((hash)*(Prime1) + (x & 0xFF)) * (Prime1) + \
+ ((x >> 8) & 0xFF)) * (Prime1) + \
+ ((x >> 16) & 0xFF)) * (Prime1) + \
(x >> 24)); \
} while(0)
-#define UINT32_HASH_RET(Expr, Prime1, Prime2) \
+#define UINT32_HASH_RET(Expr, Prime1, Prime2) \
UINT32_HASH_STEP(Expr, Prime1); \
hash = hash * (Prime2); \
- break
-
-
+ break
+
+
/*
* Significant additions needed for real 64 bit port with larger fixnums.
- */
+ */
/*
* Note, for the simple 64bit port, not utilizing the
@@ -864,7 +866,7 @@ tail_recur:
hash = hash*FUNNY_NUMBER3 + 1;
break;
case ATOM_DEF:
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(term))->slot.bucket.hvalue);
break;
case SMALL_DEF:
@@ -893,9 +895,9 @@ tail_recur:
Export* ep = *((Export **) (export_val(term) + 1));
hash = hash * FUNNY_NUMBER11 + ep->code[2];
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue);
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[1]))->slot.bucket.hvalue);
break;
}
@@ -906,7 +908,7 @@ tail_recur:
Uint num_free = funp->num_free;
hash = hash * FUNNY_NUMBER10 + num_free;
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue);
hash = hash*FUNNY_NUMBER2 + funp->fe->old_index;
hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq;
@@ -931,7 +933,7 @@ tail_recur:
UINT32_HASH_RET(internal_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10);
case EXTERNAL_REF_DEF:
UINT32_HASH_RET(external_ref_numbers(term)[0],FUNNY_NUMBER9,FUNNY_NUMBER10);
- case FLOAT_DEF:
+ case FLOAT_DEF:
{
FloatDef ff;
GET_DOUBLE(term, ff);
@@ -958,12 +960,12 @@ tail_recur:
** as multiplications on a Sparc is so slow.
*/
hash = hash*FUNNY_NUMBER2 + unsigned_val(*list);
-
+
if (is_not_list(CDR(list))) {
WSTACK_PUSH(stack, MAKE_HASH_CDR_POST_OP);
term = CDR(list);
goto tail_recur;
- }
+ }
list = list_val(CDR(list));
}
WSTACK_PUSH2(stack, CDR(list), MAKE_HASH_CDR_PRE_OP);
@@ -1004,17 +1006,17 @@ tail_recur:
}
hash *= is_neg ? FUNNY_NUMBER4 : FUNNY_NUMBER3;
break;
- }
+ }
case MAP_DEF:
hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term);
break;
- case TUPLE_DEF:
+ case TUPLE_DEF:
{
Eterm* ptr = tuple_val(term);
Uint arity = arityval(*ptr);
WSTACK_PUSH3(stack, (UWord) arity, (UWord)(ptr+1), (UWord) arity);
- op = MAKE_HASH_TUPLE_OP;
+ op = MAKE_HASH_TUPLE_OP;
}/*fall through*/
case MAKE_HASH_TUPLE_OP:
case MAKE_HASH_TERM_ARRAY_OP:
@@ -1031,8 +1033,8 @@ tail_recur:
hash = hash*FUNNY_NUMBER9 + arity;
}
break;
- }
-
+ }
+
default:
erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash(0x%X,0x%X)\n", term, op);
return 0;
@@ -1159,8 +1161,8 @@ make_hash2(Eterm term)
if (y < 0) { \
UINT32_HASH(-y, AConst); \
/* Negative numbers are unnecessarily mixed twice. */ \
- } \
- UINT32_HASH(y, AConst); \
+ } \
+ UINT32_HASH(y, AConst); \
} while(0)
#define IS_SSMALL28(x) (((Uint) (((x) >> (28-1)) + 1)) < 2)
@@ -1242,7 +1244,7 @@ make_hash2(Eterm term)
int arity = header_arity(hdr);
Eterm* elem = tuple_val(term);
UINT32_HASH(arity, HCONST_9);
- if (arity == 0) /* Empty tuple */
+ if (arity == 0) /* Empty tuple */
goto hash2_common;
for (i = arity; ; i--) {
term = elem[i];
@@ -1329,7 +1331,7 @@ make_hash2(Eterm term)
{
Export* ep = *((Export **) (export_val(term) + 1));
UINT32_HASH_2
- (ep->code[2],
+ (ep->code[2],
atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue,
HCONST);
UINT32_HASH
@@ -1343,7 +1345,7 @@ make_hash2(Eterm term)
ErlFunThing* funp = (ErlFunThing *) fun_val(term);
Uint num_free = funp->num_free;
UINT32_HASH_2
- (num_free,
+ (num_free,
atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue,
HCONST);
UINT32_HASH_2
@@ -1468,7 +1470,7 @@ make_hash2(Eterm term)
goto hash2_common;
}
break;
-
+
default:
erts_exit(ERTS_ERROR_EXIT, "Invalid tag in make_hash2(0x%X)\n", term);
}
@@ -1541,7 +1543,7 @@ make_hash2(Eterm term)
}
case HASH_MAP_PAIR:
hash_xor_pairs ^= hash;
- hash = 0;
+ hash = 0;
goto hash2_common;
default:
break;
@@ -1678,17 +1680,22 @@ make_internal_hash(Eterm term)
* the order in which keys and values are encountered.
* We therefore calculate context independent hashes for all .
* key-value pairs and then xor them together.
+ *
+ * We *do* need to use an initial seed for each pair, i.e. the
+ * hash value, so the hash value is reset for each pair with 'hash'.
+ * If we don't, no additional entropy is given to the system and the
+ * hash collision resolution in maps:from_list/1 would fail.
*/
ESTACK_PUSH(s, hash_xor_pairs);
ESTACK_PUSH(s, hash);
ESTACK_PUSH(s, HASH_MAP_TAIL);
- hash = 0;
- hash_xor_pairs = 0;
for (i = size - 1; i >= 0; i--) {
+ ESTACK_PUSH(s, hash); /* initial seed for all pairs */
ESTACK_PUSH(s, HASH_MAP_PAIR);
ESTACK_PUSH(s, vs[i]);
ESTACK_PUSH(s, ks[i]);
}
+ hash_xor_pairs = 0;
goto pop_next;
}
case HAMT_SUBTAG_HEAD_ARRAY:
@@ -1700,7 +1707,6 @@ make_internal_hash(Eterm term)
ESTACK_PUSH(s, hash_xor_pairs);
ESTACK_PUSH(s, hash);
ESTACK_PUSH(s, HASH_MAP_TAIL);
- hash = 0;
hash_xor_pairs = 0;
}
switch (hdr & _HEADER_MAP_SUBTAG_MASK) {
@@ -1717,6 +1723,7 @@ make_internal_hash(Eterm term)
while (i) {
if (is_list(*ptr)) {
Eterm* cons = list_val(*ptr);
+ ESTACK_PUSH(s, hash); /* initial seed for all pairs */
ESTACK_PUSH(s, HASH_MAP_PAIR);
ESTACK_PUSH(s, CDR(cons));
ESTACK_PUSH(s, CAR(cons));
@@ -1906,6 +1913,7 @@ make_internal_hash(Eterm term)
pop_next:
if (ESTACK_ISEMPTY(s)) {
DESTROY_ESTACK(s);
+
return hash;
}
@@ -1920,7 +1928,7 @@ make_internal_hash(Eterm term)
}
case HASH_MAP_PAIR:
hash_xor_pairs ^= hash;
- hash = 0;
+ hash = (Uint32) ESTACK_POP(s); /* initial seed for all pairs */
goto pop_next;
case HASH_CDR:
@@ -1953,8 +1961,8 @@ Uint32 make_broken_hash(Eterm term)
DECLARE_WSTACK(stack);
unsigned op;
tail_recur:
- op = tag_val_def(term);
- for (;;) {
+ op = tag_val_def(term);
+ for (;;) {
switch (op) {
case NIL_DEF:
hash = hash*FUNNY_NUMBER3 + 1;
@@ -1976,8 +1984,7 @@ tail_recur:
{ /* like a bignum */
Uint32 y4 = (Uint32) y2;
hash = hash*FUNNY_NUMBER2 + ((y4 << 16) | (y4 >> 16));
- if (y3)
- {
+ if (y3) {
hash = hash*FUNNY_NUMBER2 + ((y3 << 16) | (y3 >> 16));
arity++;
}
@@ -2020,9 +2027,9 @@ tail_recur:
Export* ep = *((Export **) (export_val(term) + 1));
hash = hash * FUNNY_NUMBER11 + ep->code[2];
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[0]))->slot.bucket.hvalue);
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(ep->code[1]))->slot.bucket.hvalue);
break;
}
@@ -2033,7 +2040,7 @@ tail_recur:
Uint num_free = funp->num_free;
hash = hash * FUNNY_NUMBER10 + num_free;
- hash = hash*FUNNY_NUMBER1 +
+ hash = hash*FUNNY_NUMBER1 +
(atom_tab(atom_val(funp->fe->module))->slot.bucket.hvalue);
hash = hash*FUNNY_NUMBER2 + funp->fe->old_index;
hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq;
@@ -2065,7 +2072,7 @@ tail_recur:
case EXTERNAL_REF_DEF:
hash = hash*FUNNY_NUMBER9 + external_ref_numbers(term)[0];
break;
- case FLOAT_DEF:
+ case FLOAT_DEF:
{
FloatDef ff;
GET_DOUBLE(term, ff);
@@ -2149,7 +2156,7 @@ tail_recur:
}
#else
-#error "unsupported D_EXP size"
+#error "unsupported D_EXP size"
#endif
hash = hash * (is_neg ? FUNNY_NUMBER3 : FUNNY_NUMBER2) + arity;
}
@@ -2158,14 +2165,14 @@ tail_recur:
case MAP_DEF:
hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + make_hash2(term);
break;
- case TUPLE_DEF:
+ case TUPLE_DEF:
{
Eterm* ptr = tuple_val(term);
Uint arity = arityval(*ptr);
WSTACK_PUSH3(stack, (UWord) arity, (UWord) (ptr+1), (UWord) arity);
op = MAKE_HASH_TUPLE_OP;
- }/*fall through*/
+ }/*fall through*/
case MAKE_HASH_TUPLE_OP:
case MAKE_HASH_TERM_ARRAY_OP:
{
@@ -2193,7 +2200,7 @@ tail_recur:
DESTROY_WSTACK(stack);
return hash;
-
+
#undef MAKE_HASH_TUPLE_OP
#undef MAKE_HASH_TERM_ARRAY_OP
#undef MAKE_HASH_CDR_PRE_OP
@@ -2353,13 +2360,13 @@ static int do_send_term_to_logger(Eterm tag, Eterm gleader,
}
static ERTS_INLINE int
-send_info_to_logger(Eterm gleader, char *buf, int len)
+send_info_to_logger(Eterm gleader, char *buf, int len)
{
return do_send_to_logger(am_info_msg, gleader, buf, len);
}
static ERTS_INLINE int
-send_warning_to_logger(Eterm gleader, char *buf, int len)
+send_warning_to_logger(Eterm gleader, char *buf, int len)
{
Eterm tag;
switch (erts_error_logger_warnings) {
@@ -2371,7 +2378,7 @@ send_warning_to_logger(Eterm gleader, char *buf, int len)
}
static ERTS_INLINE int
-send_error_to_logger(Eterm gleader, char *buf, int len)
+send_error_to_logger(Eterm gleader, char *buf, int len)
{
return do_send_to_logger(am_error, gleader, buf, len);
}
@@ -2613,7 +2620,7 @@ tailrecur_ne:
break; /* not equal */
case TAG_PRIMARY_BOXED:
- {
+ {
Eterm hdr = *boxed_val(a);
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG:
@@ -2639,7 +2646,7 @@ tailrecur_ne:
Uint b_bitsize;
Uint a_bitoffs;
Uint b_bitoffs;
-
+
if (!is_binary(b)) {
goto not_equal;
}
@@ -2671,7 +2678,7 @@ tailrecur_ne:
{
ErlFunThing* f1;
ErlFunThing* f2;
-
+
if (!is_fun(b))
goto not_equal;
f1 = (ErlFunThing *) fun_val(a);
@@ -2702,7 +2709,7 @@ tailrecur_ne:
if(ap->header == bp->header && ap->node == bp->node) {
ASSERT(1 == external_data_words(a));
ASSERT(1 == external_data_words(b));
-
+
if (ap->data.ui[0] == bp->data.ui[0]) goto pop_next;
}
break; /* not equal */
@@ -2759,7 +2766,7 @@ tailrecur_ne:
if (alen == 3 && blen == 3) {
/* Most refs are of length 3 */
if (anum[1] == bnum[1] && anum[2] == bnum[2]) {
- goto pop_next;
+ goto pop_next;
} else {
goto not_equal;
}
@@ -2784,7 +2791,7 @@ tailrecur_ne:
for (i = common_len; i < blen; i++)
if (bnum[i] != 0)
goto not_equal;
- }
+ }
}
goto pop_next;
}
@@ -2792,7 +2799,7 @@ tailrecur_ne:
case NEG_BIG_SUBTAG:
{
int i;
-
+
if (!is_big(b))
goto not_equal;
aa = big_val(a);
@@ -2810,7 +2817,7 @@ tailrecur_ne:
{
FloatDef af;
FloatDef bf;
-
+
if (is_float(b)) {
GET_DOUBLE(a, af);
GET_DOUBLE(b, bf);
@@ -2889,7 +2896,7 @@ term_array: /* arrays in 'aa' and 'bb', length in 'sz' */
}
goto tailrecur_ne;
}
-
+
pop_next:
if (!WSTACK_ISEMPTY(stack)) {
UWord something = WSTACK_POP(stack);
@@ -3093,7 +3100,7 @@ tailrecur_ne:
}
anode = erts_this_node;
adata = internal_port_data(a);
-
+
port_common:
CMP_NODES(anode, bnode);
ON_CMP_GOTO((Sint)(adata - bdata));
@@ -3111,7 +3118,7 @@ tailrecur_ne:
}
anode = erts_this_node;
adata = internal_pid_data(a);
-
+
pid_common:
if (adata != bdata) {
RETURN_NEQ(adata < bdata ? -1 : 1);
@@ -3336,7 +3343,7 @@ tailrecur_ne:
diff = f1->num_free - f2->num_free;
if (diff != 0) {
RETURN_NEQ(diff);
- }
+ }
i = f1->num_free;
if (i == 0) goto pop_next;
aa = f1->env;
@@ -3397,10 +3404,10 @@ tailrecur_ne:
anum = internal_thing_ref_numbers(athing);
alen = internal_thing_ref_no_of_numbers(athing);
}
-
+
ref_common:
CMP_NODES(anode, bnode);
-
+
ASSERT(alen > 0 && blen > 0);
if (alen != blen) {
if (alen > blen) {
@@ -3418,11 +3425,11 @@ tailrecur_ne:
} while (alen < blen);
}
}
-
+
ASSERT(alen == blen);
for (i = (Sint) alen - 1; i >= 0; i--)
if (anum[i] != bnum[i])
- RETURN_NEQ((Sint32) (anum[i] - bnum[i]));
+ RETURN_NEQ(anum[i] < bnum[i] ? -1 : 1);
goto pop_next;
case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE):
if (is_internal_ref(b)) {
@@ -3633,8 +3640,8 @@ term_array: /* arrays in 'aa' and 'bb', length in 'i' */
}
a = *aa;
b = *bb;
- goto tailrecur;
-
+ goto tailrecur;
+
pop_next:
if (!WSTACK_ISEMPTY(stack)) {
UWord something = WSTACK_POP(stack);
@@ -3835,7 +3842,7 @@ store_external_or_ref_(Uint **hpp, ErlOffHeap* oh, Eterm ns)
for(i = 0; i < size; i++)
to_hp[i] = from_hp[i];
- erts_refc_inc(&((ExternalThing *) to_hp)->node->refc, 2);
+ erts_smp_refc_inc(&((ExternalThing *) to_hp)->node->refc, 2);
((struct erl_off_heap_header*) to_hp)->next = oh->first;
oh->first = (struct erl_off_heap_header*) to_hp;
@@ -3870,7 +3877,7 @@ store_external_or_ref_in_proc_(Process *proc, Eterm ns)
return store_external_or_ref_(&hp, &MSO(proc), ns);
}
-void bin_write(int to, void *to_arg, byte* buf, size_t sz)
+void bin_write(fmtfn_t to, void *to_arg, byte* buf, size_t sz)
{
size_t i;
@@ -3897,19 +3904,19 @@ intlist_to_buf(Eterm list, char *buf, Sint len)
Eterm* listptr;
Sint sz = 0;
- if (is_nil(list))
+ if (is_nil(list))
return 0;
if (is_not_list(list))
return -1;
listptr = list_val(list);
while (sz < len) {
- if (!is_byte(*listptr))
+ if (!is_byte(*listptr))
return -1;
buf[sz++] = unsigned_val(*listptr);
if (is_nil(*(listptr + 1)))
return(sz);
- if (is_not_list(*(listptr + 1)))
+ if (is_not_list(*(listptr + 1)))
return -1;
listptr = list_val(*(listptr + 1));
}
@@ -4157,10 +4164,10 @@ do { \
} else if (yield_support && --yield_count <= 0)
goto L_yield;
}
-
+
res = len;
- L_return:
+ L_return:
DESTROY_ESTACK(s);
@@ -5053,7 +5060,7 @@ Process *p;
if(p)
print_process_info(ERTS_PRINT_STDERR, NULL, p);
}
-
+
void ppi(Eterm pid)
{
pp(erts_proc_lookup(pid));
@@ -5079,5 +5086,3 @@ ps(Process* p, Eterm* stop)
}
}
#endif
-
-
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 5ce0e1de9e..31ef4167be 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -777,6 +777,7 @@ static int is_nonzero(const char *s, size_t n)
#define INET_LOPT_NETNS 38 /* Network namespace pathname */
#define INET_LOPT_TCP_SHOW_ECONNRESET 39 /* tell user about incoming RST */
#define INET_LOPT_LINE_DELIM 40 /* Line delimiting char */
+#define INET_OPT_TCLASS 41 /* IPv6 transport class */
/* SCTP options: a separate range, from 100: */
#define SCTP_OPT_RTOINFO 100
#define SCTP_OPT_ASSOCINFO 101
@@ -1024,6 +1025,7 @@ typedef struct {
inet_async_op* oph; /* queue head or NULL */
inet_async_op* opt; /* queue tail or NULL */
inet_async_op op_queue[INET_MAX_ASYNC]; /* call queue */
+ int op_ref; /* queue reference generator */
int active; /* 0 = passive, 1 = active, 2 = active once */
Sint16 active_count; /* counter for {active,N} */
@@ -1255,6 +1257,8 @@ static int tcp_shutdown_error(tcp_descriptor* desc, int err);
static int tcp_inet_output(tcp_descriptor* desc, HANDLE event);
static int tcp_inet_input(tcp_descriptor* desc, HANDLE event);
+static void tcp_desc_close(tcp_descriptor*);
+
#ifdef HAVE_UDP
typedef struct {
inet_descriptor inet; /* common data structure (DON'T MOVE) */
@@ -1272,8 +1276,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event);
/* convert descriptor pointer to inet_descriptor pointer */
#define INETP(d) (&(d)->inet)
-static int async_ref = 0; /* async reference id generator */
-#define NEW_ASYNC_ID() ((async_ref++) & 0xffff)
+#define NEW_ASYNC_ID(desc) ((desc)->op_ref++ & 0xffff)
/* check for transition from active to passive */
#define INET_CHECK_ACTIVE_TO_PASSIVE(inet) \
@@ -1330,6 +1333,7 @@ static ErlDrvTermData am_reuseaddr;
static ErlDrvTermData am_dontroute;
static ErlDrvTermData am_priority;
static ErlDrvTermData am_tos;
+static ErlDrvTermData am_tclass;
static ErlDrvTermData am_ipv6_v6only;
static ErlDrvTermData am_netns;
#endif
@@ -1923,7 +1927,7 @@ static void enq_multi_op(tcp_descriptor *desc, char *buf, int req,
ErlDrvTermData caller, MultiTimerData *timeout,
ErlDrvMonitor *monitorp)
{
- int id = NEW_ASYNC_ID();
+ int id = NEW_ASYNC_ID(INETP(desc));
enq_old_multi_op(desc,id,req,caller,timeout,monitorp);
if (buf != NULL)
put_int16(id, buf);
@@ -1992,7 +1996,7 @@ static int remove_multi_op(tcp_descriptor *desc, int *id_p, int *req_p,
static int enq_async_w_tmo(inet_descriptor* desc, char* buf, int req, unsigned timeout,
ErlDrvMonitor *monitorp)
{
- int id = NEW_ASYNC_ID();
+ int id = NEW_ASYNC_ID(desc);
inet_async_op* opp;
if ((opp = desc->oph) == NULL) /* queue empty */
@@ -3720,6 +3724,7 @@ static void inet_init_sctp(void) {
INIT_ATOM(dontroute);
INIT_ATOM(priority);
INIT_ATOM(tos);
+ INIT_ATOM(tclass);
INIT_ATOM(ipv6_v6only);
INIT_ATOM(netns);
@@ -4317,6 +4322,12 @@ static void desc_close(inet_descriptor* desc)
desc->event = INVALID_EVENT; /* closed by stop_select callback */
desc->s = INVALID_SOCKET;
desc->event_mask = 0;
+
+ /* mark as disconnected in case when socket is left lingering due to
+ * {exit_on_close, false} option in gen_tcp socket creation. Next
+ * write to socket should produce {error, enotconn} and send a
+ * message {tcp_error,#Port<>,econnreset} */
+ desc->state &= ~INET_STATE_CONNECTED;
}
}
@@ -6228,6 +6239,15 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
#else
continue;
#endif
+#if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ case INET_OPT_TCLASS:
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ propagate = 1;
+ DEBUGF(("inet_set_opts(%ld): s=%d, IPV6_TCLASS=%d\r\n",
+ (long)desc->port, desc->s, ival));
+ break;
+#endif
case TCP_OPT_NODELAY:
proto = IPPROTO_TCP;
@@ -6661,6 +6681,19 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
continue; /* Option not supported -- ignore it */
# endif
+# if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ case INET_OPT_TCLASS:
+ {
+ arg.ival= get_int32 (curr); curr += 4;
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ arg_ptr = (char*) (&arg.ival);
+ arg_sz = sizeof ( arg.ival);
+ break;
+ }
+# endif
+
+
case INET_OPT_IPV6_V6ONLY:
# if HAVE_DECL_IPV6_V6ONLY
{
@@ -7162,6 +7195,15 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
put_int32(0, ptr);
continue;
#endif
+ case INET_OPT_TCLASS:
+#if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ break;
+#else
+ TRUNCATE_TO(0,ptr);
+ continue;
+#endif
case INET_OPT_REUSEADDR:
type = SO_REUSEADDR;
break;
@@ -7556,6 +7598,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
case INET_OPT_DONTROUTE:
case INET_OPT_PRIORITY :
case INET_OPT_TOS :
+ case INET_OPT_TCLASS :
case INET_OPT_IPV6_V6ONLY:
case SCTP_OPT_AUTOCLOSE:
case SCTP_OPT_MAXSEG :
@@ -7629,6 +7672,19 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
continue;
# endif
}
+ case INET_OPT_TCLASS:
+ {
+# if defined(IPV6_TCLASS) && defined(SOL_IPV6)
+ proto = SOL_IPV6;
+ type = IPV6_TCLASS;
+ is_int = 1;
+ tag = am_tclass;
+ break;
+# else
+ /* Not supported -- ignore */
+ continue;
+# endif
+ }
case INET_OPT_IPV6_V6ONLY:
# if HAVE_DECL_IPV6_V6ONLY
{
@@ -8262,6 +8318,7 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
desc->delimiter = '\n'; /* line delimiting char */
desc->oph = NULL;
desc->opt = NULL;
+ desc->op_ref = 0;
desc->peer_ptr = NULL;
desc->name_ptr = NULL;
@@ -9090,16 +9147,31 @@ static void tcp_inet_stop(ErlDrvData e)
tcp_descriptor* desc = (tcp_descriptor*)e;
DEBUGF(("tcp_inet_stop(%ld) {s=%d\r\n",
(long)desc->inet.port, desc->inet.s));
+
tcp_close_check(desc);
- /* free input buffer & output buffer */
- if (desc->i_buf != NULL)
- release_buffer(desc->i_buf);
- desc->i_buf = NULL; /* net_mess2 may call this function recursively when
- faulty messages arrive on dist ports*/
+ tcp_clear_input(desc);
+
DEBUGF(("tcp_inet_stop(%ld) }\r\n", (long)desc->inet.port));
inet_stop(INETP(desc));
}
+/* Closes a tcp descriptor without leaving things hanging; the VM keeps trying
+ * to flush IO queues as long as it contains anything even after the port has
+ * been closed from the erlang side, which is desired behavior (Think escripts
+ * writing to files) but pretty hopeless if the underlying fd has been set to
+ * INVALID_SOCKET through desc_close.
+ *
+ * This function should be used in place of desc_close/erl_inet_close in all
+ * TCP-related operations. Note that this only closes the desc cleanly; it
+ * will be freed through tcp_inet_stop later on. */
+static void tcp_desc_close(tcp_descriptor* desc)
+{
+ tcp_clear_input(desc);
+ tcp_clear_output(desc);
+
+ erl_inet_close(INETP(desc));
+}
+
/* TCP requests from Erlang */
static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
char* buf, ErlDrvSizeT len,
@@ -9344,7 +9416,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
case INET_REQ_CLOSE:
DEBUGF(("tcp_inet_ctl(%ld): CLOSE\r\n", (long)desc->inet.port));
tcp_close_check(desc);
- erl_inet_close(INETP(desc));
+ tcp_desc_close(desc);
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
@@ -9468,7 +9540,7 @@ static void tcp_inet_timeout(ErlDrvData e)
set_busy_port(desc->inet.port, 0);
inet_reply_error_am(INETP(desc), am_timeout);
if (desc->send_timeout_close) {
- erl_inet_close(INETP(desc));
+ tcp_desc_close(desc);
}
}
else {
@@ -9482,7 +9554,7 @@ static void tcp_inet_timeout(ErlDrvData e)
else if ((state & INET_STATE_CONNECTING) == INET_STATE_CONNECTING) {
/* assume connect timeout */
/* close the socket since it's not usable (see man pages) */
- erl_inet_close(INETP(desc));
+ tcp_desc_close(desc);
async_error_am(INETP(desc), am_timeout);
}
else if ((state & INET_STATE_ACCEPTING) == INET_STATE_ACCEPTING) {
@@ -9645,8 +9717,7 @@ static int tcp_recv_closed(tcp_descriptor* desc)
/* passive mode do not terminate port ! */
tcp_clear_input(desc);
if (desc->inet.exitf) {
- tcp_clear_output(desc);
- desc_close(INETP(desc));
+ tcp_desc_close(desc);
} else {
desc_close_read(INETP(desc));
}
@@ -9689,7 +9760,7 @@ static int tcp_recv_error(tcp_descriptor* desc, int err)
driver_cancel_timer(desc->inet.port);
tcp_clear_input(desc);
if (desc->inet.exitf) {
- desc_close(INETP(desc));
+ tcp_desc_close(desc);
} else {
desc_close_read(INETP(desc));
}
@@ -10358,12 +10429,10 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err)
if (desc->inet.exitf)
driver_exit(desc->inet.port, 0);
else
- desc_close(INETP(desc));
+ tcp_desc_close(desc);
} else {
- tcp_clear_output(desc);
- tcp_clear_input(desc);
tcp_close_check(desc);
- erl_inet_close(INETP(desc));
+ tcp_desc_close(desc);
if (desc->inet.caller) {
if (show_econnreset)
@@ -11407,6 +11476,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
VALGRIND_MAKE_MEM_DEFINED(mhdr.msg_control, mhdr.msg_controllen); /*suppress "uninitialised bytes"*/
mhdr.msg_flags = 0; /* Not used with "sendmsg" */
+ inet_output_count(desc, data_len);
/* Now do the actual sending. NB: "flags" in "sendmsg" itself are NOT
used: */
code = sock_sendmsg(desc->s, &mhdr, 0);
diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c
index bfe0807df8..82266ee736 100644
--- a/erts/emulator/drivers/unix/unix_efile.c
+++ b/erts/emulator/drivers/unix/unix_efile.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2017. 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.
@@ -79,11 +79,10 @@
* Macros for testing file types.
*/
-#define ISDIR(st) (((st).st_mode & S_IFMT) == S_IFDIR)
-#define ISREG(st) (((st).st_mode & S_IFMT) == S_IFREG)
-#define ISDEV(st) \
- (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
-#define ISLNK(st) (((st).st_mode & S_IFLNK) == S_IFLNK)
+#define ISDIR(st) (S_ISDIR((st).st_mode))
+#define ISREG(st) (S_ISREG((st).st_mode))
+#define ISDEV(st) (S_ISCHR((st).st_mode) || S_ISBLK((st).st_mode))
+#define ISLNK(st) (S_ISLNK((st).st_mode))
#ifdef NO_UMASK
#define FILE_MODE 0644
#define DIR_MODE 0755
@@ -366,33 +365,6 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
int fd;
int mode; /* Open mode. */
- if (stat(name, &statbuf) < 0) {
- /* statbuf is undefined: if the caller depends on it,
- i.e. invoke_read_file(), fail the call immediately */
- if (pSize && flags == EFILE_MODE_READ)
- return check_error(-1, errInfo);
- } else if (!ISREG(statbuf)) {
- /*
- * For UNIX only, here is some ugly code to allow
- * /dev/null to be opened as a file.
- *
- * Assumption: The i-node number for /dev/null cannot be zero.
- */
- static ino_t dev_null_ino = 0;
-
- if (dev_null_ino == 0) {
- struct stat nullstatbuf;
-
- if (stat("/dev/null", &nullstatbuf) >= 0) {
- dev_null_ino = nullstatbuf.st_ino;
- }
- }
- if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) {
- errno = EISDIR;
- return check_error(-1, errInfo);
- }
- }
-
switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) {
case EFILE_MODE_READ:
mode = O_RDONLY;
@@ -411,16 +383,13 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
return check_error(-1, errInfo);
}
-
if (flags & EFILE_MODE_APPEND) {
mode &= ~O_TRUNC;
mode |= O_APPEND;
}
-
if (flags & EFILE_MODE_EXCL) {
mode |= O_EXCL;
}
-
if (flags & EFILE_MODE_SYNC) {
#ifdef O_SYNC
mode |= O_SYNC;
@@ -430,15 +399,52 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
#endif
}
- fd = open(name, mode, FILE_MODE);
+#ifdef HAVE_FSTAT
+ while (((fd = open(name, mode, FILE_MODE)) < 0) && (errno == EINTR));
+ if (!check_error(fd, errInfo)) return 0;
+#endif
- if (!check_error(fd, errInfo))
- return 0;
+ if (
+#ifdef HAVE_FSTAT
+ fstat(fd, &statbuf) < 0
+#else
+ stat(name, &statbuf) < 0
+#endif
+ ) {
+ /* statbuf is undefined: if the caller depends on it,
+ i.e. invoke_read_file(), fail the call immediately */
+ if (pSize && flags == EFILE_MODE_READ) {
+ check_error(-1, errInfo);
+#ifdef HAVE_FSTAT
+ efile_closefile(fd);
+#endif
+ return 0;
+ }
+ }
+ else if (! ISREG(statbuf)) {
+ struct stat nullstatbuf;
+ /*
+ * For UNIX only, here is some ugly code to allow
+ * /dev/null to be opened as a file.
+ */
+ if ( (stat("/dev/null", &nullstatbuf) < 0)
+ || (statbuf.st_ino != nullstatbuf.st_ino)
+ || (statbuf.st_dev != nullstatbuf.st_dev) ) {
+#ifdef HAVE_FSTAT
+ efile_closefile(fd);
+#endif
+ errno = EISDIR;
+ return check_error(-1, errInfo);
+ }
+ }
+
+#ifndef HAVE_FSTAT
+ while (((fd = open(name, mode, FILE_MODE)) < 0) && (errno == EINTR));
+ if (!check_error(fd, errInfo)) return 0;
+#endif
*pfd = fd;
- if (pSize) {
- *pSize = statbuf.st_size;
- }
+ if (pSize) *pSize = statbuf.st_size;
return 1;
}
diff --git a/erts/emulator/hipe/hipe_amd64_asm.m4 b/erts/emulator/hipe/hipe_amd64_asm.m4
index 2c0fbbee2d..409fd0ef89 100644
--- a/erts/emulator/hipe/hipe_amd64_asm.m4
+++ b/erts/emulator/hipe/hipe_amd64_asm.m4
@@ -121,6 +121,22 @@ define(NSP,%rsp)dnl
/*
+ * Debugging macros
+ *
+ * Keeps track of whether context has been saved in the debug build, allowing us
+ * to detect when the garbage collector is called when it shouldn't.
+ */
+`#ifdef DEBUG
+# define SET_GC_UNSAFE \
+ movq $1, P_GCUNSAFE(P)
+# define SET_GC_SAFE \
+ movq $0, P_GCUNSAFE(P)
+#else
+# define SET_GC_UNSAFE
+# define SET_GC_SAFE
+#endif'
+
+/*
* Context switching macros.
*/
`#define SWITCH_C_TO_ERLANG_QUICK \
@@ -133,12 +149,14 @@ define(NSP,%rsp)dnl
`#define SAVE_CACHED_STATE \
SAVE_HP; \
- SAVE_FCALLS'
+ SAVE_FCALLS; \
+ SET_GC_SAFE'
`#define RESTORE_CACHED_STATE \
RESTORE_HP; \
RESTORE_HEAP_LIMIT; \
- RESTORE_FCALLS'
+ RESTORE_FCALLS; \
+ SET_GC_UNSAFE'
`#define SWITCH_C_TO_ERLANG \
RESTORE_CACHED_STATE; \
diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4
index 9cf3bf74fd..21739726bb 100644
--- a/erts/emulator/hipe/hipe_amd64_bifs.m4
+++ b/erts/emulator/hipe/hipe_amd64_bifs.m4
@@ -600,10 +600,11 @@ noproc_primop_interface_0(nbif_handle_fp_exception, erts_restore_fpu)
define(gc_bif_interface_0,`nofail_primop_interface_0($1, $2)')
/*
- * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2).
+ * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2,3).
*/
define(gc_bif_interface_1,`standard_bif_interface_1($1, $2)')
define(gc_bif_interface_2,`standard_bif_interface_2($1, $2)')
+define(gc_bif_interface_3,`standard_bif_interface_3($1, $2)')
/*
* Implement gc_nofail_primop_interface_1 as nofail_primop_interface_1.
diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S
index b37ed3c68a..f3404888d5 100644
--- a/erts/emulator/hipe/hipe_amd64_glue.S
+++ b/erts/emulator/hipe/hipe_amd64_glue.S
@@ -94,6 +94,7 @@ ASYM(nbif_return):
.nosave_exit:
/* switch to C stack */
SWITCH_ERLANG_TO_C_QUICK
+ SET_GC_SAFE
/* restore C callee-save registers, drop frame, return */
movq (%rsp), %rbp # kills P
movq 8(%rsp), %rbx
@@ -398,6 +399,7 @@ nbif_4_simple_exception:
movl %eax, P_NARITY(P) # Note: narity is a 32-bit field
/* find and prepare to invoke the handler */
SWITCH_ERLANG_TO_C_QUICK # The cached state is clean and need not be saved.
+ SET_GC_SAFE
movq P, %rdi
call CSYM(hipe_handle_exception) # Note: hipe_handle_exception() conses
SWITCH_C_TO_ERLANG # %rsp updated by hipe_find_handler()
diff --git a/erts/emulator/hipe/hipe_arm_asm.m4 b/erts/emulator/hipe/hipe_arm_asm.m4
index ae9ec752bb..68a6faa70b 100644
--- a/erts/emulator/hipe/hipe_arm_asm.m4
+++ b/erts/emulator/hipe/hipe_arm_asm.m4
@@ -48,6 +48,24 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive
`#define TEMP_LR r8'
/*
+ * Debugging macros
+ *
+ * Keeps track of whether context has been saved in the debug build, allowing us
+ * to detect when the garbage collector is called when it shouldn't.
+ */
+`#ifdef DEBUG
+# define SET_GC_UNSAFE(SCRATCH) \
+ mov SCRATCH, #1; \
+ str SCRATCH, [P, #P_GCUNSAFE]
+# define SET_GC_SAFE(SCRATCH) \
+ mov SCRATCH, #0; \
+ str SCRATCH, [P, #P_GCUNSAFE]
+#else
+# define SET_GC_UNSAFE(SCRATCH)
+# define SET_GC_SAFE(SCRATCH)
+#endif'
+
+/*
* Context switching macros.
*
* RESTORE_CONTEXT and RESTORE_CONTEXT_QUICK do not affect
@@ -59,12 +77,14 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive
`#define RESTORE_CONTEXT_QUICK \
mov lr, TEMP_LR'
-`#define SAVE_CACHED_STATE \
- str HP, [P, #P_HP]; \
- str NSP, [P, #P_NSP]'
+`#define SAVE_CACHED_STATE \
+ str HP, [P, #P_HP]; \
+ str NSP, [P, #P_NSP]; \
+ SET_GC_SAFE(HP)'
-`#define RESTORE_CACHED_STATE \
- ldr HP, [P, #P_HP]; \
+`#define RESTORE_CACHED_STATE \
+ SET_GC_UNSAFE(HP); \
+ ldr HP, [P, #P_HP]; \
ldr NSP, [P, #P_NSP]'
`#define SAVE_CONTEXT_BIF \
@@ -75,12 +95,14 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive
ldr HP, [P, #P_HP]'
`#define SAVE_CONTEXT_GC \
+ SET_GC_SAFE(TEMP_LR); \
mov TEMP_LR, lr; \
str lr, [P, #P_NRA]; \
str NSP, [P, #P_NSP]; \
str HP, [P, #P_HP]'
`#define RESTORE_CONTEXT_GC \
+ SET_GC_UNSAFE(HP); \
ldr HP, [P, #P_HP]'
/*
diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4
index d9c9952dbf..d7a2fec04a 100644
--- a/erts/emulator/hipe/hipe_arm_bifs.m4
+++ b/erts/emulator/hipe/hipe_arm_bifs.m4
@@ -198,8 +198,9 @@ $1:
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
- * Generate native interface for a BIF with 0-2 parameters and
+ * Generate native interface for a BIF with 0-3 parameters and
* standard failure mode.
* The BIF may do a GC.
*/
@@ -279,6 +280,36 @@ $1:
.type $1, %function
#endif')
+define(gc_bif_interface_3,
+`
+#ifndef HAVE_$1
+#`define' HAVE_$1
+ .global $1
+$1:
+ /* Set up C argument registers. */
+ mov r0, P
+ NBIF_ARG(r1,3,0)
+ NBIF_ARG(r2,3,1)
+ NBIF_ARG(r3,3,2)
+
+ /* Save caller-save registers and call the C function. */
+ SAVE_CONTEXT_GC
+ str r1, [r0, #P_ARG0] /* Store BIF__ARGS in def_arg_reg[] */
+ str r2, [r0, #P_ARG1]
+ str r3, [r0, #P_ARG2]
+ add r1, r0, #P_ARG0
+ CALL_BIF($2)
+ TEST_GOT_MBUF(3)
+
+ /* Restore registers. Check for exception. */
+ cmp r0, #THE_NON_VALUE
+ RESTORE_CONTEXT_GC
+ beq nbif_3_simple_exception
+ NBIF_RET(3)
+ .size $1, .-$1
+ .type $1, %function
+#endif')
+
/*
* gc_nofail_primop_interface_1(nbif_name, cbif_name)
*
diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S
index 49ffa8b1d8..5b7f8ad52d 100644
--- a/erts/emulator/hipe/hipe_arm_glue.S
+++ b/erts/emulator/hipe/hipe_arm_glue.S
@@ -342,6 +342,7 @@ nbif_4_gc_after_bif:
str r1, [P, #P_NARITY]
str TEMP_LR, [P, #P_NRA]
str NSP, [P, #P_NSP]
+ SET_GC_SAFE(TEMP_LR)
mov TEMP_LR, lr
mov r3, #0 /* Pass 0 in arity */
mov r2, #0 /* Pass NULL in regs */
@@ -349,6 +350,7 @@ nbif_4_gc_after_bif:
mov r0, P
bl erts_gc_after_bif_call
mov lr, TEMP_LR
+ SET_GC_UNSAFE(TEMP_LR)
ldr TEMP_LR, [P, #P_NRA]
mov r1, #0
str r1, [P, #P_NARITY]
@@ -404,6 +406,7 @@ nbif_4_simple_exception:
str NSP, [P, #P_NSP]
str TEMP_LR, [P, #P_NRA]
str r1, [P, #P_NARITY]
+ SET_GC_SAFE(r0)
/* find and prepare to invoke the handler */
mov r0, P
bl hipe_handle_exception /* Note: hipe_handle_exception() conses */
@@ -423,6 +426,7 @@ nbif_4_simple_exception:
str NSP, [P, #P_NSP]
str r1, [P, #P_NARITY]
str TEMP_LR, [P, #P_NRA]
+ SET_GC_SAFE(NSP)
b .nosave_exit
/*
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 3336fded7a..c0ebc34771 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2017. 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.
@@ -992,7 +992,7 @@ BIF_RETTYPE hipe_bifs_set_native_address_in_fe_2(BIF_ALIST_2)
BIF_ERROR(BIF_P, BADARG);
fe->native_address = native_address;
- if (erts_refc_dectest(&fe->refc, 0) == 0)
+ if (erts_smp_refc_dectest(&fe->refc, 0) == 0)
erts_erase_fun_entry(fe);
BIF_RET(am_true);
}
@@ -1476,6 +1476,10 @@ int hipe_find_mfa_from_ra(const void *ra, Eterm *m, Eterm *f, unsigned int *a)
struct hipe_mfa_info **bucket;
unsigned int i, nrbuckets;
+ if (hipe_is_ra_mode_switch(ra)) {
+ return 0;
+ }
+
/* Note about locking: the table is only updated from the
loader, which runs with the rest of the system suspended. */
/* XXX: alas not true; see comment at hipe_mfa_info_table.lock */
diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c
index 08adbd474e..5e127755c6 100644
--- a/erts/emulator/hipe/hipe_bif1.c
+++ b/erts/emulator/hipe/hipe_bif1.c
@@ -124,757 +124,3 @@ BIF_RETTYPE hipe_bifs_trap_count_clear_0(BIF_ALIST_0)
hipe_trap_count = 0;
BIF_RET(make_small(count));
}
-
-/*****************************************************************************
- * BIFs for benchmarking. These only do useful things if
- * __BENCHMARK__ is defined in beam/benchmark.h. For documentation
- * about how to add new counters or maintain the existing counters,
- * see benchmark.h.
- *
- * If benchmarking is not enabled all BIFs will return false. If the
- * required benchmark feature is not enabled, the counter will remain
- * zero.
- *
- * process_info/0 -> { Number of live processes,
- * Processes spawned in total }
- *
- * Live processes are increased when a new process is created, and
- * decreased when a process dies. Processes spawned is increased
- * when a process is created.
- *
- *
- * process_info_clear/0 -> true
- *
- * Will reset the processes spawned-counters to zero. If this is
- * done at some improper time, live processes may become a negative
- * value. This is not a problem in itself, just as long as you know
- * about it.
- *
- *
- * message_info/0 -> { Messages sent,
- * Messages copied,
- * Ego messages (sender = receiver),
- * Words sent,
- * Words copied,
- * Words preallocated }
- *
- * Counting the words sent in a shared heap system will affect
- * runtime performance since it means that we have to calculate the
- * size of the mesage. With private heaps, this is done anyway and
- * will not affect performance.
- *
- *
- * message_info_clear/0 -> true
- *
- * Reset the message counters to zero.
- *
- *
- * message_sizes/0 -> true
- *
- * Displays a text-mode bar diagram with message sizes. There are no
- * guaranties that this is printed in a way the Erlang system is
- * supposed to print things.
- *
- *
- * gc_info/0 -> { Minor collections,
- * Major collections,
- * Used heap,
- * Allocated heap,
- * Max used heap,
- * Max allocated heap }
- *
- * Information about private heap garbage collections. Number of
- * minor and major collections, how much heap is used and allocated
- * and how much heap has been in use and allocated at most since the
- * counters were reset.
- *
- *
- * shared_gc_info/0 -> { Minor collections of the shared heap,
- * Major collections of the shared heap,
- * Used shared heap,
- * Allocated shared heap,
- * Max used shared heap,
- * Max allocated shared heap }
- *
- * The same as above, but for the shared heap / message area. Note,
- * that in a shared heap system the max used heap and max allocated
- * heap are mostly the same, since the heap allways is filled before
- * a garbage collection, and most garbage collections do not enlarge
- * the heap. The private heap numbers are much more interesting.
- *
- *
- * incremental_gc_info/0 -> { Complete minor GC cycles,
- * Complete major GC cycles,
- * Minor GC stages,
- * Major GC stages }
- *
- *
- * gc_info_clear/0 -> true
- *
- * Reset counters for both private and shared garbage collection.
- *
- *
- * BM Timers
- * ---------
- *
- * All timers returns tuples of the kind: { Minutes, Seconds, Milliseconds }
- * except for the max times in garbage collection where times are normally
- * small. The tuple is therefor: { Seconds, Milliseconds, Microseconds }
- *
- * system_timer/0 -> Mutator time
- *
- * This timer is not a real-time clock, it only runs when a process
- * is scheduled to run. You can not find out the accual time a
- * program has taken to run using this timer.
- *
- *
- * system_timer_clear/0 -> true
- *
- * Reset system timer to zero.
- *
- *
- * send_timer/0 -> { Send time,
- * Copy time,
- * Size time }
- *
- * Time spent in sending messages. The copy time and size time are
- * only active if the copying is needed in send. Copying of data
- * into ETS-tables etc is not timed with this timer.
- *
- *
- * send_timer_clear/0 -> true
- *
- * Reset send timers to zero.
- *
- *
- * gc_timer/0 -> { Time in minor collection,
- * Time in major collection,
- * Max time in minor collection (�s),
- * Max time in major collection (�s) }
- *
- * Total time spent in garbage collection of the private heaps. The
- * max times are for one separate collection.
- *
- *
- * shared_gc_timer/0 -> { Time in minor collection,
- * Time in major collection,
- * Max time in minor collection (�s),
- * Max time in major collection (�s) }
- *
- * Total time spent in garbage collection of the shared heap /
- * message area. The max times are for one separate collection.
- *
- *
- * gc_timer_clear/0 -> true
- *
- * Reset private and shared garbage collection timers to zero. Note,
- * that the max-times are also reset.
- *
- *
- * misc_timer/0 -> { Misc 0, Misc 1, Misc 2 }
- *
- * Timers for debug purposes. In a normal system, these timers are
- * never used. Add these timers at places where you want to time
- * something not covered here. Use BM_SWAP_TIMER(from,to) to start
- * one of the misc timers.
- *
- * ... code timed by the system timer ...
- * BM_SWAP_TIMER(system,misc1);
- * ... code we want to time ...
- * BM_SWAP_TIMER(misc1,system);
- * ... back on system time ...
- *
- *
- * misc_timer_clear/0 -> true
- *
- * Reset misc timers to zero.
- */
-
-BIF_RETTYPE hipe_bifs_process_info_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#ifndef BM_COUNTERS
- Uint processes_busy = 0;
- Uint processes_spawned = 0;
-#endif
- Eterm *hp;
-
- hp = HAlloc(BIF_P, 3);
- BIF_RET(TUPLE2(hp,
- make_small(processes_busy),
- make_small(processes_spawned)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_process_info_clear_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#ifdef BM_COUNTERS
- processes_spawned = 0;
-#endif
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_message_info_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- Eterm *hp;
-#ifndef BM_COUNTERS
- unsigned long messages_sent = 0;
- unsigned long messages_copied = 0;
- unsigned long messages_ego = 0;
-#endif
-#ifndef BM_MESSAGE_SIZES
- unsigned long words_sent = 0;
- unsigned long words_copied = 0;
- unsigned long words_prealloc = 0;
-#endif
-
- hp = HAlloc(BIF_P, 7);
- BIF_RET(TUPLE6(hp,
- make_small(messages_sent),
- make_small(messages_copied),
- make_small(messages_ego),
- make_small(words_sent),
- make_small(words_copied),
- make_small(words_prealloc)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_message_info_clear_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#ifdef BM_COUNTERS
- messages_sent = 0;
- messages_copied = 0;
- messages_ego = 0;
-#endif
-#ifdef BM_MESSAGE_SIZES
- words_sent = 0;
- words_copied = 0;
- words_prealloc = 0;
- {
- int i;
- for (i = 0; i < 1000; i++)
- message_sizes[i] = 0;
- }
-#endif
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_message_sizes_0(BIF_ALIST_0)
-{
-#ifdef BM_MESSAGE_SIZES
- int i, j, max = 0;
- int tmp[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
-
- for (i = 0; i < 65; i++) {
- tmp[0] += message_sizes[i];
- if (tmp[0] > max)
- max = tmp[0];
- }
- for (i = 65; i < 999; i++) {
- tmp[i / 100 + 1] += message_sizes[i];
- if (tmp[i / 100 + 1] > max)
- max = tmp[i / 100 + 1];
- }
- tmp[11] = message_sizes[999];
- if (tmp[11] > max)
- max = tmp[11];
- for (i = -1; i < 11; i++) {
- int num = (tmp[i + 1] * 50) / max;
- if (i == -1)
- printf("\n\r 0 - 64: (%6d) |", tmp[0]);
- else if (i == 0)
- printf("\n\r 65 - 99: (%6d) |", tmp[1]);
- else if (i == 10)
- printf("\n\r >= 1000: (%6d) |", tmp[11]);
- else
- printf("\n\r%3d - %3d: (%6d) |", i * 100, i * 100 + 99,
- tmp[i + 1]);
-
- for (j = 0; j < num; j++)
- printf(".");
- }
- printf("\n\r");
-
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_gc_info_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#ifndef BM_COUNTERS
- Uint minor_gc = 0;
- Uint major_gc = 0;
-#endif
-#ifndef BM_HEAP_SIZES
- Uint max_used_heap = 0;
- Uint max_allocated_heap = 0;
-#endif
- Eterm *hp;
- Uint used_heap = (BIF_P->htop - BIF_P->heap) +
- (OLD_HTOP(BIF_P) - OLD_HEAP(BIF_P)) +
- MBUF_SIZE(BIF_P);
-
- Uint alloc_heap = (BIF_P->hend - BIF_P->heap) +
- (OLD_HEND(BIF_P) - OLD_HEAP(BIF_P)) +
- MBUF_SIZE(BIF_P);
-
- hp = HAlloc(BIF_P, 7);
- BIF_RET(TUPLE6(hp,
- make_small((Uint)minor_gc),
- make_small((Uint)major_gc),
- make_small((Uint)used_heap),
- make_small((Uint)alloc_heap),
- make_small(max_used_heap),
- make_small(max_allocated_heap)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#if !(defined(BM_COUNTERS))
- Uint minor_global_gc = 0;
- Uint major_global_gc = 0;
-#endif
-#ifndef BM_HEAP_SIZES
- Uint max_used_global_heap = 0;
- Uint max_allocated_global_heap = 0;
-#endif
- Eterm *hp;
-
- Uint tmp_used_heap = 0;
- Uint tmp_allocated_heap = 0;
-
- hp = HAlloc(BIF_P, 7);
- BIF_RET(TUPLE6(hp,
- make_small((uint)minor_global_gc),
- make_small((uint)major_global_gc),
- make_small(tmp_used_heap),
- make_small(tmp_allocated_heap),
- make_small(max_used_global_heap),
- make_small(max_allocated_global_heap)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_incremental_gc_info_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-#if !defined(BM_COUNTERS)
- Uint minor_gc_cycles = 0;
- Uint major_gc_cycles = 0;
- Uint minor_gc_stages = 0;
- Uint major_gc_stages = 0;
-#endif
- Eterm *hp;
-
- hp = HAlloc(BIF_P, 5);
- BIF_RET(TUPLE4(hp,
- make_small(minor_gc_cycles),
- make_small(major_gc_cycles),
- make_small(minor_gc_stages),
- make_small(major_gc_stages)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_gc_info_clear_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
-
-#ifdef BM_COUNTERS
- minor_gc = 0;
- major_gc = 0;
-#endif
-
-#ifdef BM_HEAP_SIZES
- max_used_heap = 0;
- max_allocated_heap = 0;
- max_used_global_heap = 0;
- max_allocated_global_heap = 0;
-#endif
-
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_pause_times_0(BIF_ALIST_0)
-{
-#ifdef BM_TIMERS
- int i;
- int total_time = 0, n = 0;
- int left = 0, right = 0, mid = 0;
-
- printf("Pause times in minor collection:\r\n");
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (pause_times[i] > 0) {
- printf("%d: %ld\r\n", i, pause_times[i]);
- total_time += pause_times[i] * i;
- n += pause_times[i];
-
- if (i > mid)
- right += pause_times[i];
-
- while (right > left) {
- left += pause_times[mid++];
- right -= pause_times[mid];
- }
- }
- }
-
- printf("Number of collections: %d\r\n", n);
- printf("Total collection time: %d\r\n", total_time);
- if (n > 0)
- printf("Mean pause time: %d\r\n", total_time / n);
-
- printf("Geometrical mean: %d\r\n", mid);
-
- total_time = 0; n = 0;
- left = 0; right = 0; mid = 0;
- printf("Pause times in major collection:\r\n");
- for (i = 0; i < MAX_PAUSE_TIME; i++) {
- if (pause_times_old[i] > 0) {
- printf("%d: %ld\r\n", i, pause_times_old[i]);
- total_time += pause_times_old[i] * i;
- n += pause_times_old[i];
- }
- }
-
- printf("Number of collections: %d\r\n", n);
- printf("Total collection time: %d\r\n", total_time);
- if (n > 0)
- printf("Mean pause time: %d\r\n", total_time / n);
-
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-/* XXX: these macros have free variables */
-#ifdef BM_TIMERS
-#define MAKE_TIME(_timer_) { \
- BM_TIMER_T tmp = _timer_##_time / 1000000; \
- milli = tmp % 1000; \
- tmp /= 1000; \
- sec = tmp % 60; \
- min = tmp / 60; }
-
-#define MAKE_MICRO_TIME(_timer_) { \
- BM_TIMER_T tmp = _timer_##_time / 1000; \
- micro = tmp % 1000; \
- tmp /= 1000; \
- milli = tmp % 1000; \
- sec = tmp / 1000; }
-
-#else
-#define MAKE_TIME(_timer_)
-#define MAKE_MICRO_TIME(_timer_)
-#endif
-
-BIF_RETTYPE hipe_bifs_system_timer_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- uint min = 0;
- uint sec = 0;
- uint milli = 0;
- Eterm *hp;
-
- hp = HAlloc(BIF_P, 4);
- MAKE_TIME(system);
- BIF_RET(TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli)));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_system_timer_clear_0(BIF_ALIST_0)
-{
-#ifdef BM_TIMERS
- system_time = 0;
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_send_timer_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- uint min = 0;
- uint sec = 0;
- uint milli = 0;
- Eterm *hp;
- Eterm sendtime, copytime, sizetime;
-
- hp = HAlloc(BIF_P, 4 * 4);
-
- MAKE_TIME(send);
- sendtime = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(copy);
- copytime = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(size);
- sizetime = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
- BIF_RET(TUPLE3(hp, sendtime, copytime, sizetime));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_send_timer_clear_0(BIF_ALIST_0)
-{
-#ifdef BM_TIMERS
- send_time = 0;
- copy_time = 0;
- size_time = 0;
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_gc_timer_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- Eterm *hp;
- uint min = 0;
- uint sec = 0;
- uint milli = 0;
- uint micro = 0;
- Eterm minor, major, max_min, max_maj;
-
- hp = HAlloc(BIF_P, 4 * 4 + 5);
-
- MAKE_TIME(minor_gc);
- minor = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(major_gc);
- major = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_MICRO_TIME(max_minor);
- max_min = TUPLE3(hp,
- make_small(sec),
- make_small(milli),
- make_small(micro));
- hp += 4;
-
- MAKE_MICRO_TIME(max_major);
- max_maj = TUPLE3(hp,
- make_small(sec),
- make_small(milli),
- make_small(micro));
- hp += 4;
-
- BIF_RET(TUPLE4(hp, minor, major, max_min, max_maj));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_shared_gc_timer_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- Eterm *hp;
- uint min = 0;
- uint sec = 0;
- uint milli = 0;
- uint micro = 0;
- Eterm minor, major, max_min, max_maj;
-
- hp = HAlloc(BIF_P, 4 * 4 + 5);
-
- MAKE_TIME(minor_global_gc);
- minor = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(major_global_gc);
- major = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_MICRO_TIME(max_global_minor);
- max_min = TUPLE3(hp,
- make_small(sec),
- make_small(milli),
- make_small(micro));
- hp += 4;
-
- MAKE_MICRO_TIME(max_global_major);
- max_maj = TUPLE3(hp,
- make_small(sec),
- make_small(milli),
- make_small(micro));
- hp += 4;
-
- BIF_RET(TUPLE4(hp, minor, major, max_min, max_maj));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_gc_timer_clear_0(BIF_ALIST_0)
-{
-#ifdef BM_TIMERS
- minor_gc_time = 0;
- major_gc_time = 0;
- max_minor_time = 0;
- max_major_time = 0;
- minor_global_gc_time = 0;
- major_global_gc_time = 0;
- max_global_minor_time = 0;
- max_global_major_time = 0;
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_misc_timer_0(BIF_ALIST_0)
-{
-#ifdef __BENCHMARK__
- uint min = 0;
- uint sec = 0;
- uint milli = 0;
- Eterm *hp;
- Eterm misctime1, misctime2, misctime3;
-
- hp = HAlloc(BIF_P, 4 * 4);
-
- MAKE_TIME(misc0);
- misctime1 = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(misc1);
- misctime2 = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
-
- MAKE_TIME(misc2);
- misctime3 = TUPLE3(hp,
- make_small(min),
- make_small(sec),
- make_small(milli));
- hp += 4;
- BIF_RET(TUPLE3(hp, misctime1, misctime2, misctime3));
-#else
- BIF_RET(am_false);
-#endif
-}
-
-BIF_RETTYPE hipe_bifs_misc_timer_clear_0(BIF_ALIST_0)
-{
-#ifdef BM_TIMERS
- misc0_time = 0;
- misc1_time = 0;
- misc2_time = 0;
- BIF_RET(am_true);
-#else
- BIF_RET(am_false);
-#endif
-}
-
-#undef MAKE_TIME
-#undef MAKE_MICRO_TIME
-
-/*
- * HiPE hrvtime().
- * These implementations are currently available:
- * + The fallback, which is the same as {X,_} = runtime(statistics).
- */
-
-static double fallback_get_hrvtime(void)
-{
- unsigned long ms_user;
-
- elapsed_time_both(&ms_user, NULL, NULL, NULL);
- return (double)ms_user;
-}
-
-/*
- * Fallback, if nothing better exists.
- * This is the same as {X,_} = statistics(runtime), which uses
- * times(2) on Unix systems.
- */
-
-#define hrvtime_is_started() 1
-#define start_hrvtime() do{}while(0)
-#define stop_hrvtime() do{}while(0)
-#define get_hrvtime() fallback_get_hrvtime()
-
-BIF_RETTYPE hipe_bifs_get_hrvtime_0(BIF_ALIST_0)
-{
- Eterm *hp;
- Eterm res;
- FloatDef f;
-
- if (!hrvtime_is_started())
- start_hrvtime();
- f.fd = get_hrvtime();
- hp = HAlloc(BIF_P, FLOAT_SIZE_OBJECT);
- res = make_float(hp);
- PUT_DOUBLE(f, hp);
- BIF_RET(res);
-}
-
-BIF_RETTYPE hipe_bifs_stop_hrvtime_0(BIF_ALIST_0)
-{
- stop_hrvtime();
- BIF_RET(am_true);
-}
diff --git a/erts/emulator/hipe/hipe_bif1.tab b/erts/emulator/hipe/hipe_bif1.tab
index c5b452f199..4be0ad0e9c 100644
--- a/erts/emulator/hipe/hipe_bif1.tab
+++ b/erts/emulator/hipe/hipe_bif1.tab
@@ -27,24 +27,3 @@ bif hipe_bifs:call_count_get/1
bif hipe_bifs:call_count_clear/1
bif hipe_bifs:trap_count_get/0
bif hipe_bifs:trap_count_clear/0
-bif hipe_bifs:process_info/0
-bif hipe_bifs:process_info_clear/0
-bif hipe_bifs:message_info/0
-bif hipe_bifs:message_info_clear/0
-bif hipe_bifs:message_sizes/0
-bif hipe_bifs:gc_info/0
-bif hipe_bifs:shared_gc_info/0
-bif hipe_bifs:incremental_gc_info/0
-bif hipe_bifs:gc_info_clear/0
-bif hipe_bifs:pause_times/0
-bif hipe_bifs:system_timer/0
-bif hipe_bifs:system_timer_clear/0
-bif hipe_bifs:send_timer/0
-bif hipe_bifs:send_timer_clear/0
-bif hipe_bifs:gc_timer/0
-bif hipe_bifs:shared_gc_timer/0
-bif hipe_bifs:gc_timer_clear/0
-bif hipe_bifs:misc_timer/0
-bif hipe_bifs:misc_timer_clear/0
-bif hipe_bifs:get_hrvtime/0
-bif hipe_bifs:stop_hrvtime/0
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index 29095a5389..dcf3447af9 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -96,6 +96,7 @@
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
* A BIF which may do a GC or walk the native stack.
* May read NSP, NSP_LIMIT, NRA, HP, HP_LIMIT, and FCALLS.
@@ -263,32 +264,34 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc)
',)dnl
/*
- * Standard BIFs.
- * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index)
+ * BIFs that disable GC while trapping are called via a wrapper
+ * to reserve stack space for the "trap frame".
+ * They occasionally need to call the garbage collector in order to make room
+ * for the trap frame on the BEAM stack.
*/
+gc_bif_interface_1(nbif_term_to_binary_1, hipe_wrapper_term_to_binary_1)
+gc_bif_interface_2(nbif_term_to_binary_2, hipe_wrapper_term_to_binary_2)
+gc_bif_interface_1(nbif_binary_to_term_1, hipe_wrapper_binary_to_term_1)
+gc_bif_interface_2(nbif_binary_to_term_2, hipe_wrapper_binary_to_term_2)
+gc_bif_interface_1(nbif_binary_to_list_1, hipe_wrapper_binary_to_list_1)
+gc_bif_interface_3(nbif_binary_to_list_3, hipe_wrapper_binary_to_list_3)
+gc_bif_interface_1(nbif_bitstring_to_list_1, hipe_wrapper_bitstring_to_list_1)
+gc_bif_interface_1(nbif_list_to_binary_1, hipe_wrapper_list_to_binary_1)
+gc_bif_interface_1(nbif_iolist_to_binary_1, hipe_wrapper_iolist_to_binary_1)
+gc_bif_interface_1(nbif_binary_list_to_bin_1, hipe_wrapper_binary_list_to_bin_1)
+gc_bif_interface_1(nbif_list_to_bitstring_1, hipe_wrapper_list_to_bitstring_1)
+gc_bif_interface_2(nbif_send_2, hipe_wrapper_send_2)
+gc_bif_interface_3(nbif_send_3, hipe_wrapper_send_3)
+gc_bif_interface_2(nbif_ebif_bang_2, hipe_wrapper_ebif_bang_2)
+gc_bif_interface_2(nbif_maps_merge_2, hipe_wrapper_maps_merge_2)
-/* BIFs that disable GC while trapping are called via a wrapper
- * to reserve stack space for the "trap frame".
+
+/*
+ * Standard BIFs.
+ * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index)
*/
-define(CFUN,`ifelse(
-$1, term_to_binary_1, hipe_wrapper_$1,
-$1, term_to_binary_2, hipe_wrapper_$1,
-$1, binary_to_term_1, hipe_wrapper_$1,
-$1, binary_to_term_2, hipe_wrapper_$1,
-$1, binary_to_list_1, hipe_wrapper_$1,
-$1, binary_to_list_3, hipe_wrapper_$1,
-$1, bitstring_to_list_1, hipe_wrapper_$1,
-$1, list_to_binary_1, hipe_wrapper_$1,
-$1, iolist_to_binary_1, hipe_wrapper_$1,
-$1, binary_list_to_bin_1, hipe_wrapper_$1,
-$1, list_to_bitstring_1, hipe_wrapper_$1,
-$1, send_2, hipe_wrapper_$1,
-$1, send_3, hipe_wrapper_$1,
-$1, ebif_bang_2, hipe_wrapper_$1,
-$1, maps_merge_2, hipe_wrapper_$1,
-$1)')
-define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))')
+define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, $4)')
include(TARGET/`erl_bif_list.h')
/*
diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c
index d0619a0609..68c65dea27 100644
--- a/erts/emulator/hipe/hipe_gc.c
+++ b/erts/emulator/hipe/hipe_gc.c
@@ -46,6 +46,8 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
/* arch-specific nstack walk state */
struct nstack_walk_state walk_state;
+ ASSERT(!p->hipe.gc_is_unsafe);
+
if (!p->hipe.nstack) {
ASSERT(!p->hipe.nsp && !p->hipe.nstend);
return n_htop;
@@ -136,6 +138,8 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
char *mature;
Uint mature_size;
+ ASSERT(!p->hipe.gc_is_unsafe);
+
if (!p->hipe.nstack) {
ASSERT(!p->hipe.nsp && !p->hipe.nstend);
return;
@@ -233,3 +237,122 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
}
abort();
}
+
+Eterm *sweep_literals_nstack(Process *p, Eterm *old_htop, char *area,
+ Uint area_size)
+{
+ /* known nstack walk state */
+ Eterm *nsp;
+ Eterm *nsp_end;
+ const struct sdesc *sdesc;
+ /* arch-specific nstack walk state */
+ struct nstack_walk_state walk_state;
+
+ ASSERT(!p->hipe.gc_is_unsafe);
+
+ if (!p->hipe.nstack) {
+ ASSERT(!p->hipe.nsp && !p->hipe.nstend);
+ return old_htop;
+ }
+ if (!nstack_walk_init_check(p))
+ return old_htop;
+
+ ASSERT(p->hipe.nsp && p->hipe.nstend);
+ nsp = nstack_walk_nsp_begin(p);
+ nsp_end = nstack_walk_nsp_end(p);
+ sdesc = nstack_walk_init_sdesc_ignore_trap(p, &walk_state);
+
+ while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) {
+ unsigned long ra;
+ unsigned sdesc_size = nstack_walk_frame_size(sdesc);
+ unsigned i = 0;
+ unsigned mask = sdesc->livebits[0];
+ for (;;) {
+ if (mask & 1) {
+ Eterm *nsp_i = nstack_walk_frame_index(nsp, i);
+ Eterm gval = *nsp_i;
+ if (is_boxed(gval)) {
+ Eterm *ptr = boxed_val(gval);
+ Eterm val = *ptr;
+ if (IS_MOVED_BOXED(val)) {
+ ASSERT(is_boxed(val));
+ *nsp_i = val;
+ } else if (ErtsInArea(ptr, area, area_size)) {
+ MOVE_BOXED(ptr, val, old_htop, nsp_i);
+ }
+ } else if (is_list(gval)) {
+ Eterm *ptr = list_val(gval);
+ Eterm val = *ptr;
+ if (IS_MOVED_CONS(val)) {
+ *nsp_i = ptr[1];
+ } else if (ErtsInArea(ptr, area, area_size)) {
+ MOVE_CONS(ptr, val, old_htop, nsp_i);
+ }
+ }
+ }
+ if (++i >= sdesc_size)
+ break;
+ if (i & 31)
+ mask >>= 1;
+ else
+ mask = sdesc->livebits[i >> 5];
+ }
+ ra = nstack_walk_frame_ra(nsp, sdesc);
+ if (ra == (unsigned long)nbif_stack_trap_ra)
+ ra = (unsigned long)p->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ nsp = nstack_walk_next_frame(nsp, sdesc_size);
+ }
+ return old_htop;
+}
+
+int
+nstack_any_heap_ref_ptrs(Process *rp, char* mod_start, Uint mod_size)
+{
+ Eterm *nsp;
+ Eterm *nsp_end;
+ const struct sdesc *sdesc;
+ /* arch-specific nstack walk state */
+ struct nstack_walk_state walk_state;
+
+ ASSERT(!rp->hipe.gc_is_unsafe);
+
+ if (!rp->hipe.nstack || !nstack_walk_init_check(rp)) return 0;
+ ASSERT(rp->hipe.nsp && rp->hipe.nstend);
+ nsp = nstack_walk_nsp_begin(rp);
+ nsp_end = nstack_walk_nsp_end(rp);
+ sdesc = nstack_walk_init_sdesc_ignore_trap(rp, &walk_state);
+
+ while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) {
+ unsigned long ra;
+ unsigned sdesc_size = nstack_walk_frame_size(sdesc);
+ unsigned i = 0;
+ unsigned mask = sdesc->livebits[0];
+ for (;;) {
+ if (mask & 1) {
+ Eterm *nsp_i = nstack_walk_frame_index(nsp, i);
+ Eterm val = *nsp_i;
+ switch (primary_tag(val)) {
+ case TAG_PRIMARY_BOXED:
+ case TAG_PRIMARY_LIST:
+ if (ErtsInArea(val, mod_start, mod_size)) {
+ return 1;
+ }
+ break;
+ }
+ }
+ if (++i >= sdesc_size)
+ break;
+ if (i & 31)
+ mask >>= 1;
+ else
+ mask = sdesc->livebits[i >> 5];
+ }
+ ra = nstack_walk_frame_ra(nsp, sdesc);
+ if (ra == (unsigned long)nbif_stack_trap_ra)
+ ra = (unsigned long)rp->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ nsp = nstack_walk_next_frame(nsp, sdesc_size);
+ }
+ return 0;
+}
diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c
index 0d3493ec6c..b9d4226705 100644
--- a/erts/emulator/hipe/hipe_mkliterals.c
+++ b/erts/emulator/hipe/hipe_mkliterals.c
@@ -525,6 +525,12 @@ static const struct rts_param rts_params[] = {
{ 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) },
{ 52, "THE_NON_VALUE", 1, (int)THE_NON_VALUE },
+
+ { 53, "P_GCUNSAFE",
+#ifdef DEBUG
+ 1, offsetof(struct process, hipe.gc_is_unsafe)
+#endif
+ },
};
#define NR_PARAMS ARRAY_SIZE(rts_params)
diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h
index c40077d558..e54b81cf78 100644
--- a/erts/emulator/hipe/hipe_mode_switch.h
+++ b/erts/emulator/hipe/hipe_mode_switch.h
@@ -64,6 +64,7 @@ Eterm hipe_build_stacktrace(Process *p, struct StackTrace *s);
ERTS_GLB_INLINE void hipe_reserve_beam_trap_frame(Process*, Eterm reg[], unsigned arity);
ERTS_GLB_INLINE void hipe_unreserve_beam_trap_frame(Process*);
+ERTS_GLB_INLINE int hipe_is_ra_mode_switch(const void* ra);
extern Uint hipe_beam_pc_return[];
extern Uint hipe_beam_pc_throw[];
@@ -112,6 +113,11 @@ ERTS_GLB_INLINE void hipe_unreserve_beam_trap_frame(Process *p)
p->stop += 2;
}
+ERTS_GLB_INLINE int hipe_is_ra_mode_switch(const void* ra)
+{
+ return ra == nbif_return;
+}
+
#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
#endif /* ASM */
diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c
index 9c03b3811c..860791074d 100644
--- a/erts/emulator/hipe/hipe_native_bif.c
+++ b/erts/emulator/hipe/hipe_native_bif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2017. 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.
@@ -226,11 +226,6 @@ void hipe_handle_exception(Process *c_p)
ASSERT(c_p->freason != TRAP); /* Should have been handled earlier. */
- if (c_p->mbuf) {
- erts_printf("%s line %u: p==%p, p->mbuf==%p\n", __FUNCTION__, __LINE__, c_p, c_p->mbuf);
- /* erts_garbage_collect(c_p, 0, NULL, 0); */
- }
-
/*
* Check if we have an arglist for the top level call. If so, this
* is encoded in Value, so we have to dig out the real Value as well
@@ -259,11 +254,6 @@ void hipe_handle_exception(Process *c_p)
/* Synthesized to avoid having to generate code for it. */
c_p->def_arg_reg[0] = exception_tag[GET_EXC_CLASS(c_p->freason)];
- if (c_p->mbuf) {
- /* erts_printf("%s line %u: p==%p, p->mbuf==%p, p->lastbif==%p\n", __FUNCTION__, __LINE__, c_p, c_p->mbuf, c_p->hipe.lastbif); */
- erts_garbage_collect(c_p, 0, NULL, 0);
- }
-
hipe_find_handler(c_p);
}
@@ -331,7 +321,7 @@ char *hipe_bs_allocate(int len)
Binary *bptr;
bptr = erts_bin_nrml_alloc(len);
- erts_smp_atomic_init_nob(&bptr->refc, 1);
+ erts_refc_init(&bptr->refc, 1);
return bptr->orig_bytes;
}
diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4
index 57b4208bee..b540562185 100644
--- a/erts/emulator/hipe/hipe_ppc_bifs.m4
+++ b/erts/emulator/hipe/hipe_ppc_bifs.m4
@@ -212,8 +212,9 @@ ASYM($1):
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
- * Generate native interface for a BIF with 0-2 parameters and
+ * Generate native interface for a BIF with 0-3 parameters and
* standard failure mode.
* The BIF may do a GC.
*/
@@ -300,6 +301,39 @@ ASYM($1):
TYPE_FUNCTION(ASYM($1))
#endif')
+define(gc_bif_interface_3,
+`
+#ifndef HAVE_$1
+#`define' HAVE_$1
+ GLOBAL(ASYM($1))
+ASYM($1):
+ /* Set up C argument registers. */
+ mr r3, P
+ NBIF_ARG(r4,3,0)
+ NBIF_ARG(r5,3,1)
+ NBIF_ARG(r6,3,2)
+
+ /* Save caller-save registers and call the C function. */
+ SAVE_CONTEXT_GC
+ STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
+ STORE r5, P_ARG1(r3)
+ STORE r6, P_ARG2(r3)
+ addi r4, r3, P_ARG0
+ CALL_BIF($2)
+ TEST_GOT_MBUF
+
+ /* Restore registers. Check for exception. */
+ CMPI r3, THE_NON_VALUE
+ RESTORE_CONTEXT_GC
+ beq- 1f
+ NBIF_RET(3)
+1: /* workaround for bc:s small offset operand */
+ b CSYM(nbif_3_simple_exception)
+ HANDLE_GOT_MBUF(3)
+ SET_SIZE(ASYM($1))
+ TYPE_FUNCTION(ASYM($1))
+#endif')
+
/*
* gc_nofail_primop_interface_1(nbif_name, cbif_name)
*
diff --git a/erts/emulator/hipe/hipe_process.h b/erts/emulator/hipe/hipe_process.h
index 21c4239753..a8d5972280 100644
--- a/erts/emulator/hipe/hipe_process.h
+++ b/erts/emulator/hipe/hipe_process.h
@@ -52,6 +52,9 @@ struct hipe_process_state {
#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
void (*bif_callee)(void); /* When calling BIF's via debug wrapper */
#endif
+#ifdef DEBUG
+ UWord gc_is_unsafe; /* Nonzero when GC-required state is on stack */
+#endif
};
extern void hipe_arch_print_pcb(struct hipe_process_state *p);
@@ -68,6 +71,9 @@ static __inline__ void hipe_init_process(struct hipe_process_state *p)
p->nra = NULL;
#endif
p->narity = 0;
+#ifdef DEBUG
+ p->gc_is_unsafe = 0;
+#endif
}
static __inline__ void hipe_delete_process(struct hipe_process_state *p)
diff --git a/erts/emulator/hipe/hipe_risc_gc.h b/erts/emulator/hipe/hipe_risc_gc.h
index 315f8e7f9f..09568c140e 100644
--- a/erts/emulator/hipe/hipe_risc_gc.h
+++ b/erts/emulator/hipe/hipe_risc_gc.h
@@ -51,6 +51,19 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state)
return sdesc;
}
+static inline const struct sdesc*
+nstack_walk_init_sdesc_ignore_trap(const Process *p,
+ struct nstack_walk_state *state)
+{
+ unsigned long ra = (unsigned long)p->hipe.nra;
+ const struct sdesc *sdesc;
+ if (ra == (unsigned long)&nbif_stack_trap_ra)
+ ra = (unsigned long)p->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ state->sdesc0 = sdesc;
+ return sdesc;
+}
+
static inline void nstack_walk_update_trap(Process *p, const struct sdesc *sdesc0)
{
Eterm *nsp = p->hipe.nsp;
diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4
index 2e886ec1d1..1389beaa61 100644
--- a/erts/emulator/hipe/hipe_sparc_bifs.m4
+++ b/erts/emulator/hipe/hipe_sparc_bifs.m4
@@ -210,8 +210,9 @@ $1:
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
- * Generate native interface for a BIF with 0-2 parameters and
+ * Generate native interface for a BIF with 0-3 parameters and
* standard failure mode.
* The BIF may do a GC.
*/
@@ -295,6 +296,37 @@ $1:
.type $1, #function
#endif')
+define(gc_bif_interface_3,
+`
+#ifndef HAVE_$1
+#`define' HAVE_$1
+ .global $1
+$1:
+ /* Set up C argument registers. */
+ mov P, %o0
+ NBIF_ARG(%o1,3,0)
+ NBIF_ARG(%o2,3,1)
+ NBIF_ARG(%o3,3,2)
+
+ /* Save caller-save registers and call the C function. */
+ SAVE_CONTEXT_GC
+ st %o1, [%o0+P_ARG0] ! Store BIF__ARGS in def_arg_reg
+ st %o2, [%o0+P_ARG1]
+ st %o3, [%o0+P_ARG2]
+ add %o0, P_ARG0, %o1
+ CALL_BIF($2)
+ nop
+ TEST_GOT_MBUF
+
+ /* Restore registers. Check for exception. */
+ TEST_GOT_EXN(3)
+ RESTORE_CONTEXT_GC
+ NBIF_RET(3)
+ HANDLE_GOT_MBUF(3)
+ .size $1, .-$1
+ .type $1, #function
+#endif')
+
/*
* gc_nofail_primop_interface_1(nbif_name, cbif_name)
*
diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h
index 4ea7d5c031..afa0ed4256 100644
--- a/erts/emulator/hipe/hipe_stack.h
+++ b/erts/emulator/hipe/hipe_stack.h
@@ -131,5 +131,8 @@ static __inline__ void hipe_check_nstack(Process *p, unsigned nwords)
*/
extern Eterm *fullsweep_nstack(Process *p, Eterm *n_htop);
extern void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop);
+extern Eterm *sweep_literals_nstack(Process *p, Eterm *n_htop, char *area,
+ Uint area_size);
+extern int nstack_any_heap_ref_ptrs(Process *, char* mod_start, Uint mod_size);
#endif /* HIPE_STACK_H */
diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4
index b8ac5046d5..c0c149733c 100644
--- a/erts/emulator/hipe/hipe_x86_bifs.m4
+++ b/erts/emulator/hipe/hipe_x86_bifs.m4
@@ -671,10 +671,11 @@ noproc_primop_interface_0(nbif_handle_fp_exception, erts_restore_fpu)
define(gc_bif_interface_0,`nofail_primop_interface_0($1, $2)')
/*
- * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2).
+ * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2,3).
*/
define(gc_bif_interface_1,`standard_bif_interface_1($1, $2)')
define(gc_bif_interface_2,`standard_bif_interface_2($1, $2)')
+define(gc_bif_interface_3,`standard_bif_interface_3($1, $2)')
/*
* Implement gc_nofail_primop_interface_1 as nofail_primop_interface_1.
diff --git a/erts/emulator/hipe/hipe_x86_gc.h b/erts/emulator/hipe/hipe_x86_gc.h
index c22b28c2d5..00fe03d8f9 100644
--- a/erts/emulator/hipe/hipe_x86_gc.h
+++ b/erts/emulator/hipe/hipe_x86_gc.h
@@ -81,6 +81,23 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state)
#endif
}
+static inline const struct sdesc*
+nstack_walk_init_sdesc_ignore_trap(const Process *p,
+ struct nstack_walk_state *state)
+{
+#ifdef SKIP_YOUNGEST_FRAME
+ unsigned long ra = p->hipe.nsp[0];
+ const struct sdesc *sdesc;
+ if (ra == (unsigned long)nbif_stack_trap_ra)
+ ra = (unsigned long)p->hipe.ngra;
+ sdesc = hipe_find_sdesc(ra);
+ state->sdesc0 = sdesc;
+ return sdesc;
+#else
+ return nstack_walk_init_sdesc(p, state);
+#endif
+}
+
static inline void nstack_walk_update_trap(Process *p, const struct sdesc *sdesc0)
{
#ifdef SKIP_YOUNGEST_FRAME
diff --git a/erts/emulator/internal_doc/Tracing.md b/erts/emulator/internal_doc/Tracing.md
index 30bc5327a7..728f315263 100644
--- a/erts/emulator/internal_doc/Tracing.md
+++ b/erts/emulator/internal_doc/Tracing.md
@@ -57,7 +57,7 @@ generations of breakpoints are kept and indentified by index of 0 and
1. The global atomic variables `erts_active_bp_index` will determine
which generation of breakpoints running code will use.
-### Atomicy Without Atomic Operations
+### Atomicity Without Atomic Operations
Not using the code loading generations (or any other code duplication)
means that `trace_pattern` must at some point write to the active beam
diff --git a/erts/emulator/nifs/common/erl_tracer_nif.c b/erts/emulator/nifs/common/erl_tracer_nif.c
index c0cc48ff42..0bde60d057 100644
--- a/erts/emulator/nifs/common/erl_tracer_nif.c
+++ b/erts/emulator/nifs/common/erl_tracer_nif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson 2015. All Rights Reserved.
+ * Copyright Ericsson 2015-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.
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 44a77f3ea5..8f2f1f9521 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1617,6 +1617,11 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
erts_do_break_handling();
#endif
+#ifdef ERTS_SIGNAL_SIGTERM
+ if (ERTS_SIGNAL_SIGTERM)
+ erts_handle_signal_sigterm();
+#endif
+
/* Figure out timeout value */
timeout_time = (do_wait
? erts_check_next_timeout_time(esdp)
@@ -1654,6 +1659,11 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
erts_do_break_handling();
#endif
+#ifdef ERTS_SIGNAL_SIGTERM
+ if (ERTS_SIGNAL_SIGTERM)
+ erts_handle_signal_sigterm();
+#endif
+
if (poll_ret != 0) {
erts_smp_atomic_set_nob(&pollset.in_poll_wait, 0);
forget_removed(&pollset);
diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c
index 7bbb406f29..a5714f8325 100644
--- a/erts/emulator/sys/common/erl_mmap.c
+++ b/erts/emulator/sys/common/erl_mmap.c
@@ -2390,7 +2390,7 @@ add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
}
Eterm erts_mmap_info(ErtsMemMapper* mm,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Eterm** hpp, Uint* szp,
struct erts_mmap_info_struct* emis)
@@ -2431,7 +2431,7 @@ Eterm erts_mmap_info(ErtsMemMapper* mm,
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
if (mm->supercarrier) {
const char* prefix = "supercarrier ";
@@ -2485,7 +2485,7 @@ Eterm erts_mmap_info(ErtsMemMapper* mm,
Eterm erts_mmap_info_options(ErtsMemMapper* mm,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -2496,7 +2496,7 @@ Eterm erts_mmap_info_options(ErtsMemMapper* mm,
Eterm res = THE_NON_VALUE;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to, arg, "%sscs: %bpu\n", prefix, scs);
if (mm->supercarrier) {
diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h
index fa51b663fa..623b5188ac 100644
--- a/erts/emulator/sys/common/erl_mmap.h
+++ b/erts/emulator/sys/common/erl_mmap.h
@@ -22,6 +22,7 @@
#define ERL_MMAP_H__
#include "sys.h"
+#include "erl_printf.h"
#define ERTS_MMAP_SUPERALIGNED_BITS (18)
/* Affects hard limits for sbct and lmbcs documented in erts_alloc.xml */
@@ -146,10 +147,10 @@ struct erts_mmap_info_struct
UWord segs[6];
UWord os_used;
};
-Eterm erts_mmap_info(ErtsMemMapper*, int *print_to_p, void *print_to_arg,
+Eterm erts_mmap_info(ErtsMemMapper*, fmtfn_t *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,
+ char *prefix, fmtfn_t *print_to_p, void *print_to_arg,
Uint **hpp, Uint *szp);
diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c
index f3306a888c..968f71211c 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -87,6 +87,7 @@ static const int debruijn[32] = {
#define CACHE_AREAS (32 - MSEG_ALIGN_BITS)
+/* FIXME: segment sizes > 2 GB result in bogus negative indices */
#define SIZE_TO_CACHE_AREA_IDX(S) (LOG2((S)) - MSEG_ALIGN_BITS)
#define MAX_CACHE_SIZE (30)
@@ -396,6 +397,9 @@ static ERTS_INLINE int cache_bless_segment(ErtsMsegAllctr_t *ma, void *seg, UWor
if (MSEG_FLG_IS_2POW(flags)) {
int ix = SIZE_TO_CACHE_AREA_IDX(size);
+ if (ix < 0)
+ return 0;
+
ASSERT(ix < CACHE_AREAS);
ASSERT((1 << (ix + MSEG_ALIGN_BITS)) == size);
@@ -471,6 +475,9 @@ static ERTS_INLINE void *cache_get_segment(ErtsMsegAllctr_t *ma, UWord *size_p,
ASSERT(IS_2POW(size));
+ if (ix < 0)
+ return NULL;
+
for( i = ix; i < CACHE_AREAS; i++) {
if (erts_circleq_is_empty(&(ma->cache_powered_node[i])))
@@ -991,7 +998,7 @@ add_4tup(Uint **hpp, Uint *szp, Eterm *lp,
static Eterm
info_options(ErtsMsegAllctr_t *ma,
char *prefix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
Uint **hpp,
Uint *szp)
@@ -999,7 +1006,7 @@ info_options(ErtsMsegAllctr_t *ma,
Eterm res = NIL;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
erts_print(to, arg, "%samcbf: %beu\n", prefix, ma->abs_max_cache_bad_fit);
erts_print(to, arg, "%srmcbf: %beu\n", prefix, ma->rel_max_cache_bad_fit);
@@ -1027,7 +1034,7 @@ info_options(ErtsMsegAllctr_t *ma,
}
static Eterm
-info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp)
+info_calls(ErtsMsegAllctr_t *ma, fmtfn_t *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
@@ -1040,7 +1047,7 @@ info_calls(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp
erts_print(TO, TOA, "mseg_%s calls: %b32u%09b32u\n", #CC, \
ma->calls.CC.giga_no, ma->calls.CC.no)
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
PRINT_CC(to, arg, alloc);
@@ -1106,7 +1113,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,
+info_status(ErtsMsegAllctr_t *ma, fmtfn_t *print_to_p, void *print_to_arg,
int begin_new_max_period, int only_sz, Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
@@ -1117,7 +1124,7 @@ info_status(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg,
ma->segments.max_ever.sz = ma->segments.max.sz;
if (print_to_p) {
- int to = *print_to_p;
+ fmtfn_t to = *print_to_p;
void *arg = print_to_arg;
if (!only_sz) {
@@ -1165,7 +1172,7 @@ info_status(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg,
return res;
}
-static Eterm info_memkind(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg,
+static Eterm info_memkind(ErtsMsegAllctr_t *ma, fmtfn_t *print_to_p, void *print_to_arg,
int begin_max_per, int only_sz, Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
@@ -1196,7 +1203,7 @@ static Eterm info_memkind(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_
}
static Eterm
-info_version(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp)
+info_version(ErtsMsegAllctr_t *ma, fmtfn_t *print_to_p, void *print_to_arg, Uint **hpp, Uint *szp)
{
Eterm res = THE_NON_VALUE;
@@ -1218,7 +1225,7 @@ info_version(ErtsMsegAllctr_t *ma, int *print_to_p, void *print_to_arg, Uint **h
Eterm
erts_mseg_info_options(int ix,
- int *print_to_p, void *print_to_arg,
+ fmtfn_t *print_to_p, void *print_to_arg,
Uint **hpp, Uint *szp)
{
ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_IX(ix);
@@ -1231,7 +1238,7 @@ erts_mseg_info_options(int ix,
Eterm
erts_mseg_info(int ix,
- int *print_to_p,
+ fmtfn_t *print_to_p,
void *print_to_arg,
int begin_max_per,
int only_sz,
diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h
index a43b409e94..bba0dec499 100644
--- a/erts/emulator/sys/common/erl_mseg.h
+++ b/erts/emulator/sys/common/erl_mseg.h
@@ -98,8 +98,8 @@ Uint erts_mseg_unit_size(void);
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, int, Uint **, Uint *);
+Eterm erts_mseg_info_options(int, fmtfn_t*, void*, Uint **, Uint *);
+Eterm erts_mseg_info(int, fmtfn_t *, void*, int, int, Uint **, Uint *);
#endif /* #if HAVE_ERTS_MSEG */
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index e394d84f73..b8a28bcc18 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -75,6 +75,7 @@
#include "erl_driver.h"
#include "erl_alloc.h"
#include "erl_msacc.h"
+#include "erl_misc_utils.h"
#if !defined(ERTS_POLL_USE_EPOLL) \
&& !defined(ERTS_POLL_USE_DEVPOLL) \
@@ -2132,16 +2133,19 @@ get_timeout(ErtsPollSet ps,
if (timeout > (ErtsMonotonicTime) INT_MAX)
timeout = (ErtsMonotonicTime) INT_MAX;
save_timeout_time += ERTS_MSEC_TO_MONOTONIC(timeout);
+ timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000);
break;
case 1000000:
/* Round up to nearest even micro second */
timeout = ERTS_MONOTONIC_TO_USEC(diff_time - 1) + 1;
save_timeout_time += ERTS_USEC_TO_MONOTONIC(timeout);
+ timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000);
break;
case 1000000000:
/* Round up to nearest even nano second */
timeout = ERTS_MONOTONIC_TO_NSEC(diff_time - 1) + 1;
save_timeout_time += ERTS_NSEC_TO_MONOTONIC(timeout);
+ timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000*1000);
break;
default:
ERTS_INTERNAL_ERROR("Invalid resolution");
@@ -2452,7 +2456,15 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps,
}
#endif
- res = check_fd_events(ps, to, no_fds);
+ while (1) {
+ res = check_fd_events(ps, to, no_fds);
+ if (res != 0)
+ break;
+ if (to == ERTS_POLL_NO_TIMEOUT)
+ break;
+ if (erts_get_monotonic_time(NULL) >= timeout_time)
+ break;
+ }
woke_up(ps);
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index 6beb316350..69fc6c2879 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -54,6 +54,7 @@
#include <stdlib.h>
#include <stdio.h>
+#include <stdarg.h>
#include <sys/wait.h>
#define WANT_NONBLOCKING
@@ -74,15 +75,22 @@
//#define HARD_DEBUG
#ifdef HARD_DEBUG
-#define DEBUG_PRINT(fmt, ...) fprintf(stderr, fmt "\r\n", ##__VA_ARGS__)
+#define DEBUG_PRINT(fmt, ...) fprintf(stderr, "%d:" fmt "\r\n", getpid(), ##__VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...)
#endif
-#define ABORT(fmt, ...) do { \
- fprintf(stderr, "erl_child_setup: " fmt "\r\n", ##__VA_ARGS__); \
- abort(); \
- } while(0)
+static char abort_reason[200]; /* for core dump inspection */
+
+static void ABORT(const char* fmt, ...)
+{
+ va_list arglist;
+ va_start(arglist, fmt);
+ vsprintf(abort_reason, fmt, arglist);
+ fprintf(stderr, "erl_child_setup: %s\r\n", abort_reason);
+ va_end(arglist);
+ abort();
+}
#ifdef DEBUG
void
@@ -123,12 +131,13 @@ static int sigchld_pipe[2];
static int
start_new_child(int pipes[])
{
+ int errln = -1;
int size, res, i, pos = 0;
char *buff, *o_buff;
- char *cmd, *wd, **new_environ, **args = NULL;
+ char *cmd, *cwd, *wd, **new_environ, **args = NULL;
- Sint cnt, flags;
+ Sint32 cnt, flags;
/* only child executes here */
@@ -137,6 +146,7 @@ start_new_child(int pipes[])
} while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK));
if (res <= 0) {
+ errln = __LINE__;
goto child_error;
}
@@ -148,10 +158,12 @@ start_new_child(int pipes[])
if ((res = read(pipes[0], buff + pos, size - pos)) < 0) {
if (errno == ERRNO_BLOCK || errno == EINTR)
continue;
+ errln = __LINE__;
goto child_error;
}
if (res == 0) {
errno = EPIPE;
+ errln = __LINE__;
goto child_error;
}
pos += res;
@@ -160,12 +172,16 @@ start_new_child(int pipes[])
o_buff = buff;
flags = get_int32(buff);
- buff += sizeof(Sint32);
+ buff += sizeof(flags);
DEBUG_PRINT("flags = %d", flags);
cmd = buff;
buff += strlen(buff) + 1;
+
+ cwd = buff;
+ buff += strlen(buff) + 1;
+
if (*buff == '\0') {
wd = NULL;
} else {
@@ -177,10 +193,10 @@ start_new_child(int pipes[])
DEBUG_PRINT("wd = %s", wd);
cnt = get_int32(buff);
- buff += sizeof(Sint32);
+ buff += sizeof(cnt);
new_environ = malloc(sizeof(char*)*(cnt + 1));
- DEBUG_PRINT("env_len = %ld", cnt);
+ DEBUG_PRINT("env_len = %d", cnt);
for (i = 0; i < cnt; i++, buff++) {
new_environ[i] = buff;
while(*buff != '\0') buff++;
@@ -190,7 +206,7 @@ start_new_child(int pipes[])
if (o_buff + size != buff) {
/* This is a spawn executable call */
cnt = get_int32(buff);
- buff += sizeof(Sint32);
+ buff += sizeof(cnt);
args = malloc(sizeof(char*)*(cnt + 1));
for (i = 0; i < cnt; i++, buff++) {
args[i] = buff;
@@ -201,7 +217,12 @@ start_new_child(int pipes[])
if (o_buff + size != buff) {
errno = EINVAL;
- goto child_error;
+ errln = __LINE__;
+ fprintf(stderr,"erl_child_setup: failed with protocol "
+ "error %d on line %d", errno, errln);
+ /* we abort here as it is most likely a symptom of an
+ emulator/erl_child_setup bug */
+ abort();
}
DEBUG_PRINT("read ack");
@@ -213,12 +234,32 @@ start_new_child(int pipes[])
ASSERT(res == sizeof(proto));
}
} while(res < 0 && (errno == EINTR || errno == ERRNO_BLOCK));
+
if (res < 1) {
errno = EPIPE;
+ errln = __LINE__;
goto child_error;
}
- DEBUG_PRINT("Do that forking business: '%s'\n",cmd);
+ DEBUG_PRINT("Set cwd to: '%s'",cwd);
+
+ if (chdir(cwd) < 0) {
+ /* This is not good, it probably means that the cwd of
+ beam is invalid. We ignore it and try anyways as
+ the child might now need a cwd or the chdir below
+ could take us to a valid directory.
+ */
+ }
+
+ DEBUG_PRINT("Set wd to: '%s'",wd);
+
+ if (wd && chdir(wd) < 0) {
+ int err = errno;
+ fprintf(stderr,"spawn: Could not cd to %s\r\n", wd);
+ _exit(err);
+ }
+
+ DEBUG_PRINT("Do that forking business: '%s'",cmd);
/* When the dup2'ing below is done, only
fd's 0, 1, 2 and maybe 3, 4 should survive the
@@ -228,25 +269,34 @@ start_new_child(int pipes[])
if (flags & FORKER_FLAG_USE_STDIO) {
/* stdin for process */
if (flags & FORKER_FLAG_DO_WRITE &&
- dup2(pipes[0], 0) < 0)
+ dup2(pipes[0], 0) < 0) {
+ errln = __LINE__;
goto child_error;
+ }
/* stdout for process */
if (flags & FORKER_FLAG_DO_READ &&
- dup2(pipes[1], 1) < 0)
+ dup2(pipes[1], 1) < 0) {
+ errln = __LINE__;
goto child_error;
+ }
}
else { /* XXX will fail if pipes[0] == 4 (unlikely..) */
- if (flags & FORKER_FLAG_DO_READ && dup2(pipes[1], 4) < 0)
+ if (flags & FORKER_FLAG_DO_READ && dup2(pipes[1], 4) < 0) {
+ errln = __LINE__;
goto child_error;
- if (flags & FORKER_FLAG_DO_WRITE && dup2(pipes[0], 3) < 0)
+ }
+ if (flags & FORKER_FLAG_DO_WRITE && dup2(pipes[0], 3) < 0) {
+ errln = __LINE__;
goto child_error;
+ }
}
- if (dup2(pipes[2], 2) < 0)
- goto child_error;
-
- if (wd && chdir(wd) < 0)
+ /* we do the dup2 of stderr last so that errors
+ in child_error will be printed to stderr */
+ if (dup2(pipes[2], 2) < 0) {
+ errln = __LINE__;
goto child_error;
+ }
#if defined(USE_SETPGRP_NOARGS) /* SysV */
(void) setpgrp();
@@ -268,9 +318,14 @@ start_new_child(int pipes[])
} else {
execle(SHELL, "sh", "-c", cmd, (char *) NULL, new_environ);
}
+
+ DEBUG_PRINT("exec error: %d",errno);
+ _exit(errno);
+
child_error:
- DEBUG_PRINT("exec error: %d\r\n",errno);
- _exit(128 + errno);
+ fprintf(stderr,"erl_child_setup: failed with error %d on line %d\r\n",
+ errno, errln);
+ _exit(errno);
}
@@ -293,7 +348,7 @@ child_error:
* for posterity. */
static void handle_sigchld(int sig) {
- int buff[2], res;
+ int buff[2], res, __preverrno = errno;
sys_sigblock(SIGCHLD);
@@ -307,6 +362,16 @@ static void handle_sigchld(int sig) {
}
sys_sigrelease(SIGCHLD);
+
+ /* We save and restore the original errno as otherwise
+ the thread we are running in may end up with an
+ unexpected errno. An example of when this happened
+ was when the select in main had gotten an EINTR but
+ before the errno was checked the signal handler
+ was called and set errno to ECHILD from waitpid
+ which caused erl_child_setup to abort as it does
+ not expect ECHILD to be set after select */
+ errno = __preverrno;
}
#if defined(__ANDROID__)
@@ -368,7 +433,7 @@ main(int argc, char *argv[])
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, 0) == -1) {
- perror(0);
+ perror(NULL);
exit(1);
}
@@ -461,7 +526,7 @@ main(int argc, char *argv[])
proto.action = ErtsSysForkerProtoAction_SigChld;
proto.u.sigchld.error_number = ibuff[1];
- DEBUG_PRINT("send %s to %d", buff, uds_fd);
+ DEBUG_PRINT("send sigchld to %d (errno = %d)", uds_fd, ibuff[1]);
if (write(uds_fd, &proto, sizeof(proto)) < 0) {
if (errno == EINTR)
continue;
diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h
index 3a0d23cd36..054a4678c8 100644
--- a/erts/emulator/sys/unix/erl_unix_sys.h
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -282,7 +282,7 @@ ERTS_GLB_INLINE ErtsSysPerfCounter erts_sys_perf_counter(void);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE ErtsSysPerfCounter
+ERTS_GLB_FORCE_INLINE ErtsSysPerfCounter
erts_sys_perf_counter()
{
return (*erts_sys_time_data__.r.o.perf_counter)();
@@ -322,6 +322,7 @@ extern SIGFUNC sys_signal(int, SIGFUNC);
extern void sys_sigrelease(int);
extern void sys_sigblock(int);
extern void sys_init_suspend_handler(void);
+extern void erts_sys_unix_later_init(void);
/*
* Handling of floating point exceptions.
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 6fb86f6dda..7f738751ff 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2016. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2017. 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.
@@ -90,8 +90,6 @@ extern void erl_sys_args(int*, char**);
extern void erts_sys_init_float(void);
-extern void erl_crash_dump(char* file, int line, char* fmt, ...);
-
#ifdef DEBUG
static int debug_log = 0;
@@ -121,9 +119,8 @@ erts_smp_atomic_t sys_misc_mem_sz;
static void smp_sig_notify(char c);
static int sig_notify_fds[2] = {-1, -1};
-#if !defined(ETHR_UNUSABLE_SIGUSRX) && defined(ERTS_THR_HAVE_SIG_FUNCS)
+#ifdef ERTS_SYS_SUSPEND_SIGNAL
static int sig_suspend_fds[2] = {-1, -1};
-#define ERTS_SYS_SUSPEND_SIGNAL SIGUSR2
#endif
#endif
@@ -151,11 +148,18 @@ volatile int erts_break_requested = 0;
#define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1)
#define ERTS_UNSET_BREAK_REQUESTED (erts_break_requested = 0)
#endif
+
+#ifndef ERTS_SMP
+volatile Uint erts_signal_sigterm = 0;
+#define ERTS_SET_SIGNAL_SIGTERM (erts_signal_sigterm = 1)
+#define ERTS_CLEAR_SIGNAL_SIGTERM (erts_signal_sigterm = 0)
+#endif
+
/* set early so the break handler has access to initial mode */
static struct termios initial_tty_mode;
static int replace_intr = 0;
/* assume yes initially, ttsl_init will clear it */
-int using_oldshell = 1;
+int using_oldshell = 1;
#ifdef ERTS_ENABLE_KERNEL_POLL
@@ -409,11 +413,16 @@ erts_sys_pre_init(void)
#ifdef ERTS_THR_HAVE_SIG_FUNCS
sigemptyset(&thr_create_sigmask);
sigaddset(&thr_create_sigmask, SIGINT); /* block interrupt */
+ sigaddset(&thr_create_sigmask, SIGTERM); /* block terminate signal */
sigaddset(&thr_create_sigmask, SIGUSR1); /* block user defined signal */
#endif
erts_thr_init(&eid);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_init();
+#endif
+
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_init();
#endif
@@ -653,6 +662,40 @@ static RETSIGTYPE request_break(int signum)
#endif
}
+static void stop_requested(void) {
+ Process* p = NULL;
+ Eterm msg, *hp;
+ ErtsProcLocks locks = 0;
+ ErlOffHeap *ohp;
+ Eterm id = erts_whereis_name_to_id(NULL, am_init);
+
+ if ((p = (erts_pid2proc_opt(NULL, 0, id, 0, ERTS_P2P_FLG_INC_REFC))) != NULL) {
+ ErtsMessage *msgp = erts_alloc_message_heap(p, &locks, 3, &hp, &ohp);
+
+ /* init ! {stop,stop} */
+ msg = TUPLE2(hp, am_stop, am_stop);
+ erts_queue_message(p, locks, msgp, msg, am_system);
+
+ if (locks)
+ erts_smp_proc_unlock(p, locks);
+ erts_proc_dec_refc(p);
+ }
+}
+
+#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL))
+static RETSIGTYPE request_stop(void)
+#else
+static RETSIGTYPE request_stop(int signum)
+#endif
+{
+#ifdef ERTS_SMP
+ smp_sig_notify('S');
+#else
+ ERTS_SET_SIGNAL_SIGTERM;
+ ERTS_CHK_IO_AS_INTR();
+#endif
+}
+
static ERTS_INLINE void
sigusr1_exit(void)
{
@@ -672,7 +715,7 @@ sigusr1_exit(void)
}
prepare_crash_dump(secs);
- erts_exit(ERTS_ERROR_EXIT, "Received SIGUSR1\n");
+ erts_exit(ERTS_DUMP_EXIT, "Received SIGUSR1\n");
}
#ifdef ETHR_UNUSABLE_SIGUSRX
@@ -715,11 +758,13 @@ static RETSIGTYPE suspend_signal(void)
static RETSIGTYPE suspend_signal(int signum)
#endif
{
- int res;
- int buf[1];
- do {
- res = read(sig_suspend_fds[0], buf, sizeof(int));
- } while (res < 0 && errno == EINTR);
+ int res, buf[1], tmp_errno = errno;
+ do {
+ res = read(sig_suspend_fds[0], buf, sizeof(int));
+ } while (res < 0 && errno == EINTR);
+
+ /* restore previous errno in case read changed it */
+ errno = tmp_errno;
}
#endif /* #ifdef ERTS_SYS_SUSPEND_SIGNAL */
@@ -746,9 +791,14 @@ static RETSIGTYPE do_quit(int signum)
/* Disable break */
void erts_set_ignore_break(void) {
- sys_signal(SIGINT, SIG_IGN);
- sys_signal(SIGQUIT, SIG_IGN);
- sys_signal(SIGTSTP, SIG_IGN);
+ /*
+ * Ignore signals that can be sent to the VM by
+ * typing certain key combinations at the
+ * controlling terminal...
+ */
+ sys_signal(SIGINT, SIG_IGN); /* Ctrl-C */
+ sys_signal(SIGQUIT, SIG_IGN); /* Ctrl-\ */
+ sys_signal(SIGTSTP, SIG_IGN); /* Ctrl-Z */
}
/* Don't use ctrl-c for break handler but let it be
@@ -785,6 +835,19 @@ void sys_init_suspend_handler(void)
#endif
}
+void
+erts_sys_unix_later_init(void)
+{
+ char env[5];
+ size_t envsz = sizeof(env);
+
+ if (erts_sys_getenv_raw("ERL_ZZ_SIGTERM_KILL", env, &envsz) == 0)
+ if (envsz == 4 && sys_strncmp("true", env, 4) == 0)
+ return;
+
+ sys_signal(SIGTERM, request_stop);
+}
+
int sys_max_files(void)
{
return(max_files);
@@ -918,6 +981,13 @@ void erts_do_break_handling(void)
erts_smp_thr_progress_unblock();
}
+#ifdef ERTS_SIGNAL_SIGTERM
+void erts_handle_signal_sigterm(void) {
+ ERTS_CLEAR_SIGNAL_SIGTERM;
+ stop_requested();
+}
+#endif
+
/* Fills in the systems representation of the jam/beam process identifier.
** The Pid is put in STRING representation in the supplied buffer,
** no interpretatione of this should be done by the rest of the
@@ -1285,6 +1355,9 @@ signal_dispatcher_thread_func(void *unused)
switch (buf[i]) {
case 0: /* Emulator initialized */
break;
+ case 'S': /* SIGTERM */
+ stop_requested();
+ break;
case 'I': /* SIGINT */
break_requested();
break;
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
index 812112fb91..93cf64719a 100644
--- a/erts/emulator/sys/unix/sys_drivers.c
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -204,6 +204,7 @@ erl_sys_late_init(void)
#ifdef ERTS_SMP
erts_mtx_unlock(port->lock);
#endif
+ erts_sys_unix_later_init(); /* Need to be called after forker has been started */
}
/* II. Prototypes */
@@ -554,7 +555,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
ErtsSysDriverData *dd;
char *cmd_line;
char wd_buff[MAXPATHLEN+1];
- char *wd;
+ char *wd, *cwd;
int ifd[2], ofd[2], stderrfd;
if (pipe(ifd) < 0) return ERL_DRV_ERROR_ERRNO;
@@ -631,24 +632,22 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
return ERL_DRV_ERROR_ERRNO;
}
- if (opts->wd == NULL) {
- if ((wd = getcwd(wd_buff, MAXPATHLEN+1)) == NULL) {
- /* on some OSs this call opens a fd in the
- background which means that this can
- return EMFILE */
- int err = errno;
- close_pipes(ifd, ofd);
- erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- errno = err;
- return ERL_DRV_ERROR_ERRNO;
- }
- } else {
- wd = opts->wd;
+ if ((cwd = getcwd(wd_buff, MAXPATHLEN+1)) == NULL) {
+ /* on some OSs this call opens a fd in the
+ background which means that this can
+ return EMFILE */
+ int err = errno;
+ close_pipes(ifd, ofd);
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+ if (new_environ != environ)
+ erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ errno = err;
+ return ERL_DRV_ERROR_ERRNO;
}
+ wd = opts->wd;
+
{
struct iovec *io_vector;
int iov_len = 5;
@@ -660,6 +659,8 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
| (opts->read_write & DO_READ ? FORKER_FLAG_DO_READ : 0)
| (opts->read_write & DO_WRITE ? FORKER_FLAG_DO_WRITE : 0);
+ if (wd) iov_len++;
+
/* count number of elements in environment */
while(new_environ[env_len] != NULL)
env_len++;
@@ -688,6 +689,10 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
return ERL_DRV_ERROR_ERRNO;
}
+ /*
+ * Whitebox test port_SUITE:pipe_limit_env
+ * assumes this command payload format.
+ */
io_vector[i].iov_base = (void*)&buffsz;
io_vector[i++].iov_len = sizeof(buffsz);
@@ -700,10 +705,16 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
io_vector[i++].iov_len = len;
buffsz += len;
- io_vector[i].iov_base = wd;
+ io_vector[i].iov_base = cwd;
io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1;
buffsz += io_vector[i++].iov_len;
+ if (wd) {
+ io_vector[i].iov_base = wd;
+ io_vector[i].iov_len = strlen(io_vector[i].iov_base) + 1;
+ buffsz += io_vector[i++].iov_len;
+ }
+
io_vector[i].iov_base = nullbuff;
io_vector[i++].iov_len = 1;
buffsz += io_vector[i-1].iov_len;
@@ -765,9 +776,14 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
if (res < (buffsz + sizeof(buffsz))) {
/* we only wrote part of the command payload. Enqueue the rest. */
for (i = 0; i < iov_len; i++) {
- driver_enq(port_num, io_vector[i].iov_base, io_vector[i].iov_len);
+ if (res >= io_vector[i].iov_len)
+ res -= io_vector[i].iov_len;
+ else {
+ driver_enq(port_num, io_vector[i].iov_base + res,
+ io_vector[i].iov_len - res);
+ res = 0;
+ }
}
- driver_deq(port_num, res);
driver_select(port_num, ofd[1], ERL_DRV_WRITE|ERL_DRV_USE, 1);
}
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
index 04fbf23109..78005aada9 100644
--- a/erts/emulator/sys/win32/erl_win_sys.h
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -182,6 +182,8 @@ typedef LONGLONG ErtsMonotonicTime;
typedef LONGLONG ErtsSysHrTime;
#endif
+#define ErtsStrToSint64 _strtoi64
+
typedef ErtsMonotonicTime ErtsSystemTime;
typedef ErtsMonotonicTime ErtsSysPerfCounter;
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index cf821b05cb..f3881e0736 100644
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -40,7 +40,6 @@ void erts_sys_init_float(void);
void erl_start(int, char**);
void erts_exit(int n, char*, ...);
void erl_error(char*, va_list);
-void erl_crash_dump(char*, int, char*, ...);
/*
* Microsoft-specific function to map a WIN32 error code to a Posix errno.
@@ -3187,16 +3186,16 @@ erts_sys_pre_init(void)
eid.thread_create_parent_func = thr_create_cleanup;
erts_thr_init(&eid);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_init();
#endif
-
- erts_init_sys_time_sup();
-
-#ifdef USE_THREADS
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_init();
#endif
#endif
+ erts_init_sys_time_sup();
+
erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0);
}
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index b580211eff..b8e907f769 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
+# Copyright Ericsson AB 1997-2017. 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.
@@ -70,6 +70,7 @@ MODULES= \
guard_SUITE \
hash_SUITE \
hibernate_SUITE \
+ hipe_SUITE \
list_bif_SUITE \
lttng_SUITE \
map_SUITE \
@@ -86,6 +87,7 @@ MODULES= \
op_SUITE \
port_SUITE \
port_bif_SUITE \
+ prim_eval_SUITE \
process_SUITE \
pseudoknot_SUITE \
receive_SUITE \
diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl
index 4f20ad3656..b1f7e06bf5 100644
--- a/erts/emulator/test/after_SUITE.erl
+++ b/erts/emulator/test/after_SUITE.erl
@@ -223,7 +223,7 @@ recv_after_32bit(_, _) ->
blaster() ->
receive
{go, TimeoutTime} ->
- Tmo = TimeoutTime - erlang:monotonic_time(milli_seconds),
+ Tmo = TimeoutTime - erlang:monotonic_time(millisecond),
receive after Tmo -> ok end
end.
@@ -234,7 +234,7 @@ spawn_blasters(N) ->
receive_after_blast(Config) when is_list(Config) ->
PMs = spawn_blasters(10000),
- TimeoutTime = erlang:monotonic_time(milli_seconds) + 5000,
+ TimeoutTime = erlang:monotonic_time(millisecond) + 5000,
lists:foreach(fun ({P, _}) -> P ! {go, TimeoutTime} end, PMs),
lists:foreach(fun ({P, M}) ->
receive
diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl
index 84cf4921d3..3a721095e2 100644
--- a/erts/emulator/test/alloc_SUITE.erl
+++ b/erts/emulator/test/alloc_SUITE.erl
@@ -342,7 +342,7 @@ start_node_1(Config, Opts) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index d31399e4af..e795057918 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -31,7 +31,9 @@
t_list_to_existing_atom/1,os_env/1,otp_7526/1,
binary_to_atom/1,binary_to_existing_atom/1,
atom_to_binary/1,min_max/1, erlang_halt/1,
- is_builtin/1]).
+ erl_crash_dump_bytes/1,
+ is_builtin/1, error_stacktrace/1,
+ error_stacktrace_during_call_trace/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -43,7 +45,8 @@ all() ->
t_list_to_existing_atom, os_env, otp_7526,
display,
atom_to_binary, binary_to_atom, binary_to_existing_atom,
- min_max, erlang_halt, is_builtin].
+ erl_crash_dump_bytes, min_max, erlang_halt, is_builtin,
+ error_stacktrace, error_stacktrace_during_call_trace].
%% Uses erlang:display to test that erts_printf does not do deep recursion
display(Config) when is_list(Config) ->
@@ -461,6 +464,9 @@ binary_to_atom(Config) when is_list(Config) ->
?BADARG(binary_to_atom(id(<<255>>), utf8)),
?BADARG(binary_to_atom(id(<<255,0>>), utf8)),
?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0.
+ <<B:1/binary, _/binary>> = id(<<194, 163>>), %Truncated character ERL-474
+ ?BADARG(binary_to_atom(B, utf8)),
+
[?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || C <- lists:seq(256, 16#D7FF)],
[?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || C <- lists:seq(16#E000, 16#FFFD)],
[?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || C <- lists:seq(16#10000, 16#8FFFF)],
@@ -664,7 +670,7 @@ erlang_halt(Config) when is_list(Config) ->
[available_internal_state, true]),
{badrpc,nodedown} = rpc:call(N4, erts_debug, set_internal_state,
[broken_halt, "Validate correct crash dump"]),
- ok = wait_until_stable_size(CrashDump,-1),
+ {ok,_} = wait_until_stable_size(CrashDump,-1),
{ok, Bin} = file:read_file(CrashDump),
case {string:str(binary_to_list(Bin),"\n=end\n"),
string:str(binary_to_list(Bin),"\r\n=end\r\n")} of
@@ -681,11 +687,34 @@ wait_until_stable_size(File,PrevSz) ->
wait_until_stable_size(File,PrevSz-1);
{ok,#file_info{size = PrevSz }} when PrevSz /= -1 ->
io:format("Crashdump file size was: ~p (~s)~n",[PrevSz,File]),
- ok;
+ {ok,PrevSz};
{ok,#file_info{size = NewSz }} ->
wait_until_stable_size(File,NewSz)
end.
+% Test erlang:halt with ERL_CRASH_DUMP_BYTES
+erl_crash_dump_bytes(Config) when is_list(Config) ->
+ Bytes = 1000,
+ CrashDump = do_limited_crash_dump(Config, Bytes),
+ {ok,ActualBytes} = wait_until_stable_size(CrashDump,-1),
+ true = ActualBytes < (Bytes + 100),
+
+ NoDump = do_limited_crash_dump(Config,0),
+ {error,enoent} = wait_until_stable_size(NoDump,-8),
+ ok.
+
+do_limited_crash_dump(Config, Bytes) ->
+ H = hostname(),
+ {ok,N} = slave:start(H, halt_node),
+ BytesStr = integer_to_list(Bytes),
+ CrashDump = filename:join(proplists:get_value(priv_dir,Config),
+ "erl_crash." ++ BytesStr ++ ".dump"),
+ true = rpc:call(N, os, putenv, ["ERL_CRASH_DUMP",CrashDump]),
+ true = rpc:call(N, os, putenv, ["ERL_CRASH_DUMP_BYTES",BytesStr]),
+ {badrpc,nodedown} = rpc:call(N, erlang, halt, ["Testing ERL_CRASH_DUMP_BYTES"]),
+ CrashDump.
+
+
is_builtin(_Config) ->
Exp0 = [{M,F,A} || {M,_} <- code:all_loaded(),
{F,A} <- M:module_info(exports)],
@@ -702,6 +731,172 @@ is_builtin(_Config) ->
ok.
+error_stacktrace(Config) when is_list(Config) ->
+ error_stacktrace_test().
+
+error_stacktrace_during_call_trace(Config) when is_list(Config) ->
+ Tracer = spawn_link(fun () ->
+ receive after infinity -> ok end
+ end),
+ Mprog = [{'_',[],[{exception_trace}]}],
+ erlang:trace_pattern({?MODULE,'_','_'}, Mprog, [local]),
+ 1 = erlang:trace_pattern({erlang,error,2}, Mprog, [local]),
+ 1 = erlang:trace_pattern({erlang,error,1}, Mprog, [local]),
+ erlang:trace(all, true, [call,return_to,timestamp,{tracer, Tracer}]),
+ try
+ error_stacktrace_test()
+ after
+ erlang:trace(all, false, [call,return_to,timestamp,{tracer, Tracer}]),
+ erlang:trace_pattern({erlang,error,2}, false, [local]),
+ erlang:trace_pattern({erlang,error,1}, false, [local]),
+ erlang:trace_pattern({?MODULE,'_','_'}, false, [local]),
+ unlink(Tracer),
+ exit(Tracer, kill),
+ Mon = erlang:monitor(process, Tracer),
+ receive
+ {'DOWN', Mon, process, Tracer, _} -> ok
+ end
+ end,
+ ok.
+
+
+error_stacktrace_test() ->
+ Types = [apply_const_last, apply_const, apply_last,
+ apply, double_apply_const_last, double_apply_const,
+ double_apply_last, double_apply, multi_apply_const_last,
+ multi_apply_const, multi_apply_last, multi_apply,
+ call_const_last, call_last, call_const, call],
+ lists:foreach(fun (Type) ->
+ {Pid, Mon} = spawn_monitor(
+ fun () ->
+ stk([a,b,c,d], Type, error_2)
+ end),
+ receive
+ {'DOWN', Mon, process, Pid, Reason} ->
+ {oops, Stack} = Reason,
+%% io:format("Type: ~p Stack: ~p~n",
+%% [Type, Stack]),
+ [{?MODULE, do_error_2, [Type], _},
+ {?MODULE, stk, 3, _},
+ {?MODULE, stk, 3, _}] = Stack
+ end
+ end,
+ Types),
+ lists:foreach(fun (Type) ->
+ {Pid, Mon} = spawn_monitor(
+ fun () ->
+ stk([a,b,c,d], Type, error_1)
+ end),
+ receive
+ {'DOWN', Mon, process, Pid, Reason} ->
+ {oops, Stack} = Reason,
+%% io:format("Type: ~p Stack: ~p~n",
+%% [Type, Stack]),
+ [{?MODULE, do_error_1, 1, _},
+ {?MODULE, stk, 3, _},
+ {?MODULE, stk, 3, _}] = Stack
+ end
+ end,
+ Types),
+ ok.
+
+stk([], Type, Func) ->
+ tail(Type, Func, jump),
+ ok;
+stk([_|L], Type, Func) ->
+ stk(L, Type, Func),
+ ok.
+
+tail(Type, Func, jump) ->
+ tail(Type, Func, do);
+tail(Type, error_1, do) ->
+ do_error_1(Type);
+tail(Type, error_2, do) ->
+ do_error_2(Type).
+
+do_error_2(apply_const_last) ->
+ erlang:apply(erlang, error, [oops, [apply_const_last]]);
+do_error_2(apply_const) ->
+ erlang:apply(erlang, error, [oops, [apply_const]]),
+ ok;
+do_error_2(apply_last) ->
+ erlang:apply(id(erlang), id(error), id([oops, [apply_last]]));
+do_error_2(apply) ->
+ erlang:apply(id(erlang), id(error), id([oops, [apply]])),
+ ok;
+do_error_2(double_apply_const_last) ->
+ erlang:apply(erlang, apply, [erlang, error, [oops, [double_apply_const_last]]]);
+do_error_2(double_apply_const) ->
+ erlang:apply(erlang, apply, [erlang, error, [oops, [double_apply_const]]]),
+ ok;
+do_error_2(double_apply_last) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops, [double_apply_last]])]);
+do_error_2(double_apply) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops, [double_apply]])]),
+ ok;
+do_error_2(multi_apply_const_last) ->
+ erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops, [multi_apply_const_last]]]]]);
+do_error_2(multi_apply_const) ->
+ erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops, [multi_apply_const]]]]]),
+ ok;
+do_error_2(multi_apply_last) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops, [multi_apply_last]])]]]);
+do_error_2(multi_apply) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops, [multi_apply]])]]]),
+ ok;
+do_error_2(call_const_last) ->
+ erlang:error(oops, [call_const_last]);
+do_error_2(call_last) ->
+ erlang:error(id(oops), id([call_last]));
+do_error_2(call_const) ->
+ erlang:error(oops, [call_const]),
+ ok;
+do_error_2(call) ->
+ erlang:error(id(oops), id([call])).
+
+
+do_error_1(apply_const_last) ->
+ erlang:apply(erlang, error, [oops]);
+do_error_1(apply_const) ->
+ erlang:apply(erlang, error, [oops]),
+ ok;
+do_error_1(apply_last) ->
+ erlang:apply(id(erlang), id(error), id([oops]));
+do_error_1(apply) ->
+ erlang:apply(id(erlang), id(error), id([oops])),
+ ok;
+do_error_1(double_apply_const_last) ->
+ erlang:apply(erlang, apply, [erlang, error, [oops]]);
+do_error_1(double_apply_const) ->
+ erlang:apply(erlang, apply, [erlang, error, [oops]]),
+ ok;
+do_error_1(double_apply_last) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops])]);
+do_error_1(double_apply) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops])]),
+ ok;
+do_error_1(multi_apply_const_last) ->
+ erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops]]]]);
+do_error_1(multi_apply_const) ->
+ erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops]]]]),
+ ok;
+do_error_1(multi_apply_last) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops])]]]);
+do_error_1(multi_apply) ->
+ erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops])]]]),
+ ok;
+do_error_1(call_const_last) ->
+ erlang:error(oops);
+do_error_1(call_last) ->
+ erlang:error(id(oops));
+do_error_1(call_const) ->
+ erlang:error(oops),
+ ok;
+do_error_1(call) ->
+ erlang:error(id(oops)).
+
+
+
%% Helpers
diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl
index 402751393a..c308760211 100644
--- a/erts/emulator/test/big_SUITE.erl
+++ b/erts/emulator/test/big_SUITE.erl
@@ -24,6 +24,7 @@
-export([t_div/1, eq_28/1, eq_32/1, eq_big/1, eq_math/1, big_literals/1,
borders/1, negative/1, big_float_1/1, big_float_2/1,
+ bxor_2pow/1,
shift_limit_1/1, powmod/1, system_limit/1, toobig/1, otp_6692/1]).
%% Internal exports.
@@ -42,6 +43,7 @@ suite() ->
all() ->
[t_div, eq_28, eq_32, eq_big, eq_math, big_literals,
borders, negative, {group, big_float}, shift_limit_1,
+ bxor_2pow,
powmod, system_limit, toobig, otp_6692].
groups() ->
@@ -396,3 +398,54 @@ loop2(X,Y,N,M) ->
end,
loop2(X,Y,N+1,M).
+
+%% ERL-450
+bxor_2pow(_Config) ->
+ IL = lists:seq(8*3, 8*16, 4),
+ JL = lists:seq(0, 64),
+ [bxor_2pow_1((1 bsl I), (1 bsl J))
+ || I <- IL, J <- JL],
+ ok.
+
+bxor_2pow_1(A, B) ->
+ for(-1,1, fun(Ad) ->
+ for(-1,1, fun(Bd) ->
+ bxor_2pow_2(A+Ad, B+Bd),
+ bxor_2pow_2(-A+Ad, B+Bd),
+ bxor_2pow_2(A+Ad, -B+Bd),
+ bxor_2pow_2(-A+Ad, -B+Bd)
+ end)
+ end).
+
+for(From, To, _Fun) when From > To ->
+ ok;
+for(From, To, Fun) ->
+ Fun(From),
+ for(From+1, To, Fun).
+
+bxor_2pow_2(A, B) ->
+ Correct = my_bxor(A, B),
+ case A bxor B of
+ Correct -> ok;
+ Wrong ->
+ io:format("~.16b bxor ~.16b\n", [A,B]),
+ io:format("Expected ~.16b\n", [Correct]),
+ io:format("Got ~.16b\n", [Wrong]),
+ ct:fail({failed, 'bxor'})
+
+ end.
+
+%% Implement bxor without bxor
+my_bxor(A, B) ->
+ my_bxor(A, B, 0, 0).
+
+my_bxor(0, 0, _, Acc) -> Acc;
+my_bxor(-1, -1, _, Acc) -> Acc;
+my_bxor(-1, 0, N, Acc) -> (-1 bsl N) bor Acc; % sign extension
+my_bxor(0, -1, N, Acc) -> (-1 bsl N) bor Acc; % sign extension
+my_bxor(A, B, N, Acc0) ->
+ Acc1 = case (A band 1) =:= (B band 1) of
+ true -> Acc0;
+ false -> Acc0 bor (1 bsl N)
+ end,
+ my_bxor(A bsr 1, B bsr 1, N+1, Acc1).
diff --git a/erts/emulator/test/big_SUITE_data/borders.dat b/erts/emulator/test/big_SUITE_data/borders.dat
index 52e4f35861..c38ff93383 100644
--- a/erts/emulator/test/big_SUITE_data/borders.dat
+++ b/erts/emulator/test/big_SUITE_data/borders.dat
@@ -1114,3 +1114,38 @@
1 = 16#800000000000001 rem (-16#800000000000000).
0 = 16#FFFFFFFFFFFFFFF800000000 rem 16#FFFFFFFFFFFFFFF80.
+% ERL-450 bxor of big negative 2-pow
+-(1 bsl 8) bxor -1 = 16#ff.
+-(1 bsl 16) bxor -1 = 16#ffff.
+-(1 bsl 24) bxor -1 = 16#ffffff.
+-(1 bsl 32) bxor -1 = 16#ffffffff.
+-(1 bsl 40) bxor -1 = 16#ffffffffff.
+-(1 bsl 48) bxor -1 = 16#ffffffffffff.
+-(1 bsl 56) bxor -1 = 16#ffffffffffffff.
+-(1 bsl 64) bxor -1 = 16#ffffffffffffffff.
+-(1 bsl 72) bxor -1 = 16#ffffffffffffffffff.
+-(1 bsl 80) bxor -1 = 16#ffffffffffffffffffff.
+-(1 bsl 88) bxor -1 = 16#ffffffffffffffffffffff.
+-(1 bsl 96) bxor -1 = 16#ffffffffffffffffffffffff.
+-(1 bsl 104) bxor -1 = 16#ffffffffffffffffffffffffff.
+-(1 bsl 112) bxor -1 = 16#ffffffffffffffffffffffffffff.
+-(1 bsl 120) bxor -1 = 16#ffffffffffffffffffffffffffffff.
+-(1 bsl 128) bxor -1 = 16#ffffffffffffffffffffffffffffffff.
+-(1 bsl 136) bxor -1 = 16#ffffffffffffffffffffffffffffffffff.
+-(1 bsl 8) bxor 1 = -16#ff.
+-(1 bsl 16) bxor 1 = -16#ffff.
+-(1 bsl 24) bxor 1 = -16#ffffff.
+-(1 bsl 32) bxor 1 = -16#ffffffff.
+-(1 bsl 40) bxor 1 = -16#ffffffffff.
+-(1 bsl 48) bxor 1 = -16#ffffffffffff.
+-(1 bsl 56) bxor 1 = -16#ffffffffffffff.
+-(1 bsl 64) bxor 1 = -16#ffffffffffffffff.
+-(1 bsl 72) bxor 1 = -16#ffffffffffffffffff.
+-(1 bsl 80) bxor 1 = -16#ffffffffffffffffffff.
+-(1 bsl 88) bxor 1 = -16#ffffffffffffffffffffff.
+-(1 bsl 96) bxor 1 = -16#ffffffffffffffffffffffff.
+-(1 bsl 104) bxor 1 = -16#ffffffffffffffffffffffffff.
+-(1 bsl 112) bxor 1 = -16#ffffffffffffffffffffffffffff.
+-(1 bsl 120) bxor 1 = -16#ffffffffffffffffffffffffffffff.
+-(1 bsl 128) bxor 1 = -16#ffffffffffffffffffffffffffffffff.
+-(1 bsl 136) bxor 1 = -16#ffffffffffffffffffffffffffffffffff.
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 1c7d278bb0..7a2503178a 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -601,6 +601,9 @@ bad_binary_to_term(Config) when is_list(Config) ->
%% Bad float.
bad_bin_to_term(<<131,70,-1:64>>),
+
+ %% Truncated UTF8 character (ERL-474)
+ bad_bin_to_term(<<131,119,1,194,163>>),
ok.
bad_bin_to_term(BadBin) ->
diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl
index bb0632ae08..7094cee992 100644
--- a/erts/emulator/test/busy_port_SUITE.erl
+++ b/erts/emulator/test/busy_port_SUITE.erl
@@ -469,12 +469,12 @@ hs_busy_pcmd(Prt, Opts, StartFun, EndFun) ->
P = spawn_link(fun () ->
erlang:yield(),
Tester ! {self(), doing_port_command},
- Start = erlang:monotonic_time(micro_seconds),
+ Start = erlang:monotonic_time(microsecond),
Res = try {return,
port_command(Prt, [], Opts)}
catch Exception:Error -> {Exception, Error}
end,
- End = erlang:monotonic_time(micro_seconds),
+ End = erlang:monotonic_time(microsecond),
Time = round((End - Start)/1000),
Tester ! {self(), port_command_result, Res, Time}
end),
@@ -717,7 +717,7 @@ run_command(_M,spawn,{Args,Opts}) ->
run_command(M,spawn,Args) ->
run_command(M,spawn,{Args,[]});
run_command(Mod,Func,Args) ->
- erlang:display({{Mod,Func,Args}, erlang:system_time(micro_seconds)}),
+ erlang:display({{Mod,Func,Args}, erlang:system_time(microsecond)}),
apply(Mod,Func,Args).
validate_scenario(Data,[{print,Var}|T]) ->
diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index 6ba6301c7c..f7ff04430a 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -1090,8 +1090,7 @@ exception_nocatch() ->
{trace,t2,exception_from,{erlang,throw,1},
{error,{nocatch,Q2}}}],
exception_from, {error,{nocatch,Q2}}),
- expect({trace,T2,exit,{{nocatch,Q2},[{erlang,throw,[Q2],[]},
- {?MODULE,deep_4,1,
+ expect({trace,T2,exit,{{nocatch,Q2},[{?MODULE,deep_4,1,
Deep4LocThrow}]}}),
Q3 = {dump,[dump,{dump}]},
T3 =
@@ -1100,8 +1099,7 @@ exception_nocatch() ->
{trace,t3,exception_from,{erlang,error,1},
{error,Q3}}],
exception_from, {error,Q3}),
- expect({trace,T3,exit,{Q3,[{erlang,error,[Q3],[]},
- {?MODULE,deep_4,1,Deep4LocError}]}}),
+ expect({trace,T3,exit,{Q3,[{?MODULE,deep_4,1,Deep4LocError}]}}),
T4 =
exception_nocatch(?LINE, '=', [17,4711], 5, [],
exception_from, {error,{badmatch,4711}}),
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index 2347a3d4ef..1065b56e19 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -20,7 +20,8 @@
-module(code_SUITE).
-export([all/0, suite/0, init_per_suite/1, end_per_suite/1,
- versions/1,new_binary_types/1,
+ versions/1,new_binary_types/1, call_purged_fun_code_gone/1,
+ call_purged_fun_code_reload/1, call_purged_fun_code_there/1,
t_check_process_code/1,t_check_old_code/1,
t_check_process_code_ets/1,
external_fun/1,get_chunk/1,module_md5/1,make_stub/1,
@@ -34,7 +35,8 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [versions, new_binary_types, t_check_process_code,
+ [versions, new_binary_types, call_purged_fun_code_gone,
+ call_purged_fun_code_reload, call_purged_fun_code_there, t_check_process_code,
t_check_process_code_ets, t_check_old_code, external_fun, get_chunk,
module_md5, make_stub, make_stub_many_funs,
constant_pools, constant_refc_binaries, false_dependency,
@@ -64,9 +66,9 @@ versions(Config) when is_list(Config) ->
2 = versions:version(),
%% Kill processes, unload code.
- P1 ! P2 ! done,
_ = monitor(process, P1),
_ = monitor(process, P2),
+ P1 ! P2 ! done,
receive
{'DOWN',_,process,P1,normal} -> ok
end,
@@ -127,12 +129,169 @@ new_binary_types(Config) when is_list(Config) ->
bit_sized_binary(Bin))),
ok.
+call_purged_fun_code_gone(Config) when is_list(Config) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ call_purged_fun_test(Priv, Data, code_gone),
+ ok.
+
+call_purged_fun_code_reload(Config) when is_list(Config) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ Path = code:get_path(),
+ true = code:add_path(Priv),
+ try
+ call_purged_fun_test(Priv, Data, code_reload)
+ after
+ code:set_path(Path)
+ end,
+ ok.
+
+call_purged_fun_code_there(Config) when is_list(Config) ->
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ call_purged_fun_test(Priv, Data, code_there),
+ ok.
+
+call_purged_fun_test(Priv, Data, Type) ->
+ File = filename:join(Data, "my_code_test2"),
+ Code = filename:join(Priv, "my_code_test2"),
+
+ catch erlang:purge_module(my_code_test2),
+ catch erlang:delete_module(my_code_test2),
+ catch erlang:purge_module(my_code_test2),
+
+ {ok,my_code_test2} = c:c(File, [{outdir,Priv}]),
+
+ T = ets:new(my_code_test2_fun_table, []),
+ ets:insert(T, {my_fun,my_code_test2:make_fun(4711)}),
+ ets:insert(T, {my_fun2,my_code_test2:make_fun2()}),
+
+ spawn(fun () ->
+ [{my_fun2,F2}] = ets:lookup(T, my_fun2),
+ F2(fun () ->
+ receive after infinity -> ok end
+ end,
+ fun () -> ok end),
+ exit(completed)
+ end),
+
+ PurgeType = case Type of
+ code_gone ->
+ ok = file:delete(Code++".beam"),
+ true;
+ code_reload ->
+ true;
+ code_there ->
+ false
+ end,
+
+ true = erlang:delete_module(my_code_test2),
+
+ Purge = start_purge(my_code_test2, PurgeType),
+
+ {P0, M0} = spawn_monitor(fun () ->
+ [{my_fun,F}] = ets:lookup(T, my_fun),
+ 4712 = F(1),
+ exit(completed)
+ end),
+
+ wait_until(fun () ->
+ {status, suspended}
+ == process_info(P0, status)
+ end),
+
+ ok = continue_purge(Purge),
+
+ {P1, M1} = spawn_monitor(fun () ->
+ [{my_fun,F}] = ets:lookup(T, my_fun),
+ 4713 = F(2),
+ exit(completed)
+ end),
+ {P2, M2} = spawn_monitor(fun () ->
+ [{my_fun,F}] = ets:lookup(T, my_fun),
+ 4714 = F(3),
+ exit(completed)
+ end),
+
+ wait_until(fun () ->
+ {status, suspended}
+ == process_info(P1, status)
+ end),
+ wait_until(fun () ->
+ {status, suspended}
+ == process_info(P2, status)
+ end),
+
+ {current_function,
+ {erts_code_purger,
+ pending_purge_lambda,
+ 3}} = process_info(P0, current_function),
+ {current_function,
+ {erts_code_purger,
+ pending_purge_lambda,
+ 3}} = process_info(P1, current_function),
+ {current_function,
+ {erts_code_purger,
+ pending_purge_lambda,
+ 3}} = process_info(P2, current_function),
+
+ case Type of
+ code_there ->
+ false = complete_purge(Purge);
+ _ ->
+ {true, true} = complete_purge(Purge)
+ end,
+
+ case Type of
+ code_gone ->
+ receive
+ {'DOWN', M0, process, P0, Reason0} ->
+ {undef, _} = Reason0
+ end,
+ receive
+ {'DOWN', M1, process, P1, Reason1} ->
+ {undef, _} = Reason1
+ end,
+ receive
+ {'DOWN', M2, process, P2, Reason2} ->
+ {undef, _} = Reason2
+ end;
+ _ ->
+ receive
+ {'DOWN', M0, process, P0, Reason0} ->
+ completed = Reason0
+ end,
+ receive
+ {'DOWN', M1, process, P1, Reason1} ->
+ completed = Reason1
+ end,
+ receive
+ {'DOWN', M2, process, P2, Reason2} ->
+ completed = Reason2
+ end,
+ catch erlang:purge_module(my_code_test2),
+ catch erlang:delete_module(my_code_test2),
+ catch erlang:purge_module(my_code_test2)
+ end,
+ ok.
+
t_check_process_code(Config) when is_list(Config) ->
+ case check_process_code_handle(indirect_references) of
+ false -> {skipped, "check_process_code() ignores funs"};
+ true -> t_check_process_code_test(Config)
+ end.
+
+t_check_process_code_test(Config) ->
Priv = proplists:get_value(priv_dir, Config),
Data = proplists:get_value(data_dir, Config),
File = filename:join(Data, "my_code_test"),
Code = filename:join(Priv, "my_code_test"),
+ catch erlang:purge_module(my_code_test),
+ catch erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
+
{ok,my_code_test} = c:c(File, [{outdir,Priv}]),
MyFun = fun(X, Y) -> X + Y end, %Confuse things.
@@ -231,11 +390,16 @@ gc1() -> ok.
%% Test check_process_code/2 in combination with a fun obtained from an ets table.
t_check_process_code_ets(Config) when is_list(Config) ->
- case test_server:is_native(?MODULE) of
- true ->
- {skip,"Native code"};
- false ->
- do_check_process_code_ets(Config)
+ case check_process_code_handle(indirect_references) of
+ false ->
+ {skipped, "check_process_code() ignores funs"};
+ true ->
+ case test_server:is_native(?MODULE) of
+ true ->
+ {skip,"Native code"};
+ false ->
+ do_check_process_code_ets(Config)
+ end
end.
do_check_process_code_ets(Config) ->
@@ -243,8 +407,9 @@ do_check_process_code_ets(Config) ->
Data = proplists:get_value(data_dir, Config),
File = filename:join(Data, "my_code_test"),
- erlang:purge_module(my_code_test),
- erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
+ catch erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
{ok,my_code_test} = c:c(File, [{outdir,Priv}]),
T = ets:new(my_code_test, []),
@@ -295,8 +460,8 @@ t_check_old_code(Config) when is_list(Config) ->
Data = proplists:get_value(data_dir, Config),
File = filename:join(Data, "my_code_test"),
- erlang:purge_module(my_code_test),
- erlang:delete_module(my_code_test),
+ catch erlang:purge_module(my_code_test),
+ catch erlang:delete_module(my_code_test),
catch erlang:purge_module(my_code_test),
false = erlang:check_old_code(my_code_test),
@@ -480,9 +645,40 @@ constant_pools(Config) when is_list(Config) ->
erlang:purge_module(literals),
OldHeap ! done,
receive
- {'EXIT',OldHeap,{A,B,C,[1,2,3|_]=Seq}} when length(Seq) =:= 16 ->
- ok
- end.
+ {'EXIT',OldHeap,{A,B,C,[1,2,3|_]=Seq}} when length(Seq) =:= 16 ->
+ ok
+ end,
+
+ {module,literals} = erlang:load_module(literals, Code),
+ %% Have a hibernated process that references the literals
+ %% in the 'literals' module.
+ {Hib, Mon} = spawn_monitor(fun() -> hibernated(Self) end),
+ receive go -> ok end,
+ [{heap_size,OldHeapSz},
+ {total_heap_size,OldTotHeapSz}] = process_info(Hib, [heap_size,
+ total_heap_size]),
+ OldHeapSz = OldTotHeapSz,
+ io:format("OldHeapSz=~p OldTotHeapSz=~p~n", [OldHeapSz, OldTotHeapSz]),
+ true = erlang:delete_module(literals),
+ false = erlang:check_process_code(Hib, literals),
+ erlang:check_process_code(self(), literals),
+ erlang:purge_module(literals),
+ receive after 1000 -> ok end,
+ [{heap_size,HeapSz},
+ {total_heap_size,TotHeapSz}] = process_info(Hib, [heap_size,
+ total_heap_size]),
+ io:format("HeapSz=~p TotHeapSz=~p~n", [HeapSz, TotHeapSz]),
+ Hib ! hej,
+ receive
+ {'DOWN', Mon, process, Hib, Reason} ->
+ {undef, [{no_module,
+ no_function,
+ [{A,B,C,[1,2,3|_]=Seq}], _}]} = Reason,
+ 16 = length(Seq)
+ end,
+ HeapSz = TotHeapSz, %% Ensure restored to hibernated state...
+ true = HeapSz > OldHeapSz,
+ ok.
no_old_heap(Parent) ->
A = literals:a(),
@@ -505,6 +701,13 @@ old_heap(Parent) ->
exit(Res)
end.
+hibernated(Parent) ->
+ A = literals:a(),
+ B = literals:b(),
+ Res = {A,B,literals:huge_bignum(),lists:seq(1, 16)},
+ Parent ! go,
+ erlang:hibernate(no_module, no_function, [Res]).
+
create_old_heap() ->
case process_info(self(), [heap_size,total_heap_size]) of
[{heap_size,Sz},{total_heap_size,Total}] when Sz < Total ->
@@ -971,3 +1174,39 @@ flush() ->
receive _ -> flush() after 0 -> ok end.
id(I) -> I.
+
+check_process_code_handle(What) ->
+ lists:member(What, erlang:system_info(check_process_code)).
+
+wait_until(Fun) ->
+ case Fun() of
+ true ->
+ ok;
+ false ->
+ receive after 100 -> ok end,
+ wait_until(Fun)
+ end.
+
+start_purge(Mod, Type) when is_atom(Mod)
+ andalso ((Type == true)
+ orelse (Type == false)) ->
+ Ref = make_ref(),
+ erts_code_purger ! {test_purge, Mod, self(), Type, Ref},
+ receive
+ {started, Ref} ->
+ Ref
+ end.
+
+continue_purge(Ref) when is_reference(Ref) ->
+ erts_code_purger ! {continue, Ref},
+ receive
+ {continued, Ref} ->
+ ok
+ end.
+
+complete_purge(Ref) when is_reference(Ref) ->
+ erts_code_purger ! {complete, Ref},
+ receive
+ {test_purge, Res, Ref} ->
+ Res
+ end.
diff --git a/erts/emulator/test/code_SUITE_data/my_code_test.erl b/erts/emulator/test/code_SUITE_data/my_code_test.erl
index d2386157d6..9d12aa9897 100644
--- a/erts/emulator/test/code_SUITE_data/my_code_test.erl
+++ b/erts/emulator/test/code_SUITE_data/my_code_test.erl
@@ -24,5 +24,3 @@
make_fun(A) ->
fun(X) -> A + X end.
-
-
diff --git a/erts/emulator/test/code_SUITE_data/my_code_test2.erl b/erts/emulator/test/code_SUITE_data/my_code_test2.erl
new file mode 100644
index 0000000000..57973535d4
--- /dev/null
+++ b/erts/emulator/test/code_SUITE_data/my_code_test2.erl
@@ -0,0 +1,32 @@
+%%
+%% %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(my_code_test2).
+
+-export([make_fun/1, make_fun2/0]).
+
+make_fun(A) ->
+ fun(X) -> A + X end.
+
+make_fun2() ->
+ fun (F1,F2) ->
+ F1(),
+ F2()
+ end.
diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl
index 83b098a704..a61fd92a18 100644
--- a/erts/emulator/test/dirty_nif_SUITE.erl
+++ b/erts/emulator/test/dirty_nif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2014. 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.
@@ -33,7 +33,8 @@
dirty_nif_exception/1, call_dirty_nif_exception/1,
dirty_scheduler_exit/1, dirty_call_while_terminated/1,
dirty_heap_access/1, dirty_process_info/1,
- dirty_process_register/1, dirty_process_trace/1]).
+ dirty_process_register/1, dirty_process_trace/1,
+ code_purge/1, dirty_nif_send_traced/1]).
-define(nif_stub,nif_stub_error(?LINE)).
@@ -48,7 +49,9 @@ all() ->
dirty_heap_access,
dirty_process_info,
dirty_process_register,
- dirty_process_trace].
+ dirty_process_trace,
+ code_purge,
+ dirty_nif_send_traced].
init_per_suite(Config) ->
try erlang:system_info(dirty_cpu_schedulers) of
@@ -145,9 +148,9 @@ dirty_scheduler_exit(Config) when is_list(Config) ->
[ok] = mcall(Node,
[fun() ->
ok = erlang:load_nif(NifLib, []),
- Start = erlang:monotonic_time(milli_seconds),
+ Start = erlang:monotonic_time(millisecond),
ok = test_dirty_scheduler_exit(),
- End = erlang:monotonic_time(milli_seconds),
+ End = erlang:monotonic_time(millisecond),
io:format("Time=~p ms~n", [End-Start]),
ok
end]),
@@ -230,7 +233,11 @@ dirty_call_while_terminated(Config) when is_list(Config) ->
process_info(self(),
binary))),
process_flag(trap_exit, OT),
- ok.
+ try
+ blipp:blupp(Bin)
+ catch
+ _ : _ -> ok
+ end.
dirty_heap_access(Config) when is_list(Config) ->
{ok, Node} = start_node(Config),
@@ -349,6 +356,103 @@ dirty_process_trace(Config) when is_list(Config) ->
ok
end).
+dirty_code_test_code() ->
+ "
+-module(dirty_code_test).
+
+-export([func/1]).
+
+func(Fun) ->
+ Fun(),
+ blipp:blapp().
+
+".
+
+code_purge(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ File = filename:join(Path, "dirty_code_test.erl"),
+ ok = file:write_file(File, dirty_code_test_code()),
+ {ok, dirty_code_test, Bin} = compile:file(File, [binary]),
+ {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin),
+ Start = erlang:monotonic_time(),
+ {Pid1, Mon1} = spawn_monitor(fun () ->
+ dirty_code_test:func(fun () ->
+ %% Sleep for 6 seconds
+ %% in dirty nif...
+ dirty_sleeper()
+ end)
+ end),
+ {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin),
+ {Pid2, Mon2} = spawn_monitor(fun () ->
+ dirty_code_test:func(fun () ->
+ %% Sleep for 6 seconds
+ %% in dirty nif...
+ dirty_sleeper()
+ end)
+ end),
+ receive
+ {'DOWN', Mon1, process, Pid1, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:purge_module(dirty_code_test),
+ receive
+ {'DOWN', Mon1, process, Pid1, Reason1} ->
+ killed = Reason1
+ end,
+ receive
+ {'DOWN', Mon2, process, Pid2, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:delete_module(dirty_code_test),
+ receive
+ {'DOWN', Mon2, process, Pid2, _} ->
+ ct:fail(premature_death)
+ after 100 ->
+ ok
+ end,
+ true = erlang:purge_module(dirty_code_test),
+ receive
+ {'DOWN', Mon2, process, Pid2, Reason2} ->
+ killed = Reason2
+ end,
+ End = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(End-Start, native, milli_seconds),
+ io:format("Time=~p~n", [Time]),
+ true = Time =< 1000,
+ ok.
+
+dirty_nif_send_traced(Config) when is_list(Config) ->
+ Parent = self(),
+ Rcvr = spawn_link(fun() ->
+ Self = self(),
+ receive {ok, Self} -> ok end,
+ Parent ! {Self, received}
+ end),
+ Sndr = spawn_link(fun () ->
+ receive {Parent, go} -> ok end,
+ {ok, Rcvr} = send_wait_from_dirty_nif(Rcvr),
+ Parent ! {self(), sent}
+ end),
+ 1 = erlang:trace(Sndr, true, [send]),
+ Start = erlang:monotonic_time(),
+ Sndr ! {self(), go},
+ receive {trace, Sndr, send, {ok, Rcvr}, Rcvr} -> ok end,
+ receive {Rcvr, received} -> ok end,
+ End1 = erlang:monotonic_time(),
+ Time1 = erlang:convert_time_unit(End1-Start, native, 1000),
+ io:format("Time1: ~p milliseconds~n", [Time1]),
+ true = Time1 < 500,
+ receive {Sndr, sent} -> ok end,
+ End2 = erlang:monotonic_time(),
+ Time2 = erlang:convert_time_unit(End2-Start, native, 1000),
+ io:format("Time2: ~p milliseconds~n", [Time2]),
+ true = Time2 >= 1900,
+ ok.
+
%%
%% Internal...
%%
@@ -400,7 +504,7 @@ start_node(Config, Args) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
@@ -431,6 +535,7 @@ mcall(Node, Funs) ->
lib_loaded() -> false.
call_dirty_nif(_,_,_) -> ?nif_stub.
send_from_dirty_nif(_) -> ?nif_stub.
+send_wait_from_dirty_nif(_) -> ?nif_stub.
call_dirty_nif_exception(_) -> ?nif_stub.
call_dirty_nif_zero_args() -> ?nif_stub.
dirty_call_while_terminated_nif(_) -> ?nif_stub.
diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
index d92933a096..08efa23c81 100644
--- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
+++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2009-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.
@@ -100,6 +100,32 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_
return result;
}
+static ERL_NIF_TERM send_wait_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM result;
+ ErlNifPid pid;
+ ErlNifEnv* menv;
+ int res;
+
+ if (!enif_get_local_pid(env, argv[0], &pid))
+ return enif_make_badarg(env);
+ result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid));
+ menv = enif_alloc_env();
+ res = enif_send(env, &pid, menv, result);
+ enif_free_env(menv);
+
+#ifdef __WIN32__
+ Sleep(2000);
+#else
+ sleep(2);
+#endif
+
+ if (!res)
+ return enif_make_badarg(env);
+ else
+ return result;
+}
+
static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
switch (argc) {
@@ -237,6 +263,7 @@ static ErlNifFunc nif_funcs[] =
{"lib_loaded", 0, lib_loaded},
{"call_dirty_nif", 3, call_dirty_nif},
{"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
+ {"send_wait_from_dirty_nif", 1, send_wait_from_dirty_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND},
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index c6939a695d..3a637b5682 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -43,7 +43,7 @@
lost_exit/1, link_to_dead/1, link_to_dead_new_node/1,
applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1,
trap_bif_1/1, trap_bif_2/1, trap_bif_3/1,
- stop_dist/1,
+ stop_dist/1,
dist_auto_connect_never/1, dist_auto_connect_once/1,
dist_parallel_send/1,
atom_roundtrip/1,
@@ -56,6 +56,7 @@
bad_dist_ext_process_info/1,
bad_dist_ext_control/1,
bad_dist_ext_connection_id/1,
+ bad_dist_ext_size/1,
start_epmd_false/1, epmd_module/1]).
%% Internal exports.
@@ -66,13 +67,13 @@
sendersender/4, sendersender2/4]).
%% epmd_module exports
--export([start_link/0, register_node/2, port_please/2]).
+-export([start_link/0, register_node/2, register_node/3, port_please/2]).
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 4}}].
-all() ->
+all() ->
[ping, {group, bulk_send}, {group, local_send},
link_to_busy, exit_to_busy, lost_exit, link_to_dead,
link_to_dead_new_node, applied_monitor_node,
@@ -83,7 +84,7 @@ all() ->
bad_dist_structure, {group, bad_dist_ext},
start_epmd_false, epmd_module].
-groups() ->
+groups() ->
[{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
{local_send, [],
[local_send_small, local_send_big, local_send_legal]},
@@ -92,6 +93,7 @@ groups() ->
[dist_auto_connect_never, dist_auto_connect_once]},
{bad_dist_ext, [],
[bad_dist_ext_receive, bad_dist_ext_process_info,
+ bad_dist_ext_size,
bad_dist_ext_control, bad_dist_ext_connection_id]}].
%% Tests pinging a node in different ways.
@@ -844,59 +846,50 @@ dist_auto_connect_once(Config) when is_list(Config) ->
%% Result is sent here through relay node.
dist_auto_connect_never(Config) when is_list(Config) ->
Self = self(),
- {ok, RelayNode} =
- start_node(dist_auto_connect_relay),
- spawn(RelayNode,
+ {ok, RelayNode} = start_node(dist_auto_connect_relay),
+ spawn(RelayNode,
fun() ->
register(dist_auto_connect_relay, self()),
- dist_auto_connect_relay(Self)
+ dist_auto_connect_relay(Self)
end),
{ok, Handle} = dist_auto_connect_start(dist_auto_connect, never),
- Result =
- receive
- {do_dist_auto_connect, ok} ->
- ok;
- {do_dist_auto_connect, Error} ->
- {error, Error};
- Other ->
- {error, Other}
- after 32000 ->
- timeout
- end,
+ Result = receive
+ {do_dist_auto_connect, ok} ->
+ ok;
+ {do_dist_auto_connect, Error} ->
+ {error, Error};
+ Other ->
+ {error, Other}
+ after 32000 ->
+ timeout
+ end,
stop_node(RelayNode),
- Stopped = dist_auto_connect_stop(Handle),
- Junk =
- receive
- {do_dist_auto_connect, _} = J ->
- J
- after 0 ->
- ok
- end,
+ Stopped = dist_auto_connect_stop(Handle),
+ Junk = receive
+ {do_dist_auto_connect, _} = J -> J
+ after 0 -> ok
+ end,
{ok, ok, ok} = {Result, Stopped, Junk},
ok.
do_dist_auto_connect([never]) ->
Node = list_to_atom("dist_auto_connect_relay@" ++ hostname()),
- io:format("~p:do_dist_auto_connect([false]) Node=~p~n",
- [?MODULE, Node]),
+ io:format("~p:do_dist_auto_connect([false]) Node=~p~n", [?MODULE, Node]),
Ping = net_adm:ping(Node),
- io:format("~p:do_dist_auto_connect([false]) Ping=~p~n",
- [?MODULE, Ping]),
+ io:format("~p:do_dist_auto_connect([false]) Ping=~p~n", [?MODULE, Ping]),
Result = case Ping of
pang -> ok;
_ -> {error, Ping}
end,
- io:format("~p:do_dist_auto_connect([false]) Result=~p~n",
- [?MODULE, Result]),
+ io:format("~p:do_dist_auto_connect([false]) Result=~p~n", [?MODULE, Result]),
net_kernel:connect_node(Node),
catch {dist_auto_connect_relay, Node} ! {do_dist_auto_connect, Result};
% receive after 1000 -> ok end,
% halt();
do_dist_auto_connect(Arg) ->
- io:format("~p:do_dist_auto_connect(~p)~n",
- [?MODULE, Arg]),
+ io:format("~p:do_dist_auto_connect(~p)~n", [?MODULE, Arg]),
receive after 10000 -> ok end,
halt().
@@ -912,11 +905,11 @@ dist_auto_connect_start(Name, Value) when is_list(Name), is_atom(Value) ->
[%"xterm -e ",
atom_to_list(lib:progname()),
% " -noinput ",
- " -detached ",
+ " -detached ",
long_or_short(), " ", Name,
" -setcookie ", Cookie,
" -pa ", ModuleDir,
- " -s ", atom_to_list(?MODULE),
+ " -s ", atom_to_list(?MODULE),
" do_dist_auto_connect ", ValueStr,
" -kernel dist_auto_connect ", ValueStr]),
io:format("~p:dist_auto_connect_start() cmd: ~p~n", [?MODULE, Cmd]),
@@ -947,7 +940,7 @@ dist_auto_connect_stop(Port, Node, Pid, N) when is_integer(N) ->
end.
-dist_auto_connect_relay(Parent) ->
+dist_auto_connect_relay(Parent) ->
receive X ->
catch Parent ! X
end,
@@ -1321,7 +1314,7 @@ get_conflicting_unicode_atoms(CIX, N) ->
start_monitor(Offender,P) ->
Parent = self(),
Q = spawn(Offender,
- fun () ->
+ fun () ->
Ref = erlang:monitor(process,P),
Parent ! {self(),ref,Ref},
receive
@@ -1458,8 +1451,8 @@ bad_dist_structure(Config) when is_list(Config) ->
pong = rpc:call(Victim, net_adm, ping, [Offender]),
P ! two,
P ! check_msgs,
- receive
- {P, messages_checked} -> ok
+ receive
+ {P, messages_checked} -> ok
after 5000 ->
exit(victim_is_dead)
end,
@@ -1692,6 +1685,57 @@ bad_dist_ext_connection_id(Config) when is_list(Config) ->
stop_node(Offender),
stop_node(Victim).
+%% OTP-14661: Bad message is discovered by erts_msg_attached_data_size
+bad_dist_ext_size(Config) when is_list(Config) ->
+ {ok, Offender} = start_node(bad_dist_ext_process_info_offender),
+ %%Prog = "Prog=/home/uabseri/src/otp_new3/bin/cerl -rr -debug",
+ Prog = [],
+ {ok, Victim} = start_node(bad_dist_ext_process_info_victim, [], Prog),
+ start_node_monitors([Offender,Victim]),
+
+ Parent = self(),
+ P = spawn_link(Victim,
+ fun () ->
+ Parent ! {self(), started},
+ receive check_msgs -> ok end, %% DID CRASH HERE
+ bad_dist_ext_check_msgs([one]),
+ Parent ! {self(), messages_checked}
+ end),
+
+ receive {P, started} -> ok end,
+ P ! one,
+
+ Suspended = make_ref(),
+ S = spawn(Victim,
+ fun () ->
+ erlang:suspend_process(P),
+ Parent ! Suspended,
+ receive after infinity -> ok end
+ end),
+
+ receive Suspended -> ok end,
+ pong = rpc:call(Victim, net_adm, ping, [Offender]),
+ verify_up(Offender, Victim),
+ send_bad_msgs(Offender, P, 1, dmsg_bad_tag()),
+
+ %% Make sure bad msgs has reached Victim
+ rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
+
+ verify_still_up(Offender, Victim),
+
+ rpc:call(Victim, erlang, process_info, [P, total_heap_size]),
+
+ verify_down(Offender, connection_closed, Victim, killed),
+
+ P ! check_msgs,
+ exit(S, bang), % resume Victim
+ receive {P, messages_checked} -> ok end,
+
+ unlink(P),
+ verify_no_down(Offender, Victim),
+ stop_node(Offender),
+ stop_node(Victim).
+
bad_dist_struct_check_msgs([]) ->
receive
@@ -1765,7 +1809,7 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
pong = net_adm:ping(Node),
DPrt = dport(Node),
Bad1 = case WhereToPutSelf of
- 0 ->
+ 0 ->
Bad;
N when N > 0 ->
setelement(N,Bad,self())
@@ -1779,8 +1823,8 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
port_command(DPrt, DData),
Parent ! {DData,Done}
end),
- receive
- {WhatSent,Done} ->
+ receive
+ {WhatSent,Done} ->
io:format("Offender sent ~p~n",[WhatSent]),
ok
after 5000 ->
@@ -1795,9 +1839,12 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
send_bad_msg(BadNode, To) ->
send_bad_msgs(BadNode, To, 1).
-send_bad_msgs(BadNode, To, Repeat) when is_atom(BadNode),
- is_pid(To),
- is_integer(Repeat) ->
+send_bad_msgs(BadNode, To, Repeat) ->
+ send_bad_msgs(BadNode, To, Repeat, dmsg_bad_atom_cache_ref()).
+
+send_bad_msgs(BadNode, To, Repeat, BadTerm) when is_atom(BadNode),
+ is_pid(To),
+ is_integer(Repeat) ->
Parent = self(),
Done = make_ref(),
spawn_link(BadNode,
@@ -1807,7 +1854,7 @@ send_bad_msgs(BadNode, To, Repeat) when is_atom(BadNode),
DPrt = dport(Node),
DData = [dmsg_hdr(),
dmsg_ext({?DOP_SEND, ?COOKIE, To}),
- dmsg_bad_atom_cache_ref()],
+ BadTerm],
repeat(fun () -> port_command(DPrt, DData) end, Repeat),
Parent ! Done
end),
@@ -1887,13 +1934,16 @@ dmsg_fake_hdr2() ->
1, size(A2), A2,
2, size(A3), A3].
-dmsg_ext(Term) ->
+dmsg_ext(Term) ->
<<131, Res/binary>> = term_to_binary(Term),
Res.
dmsg_bad_atom_cache_ref() ->
[$R, 137].
+dmsg_bad_tag() -> %% Will fail early at heap size calculation
+ [$?, 66].
+
start_epmd_false(Config) when is_list(Config) ->
%% Start a node with the option -start_epmd false.
{ok, OtherNode} = start_node(start_epmd_false, "-start_epmd false"),
@@ -1934,7 +1984,9 @@ epmd_module(Config) when is_list(Config) ->
start_link() ->
ignore.
-register_node(_Name, Port) ->
+register_node(Name, Port) ->
+ register_node(Name, Port, inet_tcp).
+register_node(_Name, Port, _Driver) ->
%% Save the port number we're listening on.
application:set_env(kernel, dist_listen_port, Port),
Creation = rand:uniform(3),
@@ -1957,7 +2009,7 @@ port_please(_Name, _Ip) ->
%%% Utilities
timestamp() ->
- erlang:monotonic_time(milli_seconds).
+ erlang:monotonic_time(millisecond).
start_node(X) ->
start_node(X, [], []).
@@ -1972,7 +2024,7 @@ start_node(Name, Args, Rel) when is_atom(Name), is_list(Rel) ->
[] -> [];
_ -> [{erl,[{release,Rel}]}]
end,
- test_server:start_node(Name, slave,
+ test_server:start_node(Name, slave,
[{args,
Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""}
| RelArg]);
@@ -1981,7 +2033,7 @@ start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive])))),
start_node(Name, Args, Rel).
@@ -2040,17 +2092,15 @@ inet_rpc_server_loop(Sock) ->
start_relay_node(Node, Args) ->
Pa = filename:dirname(code:which(?MODULE)),
Cookie = "NOT"++atom_to_list(erlang:get_cookie()),
- {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4},
- {active, false}]),
+ {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, {active, false}]),
{ok, Port} = inet:port(LSock),
{ok, Host} = inet:gethostname(),
RunArg = "-run " ++ atom_to_list(?MODULE) ++ " inet_rpc_server " ++
Host ++ " " ++ integer_to_list(Port),
- {ok, NN} =
- test_server:start_node(Node, peer,
- [{args, Args ++
- " -setcookie "++Cookie++" -pa "++Pa++" "++
- RunArg}]),
+ {ok, NN} = test_server:start_node(Node, peer,
+ [{args, Args ++
+ " -setcookie "++Cookie++" -pa "++Pa++" "++
+ RunArg}]),
[N,H] = string:tokens(atom_to_list(NN),"@"),
{ok, Sock} = gen_tcp:accept(LSock),
pang = net_adm:ping(NN),
@@ -2066,7 +2116,7 @@ wait_dead(N,H,0) ->
wait_dead(N,H,X) ->
case erl_epmd:port_please(N,H) of
{port,_,_} ->
- receive
+ receive
after 1000 ->
ok
end,
@@ -2100,7 +2150,7 @@ node_monitor(Master) ->
Master ! {nodeup, node(), Node}
end,
Nodes0),
- io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]),
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Nodes0]),
node_monitor_loop(Master);
false ->
net_kernel:monitor_nodes(false, Opts),
@@ -2121,7 +2171,7 @@ node_monitor_loop(Master) ->
receive
{nodeup, Node, _InfoList} = Msg ->
Master ! {nodeup, node(), Node},
- io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]),
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]),
node_monitor_loop(Master);
{nodedown, Node, InfoList} = Msg ->
Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of
@@ -2129,7 +2179,7 @@ node_monitor_loop(Master) ->
_ -> undefined
end,
Master ! {nodedown, node(), Node, Reason},
- io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]),
+ io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]),
node_monitor_loop(Master)
end.
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index f134a197aa..2fbf6eae61 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -114,7 +114,7 @@
-define(MAX_DATA_SIZE, 16384).
% This is the allowed delay when testing the driver timer functionality
--define(delay, 100).
+-define(delay, 400).
-define(heap_binary_size, 64).
@@ -401,7 +401,7 @@ try_timeouts(Port, Timeout) ->
true ->
try_timeouts(Port, Timeout div 2)
end
- after Timeout + ?delay ->
+ after Timeout + 100*?delay ->
ct:fail("driver failed to timeout")
end.
@@ -437,7 +437,7 @@ try_cancel(Port, Timeout) ->
Timeout == 0 -> ok;
true -> try_cancel(Port, Timeout div 2)
end
- after ?delay ->
+ after 100*?delay ->
ct:fail("No message from driver")
end
end.
@@ -452,11 +452,7 @@ timer_delay(Config) when is_list(Config) ->
TimeBefore = erlang:monotonic_time(),
Timeout0 = 350,
erlang:port_command(Port, <<?DELAY_START_TIMER,Timeout0:32>>),
- Timeout = Timeout0 +
- case os:type() of
- {win32,_} -> 0; %Driver doesn't sleep on Windows.
- _ -> 1000
- end,
+ Timeout = Timeout0 + 1000,
receive
{Port,{data,[?TIMER]}} ->
Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
@@ -505,7 +501,7 @@ try_change_timer(Port, Timeout) ->
true ->
try_timeouts(Port, Timeout div 2)
end
- after Timeout + ?delay ->
+ after Timeout + 100*?delay ->
ct:fail("driver failed to timeout")
end.
@@ -2427,7 +2423,7 @@ erl_millisecs() ->
erl_millisecs(erlang:monotonic_time()).
erl_millisecs(MonotonicTime) ->
- (1000*MonotonicTime)/erlang:convert_time_unit(1,seconds,native).
+ (1000*MonotonicTime)/erlang:convert_time_unit(1,second,native).
%% Start/stop drivers.
start_driver(Config, Name, Binary) ->
@@ -2481,7 +2477,7 @@ start_node(Config) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa}]).
diff --git a/erts/emulator/test/driver_SUITE_data/timer_drv.c b/erts/emulator/test/driver_SUITE_data/timer_drv.c
index 57538e0d57..c3ce3b6e49 100644
--- a/erts/emulator/test/driver_SUITE_data/timer_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/timer_drv.c
@@ -1,5 +1,13 @@
#include <stdio.h>
#include "erl_driver.h"
+#ifdef __WIN32__
+# include <windows.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/select.h>
+# include <unistd.h>
+#endif
#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
(((unsigned char*) (s))[1] << 16) | \
@@ -17,6 +25,7 @@ static ErlDrvData timer_start(ErlDrvPort, char*);
static void timer_stop(ErlDrvData);
static void timer_read(ErlDrvData, char*, ErlDrvSizeT);
static void timer(ErlDrvData);
+static void ms_sleep(int ms);
static ErlDrvEntry timer_driver_entry =
{
@@ -75,9 +84,7 @@ static void timer_read(ErlDrvData p, char *buf, ErlDrvSizeT len)
reply[0] = CANCELLED;
driver_output(port, reply, 1);
} else if (buf[0] == DELAY_START_TIMER) {
-#ifndef __WIN32__
- sleep(1);
-#endif
+ ms_sleep(1000);
driver_set_timer(port, get_int32(buf + 1));
}
}
@@ -95,3 +102,34 @@ static void timer(ErlDrvData port)
reply[0] = TIMER;
driver_output((ErlDrvPort)port, reply, 1);
}
+
+static void
+ms_sleep(int ms)
+{
+ /* Important that we do not return too early... */
+ ErlDrvTime time, timeout_time;
+
+ time = erl_drv_monotonic_time(ERL_DRV_USEC);
+
+ timeout_time = time + ((ErlDrvTime) ms)*1000;
+
+ while (time < timeout_time) {
+ ErlDrvTime timeout = timeout_time - time;
+
+#ifdef __WIN32__
+ Sleep((DWORD) (timeout / 1000));
+#else
+ {
+ struct timeval tv;
+
+ tv.tv_sec = (long) timeout / (1000*1000);
+ tv.tv_usec = (long) timeout % (1000*1000);
+
+ select(0, NULL, NULL, NULL, &tv);
+ }
+#endif
+
+ time = erl_drv_monotonic_time(ERL_DRV_USEC);
+ }
+
+}
diff --git a/erts/emulator/test/emulator_smoke.spec b/erts/emulator/test/emulator_smoke.spec
index 3219aeb823..b2d0de8835 100644
--- a/erts/emulator/test/emulator_smoke.spec
+++ b/erts/emulator/test/emulator_smoke.spec
@@ -1,3 +1,9 @@
-{suites,"../emulator_test",[smoke_test_SUITE,time_SUITE]}.
-{cases,"../emulator_test",crypto_SUITE,[t_md5]}.
-{cases,"../emulator_test",float_SUITE,[fpe,cmp_integer]}. \ No newline at end of file
+{define,'Dir',"../emulator_test"}.
+{suites,'Dir',[smoke_test_SUITE]}.
+{suites,'Dir',[time_SUITE]}.
+{skip_cases,'Dir',time_SUITE,
+ [univ_to_local,local_to_univ],"Depends on CET timezone"}.
+{skip_cases,'Dir',time_SUITE,
+ [consistency],"Not reliable in October and March"}.
+{cases,'Dir',crypto_SUITE,[t_md5]}.
+{cases,'Dir',float_SUITE,[fpe,cmp_integer]}.
diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl
index 93d2065ba3..89e1aefb50 100644
--- a/erts/emulator/test/erl_link_SUITE.erl
+++ b/erts/emulator/test/erl_link_SUITE.erl
@@ -1011,7 +1011,7 @@ get_names(N, T, Acc) ->
++ "-"
++ atom_to_list(T)
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))) | Acc]).
diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl
index 1180a45585..3ce849b88e 100644
--- a/erts/emulator/test/estone_SUITE.erl
+++ b/erts/emulator/test/estone_SUITE.erl
@@ -364,7 +364,7 @@ monotonic_time() ->
try erlang:monotonic_time() catch error:undef -> erlang:now() end.
subtr(Before, After) when is_integer(Before), is_integer(After) ->
- erlang:convert_time_unit(After-Before, native, micro_seconds);
+ erlang:convert_time_unit(After-Before, native, microsecond);
subtr({_,_,_}=Before, {_,_,_}=After) ->
timer:now_diff(After, Before).
diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl
index e85addae3a..36b1f9179f 100644
--- a/erts/emulator/test/float_SUITE.erl
+++ b/erts/emulator/test/float_SUITE.erl
@@ -276,7 +276,7 @@ start_node(Config) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa}]).
diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl
index e155e5f49f..1a93a9f5c2 100644
--- a/erts/emulator/test/guard_SUITE.erl
+++ b/erts/emulator/test/guard_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. 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.
@@ -317,6 +317,7 @@ guard_bifs(Config) when is_list(Config) ->
try_gbif('float/1', Big, float(id(Big))),
try_gbif('trunc/1', Float, 387924.0),
try_gbif('round/1', Float, 387925.0),
+ try_gbif('round/1', 6209607916799025.0, 6209607916799025),
try_gbif('length/1', [], 0),
try_gbif('length/1', [a], 1),
diff --git a/erts/emulator/test/hipe_SUITE.erl b/erts/emulator/test/hipe_SUITE.erl
new file mode 100644
index 0000000000..a556b4ddc0
--- /dev/null
+++ b/erts/emulator/test/hipe_SUITE.erl
@@ -0,0 +1,67 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(hipe_SUITE).
+-export([all/0, t_copy_literals/1]).
+
+all() ->
+ case erlang:system_info(hipe_architecture) of
+ undefined -> {skip, "HiPE is disabled"};
+ _ -> [t_copy_literals]
+ end.
+
+t_copy_literals(doc) ->
+ "Check that BEAM literals referenced from HiPE stack are copied by"
+ " check_process_code";
+t_copy_literals(Config) when is_list(Config) ->
+ %% Compile the the ref_cell and literals modules.
+ Data = proplists:get_value(data_dir, Config),
+ Priv = proplists:get_value(priv_dir, Config),
+ RefFile = filename:join(Data, "ref_cell"),
+ {ok,ref_cell} = c:c(RefFile, [{outdir,Priv},native]),
+ true = code:is_module_native(ref_cell),
+ LitFile = filename:join(Data, "literals"),
+ {ok,literals} = c:c(LitFile, [{outdir,Priv}]),
+
+ %% store references to literals on HiPE stacks
+ PA = ref_cell:start_link(),
+ ref_cell:call(PA, {put_res_of, fun literals:a/0}),
+ PB = ref_cell:start_link_deep(),
+ ref_cell:call(PB, {put_res_of, fun literals:b/0}),
+
+ %% purge the literals
+ _ = (catch erlang:purge_module(literals)),
+ true = erlang:delete_module(literals),
+ true = erlang:purge_module(literals),
+
+ %% Give the literal collector some time to work...
+ receive after 2000 -> ok end,
+
+ %% check that the ex-literals are ok
+ [a,b,c] = ref_cell:call(PA, get),
+ {a,b,c} = ref_cell:call(PB, get),
+
+ %% cleanup
+ ref_cell:call(PA, done),
+ ref_cell:call(PB, done),
+ _ = (catch erlang:purge_module(ref_cell)),
+ true = erlang:delete_module(ref_cell),
+ true = erlang:purge_module(ref_cell),
+ ok.
diff --git a/erts/emulator/test/hipe_SUITE_data/literals.erl b/erts/emulator/test/hipe_SUITE_data/literals.erl
new file mode 100644
index 0000000000..31e443970f
--- /dev/null
+++ b/erts/emulator/test/hipe_SUITE_data/literals.erl
@@ -0,0 +1,26 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(literals).
+
+-export([a/0, b/0]).
+
+a() -> [a,b,c].
+b() -> {a,b,c}.
diff --git a/erts/emulator/test/hipe_SUITE_data/ref_cell.erl b/erts/emulator/test/hipe_SUITE_data/ref_cell.erl
new file mode 100644
index 0000000000..2654e4077b
--- /dev/null
+++ b/erts/emulator/test/hipe_SUITE_data/ref_cell.erl
@@ -0,0 +1,64 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ref_cell).
+
+-export([start_link/0, start_link_deep/0, call/2]).
+
+-compile(native).
+
+-define(DEPTH, 100).
+-define(ALLOCS, 500).
+
+start_link() ->
+ spawn_link(fun() -> loop(undefined) end).
+
+start_link_deep() ->
+ spawn_link(fun() -> go_deep(?DEPTH) end).
+
+%% Create a stack large enough to get a graylimit trap placed next time there's
+%% a minor gc.
+go_deep(0) ->
+ alloc_some(?ALLOCS),
+ loop(undefined),
+ 0;
+go_deep(Depth) ->
+ go_deep(Depth-1)+1.
+
+%% Do some allocation to trigger a minor gc
+alloc_some(Amount) ->
+ Check = (Amount * (Amount + 1)) div 2,
+ Check = lists:sum(lists:seq(1, Amount)).
+
+call(Pid, Call) ->
+ Pid ! {Call, self()},
+ receive {Pid, Res} -> Res end.
+
+loop(Thing) ->
+ receive
+ {done, Pid} -> Pid ! {self(), done};
+ {{put_res_of, Fun}, Pid} ->
+ NewThing = Fun(),
+ Pid ! {self(), put},
+ loop(NewThing);
+ {get, Pid} ->
+ Pid ! {self(), Thing},
+ loop(Thing)
+ end.
diff --git a/erts/emulator/test/long_timers_test.erl b/erts/emulator/test/long_timers_test.erl
index 7c055a31f9..c9a380a229 100644
--- a/erts/emulator/test/long_timers_test.erl
+++ b/erts/emulator/test/long_timers_test.erl
@@ -27,11 +27,16 @@
%%% Created : 21 Aug 2006 by Rickard Green <[email protected]>
%%%-------------------------------------------------------------------
+-define(HIGH_CPU_INFO, "Ignored due to high CPU utilization.").
+-define(MISSING_CPU_INFO, "Ignored due to missing CPU utilization information.").
-define(MAX_TIMEOUT, 60). % Minutes
--define(MAX_LATE_MS, 15*1000). % Milliseconds
+-define(MAX_LATE_MS, 1000). % Milliseconds
-define(REG_NAME, '___LONG___TIMERS___TEST___SERVER___').
+-define(HIGH_UTIL, 96.0).
+-define(UTIL_INTERVAL, 10000).
+
-define(DRV_NAME, timer_driver).
% First byte in communication with the timer driver
@@ -72,52 +77,149 @@ check_result() ->
receive
{'DOWN', Mon, process, _, Reason} ->
{?REG_NAME, 'DOWN', Reason};
- {result, ?REG_NAME, TORs, Start, End} ->
+ {result, ?REG_NAME, TORs, Start, End, UtilData} ->
erlang:demonitor(Mon),
receive {'DOWN', Mon, _, _, _} -> ok after 0 -> ok end,
stop_node(Node),
- check(TORs, ms((End - Start) - max_late()), ok)
+ Res = check(TORs, Start, End, UtilData, ms((End - Start) - max_late()), ok),
+ io:format("Start = ~p~n End = ~p~n UtilData = ~p~n", [Start, End, UtilData]),
+ Res
end.
+res(New, Old) when New == failed; Old == failed ->
+ failed;
+res(New, Old) when New == missing_cpu_info; Old == missing_cpu_info ->
+ missing_cpu_info;
+res(New, Old) when New == high_cpu; Old == high_cpu ->
+ high_cpu;
+res(New, _Old) ->
+ New.
+
check([#timeout_rec{timeout = Timeout,
type = Type,
timeout_diff = undefined} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
- _Ok) when Timeout < NeedRes ->
- io:format("~p timeout = ~p ms failed! No timeout.~n",
- [Type, Timeout]),
- check(TORs, NeedRes, failed);
+ Ok) when Timeout < NeedRes ->
+ {NewOk, HCPU} = case had_high_cpu_util(Start,
+ Timeout,
+ End - Timeout*1000,
+ UtilData) of
+ yes -> {res(high_cpu, Ok), ?HIGH_CPU_INFO};
+ no -> {res(failed, Ok), ""};
+ missing -> {res(missing_cpu_info, Ok), "FAILED", ?MISSING_CPU_INFO}
+ end,
+ io:format("~p timeout = ~p ms FAILED! No timeout. ~s~n",
+ [Type, Timeout, HCPU]),
+ check(TORs, Start, End, UtilData, NeedRes, NewOk);
check([#timeout_rec{timeout_diff = undefined} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
Ok) ->
- check(TORs, NeedRes, Ok);
+ check(TORs, Start, End, UtilData, NeedRes, Ok);
check([#timeout_rec{timeout = Timeout,
type = Type,
timeout_diff = {error, Reason}} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
_Ok) ->
- io:format("~p timeout = ~p ms failed! exit reason ~p~n",
+ io:format("~p timeout = ~p ms FAILED! exit reason ~p~n",
[Type, Timeout, Reason]),
- check(TORs, NeedRes, failed);
+ check(TORs, Start, End, UtilData, NeedRes, failed);
check([#timeout_rec{timeout = Timeout,
type = Type,
timeout_diff = TimeoutDiff} | TORs],
+ Start,
+ End,
+ UtilData,
NeedRes,
Ok) ->
- {NewOk, SuccessStr} = case ((0 =< TimeoutDiff)
- andalso (TimeoutDiff =< max_late())) of
- true -> {Ok, "succeeded"};
- false -> {failed, "FAILED"}
+ {NewOk, SuccessStr, HCPU} = case {(0 =< TimeoutDiff),
+ (TimeoutDiff =< max_late())} of
+ {true, true} ->
+ {res(ok, Ok), "succeeded", ""};
+ {false, _} ->
+ {res(failed, Ok), "FAILED", ""};
+ _ ->
+ case had_high_cpu_util(Start,
+ Timeout,
+ TimeoutDiff,
+ UtilData) of
+ yes -> {res(high_cpu, Ok), "FAILED", ?HIGH_CPU_INFO};
+ no -> {res(failed, Ok), "FAILED", ""};
+ missing -> {res(missing_cpu_info, Ok), "FAILED", ?MISSING_CPU_INFO}
+ end
end,
- io:format("~s timeout = ~s ms ~s! timeout diff = ~s.~n",
+ io:format("~s timeout = ~s ms ~s! timeout diff = ~s. ~s~n",
[type_str(Type),
time_str(Timeout),
SuccessStr,
- time_str(TimeoutDiff, erlang:convert_time_unit(1, seconds, native))]),
- check(TORs, NeedRes, NewOk);
-check([], _NeedRes, Ok) ->
+ time_str(TimeoutDiff, 1000000),
+ HCPU]),
+ check(TORs, Start, End, UtilData, NeedRes, NewOk);
+check([],_Start,_End,_UtilData,_NeedRes, Ok) ->
Ok.
+% TargetTimeout in ms, other in us.
+had_high_cpu_util(StartTime,
+ TargetTimeout,
+ TimeoutDiff,
+ UtilData) ->
+ TargetTo = StartTime + TargetTimeout*1000,
+ ActTo = TargetTo + TimeoutDiff,
+ hcpu(ActTo, TargetTo, UtilData).
+
+hcpu(_ActTo, _TargetTo, [{UT, 0} | _] = UD) ->
+ missing; %% Util is the integer zero when not supported...
+%% UT2 =:= UT1
+hcpu(ActTo, TargetTo, [{UT, _}, {UT, _} | _] = UD) ->
+ hcpu(ActTo, TargetTo, tl(UD));
+%% UT2 > UT1 > ActTo > TargetTo
+hcpu(ActTo, TargetTo, [{_UT2, _}, {UT1, _} | _] = UD) when UT1 > ActTo ->
+ hcpu(ActTo, TargetTo, tl(UD));
+%% UT2 >= ActTo > TargetTo >= UT1
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _]) when UT2 >= ActTo,
+ TargetTo >= UT1 ->
+ case U >= (((ActTo - TargetTo) / (UT2 - UT1))
+ * (?HIGH_UTIL/100.0)) of
+ true -> yes;
+ false -> no
+ end;
+%% UT2 >= ActTo >= UT1 > TargetTo
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _] = UD) when UT2 >= ActTo,
+ ActTo >= UT1,
+ UT1 > TargetTo ->
+ case U >= (((ActTo - UT1) / (UT2 - UT1))
+ * (?HIGH_UTIL/100.0)) of
+ true -> hcpu(ActTo, TargetTo, tl(UD));
+ false -> no
+ end;
+%% ActTo > UT2 >= TargetTo >= UT1
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _]) when ActTo > UT2,
+ TargetTo >= UT1 ->
+ case U >= (((UT2 - TargetTo) / (UT2 - UT1))
+ * (?HIGH_UTIL/100.0)) of
+ true -> yes;
+ false -> no
+ end;
+%% ActTo > UT2 > UT1 > TargetTo
+hcpu(ActTo, TargetTo,
+ [{UT2, U}, {UT1, _} | _] = UD) when ActTo > UT2,
+ UT1 > TargetTo ->
+ case U >= ?HIGH_UTIL of
+ true -> hcpu(ActTo, TargetTo, tl(UD));
+ false -> no
+ end.
+
type_str(receive_after) -> "receive ... after";
type_str(bif_timer) -> "BIF timer";
type_str(driver) -> "driver".
@@ -142,24 +244,24 @@ unit_str(Res) -> Res.
to_diff(Timeout, Start, Stop) ->
%% 'Timeout' in milli seconds
- %% 'Start', 'Stop', and result in native unit
- (Stop - Start) - erlang:convert_time_unit(Timeout, milli_seconds, native).
+ %% 'Start', 'Stop', and result in micro seconds
+ (Stop - Start) - Timeout*1000.
ms(Time) ->
- erlang:convert_time_unit(Time, native, milli_seconds).
+ erlang:convert_time_unit(Time, microsecond, millisecond).
max_late() ->
- erlang:convert_time_unit(?MAX_LATE_MS, milli_seconds, native).
+ erlang:convert_time_unit(?MAX_LATE_MS, millisecond, microsecond).
receive_after(Timeout) ->
- Start = erlang:monotonic_time(),
+ Start = erlang:monotonic_time(microsecond),
receive
{get_result, ?REG_NAME} ->
?REG_NAME ! #timeout_rec{pid = self(),
type = receive_after,
timeout = Timeout}
after Timeout ->
- Stop = erlang:monotonic_time(),
+ Stop = erlang:monotonic_time(microsecond),
receive
{get_result, ?REG_NAME} ->
?REG_NAME ! #timeout_rec{pid = self(),
@@ -174,7 +276,7 @@ receive_after(Timeout) ->
driver(Timeout) ->
Port = open_port({spawn, ?DRV_NAME},[]),
link(Port),
- Start = erlang:monotonic_time(),
+ Start = erlang:monotonic_time(microsecond),
erlang:port_command(Port, <<?START_TIMER, Timeout:32>>),
receive
{get_result, ?REG_NAME} ->
@@ -182,7 +284,7 @@ driver(Timeout) ->
type = driver,
timeout = Timeout};
{Port,{data,[?TIMER]}} ->
- Stop = erlang:monotonic_time(),
+ Stop = erlang:monotonic_time(microsecond),
unlink(Port),
true = erlang:port_close(Port),
receive
@@ -197,7 +299,7 @@ driver(Timeout) ->
end.
bif_timer(Timeout) ->
- Start = erlang:monotonic_time(),
+ Start = erlang:monotonic_time(microsecond),
Tmr = erlang:start_timer(Timeout, self(), ok),
receive
{get_result, ?REG_NAME} ->
@@ -205,7 +307,7 @@ bif_timer(Timeout) ->
type = bif_timer,
timeout = Timeout};
{timeout, Tmr, ok} ->
- Stop = erlang:monotonic_time(),
+ Stop = erlang:monotonic_time(microsecond),
receive
{get_result, ?REG_NAME} ->
?REG_NAME ! #timeout_rec{pid = self(),
@@ -218,13 +320,22 @@ bif_timer(Timeout) ->
end.
test(Starter, DrvDir, StartDone) ->
+ process_flag(priority, high),
erl_ddll:start(),
ok = load_driver(DrvDir, ?DRV_NAME),
process_flag(trap_exit, true),
register(?REG_NAME, self()),
{group_leader, GL} = process_info(whereis(net_kernel),group_leader),
group_leader(GL, self()),
- Start = erlang:monotonic_time(),
+ try
+ application:start(sasl),
+ application:start(os_mon)
+ catch
+ _ : _ ->
+ ok
+ end,
+ UtilData = new_util(),
+ Start = erlang:monotonic_time(microsecond),
TORs = lists:map(fun (Min) ->
TO = Min*60*1000,
[#timeout_rec{pid = spawn_opt(
@@ -252,16 +363,27 @@ test(Starter, DrvDir, StartDone) ->
lists:seq(1, ?MAX_TIMEOUT)),
FlatTORs = lists:flatten(TORs),
Starter ! StartDone,
- test_loop(FlatTORs, Start).
+ test_loop(FlatTORs, Start, UtilData).
+
+new_util() ->
+ new_util([]).
+
+new_util(UtilData) ->
+ Util = cpu_sup:util(),
+ Time = erlang:monotonic_time(microsecond),
+ [{Time, Util} | UtilData].
-test_loop(TORs, Start) ->
+test_loop(TORs, Start, UtilData) ->
receive
{get_result, ?REG_NAME, Pid} ->
- End = erlang:monotonic_time(),
- Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End},
+ End = erlang:monotonic_time(microsecond),
+ EndUtilData = new_util(UtilData),
+ Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End, EndUtilData},
erl_ddll:unload_driver(?DRV_NAME),
erl_ddll:stop(),
exit(bye)
+ after ?UTIL_INTERVAL ->
+ test_loop(TORs, Start, new_util(UtilData))
end.
get_test_results(TORs) ->
diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl
index 6b7ad836f5..c12f63706a 100644
--- a/erts/emulator/test/lttng_SUITE.erl
+++ b/erts/emulator/test/lttng_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. 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.
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index b3870f0313..5af676c409 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -77,6 +77,7 @@
t_ets/1,
t_dets/1,
t_tracing/1,
+ t_hash_entropy/1,
%% instruction-level tests
t_has_map_fields/1,
@@ -140,6 +141,7 @@ all() -> [t_build_and_match_literals, t_build_and_match_literals_large,
t_pdict,
t_ets,
t_tracing,
+ t_hash_entropy,
%% instruction-level tests
t_has_map_fields,
@@ -3020,6 +3022,39 @@ do_badmap_17(Config) ->
id(I) -> I.
+%% OTP-13763
+t_hash_entropy(Config) when is_list(Config) ->
+ %% entropy bug in 18.3, 19.0
+ M1 = maps:from_list([{#{"id" => I}, ok}||I <- lists:seq(1,50000)]),
+
+ #{ #{"id" => 100} := ok,
+ #{"id" => 200} := ok,
+ #{"id" => 300} := ok,
+ #{"id" => 400} := ok,
+ #{"id" => 500} := ok,
+ #{"id" => 600} := ok,
+ #{"id" => 700} := ok,
+ #{"id" => 800} := ok,
+ #{"id" => 900} := ok,
+ #{"id" => 25061} := ok,
+ #{"id" => 39766} := ok } = M1,
+
+ M0 = maps:from_list([{I,ok}||I <- lists:seq(1,33)]),
+ M2 = maps:from_list([{M0#{"id" => I}, ok}||I <- lists:seq(1,50000)]),
+
+ ok = maps:get(M0#{"id" => 100}, M2),
+ ok = maps:get(M0#{"id" => 200}, M2),
+ ok = maps:get(M0#{"id" => 300}, M2),
+ ok = maps:get(M0#{"id" => 400}, M2),
+ ok = maps:get(M0#{"id" => 500}, M2),
+ ok = maps:get(M0#{"id" => 600}, M2),
+ ok = maps:get(M0#{"id" => 700}, M2),
+ ok = maps:get(M0#{"id" => 800}, M2),
+ ok = maps:get(M0#{"id" => 900}, M2),
+ ok = maps:get(M0#{"id" => 25061}, M2),
+ ok = maps:get(M0#{"id" => 39766}, M2),
+ ok.
+
%% OTP-13146
%% Provoke major GC with a lot of "fat" maps on external format in msg queue
%% causing heap fragments to be allocated.
diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl
index 44e77dfad0..e084b9482d 100644
--- a/erts/emulator/test/message_queue_data_SUITE.erl
+++ b/erts/emulator/test/message_queue_data_SUITE.erl
@@ -200,7 +200,7 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]).
diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl
index 90d2bd8c5d..827ed817cc 100644
--- a/erts/emulator/test/monitor_SUITE.erl
+++ b/erts/emulator/test/monitor_SUITE.erl
@@ -697,7 +697,7 @@ mixer(Config) when is_list(Config) ->
named_down(Config) when is_list(Config) ->
Name = list_to_atom(atom_to_list(?MODULE)
++ "-named_down-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
Prio = process_flag(priority,high),
%% Spawn a bunch of high prio cpu bound processes to prevent
diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl
index 1493e52655..12928ed6d8 100644
--- a/erts/emulator/test/mtx_SUITE.erl
+++ b/erts/emulator/test/mtx_SUITE.erl
@@ -443,7 +443,7 @@ hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) ->
{'DOWN', M, process, P, _} -> ok
end
end, Ps),
- Res = (Stop-Start)/erlang:convert_time_unit(1,seconds,native),
+ Res = (Stop-Start)/erlang:convert_time_unit(1,second,native),
Caller ! {?MODULE, self(), Res}
end,
TP = spawn_link(T),
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index a0e9f1bad6..9c1694fa8a 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -29,6 +29,7 @@
-export([all/0, suite/0,
init_per_testcase/2, end_per_testcase/2,
basic/1, reload/1, upgrade/1, heap_frag/1,
+ t_on_load/1,
types/1, many_args/1, binaries/1, get_string/1, get_atom/1,
maps/1,
api_macros/1,
@@ -68,6 +69,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, reload, upgrade, heap_frag, types, many_args,
+ t_on_load,
binaries, get_string, get_atom, maps, api_macros, from_array,
iolist_as_binary, resource, resource_binary,
resource_takeover, threading, send, send2, send3,
@@ -83,10 +85,19 @@ all() ->
nif_port_command,
nif_snprintf].
+init_per_testcase(t_on_load, Config) ->
+ ets:new(nif_SUITE, [named_table]),
+ Config;
init_per_testcase(_Case, Config) ->
Config.
+end_per_testcase(t_on_load, _Config) ->
+ ets:delete(nif_SUITE),
+ testcase_cleanup();
end_per_testcase(_Func, _Config) ->
+ testcase_cleanup().
+
+testcase_cleanup() ->
P1 = code:purge(nif_mod),
Del = code:delete(nif_mod),
P2 = code:purge(nif_mod),
@@ -174,11 +185,33 @@ upgrade(Config) when is_list(Config) ->
true = erlang:delete_module(nif_mod),
[] = nif_mod_call_history(),
+ %% Repeat upgrade again but from old (deleted) instance
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,9,109}] = nif_mod_call_history(),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+ 1 = nif_mod:lib_version(),
+ [{upgrade,1,10,110},{lib_version,1,11,111}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,1,12,112}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,13,113}] = nif_mod_call_history(),
+
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
+
+
Pid ! die,
{'DOWN', MRef, process, Pid, normal} = receive_any(),
false = check_process_code(Pid, nif_mod),
true = erlang:purge_module(nif_mod),
- [{unload,1,9,109}] = nif_mod_call_history(),
+ [{unload,1,14,114}] = nif_mod_call_history(),
%% Module upgrade with different lib version
{module,nif_mod} = erlang:load_module(nif_mod,Bin),
@@ -215,17 +248,170 @@ upgrade(Config) when is_list(Config) ->
true = erlang:delete_module(nif_mod),
[] = nif_mod_call_history(),
+
+ %% Reverse upgrade but from old (deleted) instance
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ [] = nif_mod_call_history(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,4,204}] = nif_mod_call_history(),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+ 1 = nif_mod:lib_version(),
+ [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
+
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,5,205}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,2,6,206}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
+
+
Pid2 ! die,
{'DOWN', MRef2, process, Pid2, normal} = receive_any(),
false= check_process_code(Pid2, nif_mod),
true = erlang:purge_module(nif_mod),
- [{unload,2,4,204}] = nif_mod_call_history(),
+ [{unload,1,4,104}] = nif_mod_call_history(),
true = lists:member(?MODULE, erlang:system_info(taints)),
true = lists:member(nif_mod, erlang:system_info(taints)),
verify_tmpmem(TmpMem),
ok.
+%% Test loading/upgrade in on_load
+t_on_load(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
+ ensure_lib_loaded(Config),
+
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "nif_mod"),
+ {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors,
+ {d,'USE_ON_LOAD'}]),
+
+ %% Use ETS to tell nif_mod:on_load what to do
+ ets:insert(nif_SUITE, {data_dir, Data}),
+ ets:insert(nif_SUITE, {lib_version, 1}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+
+ {Pid,MRef} = nif_mod:start(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ %% Module upgrade with same lib-version
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ 1 = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{upgrade,1,4,104},{lib_version,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,7,107}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,8,108}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+ %% Repeat upgrade again but from old (deleted) instance
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,1,9,109}] = nif_mod_call_history(),
+ 1 = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,10,110},{lib_version,1,11,111}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,12,112}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,13,113}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+
+ Pid ! die,
+ {'DOWN', MRef, process, Pid, normal} = receive_any(),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,14,114}] = nif_mod_call_history(),
+
+ %% Module upgrade with different lib version
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ {Pid2,MRef2} = nif_mod:start(),
+ 1 = call(Pid2,lib_version),
+ [{lib_version,1,3,103},{lib_version,1,4,104}] = nif_mod_call_history(),
+
+ true = ets:insert(nif_SUITE,{lib_version,2}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,2,1,201}] = nif_mod_call_history(),
+
+ 2 = nif_mod:lib_version(),
+ 1 = call(Pid2,lib_version),
+ [{lib_version,2,2,202},{lib_version,1,5,105}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,6,106}] = nif_mod_call_history(),
+
+ 2 = nif_mod:lib_version(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,3,203},{lib_version,2,4,204}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+ %% Reverse upgrade but from old (deleted) instance
+ ets:insert(nif_SUITE,{lib_version,1}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,1,1,101}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,1,2,102},{lib_version,2,5,205}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,2,6,206}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+
+ Pid2 ! die,
+ {'DOWN', MRef2, process, Pid2, normal} = receive_any(),
+ false= check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,4,104}] = nif_mod_call_history(),
+
+ true = lists:member(?MODULE, erlang:system_info(taints)),
+ true = lists:member(nif_mod, erlang:system_info(taints)),
+ verify_tmpmem(TmpMem),
+ ok.
+
+
%% Test NIF building heap fragments
heap_frag(Config) when is_list(Config) ->
TmpMem = tmpmem(),
@@ -1443,6 +1629,17 @@ otp_9828_loop(Bin, Val) ->
consume_timeslice(Config) when is_list(Config) ->
+ case {erlang:system_info(debug_compiled),
+ erlang:system_info(lock_checking)} of
+ {false, false} ->
+ consume_timeslice_test(Config);
+ {false, true} ->
+ {skipped, "Lock checking enabled"};
+ _ ->
+ {skipped, "Debug compiled"}
+ end.
+
+consume_timeslice_test(Config) when is_list(Config) ->
CONTEXT_REDS = 2000,
Me = self(),
Go = make_ref(),
@@ -1692,7 +1889,7 @@ nif_raise_exceptions(NifFunc) ->
end, ok, ExcTerms).
-define(ERL_NIF_TIME_ERROR, -9223372036854775808).
--define(TIME_UNITS, [seconds, milli_seconds, micro_seconds, nano_seconds]).
+-define(TIME_UNITS, [second, millisecond, microsecond, nanosecond]).
nif_monotonic_time(Config) ->
?ERL_NIF_TIME_ERROR = monotonic_time(invalid_time_unit),
@@ -1758,8 +1955,8 @@ chk_toffs([TU|TUs]) ->
chk_toffs(TUs).
nif_convert_time_unit(Config) ->
- ?ERL_NIF_TIME_ERROR = convert_time_unit(0, seconds, invalid_time_unit),
- ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, seconds),
+ ?ERL_NIF_TIME_ERROR = convert_time_unit(0, second, invalid_time_unit),
+ ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, second),
?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, invalid_time_unit),
lists:foreach(fun (Offset) ->
lists:foreach(fun (Diff) ->
@@ -1808,7 +2005,7 @@ nif_convert_time_unit(Config) ->
ctu_loop(0) ->
ok;
ctu_loop(N) ->
- chk_ctu(erlang:monotonic_time(nano_seconds)),
+ chk_ctu(erlang:monotonic_time(nanosecond)),
ctu_loop(N-1).
chk_ctu(Time) ->
@@ -1823,7 +2020,7 @@ chk_ctu(Time, [FromTU|FromTUs]) ->
chk_ctu(_Time, _FromTU, []) ->
ok;
chk_ctu(Time, FromTU, [ToTU|ToTUs]) ->
- T = erlang:convert_time_unit(Time, nano_seconds, FromTU),
+ T = erlang:convert_time_unit(Time, nanosecond, FromTU),
TE = erlang:convert_time_unit(T, FromTU, ToTU),
TN = convert_time_unit(T, FromTU, ToTU),
case TE =:= TN of
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 13846244d4..f2b1ef9d24 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -38,10 +38,10 @@ static ERL_NIF_TERM atom_self;
static ERL_NIF_TERM atom_ok;
static ERL_NIF_TERM atom_join;
static ERL_NIF_TERM atom_binary_resource_type;
-static ERL_NIF_TERM atom_seconds;
-static ERL_NIF_TERM atom_milli_seconds;
-static ERL_NIF_TERM atom_micro_seconds;
-static ERL_NIF_TERM atom_nano_seconds;
+static ERL_NIF_TERM atom_second;
+static ERL_NIF_TERM atom_millisecond;
+static ERL_NIF_TERM atom_microsecond;
+static ERL_NIF_TERM atom_nanosecond;
typedef struct
@@ -147,10 +147,10 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_ok = enif_make_atom(env,"ok");
atom_join = enif_make_atom(env,"join");
atom_binary_resource_type = enif_make_atom(env,"binary_resource_type");
- atom_seconds = enif_make_atom(env,"seconds");
- atom_milli_seconds = enif_make_atom(env,"milli_seconds");
- atom_micro_seconds = enif_make_atom(env,"micro_seconds");
- atom_nano_seconds = enif_make_atom(env,"nano_seconds");
+ atom_second = enif_make_atom(env,"second");
+ atom_millisecond = enif_make_atom(env,"millisecond");
+ atom_microsecond = enif_make_atom(env,"microsecond");
+ atom_nanosecond = enif_make_atom(env,"nanosecond");
*priv_data = data;
return 0;
@@ -1808,13 +1808,13 @@ static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM
if (argc != 1)
return atom_false;
- if (enif_compare(argv[0], atom_seconds) == 0)
+ if (enif_compare(argv[0], atom_second) == 0)
time_unit = ERL_NIF_SEC;
- else if (enif_compare(argv[0], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[0], atom_millisecond) == 0)
time_unit = ERL_NIF_MSEC;
- else if (enif_compare(argv[0], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[0], atom_microsecond) == 0)
time_unit = ERL_NIF_USEC;
- else if (enif_compare(argv[0], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[0], atom_nanosecond) == 0)
time_unit = ERL_NIF_NSEC;
else
time_unit = 4711; /* invalid time unit */
@@ -1829,13 +1829,13 @@ static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
if (argc != 1)
return atom_false;
- if (enif_compare(argv[0], atom_seconds) == 0)
+ if (enif_compare(argv[0], atom_second) == 0)
time_unit = ERL_NIF_SEC;
- else if (enif_compare(argv[0], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[0], atom_millisecond) == 0)
time_unit = ERL_NIF_MSEC;
- else if (enif_compare(argv[0], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[0], atom_microsecond) == 0)
time_unit = ERL_NIF_USEC;
- else if (enif_compare(argv[0], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[0], atom_nanosecond) == 0)
time_unit = ERL_NIF_NSEC;
else
time_unit = 4711; /* invalid time unit */
@@ -1856,24 +1856,24 @@ static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TE
val = (ErlNifTime) i64;
- if (enif_compare(argv[1], atom_seconds) == 0)
+ if (enif_compare(argv[1], atom_second) == 0)
from = ERL_NIF_SEC;
- else if (enif_compare(argv[1], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[1], atom_millisecond) == 0)
from = ERL_NIF_MSEC;
- else if (enif_compare(argv[1], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[1], atom_microsecond) == 0)
from = ERL_NIF_USEC;
- else if (enif_compare(argv[1], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[1], atom_nanosecond) == 0)
from = ERL_NIF_NSEC;
else
from = 4711; /* invalid time unit */
- if (enif_compare(argv[2], atom_seconds) == 0)
+ if (enif_compare(argv[2], atom_second) == 0)
to = ERL_NIF_SEC;
- else if (enif_compare(argv[2], atom_milli_seconds) == 0)
+ else if (enif_compare(argv[2], atom_millisecond) == 0)
to = ERL_NIF_MSEC;
- else if (enif_compare(argv[2], atom_micro_seconds) == 0)
+ else if (enif_compare(argv[2], atom_microsecond) == 0)
to = ERL_NIF_USEC;
- else if (enif_compare(argv[2], atom_nano_seconds) == 0)
+ else if (enif_compare(argv[2], atom_nanosecond) == 0)
to = ERL_NIF_NSEC;
else
to = 4711; /* invalid time unit */
diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl
index eec1bb8858..1fcc33faa4 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl
+++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl
@@ -29,6 +29,16 @@
-define(nif_stub,nif_stub_error(?LINE)).
+-ifdef(USE_ON_LOAD).
+-on_load(on_load/0).
+
+on_load() ->
+ [{data_dir, Path}] = ets:lookup(nif_SUITE, data_dir),
+ [{lib_version, Ver}] = ets:lookup(nif_SUITE, lib_version),
+ erlang:load_nif(filename:join(Path,libname(Ver)), []).
+
+-endif.
+
load_nif_lib(Config, Ver) ->
load_nif_lib(Config, Ver, []).
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
index 536c91d4ae..af18545bff 100644
--- a/erts/emulator/test/node_container_SUITE.erl
+++ b/erts/emulator/test/node_container_SUITE.erl
@@ -1092,7 +1092,7 @@ wait_until(Pred) ->
get_nodefirstname_string() ->
atom_to_list(?MODULE)
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive])).
diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl
index d1c9648017..f31e73c85b 100644
--- a/erts/emulator/test/num_bif_SUITE.erl
+++ b/erts/emulator/test/num_bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. 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.
@@ -293,6 +293,9 @@ t_round(Config) when is_list(Config) ->
4294967297 = round(id(4294967296.9)),
-4294967296 = -round(id(4294967296.1)),
-4294967297 = -round(id(4294967296.9)),
+
+ 6209607916799025 = round(id(6209607916799025.0)),
+ -6209607916799025 = round(id(-6209607916799025.0)),
ok.
t_trunc(Config) when is_list(Config) ->
diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl
index f91d84beea..ffe7d40139 100644
--- a/erts/emulator/test/old_scheduler_SUITE.erl
+++ b/erts/emulator/test/old_scheduler_SUITE.erl
@@ -309,13 +309,13 @@ receiver(T0, TimeSec, Main, {P1,P1N}, {P2,P2N}) ->
%% uncomment lines below to get life sign (debug)
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 0) ->
- % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, milli_seconds),
+ % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, millisecond),
% erlang:display({round(T/1000),P1Rs,P2Rs}),
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 100000);
receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, C) ->
Remain = Time - erlang:convert_time_unit(erlang:monotonic_time() - T0,
- native, milli_seconds), % test time remaining
+ native, millisecond), % test time remaining
Remain1 = if Remain < 0 ->
0;
true ->
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index ee07699884..3a2243f553 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -83,14 +83,17 @@
bad_port_messages/1,
basic_ping/1,
cd/1,
+ cd_relative/1,
close_deaf_port/1,
count_fds/1,
+ dropped_commands/1,
dying_port/1,
env/1,
eof/1,
exit_status/1,
exit_status_multi_scheduling_block/1,
huge_env/1,
+ pipe_limit_env/1,
input_only/1,
iter_max_ports/1,
line/1,
@@ -102,6 +105,7 @@
mon_port_name_demonitor/1,
mon_port_named/1,
mon_port_origin_dies/1,
+ mon_port_owner_dies/1,
mon_port_pid_demonitor/1,
mon_port_remote_on_remote/1,
mon_port_driver_die/1,
@@ -137,7 +141,7 @@
win_massive_client/1
]).
--export([do_iter_max_ports/2]).
+-export([do_iter_max_ports/2, relative_cd/0]).
%% Internal exports.
-export([tps/3]).
@@ -158,7 +162,7 @@ all() ->
{group, multiple_packets}, parallell, dying_port,
port_program_with_path, open_input_file_port,
open_output_file_port, name1, env, huge_env, bad_env, cd,
- bad_args,
+ cd_relative, pipe_limit_env, bad_args,
exit_status, iter_max_ports, count_fds, t_exit, {group, tps}, line,
stderr_to_stdout, otp_3906, otp_4389, win_massive,
mix_up_ports, otp_5112, otp_5119,
@@ -171,6 +175,7 @@ all() ->
mon_port_remote_on_remote,
mon_port_bad_remote_on_local,
mon_port_origin_dies,
+ mon_port_owner_dies,
mon_port_named,
mon_port_bad_named,
mon_port_pid_demonitor,
@@ -544,6 +549,45 @@ make_dying_port(Config) when is_list(Config) ->
Command = lists:concat([PortTest, " -h0 -d -q"]),
open_port({spawn, Command}, [stream]).
+%% Test that dropped port_commands work correctly.
+%% This used to cause a segfault.
+%%
+%% This testcase creates a port and then lets many processes
+%% do parallel commands to it. After a while it closes the
+%% port and we are trying to catch the race when doing a
+%% command while the port is closing.
+dropped_commands(Config) ->
+ %% Test with output callback
+ dropped_commands(Config, false, {self(), {command, "1"}}),
+ %% Test with outputv callback
+ dropped_commands(Config, true, {self(), {command, "1"}}).
+
+dropped_commands(Config, Outputv, Cmd) ->
+ Path = proplists:get_value(data_dir, Config),
+ os:putenv("ECHO_DRV_USE_OUTPUTV", atom_to_list(Outputv)),
+ ok = load_driver(Path, "echo_drv"),
+ [dropped_commands_test(Cmd) || _ <- lists:seq(1, 100)],
+ timer:sleep(100),
+ erl_ddll:unload_driver("echo_drv"),
+ ok.
+
+dropped_commands_test(Cmd) ->
+ Port = erlang:open_port({spawn_driver, "echo_drv"}, [{parallelism, true}]),
+ spawn_monitor(
+ fun() ->
+ [spawn_link(fun() -> spin(Port, Cmd) end) || _ <- lists:seq(1,8)],
+ timer:sleep(5),
+ port_close(Port),
+ timer:sleep(5),
+ exit(nok)
+ end),
+ receive _M -> timer:sleep(5) end.
+
+spin(P, Cmd) ->
+ P ! Cmd,
+ spin(P, Cmd).
+
+
%% Tests that port program with complete path (but without any
%% .exe extension) can be started, even if there is a file with
%% the same name but without the extension in the same directory.
@@ -972,21 +1016,21 @@ try_bad_env(Env) ->
%% Test that we can handle a very very large environment gracefully.
huge_env(Config) when is_list(Config) ->
ct:timetrap({minutes, 2}),
- Vars = case os:type() of
- {win32,_} -> 500;
- _ ->
- %% We create a huge environment,
- %% 20000 variables is about 25MB
- %% which seems to be the limit on Linux.
- 20000
- end,
+ {Vars, Cmd} = case os:type() of
+ {win32,_} -> {500, "cmd /q /c ls"};
+ _ ->
+ %% We create a huge environment,
+ %% 20000 variables is about 25MB
+ %% which seems to be the limit on Linux.
+ {20000, "ls"}
+ end,
Env = [{[$a + I div (25*25*25*25) rem 25,
$a + I div (25*25*25) rem 25,
$a + I div (25*25) rem 25,
$a+I div 25 rem 25, $a+I rem 25],
lists:duplicate(100,$a+I rem 25)}
|| I <- lists:seq(1,Vars)],
- try erlang:open_port({spawn,"ls"},[exit_status, {env, Env}]) of
+ try erlang:open_port({spawn,Cmd},[exit_status, {env, Env}]) of
P ->
receive
{P, {exit_status,N}} = M ->
@@ -1002,6 +1046,58 @@ huge_env(Config) when is_list(Config) ->
ct:fail("Open port failed ~p:~p",[E,R])
end.
+%% Test to spawn program with command payload buffer
+%% just around pipe capacity (9f779819f6bda734c5953468f7798)
+pipe_limit_env(Config) when is_list(Config) ->
+ Cmd = case os:type() of
+ {win32,_} -> "cmd /q /c true";
+ _ -> "true"
+ end,
+ CmdSize = command_payload_size(Cmd),
+ Limits = [4096, 16384, 65536], % Try a couple of common pipe buffer sizes
+
+ lists:foreach(fun(Lim) ->
+ lists:foreach(fun(L) -> pipe_limit_env_do(L, Cmd, CmdSize)
+ end, lists:seq(Lim-5, Lim+5))
+ end, Limits),
+ ok.
+
+pipe_limit_env_do(Bytes, Cmd, CmdSize) ->
+ case env_of_bytes(Bytes-CmdSize) of
+ [] -> skip;
+ Env ->
+ try erlang:open_port({spawn,Cmd},[exit_status, {env, Env}]) of
+ P ->
+ receive
+ {P, {exit_status,N}} ->
+ %% Bug caused exit_status 150 (EINVAL+128)
+ 0 = N
+ end
+ catch E:R ->
+ %% Have to catch the error here, as printing the stackdump
+ %% in the ct log is way to heavy for some test machines.
+ ct:fail("Open port failed ~p:~p",[E,R])
+ end
+ end.
+
+%% environ format: KEY=VALUE\0
+env_of_bytes(Bytes) when Bytes > 3 ->
+ Env = [{"X",lists:duplicate(Bytes-3, $x)}];
+env_of_bytes(_) -> [].
+
+%% White box assumption about payload written to pipe
+%% for Cmd and current environment (see spawn_start in sys_driver.c)
+command_payload_size(Cmd) ->
+ EnvSize = lists:foldl(fun(E,Acc) -> length(E) + 1 + Acc end,
+ 0, os:getenv()),
+ {ok, PWD} = file:get_cwd(),
+ (4 % buffsz
+ + 4 % flags
+ + 5 + length(Cmd) + 1 % "exec $Cmd"
+ + length(PWD) + 1 % $PWD
+ + 1 % nullbuff
+ + 4 % env_len
+ + EnvSize).
%% Test bad 'args' options.
bad_args(Config) when is_list(Config) ->
@@ -1036,8 +1132,7 @@ cd(Config) when is_list(Config) ->
Cmd = Program ++ " -pz " ++ DataDir ++
" -noshell -s port_test pwd -s erlang halt",
_ = open_port({spawn, Cmd},
- [{cd, TestDir},
- {line, 256}]),
+ [{cd, TestDir}, {line, 256}]),
receive
{_, {data, {eol, String}}} ->
case filename_equal(String, TestDir) of
@@ -1063,7 +1158,74 @@ cd(Config) when is_list(Config) ->
Other3 ->
ct:fail({env, Other3})
end,
- ok.
+
+ InvalidDir = filename:join(DataDir, "invaliddir"),
+ try open_port({spawn, Cmd},
+ [{cd, InvalidDir}, exit_status, {line, 256}]) of
+ _ ->
+ receive
+ {_, {exit_status, _}} ->
+ ok;
+ Other4 ->
+ ct:fail({env, Other4})
+ end
+ catch error:eacces ->
+ %% This happens on Windows
+ ok
+ end,
+
+ %% Check that there are no lingering messages
+ receive
+ Other5 ->
+ ct:fail({env, Other5})
+ after 10 ->
+ ok
+ end.
+
+%% Test that an emulator that has set it's cwd to
+%% something other then when it started, can use
+%% relative {cd,"./"} to open port and that cd will
+%% be relative the new cwd and not the original
+cd_relative(Config) ->
+
+ Program = atom_to_list(lib:progname()),
+ DataDir = proplists:get_value(data_dir, Config),
+ TestDir = filename:join(DataDir, "dir"),
+
+ Cmd = Program ++ " -pz " ++ filename:dirname(code:where_is_file("port_SUITE.beam")) ++
+ " -noshell -s port_SUITE relative_cd -s erlang halt",
+
+ _ = open_port({spawn, Cmd}, [{line, 256}, {cd, TestDir}]),
+
+ receive
+ {_, {data, {eol, String}}} ->
+ case filename_equal(String, TestDir) of
+ true ->
+ ok;
+ false ->
+ ct:fail({cd_relative, String})
+ end;
+ Other ->
+ ct:fail(Other)
+ end.
+
+relative_cd() ->
+
+ Program = atom_to_list(lib:progname()),
+ ok = file:set_cwd(".."),
+ {ok, Cwd} = file:get_cwd(),
+
+ Cmd = Program ++ " -pz " ++ Cwd ++
+ " -noshell -s port_test pwd -s erlang halt",
+
+ _ = open_port({spawn, Cmd}, [{line, 256}, {cd, "./dir"}, exit_status]),
+
+ receive
+ {_, {data, {eol, String}}} ->
+ io:format("~s~n",[String]);
+ Other ->
+ io:format("ERROR: ~p~n",[Other])
+ end.
filename_equal(A, B) ->
case os:type() of
@@ -1867,7 +2029,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
Parent = self(),
io:format("SleepSecs = ~p~n", [SleepSecs]),
PortProg = "sleep " ++ integer_to_list(SleepSecs),
- Start = erlang:monotonic_time(micro_seconds),
+ Start = erlang:monotonic_time(microsecond),
NoProcs = case NoSchedsOnln of
NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS ->
NProcs;
@@ -1941,12 +2103,12 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) ->
receive {P, started, SIds} -> SIds end
end,
Procs),
- StartedTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000,
+ StartedTime = (erlang:monotonic_time(microsecond) - Start)/1000000,
io:format("StartedTime = ~p~n", [StartedTime]),
true = StartedTime < SleepSecs,
erlang:system_flag(multi_scheduling, block),
lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs),
- DoneTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000,
+ DoneTime = (erlang:monotonic_time(microsecond) - Start)/1000000,
io:format("DoneTime = ~p~n", [DoneTime]),
true = DoneTime > SleepSecs,
ok = verify_multi_scheduling_blocked(),
@@ -2170,7 +2332,7 @@ maybe_to_list(List) ->
List.
format({Eol,List}) ->
- io_lib:format("tuple<~w,~s>",[Eol, maybe_to_list(List)]);
+ io_lib:format("tuple<~w,~w>",[Eol, maybe_to_list(List)]);
format(List) when is_list(List) ->
case list_at_least(50, List) of
true ->
@@ -2520,6 +2682,29 @@ mon_port_origin_dies(Config) ->
Port5 ! {self(), {command, <<"1">>}}, % make port quit
ok.
+%% Port and Monitor owner dies before port is closed
+%% This testcase checks for a regression memory leak in erts
+%% when the controlling and monitoring process is the same process
+%% and the process dies
+mon_port_owner_dies(Config) ->
+ Self = self(),
+ Proc = spawn(fun() ->
+ Port = create_port(Config, ["-h1", "-q"]),
+ Self ! {test_started, Port},
+ erlang:monitor(port, Port),
+ receive stop -> ok end
+ end),
+ erlang:monitor(process, Proc), % we want to sync with its death
+ Port = receive {test_started,P} -> P
+ after 1000 -> ?assert(false) end,
+ ?assertMatch({proc_monitors, true, port_monitored_by, true},
+ port_is_monitored(Proc, Port)),
+ Proc ! stop,
+ %% receive from monitor
+ receive ExitP5 -> ?assertMatch({'DOWN', _, process, Proc, _}, ExitP5)
+ after 1000 -> ?assert(false) end,
+ ok.
+
%% Monitor a named port
mon_port_named(Config) ->
Name6 = test_port6,
diff --git a/erts/emulator/test/port_SUITE_data/Makefile.src b/erts/emulator/test/port_SUITE_data/Makefile.src
index fb7685c4b6..3a343e6d17 100644
--- a/erts/emulator/test/port_SUITE_data/Makefile.src
+++ b/erts/emulator/test/port_SUITE_data/Makefile.src
@@ -20,6 +20,12 @@ echo_args@exe@: echo_args@obj@
echo_args@obj@: echo_args.c
$(CC) -c -o echo_args@obj@ $(CFLAGS) echo_args.c
+dead_port@exe@: dead_port@obj@
+ $(LD) $(CROSSLDFLAGS) -o dead_port dead_port@obj@ @LIBS@
+
+dead_port@obj@: dead_port.c
+ $(CC) -c -o dead_port@obj@ $(CFLAGS) dead_port.c
+
port_test.@EMULATOR@: port_test.erl
@erl_name@ -compile port_test
diff --git a/erts/emulator/test/port_SUITE_data/echo_drv.c b/erts/emulator/test/port_SUITE_data/echo_drv.c
index 1d39c6a00c..b4370f6455 100644
--- a/erts/emulator/test/port_SUITE_data/echo_drv.c
+++ b/erts/emulator/test/port_SUITE_data/echo_drv.c
@@ -18,8 +18,11 @@ typedef struct _erl_drv_data EchoDrvData;
static EchoDrvData *echo_drv_start(ErlDrvPort port, char *command);
static void echo_drv_stop(EchoDrvData *data_p);
-static void echo_drv_output(ErlDrvData drv_data, char *buf,
- ErlDrvSizeT len);
+static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len);
+static ErlDrvSSizeT echo_control(ErlDrvData drv_data,
+ unsigned int command, char *buf,
+ ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen);
+static void echo_outputv(ErlDrvData drv_data, ErlIOVec *ev);
static void echo_drv_finish(void);
static ErlDrvEntry echo_drv_entry = {
@@ -32,9 +35,9 @@ static ErlDrvEntry echo_drv_entry = {
"echo_drv",
echo_drv_finish,
NULL, /* handle */
- NULL, /* control */
+ echo_control, /* control */
NULL, /* timeout */
- NULL, /* outputv */
+ echo_outputv, /* outputv */
NULL, /* ready_async */
NULL,
NULL,
@@ -56,6 +59,14 @@ static ErlDrvEntry echo_drv_entry = {
DRIVER_INIT(echo_drv)
{
+ char buf[10];
+ size_t bufsz = sizeof(buf);
+ char *use_outputv;
+ use_outputv = (erl_drv_getenv("ECHO_DRV_USE_OUTPUTV", buf, &bufsz) == 0
+ ? buf
+ : "false");
+ if (strcmp(use_outputv, "true") != 0)
+ echo_drv_entry.outputv = NULL;
return &echo_drv_entry;
}
@@ -87,3 +98,15 @@ static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
static void echo_drv_finish() {
}
+
+static ErlDrvSSizeT echo_control(ErlDrvData drv_data,
+ unsigned int command, char *buf,
+ ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)
+{
+ return 0;
+}
+
+static void echo_outputv(ErlDrvData drv_data, ErlIOVec *ev)
+{
+ return;
+}
diff --git a/erts/emulator/test/port_SUITE_data/port_test.c b/erts/emulator/test/port_SUITE_data/port_test.c
index cc3ebdf0f8..e199a0fc13 100644
--- a/erts/emulator/test/port_SUITE_data/port_test.c
+++ b/erts/emulator/test/port_SUITE_data/port_test.c
@@ -10,6 +10,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <ctype.h>
#ifndef __WIN32__
#include <unistd.h>
@@ -33,7 +34,7 @@
exit(1); \
}
-#define MAIN(argc, argv) main(argc, argv)
+#define ASSERT(e) ((void) ((e) ? 1 : abort()))
extern int errno;
@@ -105,9 +106,7 @@ int err;
#endif
-MAIN(argc, argv)
-int argc;
-char *argv[];
+int main(int argc, char *argv[])
{
int ret, fd_count;
if((port_data = (PORT_TEST_DATA *) malloc(sizeof(PORT_TEST_DATA))) == NULL) {
@@ -377,9 +376,11 @@ write_reply(buf, size)
int size; /* Size of buffer to send. */
{
int n; /* Temporary to hold size. */
+ int rv;
if (port_data->slow_writes <= 0) { /* Normal, "fast", write. */
- write(port_data->fd_to_erl, buf, size);
+ rv = write(port_data->fd_to_erl, buf, size);
+ ASSERT(rv == size);
} else {
/*
* Write chunks with delays in between.
@@ -387,7 +388,8 @@ write_reply(buf, size)
while (size > 0) {
n = size > port_data->slow_writes ? port_data->slow_writes : size;
- write(port_data->fd_to_erl, buf, n);
+ rv = write(port_data->fd_to_erl, buf, n);
+ ASSERT(rv == n);
size -= n;
buf += n;
if (size)
@@ -558,7 +560,7 @@ char* spec; /* Specification for reply. */
buf = (char *) malloc(total_size);
if (buf == NULL) {
fprintf(stderr, "%s: insufficent memory for reply buffer of size %d\n",
- port_data->progname, total_size);
+ port_data->progname, (int)total_size);
exit(1);
}
diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl
index 5d9a75bcd3..03efdc15db 100644
--- a/erts/emulator/test/port_trace_SUITE.erl
+++ b/erts/emulator/test/port_trace_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2012. 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.
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 b545523192..20ec33a594 100644
--- a/erts/emulator/test/port_trace_SUITE_data/echo_drv.c
+++ b/erts/emulator/test/port_trace_SUITE_data/echo_drv.c
@@ -2,23 +2,30 @@
#include "erl_driver.h"
#include <errno.h>
#include <string.h>
+#include <assert.h>
/* -------------------------------------------------------------------------
** Data types
**/
+struct my_thread {
+ struct my_thread* next;
+ ErlDrvTid tid;
+};
typedef struct _erl_drv_data {
ErlDrvPort erlang_port;
ErlDrvTermData caller;
+ struct my_thread* threads;
} EchoDrvData;
struct remote_send_term {
- char *buf;
- int len;
+ struct my_thread thread;
ErlDrvTermData port;
ErlDrvTermData caller;
+ int len;
+ char buf[1]; /* buf[len] */
};
#define ECHO_DRV_NOOP 0
@@ -86,7 +93,7 @@ static ErlDrvEntry echo_drv_entry = {
NULL
};
-static void send_term_thread(void *);
+static void* send_term_thread(void *);
/* -------------------------------------------------------------------------
** Entry functions
@@ -111,10 +118,22 @@ static EchoDrvData *echo_drv_start(ErlDrvPort port, char *command)
EchoDrvData *echo_drv_data_p = driver_alloc(sizeof(EchoDrvData));
echo_drv_data_p->erlang_port = port;
echo_drv_data_p->caller = driver_caller(port);
+ echo_drv_data_p->threads = NULL;
return echo_drv_data_p;
}
-static void echo_drv_stop(EchoDrvData *data_p) {
+static void echo_drv_stop(EchoDrvData *data_p)
+{
+ struct my_thread* thr = data_p->threads;
+
+ while (thr) {
+ struct my_thread* next = thr->next;
+ void* exit_value;
+ int ret = erl_drv_thread_join(thr->tid, &exit_value);
+ assert(ret == 0 && exit_value == NULL);
+ driver_free(thr);
+ thr = next;
+ }
driver_free(data_p);
}
@@ -212,14 +231,14 @@ static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
}
case ECHO_DRV_REMOTE_SEND_TERM:
{
- ErlDrvTid tid;
- struct remote_send_term *t = malloc(sizeof(struct remote_send_term));
+ struct remote_send_term *t = driver_alloc(sizeof(struct remote_send_term) + len);
t->len = len-1;
- t->buf = malloc(len-1);
t->port = driver_mk_port(port);
t->caller = data_p->caller;
memcpy(t->buf, buf+1, t->len);
- erl_drv_thread_create("tmp_thread", &tid, send_term_thread, t, NULL);
+ erl_drv_thread_create("tmp_thread", &t->thread.tid, send_term_thread, t, NULL);
+ t->thread.next = data_p->threads;
+ data_p->threads = &t->thread;
break;
}
case ECHO_DRV_SAVE_CALLER:
@@ -262,7 +281,7 @@ static ErlDrvSSizeT echo_drv_call(ErlDrvData drv_data,
return len-command;
}
-static void send_term_thread(void *a)
+static void* send_term_thread(void *a)
{
struct remote_send_term *t = (struct remote_send_term*)a;
ErlDrvTermData term[] = {
@@ -273,5 +292,5 @@ static void send_term_thread(void *a)
ERL_DRV_TUPLE, 3};
erl_drv_send_term(t->port, t->caller,
term, sizeof(term) / sizeof(ErlDrvTermData));
- return;
+ return NULL;
}
diff --git a/erts/emulator/test/prim_eval_SUITE.erl b/erts/emulator/test/prim_eval_SUITE.erl
new file mode 100644
index 0000000000..3f4965f96d
--- /dev/null
+++ b/erts/emulator/test/prim_eval_SUITE.erl
@@ -0,0 +1,78 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. 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(prim_eval_SUITE).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
+ init_per_group/2, end_per_group/2]).
+
+-export(['ERL-365'/1]).
+
+init_per_testcase(_Case, Config) ->
+ Config.
+
+end_per_testcase(_Case, _Config) ->
+ ok.
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,1}}].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+all() ->
+ ['ERL-365'].
+
+'ERL-365'(Config) when is_list(Config) ->
+ %% def_arg_reg[0] is used for storage of timeout instruction
+ %% when a 'receive after' is executed. When a process was
+ %% scheduled out inside prim_eval:'receive'/0 due to a function
+ %% call, def_arg_reg[0] was overwritten due to storage of live
+ %% registers.
+ P = spawn_link(fun () ->
+ prim_eval:'receive'(fun (_M) ->
+ erlang:bump_reductions((1 bsl 27)-1),
+ id(true),
+ nomatch
+ end,
+ 200)
+ end),
+ receive after 100 -> ok end,
+ P ! {wont, match},
+ receive after 200 -> ok end,
+ ok.
+
+
+
+id(X) ->
+ X.
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index dae8990f56..ab1587a90c 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. 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.
@@ -372,7 +372,7 @@ eat_high(Low) ->
process_flag(priority, high),
receive after 1000 -> ok end,
exit(Low, {you, are, dead}),
- loop(erlang:monotonic_time() + erlang:convert_time_unit(5,seconds,native)).
+ loop(erlang:monotonic_time() + erlang:convert_time_unit(5,second,native)).
%% Busy loop for 5 seconds.
@@ -437,11 +437,22 @@ t_process_info(Config) when is_list(Config) ->
verify_loc(Line2, Res2),
pi_stacktrace([{?MODULE,t_process_info,1,?LINE}]),
+ verify_stacktrace_depth(),
+
Gleader = group_leader(),
{group_leader, Gleader} = process_info(self(), group_leader),
{'EXIT',{badarg,_Info}} = (catch process_info('not_a_pid')),
ok.
+verify_stacktrace_depth() ->
+ CS = current_stacktrace,
+ OldDepth = erlang:system_flag(backtrace_depth, 0),
+ {CS,[]} = erlang:process_info(self(), CS),
+ _ = erlang:system_flag(backtrace_depth, 8),
+ {CS,[{?MODULE,verify_stacktrace_depth,0,_},_|_]} =
+ erlang:process_info(self(), CS),
+ _ = erlang:system_flag(backtrace_depth, OldDepth).
+
pi_stacktrace(Expected0) ->
{Line,Res} = {?LINE,erlang:process_info(self(), current_stacktrace)},
{current_stacktrace,Stack} = Res,
@@ -1611,6 +1622,7 @@ spawn_initial_hangarounds(_Cleaner, NP, Max, Len, HAs) when NP > Max ->
{Len, HAs};
spawn_initial_hangarounds(Cleaner, NP, Max, Len, HAs) ->
Skip = 30,
+ wait_for_proc_slots(Skip+3),
HA1 = spawn_opt(?MODULE, hangaround, [Cleaner, initial_hangaround],
[{priority, low}]),
HA2 = spawn_opt(?MODULE, hangaround, [Cleaner, initial_hangaround],
@@ -1620,6 +1632,15 @@ spawn_initial_hangarounds(Cleaner, NP, Max, Len, HAs) ->
spawn_drop(Skip),
spawn_initial_hangarounds(Cleaner, NP+Skip, Max, Len+3, [HA1,HA2,HA3|HAs]).
+wait_for_proc_slots(MinFreeSlots) ->
+ case erlang:system_info(process_limit) - erlang:system_info(process_count) of
+ FreeSlots when FreeSlots < MinFreeSlots ->
+ receive after 10 -> ok end,
+ wait_for_proc_slots(MinFreeSlots);
+ _FreeSlots ->
+ ok
+ end.
+
spawn_drop(N) when N =< 0 ->
ok;
spawn_drop(N) ->
@@ -2376,7 +2397,7 @@ no_priority_inversion2(Config) when is_list(Config) ->
[{priority, max}, monitor, link])
end,
lists:seq(1, 2*erlang:system_info(schedulers))),
- receive after 500 -> ok end,
+ receive after 2000 -> ok end,
{PL, ML} = spawn_opt(fun () ->
tok_loop()
end,
@@ -2567,7 +2588,7 @@ start_node(Config, Args) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
diff --git a/erts/emulator/test/ref_SUITE.erl b/erts/emulator/test/ref_SUITE.erl
index 5f519d522e..74df857c65 100644
--- a/erts/emulator/test/ref_SUITE.erl
+++ b/erts/emulator/test/ref_SUITE.erl
@@ -22,6 +22,7 @@
-export([all/0, suite/0]).
-export([wrap_1/1]).
+-export([compare_list/1, compare_ets/1]).
-export([loop_ref/1]).
@@ -32,7 +33,7 @@ suite() ->
{timetrap, {minutes, 2}}].
all() ->
- [wrap_1].
+ [wrap_1, compare_list, compare_ets].
%% Check that refs don't wrap around easily.
wrap_1(Config) when is_list(Config) ->
@@ -53,3 +54,28 @@ loop_ref(Parent) ->
loop_ref(R, R, _) -> ok;
loop_ref(R0, _, N) ->
loop_ref(R0, make_ref(), N+1).
+
+%% Check that ref ordering works
+compare_list(Config) when is_list(Config) ->
+ %% Although this test uses external refs, it would apply the same to plain refs
+ ExtRef1 = <<131,114,0,3,100,0,3,110,64,98,3, 0,0,173,156, 0,216,0,4, 0,0,0,0>>,
+ ExtRef2 = <<131,114,0,3,100,0,3,110,64,98,3, 0,1,31,27, 129,4,0,1, 0,0,0,0>>,
+
+ Ref1 = binary_to_term(ExtRef1), %% #Ref<[email protected]>
+ Ref2 = binary_to_term(ExtRef2), %% #Ref<[email protected]>
+ OrderedList = [Ref1, Ref2],
+ OrderedList = lists:sort(OrderedList),
+ ok.
+
+%% This is the scarier case since it makes terms "invisible" in ets or Mnesia
+%% (the underlying fault cause is the same as compare_list/1)
+compare_ets(Config) when is_list(Config) ->
+ W2s = [610350147,899574699,2994196869,686384822,2397690439, 923302211],
+ ExtRefBase = <<131,114,0,3,100,0,3,110,64,98,3>>,
+ ExtRefs = [<<ExtRefBase/binary, 1:32, W2:32, 0:32>> || W2 <- W2s],
+ Refs = [binary_to_term(Bin) || Bin <- ExtRefs],
+
+ Ets = ets:new(refbug, [ordered_set]),
+ ets:insert(Ets, [{Ref,Ref} || Ref <- Refs]),
+ 0 = length([R || R <- ets:tab2list(Ets), ets:lookup(Ets, element(1,R)) == []]),
+ ok.
diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl
index f18d79d770..3aee15a8fc 100644
--- a/erts/emulator/test/scheduler_SUITE.erl
+++ b/erts/emulator/test/scheduler_SUITE.erl
@@ -793,13 +793,13 @@ update_cpu_info(Config) when is_list(Config) ->
io:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
[OldAff, OldOnline, erlang:system_info(scheduler_bindings)]),
case {erlang:system_info(logical_processors_available), OldAff} of
- {Avail, _} when Avail == unknown; OldAff == unknown ->
+ {Avail, _} when Avail == unknown; OldAff == unknown; OldAff == 1 ->
%% Nothing much to test; just a smoke test
case erlang:system_info(update_cpu_info) of
unchanged -> ok;
changed -> ok
end;
- _ ->
+ {Avail, _} ->
try
adjust_schedulers_online(),
case erlang:system_info(schedulers_online) of
@@ -810,7 +810,7 @@ update_cpu_info(Config) when is_list(Config) ->
%% unset least significant bit
Aff = (OldAff band (OldAff - 1)),
set_affinity_mask(Aff),
- Onln1 = Onln0 - 1,
+ Onln1 = Avail - 1,
case adjust_schedulers_online() of
{Onln0, Onln1} ->
Onln1 = erlang:system_info(schedulers_online),
@@ -1072,20 +1072,27 @@ scheduler_threads(Config) when is_list(Config) ->
{Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"),
%% Configure 2x scheduler threads only
{TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"),
- %% Test resetting the scheduler counts
- ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
- {Sched, SchedOnln, _} = get_sstate(Config, ResetCmd),
- %% Test negative +S settings, but only for SMP-enabled emulators
- case SmpSupport of
- false -> ok;
- true ->
- SchedMinus1 = Sched-1,
- SchedOnlnMinus1 = SchedOnln-1,
- {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
- {Sched, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
- {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1")
- end,
- ok.
+ case {erlang:system_info(logical_processors),
+ erlang:system_info(logical_processors_available)} of
+ {LProc, LProcAvail} when is_integer(LProc), is_integer(LProcAvail) ->
+ %% Test resetting the scheduler counts
+ ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
+ {LProc, LProcAvail, _} = get_sstate(Config, ResetCmd),
+ %% Test negative +S settings, but only for SMP-enabled emulators
+ case {SmpSupport, LProc > 1, LProcAvail > 1} of
+ {true, true, true} ->
+ SchedMinus1 = LProc-1,
+ SchedOnlnMinus1 = LProcAvail-1,
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
+ {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"),
+ ok;
+ _ ->
+ {comment, "Skipped reduced amount of schedulers test due to too few logical processors"}
+ end;
+ _ -> %% Skipped when missing info about logical processors...
+ {comment, "Skipped reset amount of schedulers test, and reduced amount of schedulers test due to too unknown amount of logical processors"}
+ end.
dirty_scheduler_threads(Config) when is_list(Config) ->
SmpSupport = erlang:system_info(smp_support),
@@ -1312,11 +1319,33 @@ scheduler_suspend_test(Config, Schedulers) ->
true ->
ok
end,
- erlang:system_info(schedulers_state)
+ until(fun () ->
+ {_A, B, C} = erlang:system_info(
+ schedulers_state),
+ B == C
+ end,
+ erlang:monotonic_time()
+ + erlang:convert_time_unit(1,
+ seconds,
+ native)),
+ erlang:system_info(schedulers_state)
end]),
stop_node(Node),
ok.
-
+
+until(Pred, MaxTime) ->
+ case Pred() of
+ true ->
+ true;
+ false ->
+ case erlang:monotonic_time() > MaxTime of
+ true ->
+ false;
+ false ->
+ receive after 100 -> ok end,
+ until(Pred, MaxTime)
+ end
+ end.
sst0_loop(0) ->
ok;
@@ -1978,10 +2007,10 @@ do_it(Tracer, Low, Normal, High, Max) ->
do_it(Tracer, Low, Normal, High, Max, RedsPerSchedLimit) ->
OldPrio = process_flag(priority, max),
go_work(Low, Normal, High, Max),
- StartWait = erlang:monotonic_time(milli_seconds),
+ StartWait = erlang:monotonic_time(millisecond),
%% Give the emulator a chance to balance the load...
wait_balance(5),
- EndWait = erlang:monotonic_time(milli_seconds),
+ EndWait = erlang:monotonic_time(millisecond),
BalanceWait = EndWait-StartWait,
erlang:display({balance_wait, BalanceWait}),
Timeout = (15 - 4)*60*1000 - BalanceWait,
@@ -2181,7 +2210,7 @@ start_node(Config, Args) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl
index 0b11fa13f5..7e516176f7 100644
--- a/erts/emulator/test/signal_SUITE.erl
+++ b/erts/emulator/test/signal_SUITE.erl
@@ -484,7 +484,7 @@ repeat(Fun, N) when is_integer(N) ->
start_node(Config) ->
Name = list_to_atom(atom_to_list(?MODULE)
++ "-" ++ atom_to_list(proplists:get_value(testcase, Config))
- ++ "-" ++ integer_to_list(erlang:system_time(seconds))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
++ "-" ++ integer_to_list(erlang:unique_integer([positive]))),
Pa = filename:dirname(code:which(?MODULE)),
test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]).
diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl
index 042c7225d5..5eccdc562b 100644
--- a/erts/emulator/test/smoke_test_SUITE.erl
+++ b/erts/emulator/test/smoke_test_SUITE.erl
@@ -152,7 +152,7 @@ start_node(Config, Args) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
Opts = [{args, "-pa "++Pa++" "++Args}],
diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl
index 71ef003b25..a1f12ba93c 100644
--- a/erts/emulator/test/statistics_SUITE.erl
+++ b/erts/emulator/test/statistics_SUITE.erl
@@ -129,11 +129,15 @@ do_runtime_update(0) ->
{comment,"Never close enough"};
do_runtime_update(N) ->
{T1,Diff0} = statistics(runtime),
- spawn_link(fun cpu_heavy/0),
+ {CPUHog, CPUHogMon} = spawn_opt(fun cpu_heavy/0,[link,monitor]),
receive after 1000 -> ok end,
{T2,Diff} = statistics(runtime),
+ unlink(CPUHog),
+ exit(CPUHog, kill),
+
true = is_integer(T1+T2+Diff0+Diff),
io:format("T1 = ~p, T2 = ~p, Diff = ~p, T2-T1 = ~p", [T1,T2,Diff,T2-T1]),
+ receive {'DOWN',CPUHogMon,process,CPUHog,_} -> ok end,
if
T2 - T1 =:= Diff, 900 =< Diff, Diff =< 1500 -> ok;
true -> do_runtime_update(N-1)
@@ -311,8 +315,17 @@ scheduler_wall_time(Config) when is_list(Config) ->
true -> exit({fullload, FullLoad})
end,
- [exit(Pid, kill) || Pid <- [P1|HalfHogs++LastHogs]],
+ KillHog = fun (HP) ->
+ HPM = erlang:monitor(process, HP),
+ exit(HP, kill),
+ receive
+ {'DOWN', HPM, process, HP, killed} ->
+ ok
+ end
+ end,
+ [KillHog(Pid) || Pid <- [P1|HalfHogs++LastHogs]],
AfterLoad = get_load(),
+ io:format("AfterLoad=~p~n", [AfterLoad]),
{false,_} = {lists:any(fun(Load) -> Load > 25 end, AfterLoad),AfterLoad},
true = erlang:system_flag(scheduler_wall_time, false)
after
diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl
index f31d474c20..a4aedb31f6 100644
--- a/erts/emulator/test/system_info_SUITE.erl
+++ b/erts/emulator/test/system_info_SUITE.erl
@@ -508,7 +508,7 @@ start_node(Config, Envs) when is_list(Config) ->
++ "-"
++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(erlang:system_time(seconds))
+ ++ integer_to_list(erlang:system_time(second))
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))),
test_server:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]).
diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl
index 87b8c62cfa..9501569814 100644
--- a/erts/emulator/test/time_SUITE.erl
+++ b/erts/emulator/test/time_SUITE.erl
@@ -295,7 +295,7 @@ timestamp(Config) when is_list(Config) ->
os_system_time_offset() ->
erlang:convert_time_unit(os:system_time() - erlang:monotonic_time(),
- native, micro_seconds).
+ native, microsecond).
had_time_warp(Secs) ->
had_time_warp(os_system_time_offset(), Secs).
@@ -488,12 +488,12 @@ check_time_warp_mode(Config, TimeCorrection, TimeWarpMode) ->
MonotonicTimeUnit = rpc:call(Node,
erlang,
convert_time_unit,
- [1, seconds, native]),
+ [1, second, native]),
UpMilliSeconds = erlang:convert_time_unit(MonotonicTime - StartTime,
MonotonicTimeUnit,
- milli_seconds),
+ millisecond),
io:format("UpMilliSeconds=~p~n", [UpMilliSeconds]),
- End = erlang:monotonic_time(milli_seconds),
+ End = erlang:monotonic_time(millisecond),
stop_node(Node),
try
true = (UpMilliSeconds > (98*MonotonicityTimeout) div 100),
@@ -810,10 +810,10 @@ do_check_erlang_timestamp(Done, Mon, TO) ->
MaxMon = erlang:monotonic_time(),
TsMin = erlang:convert_time_unit(MinMon+TO,
native,
- micro_seconds),
+ microsecond),
TsMax = erlang:convert_time_unit(MaxMon+TO,
native,
- micro_seconds),
+ microsecond),
TsTime = (MegaSec*1000000+Sec)*1000000+MicroSec,
case (TsMin =< TsTime) andalso (TsTime =< TsMax) of
true ->
diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl
index a5f11bd959..7cbd93a0f3 100644
--- a/erts/emulator/test/timer_bif_SUITE.erl
+++ b/erts/emulator/test/timer_bif_SUITE.erl
@@ -74,7 +74,7 @@ all() ->
%% Basic start_timer/3 functionality
start_timer_1(Config) when is_list(Config) ->
Ref1 = erlang:start_timer(1000, self(), plopp),
- ok = get(1100, {timeout, Ref1, plopp}),
+ ok = get(1400, {timeout, Ref1, plopp}),
false = erlang:read_timer(Ref1),
false = erlang:cancel_timer(Ref1),
@@ -83,12 +83,12 @@ start_timer_1(Config) when is_list(Config) ->
Ref2 = erlang:start_timer(1000, self(), plapp),
Left2 = erlang:cancel_timer(Ref2),
UpperLimit = 1000,
- true = (Left2 > 900) and (Left2 =< UpperLimit),
+ true = (Left2 > 600) and (Left2 =< UpperLimit),
empty = get_msg(),
false = erlang:cancel_timer(Ref2),
Ref3 = erlang:start_timer(1000, self(), plopp),
- no_message = get(900, {timeout, Ref3, plopp}),
+ no_message = get(600, {timeout, Ref3, plopp}),
ok.
%% Basic send_after/3 functionality
@@ -489,7 +489,7 @@ registered_process(Config) when is_list(Config) ->
same_time_yielding(Config) when is_list(Config) ->
Mem = mem(),
SchdlrsOnln = erlang:system_info(schedulers_online),
- Tmo = erlang:monotonic_time(milli_seconds) + 3000,
+ Tmo = erlang:monotonic_time(millisecond) + 3000,
Tmrs = lists:map(fun (I) ->
process_flag(scheduler, (I rem SchdlrsOnln) + 1),
erlang:start_timer(Tmo, self(), hej, [{abs, true}])
@@ -497,7 +497,7 @@ same_time_yielding(Config) when is_list(Config) ->
lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)),
true = mem_larger_than(Mem),
lists:foreach(fun (Tmr) -> receive {timeout, Tmr, hej} -> ok end end, Tmrs),
- Done = erlang:monotonic_time(milli_seconds),
+ Done = erlang:monotonic_time(millisecond),
true = Done >= Tmo,
case erlang:system_info(build_type) of
opt -> true = Done < Tmo + 200;
@@ -517,10 +517,10 @@ same_time_yielding_with_cancel_other(Config) when is_list(Config) ->
do_cancel_tmrs(Tmo, Tmrs, Tester) ->
BeginCancel = erlang:convert_time_unit(Tmo,
- milli_seconds,
- micro_seconds) - 100,
+ millisecond,
+ microsecond) - 100,
busy_wait_until(fun () ->
- erlang:monotonic_time(micro_seconds) >= BeginCancel
+ erlang:monotonic_time(microsecond) >= BeginCancel
end),
lists:foreach(fun (Tmr) ->
erlang:cancel_timer(Tmr,
@@ -535,7 +535,7 @@ do_cancel_tmrs(Tmo, Tmrs, Tester) ->
same_time_yielding_with_cancel_test(Other, Accessor) ->
Mem = mem(),
SchdlrsOnln = erlang:system_info(schedulers_online),
- Tmo = erlang:monotonic_time(milli_seconds) + 3000,
+ Tmo = erlang:monotonic_time(millisecond) + 3000,
Tester = self(),
Cancelor = case Other of
false ->
@@ -656,7 +656,7 @@ get_msg() ->
start_slave() ->
Pa = filename:dirname(code:which(?MODULE)),
Name = atom_to_list(?MODULE)
- ++ "-" ++ integer_to_list(erlang:system_time(seconds))
+ ++ "-" ++ integer_to_list(erlang:system_time(second))
++ "-" ++ integer_to_list(erlang:unique_integer([positive])),
{ok, Node} = test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]),
Node.
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index da6a6bdea4..f846b0f4b9 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -30,7 +30,7 @@
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,
+ new_clear/1, existing_clear/1, tracer_die/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,
@@ -54,7 +54,7 @@ all() ->
send_trace, procs_trace, dist_procs_trace, suspend,
mutual_suspend, suspend_exit, suspender_exit,
suspend_system_limit, suspend_opts, suspend_waiting,
- new_clear, existing_clear, set_on_spawn,
+ new_clear, existing_clear, tracer_die, set_on_spawn,
set_on_first_spawn, set_on_link, set_on_first_link,
system_monitor_args,
more_system_monitor_args, system_monitor_long_gc_1,
@@ -1636,6 +1636,34 @@ existing_clear(Config) when is_list(Config) ->
ok.
+%% Test that erlang:trace/3 can be called on processes where the
+%% tracer has died. OTP-13928
+tracer_die(Config) when is_list(Config) ->
+ Proc = spawn(fun receiver/0),
+
+ Tracer = spawn(fun receiver/0),
+ timer:sleep(1),
+ N = erlang:trace(existing, true, [send, {tracer, Tracer}]),
+ {flags, [send]} = erlang:trace_info(Proc, flags),
+ {tracer, Tracer} = erlang:trace_info(Proc, tracer),
+ exit(Tracer, die),
+
+ Tracer2 = spawn(fun receiver/0),
+ timer:sleep(1),
+ N = erlang:trace(existing, true, [send, {tracer, Tracer2}]),
+ {flags, [send]} = erlang:trace_info(Proc, flags),
+ {tracer, Tracer2} = erlang:trace_info(Proc, tracer),
+ exit(Tracer2, die),
+
+ Tracer3 = spawn(fun receiver/0),
+ timer:sleep(1),
+ 1 = erlang:trace(Proc, true, [send, {tracer, Tracer3}]),
+ {flags, [send]} = erlang:trace_info(Proc, flags),
+ {tracer, Tracer3} = erlang:trace_info(Proc, tracer),
+ exit(Tracer3, die),
+
+ ok.
+
%% Test that an invalid flag cause badarg
bad_flag(Config) when is_list(Config) ->
%% A bad flag could deadlock the SMP emulator in erts-5.5
diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl
index 491b37ae46..f60c777ba1 100644
--- a/erts/emulator/test/trace_bif_SUITE.erl
+++ b/erts/emulator/test/trace_bif_SUITE.erl
@@ -289,9 +289,9 @@ receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}, PrevTs, TsTy
make_ts(timestamp) ->
erlang:now();
make_ts(monotonic_timestamp) ->
- erlang:monotonic_time(nano_seconds);
+ erlang:monotonic_time(nanosecond);
make_ts(strict_monotonic_timestamp) ->
- MT = erlang:monotonic_time(nano_seconds),
+ MT = erlang:monotonic_time(nanosecond),
UMI = erlang:unique_integer([monotonic]),
{MT, UMI}.
diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl
index 6582ad134b..26f96a1766 100644
--- a/erts/emulator/test/trace_call_time_SUITE.erl
+++ b/erts/emulator/test/trace_call_time_SUITE.erl
@@ -294,7 +294,7 @@ combo(Config) when is_list(Config) ->
T0 = erlang:monotonic_time(),
with_bif(Nbc),
T1 = erlang:monotonic_time(),
- TimeB = erlang:convert_time_unit(T1-T0, native, micro_seconds),
+ TimeB = erlang:convert_time_unit(T1-T0, native, microsecond),
%%
List = collect(100),
@@ -655,13 +655,13 @@ execute(Pids, Mfa) when is_list(Pids) ->
[P ! {self(), execute, Mfa} || P <- Pids],
As = [receive {P, answer, Answer} -> Answer end || P <- Pids],
T1 = erlang:monotonic_time(),
- {As, erlang:convert_time_unit(T1-T0, native, micro_seconds)};
+ {As, erlang:convert_time_unit(T1-T0, native, microsecond)};
execute(P, Mfa) ->
T0 = erlang:monotonic_time(),
P ! {self(), execute, Mfa},
A = receive {P, answer, Answer} -> Answer end,
T1 = erlang:monotonic_time(),
- {A, erlang:convert_time_unit(T1-T0, native, micro_seconds)}.
+ {A, erlang:convert_time_unit(T1-T0, native, microsecond)}.
diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl
index 74c05f24e0..c297acd78b 100644
--- a/erts/emulator/test/trace_local_SUITE.erl
+++ b/erts/emulator/test/trace_local_SUITE.erl
@@ -439,6 +439,14 @@ return_test() ->
?RT(?MODULE,slave,2),
shutdown(),
?NM,
+
+ %% Test a regression where turning off return_to tracing
+ %% on yourself would cause a segfault.
+ Pid = setup([call,return_to]),
+ erlang:trace_pattern({'_','_','_'},[],[local]),
+ apply_slave(erlang,trace,[Pid, false, [all]]),
+ shutdown(),
+
ok.
on_and_off_test() ->
diff --git a/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl
index 9eb55c9af3..730c43d8c2 100644
--- a/erts/emulator/test/tracer_SUITE.erl
+++ b/erts/emulator/test/tracer_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/tracer_SUITE_data/tracer_test.c b/erts/emulator/test/tracer_SUITE_data/tracer_test.c
index a26bb33600..d9543b7ab9 100644
--- a/erts/emulator/test/tracer_SUITE_data/tracer_test.c
+++ b/erts/emulator/test/tracer_SUITE_data/tracer_test.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2009-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.
diff --git a/erts/emulator/test/tracer_test.erl b/erts/emulator/test/tracer_test.erl
index 1da80bfe31..a82fd04d2e 100644
--- a/erts/emulator/test/tracer_test.erl
+++ b/erts/emulator/test/tracer_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl
index d1085c1958..ab56018373 100644
--- a/erts/emulator/test/z_SUITE.erl
+++ b/erts/emulator/test/z_SUITE.erl
@@ -191,7 +191,13 @@ node_container_refc_check(Config) when is_list(Config) ->
ok.
long_timers(Config) when is_list(Config) ->
- ok = long_timers_test:check_result().
+ case long_timers_test:check_result() of
+ ok -> ok;
+ high_cpu -> {comment, "Ignored failures due to high CPU utilization"};
+ missing_cpu_info -> {comment, "Ignored failures due to missing CPU utilization information"};
+ Fail -> ct:fail(Fail)
+ end.
+
pollset_size(Config) when is_list(Config) ->
Name = pollset_size_testcase_initial_state_holder,
diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c
index d67b997d6d..bc353e384e 100644
--- a/erts/etc/common/heart.c
+++ b/erts/etc/common/heart.c
@@ -48,13 +48,10 @@
*
* HEART_BEATING
*
- * This program expects a heart beat messages. If it does not receive a
- * heart beat message from Erlang within heart_beat_timeout seconds, it
- * reboots the system. The variable heart_beat_timeout is exported (so
- * that it can be set from the shell in VxWorks, as is the variable
- * heart_beat_report_delay). When using Solaris, the system is rebooted
- * by executing the command stored in the environment variable
- * HEART_COMMAND.
+ * This program expects a heart beat message. If it does not receive a
+ * heart beat message from Erlang within heart_beat_timeout seconds, it
+ * reboots the system. The system is rebooted by executing the command
+ * stored in the environment variable HEART_COMMAND.
*
* BLOCKING DESCRIPTORS
*
@@ -149,27 +146,17 @@ struct msg {
/* Maybe interesting to change */
/* Times in seconds */
-#define HEART_BEAT_BOOT_DELAY 60 /* 1 minute */
#define SELECT_TIMEOUT 5 /* Every 5 seconds we reset the
watchdog timer */
/* heart_beat_timeout is the maximum gap in seconds between two
- consecutive heart beat messages from Erlang, and HEART_BEAT_BOOT_DELAY
- is the the extra delay that wd_keeper allows for, to give heart a
- chance to reboot in the "normal" way before the hardware watchdog
- enters the scene. heart_beat_report_delay is the time allowed for reporting
- before rebooting under VxWorks. */
+ consecutive heart beat messages from Erlang. */
int heart_beat_timeout = 60;
-int heart_beat_report_delay = 30;
-int heart_beat_boot_delay = HEART_BEAT_BOOT_DELAY;
/* All current platforms have a process identifier that
fits in an unsigned long and where 0 is an impossible or invalid value */
unsigned long heart_beat_kill_pid = 0;
-#define VW_WD_TIMEOUT (heart_beat_timeout+heart_beat_report_delay+heart_beat_boot_delay)
-#define SOL_WD_TIMEOUT (heart_beat_timeout+heart_beat_boot_delay)
-
/* reasons for reboot */
#define R_TIMEOUT (1)
#define R_CLOSED (2)
@@ -297,7 +284,6 @@ free_env_val(char *value)
static void get_arguments(int argc, char** argv) {
int i = 1;
int h;
- int w;
unsigned long p;
while (i < argc) {
@@ -313,15 +299,6 @@ static void get_arguments(int argc, char** argv) {
i++;
}
break;
- case 'w':
- if (strcmp(argv[i], "-wt") == 0)
- if (sscanf(argv[i+1],"%i",&w) ==1)
- if ((w > 10) && (w <= 65535)) {
- heart_beat_boot_delay = w;
- fprintf(stderr,"heart_beat_boot_delay = %d\n",w);
- i++;
- }
- break;
case 'p':
if (strcmp(argv[i], "-pid") == 0)
if (sscanf(argv[i+1],"%lu",&p) ==1){
@@ -347,7 +324,7 @@ static void get_arguments(int argc, char** argv) {
}
i++;
}
- debugf("arguments -ht %d -wt %d -pid %lu\n",h,w,p);
+ debugf("arguments -ht %d -pid %lu\n",h,p);
}
int main(int argc, char **argv) {
@@ -674,11 +651,6 @@ void win_system(char *command)
*/
static void
do_terminate(int erlin_fd, int reason) {
- /*
- When we get here, we have HEART_BEAT_BOOT_DELAY secs to finish
- (plus heart_beat_report_delay if under VxWorks), so we don't need
- to call wd_reset().
- */
int ret = 0, tmo=0;
char *tmo_env;
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index c5422ab2ed..30f2d831b5 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -399,19 +399,29 @@ elif [ "x$GDB" = "xdump" ]; then
cmdfile="/tmp/.cerlgdb.$$"
case "x$core" in
x/*)
- gdbcmd="$EMU_NAME ${core}"
;;
*)
dir=`pwd`
- gdbcmd="$EMU_NAME ${dir}/${core}"
+ core="${dir}/${core}"
;;
esac
- echo "set width 0
+ case `uname` in
+ Darwin)
+ echo "
+thread backtrace all
+quit
+" > $cmdfile
+ exec lldb -s $cmdfile -c ${core} $EMU_NAME
+ ;;
+ *)
+ echo "set width 0
set height 0
set verbose off
source $ROOTDIR/erts/etc/unix/etp-commands
thread apply all bt
" > $cmdfile
- exec gdb --batch --command=$cmdfile $gdbcmd
+ exec gdb --batch --command=$cmdfile $EMU_NAME $core
+ ;;
+ esac
fi
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index 15fb718c47..f2babc48d2 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -52,7 +52,7 @@ document etp-help
% etpf-cons, etpf-boxed,
%
% Special commands for not really terms:
-% etp-mfa, etp-cp,
+% etp-mfa, etp-cp, etp-disasm,
% etp-msgq, etpf-msgq,
% etp-stacktrace, etp-stackdump, etpf-stackdump, etp-dictdump
% etp-process-info, etp-process-memory-info
@@ -1090,12 +1090,10 @@ document etp-mfa
%---------------------------------------------------------------------------
end
-
-
-define etp-cp-1
+define etp-cp-func-info-1
# Args: Eterm cp
#
-# Non-reentrant
+# Non-reentrant, takes cp, sets $etp_cp_p to MFA in func_info
#
set $etp_cp = (Eterm)($arg0)
set $etp_ranges = &r[(int)the_active_code_index]
@@ -1137,8 +1135,21 @@ define etp-cp-1
end
end
if $etp_cp_p
+ set $cp_cp_p_offset = ($etp_cp-((Eterm)($etp_cp_p-2)))
+ else
+ set $cp_cp_p_offset = 0
+ end
+end
+
+define etp-cp-1
+# Args: Eterm cp
+#
+# Non-reentrant
+#
+ etp-cp-func-info-1 $arg0
+ if $etp_cp_p
printf "#Cp"
- etp-mfa-1 ($etp_cp_p) ($etp_cp-((Eterm)($etp_cp_p-2)))
+ etp-mfa-1 $etp_cp_p $cp_cp_p_offset
else
if $etp_cp == beam_apply+1
printf "#Cp<terminate process normally>"
@@ -1311,7 +1322,11 @@ define etp-stacktrace
set $etp_stacktrace_p = ($arg0)->stop
set $etp_stacktrace_end = ($arg0)->hend
printf "%% Stacktrace (%u): ", $etp_stacktrace_end-$etp_stacktrace_p
- etp ($arg0)->cp
+ if ($arg0)->cp == 0x0
+ printf "NULL\n"
+ else
+ etp ($arg0)->cp
+ end
while $etp_stacktrace_p < $etp_stacktrace_end
if ($etp_stacktrace_p[0] & 0x3) == 0x0
# Continuation pointer
@@ -1339,7 +1354,11 @@ define etp-stackdump
set $etp_stackdump_p = ($arg0)->stop
set $etp_stackdump_end = ($arg0)->hend
printf "%% Stackdump (%u): ", $etp_stackdump_end-$etp_stackdump_p
- etp ($arg0)->cp
+ if ($arg0)->cp == 0x0
+ printf "NULL\n"
+ else
+ etp ($arg0)->cp
+ end
while $etp_stackdump_p < $etp_stackdump_end
etp $etp_stackdump_p[0]
set $etp_stackdump_p++
@@ -2432,57 +2451,155 @@ define etp-schedulers
if (!erts_initialized)
printf "No schedulers, since system isn't initialized!\n"
else
+ set $sched_type = 0
set $sched_ix = 0
while $sched_ix < erts_no_schedulers
- printf "--- Scheduler %d ---\n", $sched_ix+1
- printf " IX: %d\n", $sched_ix
- if (erts_aligned_scheduler_data[$sched_ix].esd.cpu_id < 0)
- printf " CPU Binding: unbound\n"
- else
- printf " CPU Binding: %d\n", erts_aligned_scheduler_data[$sched_ix].esd.cpu_id
- end
- printf " Aux work Flags:"
- set $aux_work_flags = *((Uint32 *) &erts_aligned_scheduler_data[$sched_ix].esd.ssi->aux_work)
- etp-aux-work-flags $aux_work_flags
- printf " Sleep Info Flags:"
- set $ssi_flags = *((Uint32 *) &erts_aligned_scheduler_data[$sched_ix].esd.ssi->flags)
- etp-ssi-flags $ssi_flags
- printf " Pointer: (ErtsSchedulerData *) %p\n", &erts_aligned_scheduler_data[$sched_ix].esd
- printf " - Run Queue -\n"
- if (etp_smp_compiled)
- set $runq = erts_aligned_scheduler_data[$sched_ix].esd.run_queue
- else
- set $runq = &erts_aligned_run_queues[0].runq
- end
- printf " Length: total=%d", *((Uint32 *) &($runq->len))
- printf ", max=%d", *((Uint32 *) &($runq->procs.prio_info[0].len))
- printf ", high=%d", *((Uint32 *) &($runq->procs.prio_info[1].len))
- printf ", normal=%d", *((Uint32 *) &($runq->procs.prio_info[2].len))
- printf ", low=%d", *((Uint32 *) &($runq->procs.prio_info[3].len))
- printf ", port=%d\n", *((Uint32 *) &($runq->ports.info.len))
- if ($runq->misc.start)
- printf " Misc Jobs: yes\n"
- else
- printf " Misc Jobs: no\n"
- end
- set $rq_flags = *((Uint32 *) &($runq->flags))
- etp-rq-flags-int $rq_flags
- printf " Pointer: (ErtsRunQueue *) %p\n", $runq
-
+ etp-scheduler-info-internal
+ etp-run-queue-info-internal
set $sched_ix++
end
- printf "-------------------\n",
+ printf "---------------------\n"
+ if (erts_no_dirty_cpu_schedulers)
+ printf "\n\n"
+ set $sched_type = 1
+ set $sched_ix = 0
+ while $sched_ix < erts_no_dirty_cpu_schedulers
+ etp-scheduler-info-internal
+ set $sched_ix++
+ end
+ etp-run-queue-info-internal
+ printf "---------------------\n"
+ end
+ if (erts_no_dirty_io_schedulers)
+ printf "\n\n"
+ set $sched_type = 2
+ set $sched_ix = 0
+ while $sched_ix < erts_no_dirty_io_schedulers
+ etp-scheduler-info-internal
+ set $sched_ix++
+ end
+ etp-run-queue-info-internal
+ printf "---------------------\n"
+ end
end
end
document etp-schedulers
%---------------------------------------------------------------------------
% etp-schedulers
-%
+%
% Print misc info about all schedulers
%---------------------------------------------------------------------------
end
+define etp-scheduler-info-internal
+ if ($sched_type == 0)
+ printf "--- Scheduler %d ---\n", $sched_ix+1
+ set $sched_data=&erts_aligned_scheduler_data[$sched_ix].esd
+ else
+ if ($sched_type == 1)
+ printf "--- Dirty CPU Scheduler %d ---\n", $sched_ix+1
+ set $sched_data=&erts_aligned_dirty_cpu_scheduler_data[$sched_ix].esd
+ else
+ printf "--- Dirty I/O Scheduler %d ---\n", $sched_ix+1
+ set $sched_data=&erts_aligned_dirty_io_scheduler_data[$sched_ix].esd
+ end
+ end
+ printf " IX: %d\n", $sched_ix
+ if ($sched_data->cpu_id < 0)
+ printf " CPU Binding: unbound\n"
+ else
+ printf " CPU Binding: %d\n", $sched_data->cpu_id
+ end
+ printf " Aux work Flags:"
+ set $aux_work_flags = *((Uint32 *) &$sched_data->ssi->aux_work)
+ etp-aux-work-flags $aux_work_flags
+ printf " Sleep Info Flags:"
+ set $ssi_flags = *((Uint32 *) &$sched_data->ssi->flags)
+ etp-ssi-flags $ssi_flags
+ printf " Pointer: (ErtsSchedulerData *) %p\n", $sched_data
+end
+
+define etp-run-queue-info-internal
+ if ($sched_type == 0)
+ printf " - Run Queue -\n"
+ if (etp_smp_compiled)
+ set $runq = erts_aligned_scheduler_data[$sched_ix].esd.run_queue
+ else
+ set $runq = &erts_aligned_run_queues[0].runq
+ end
+ else
+ if ($sched_type == 1)
+ printf "\n--- Dirty CPU Run Queue ---\n"
+ set $runq = &erts_aligned_run_queues[-1].runq
+ else
+ printf "\n--- Dirty I/O Run Queue ---\n"
+ set $runq = &erts_aligned_run_queues[-2].runq
+ end
+ end
+ printf " Length: total=%d", *((Uint32 *) &($runq->len))
+ printf ", max=%d", *((Uint32 *) &($runq->procs.prio_info[0].len))
+ printf ", high=%d", *((Uint32 *) &($runq->procs.prio_info[1].len))
+ printf ", normal=%d", *((Uint32 *) &($runq->procs.prio_info[2].len))
+ printf ", low=%d", *((Uint32 *) &($runq->procs.prio_info[3].len))
+ printf ", port=%d\n", *((Uint32 *) &($runq->ports.info.len))
+ if ($runq->misc.start)
+ printf " Misc Jobs: yes\n"
+ else
+ printf " Misc Jobs: no\n"
+ end
+ set $rq_flags = *((Uint32 *) &($runq->flags))
+ etp-rq-flags-int $rq_flags
+ printf " Pointer: (ErtsRunQueue *) %p\n", $runq
+
+
+define etp-disasm-1
+ set $code_ptr = ((BeamInstr*)$arg0)
+ set $addr = *$code_ptr
+ set $i = 0
+ while $i < (sizeof(opc) / sizeof(OpEntry))
+ if $addr == beam_ops[$i]
+ printf "%s %d", opc[$i].name, opc[$i].sz
+ set $next_i = $code_ptr + opc[$i].sz
+ set $i += 4999
+ end
+ set $i++
+ end
+end
+
+define etp-disasm
+ etp-cp-func-info-1 $arg0
+ if $etp_cp_p == 0
+ printf "invalid argument"
+ else
+ etp-mfa-1 $etp_cp_p $cp_cp_p_offset
+ printf ": "
+ etp-disasm-1 $arg0
+ printf "\r\n"
+ while $next_i < ((BeamInstr*)$arg1)
+ set $prev_i = $next_i
+ etp-cp-func-info-1 $next_i
+ etp-mfa-1 $etp_cp_p $cp_cp_p_offset
+ printf ": "
+ etp-disasm-1 $next_i
+ if $prev_i == $next_i
+ # ptr did not advance, we are inside some strange opcode with argument
+ set $next_i++
+ printf "instr argument"
+ end
+ printf "\r\n"
+ end
+ end
+end
+
+document etp-disasm
+%---------------------------------------------------------------------------
+% etp-disasm StartI EndI
+%
+% Disassemble the code inbetween StartI and EndI
+%---------------------------------------------------------------------------
+end
+
define etp-migration-info
set $minfo = (ErtsMigrationPaths *) *((UWord *) &erts_migration_paths)
set $rq_ix = 0
@@ -3581,9 +3698,24 @@ document etp-block
%---------------------------------------------------------------------------
end
+define etp-smp-atomic
+ if (etp_smp_compiled)
+ set $arg1 = (($arg0).counter)
+ else
+ set $arg1 = ($arg0)
+ end
+end
+
+document etp-smp-atomic
+%---------------------------------------------------------------------------
+% Read an erts_smp_atomic_t value from $arg0 into $arg1
+%---------------------------------------------------------------------------
+end
+
define etp-carrier-blocks
set $etp_crr = (Carrier_t*) $arg0
- set $etp_alc = (Allctr_t*)($etp_crr->allctr.counter & ~7)
+ etp-smp-atomic $etp_crr->allctr $etp_alc
+ set $etp_alc = (Allctr_t*)($etp_alc & ~7)
set $etp_crr_end = ((char*)$etp_crr + ($etp_crr->chdr & ~7) - (sizeof(void*) & ~8))
set $etp_blk = (Block_t*) ((char*)$etp_crr + $etp_alc->mbc_header_size)
set $etp_prev_blk = 0
diff --git a/erts/etc/unix/format_man_pages b/erts/etc/unix/format_man_pages
index 54f2d90c28..0afff13571 100644
--- a/erts/etc/unix/format_man_pages
+++ b/erts/etc/unix/format_man_pages
@@ -107,7 +107,7 @@ case :"$TARGET" in
if [ -f $file ]; then
name=`echo $file | sed 's/\.[^.]*$//'`
sec=`echo $file | sed 's/.*\.//'`
- /usr/bin/groff -Tascii -mandoc $ERL_ROOT/man/man$sec/$file \
+ /usr/bin/groff -Tutf8 -mandoc $ERL_ROOT/man/man$sec/$file \
> $ERL_ROOT/man/cat$sec/$file
fi
done
diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c
index 30210ac172..debd643da7 100644
--- a/erts/etc/unix/run_erl.c
+++ b/erts/etc/unix/run_erl.c
@@ -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.
@@ -240,6 +240,7 @@ int main(int argc, char **argv)
int off_argv;
int calculated_pipename = 0;
int highest_pipe_num = 0;
+ int sleepy_child = 0;
program_name = argv[0];
@@ -250,6 +251,11 @@ int main(int argc, char **argv)
init_outbuf();
+ if (!strcmp(argv[1],"-sleepy-child")) { /* For test purpose only */
+ sleepy_child = 1;
+ ++i;
+ }
+
if (!strcmp(argv[1],"-daemon")) {
daemon_init();
++i;
@@ -392,6 +398,9 @@ int main(int argc, char **argv)
exit(1);
}
if (childpid == 0) {
+ if (sleepy_child)
+ sleep(1);
+
/* Child */
sf_close(mfd);
/* disassociate from control terminal */
@@ -904,20 +913,32 @@ static int create_fifo(char *name, int perm)
* Find a master device, open and return fd and slave device name.
*/
+#ifdef HAVE_WORKING_POSIX_OPENPT
+ /*
+ * Use openpty() on OpenBSD even if we have posix_openpt()
+ * as there is a race when read from master pty returns 0
+ * if child has not yet opened slave pty.
+ * (maybe other BSD's have the same problem?)
+ */
+# if !(defined(__OpenBSD__) && defined(HAVE_OPENPTY))
+# define TRY_POSIX_OPENPT
+# endif
+#endif
+
static int open_pty_master(char **ptyslave, int *sfdp)
{
int mfd;
/* Use the posix_openpt if working, as this guarantees creation of the
slave device properly. */
-#if defined(HAVE_WORKING_POSIX_OPENPT) || (defined(__sun) && defined(__SVR4))
-# ifdef HAVE_WORKING_POSIX_OPENPT
- if ((mfd = posix_openpt(O_RDWR)) >= 0) {
+#if defined(TRY_POSIX_OPENPT) || (defined(__sun) && defined(__SVR4))
+# ifdef TRY_POSIX_OPENPT
+ mfd = posix_openpt(O_RDWR);
# elif defined(__sun) && defined(__SVR4)
mfd = sf_open("/dev/ptmx", O_RDWR, 0);
+# endif
if (mfd >= 0) {
-# endif
if ((*ptyslave = ptsname(mfd)) != NULL &&
grantpt(mfd) == 0 &&
unlockpt(mfd) == 0) {
diff --git a/erts/example/time_compat.erl b/erts/example/time_compat.erl
index b87c6cc550..6472a271b6 100644
--- a/erts/example/time_compat.erl
+++ b/erts/example/time_compat.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2014-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.
@@ -272,6 +272,10 @@ system_flag(Flag, Value) ->
%%
integer_time_unit(native) -> 1000*1000;
+integer_time_unit(nanosecond) -> 1000*1000*1000;
+integer_time_unit(microsecond) -> 1000*1000;
+integer_time_unit(millisecond) -> 1000;
+integer_time_unit(second) -> 1;
integer_time_unit(nano_seconds) -> 1000*1000*1000;
integer_time_unit(micro_seconds) -> 1000*1000;
integer_time_unit(milli_seconds) -> 1000;
diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h
index a4a5d1d510..55566ddf74 100644
--- a/erts/include/internal/erl_misc_utils.h
+++ b/erts/include/internal/erl_misc_utils.h
@@ -56,4 +56,33 @@ int erts_map_win_error_to_errno(DWORD win_error);
int erts_get_last_win_errno(void);
#endif
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+#define __DARWIN__ 1
+#endif
+
+/*
+ * ERTS_PREMATURE_TIMEOUT() expects time units
+ * 1000 (millisec), 1000000 (microsec), or
+ * 1000000000 (nanosec). Might not work properly
+ * otherwise.
+ */
+#undef ERTS_USE_PREMATURE_TIMEOUT
+#undef ERTS_PREMATURE_TIMEOUT
+
+#if defined(__DARWIN__)
+#define ERTS_USE_PREMATURE_TIMEOUT 1
+#define ERTS_PREMATURE_TIMEOUT(TMO, TU) \
+ ((TMO) >= 1 * ((TU) / 1000) \
+ ? ((TMO) >= 20 * ((TU) / 1000) \
+ ? 15 * ((TU) / 1000) \
+ : ((TMO) >= 5 * ((TU) / 1000) \
+ ? 3 * ((TU) / 1000) \
+ : 5 * ((TU) / 10000))) \
+ : 0)
+
+#else
+#define ERTS_USE_PREMATURE_TIMEOUT 0
+#define ERTS_PREMATURE_TIMEOUT(TMO, TU) (0)
+#endif
+
#endif /* #ifndef ERL_MISC_UTILS_H_ */
diff --git a/erts/include/internal/erl_printf.h b/erts/include/internal/erl_printf.h
index c4565dfafc..f180a53f18 100644
--- a/erts/include/internal/erl_printf.h
+++ b/erts/include/internal/erl_printf.h
@@ -41,12 +41,18 @@ struct erts_dsprintf_buf_t_ {
#define ERTS_DSPRINTF_BUF_INITER(GFUNC) {NULL, 0, 0, (GFUNC)}
+typedef int (*fmtfn_t)(void*, char*, size_t);
+
+int erts_write_fd(void *vfdp, char* buf, size_t len);
+int erts_write_ds(void *vdsbufp, char* buf, size_t len);
+
int erts_printf(const char *, ...);
int erts_fprintf(FILE *, const char *, ...);
int erts_fdprintf(int, const char *, ...);
int erts_sprintf(char *, const char *, ...);
int erts_snprintf(char *, size_t, const char *, ...);
int erts_dsprintf(erts_dsprintf_buf_t *, const char *, ...);
+int erts_cbprintf(fmtfn_t, void*, const char*, ...);
int erts_vprintf(const char *, va_list);
int erts_vfprintf(FILE *, const char *, va_list);
@@ -54,5 +60,6 @@ int erts_vfdprintf(int, const char *, va_list);
int erts_vsprintf(char *, const char *, va_list);
int erts_vsnprintf(char *, size_t, const char *, va_list);
int erts_vdsprintf(erts_dsprintf_buf_t *, const char *, va_list);
+int erts_vcbprintf(fmtfn_t, void*, const char*, va_list);
#endif /* #ifndef ERL_PRINTF_H_ */
diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h
index 4f969bdbcb..56ec032bd1 100644
--- a/erts/include/internal/erl_printf_format.h
+++ b/erts/include/internal/erl_printf_format.h
@@ -30,6 +30,7 @@
#include <stdlib.h>
#include "erl_int_sizes_config.h"
+#include "erl_printf.h"
#if SIZEOF_VOID_P == SIZEOF_LONG
typedef unsigned long ErlPfUWord;
@@ -44,8 +45,6 @@ typedef long long ErlPfSWord;
#error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint'
#endif
-typedef int (*fmtfn_t)(void*, char*, size_t);
-
extern int erts_printf_format(fmtfn_t, void*, char*, va_list);
extern int erts_printf_char(fmtfn_t, void*, char);
diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c
index b5e90dfeef..7781fc2196 100644
--- a/erts/lib_src/common/erl_printf.c
+++ b/erts/lib_src/common/erl_printf.c
@@ -165,8 +165,8 @@ write_f(void *vfp, char* buf, size_t len)
return len;
}
-static int
-write_fd(void *vfdp, char* buf, size_t len)
+int
+erts_write_fd(void *vfdp, char* buf, size_t len)
{
ssize_t size;
size_t res = len;
@@ -226,8 +226,8 @@ write_sn(void *vwsnap, char* buf, size_t len)
return rv;
}
-static int
-write_ds(void *vdsbufp, char* buf, size_t len)
+int
+erts_write_ds(void *vdsbufp, char* buf, size_t len)
{
erts_dsprintf_buf_t *dsbufp = (erts_dsprintf_buf_t *) vdsbufp;
size_t need_len = len + 1; /* Also trailing '\0' */
@@ -301,7 +301,7 @@ erts_fdprintf(int fd, const char *format, ...)
va_list arglist;
va_start(arglist, format);
errno = 0;
- res = erts_printf_format(write_fd,(void *)&fd,(char *)format,arglist);
+ res = erts_printf_format(erts_write_fd,(void *)&fd,(char *)format,arglist);
va_end(arglist);
return res;
}
@@ -355,7 +355,7 @@ erts_dsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, ...)
return -EINVAL;
va_start(arglist, format);
errno = 0;
- res = erts_printf_format(write_ds, (void *)dsbufp, (char *)format, arglist);
+ res = erts_printf_format(erts_write_ds, (void *)dsbufp, (char *)format, arglist);
if (dsbufp->str) {
if (res < 0)
dsbufp->str[0] = '\0';
@@ -366,6 +366,20 @@ erts_dsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, ...)
return res;
}
+/*
+ * Callback printf
+ */
+int erts_cbprintf(fmtfn_t cb_fn, void* cb_arg, const char* format, ...)
+{
+ int res;
+ va_list arglist;
+ va_start(arglist, format);
+ errno = 0;
+ res = erts_printf_format(cb_fn, cb_arg, (char *)format, arglist);
+ va_end(arglist);
+ return res;
+}
+
int
erts_vprintf(const char *format, va_list arglist)
{
@@ -411,7 +425,7 @@ erts_vfdprintf(int fd, const char *format, va_list arglist)
{
int res;
errno = 0;
- res = erts_printf_format(write_fd,(void *)&fd,(char *)format,arglist);
+ res = erts_printf_format(erts_write_fd,(void *)&fd,(char *)format,arglist);
return res;
}
@@ -456,7 +470,7 @@ erts_vdsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, va_list arglist)
if (!dsbufp)
return -EINVAL;
errno = 0;
- res = erts_printf_format(write_ds, (void *)dsbufp, (char *)format, arglist);
+ res = erts_printf_format(erts_write_ds, (void *)dsbufp, (char *)format, arglist);
if (dsbufp->str) {
if (res < 0)
dsbufp->str[0] = '\0';
@@ -465,3 +479,10 @@ erts_vdsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, va_list arglist)
}
return res;
}
+
+int
+erts_vcbprintf(fmtfn_t cb_fn, void* cb_arg, const char *format, va_list arglist)
+{
+ errno = 0;
+ return erts_printf_format(cb_fn, cb_arg, (char *)format, arglist);
+}
diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c
index 420efd725f..3501fe335a 100644
--- a/erts/lib_src/common/ethr_aux.c
+++ b/erts/lib_src/common/ethr_aux.c
@@ -220,7 +220,7 @@ ethr_init_common__(ethr_init_data *id)
ethr_min_stack_size__ += ethr_pagesize__;
#endif
/* The system may think that we need more stack */
-#if defined(PTHREAD_STACK_MIN)
+#if defined(ETHR_HAVE_USABLE_PTHREAD_STACK_MIN)
if (ethr_min_stack_size__ < PTHREAD_STACK_MIN)
ethr_min_stack_size__ = PTHREAD_STACK_MIN;
#elif defined(_SC_THREAD_STACK_MIN)
diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c
index eef88d5002..464875570a 100644
--- a/erts/lib_src/pthread/ethr_event.c
+++ b/erts/lib_src/pthread/ethr_event.c
@@ -184,6 +184,8 @@ return_event_on:
#include <errno.h>
#include <string.h>
+#include "erl_misc_utils.h"
+
#ifdef __DARWIN__
struct ethr_event_fdsets___ {
@@ -346,12 +348,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
ethr_sint32_t val;
int res, ulres;
int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
- ethr_sint64_t time = 0; /* SHUT UP annoying faulty warning... */
+ ethr_sint64_t time = 0;
#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- ethr_sint64_t start = 0; /* SHUT UP annoying faulty warning... */
-#endif
-#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
- struct timespec cond_timeout;
+ ethr_sint64_t timeout_time = 0; /* SHUT UP annoying faulty warning... */
#endif
val = ethr_atomic32_read(&e->state);
@@ -365,30 +364,27 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
if (timeout == 0)
return ETIMEDOUT;
else {
- time = timeout;
- switch (e->fd[0]) {
- case ETHR_EVENT_INVALID_FD__:
#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- start = ethr_get_monotonic_time();
+ timeout_time = ethr_get_monotonic_time();
+ timeout_time += timeout;
#endif
+ switch (e->fd[0]) {
+ case ETHR_EVENT_INVALID_FD__:
+ time = timeout;
setup_nonblocking_pipe(e);
break;
#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
case ETHR_EVENT_COND_TIMEDWAIT__:
- time += ethr_get_monotonic_time();
- cond_timeout.tv_sec = time / (1000*1000*1000);
- cond_timeout.tv_nsec = time % (1000*1000*1000);
+ time = -1;
if (spincount == 0)
goto set_event_off_waiter;
break;
#endif
default:
+ time = timeout;
/* Already initialized pipe... */
if (spincount == 0)
goto set_select_timeout;
-#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- start = ethr_get_monotonic_time();
-#endif
break;
}
}
@@ -418,6 +414,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
|| e->fd[0] == ETHR_EVENT_COND_TIMEDWAIT__
#endif
) {
+#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
+ struct timespec cond_timeout;
+#endif
set_event_off_waiter:
@@ -446,9 +445,35 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
if (timeout > 0) {
+ if (time != timeout_time) {
+ time = timeout_time;
+
+#if ERTS_USE_PREMATURE_TIMEOUT
+ {
+ ethr_sint64_t rtmo;
+
+ rtmo = timeout_time - ethr_get_monotonic_time();
+ if (rtmo <= 0) {
+ res = ETIMEDOUT;
+ break;
+ }
+ time = timeout_time;
+ time -= ERTS_PREMATURE_TIMEOUT(rtmo, 1000*1000*1000);
+ }
+#endif
+
+ cond_timeout.tv_sec = time / (1000*1000*1000);
+ cond_timeout.tv_nsec = time % (1000*1000*1000);
+ }
res = pthread_cond_timedwait(&e->cnd, &e->mtx, &cond_timeout);
- if (res == EINTR || res == ETIMEDOUT)
+ if (res == EINTR
+ || (res == ETIMEDOUT
+#if ERTS_USE_PREMATURE_TIMEOUT
+ && time == timeout_time
+#endif
+ )) {
break;
+ }
}
else
#endif
@@ -477,7 +502,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
struct timeval select_timeout;
#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
- time -= ethr_get_monotonic_time() - start;
+#if ERTS_USE_PREMATURE_TIMEOUT
+ restart_select:
+#endif
+
+ time = timeout_time - ethr_get_monotonic_time();
if (time <= 0)
return ETIMEDOUT;
#endif
@@ -492,6 +521,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
*/
time = ((time - 1) / 1000) + 1;
+#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \
+ && ERTS_USE_PREMATURE_TIMEOUT
+ time -= ERTS_PREMATURE_TIMEOUT(time, 1000*1000);
+#endif
+
select_timeout.tv_sec = time / (1000*1000);
select_timeout.tv_usec = time % (1000*1000);
@@ -559,7 +593,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
val = ethr_atomic32_read(&e->state);
if (val == ETHR_EVENT_ON__)
goto return_event_on;
-
+#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \
+ && ERTS_USE_PREMATURE_TIMEOUT
+ if (res == ETIMEDOUT)
+ goto restart_select; /* Verify timeout */
+#endif
}
return res;
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 796cbd74c5..6af1633726 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 4406a82a36..d0ed09dc91 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 b62da04bfd..7a2d9e5a81 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 a0da864824..ab63e4745f 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_dirty_process_code_checker.beam b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
new file mode 100644
index 0000000000..9c99d80581
--- /dev/null
+++ b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index d897c8e92f..c84acc12a1 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam
new file mode 100644
index 0000000000..2c1ec7473a
--- /dev/null
+++ b/erts/preloaded/ebin/erts_literal_area_collector.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index b856bff4fe..5c0c6605af 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 b601c048b3..2eb09599be 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 77909b01f0..7d3e32b452 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 5bbbaf14d5..b8b8402a07 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 fba03d52bd..3fddd2f357 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 6afeb454d6..102a29396e 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 4c48742344..8955885373 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile
index 4a447d3a09..2ab9edaf5e 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -44,7 +44,9 @@ PRE_LOADED_ERL_MODULES = \
erts_code_purger \
erlang \
erts_internal \
- erl_tracer
+ erl_tracer \
+ erts_literal_area_collector \
+ erts_dirty_process_code_checker
PRE_LOADED_BEAM_MODULES = \
prim_eval
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index b3ec73a60e..1d09aeded9 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -555,17 +555,18 @@ efile_gm_get(Paths, Mod, ParentRef, Process) ->
efile_gm_get_1([P|Ps], File0, Mod, {Parent,Ref}=PR, Process) ->
File = join(P, File0),
- Res = try prim_file:read_file(File) of
- {ok,Bin} ->
- gm_process(Mod, File, Bin, Process);
- Error ->
- _ = check_file_result(get_modules, File, Error),
- efile_gm_get_1(Ps, File0, Mod, PR, Process)
- catch
- _:Reason ->
- {error,{crash,Reason}}
- end,
- Parent ! {Ref,Mod,Res};
+ try prim_file:read_file(File) of
+ {ok,Bin} ->
+ Res = gm_process(Mod, File, Bin, Process),
+ Parent ! {Ref,Mod,Res};
+ Error ->
+ _ = check_file_result(get_modules, File, Error),
+ efile_gm_get_1(Ps, File0, Mod, PR, Process)
+ catch
+ _:Reason ->
+ Res = {error,{crash,Reason}},
+ Parent ! {Ref,Mod,Res}
+ end;
efile_gm_get_1([], _, Mod, {Parent,Ref}, _Process) ->
Parent ! {Ref,Mod,{error,enoent}}.
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index edf79b8f75..8771089b65 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -59,6 +59,7 @@
-export_type([timestamp/0]).
-export_type([time_unit/0]).
+-export_type([deprecated_time_unit/0]).
-type ext_binary() :: binary().
-type timestamp() :: {MegaSecs :: non_neg_integer(),
@@ -67,12 +68,20 @@
-type time_unit() ::
pos_integer()
- | 'seconds'
+ | 'second'
+ | 'millisecond'
+ | 'microsecond'
+ | 'nanosecond'
+ | 'native'
+ | 'perf_counter'
+ | deprecated_time_unit().
+
+%% Deprecated symbolic units...
+-type deprecated_time_unit() ::
+ 'seconds'
| 'milli_seconds'
| 'micro_seconds'
- | 'nano_seconds'
- | 'native'
- | 'perf_counter'.
+ | 'nano_seconds'.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Native code BIF stubs and their types
@@ -1365,19 +1374,33 @@ convert_time_unit(Time, FromUnit, ToUnit) ->
FU = case FromUnit of
native -> erts_internal:time_unit();
perf_counter -> erts_internal:perf_counter_unit();
+ nanosecond -> 1000*1000*1000;
+ microsecond -> 1000*1000;
+ millisecond -> 1000;
+ second -> 1;
+
+ %% Deprecated symbolic units...
nano_seconds -> 1000*1000*1000;
micro_seconds -> 1000*1000;
milli_seconds -> 1000;
seconds -> 1;
+
_ when FromUnit > 0 -> FromUnit
end,
TU = case ToUnit of
native -> erts_internal:time_unit();
perf_counter -> erts_internal:perf_counter_unit();
+ nanosecond -> 1000*1000*1000;
+ microsecond -> 1000*1000;
+ millisecond -> 1000;
+ second -> 1;
+
+ %% Deprecated symbolic units...
nano_seconds -> 1000*1000*1000;
micro_seconds -> 1000*1000;
milli_seconds -> 1000;
seconds -> 1;
+
_ when ToUnit > 0 -> ToUnit
end,
case Time < 0 of
@@ -2385,10 +2408,11 @@ term_to_binary(_Term, _Options) ->
tl(_List) ->
erlang:nif_error(undefined).
+-type match_variable() :: atom(). % Approximation of '$1' | '$2' | ...
-type trace_pattern_mfa() ::
{atom(),atom(),arity() | '_'} | on_load.
-type trace_match_spec() ::
- [{[term()] | '_' ,[term()],[term()]}].
+ [{[term()] | '_' | match_variable() ,[term()],[term()]}].
-spec erlang:trace_pattern(MFA, MatchSpec) -> non_neg_integer() when
MFA :: trace_pattern_mfa() | send | 'receive',
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index e18da28905..7ab06164b4 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -37,7 +37,7 @@
{registered, []},
{applications, []},
{env, []},
- {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0"]}
+ {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0.1"]}
]}.
%% vim: ft=erlang
diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl
index d1e64342e0..28d71fd07e 100644
--- a/erts/preloaded/src/erts_code_purger.erl
+++ b/erts/preloaded/src/erts_code_purger.erl
@@ -22,7 +22,8 @@
%% Purpose : Implement system process erts_code_purger
%% to handle code module purging.
--export([start/0, purge/1, soft_purge/1]).
+-export([start/0, purge/1, soft_purge/1, pending_purge_lambda/3,
+ finish_after_on_load/2]).
-spec start() -> term().
start() ->
@@ -40,10 +41,48 @@ loop() ->
Res = do_soft_purge(Mod),
From ! {reply, soft_purge, Res, Ref};
+ {finish_after_on_load,{Mod,Keep},From,Ref}
+ when is_atom(Mod), is_pid(From) ->
+ Res = do_finish_after_on_load(Mod, Keep),
+ From ! {reply, finish_after_on_load, Res, Ref};
+
+ {test_purge, Mod, From, Type, Ref} when is_atom(Mod), is_pid(From) ->
+ do_test_purge(Mod, From, Type, Ref);
+
_Other -> ignore
end,
loop().
+%%
+%% Processes that tries to call a fun that belongs to
+%% a module that currently is being purged will end
+%% up here (pending_purge_lambda) in a suspended state.
+%% When the purge operation completes or aborts (soft
+%% purge that failed) these processes will be resumed.
+%%
+pending_purge_lambda(_Module, Fun, Args) ->
+ %%
+ %% When the process is resumed, the following
+ %% scenarios exist:
+ %% * The code that the fun refers to is still
+ %% there due to a failed soft purge. The
+ %% call to the fun will succeed via apply/2.
+ %% * The code was purged, and a current version
+ %% of the module is loaded which does not
+ %% contain this fun. The call will result
+ %% in an exception being raised.
+ %% * The code was purged, and no current
+ %% version of the module is loaded. An attempt
+ %% to load the module (via the error_handler)
+ %% will be made. This may or may not succeed.
+ %% If the module is loaded, it may or may
+ %% not contain the fun. The call will
+ %% succeed if the error_handler was able
+ %% to load the module and loaded module
+ %% contains this fun; otherwise, an exception
+ %% will be raised.
+ %%
+ apply(Fun, Args).
%% purge(Module)
%% Kill all processes running code from *old* Module, and then purge the
@@ -60,16 +99,14 @@ purge(Mod) when is_atom(Mod) ->
Result
end.
-
do_purge(Mod) ->
- case erts_internal:copy_literals(Mod, true) of
- false ->
- {false, false};
- true ->
- DidKill = check_proc_code(erlang:processes(), Mod, true),
- true = erts_internal:copy_literals(Mod, false),
- WasPurged = erts_internal:purge_module(Mod),
- {WasPurged, DidKill}
+ case erts_internal:purge_module(Mod, prepare) of
+ false ->
+ {false, false};
+ true ->
+ DidKill = check_proc_code(erlang:processes(), Mod, true),
+ true = erts_internal:purge_module(Mod, complete),
+ {true, DidKill}
end.
%% soft_purge(Module)
@@ -85,23 +122,48 @@ soft_purge(Mod) ->
Result
end.
-
do_soft_purge(Mod) ->
- case erts_internal:copy_literals(Mod, true) of
+ case erts_internal:purge_module(Mod, prepare) of
false ->
true;
true ->
- DoPurge = check_proc_code(erlang:processes(), Mod, false),
- true = erts_internal:copy_literals(Mod, false),
- case DoPurge of
+ Res = check_proc_code(erlang:processes(), Mod, false),
+ erts_internal:purge_module(Mod,
+ case Res of
+ false -> abort;
+ true -> complete
+ end)
+ end.
+
+%% finish_after_on_load(Module, Keep)
+%% Finish after running on_load function. If Keep is false,
+%% purge the code for the on_load function.
+
+finish_after_on_load(Mod, Keep) ->
+ Ref = make_ref(),
+ erts_code_purger ! {finish_after_on_load, {Mod,Keep}, self(), Ref},
+ receive
+ {reply, finish_after_on_load, Result, Ref} ->
+ Result
+ end.
+
+do_finish_after_on_load(Mod, Keep) ->
+ erlang:finish_after_on_load(Mod, Keep),
+ case Keep of
+ true ->
+ ok;
+ false ->
+ case erts_internal:purge_module(Mod, prepare_on_load) of
false ->
- false;
+ true;
true ->
- erts_internal:purge_module(Mod),
- true
+ _ = check_proc_code(erlang:processes(), Mod, true),
+ true = erts_internal:purge_module(Mod, complete)
end
end.
+
+
%%
%% check_proc_code(Pids, Mod, Hard) - Send asynchronous
%% requests to all processes to perform a check_process_code
@@ -283,8 +345,7 @@ cpc_sched_kill(Pid,
cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid, AllowGc) ->
erts_internal:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}},
- {allow_gc, AllowGc},
- {copy_literals, true}]).
+ {allow_gc, AllowGc}]).
cpc_request_gc(CpcS, [Pid|Pids]) ->
cpc_request(CpcS, Pid, true),
@@ -297,3 +358,72 @@ cpc_init(CpcS, [Pid|Pids], NoReqs) ->
cpc_init(CpcS, Pids, NoReqs+1).
% end of check_proc_code() implementation.
+
+%%
+%% FOR TESTING ONLY
+%%
+%% do_test_purge() is for testing only. The purge is done
+%% as usual, but the tester can control when to enter the
+%% specific phases.
+%%
+do_test_purge(Mod, From, Type, Ref) when Type == true; Type == false ->
+ Mon = erlang:monitor(process, From),
+ Res = case Type of
+ true -> do_test_hard_purge(Mod, From, Ref, Mon);
+ false -> do_test_soft_purge(Mod, From, Ref, Mon)
+ end,
+ From ! {test_purge, Res, Ref},
+ erlang:demonitor(Mon, [flush]),
+ ok;
+do_test_purge(_, _, _, _) ->
+ ok.
+
+do_test_soft_purge(Mod, From, Ref, Mon) ->
+ PrepRes = erts_internal:purge_module(Mod, prepare),
+ TestRes = test_progress(started, From, Mon, Ref, ok),
+ case PrepRes of
+ false ->
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ true;
+ true ->
+ Res = check_proc_code(erlang:processes(), Mod, false),
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ erts_internal:purge_module(Mod,
+ case Res of
+ false -> abort;
+ true -> complete
+ end)
+ end.
+
+do_test_hard_purge(Mod, From, Ref, Mon) ->
+ PrepRes = erts_internal:purge_module(Mod, prepare),
+ TestRes = test_progress(started, From, Mon, Ref, ok),
+ case PrepRes of
+ false ->
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ {false, false};
+ true ->
+ DidKill = check_proc_code(erlang:processes(), Mod, true),
+ _ = test_progress(continued, From, Mon, Ref, TestRes),
+ true = erts_internal:purge_module(Mod, complete),
+ {true, DidKill}
+ end.
+
+test_progress(_State, _From, _Mon, _Ref, died) ->
+ %% Test process died; continue so we wont
+ %% leave the system in an inconsistent
+ %% state...
+ died;
+test_progress(started, From, Mon, Ref, ok) ->
+ From ! {started, Ref},
+ receive
+ {'DOWN', Mon, process, From, _} -> died;
+ {continue, Ref} -> ok
+ end;
+test_progress(continued, From, Mon, Ref, ok) ->
+ From ! {continued, Ref},
+ receive
+ {'DOWN', Mon, process, From, _} -> died;
+ {complete, Ref} -> ok
+ end.
+
diff --git a/erts/preloaded/src/erts_dirty_process_code_checker.erl b/erts/preloaded/src/erts_dirty_process_code_checker.erl
new file mode 100644
index 0000000000..911642082c
--- /dev/null
+++ b/erts/preloaded/src/erts_dirty_process_code_checker.erl
@@ -0,0 +1,82 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erts_dirty_process_code_checker).
+
+-export([start/0]).
+
+%%
+%% The erts_dirty_process_code_checker is started at
+%% VM boot by the VM. It is a spawned as a system
+%% process, i.e, the whole VM will terminate if
+%% this process terminates.
+%%
+start() ->
+ process_flag(trap_exit, true),
+ msg_loop().
+
+msg_loop() ->
+ _ = receive
+ Request ->
+ handle_request(Request)
+ end,
+ msg_loop().
+
+check_process(Requester, Target, ReqId, Module) ->
+ Result = erts_internal:check_dirty_process_code(Target, Module),
+ Requester ! {check_process_code, ReqId, Result}.
+
+handle_request({Requester,
+ Target,
+ Prio,
+ {check_process_code,
+ ReqId,
+ Module,
+ _Flags} = Op}) ->
+ %%
+ %% Target may have stopped executing dirty since the
+ %% initial request was made. Check its current state
+ %% and try to send the request if possible; otherwise,
+ %% check the dirty executing process and send the result...
+ %%
+ try
+ case erts_internal:is_process_executing_dirty(Target) of
+ true ->
+ check_process(Requester, Target, ReqId, Module);
+ false ->
+ case erts_internal:request_system_task(Requester,
+ Target,
+ Prio,
+ Op) of
+ ok ->
+ ok;
+ dirty_execution ->
+ check_process(Requester, Target, ReqId, Module)
+ end
+ end
+ catch
+ _ : _ ->
+ ok %% Ignore all failures; someone passed us garbage...
+ end;
+handle_request(_Garbage) ->
+ ignore.
+
+
+
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 2459ea2a2c..f4518c4008 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -38,11 +38,13 @@
-export([system_check/1,
gather_system_check_result/1]).
--export([request_system_task/3]).
+-export([request_system_task/3, request_system_task/4]).
-export([check_process_code/3]).
--export([copy_literals/2]).
--export([purge_module/1]).
+-export([check_dirty_process_code/2]).
+-export([is_process_executing_dirty/1]).
+-export([release_literal_area_switch/0]).
+-export([purge_module/2]).
-export([flush_monitor_messages/3]).
@@ -204,14 +206,25 @@ port_info(_Result, _Item) ->
-spec request_system_task(Pid, Prio, Request) -> 'ok' when
Prio :: 'max' | 'high' | 'normal' | 'low',
Request :: {'garbage_collect', term()}
- | {'check_process_code', term(), module(), non_neg_integer()},
+ | {'check_process_code', term(), module(), non_neg_integer()}
+ | {'copy_literals', term(), boolean()},
Pid :: pid().
request_system_task(_Pid, _Prio, _Request) ->
erlang:nif_error(undefined).
+-spec request_system_task(RequesterPid, TargetPid, Prio, Request) -> 'ok' | 'dirty_execution' when
+ Prio :: 'max' | 'high' | 'normal' | 'low',
+ Request :: {'garbage_collect', term()}
+ | {'check_process_code', term(), module(), non_neg_integer()}
+ | {'copy_literals', term(), boolean()},
+ RequesterPid :: pid(),
+ TargetPid :: pid().
+
+request_system_task(_RequesterPid, _TargetPid, _Prio, _Request) ->
+ erlang:nif_error(undefined).
+
-define(ERTS_CPC_ALLOW_GC, (1 bsl 0)).
--define(ERTS_CPC_COPY_LITERALS, (1 bsl 1)).
-spec check_process_code(Module, Flags) -> boolean() when
Module :: module(),
@@ -223,7 +236,7 @@ check_process_code(_Module, _Flags) ->
Pid :: pid(),
Module :: module(),
RequestId :: term(),
- Option :: {async, RequestId} | {allow_gc, boolean()} | {copy_literals, boolean()},
+ Option :: {async, RequestId} | {allow_gc, boolean()},
OptionList :: [Option],
CheckResult :: boolean() | aborted.
check_process_code(Pid, Module, OptionList) ->
@@ -265,8 +278,6 @@ get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, Flags) ->
get_cpc_opts(Options, AsyncTuple, Flags);
get_cpc_opts([{allow_gc, AllowGC} | Options], Async, Flags) ->
get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_ALLOW_GC, AllowGC));
-get_cpc_opts([{copy_literals, CopyLit} | Options], Async, Flags) ->
- get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_COPY_LITERALS, CopyLit));
get_cpc_opts([], Async, Flags) ->
{Async, Flags}.
@@ -275,15 +286,26 @@ cpc_flags(OldFlags, Bit, true) ->
cpc_flags(OldFlags, Bit, false) ->
OldFlags band (bnot Bit).
--spec copy_literals(Module,Bool) -> 'true' | 'false' | 'aborted' when
- Module :: module(),
- Bool :: boolean().
-copy_literals(_Mod, _Bool) ->
+-spec check_dirty_process_code(Pid,Module) -> 'true' | 'false' when
+ Pid :: pid(),
+ Module :: module().
+check_dirty_process_code(_Pid,_Module) ->
erlang:nif_error(undefined).
--spec purge_module(Module) -> boolean() when
- Module :: module().
-purge_module(_Module) ->
+-spec is_process_executing_dirty(Pid) -> 'true' | 'false' when
+ Pid :: pid().
+is_process_executing_dirty(_Pid) ->
+ erlang:nif_error(undefined).
+
+-spec release_literal_area_switch() -> 'true' | 'false'.
+
+release_literal_area_switch() ->
+ erlang:nif_error(undefined).
+
+-spec purge_module(Module, Op) -> boolean() when
+ Module :: module(),
+ Op :: 'prepare' | 'prepare_on_load' | 'abort' | 'complete'.
+purge_module(_Module, _Op) ->
erlang:nif_error(undefined).
-spec system_check(Type) -> 'ok' when
@@ -416,10 +438,11 @@ microstate_accounting(Ref, Threads) ->
trace(_PidSpec, _How, _FlagList) ->
erlang:nif_error(undefined).
+-type match_variable() :: atom(). % Approximation of '$1' | '$2' | ...
-type trace_pattern_mfa() ::
{atom(),atom(),arity() | '_'} | on_load.
-type trace_match_spec() ::
- [{[term()] | '_' ,[term()],[term()]}].
+ [{[term()] | '_' | match_variable() ,[term()],[term()]}].
-spec trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when
MFA :: trace_pattern_mfa(),
diff --git a/erts/preloaded/src/erts_literal_area_collector.erl b/erts/preloaded/src/erts_literal_area_collector.erl
new file mode 100644
index 0000000000..3befad8dfb
--- /dev/null
+++ b/erts/preloaded/src/erts_literal_area_collector.erl
@@ -0,0 +1,113 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erts_literal_area_collector).
+
+-export([start/0]).
+
+%% Currently we only allow two outstanding literal
+%% copying jobs that garbage collect in order to
+%% copy the literals. Maybe we could allow more
+%% than two outstanding processes, but for now we
+%% play it safe...
+-define(MAX_GC_OUTSTND, 2).
+
+%%
+%% The erts_literal_area_collector is started at
+%% VM boot by the VM. It is a spawned as a system
+%% process, i.e, the whole VM will terminate if
+%% this process terminates.
+%%
+start() ->
+ process_flag(trap_exit, true),
+ msg_loop(undefined, 0, 0, []).
+
+%%
+%% The VM will send us a 'copy_literals' message
+%% when it has a new literal area that needs to
+%% be handled is added. We will also be informed
+%% about more areas when we call
+%% erts_internal:release_literal_area_switch().
+%%
+msg_loop(Area, Outstnd, GcOutstnd, NeedGC) ->
+ receive
+
+ %% A new area to handle has arrived...
+ copy_literals when Outstnd == 0 ->
+ switch_area();
+
+ %% Process (_Pid) has completed the request...
+ {copy_literals, {Area, _GcAllowed, _Pid}, ok} when Outstnd == 1 ->
+ switch_area(); %% Last process completed...
+ {copy_literals, {Area, false, _Pid}, ok} ->
+ msg_loop(Area, Outstnd-1, GcOutstnd, NeedGC);
+ {copy_literals, {Area, true, _Pid}, ok} when NeedGC == [] ->
+ msg_loop(Area, Outstnd-1, GcOutstnd-1, []);
+ {copy_literals, {Area, true, _Pid}, ok} ->
+ send_copy_req(hd(NeedGC), Area, true),
+ msg_loop(Area, Outstnd-1, GcOutstnd, tl(NeedGC));
+
+ %% Process (Pid) failed to complete the request
+ %% since it needs to garbage collect in order to
+ %% complete the request...
+ {copy_literals, {Area, false, Pid}, need_gc} when GcOutstnd < ?MAX_GC_OUTSTND ->
+ send_copy_req(Pid, Area, true),
+ msg_loop(Area, Outstnd, GcOutstnd+1, NeedGC);
+ {copy_literals, {Area, false, Pid}, need_gc} ->
+ msg_loop(Area, Outstnd, GcOutstnd, [Pid|NeedGC]);
+
+ %% Not handled message regarding the area that we
+ %% currently are working with. Crash the VM so
+ %% we notice this bug...
+ {copy_literals, {Area, _, _}, _} = Msg when erlang:is_reference(Area) ->
+ exit({not_handled_message, Msg});
+
+ %% Unexpected garbage message. Get rid of it...
+ _Ignore ->
+ msg_loop(Area, Outstnd, GcOutstnd, NeedGC)
+
+ end.
+
+switch_area() ->
+ Res = erts_internal:release_literal_area_switch(),
+ erlang:garbage_collect(), %% Almost no live data now...
+ case Res of
+ false ->
+ %% No more areas to handle...
+ msg_loop(undefined, 0, 0, []);
+ true ->
+ %% Send requests to all processes to copy
+ %% all live data they have referring to the
+ %% literal area that is to be released...
+ Area = make_ref(),
+ Outstnd = send_copy_reqs(erlang:processes(), Area, false),
+ msg_loop(Area, Outstnd, 0, [])
+ end.
+
+send_copy_reqs(Ps, Area, GC) ->
+ send_copy_reqs(Ps, Area, GC, 0).
+
+send_copy_reqs([], _Area, _GC, N) ->
+ N;
+send_copy_reqs([P|Ps], Area, GC, N) ->
+ send_copy_req(P, Area, GC),
+ send_copy_reqs(Ps, Area, GC, N+1).
+
+send_copy_req(P, Area, GC) ->
+ erts_internal:request_system_task(P, normal, {copy_literals, {Area, GC, P}, GC}).
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 45468b3b9c..551ca4ea40 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -205,7 +205,7 @@ boot(BootArgs) ->
{Start0,Flags,Args} = parse_boot_args(BootArgs),
%% We don't get to profile parsing of BootArgs
- case get_flag(profile_boot, Flags, false) of
+ case b2a(get_flag(profile_boot, Flags, false)) of
false -> ok;
true -> debug_profile_start()
end,
@@ -423,9 +423,6 @@ loop(State) ->
Loaded = State#state.loaded, %% boot_loop but is handled here
From ! {init,Loaded}, %% anyway.
loop(State);
- {From, {ensure_loaded, _}} ->
- From ! {init, not_allowed},
- loop(State);
Msg ->
loop(handle_msg(Msg,State))
end.
@@ -465,6 +462,8 @@ do_handle_msg(Msg,State) ->
From ! {init,ok},
{new_state,State#state{subscribed = [Pid|Subscribed]}}
end;
+ {From, {ensure_loaded, _}} ->
+ From ! {init, not_allowed};
X ->
case whereis(user) of
undefined ->
@@ -670,9 +669,9 @@ unload(_) ->
do_unload(sub([heart|erlang:pre_loaded()],erlang:loaded())).
do_unload([M|Mods]) ->
- catch erts_internal:purge_module(M),
+ catch erlang:purge_module(M),
catch erlang:delete_module(M),
- catch erts_internal:purge_module(M),
+ catch erlang:purge_module(M),
do_unload(Mods);
do_unload([]) ->
purge_all_hipe_refs(),
@@ -783,7 +782,7 @@ do_boot(Init,Flags,Start) ->
(catch erlang:system_info({purify, "Node: " ++ atom_to_list(node())})),
start_em(Start),
- case get_flag(profile_boot,Flags,false) of
+ case b2a(get_flag(profile_boot,Flags,false)) of
false -> ok;
true ->
debug_profile_format_mfas(debug_profile_mfas()),
diff --git a/erts/preloaded/src/prim_eval.S b/erts/preloaded/src/prim_eval.S
index e7f09a870c..e4b1560517 100644
--- a/erts/preloaded/src/prim_eval.S
+++ b/erts/preloaded/src/prim_eval.S
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. 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.
@@ -26,7 +26,7 @@
{attributes, []}.
-{labels, 10}.
+{labels, 14}.
{function, 'receive', 2, 2}.
@@ -36,6 +36,9 @@
{allocate,2,2}.
{move,{x,1},{y,0}}.
{move,{x,0},{y,1}}.
+ %% Call arg_reg_alloc() in order to ensure
+ %% that def_arg_reg[0] isn't clobbered
+ {call,0,{f,7}}.
{label,3}.
{loop_rec,{f,5},{x,0}}.
{move,{y,1},{x,1}}.
@@ -53,19 +56,43 @@
{deallocate,2}.
return.
-
-{function, module_info, 0, 8}.
+{function, arg_reg_alloc, 0, 7}.
{label,6}.
- {func_info,{atom,prim_eval},{atom,module_info},0}.
+ {func_info,{atom,prim_eval},{atom,arg_reg_alloc},0}.
{label,7}.
+ {allocate,0,0}.
+ {move,{integer,134217727},{x,0}}.
+ {call_ext,1,{extfunc,erlang,bump_reductions,1}}.
+ {move,{atom,true},{x,3}}.
+ {move,{atom,true},{x,4}}.
+ {move,{atom,true},{x,2}}.
+ {move,{atom,true},{x,5}}.
+ {move,{atom,true},{x,1}}.
+ {move,{atom,true},{x,6}}.
+ {move,{atom,true},{x,0}}.
+ {call_last,7,{f,9},0}.
+
+
+{function, arg_reg_alloc, 7, 9}.
+ {label,8}.
+ {func_info,{atom,prim_eval},{atom,arg_reg_alloc},7}.
+ {label,9}.
+ {move,{atom,ok},{x,0}}.
+ return.
+
+
+{function, module_info, 0, 11}.
+ {label,10}.
+ {func_info,{atom,prim_eval},{atom,module_info},0}.
+ {label,11}.
{move,{atom,prim_eval},{x,0}}.
{call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
-{function, module_info, 1, 10}.
- {label,8}.
+{function, module_info, 1, 13}.
+ {label,12}.
{func_info,{atom,prim_eval},{atom,module_info},1}.
- {label,9}.
+ {label,13}.
{move,{x,0},{x,1}}.
{move,{atom,prim_eval},{x,0}}.
{call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 560810d222..61f727e8a4 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -347,7 +347,17 @@ accept_opts(L, S) ->
case getopts(L, [active, nodelay, keepalive, delay_send, priority, tos]) of
{ok, Opts} ->
case setopts(S, Opts) of
- ok -> {ok, S};
+ ok ->
+ case getopts(L, [tclass]) of
+ {ok, []} ->
+ {ok, S};
+ {ok, TClassOpts} ->
+ case setopts(S, TClassOpts) of
+ ok ->
+ {ok, S};
+ Error -> close(S), Error
+ end
+ end;
Error -> close(S), Error
end;
Error ->
@@ -1196,6 +1206,7 @@ enc_opt(sndbuf) -> ?INET_OPT_SNDBUF;
enc_opt(recbuf) -> ?INET_OPT_RCVBUF;
enc_opt(priority) -> ?INET_OPT_PRIORITY;
enc_opt(tos) -> ?INET_OPT_TOS;
+enc_opt(tclass) -> ?INET_OPT_TCLASS;
enc_opt(nodelay) -> ?TCP_OPT_NODELAY;
enc_opt(multicast_if) -> ?UDP_OPT_MULTICAST_IF;
enc_opt(multicast_ttl) -> ?UDP_OPT_MULTICAST_TTL;
@@ -1255,6 +1266,7 @@ dec_opt(?INET_OPT_SNDBUF) -> sndbuf;
dec_opt(?INET_OPT_RCVBUF) -> recbuf;
dec_opt(?INET_OPT_PRIORITY) -> priority;
dec_opt(?INET_OPT_TOS) -> tos;
+dec_opt(?INET_OPT_TCLASS) -> tclass;
dec_opt(?TCP_OPT_NODELAY) -> nodelay;
dec_opt(?UDP_OPT_MULTICAST_IF) -> multicast_if;
dec_opt(?UDP_OPT_MULTICAST_TTL) -> multicast_ttl;
@@ -1329,6 +1341,7 @@ type_opt_1(sndbuf) -> int;
type_opt_1(recbuf) -> int;
type_opt_1(priority) -> int;
type_opt_1(tos) -> int;
+type_opt_1(tclass) -> int;
type_opt_1(nodelay) -> bool;
type_opt_1(ipv6_v6only) -> bool;
%% multicast
@@ -2401,13 +2414,13 @@ get_addrs([F|Addrs]) ->
{Addr,Rest} = get_addr(F, Addrs),
[Addr|get_addrs(Rest)].
-get_addr(?INET_AF_LOCAL, [0]) ->
- {{local,<<>>},[]};
get_addr(?INET_AF_LOCAL, [N|Addr]) ->
{A,Rest} = lists:split(N, Addr),
{{local,iolist_to_binary(A)},Rest};
+get_addr(?INET_AF_UNSPEC, Rest) ->
+ {{unspec,<<>>},Rest};
get_addr(?INET_AF_UNDEFINED, Rest) ->
- {{undefined,0},Rest};
+ {{undefined,<<>>},Rest};
get_addr(Family, [P1,P0|Addr]) ->
{IP,Rest} = get_ip(Family, Addr),
{{IP,?u16(P1, P0)},Rest}.
diff --git a/erts/start_scripts/Makefile b/erts/start_scripts/Makefile
index dfd8153f32..ae2521474e 100644
--- a/erts/start_scripts/Makefile
+++ b/erts/start_scripts/Makefile
@@ -17,6 +17,9 @@
#
# %CopyrightEnd%
#
+
+.NOTPARALLEL:
+
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -69,7 +72,7 @@ debug opt script: rel $(INSTALL_SCRIPTS) $(RELEASES_SRC)
rel: $(REL_SCRIPTS)
-RELEASES.src:
+RELEASES.src: $(SS_ROOT)/start_sasl.rel
$(gen_verbose)$(INSTALL_DIR) $(SS_TMP)
$(V_at)( cd $(SS_TMP) && \
$(ERL) -noinput +B -eval 'release_handler:create_RELEASES("%ERL_ROOT%", "$(SS_ROOT)", "$(SS_ROOT)/start_sasl.rel", []), halt()')
diff --git a/erts/test/install_SUITE.erl b/erts/test/install_SUITE.erl
index 2c7e8972f6..d6c6d6f30e 100644
--- a/erts/test/install_SUITE.erl
+++ b/erts/test/install_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -18,7 +18,6 @@
%% %CopyrightEnd%
%%
-
%%%-------------------------------------------------------------------
%%% File : install_SUITE.erl
%%% Author : Rickard Green
@@ -63,12 +62,12 @@
erlang_bindir = "",
bindir_symlinks = ""}).
-need_symlink_cases() ->
+need_symlink_cases() ->
[bin_unreachable_absolute, bin_unreachable_relative,
bin_same_dir, bin_ok_symlink, bin_dirname_fail,
bin_no_use_dirname_fail].
-dont_need_symlink_cases() ->
+dont_need_symlink_cases() ->
[bin_default, bin_default_dirty, bin_outside_eprfx,
bin_outside_eprfx_dirty, bin_not_abs,
bin_unreasonable_path, 'bin white space',
@@ -78,10 +77,9 @@ suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 1}}].
-all() ->
+all() ->
dont_need_symlink_cases() ++ need_symlink_cases().
-
%%
%% The test cases
%%
@@ -533,21 +531,19 @@ bin_no_srcfile(Config) when is_list(Config) ->
ChkRes).
%%
-%%
%% Auxiliary functions
%%
-%%
expect(X, X) ->
- io:format("result: ~p~n", [X]),
+ io:format("result: ~tp~n", [X]),
io:format("-----------------------------------------------~n", []),
ok;
expect(X, Y) ->
- io:format("expected: ~p~n", [X]),
- io:format("got : ~p~n", [Y]),
+ io:format("expected: ~tp~n", [X]),
+ io:format("got : ~tp~n", [Y]),
io:format("-----------------------------------------------~n", []),
ct:fail({X,Y}).
-
+
init_per_suite(Config) ->
PD = proplists:get_value(priv_dir, Config),
SymLinks = case os:type() of
@@ -630,8 +626,8 @@ install_bin(Config, #inst{mkdirs = MkDirs,
true -> ok;
false -> {comment, "No symlink tests run, since symlinks not working"}
end.
-
-
+
+
install_bin2(Config, Inst, ChkRes) ->
install_bin3(Config, Inst#inst{symlinks = false,
ln_s = "ln"}, ChkRes),
@@ -662,8 +658,6 @@ install_bin2(Config, Inst, ChkRes) ->
false ->
ok
end.
-
-
install_bin3(Config,
#inst{cmd_prefix = CMD_PRFX,
@@ -690,20 +684,20 @@ install_bin3(Config,
++ "\" --exec-prefix \"" ++ EXEC_PREFIX
++ "\" --test-file \"" ++ ResFile ++ "\" erl erlc",
- io:format("CMD_PRFX = \"~s\"~n"
- "LN_S = \"~s\"~n"
- "BINDIR_SYMLINKS = \"~s\"~n"
- "exec_prefix = \"~s\"~n"
- "bindir = \"~s\"~n"
- "erlang_bindir = \"~s\"~n"
- "EXTRA_PREFIX = \"~s\"~n"
- "DESTDIR = \"~s\"~n",
+ io:format("CMD_PRFX = \"~ts\"~n"
+ "LN_S = \"~ts\"~n"
+ "BINDIR_SYMLINKS = \"~ts\"~n"
+ "exec_prefix = \"~ts\"~n"
+ "bindir = \"~ts\"~n"
+ "erlang_bindir = \"~ts\"~n"
+ "EXTRA_PREFIX = \"~ts\"~n"
+ "DESTDIR = \"~ts\"~n",
[CMD_PRFX, LN_S, BINDIR_SYMLINKS, EXEC_PREFIX, BINDIR,
ERLANG_BINDIR, EXTRA_PREFIX, DESTDIR]),
- io:format("$ ~s~n", [Cmd]),
+ io:format("$ ~ts~n", [Cmd]),
CmdOutput = os:cmd(Cmd),
- io:format("~s~n", [CmdOutput]),
+ io:format("~ts~n", [CmdOutput]),
ChkRes(case file:consult(ResFile) of
{ok, [Res]} -> Res;
Err -> exit({result, Err})
diff --git a/erts/test/run_erl_SUITE.erl b/erts/test/run_erl_SUITE.erl
index 47d38bde7c..fe1ccba1e2 100644
--- a/erts/test/run_erl_SUITE.erl
+++ b/erts/test/run_erl_SUITE.erl
@@ -22,6 +22,7 @@
-export([all/0, suite/0]).
-export([basic/1,heavy/1,heavier/1,defunct/1]).
+-export([sleepy_child/1]).
-export([ping_me_back/1]).
-include_lib("common_test/include/ct.hrl").
@@ -31,17 +32,17 @@ suite() ->
{timetrap, {minutes, 2}}].
all() ->
- [basic, heavy, heavier, defunct].
+ [basic, sleepy_child, heavy, heavier, defunct].
basic(Config) when is_list(Config) ->
case os:type() of
- {unix,_} -> basic_1(Config);
+ {unix,_} -> basic_1(Config, "basic", "");
_ -> {skip,"Not Unix"}
end.
-basic_1(Config) ->
- {Node,Pipe} = do_run_erl(Config, "basic"),
+basic_1(Config, Case, Opt) ->
+ {Node,Pipe} = do_run_erl(Config, Case, Opt),
ToErl = open_port({spawn,"to_erl "++Pipe}, []),
erlang:port_command(ToErl, "halt().\r\n"),
@@ -55,6 +56,12 @@ basic_1(Config) ->
ok.
+sleepy_child(Config) when is_list(Config) ->
+ case os:type() of
+ {unix,_} -> basic_1(Config, "sleepy_child", "-sleepy-child ");
+ _ -> {skip,"Not Unix"}
+ end.
+
heavy(Config) when is_list(Config) ->
case os:type() of
{unix,_} -> heavy_1(Config);
@@ -233,12 +240,14 @@ defunct_2(Config, Perl) ->
%%% Utilities.
do_run_erl(Config, Case) ->
+ do_run_erl(Config, Case, "").
+do_run_erl(Config, Case, Opt) ->
Priv = proplists:get_value(priv_dir, Config),
LogDir = filename:join(Priv, Case),
ok = file:make_dir(LogDir),
Pipe = LogDir ++ "/",
NodeName = "run_erl_node_" ++ Case,
- Cmd = "run_erl "++Pipe++" "++LogDir++" \"erl -sname " ++ NodeName ++
+ Cmd = "run_erl "++Opt++Pipe++" "++LogDir++" \"erl -sname " ++ NodeName ++
" -pa " ++ filename:dirname(code:which(?MODULE)) ++
" -s " ++ ?MODULE_STRING ++ " ping_me_back " ++
atom_to_list(node()) ++ "\"",
diff --git a/erts/test/system_smoke.spec b/erts/test/system_smoke.spec
index 933d1ba22d..99092c1dab 100644
--- a/erts/test/system_smoke.spec
+++ b/erts/test/system_smoke.spec
@@ -1,3 +1,8 @@
{suites,"../system_test",[ethread_SUITE]}.
-{cases,"../system_test",otp_SUITE,[undefined_functions]}.
+{cases,"../system_test",otp_SUITE,
+ [undefined_functions,
+ deprecated_not_in_obsolete,
+ obsolete_but_not_deprecated,
+ call_to_size_1,
+ call_to_now_0]}.
{skip_cases,"../system_test",ethread_SUITE,[max_threads],"Skip"}.
diff --git a/erts/test/upgrade_SUITE.erl b/erts/test/upgrade_SUITE.erl
index 174c028ac7..aa7a33bb3f 100644
--- a/erts/test/upgrade_SUITE.erl
+++ b/erts/test/upgrade_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2014-2017. 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.
@@ -54,6 +54,10 @@ init_per_suite(Config) ->
Config
end.
+end_per_suite(_Config) ->
+ %% This function is required since init_per_suite/1 exists.
+ ok.
+
init_per_testcase(Case,Config) ->
PrivDir = filename:join([proplists:get_value(data_dir,Config),priv_dir,Case]),
CreateDir = filename:join([PrivDir,create]),
diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl
index 281a47134f..d474c71c4f 100644
--- a/erts/test/z_SUITE.erl
+++ b/erts/test/z_SUITE.erl
@@ -214,7 +214,19 @@ dump_core(#core_search_conf{ cerl = Cerl }, Core) ->
format_core(Conf, {ignore, Core}) ->
format_core(Conf, Core, "[ignored] ");
format_core(Conf, Core) ->
- format_core(Conf, Core, "").
+ format_core(Conf, Core, ""),
+
+ %% Try print (log dir) name of offending application
+ CoreDir = filename:dirname(Core),
+ lists:foreach(fun(TestDir) ->
+ case filelib:is_dir(filename:join(CoreDir,TestDir)) of
+ true ->
+ io:format(" from ~s~n", [TestDir]);
+ false ->
+ no
+ end
+ end,
+ filelib:wildcard("*.logs", CoreDir)).
format_core(#core_search_conf{file = false}, Core, Ignore) ->
io:format(" ~s~s " ++ time_fstr() ++ "~s~n",
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 6ad3680213..7cb89369f2 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
+# Copyright Ericsson AB 1997-2017. 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.
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 8.0
+VSN = 8.3.5.6
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/asn1/doc/src/asn1_getting_started.xml b/lib/asn1/doc/src/asn1_getting_started.xml
index 3c8ec24723..d40b294c39 100644
--- a/lib/asn1/doc/src/asn1_getting_started.xml
+++ b/lib/asn1/doc/src/asn1_getting_started.xml
@@ -678,7 +678,7 @@ ok
1081,32,1043,1085,1086,1084]</pre>
<p>For details, see the <seealso marker="stdlib:unicode">unicode</seealso>
- module in <c>stdlib</c>.</p>
+ module in STDLIB.</p>
<p>In the following example, this ASN.1 specification is used:</p>
<pre>
diff --git a/lib/asn1/doc/src/asn1_introduction.xml b/lib/asn1/doc/src/asn1_introduction.xml
index d8b81aa467..e4f406364d 100644
--- a/lib/asn1/doc/src/asn1_introduction.xml
+++ b/lib/asn1/doc/src/asn1_introduction.xml
@@ -30,7 +30,7 @@
<file>asn1_introduction.xml</file>
</header>
- <p>The <c>ASN.1</c> application provides the following:</p>
+ <p>The ASN.1 application provides the following:</p>
<list type="bulleted">
<item>An ASN.1 compiler for Erlang, which generates encode and
diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index 68d335f451..499a7e40c3 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 4.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Compiling multiple ASN.1 modules in the same directory
+ with parallel make (make -j) should now be safe.</p>
+ <p>
+ Own Id: OTP-13624</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 4.0.3</title>
<section><title>Improvements and New Features</title>
@@ -329,7 +345,7 @@
</item>
<item>
<p>
- The <c>asn1</c> application would fail to build if the
+ The ASN.1 application would fail to build if the
<c>.erlang</c> file printed something to standard output.</p>
<p>
Own Id: OTP-11360</p>
@@ -997,7 +1013,7 @@
also been extended. </item><item> The <c>configure</c>
scripts of <c>erl_interface</c> and <c>odbc</c> now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl
index 557eca0ffd..869ea310aa 100644
--- a/lib/asn1/src/asn1_db.erl
+++ b/lib/asn1/src/asn1_db.erl
@@ -106,7 +106,9 @@ loop(#state{parent = Parent, monitor = MRef, table = Table,
loop(State);
{save, OutFile, Mod} ->
[{_,Mtab}] = ets:lookup(Table, Mod),
- ok = ets:tab2file(Mtab, OutFile),
+ TempFile = OutFile ++ ".#temp",
+ ok = ets:tab2file(Mtab, TempFile),
+ ok = file:rename(TempFile, OutFile),
loop(State);
{From, {new, Mod, Erule}} ->
[] = ets:lookup(Table, Mod), %Assertion.
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index dd269f095d..8783b5418d 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -221,9 +221,8 @@ check_pass(#st{code=M,file=File,includes=Includes,
{error,St#st{error=Reason}}
end.
-save_pass(#st{code=M,erule=Erule,dbfile=DbFile}=St) ->
+save_pass(#st{code=M,erule=Erule}=St) ->
ok = asn1ct_check:storeindb(#state{erule=Erule}, M),
- asn1_db:dbsave(DbFile,M#module.name),
{ok,St}.
parse_listing(#st{code=Code,outfile=OutFile0}=St) ->
diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl
index 1b4c3b3c77..dc614db4f2 100644
--- a/lib/asn1/test/asn1_test_lib.erl
+++ b/lib/asn1/test/asn1_test_lib.erl
@@ -34,12 +34,16 @@ run_dialyzer() ->
compile(File, Config, Options) -> compile_all([File], Config, Options).
-compile_all(Files, Config, Options) ->
+compile_all(Files, Config, Options0) ->
DataDir = proplists:get_value(data_dir, Config),
CaseDir = proplists:get_value(case_dir, Config),
- [compile_file(filename:join(DataDir, F), [{outdir, CaseDir},
- debug_info|Options])
- || F <- Files],
+ Options = [{outdir,CaseDir},debug_info|Options0],
+
+ Comp = fun(F) ->
+ compile_file(filename:join(DataDir, F), Options)
+ end,
+ p_run(Comp, Files),
+
dialyze(Files, Options),
ok.
@@ -94,9 +98,9 @@ compile_file(File, Options) ->
try
ok = asn1ct:compile(File, [warnings_as_errors|Options])
catch
- Class:Reason ->
- ct:print("Failed to compile ~s\n", [File]),
- erlang:error({compile_failed, {File, Options}, {Class, Reason}})
+ _:Reason ->
+ ct:print("Failed to compile ~s\n~p", [File,Reason]),
+ error
end.
compile_erlang(Mod, Config, Options) ->
@@ -219,3 +223,38 @@ ber_get_len(<<0:1,L:7,T/binary>>) ->
ber_get_len(<<1:1,Octets:7,T0/binary>>) ->
<<L:Octets/unit:8,T/binary>> = T0,
{L,T}.
+
+%% p_run(fun(Data) -> ok|error, List) -> ok
+%% Will fail the test case if there were any errors.
+
+p_run(Test, List) ->
+ S = erlang:system_info(schedulers),
+ N = case test_server:is_cover() of
+ false ->
+ S + 1;
+ true ->
+ %% Cover is running. Using too many processes
+ %% could slow us down.
+ min(S, 4)
+ end,
+ %%io:format("p_run: ~p parallel processes\n", [N]),
+ p_run_loop(Test, List, N, [], 0).
+
+p_run_loop(_, [], _, [], Errors) ->
+ case Errors of
+ 0 -> ok;
+ N -> ct:fail({N,errors})
+ end;
+p_run_loop(Test, [H|T], N, Refs, Errors) when length(Refs) < N ->
+ {_,Ref} = erlang:spawn_monitor(fun() -> exit(Test(H)) end),
+ p_run_loop(Test, T, N, [Ref|Refs], Errors);
+p_run_loop(Test, List, N, Refs0, Errors0) ->
+ receive
+ {'DOWN',Ref,process,_,Res} ->
+ Errors = case Res of
+ ok -> Errors0;
+ error -> Errors0+1
+ end,
+ Refs = Refs0 -- [Ref],
+ p_run_loop(Test, List, N, Refs, Errors)
+ end.
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 527af05da1..e4bf3e2236 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1 +1 @@
-ASN1_VSN = 4.0.3
+ASN1_VSN = 4.0.4
diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile
index e495f587a3..152ece5d25 100644
--- a/lib/common_test/doc/src/Makefile
+++ b/lib/common_test/doc/src/Makefile
@@ -53,7 +53,8 @@ XML_REF3_FILES = ct.xml \
ct_slave.xml \
ct_property_test.xml \
ct_netconfc.xml \
- ct_hooks.xml
+ ct_hooks.xml \
+ ct_testspec.xml
XML_REF6_FILES = common_test_app.xml
XML_PART_FILES = part.xml
diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml
index 3f83747485..a3b3f927eb 100644
--- a/lib/common_test/doc/src/common_test_app.xml
+++ b/lib/common_test/doc/src/common_test_app.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2016</year>
+ <year>2003</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -224,7 +224,9 @@
</type>
<desc>
- <p>OPTIONAL</p>
+ <p>OPTIONAL; if this function is defined, then <seealso
+ marker="#Module:end_per_suite-1"><c>end_per_suite/1</c></seealso>
+ must also be defined.</p>
<p>This configuration function is called as the first function in the
suite. It typically contains initializations that are common for
@@ -256,7 +258,9 @@
</type>
<desc>
- <p>OPTIONAL</p>
+ <p>OPTIONAL; if this function is defined, then <seealso
+ marker="#Module:init_per_suite-1"><c>init_per_suite/1</c></seealso>
+ must also be defined.</p>
<p>This function is called as the last test case in the
suite. It is meant to be used for cleaning up after
@@ -360,7 +364,9 @@
</type>
<desc>
- <p>OPTIONAL</p>
+ <p>OPTIONAL; if this function is defined, then <seealso
+ marker="#Module:end_per_group-2"><c>end_per_group/2</c></seealso>
+ must also be defined.</p>
<p>This configuration function is called before execution of a
test case group. It typically contains initializations that are
@@ -396,7 +402,9 @@
</type>
<desc>
- <p>OPTIONAL</p>
+ <p>OPTIONAL; if this function is defined, then <seealso
+ marker="#Module:init_per_group-2"><c>init_per_group/2</c></seealso>
+ must also be defined.</p>
<p>This function is called after the execution of a test case group
is finished. It is meant to be used for cleaning up after
@@ -427,7 +435,10 @@
</type>
<desc>
- <p>OPTIONAL</p>
+ <p>OPTIONAL; if this function is defined,
+ then <seealso marker="#Module:end_per_testcase-2">
+ <c>end_per_testcase/2</c></seealso> must also be
+ defined.</p>
<p>This function is called before each test case. Argument
<c>TestCase</c> is the test case name, and
@@ -454,7 +465,10 @@
</type>
<desc>
- <p>OPTIONAL</p>
+ <p>OPTIONAL; if this function is defined,
+ then <seealso marker="#Module:init_per_testcase-2">
+ <c>init_per_testcase/2</c></seealso> must also be
+ defined.</p>
<p>This function is called after each test case, and can be used
to clean up after
@@ -566,7 +580,7 @@
(which also causes the test case process to terminate).</p>
<p>Elements from the <c>Config</c> list can, for example, be read
- with <c>proplists:get_value/2</c> in <c>STDLIB</c>
+ with <c>proplists:get_value/2</c> in STDLIB
(or the macro <c>?config</c> defined in <c>ct.hrl</c>).</p>
<p>If you decide not to run the test case after all, return
diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml
index 264bcff251..ea9f956271 100644
--- a/lib/common_test/doc/src/ct.xml
+++ b/lib/common_test/doc/src/ct.xml
@@ -620,6 +620,21 @@
</func>
<func>
+ <name>get_verbosity(Category) -&gt; Level | undefined</name>
+ <fsummary>Read the verbosity level for a logging category.</fsummary>
+ <type>
+ <v>Category = default | atom()</v>
+ <v>Level = integer()</v>
+ </type>
+ <desc><marker id="get_verbosity-1"/>
+ <p>This function returns the verbosity level for the specified logging
+ category. See the <seealso marker="write_test_chapter#logging">
+ User's Guide</seealso> for details. Use the value <c>default</c> to read
+ the general verbosity level.</p>
+ </desc>
+ </func>
+
+ <func>
<name>install(Opts) -&gt; ok | {error, Reason}</name>
<fsummary>Installs configuration files and event handlers.</fsummary>
<type>
@@ -725,7 +740,7 @@
<v>Format = string()</v>
<v>FormatArgs = list()</v>
<v>Opts = [Opt]</v>
- <v>Opt = no_css | esc_chars</v>
+ <v>Opt = {heading,string()} | no_css | esc_chars</v>
</type>
<desc><marker id="log-5"/>
<p>Prints from a test case to the log file.</p>
@@ -777,59 +792,77 @@
caught by any installed event manager.</p>
<p>See also
- <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p>
+ <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.</p>
</desc>
</func>
<func>
<name>pal(Format) -&gt; ok</name>
- <fsummary>Equivalent to pal(default, 50, Format, []).</fsummary>
+ <fsummary>Equivalent to pal(default, 50, Format, [], []).</fsummary>
<desc><marker id="pal-1"/>
<p>Equivalent to
- <seealso marker="#pal-4"><c>ct:pal(default, 50, Format,
- [])</c></seealso>.</p>
+ <seealso marker="#pal-5"><c>ct:pal(default, 50, Format,
+ [], [])</c></seealso>.</p>
</desc>
</func>
<func>
<name>pal(X1, X2) -&gt; ok</name>
<fsummary>Equivalent to pal(Category, Importance, Format,
- FormatArgs).</fsummary>
+ FormatArgs, []).</fsummary>
<type>
<v>X1 = Category | Importance | Format</v>
<v>X2 = Format | FormatArgs</v>
</type>
<desc><marker id="pal-2"/>
- <p>Equivalent to <seealso marker="#pal-4"><c>ct:pal(Category,
- Importance, Format, FormatArgs)</c></seealso>.</p>
+ <p>Equivalent to <seealso marker="#pal-5"><c>ct:pal(Category,
+ Importance, Format, FormatArgs, [])</c></seealso>.</p>
</desc>
</func>
<func>
<name>pal(X1, X2, X3) -&gt; ok</name>
<fsummary>Equivalent to pal(Category, Importance, Format,
- FormatArgs).</fsummary>
+ FormatArgs, Opts).</fsummary>
<type>
<v>X1 = Category | Importance</v>
<v>X2 = Importance | Format</v>
- <v>X3 = Format | FormatArgs</v>
+ <v>X3 = Format | FormatArgs | Opts</v>
</type>
<desc><marker id="pal-3"/>
- <p>Equivalent to <seealso marker="#pal-4"><c>ct:pal(Category,
- Importance, Format, FormatArgs)</c></seealso>.</p>
+ <p>Equivalent to <seealso marker="#pal-5"><c>ct:pal(Category,
+ Importance, Format, FormatArgs, Opts)</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pal(X1, X2, X3, X4) -&gt; ok</name>
+ <fsummary>Equivalent to pal(Category, Importance, Format,
+ FormatArgs, Opts).</fsummary>
+ <type>
+ <v>X1 = Category | Importance</v>
+ <v>X2 = Importance | Format</v>
+ <v>X3 = Format | FormatArgs</v>
+ <v>X4 = FormatArgs | Opts</v>
+ </type>
+ <desc><marker id="pal-4"/>
+ <p>Equivalent to <seealso marker="#pal-5"><c>ct:pal(Category,
+ Importance, Format, FormatArgs, Opts)</c></seealso>.</p>
</desc>
</func>
<func>
- <name>pal(Category, Importance, Format, FormatArgs) -&gt; ok</name>
+ <name>pal(Category, Importance, Format, FormatArgs, Opts) -&gt; ok</name>
<fsummary>Prints and logs from a test case.</fsummary>
<type>
<v>Category = atom()</v>
<v>Importance = integer()</v>
<v>Format = string()</v>
<v>FormatArgs = list()</v>
+ <v>Opts = [Opt]</v>
+ <v>Opt = {heading,string()} | no_css</v>
</type>
- <desc><marker id="pal-4"/>
+ <desc><marker id="pal-5"/>
<p>Prints and logs from a test case.</p>
<p>This function is meant for printing a string from a test case,
@@ -873,52 +906,70 @@
<func>
<name>print(Format) -&gt; ok</name>
- <fsummary>Equivalent to print(default, 50, Format, []).</fsummary>
+ <fsummary>Equivalent to print(default, 50, Format, [], []).</fsummary>
<desc><marker id="print-1"/>
- <p>Equivalent to <seealso marker="#print-4"><c>ct:print(default,
- 50, Format, [])</c></seealso>.</p>
+ <p>Equivalent to <seealso marker="#print-5"><c>ct:print(default,
+ 50, Format, [], [])</c></seealso>.</p>
</desc>
</func>
<func>
<name>print(X1, X2) -&gt; ok</name>
<fsummary>Equivalent to print(Category, Importance, Format,
- FormatArgs).</fsummary>
+ FormatArgs, []).</fsummary>
<type>
<v>X1 = Category | Importance | Format</v>
<v>X2 = Format | FormatArgs</v>
</type>
<desc><marker id="print-2"/>
- <p>Equivalent to <seealso marker="#print-4"><c>ct:print(Category,
- Importance, Format, FormatArgs)</c></seealso>.</p>
+ <p>Equivalent to <seealso marker="#print-5"><c>ct:print(Category,
+ Importance, Format, FormatArgs, [])</c></seealso>.</p>
</desc>
</func>
<func>
<name>print(X1, X2, X3) -&gt; ok</name>
<fsummary>Equivalent to print(Category, Importance, Format,
- FormatArgs).</fsummary>
+ FormatArgs, Opts).</fsummary>
<type>
<v>X1 = Category | Importance</v>
<v>X2 = Importance | Format</v>
- <v>X3 = Format | FormatArgs</v>
+ <v>X3 = Format | FormatArgs | Opts</v>
</type>
<desc><marker id="print-3"/>
- <p>Equivalent to <seealso marker="#print-4"><c>ct:print(Category,
- Importance, Format, FormatArgs)</c></seealso>.</p>
+ <p>Equivalent to <seealso marker="#print-5"><c>ct:print(Category,
+ Importance, Format, FormatArgs, Opts)</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>print(X1, X2, X3, X4) -&gt; ok</name>
+ <fsummary>Equivalent to print(Category, Importance, Format,
+ FormatArgs, Opts).</fsummary>
+ <type>
+ <v>X1 = Category | Importance</v>
+ <v>X2 = Importance | Format</v>
+ <v>X3 = Format | FormatArgs</v>
+ <v>X4 = FormatArgs | Opts</v>
+ </type>
+ <desc><marker id="print-4"/>
+ <p>Equivalent to <seealso marker="#print-5"><c>ct:print(Category,
+ Importance, Format, FormatArgs, Opts)</c></seealso>.</p>
</desc>
</func>
<func>
- <name>print(Category, Importance, Format, FormatArgs) -&gt; ok</name>
+ <name>print(Category, Importance, Format, FormatArgs, Opts) -&gt; ok</name>
<fsummary>Prints from a test case to the console.</fsummary>
<type>
<v>Category = atom()</v>
<v>Importance = integer()</v>
<v>Format = string()</v>
<v>FormatArgs = list()</v>
+ <v>Opts = [Opt]</v>
+ <v>Opt = {heading,string()}</v>
</type>
- <desc><marker id="print-4"/>
+ <desc><marker id="print-5"/>
<p>Prints from a test case to the console.</p>
<p>This function is meant for printing a string from a test case to
@@ -1225,6 +1276,21 @@
</func>
<func>
+ <name>set_verbosity(Category, Level) -&gt; ok</name>
+ <fsummary>Set the verbosity level for a logging category.</fsummary>
+ <type>
+ <v>Category = default | atom()</v>
+ <v>Level = integer()</v>
+ </type>
+ <desc><marker id="set_verbosity-2"/>
+ <p>Use this function to set, or modify, the verbosity level for a logging
+ category. See the <seealso marker="write_test_chapter#logging">
+ User's Guide</seealso> for details. Use the value <c>default</c> to set the
+ general verbosity level.</p>
+ </desc>
+ </func>
+
+ <func>
<name>sleep(Time) -&gt; ok</name>
<fsummary>This function, similar to timer:sleep/1, suspends the
test case for a specified time.</fsummary>
@@ -1236,7 +1302,7 @@
<v>Millisecs = integer() | float()</v>
</type>
<desc><marker id="sleep-1"/>
- <p>This function, similar to <c>timer:sleep/1</c> in <c>STDLIB</c>,
+ <p>This function, similar to <c>timer:sleep/1</c> in STDLIB,
suspends the test case for a specified time.
However, this function also multiplies <c>Time</c> with the
<c>multiply_timetraps</c> value (if set) and under certain
@@ -1330,7 +1396,7 @@
caught by any installed event manager.</p>
<p>See also
- <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.
+ <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.
</p>
</desc>
</func>
diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml
index 3b1e564b66..954be0ffba 100644
--- a/lib/common_test/doc/src/ct_hooks.xml
+++ b/lib/common_test/doc/src/ct_hooks.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2010</year><year>2016</year>
+ <year>2010</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -94,7 +94,7 @@
<seealso marker="#Module:id-1"><c>ct_hooks:id/1</c></seealso>,
or a <c>reference</c> (created using
<seealso marker="erts:erlang#make_ref-0">erlang:make_ref/0</seealso>
- in <c>ERTS</c>) if
+ in ERTS) if
<seealso marker="#Module:id-1"><c>ct_hooks:id/1</c></seealso>
is not implemented.</p>
@@ -208,9 +208,10 @@
</func>
<func>
- <name>Module:pre_init_per_group(GroupName, InitData, CTHState) -&gt; Result</name>
+ <name>Module:pre_init_per_group(SuiteName, GroupName, InitData, CTHState) -&gt; Result</name>
<fsummary>Called before init_per_group.</fsummary>
<type>
+ <v>SuiteName = atom()</v>
<v>GroupName = atom()</v>
<v>InitData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
@@ -231,13 +232,19 @@
but for function
<seealso marker="common_test#Module:init_per_group-2"><c>init_per_group</c></seealso>
instead.</p>
+
+ <p>If <c>Module:pre_init_per_group/4</c> is not exported, common_test
+ will attempt to call <c>Module:pre_init_per_group(GroupName,
+ InitData, CTHState)</c> instead. This is for backwards
+ compatibility.</p>
</desc>
</func>
<func>
- <name>Module:post_init_per_group(GroupName, Config, Return, CTHState) -&gt; Result</name>
+ <name>Module:post_init_per_group(SuiteName, GroupName, Config, Return, CTHState) -&gt; Result</name>
<fsummary>Called after init_per_group.</fsummary>
<type>
+ <v>SuiteName = atom()</v>
<v>GroupName = atom()</v>
<v>Config = [{Key,Value}]</v>
<v>Return = NewReturn = Config | SkipOrFail | term()</v>
@@ -258,13 +265,19 @@
but for function
<seealso marker="common_test#Module:init_per_group-2"><c>init_per_group</c></seealso>
instead.</p>
+
+ <p>If <c>Module:post_init_per_group/5</c> is not exported, common_test
+ will attempt to call <c>Module:post_init_per_group(GroupName,
+ Config, Return, CTHState)</c> instead. This is for backwards
+ compatibility.</p>
</desc>
</func>
<func>
- <name>Module:pre_init_per_testcase(TestcaseName, InitData, CTHState) -&gt; Result</name>
+ <name>Module:pre_init_per_testcase(SuiteName, TestcaseName, InitData, CTHState) -&gt; Result</name>
<fsummary>Called before init_per_testcase.</fsummary>
<type>
+ <v>SuiteName = atom()</v>
<v>TestcaseName = atom()</v>
<v>InitData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
@@ -286,6 +299,11 @@
<seealso marker="common_test#Module:init_per_testcase-2"><c>init_per_testcase</c></seealso>
instead.</p>
+ <p>If <c>Module:pre_init_per_testcase/4</c> is not exported, common_test
+ will attempt to call <c>Module:pre_init_per_testcase(TestcaseName,
+ InitData, CTHState)</c> instead. This is for backwards
+ compatibility.</p>
+
<p>CTHs cannot be added here right now. That feature may be added in
a later release, but it would right now break backwards
compatibility.</p>
@@ -293,9 +311,10 @@
</func>
<func>
- <name>Module:post_init_per_testcase(TestcaseName, Config, Return, CTHState) -&gt; Result</name>
+ <name>Module:post_init_per_testcase(SuiteName, TestcaseName, Config, Return, CTHState) -&gt; Result</name>
<fsummary>Called after init_per_testcase.</fsummary>
<type>
+ <v>SuiteName = atom()</v>
<v>TestcaseName = atom()</v>
<v>Config = [{Key,Value}]</v>
<v>Return = NewReturn = Config | SkipOrFail | term()</v>
@@ -316,15 +335,21 @@
but for function
<seealso marker="common_test#Module:init_per_testcase-2"><c>init_per_testcase</c></seealso>
instead.</p>
+
+ <p>If <c>Module:post_init_per_testcase/5</c> is not exported, common_test
+ will attempt to call <c>Module:post_init_per_testcase(TestcaseName,
+ Config, Return, CTHState)</c> instead. This is for backwards
+ compatibility.</p>
</desc>
</func>
<func>
- <name>Module:pre_end_per_testcase(TestcaseName, InitData, CTHState) -&gt; Result</name>
+ <name>Module:pre_end_per_testcase(SuiteName, TestcaseName, EndData, CTHState) -&gt; Result</name>
<fsummary>Called before end_per_testcase.</fsummary>
<type>
+ <v>SuiteName = atom()</v>
<v>TestcaseName = atom()</v>
- <v>InitData = Config</v>
+ <v>EndData = Config</v>
<v>Config = NewConfig = [{Key,Value}]</v>
<v>CTHState = NewCTHState = term()</v>
<v>Result = {NewConfig, NewCTHState}</v>
@@ -345,14 +370,20 @@
<p>This function can not change the result of the test case by returning skip or fail
tuples, but it may insert items in <c>Config</c> that can be read in
- <c>end_per_testcase/2</c> or in <c>post_end_per_testcase/4</c>.</p>
+ <c>end_per_testcase/2</c> or in <c>post_end_per_testcase/5</c>.</p>
+
+ <p>If <c>Module:pre_end_per_testcase/4</c> is not exported, common_test
+ will attempt to call <c>Module:pre_end_per_testcase(TestcaseName,
+ EndData, CTHState)</c> instead. This is for backwards
+ compatibility.</p>
</desc>
</func>
<func>
- <name>Module:post_end_per_testcase(TestcaseName, Config, Return, CTHState) -&gt; Result</name>
+ <name>Module:post_end_per_testcase(SuiteName, TestcaseName, Config, Return, CTHState) -&gt; Result</name>
<fsummary>Called after end_per_testcase.</fsummary>
<type>
+ <v>SuiteName = atom()</v>
<v>TestcaseName = atom()</v>
<v>Config = [{Key,Value}]</v>
<v>Return = NewReturn = Config | SkipOrFail | term()</v>
@@ -373,13 +404,19 @@
but for function
<seealso marker="common_test#Module:end_per_testcase-2"><c>end_per_testcase</c></seealso>
instead.</p>
+
+ <p>If <c>Module:post_end_per_testcase/5</c> is not exported, common_test
+ will attempt to call <c>Module:post_end_per_testcase(TestcaseName,
+ Config, Return, CTHState)</c> instead. This is for backwards
+ compatibility.</p>
</desc>
</func>
<func>
- <name>Module:pre_end_per_group(GroupName, EndData, CTHState) -&gt; Result</name>
+ <name>Module:pre_end_per_group(SuiteName, GroupName, EndData, CTHState) -&gt; Result</name>
<fsummary>Called before end_per_group.</fsummary>
<type>
+ <v>SuiteName = atom()</v>
<v>GroupName = atom()</v>
<v>EndData = Config | SkipOrFail</v>
<v>Config = NewConfig = [{Key,Value}]</v>
@@ -400,13 +437,19 @@
but for function
<seealso marker="common_test#Module:end_per_group-2"><c>end_per_group</c></seealso>
instead.</p>
+
+ <p>If <c>Module:pre_end_per_group/4</c> is not exported, common_test
+ will attempt to call <c>Module:pre_end_per_group(GroupName,
+ EndData, CTHState)</c> instead. This is for backwards
+ compatibility.</p>
</desc>
</func>
<func>
- <name>Module:post_end_per_group(GroupName, Config, Return, CTHState) -&gt; Result</name>
+ <name>Module:post_end_per_group(SuiteName, GroupName, Config, Return, CTHState) -&gt; Result</name>
<fsummary>Called after end_per_group.</fsummary>
<type>
+ <v>SuiteName = atom()</v>
<v>GroupName = atom()</v>
<v>Config = [{Key,Value}]</v>
<v>Return = NewReturn = Config | SkipOrFail | term()</v>
@@ -427,6 +470,11 @@
but for function
<seealso marker="common_test#Module:end_per_group-2">end_per_group</seealso>
instead.</p>
+
+ <p>If <c>Module:post_end_per_group/5</c> is not exported, common_test
+ will attempt to call <c>Module:post_end_per_group(GroupName,
+ Config, Return, CTHState)</c> instead. This is for backwards
+ compatibility.</p>
</desc>
</func>
@@ -485,9 +533,10 @@
</func>
<func>
- <name>Module:on_tc_fail(TestName, Reason, CTHState) -&gt; NewCTHState</name>
+ <name>Module:on_tc_fail(SuiteName, TestName, Reason, CTHState) -&gt; NewCTHState</name>
<fsummary>Called after the CTH scope ends.</fsummary>
<type>
+ <v>SuiteName = atom()</v>
<v>TestName = init_per_suite | end_per_suite | {init_per_group,GroupName} | {end_per_group,GroupName} | {FuncName,GroupName} | FuncName</v>
<v>FuncName = atom()</v>
<v>GroupName = atom()</v>
@@ -505,7 +554,7 @@
<item><p>If <c>init_per_suite</c> fails, this function is called after
<seealso marker="#Module:post_init_per_suite-4"><c>post_init_per_suite</c></seealso>.</p></item>
<item><p>If a test case fails, this funcion is called after
- <seealso marker="#Module:post_end_per_testcase-4"><c>post_end_per_testcase</c></seealso>.</p></item>
+ <seealso marker="#Module:post_end_per_testcase-5"><c>post_end_per_testcase</c></seealso>.</p></item>
</list>
<p>If the failed test case belongs to a test case group, the first
@@ -519,13 +568,19 @@
For details, see section
<seealso marker="event_handler_chapter#events">Event Handling</seealso>
in the User's Guide.</p>
+
+ <p>If <c>Module:on_tc_fail/4</c> is not exported, common_test
+ will attempt to call <c>Module:on_tc_fail(TestName, Reason,
+ CTHState)</c> instead. This is for backwards
+ compatibility.</p>
</desc>
</func>
<func>
- <name>Module:on_tc_skip(TestName, Reason, CTHState) -&gt; NewCTHState</name>
+ <name>Module:on_tc_skip(SuiteName, TestName, Reason, CTHState) -&gt; NewCTHState</name>
<fsummary>Called after the CTH scope ends.</fsummary>
<type>
+ <v>SuiteName = atom()</v>
<v>TestName = init_per_suite | end_per_suite | {init_per_group,GroupName} | {end_per_group,GroupName} | {FuncName,GroupName} | FuncName</v>
<v>FuncName = atom()</v>
<v>GroupName = atom()</v>
@@ -542,9 +597,9 @@
<list type="bulleted">
<item><p>If <c>init_per_group</c> is skipped, this function is
called after
- <seealso marker="#Module:post_init_per_group-4"><c>post_init_per_group</c></seealso>.</p></item>
+ <seealso marker="#Module:post_init_per_group-5"><c>post_init_per_group</c></seealso>.</p></item>
<item><p>If a test case is skipped, this function is called after
- <seealso marker="#Module:post_end_per_testcase-4"><c>post_end_per_testcase</c></seealso>.</p></item>
+ <seealso marker="#Module:post_end_per_testcase-5"><c>post_end_per_testcase</c></seealso>.</p></item>
</list>
<p>If the skipped test case belongs to a test case group, the first
@@ -559,6 +614,11 @@
For details, see section
<seealso marker="event_handler_chapter#events">Event Handling</seealso>
in the User's Guide.</p>
+
+ <p>If <c>Module:on_tc_skip/4</c> is not exported, common_test
+ will attempt to call <c>Module:on_tc_skip(TestName, Reason,
+ CTHState)</c> instead. This is for backwards
+ compatibility.</p>
</desc>
</func>
diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
index 1998f15697..7ecc2e4298 100644
--- a/lib/common_test/doc/src/ct_hooks_chapter.xml
+++ b/lib/common_test/doc/src/ct_hooks_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2011</year><year>2016</year>
+ <year>2011</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,7 +38,7 @@
extensions of the default behavior of <c>Common Test</c> using hooks
before and after all test suite calls. CTHs allow advanced <c>Common Test</c>
users to abstract out behavior that is common to multiple test suites
- without littering all test suites with library calls. this can be used
+ without littering all test suites with library calls. This can be used
for logging, starting, and monitoring external systems,
building C files needed by the tests, and so on.</p>
@@ -175,10 +175,10 @@
<row>
<cell><seealso marker="common_test#Module:init_per_group-2">
init_per_group/2</seealso></cell>
- <cell><seealso marker="ct_hooks#Module:post_init_per_group-4">
- post_init_per_group/4</seealso> is called</cell>
- <cell><seealso marker="ct_hooks#Module:post_end_per_suite-4">
- post_end_per_group/4</seealso> has been called for that group</cell>
+ <cell><seealso marker="ct_hooks#Module:post_init_per_group-5">
+ post_init_per_group/5</seealso> is called</cell>
+ <cell><seealso marker="ct_hooks#Module:post_end_per_group-5">
+ post_end_per_group/5</seealso> has been called for that group</cell>
</row>
<tcaption>Scope of a CTH</tcaption>
</table>
@@ -245,16 +245,18 @@
</list>
<p>
- This is done in the CTH functions called pre_&lt;name of function&gt;.
- These functions take the same three arguments, <c>Name</c>,
+ This is done in the CTH functions called <c>pre_&lt;name of function&gt;</c>.
+ These functions take the arguments <c>SuiteName</c>, <c>Name</c> (group or test case name, if applicable),
<c>Config</c>, and <c>CTHState</c>. The return value of the CTH function
is always a combination of a result for the suite/group/test and an
updated <c>CTHState</c>.</p>
<p>To let the test suite continue on executing, return the configuration
- list that you want the test to use as the result. To skip or
- fail the test, return a tuple with <c>skip</c> or <c>fail</c>, and a reason
- as the result.</p>
+ list that you want the test to use as the result.</p>
+
+ <p>All pre hooks, except <c>pre_end_per_testcase/4</c>, can
+ skip or fail the test by returning a tuple with <c>skip</c> or
+ <c>fail</c>, and a reason as the result.</p>
<p><em>Example:</em></p>
<code>
@@ -290,7 +292,7 @@
<p>
This is done in the CTH functions called <c>post_&lt;name of function&gt;</c>.
- These functions take the same four arguments, <c>Name</c>,
+ These functions take the arguments <c>SuiteName</c>, <c>Name</c> (group or test case name, if applicable),
<c>Config</c>, <c>Return</c>, and <c>CTHState</c>. <c>Config</c> in this
case is the same <c>Config</c> as the testcase is called with.
<c>Return</c> is the value returned by the testcase. If the testcase
@@ -308,7 +310,7 @@
<p><em>Example:</em></p>
<code>
- post_end_per_testcase(_TC, Config, {'EXIT',{_,_}}, CTHState) -&gt;
+ post_end_per_testcase(_Suite, _TC, Config, {'EXIT',{_,_}}, CTHState) -&gt;
case db:check_consistency() of
true ->
%% DB is good, pass the test.
@@ -317,7 +319,7 @@
%% DB is not good, mark as skipped instead of failing
{{skip, "DB is inconsisten!"}, CTHState}
end;
- post_end_per_testcase(_TC, Config, Return, CTHState) -&gt;
+ post_end_per_testcase(_Suite, _TC, Config, Return, CTHState) -&gt;
%% Do nothing if tc does not crash.
{Return, CTHState}.</code>
@@ -331,8 +333,8 @@
<title>Skip and Fail Hooks</title>
<p>
After any post hook has been executed for all installed CTHs,
- <seealso marker="ct_hooks#Module:on_tc_fail-3">on_tc_fail</seealso>
- or <seealso marker="ct_hooks#Module:on_tc_skip-3">on_tc_skip</seealso>
+ <seealso marker="ct_hooks#Module:on_tc_fail-4">on_tc_fail</seealso>
+ or <seealso marker="ct_hooks#Module:on_tc_skip-4">on_tc_skip</seealso>
is called if the testcase failed or was skipped, respectively.
You cannot affect the outcome of the tests any further at this point.
</p>
@@ -374,7 +376,7 @@
<title>Example CTH</title>
<p>The following CTH logs information about a test run into a format
parseable by <seealso marker="kernel:file#consult-1">file:consult/1</seealso>
- (in <c>Kernel</c>):
+ (in Kernel):
</p>
<code>
%%% @doc Common Test Example Common Test Hook module.
@@ -389,18 +391,18 @@
-export([pre_end_per_suite/3]).
-export([post_end_per_suite/4]).
- -export([pre_init_per_group/3]).
- -export([post_init_per_group/4]).
- -export([pre_end_per_group/3]).
- -export([post_end_per_group/4]).
+ -export([pre_init_per_group/4]).
+ -export([post_init_per_group/5]).
+ -export([pre_end_per_group/4]).
+ -export([post_end_per_group/5]).
- -export([pre_init_per_testcase/3]).
- -export([post_init_per_testcase/4]).
- -export([pre_end_per_testcase/3]).
- -export([post_end_per_testcase/4]).
+ -export([pre_init_per_testcase/4]).
+ -export([post_init_per_testcase/5]).
+ -export([pre_end_per_testcase/4]).
+ -export([post_end_per_testcase/5]).
- -export([on_tc_fail/3]).
- -export([on_tc_skip/3]).
+ -export([on_tc_fail/4]).
+ -export([on_tc_skip/4]).
-export([terminate/1]).
@@ -435,46 +437,46 @@
total = State#state.total + State#state.suite_total } }.
%% @doc Called before each init_per_group.
- pre_init_per_group(Group,Config,State) ->
+ pre_init_per_group(Suite,Group,Config,State) ->
{Config, State}.
%% @doc Called after each init_per_group.
- post_init_per_group(Group,Config,Return,State) ->
+ post_init_per_group(Suite,Group,Config,Return,State) ->
{Return, State}.
%% @doc Called before each end_per_group.
- pre_end_per_group(Group,Config,State) ->
+ pre_end_per_group(Suite,Group,Config,State) ->
{Config, State}.
%% @doc Called after each end_per_group.
- post_end_per_group(Group,Config,Return,State) ->
+ post_end_per_group(Suite,Group,Config,Return,State) ->
{Return, State}.
%% @doc Called before each init_per_testcase.
- pre_init_per_testcase(TC,Config,State) ->
+ pre_init_per_testcase(Suite,TC,Config,State) ->
{Config, State#state{ ts = now(), total = State#state.suite_total + 1 } }.
%% Called after each init_per_testcase (immediately before the test case).
- post_init_per_testcase(TC,Config,Return,State) ->
+ post_init_per_testcase(Suite,TC,Config,Return,State) ->
{Return, State}
%% @doc Called before each end_per_testcase (immediately after the test case).
- pre_end_per_testcase(TC,Config,State) ->
+ pre_end_per_testcase(Suite,TC,Config,State) ->
{Config, State}.
%% @doc Called after each end_per_testcase.
- post_end_per_testcase(TC,Config,Return,State) ->
- TCInfo = {testcase, TC, Return, timer:now_diff(now(), State#state.ts)},
+ post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ TCInfo = {testcase, Suite, TC, Return, timer:now_diff(now(), State#state.ts)},
{Return, State#state{ ts = undefined, tcs = [TCInfo | State#state.tcs] } }.
%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
%% post_end_per_group and post_end_per_testcase if the suite, group or test case failed.
- on_tc_fail(TC, Reason, State) ->
+ on_tc_fail(Suite, TC, Reason, State) ->
State.
%% @doc Called when a test case is skipped by either user action
%% or due to an init function failing.
- on_tc_skip(TC, Reason, State) ->
+ on_tc_skip(Suite, TC, Reason, State) ->
State.
%% @doc Called when the scope of the CTH is done
@@ -499,13 +501,13 @@
<tag><c>cth_log_redirect</c></tag>
<item>
<p>Built-in</p>
- <p>Captures all <c>error_logger</c> and <c>SASL</c> logging
+ <p>Captures all <c>error_logger</c> and SASL logging
events and prints them to the current test case log. If an event cannot be
associated with a test case, it is printed in the <c>Common Test</c> framework log.
This happens for test cases running in parallel and events occuring
in-between test cases. You can configure the level of
- <seealso marker="sasl:sasl_app"><c>SASL</c></seealso> events report
- using the normal <c>SASL</c> mechanisms.</p>
+ <seealso marker="sasl:sasl_app">SASL</seealso> events report
+ using the normal SASL mechanisms.</p>
</item>
<tag><c>cth_surefire</c></tag>
<item>
diff --git a/lib/common_test/doc/src/ct_ssh.xml b/lib/common_test/doc/src/ct_ssh.xml
index d00737aa5a..137e4c3f1d 100644
--- a/lib/common_test/doc/src/ct_ssh.xml
+++ b/lib/common_test/doc/src/ct_ssh.xml
@@ -64,7 +64,7 @@
<p><c>ConnType = ssh | sftp</c>.</p>
<p>For other types, see
- <seealso marker="ssh:ssh"><c>ssh:ssh(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh"><c>ssh(3)</c></seealso>.</p>
<p>All time-out parameters in <c>ct_ssh</c> functions are values in
milliseconds.</p>
@@ -88,7 +88,7 @@
<tag><c>ssh_sftp_return() = term()</c></tag>
<item><marker id="type-ssh_sftp_return"/>
<p>Return value from an
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp</c></seealso>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp</c></seealso>
function.</p></item>
</taglist>
</section>
@@ -104,7 +104,7 @@
</type>
<desc><marker id="apread-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -118,7 +118,7 @@
</type>
<desc><marker id="apread-5"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -132,7 +132,7 @@
</type>
<desc><marker id="apwrite-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -146,7 +146,7 @@
</type>
<desc><marker id="apwrite-5"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -160,7 +160,7 @@
</type>
<desc><marker id="aread-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -174,7 +174,7 @@
</type>
<desc><marker id="aread-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -188,7 +188,7 @@
</type>
<desc><marker id="awrite-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -202,7 +202,7 @@
</type>
<desc><marker id="awrite-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -216,7 +216,7 @@
</type>
<desc><marker id="close-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -230,7 +230,7 @@
</type>
<desc><marker id="close-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -310,7 +310,7 @@
</type>
<desc><marker id="del_dir-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -324,7 +324,7 @@
</type>
<desc><marker id="del_dir-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -338,7 +338,7 @@
</type>
<desc><marker id="delete-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -352,7 +352,7 @@
</type>
<desc><marker id="delete-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -423,7 +423,7 @@
</type>
<desc><marker id="get_file_info-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -437,7 +437,7 @@
</type>
<desc><marker id="get_file_info-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -451,7 +451,7 @@
</type>
<desc><marker id="list_dir-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -465,7 +465,7 @@
</type>
<desc><marker id="list_dir-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -479,7 +479,7 @@
</type>
<desc><marker id="make_dir-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -493,7 +493,7 @@
</type>
<desc><marker id="make_dir-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -507,7 +507,7 @@
</type>
<desc><marker id="make_symlink-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -521,7 +521,7 @@
</type>
<desc><marker id="make_symlink-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -535,7 +535,7 @@
</type>
<desc><marker id="open-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -549,7 +549,7 @@
</type>
<desc><marker id="open-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -563,7 +563,7 @@
</type>
<desc><marker id="opendir-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -577,7 +577,7 @@
</type>
<desc><marker id="opendir-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -591,7 +591,7 @@
</type>
<desc><marker id="position-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -605,7 +605,7 @@
</type>
<desc><marker id="position-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -619,7 +619,7 @@
</type>
<desc><marker id="pread-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -633,7 +633,7 @@
</type>
<desc><marker id="pread-5"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -647,7 +647,7 @@
</type>
<desc><marker id="pwrite-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -661,7 +661,7 @@
</type>
<desc><marker id="pwrite-5"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -675,7 +675,7 @@
</type>
<desc><marker id="read-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -689,7 +689,7 @@
</type>
<desc><marker id="read-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -703,7 +703,7 @@
</type>
<desc><marker id="read_file-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -717,7 +717,7 @@
</type>
<desc><marker id="read_file-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -731,7 +731,7 @@
</type>
<desc><marker id="read_file_info-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -745,7 +745,7 @@
</type>
<desc><marker id="read_file_info-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -759,7 +759,7 @@
</type>
<desc><marker id="read_link-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -773,7 +773,7 @@
</type>
<desc><marker id="read_link-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -787,7 +787,7 @@
</type>
<desc><marker id="read_link_info-2"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -801,7 +801,7 @@
</type>
<desc><marker id="read_link_info-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -853,7 +853,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p>
<p>If <c>End</c> is a fun, this fun is called with one argument, the
data value in a received <c>ssh_cm</c> message (see
- <seealso marker="ssh:ssh_connection"><c>ssh:ssh_connection(3)</c></seealso>.
+ <seealso marker="ssh:ssh_connection"><c>ssh_connection(3)</c></seealso>.
The fun is to return either <c>true</c> to end the receiving
operation (and have the so far collected data returned) or
<c>false</c> to wait for more data from the server. Even if a fun
@@ -872,7 +872,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p>
</type>
<desc><marker id="rename-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -886,7 +886,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p>
</type>
<desc><marker id="rename-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1070,7 +1070,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1084,7 +1084,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1098,7 +1098,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write_file-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1112,7 +1112,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write_file-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1126,7 +1126,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write_file_info-3"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
@@ -1140,7 +1140,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p>
</type>
<desc><marker id="write_file_info-4"/>
<p>For information and other types, see
- <seealso marker="ssh:ssh_sftp"><c>ssh:ssh_sftp(3)</c></seealso>.</p>
+ <seealso marker="ssh:ssh_sftp"><c>ssh_sftp(3)</c></seealso>.</p>
</desc>
</func>
</funcs>
diff --git a/lib/common_test/doc/src/ct_telnet.xml b/lib/common_test/doc/src/ct_telnet.xml
index e2a45e894b..8e85cccc99 100644
--- a/lib/common_test/doc/src/ct_telnet.xml
+++ b/lib/common_test/doc/src/ct_telnet.xml
@@ -198,7 +198,7 @@
<item><marker id="type-prompt_regexp"/>
<p>Regular expression matching all possible prompts for a specific
target type. <c>regexp</c> must not have any groups, that is, when
- matching, <c>re:run/3</c> (in <c>STDLIB</c>) must return a list with
+ matching, <c>re:run/3</c> (in STDLIB) must return a list with
one single element.</p></item>
</taglist>
</section>
@@ -337,7 +337,7 @@
<c>FullMatch</c> is the string matched by the whole regular
expression, and <c>SubMatchN</c> is the string that matched
subexpression number <c>N</c>. Subexpressions are denoted with
- <c>(' ')</c> in the regular expression.</p>
+ <c>'(' ')'</c> in the regular expression.</p>
<p>If a <c>Tag</c> is speciifed, the returned <c>Match</c> also
includes the matched <c>Tag</c>. Otherwise, only <c>RxMatch</c>
diff --git a/lib/common_test/doc/src/ct_testspec.xml b/lib/common_test/doc/src/ct_testspec.xml
new file mode 100644
index 0000000000..36893f66cf
--- /dev/null
+++ b/lib/common_test/doc/src/ct_testspec.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2016</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>ct_testspec</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev>A</rev>
+ <file>ct_testspec.xml</file>
+ </header>
+ <module>ct_testspec</module>
+ <modulesummary>Parsing of test specifications for Common Test.
+ </modulesummary>
+
+<description>
+
+ <p>Parsing of test specifications for <c>Common Test</c>.</p>
+
+ <p>This module exports help functions for parsing of test specifications.</p>
+
+</description>
+
+ <funcs>
+ <func>
+ <name>get_tests(SpecsIn) -&gt; {ok, [{Specs,Tests}]} | {error, Reason}</name>
+ <fsummary>Parse the given test specification files and return the tests to run and skip.</fsummary>
+ <type>
+ <v>SpecsIn = [string()] | [[string()]]</v>
+ <v>Specs = [string()]</v>
+ <v>Test = [{Node,Run,Skip}]</v>
+ <v>Node = atom()</v>
+ <v>Run = {Dir,Suites,Cases}</v>
+ <v>Skip = {Dir,Suites,Comment} | {Dir,Suites,Cases,Comment}</v>
+ <v>Dir = string()</v>
+ <v>Suites = atom | [atom()] | all</v>
+ <v>Cases = atom | [atom()] | all</v>
+ <v>Comment = string()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc><marker id="add_nodes-1"/>
+ <p>Parse the given test specification files and return the
+ tests to run and skip.</p>
+
+ <p>If <c>SpecsIn=[Spec1,Spec2,...]</c>, separate tests will be
+ created per specification. If
+ <c>SpecsIn=[[Spec1,Spec2,...]]</c>, all specifications will be
+ merge into one test.</p>
+
+ <p>For each test, a <c>{Specs,Tests}</c> element is returned,
+ where <c>Specs</c> is a list of all included test
+ specifications, and <c>Tests</c> specifies actual tests to
+ run/skip per node.</p>
+ </desc>
+ </func>
+
+ </funcs>
+
+</erlref>
+
+
diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml
index 2978226a19..bd9ed21cb4 100644
--- a/lib/common_test/doc/src/event_handler_chapter.xml
+++ b/lib/common_test/doc/src/event_handler_chapter.xml
@@ -50,7 +50,7 @@
pass the information on. The event handlers are Erlang modules
implemented by the <c>Common Test</c> user according to the <c>gen_event</c>
behavior (for details, see module
- <seealso marker="stdlib:gen_event"><c>stdlib:gen_event</c></seealso> and
+ <seealso marker="stdlib:gen_event"><c>gen_event</c></seealso> and
section
<seealso marker="doc/design_principles:events"><c>gen_event Behaviour</c></seealso>
in OTP Design Principles in the System Documentation).
@@ -69,8 +69,8 @@
manager, either by telling <c>Common Test</c> to install them before the test
run (described later), or by adding the handlers dynamically during the test
run using
- <seealso marker="stdlib:gen_event#add_handler-3"><c>stdlib:gen_event:add_handler/3</c></seealso> or
- <seealso marker="stdlib:gen_event#add_sup_handler-3"><c>stdlib:gen_event:add_sup_handler/3</c></seealso>.
+ <seealso marker="stdlib:gen_event#add_handler-3"><c>gen_event:add_handler/3</c></seealso> or
+ <seealso marker="stdlib:gen_event#add_sup_handler-3"><c>gen_event:add_sup_handler/3</c></seealso>.
In the latter scenario, the reference of the <c>Common Test</c> event manager is
required. To get it, call
<seealso marker="ct#get_event_mgr_ref-0"><c>ct:get_event_mgr_ref/0</c></seealso>
diff --git a/lib/common_test/doc/src/introduction.xml b/lib/common_test/doc/src/introduction.xml
index 40724f24e9..df12bea6dd 100644
--- a/lib/common_test/doc/src/introduction.xml
+++ b/lib/common_test/doc/src/introduction.xml
@@ -45,7 +45,7 @@
</list>
<p><c>Common Test</c> also integrates use of the OTP
<seealso marker="tools:cover">cover</seealso> tool in application
- <c>Tools</c> for code coverage analysis of Erlang/OTP programs.</p>
+ Tools for code coverage analysis of Erlang/OTP programs.</p>
<p><c>Common Test</c> executes test suite programs automatically,
without operator interaction. Test progress and results are
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index 32ae699c7a..efeacd4a72 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,218 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.14</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The following corrections and improvements are done in
+ the common_test hook handling:</p> <list> <item> <p>An
+ extra argument, <c>Suite</c>, is added as the first
+ argument to each of the following hook callback
+ functions:</p> <list>
+ <item><c>pre_init_per_group</c></item>
+ <item><c>post_init_per_group</c></item>
+ <item><c>pre_end_per_group</c></item>
+ <item><c>post_end_per_group</c></item>
+ <item><c>pre_init_per_testcase</c></item>
+ <item><c>post_init_per_testcase</c></item>
+ <item><c>pre_end_per_testcase</c></item>
+ <item><c>post_end_per_testcase</c></item>
+ <item><c>on_tc_fail</c></item>
+ <item><c>on_tc_skip</c></item> </list> <p>For backwards
+ compatibility, if the new function is not exported from a
+ hook callback module, <c>common_test</c> will fall back
+ to the old interface and call the function without the
+ <c>Suite</c> argument.</p> </item> <item> <p>If either
+ <c>init_per_suite</c> or <c>end_per_suite</c> exists, but
+ not the other, then the non-existing function will be
+ reported as failed with reason <c>undef</c> in the test
+ log. The same goes for <c>init/end_per_group</c>. This
+ has always been a requirement according to the user's
+ guide, but now <c>common_test</c> is more explicit in the
+ report.</p> </item> <item> <p>If <c>init_per_suite</c>
+ was exported from a test suite, but not
+ <c>end_per_suite</c>, then <c>pre/post_end_per_suite</c>
+ was called with <c>Suite=ct_framework</c> instead of the
+ correct suite name. This is now corrected.</p> </item>
+ <item> <p>If <c>end_per_group</c> was exported from a
+ suite, but not <c>init_per_group</c>, then
+ <c>end_per_group</c> was never called. This is now
+ corrected.</p> </item> <item> <p>Tests that were skipped
+ before calling <c>pre_init_per_*</c> got faulty calls to
+ the corresponding <c>post_init_per_*</c>. E.g. if a test
+ was skipped because <c>suite/0</c> failed, then
+ <c>post_init_per_suite</c> would be called even though
+ <c>pre_init_per_suite</c> and <c>init_per_suite</c> were
+ not called. This is now corrected so a <c>post_*</c>
+ callback will never be called unless the corresponding
+ <c>pre_*</c> callback has been called first.</p> </item>
+ <item> <p>Tests that were skipped before or in
+ <c>init_per_testcase</c> got faulty calls to
+ <c>pre_end_per_testcase</c> and
+ <c>post_end_per_testcase</c>. This is now corrected so
+ <c>pre/post_end_per_testcase</c> are not called when
+ <c>end_per_testcase</c> is not called.</p> </item> <item>
+ <p>If an exit signal causes the test case process to die
+ while running <c>init_per_testcase</c>, the case was
+ earlier reported as failed with reason <c>{skip,...}</c>.
+ This is now corrected so the case will be marked as
+ skipped.</p> </item> <item> <p>If an exist signal causes
+ the test case process to die while running
+ <c>end_per_testcase</c>, the case was earlier marked as
+ failed. This is now corrected so the status of the test
+ case is not changed - there is only a warning added to
+ the comment field.</p> </item> <item> <p>If a test case
+ was skipped because of option
+ <c>{force_stop,skip_rest}</c> or because of a failed
+ sequence, then no <c>tc_start</c> event would be sent,
+ only <c>tc_done</c>. This is now corrected so both events
+ are sent.</p> </item> <item> <p>When skipping or failing
+ in a configuration function, the configuration function
+ itself would get <c>{auto_skipped,Reason}</c>,
+ <c>{skipped,Reason}</c> or <c>{failed,Reason}</c> in the
+ hook callbacks <c>on_tc_skip</c> or <c>on_tc_fail</c>.
+ The other test cases that were skipped as a result of
+ this would only get <c>Reason</c> in <c>on_tc_skip</c>.
+ This is now corrected so even the configuration function
+ that caused the skip/fail will only get <c>Reason</c> in
+ the hook callback.</p> </item> </list>
+ <p>
+ Own Id: OTP-10599 Aux Id: kunagi-344 [255] </p>
+ </item>
+ <item>
+ <p>
+ When a test case was skipped by a <c>skip_cases</c>
+ statement in a test spec, then <c>cth_surefire</c> would
+ erroneously mark the previous test case as skipped in the
+ xml report. The actually skipped test case would not be
+ present in the xml report at all. This is now corrected.</p>
+ <p>
+ Own Id: OTP-14129 Aux Id: seq13244 </p>
+ </item>
+ <item>
+ <p>The <c>multiply_timetraps</c> and
+ <c>scale_timetraps</c> options did not work with test
+ specifications, which has been corrected.</p>
+ <p>
+ Own Id: OTP-14210</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ ct_testspec:get_tests/1 is added. This is used by rebar3
+ to get all directories that must be compiled when running
+ tests from testspec - instead of implementing testspec
+ parsing in rebar3.</p>
+ <p>
+ Own Id: OTP-14132</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.13</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Some types of printouts to screen during test runs
+ (including <c>ct:print/1,2,3,4</c>) used the local
+ <c>user</c> process as IO device and these printouts
+ would not be visible when e.g. running tests via a shell
+ on a remote node. A default Common Test group leader
+ process has been introduced to solve the problem. This
+ process routes printouts to the group leader of the
+ starting process, if available, otherwise to <c>user</c>.</p>
+ <p>
+ Own Id: OTP-13973 Aux Id: ERL-279 </p>
+ </item>
+ <item>
+ <p>
+ Some Common Test processes, that act as I/O group leaders
+ for test cases, would not terminate as expected at the
+ end of test runs. This error has been corrected.</p>
+ <p>
+ Own Id: OTP-14026 Aux Id: ERL-287 </p>
+ </item>
+ <item>
+ <p>
+ The logging verbosity feature was incorrectly documented.
+ The default verbosity levels for test runs is e.g. not 50
+ (<c>?STD_VERBOSITY</c>), but 100 (<c>?MAX_VERBOSITY</c>).
+ Also, some of the examples had errors and flaws. The
+ corresponding chapter (5.18) in the User's Guide has been
+ updated.</p>
+ <p>
+ Own Id: OTP-14044 Aux Id: seq13223 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ A feature to let the user specify headings to log
+ printouts has been added. The heading is specified as
+ <c>{heading,string()}</c> in the <c>Opts</c> list
+ argument to <c>ct:pal/3,4,5</c>, <c>ct:print/3,4,5</c>,
+ or <c>ct:log/3,4,5</c>. If the heading option is omitted,
+ the category name, or <c>"User"</c>, is used as the
+ heading instead.</p>
+ <p>
+ Own Id: OTP-14043 Aux Id: seq13226 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.12.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If the telnet server would pause during transmission of a
+ line of text before terminating the line, the
+ <c>ct_telnet:expect/3</c> function would print the line
+ twice in the test case HTML log. This problem has been
+ fixed.</p>
+ <p>
+ Own Id: OTP-13730 Aux Id: seq13135 </p>
+ </item>
+ <item>
+ <p>
+ The functions <c>ct:set_verbosity/2</c> and
+ <c>ct:get_verbosity/1</c> have been added in order to
+ make it possible for test cases, CT Hooks, or test
+ framework functions, to modify and read verbosity levels
+ for logging.</p>
+ <p>
+ Own Id: OTP-13841</p>
+ </item>
+ <item>
+ <p><c>make</c> (tools) and <c>ct_make</c> (common_test)
+ would crash if an Erlang source file contained a
+ <c>-warning()</c> directive.</p>
+ <p>
+ Own Id: OTP-13855</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.12.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/doc/src/ref_man.xml b/lib/common_test/doc/src/ref_man.xml
index d1567e2d3c..1ac20db5c2 100644
--- a/lib/common_test/doc/src/ref_man.xml
+++ b/lib/common_test/doc/src/ref_man.xml
@@ -47,6 +47,7 @@
<xi:include href="ct_slave.xml"/>
<xi:include href="ct_hooks.xml"/>
<xi:include href="ct_property_test.xml"/>
+ <xi:include href="ct_testspec.xml"/>
</application>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 43e36adfb6..76e306c4ed 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -274,7 +274,7 @@
<note><p>Directories passed to <c>Common Test</c> can have either relative or absolute paths.</p></note>
- <note><p>Any start flags to the Erlang runtime system (application <c>ERTS</c>) can also be passed as
+ <note><p>Any start flags to the Erlang runtime system (application ERTS) can also be passed as
parameters to <c>ct_run</c>. It is, for example, useful to be able to
pass directories to be added to the Erlang code server search path
with flag <c>-pa</c> or <c>-pz</c>. If you have common help- or library
@@ -286,7 +286,7 @@
<p>The absolute path of directory <c>chat_server/ebin</c>
is here passed to the code server. This is essential because relative
paths are stored by the code server as relative, and <c>Common Test</c> changes
- the current working directory of <c>ERTS</c> during the test run.</p>
+ the current working directory of ERTS during the test run.</p>
</note>
<p>The <c>ct_run</c> program sets the exit status before shutting down. The following values
@@ -1258,7 +1258,7 @@
<p>The minor log files contain full details of every single test
case, each in a separate file. This way, it is
straightforward to compare the latest results to that of previous
- test runs, even if the set of test cases changes. If application <c>SASL</c>
+ test runs, even if the set of test cases changes. If application SASL
is running, its logs are also printed to the current minor log file by the
<seealso marker="common_test:ct_hooks_chapter#builtin_cths">
cth_log_redirect built-in hook</seealso>.
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 83daf771a6..d2d41625c4 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2016</year>
+ <year>2003</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -269,10 +269,10 @@
<p>As parameter <c>Config</c> is a list of key-value tuples, that is,
a data type called a property list, it can be handled by the
- <seealso marker="stdlib:proplists"><c>stdlib:proplists</c></seealso> module.
+ <seealso marker="stdlib:proplists"><c>proplists</c></seealso> module.
A value can, for example, be searched for and returned with function
<seealso marker="stdlib:proplists#get_value-2"><c>proplists:get_value/2</c></seealso>.
- Also, or alternatively, the general <seealso marker="stdlib:lists"><c>stdlib:lists</c></seealso>
+ Also, or alternatively, the general <seealso marker="stdlib:lists"><c>lists</c></seealso>
module contains useful functions. Normally, the only operations
performed on <c>Config</c> is insert (adding a tuple to the head of the list)
and lookup. <c>Common Test</c> provides a simple macro named <c>?config</c>,
@@ -566,7 +566,7 @@
for the test cases in the group. After execution of the group is finished, function
<seealso marker="common_test#Module:end_per_group-2"><c>end_per_group(GroupName, Config)</c></seealso>
is called. This function is meant to be used for cleaning up after
- <c>init_per_group/2</c>.</p>
+ <c>init_per_group/2</c>. If the init function is defined, so must the end function be.</p>
<p>Whenever a group is executed, if <c>init_per_group</c> and
<c>end_per_group</c> do not exist in the suite, <c>Common Test</c> calls
@@ -652,7 +652,7 @@
<title>Parallel Test Cases and I/O</title>
<p>A parallel test case has a private I/O server as its group leader.
(For a description of the group leader concept, see
- <seealso marker="erts:index"><c>ERTS</c></seealso>).
+ <seealso marker="erts:index">ERTS</seealso>).
The central I/O server process, which handles the output from
regular test cases and configuration functions, does not respond to I/O messages
during execution of parallel groups. This is important to understand
@@ -986,15 +986,17 @@
<c>io:put_chars/1</c>, and so on.</p>
<p><c>Importance</c> is compared to a verbosity level set by the
- <c>verbosity</c> start flag/option. The verbosity level can be set per
- category or generally, or both. The default verbosity level,
- <c>?STD_VERBOSITY</c>, is 50, that is, all standard I/O gets printed.
- If a lower verbosity level is set, standard I/O printouts are ignored.
- <c>Common Test</c> performs the following test:</p>
+ <c>verbosity</c> start flag/option. The level can be set per
+ category or generally, or both. If <c>verbosity</c> is not set by the user,
+ a level of 100 (<c>?MAX_VERBOSITY</c> = all printouts visible) is used as
+ default value. <c>Common Test</c> performs the following test:</p>
<pre>
- Importance >= (100-VerbosityLevel)</pre>
- <p>This also means that verbosity level 0 effectively turns all logging off
- (except from printouts made by <c>Common Test</c> itself).</p>
+Importance >= (100-VerbosityLevel)</pre>
+ <p>The constant <c>?STD_VERBOSITY</c> has value 50 (see <c>ct.hrl</c>).
+ At this level, all standard I/O gets printed. If a lower verbosity level
+ is set, standard I/O printouts are ignored. Verbosity level 0 effectively
+ turns all logging off (except from printouts made by <c>Common Test</c>
+ itself).</p>
<p>The general verbosity level is not associated with any particular
category. This level sets the threshold for the standard I/O printouts,
@@ -1003,17 +1005,17 @@
<p><em>Examples:</em></p>
<p>Some printouts during test case execution:</p>
- <pre>
+ <pre>
io:format("1. Standard IO, importance = ~w~n", [?STD_IMPORTANCE]),
ct:log("2. Uncategorized, importance = ~w", [?STD_IMPORTANCE]),
- ct:log(info, "3. Categorized info, importance = ~w", [?STD_IMPORTANCE]]),
+ ct:log(info, "3. Categorized info, importance = ~w", [?STD_IMPORTANCE]),
ct:log(info, ?LOW_IMPORTANCE, "4. Categorized info, importance = ~w", [?LOW_IMPORTANCE]),
- ct:log(error, "5. Categorized error, importance = ~w", [?HI_IMPORTANCE]),
- ct:log(error, ?HI_IMPORTANCE, "6. Categorized error, importance = ~w", [?MAX_IMPORTANCE]),</pre>
+ ct:log(error, ?HI_IMPORTANCE, "5. Categorized error, importance = ~w", [?HI_IMPORTANCE]),
+ ct:log(error, ?MAX_IMPORTANCE, "6. Categorized error, importance = ~w", [?MAX_IMPORTANCE]),</pre>
- <p>If starting the test without specifying any verbosity levels as follows:</p>
+ <p>If starting the test with a general verbosity level of 50 (<c>?STD_VERBOSITY</c>):</p>
<pre>
- $ ct_run ...</pre>
+ $ ct_run -verbosity 50</pre>
<p>the following is printed:</p>
<pre>
1. Standard IO, importance = 50
@@ -1031,9 +1033,25 @@
4. Categorized info, importance = 25
6. Categorized error, importance = 99</pre>
+ <p>Note that the category argument is not required in order to only specify the
+ importance of a printout. Example:</p>
+ <pre>
+<c>ct:pal(?LOW_IMPORTANCE, "Info report: ~p", [Info])</c></pre>
+ <p>Or perhaps in combination with constants:</p>
+ <pre>
+-define(INFO, ?LOW_IMPORTANCE).
+-define(ERROR, ?HI_IMPORTANCE).
+
+ct:log(?INFO, "Info report: ~p", [Info])
+ct:pal(?ERROR, "Error report: ~p", [Error])</pre>
+
+ <p>The functions <seealso marker="ct#set_verbosity-2"><c>ct:set_verbosity/2</c></seealso>
+ and <seealso marker="ct#get_verbosity-1"><c>ct:get_verbosity/1</c></seealso> may be used
+ to modify and read verbosity levels during test execution.</p>
+
<p>The arguments <c>Format</c> and <c>FormatArgs</c> in <c>ct:log/print/pal</c> are
- always passed on to the <c>stdlib</c> function <c>io:format/3</c> (For details,
- see the <seealso marker="stdlib:io"><c>stdlib:io</c></seealso> manual page).</p>
+ always passed on to the STDLIB function <c>io:format/3</c> (For details,
+ see the <seealso marker="stdlib:io"><c>io</c></seealso> manual page).</p>
<p><c>ct:pal/4</c> and <c>ct:log/5</c> add headers to strings being printed to the
log file. The strings are also wrapped in div tags with a CSS class
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index 0f9e044f9e..9d751996ad 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -80,6 +80,7 @@ MODULES= \
ct_groups \
ct_property_test \
ct_release_test \
+ ct_default_gl \
erl2html2 \
test_server_ctrl \
test_server_gl \
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index 77588af59b..430a4fa2fb 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -1,7 +1,7 @@
% This is an -*- erlang -*- file.
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2017. 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.
@@ -22,6 +22,7 @@
{vsn, "%VSN%"},
{modules, [ct_cover,
ct,
+ ct_default_gl,
ct_event,
ct_framework,
ct_ftp,
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index d7ae81a5ce..43abb91819 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -66,8 +66,9 @@
reload_config/1,
escape_chars/1, escape_chars/2,
log/1, log/2, log/3, log/4, log/5,
- print/1, print/2, print/3, print/4,
- pal/1, pal/2, pal/3, pal/4,
+ print/1, print/2, print/3, print/4, print/5,
+ pal/1, pal/2, pal/3, pal/4, pal/5,
+ set_verbosity/2, get_verbosity/1,
capture_start/0, capture_stop/0, capture_get/0, capture_get/1,
fail/1, fail/2, comment/1, comment/2, make_priv_dir/0,
testcases/2, userdata/2, userdata/3,
@@ -591,7 +592,7 @@ log(X1,X2,X3,X4) ->
%%% Format = string()
%%% Args = list()
%%% Opts = [Opt]
-%%% Opt = esc_chars | no_css
+%%% Opt = {heading,string()} | esc_chars | no_css
%%%
%%% @doc Printout from a test case to the log file.
%%%
@@ -609,43 +610,61 @@ log(Category,Importance,Format,Args,Opts) ->
%%%-----------------------------------------------------------------
%%% @spec print(Format) -> ok
-%%% @equiv print(default,50,Format,[])
+%%% @equiv print(default,50,Format,[],[])
print(Format) ->
- print(default,?STD_IMPORTANCE,Format,[]).
+ print(default,?STD_IMPORTANCE,Format,[],[]).
%%%-----------------------------------------------------------------
%%% @spec print(X1,X2) -> ok
%%% X1 = Category | Importance | Format
%%% X2 = Format | Args
-%%% @equiv print(Category,Importance,Format,Args)
+%%% @equiv print(Category,Importance,Format,Args,[])
print(X1,X2) ->
{Category,Importance,Format,Args} =
if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]};
is_integer(X1) -> {default,X1,X2,[]};
is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2}
end,
- print(Category,Importance,Format,Args).
+ print(Category,Importance,Format,Args,[]).
%%%-----------------------------------------------------------------
%%% @spec print(X1,X2,X3) -> ok
+%%% X1 = Category | Importance | Format
+%%% X2 = Importance | Format | Args
+%%% X3 = Format | Args | Opts
+%%% @equiv print(Category,Importance,Format,Args,Opts)
+print(X1,X2,X3) ->
+ {Category,Importance,Format,Args,Opts} =
+ if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[],[]};
+ is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3,[]};
+ is_integer(X1) -> {default,X1,X2,X3,[]};
+ is_list(X1), is_list(X2) -> {default,?STD_IMPORTANCE,X1,X2,X3}
+ end,
+ print(Category,Importance,Format,Args,Opts).
+
+%%%-----------------------------------------------------------------
+%%% @spec print(X1,X2,X3,X4) -> ok
%%% X1 = Category | Importance
%%% X2 = Importance | Format
%%% X3 = Format | Args
-%%% @equiv print(Category,Importance,Format,Args)
-print(X1,X2,X3) ->
- {Category,Importance,Format,Args} =
- if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]};
- is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3};
- is_integer(X1) -> {default,X1,X2,X3}
+%%% X4 = Args | Opts
+%%% @equiv print(Category,Importance,Format,Args,Opts)
+print(X1,X2,X3,X4) ->
+ {Category,Importance,Format,Args,Opts} =
+ if is_atom(X1), is_integer(X2) -> {X1,X2,X3,X4,[]};
+ is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3,X4};
+ is_integer(X1) -> {default,X1,X2,X3,X4}
end,
- print(Category,Importance,Format,Args).
+ print(Category,Importance,Format,Args,Opts).
%%%-----------------------------------------------------------------
-%%% @spec print(Category,Importance,Format,Args) -> ok
+%%% @spec print(Category,Importance,Format,Args,Opts) -> ok
%%% Category = atom()
%%% Importance = integer()
%%% Format = string()
%%% Args = list()
+%%% Opts = [Opt]
+%%% Opt = {heading,string()}
%%%
%%% @doc Printout from a test case to the console.
%%%
@@ -657,13 +676,13 @@ print(X1,X2,X3) ->
%%% and default value for <c>Args</c> is <c>[]</c>.</p>
%%% <p>Please see the User's Guide for details on <c>Category</c>
%%% and <c>Importance</c>.</p>
-print(Category,Importance,Format,Args) ->
- ct_logs:tc_print(Category,Importance,Format,Args).
+print(Category,Importance,Format,Args,Opts) ->
+ ct_logs:tc_print(Category,Importance,Format,Args,Opts).
%%%-----------------------------------------------------------------
%%% @spec pal(Format) -> ok
-%%% @equiv pal(default,50,Format,[])
+%%% @equiv pal(default,50,Format,[],[])
pal(Format) ->
pal(default,?STD_IMPORTANCE,Format,[]).
@@ -671,35 +690,53 @@ pal(Format) ->
%%% @spec pal(X1,X2) -> ok
%%% X1 = Category | Importance | Format
%%% X2 = Format | Args
-%%% @equiv pal(Category,Importance,Format,Args)
+%%% @equiv pal(Category,Importance,Format,Args,[])
pal(X1,X2) ->
{Category,Importance,Format,Args} =
if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]};
is_integer(X1) -> {default,X1,X2,[]};
is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2}
end,
- pal(Category,Importance,Format,Args).
+ pal(Category,Importance,Format,Args,[]).
%%%-----------------------------------------------------------------
%%% @spec pal(X1,X2,X3) -> ok
+%%% X1 = Category | Importance | Format
+%%% X2 = Importance | Format | Args
+%%% X3 = Format | Args | Opts
+%%% @equiv pal(Category,Importance,Format,Args,Opts)
+pal(X1,X2,X3) ->
+ {Category,Importance,Format,Args,Opts} =
+ if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[],[]};
+ is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3,[]};
+ is_integer(X1) -> {default,X1,X2,X3,[]};
+ is_list(X1), is_list(X2) -> {default,?STD_IMPORTANCE,X1,X2,X3}
+ end,
+ pal(Category,Importance,Format,Args,Opts).
+
+%%%-----------------------------------------------------------------
+%%% @spec pal(X1,X2,X3,X4) -> ok
%%% X1 = Category | Importance
%%% X2 = Importance | Format
%%% X3 = Format | Args
-%%% @equiv pal(Category,Importance,Format,Args)
-pal(X1,X2,X3) ->
- {Category,Importance,Format,Args} =
- if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]};
- is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3};
- is_integer(X1) -> {default,X1,X2,X3}
+%%% X4 = Args | Opts
+%%% @equiv pal(Category,Importance,Format,Args,Opts)
+pal(X1,X2,X3,X4) ->
+ {Category,Importance,Format,Args,Opts} =
+ if is_atom(X1), is_integer(X2) -> {X1,X2,X3,X4,[]};
+ is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3,X4};
+ is_integer(X1) -> {default,X1,X2,X3,X4}
end,
- pal(Category,Importance,Format,Args).
+ pal(Category,Importance,Format,Args,Opts).
%%%-----------------------------------------------------------------
-%%% @spec pal(Category,Importance,Format,Args) -> ok
+%%% @spec pal(Category,Importance,Format,Args,Opts) -> ok
%%% Category = atom()
%%% Importance = integer()
%%% Format = string()
%%% Args = list()
+%%% Opts = [Opt]
+%%% Opt = {heading,string()} | no_css
%%%
%%% @doc Print and log from a test case.
%%%
@@ -711,8 +748,26 @@ pal(X1,X2,X3) ->
%%% and default value for <c>Args</c> is <c>[]</c>.</p>
%%% <p>Please see the User's Guide for details on <c>Category</c>
%%% and <c>Importance</c>.</p>
-pal(Category,Importance,Format,Args) ->
- ct_logs:tc_pal(Category,Importance,Format,Args).
+pal(Category,Importance,Format,Args,Opts) ->
+ ct_logs:tc_pal(Category,Importance,Format,Args,Opts).
+
+%%%-----------------------------------------------------------------
+%%% @spec set_verbosity(Category, Level) -> ok
+%%% Category = default | atom()
+%%% Level = integer()
+%%%
+%%% @doc Set the verbosity level for a category
+set_verbosity(Category, Level) ->
+ ct_util:set_verbosity({Category,Level}).
+
+%%%-----------------------------------------------------------------
+%%% @spec get_verbosity(Category) -> Level | undefined
+%%% Category = default | atom()
+%%% Level = integer()
+%%%
+%%% @doc Read the verbosity level for a category
+get_verbosity(Category) ->
+ ct_util:get_verbosity(Category).
%%%-----------------------------------------------------------------
%%% @spec capture_start() -> ok
diff --git a/lib/common_test/src/ct_default_gl.erl b/lib/common_test/src/ct_default_gl.erl
new file mode 100644
index 0000000000..d1b52e5f4f
--- /dev/null
+++ b/lib/common_test/src/ct_default_gl.erl
@@ -0,0 +1,83 @@
+%%
+%% %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(ct_default_gl).
+-export([start_link/1, stop/0]).
+
+-export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
+
+%% start_link()
+%% Start a new group leader process.
+start_link(ParentGL) ->
+ do_start(ParentGL, 3).
+
+do_start(_ParentGL, 0) ->
+ exit({?MODULE,startup});
+do_start(ParentGL, Retries) ->
+ case whereis(?MODULE) of
+ undefined ->
+ case gen_server:start_link(?MODULE, [ParentGL], []) of
+ {ok,Pid} ->
+ {ok,Pid};
+ Other ->
+ Other
+ end;
+ Pid ->
+ exit(Pid, kill),
+ timer:sleep(1000),
+ do_start(ParentGL, Retries-1)
+ end.
+
+%% stop(Pid)
+%% Stop a group leader process.
+stop() ->
+ gen_server:cast(whereis(?MODULE), stop).
+
+
+%%% Internal functions.
+
+init([ParentGL]) ->
+ register(?MODULE, self()),
+ {ok,#{parent_gl_pid => ParentGL,
+ parent_gl_monitor => erlang:monitor(process,ParentGL)}}.
+
+handle_cast(stop, St) ->
+ {stop,normal,St}.
+
+%% If the parent group leader dies, fall back on using the local user process
+handle_info({'DOWN',Ref,process,_,_Reason}, #{parent_gl_monitor := Ref} = St) ->
+ User = whereis(user),
+ {noreply,St#{parent_gl_pid => User,
+ parent_gl_monitor => erlang:monitor(process,User)}};
+
+handle_info({io_request,_From,_ReplyAs,_Req} = IoReq,
+ #{parent_gl_pid := ParentGL} = St) ->
+ ParentGL ! IoReq,
+ {noreply,St};
+
+handle_info(Msg, St) ->
+ io:format(user, "Common Test Group Leader process got: ~tp~n", [Msg]),
+ {noreply,St}.
+
+handle_call(_Req, _From, St) ->
+ {reply,ok,St}.
+
+terminate(_, _) ->
+ ok.
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 104515e57e..141c7f5b0a 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -52,6 +52,10 @@
%%%
%%% @doc Test server framework callback, called by the test_server
%%% when a new test case is started.
+init_tc(_,{end_per_testcase_not_run,_},[Config]) ->
+ %% Testcase is completed (skipped or failed), but end_per_testcase
+ %% is not run - don't call pre-hook.
+ {ok,[Config]};
init_tc(Mod,EPTC={end_per_testcase,_},[Config]) ->
%% in case Mod == ct_framework, lookup the suite name
Suite = get_suite_name(Mod, Config),
@@ -62,7 +66,7 @@ init_tc(Mod,EPTC={end_per_testcase,_},[Config]) ->
Other
end;
-init_tc(Mod,Func0,Args) ->
+init_tc(Mod,Func0,Args) ->
%% in case Mod == ct_framework, lookup the suite name
Suite = get_suite_name(Mod, Args),
{Func,HookFunc} = case Func0 of
@@ -84,12 +88,15 @@ init_tc(Mod,Func0,Args) ->
andalso Func=/=end_per_group
andalso ct_util:get_testdata(skip_rest) of
true ->
+ initialize(false,Mod,Func,Args),
{auto_skip,"Repeated test stopped by force_stop option"};
_ ->
case ct_util:get_testdata(curr_tc) of
{Suite,{suite0_failed,{require,Reason}}} ->
+ initialize(false,Mod,Func,Args),
{auto_skip,{require_failed_in_suite0,Reason}};
{Suite,{suite0_failed,_}=Failure} ->
+ initialize(false,Mod,Func,Args),
{fail,Failure};
_ ->
ct_util:update_testdata(curr_tc,
@@ -118,16 +125,14 @@ init_tc(Mod,Func0,Args) ->
end,
init_tc1(Mod,Suite,Func,HookFunc,Args);
{failed,Seq,BadFunc} ->
- {auto_skip,{sequence_failed,Seq,BadFunc}}
+ initialize(false,Mod,Func,Args),
+ {auto_skip,{sequence_failed,Seq,BadFunc}}
end
end
end.
init_tc1(?MODULE,_,error_in_suite,_,[Config0]) when is_list(Config0) ->
- ct_logs:init_tc(false),
- ct_event:notify(#event{name=tc_start,
- node=node(),
- data={?MODULE,error_in_suite}}),
+ initialize(false,?MODULE,error_in_suite),
_ = ct_suite_init(?MODULE,error_in_suite,[],Config0),
case ?val(error,Config0) of
undefined ->
@@ -177,27 +182,21 @@ init_tc1(Mod,Suite,Func,HookFunc,[Config0]) when is_list(Config0) ->
ct_config:delete_default_config(testcase),
HookFunc
end,
- Initialize = fun() ->
- ct_logs:init_tc(false),
- ct_event:notify(#event{name=tc_start,
- node=node(),
- data={Mod,FuncSpec}})
- end,
case add_defaults(Mod,Func,AllGroups) of
Error = {suite0_failed,_} ->
- Initialize(),
+ initialize(false,Mod,FuncSpec),
ct_util:set_testdata({curr_tc,{Suite,Error}}),
{error,Error};
Error = {group0_failed,_} ->
- Initialize(),
+ initialize(false,Mod,FuncSpec),
{auto_skip,Error};
Error = {testcase0_failed,_} ->
- Initialize(),
+ initialize(false,Mod,FuncSpec),
{auto_skip,Error};
{SuiteInfo,MergeResult} ->
case MergeResult of
{error,Reason} ->
- Initialize(),
+ initialize(false,Mod,FuncSpec),
{fail,Reason};
_ ->
init_tc2(Mod,Suite,Func,HookFunc1,
@@ -236,11 +235,8 @@ init_tc2(Mod,Suite,Func,HookFunc,SuiteInfo,MergeResult,Config) ->
Conns ->
ct_util:silence_connections(Conns)
end,
- ct_logs:init_tc(Func == init_per_suite),
FuncSpec = group_or_func(Func,Config),
- ct_event:notify(#event{name=tc_start,
- node=node(),
- data={Mod,FuncSpec}}),
+ initialize((Func==init_per_suite),Mod,FuncSpec),
case catch configure(MergedInfo,MergedInfo,SuiteInfo,
FuncSpec,[],Config) of
@@ -268,6 +264,18 @@ init_tc2(Mod,Suite,Func,HookFunc,SuiteInfo,MergeResult,Config) ->
end
end.
+initialize(RefreshLogs,Mod,Func,[Config]) when is_list(Config) ->
+ initialize(RefreshLogs,Mod,group_or_func(Func,Config));
+initialize(RefreshLogs,Mod,Func,_) ->
+ initialize(RefreshLogs,Mod,Func).
+
+initialize(RefreshLogs,Mod,FuncSpec) ->
+ ct_logs:init_tc(RefreshLogs),
+ ct_event:notify(#event{name=tc_start,
+ node=node(),
+ data={Mod,FuncSpec}}).
+
+
ct_suite_init(Suite,HookFunc,PostInitHook,Config) when is_list(Config) ->
case ct_hooks:init_tc(Suite,HookFunc,Config) of
NewConfig when is_list(NewConfig) ->
@@ -307,7 +315,7 @@ add_defaults(Mod,Func, GroupPath) ->
"~w:suite/0 failed: ~p~n",
[Suite,Reason]),
io:format(ErrStr, []),
- io:format(user, ErrStr, []),
+ io:format(?def_gl, ErrStr, []),
{suite0_failed,{exited,Reason}};
SuiteInfo when is_list(SuiteInfo) ->
case lists:all(fun(E) when is_tuple(E) -> true;
@@ -330,7 +338,7 @@ add_defaults(Mod,Func, GroupPath) ->
"~w:suite/0: ~p~n",
[Suite,SuiteInfo]),
io:format(ErrStr, []),
- io:format(user, ErrStr, []),
+ io:format(?def_gl, ErrStr, []),
{suite0_failed,bad_return_value}
end;
SuiteInfo ->
@@ -338,7 +346,7 @@ add_defaults(Mod,Func, GroupPath) ->
"Invalid return value from "
"~w:suite/0: ~p~n", [Suite,SuiteInfo]),
io:format(ErrStr, []),
- io:format(user, ErrStr, []),
+ io:format(?def_gl, ErrStr, []),
{suite0_failed,bad_return_value}
end.
@@ -366,7 +374,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo) ->
"~w:group(~w): ~p~n",
[Mod,GrName,BadGr0Val]),
io:format(Gr0ErrStr, []),
- io:format(user, Gr0ErrStr, []),
+ io:format(?def_gl, Gr0ErrStr, []),
{group0_failed,bad_return_value};
_ ->
Args = if Func == init_per_group ; Func == end_per_group ->
@@ -388,7 +396,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo) ->
"~w:~w/0: ~p~n",
[Mod,Func,BadTC0Val]),
io:format(TC0ErrStr, []),
- io:format(user, TC0ErrStr, []),
+ io:format(?def_gl, TC0ErrStr, []),
{testcase0_failed,bad_return_value};
_ ->
%% let test case info (also for all config funcs) override
@@ -675,22 +683,35 @@ end_tc(Mod,Func,{Result,[Args]}, Return) ->
end_tc(Mod,Func,self(),Result,Args,Return).
end_tc(Mod,IPTC={init_per_testcase,_Func},_TCPid,Result,Args,Return) ->
- %% in case Mod == ct_framework, lookup the suite name
- Suite = get_suite_name(Mod, Args),
- case ct_hooks:end_tc(Suite,IPTC,Args,Result,Return) of
- '$ct_no_change' ->
- ok;
- HookResult ->
- HookResult
+ case end_hook_func(IPTC,Return,IPTC) of
+ undefined -> ok;
+ _ ->
+ %% in case Mod == ct_framework, lookup the suite name
+ Suite = get_suite_name(Mod, Args),
+ case ct_hooks:end_tc(Suite,IPTC,Args,Result,Return) of
+ '$ct_no_change' ->
+ ok;
+ HookResult ->
+ HookResult
+ end
end;
end_tc(Mod,Func0,TCPid,Result,Args,Return) ->
%% in case Mod == ct_framework, lookup the suite name
Suite = get_suite_name(Mod, Args),
- {EPTC,Func} = case Func0 of
- {end_per_testcase,F} -> {true,F};
- _ -> {false,Func0}
- end,
+ {Func,FuncSpec,HookFunc} =
+ case Func0 of
+ {end_per_testcase_not_run,F} ->
+ %% Testcase is completed (skipped or failed), but
+ %% end_per_testcase is not run - don't call post-hook.
+ {F,F,undefined};
+ {end_per_testcase,F} ->
+ {F,F,Func0};
+ _ ->
+ FS = group_or_func(Func0,Args),
+ HF = end_hook_func(Func0,Return,FS),
+ {Func0,FS,HF}
+ end,
test_server:timetrap_cancel(),
@@ -717,20 +738,18 @@ end_tc(Mod,Func0,TCPid,Result,Args,Return) ->
end,
ct_util:delete_suite_data(last_saved_config),
- {FuncSpec,HookFunc} =
- if not EPTC ->
- FS = group_or_func(Func,Args),
- {FS,FS};
- true ->
- {Func,Func0}
- end,
{Result1,FinalNotify} =
- case ct_hooks:end_tc(Suite,HookFunc,Args,Result,Return) of
- '$ct_no_change' ->
- {ok,Result};
- HookResult ->
- {HookResult,HookResult}
- end,
+ case HookFunc of
+ undefined ->
+ {ok,Result};
+ _ ->
+ case ct_hooks:end_tc(Suite,HookFunc,Args,Result,Return) of
+ '$ct_no_change' ->
+ {ok,Result};
+ HookResult ->
+ {HookResult,HookResult}
+ end
+ end,
FinalResult =
case get('$test_server_framework_test') of
undefined ->
@@ -821,6 +840,34 @@ end_tc(Mod,Func0,TCPid,Result,Args,Return) ->
end,
FinalResult.
+%% This is to make sure that no post_init_per_* is ever called if the
+%% corresponding pre_init_per_* was not called.
+%% The skip or fail reasons are those that can be returned from
+%% init_tc above in situations where we never came to call
+%% ct_hooks:init_tc/3, e.g. if suite/0 fails, then we never call
+%% ct_hooks:init_tc for init_per_suite, and thus we must not call
+%% ct_hooks:end_tc for init_per_suite either.
+end_hook_func({init_per_testcase,_},{auto_skip,{sequence_failed,_,_}},_) ->
+ undefined;
+end_hook_func({init_per_testcase,_},{auto_skip,"Repeated test stopped by force_stop option"},_) ->
+ undefined;
+end_hook_func({init_per_testcase,_},{fail,{config_name_already_in_use,_}},_) ->
+ undefined;
+end_hook_func({init_per_testcase,_},{auto_skip,{InfoFuncError,_}},_)
+ when InfoFuncError==testcase0_failed;
+ InfoFuncError==require_failed ->
+ undefined;
+end_hook_func(init_per_group,{auto_skip,{InfoFuncError,_}},_)
+ when InfoFuncError==group0_failed;
+ InfoFuncError==require_failed ->
+ undefined;
+end_hook_func(init_per_suite,{auto_skip,{require_failed_in_suite0,_}},_) ->
+ undefined;
+end_hook_func(init_per_suite,{auto_skip,{failed,{error,{suite0_failed,_}}}},_) ->
+ undefined;
+end_hook_func(_,_,Default) ->
+ Default.
+
%% {error,Reason} | {skip,Reason} | {timetrap_timeout,TVal} |
%% {testcase_aborted,Reason} | testcase_aborted_or_killed |
%% {'EXIT',Reason} | {fail,Reason} | {failed,Reason} |
@@ -927,7 +974,7 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
Div = "~n- - - - - - - - - - - - - - - - - - - "
"- - - - - - - - - - - - - - - - - - - - -~n",
ErrorStr2 = io_lib:format(ErrorFormat, ErrorArgs),
- io:format(user, lists:concat([Div,ErrorStr2,Div,"~n"]),
+ io:format(?def_gl, lists:concat([Div,ErrorStr2,Div,"~n"]),
[]),
Link =
"\n\n<a href=\"#end\">"
@@ -1133,7 +1180,7 @@ get_all(Mod, ConfTests) ->
ErrStr = io_lib:format("~n*** ERROR *** "
"~w:all/0 failed: ~p~n",
[Mod,ExitReason]),
- io:format(user, ErrStr, []),
+ io:format(?def_gl, ErrStr, []),
%% save the error info so it doesn't get printed twice
ct_util:set_testdata_async({{error_in_suite,Mod},
ExitReason});
@@ -1339,25 +1386,25 @@ report(What,Data) ->
ok;
tc_done ->
{Suite,{Func,GrName},Result} = Data,
- Data1 = if GrName == undefined -> {Suite,Func,Result};
- true -> Data
- end,
+ FuncSpec = if GrName == undefined -> Func;
+ true -> {Func,GrName}
+ end,
%% Register the group leader for the process calling the report
%% function, making it possible for a hook function to print
%% in the test case log file
ReportingPid = self(),
ct_logs:register_groupleader(ReportingPid, group_leader()),
case Result of
- {failed, _} ->
- ct_hooks:on_tc_fail(What, Data1);
- {skipped,{failed,{_,init_per_testcase,_}}} ->
- ct_hooks:on_tc_skip(tc_auto_skip, Data1);
- {skipped,{require_failed,_}} ->
- ct_hooks:on_tc_skip(tc_auto_skip, Data1);
- {skipped,_} ->
- ct_hooks:on_tc_skip(tc_user_skip, Data1);
- {auto_skipped,_} ->
- ct_hooks:on_tc_skip(tc_auto_skip, Data1);
+ {failed, Reason} ->
+ ct_hooks:on_tc_fail(What, {Suite,FuncSpec,Reason});
+ {skipped,{failed,{_,init_per_testcase,_}}=Reason} ->
+ ct_hooks:on_tc_skip(tc_auto_skip, {Suite,FuncSpec,Reason});
+ {skipped,{require_failed,_}=Reason} ->
+ ct_hooks:on_tc_skip(tc_auto_skip, {Suite,FuncSpec,Reason});
+ {skipped,Reason} ->
+ ct_hooks:on_tc_skip(tc_user_skip, {Suite,FuncSpec,Reason});
+ {auto_skipped,Reason} ->
+ ct_hooks:on_tc_skip(tc_auto_skip, {Suite,FuncSpec,Reason});
_Else ->
ok
end,
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index 23ba1ab981..8b59d3ab23 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -27,7 +27,7 @@
-export([start/4, stop/1, get_conn_pid/1, check_opts/1]).
-export([call/2, call/3, return/2, do_within_time/2]).
--export([log/3, start_log/1, cont_log/2, end_log/0]).
+-export([log/3, start_log/1, cont_log/2, cont_log_no_timestamp/2, end_log/0]).
%%----------------------------------------------------------------------
%% Exported types
@@ -175,6 +175,14 @@ cont_log(Format,Args) ->
log(cont_log,[Format,Args]).
%%%-----------------------------------------------------------------
+%%% @spec cont_log_no_timestamp(Format,Args) -> ok
+%%%
+%%% @doc Log activities on the current connection (tool-internal use only).
+%%% @see ct_logs:cont_log/2
+cont_log_no_timestamp(Format,Args) ->
+ log(cont_log_no_timestamp,[Format,Args]).
+
+%%%-----------------------------------------------------------------
%%% @spec end_log() -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl
index 1375e7dcc7..333151ee1b 100644
--- a/lib/common_test/src/ct_groups.erl
+++ b/lib/common_test/src/ct_groups.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -442,17 +442,21 @@ make_conf(Mod, Name, Props, TestSpec) ->
ok
end,
{InitConf,EndConf,ExtraProps} =
- case erlang:function_exported(Mod,init_per_group,2) of
- true ->
- {{Mod,init_per_group},{Mod,end_per_group},[]};
- false ->
+ case {erlang:function_exported(Mod,init_per_group,2),
+ erlang:function_exported(Mod,end_per_group,2)} of
+ {false,false} ->
ct_logs:log("TEST INFO", "init_per_group/2 and "
"end_per_group/2 missing for group "
"~w in ~w, using default.",
[Name,Mod]),
{{ct_framework,init_per_group},
{ct_framework,end_per_group},
- [{suite,Mod}]}
+ [{suite,Mod}]};
+ _ ->
+ %% If any of these exist, the other should too
+ %% (required and documented). If it isn't, it will fail
+ %% with reason 'undef'.
+ {{Mod,init_per_group},{Mod,end_per_group},[]}
end,
{conf,[{name,Name}|Props++ExtraProps],InitConf,TestSpec,EndConf}.
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index c9a4abb5ee..8cdc6d8c75 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -92,15 +92,17 @@ init_tc(Mod, end_per_suite, Config) ->
call(fun call_generic/3, Config, [pre_end_per_suite, Mod]);
init_tc(Mod, {init_per_group, GroupName, Properties}, Config) ->
maybe_start_locker(Mod, GroupName, Properties),
- call(fun call_generic/3, Config, [pre_init_per_group, GroupName]);
-init_tc(_Mod, {end_per_group, GroupName, _}, Config) ->
- call(fun call_generic/3, Config, [pre_end_per_group, GroupName]);
-init_tc(_Mod, {init_per_testcase,TC}, Config) ->
- call(fun call_generic/3, Config, [pre_init_per_testcase, TC]);
-init_tc(_Mod, {end_per_testcase,TC}, Config) ->
- call(fun call_generic/3, Config, [pre_end_per_testcase, TC]);
-init_tc(_Mod, TC = error_in_suite, Config) ->
- call(fun call_generic/3, Config, [pre_init_per_testcase, TC]).
+ call(fun call_generic_fallback/3, Config,
+ [pre_init_per_group, Mod, GroupName]);
+init_tc(Mod, {end_per_group, GroupName, _}, Config) ->
+ call(fun call_generic_fallback/3, Config,
+ [pre_end_per_group, Mod, GroupName]);
+init_tc(Mod, {init_per_testcase,TC}, Config) ->
+ call(fun call_generic_fallback/3, Config, [pre_init_per_testcase, Mod, TC]);
+init_tc(Mod, {end_per_testcase,TC}, Config) ->
+ call(fun call_generic_fallback/3, Config, [pre_end_per_testcase, Mod, TC]);
+init_tc(Mod, TC = error_in_suite, Config) ->
+ call(fun call_generic_fallback/3, Config, [pre_init_per_testcase, Mod, TC]).
%% @doc Called as each test case is completed. This includes all configuration
%% tests.
@@ -126,23 +128,23 @@ end_tc(Mod, init_per_suite, Config, _Result, Return) ->
end_tc(Mod, end_per_suite, Config, Result, _Return) ->
call(fun call_generic/3, Result, [post_end_per_suite, Mod, Config],
'$ct_no_change');
-end_tc(_Mod, {init_per_group, GroupName, _}, Config, _Result, Return) ->
- call(fun call_generic/3, Return, [post_init_per_group, GroupName, Config],
- '$ct_no_change');
+end_tc(Mod, {init_per_group, GroupName, _}, Config, _Result, Return) ->
+ call(fun call_generic_fallback/3, Return,
+ [post_init_per_group, Mod, GroupName, Config], '$ct_no_change');
end_tc(Mod, {end_per_group, GroupName, Properties}, Config, Result, _Return) ->
- Res = call(fun call_generic/3, Result,
- [post_end_per_group, GroupName, Config], '$ct_no_change'),
+ Res = call(fun call_generic_fallback/3, Result,
+ [post_end_per_group, Mod, GroupName, Config], '$ct_no_change'),
maybe_stop_locker(Mod, GroupName, Properties),
Res;
-end_tc(_Mod, {init_per_testcase,TC}, Config, Result, _Return) ->
- call(fun call_generic/3, Result, [post_init_per_testcase, TC, Config],
- '$ct_no_change');
-end_tc(_Mod, {end_per_testcase,TC}, Config, Result, _Return) ->
- call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config],
- '$ct_no_change');
-end_tc(_Mod, TC = error_in_suite, Config, Result, _Return) ->
- call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config],
- '$ct_no_change').
+end_tc(Mod, {init_per_testcase,TC}, Config, Result, _Return) ->
+ call(fun call_generic_fallback/3, Result,
+ [post_init_per_testcase, Mod, TC, Config], '$ct_no_change');
+end_tc(Mod, {end_per_testcase,TC}, Config, Result, _Return) ->
+ call(fun call_generic_fallback/3, Result,
+ [post_end_per_testcase, Mod, TC, Config], '$ct_no_change');
+end_tc(Mod, TC = error_in_suite, Config, Result, _Return) ->
+ call(fun call_generic_fallback/3, Result,
+ [post_end_per_testcase, Mod, TC, Config], '$ct_no_change').
%% Case = TestCase | {TestCase,GroupName}
@@ -181,15 +183,21 @@ call_terminate(#ct_hook_config{ module = Mod, state = State} = Hook, _, _) ->
{[],Hook}.
call_cleanup(#ct_hook_config{ module = Mod, state = State} = Hook,
- Reason, [Function, _Suite | Args]) ->
+ Reason, [Function | Args]) ->
NewState = catch_apply(Mod,Function, Args ++ [Reason, State],
- State),
+ State, true),
{Reason, Hook#ct_hook_config{ state = NewState } }.
-call_generic(#ct_hook_config{ module = Mod, state = State} = Hook,
- Value, [Function | Args]) ->
+call_generic(Hook, Value, Meta) ->
+ do_call_generic(Hook, Value, Meta, false).
+
+call_generic_fallback(Hook, Value, Meta) ->
+ do_call_generic(Hook, Value, Meta, true).
+
+do_call_generic(#ct_hook_config{ module = Mod, state = State} = Hook,
+ Value, [Function | Args], Fallback) ->
{NewValue, NewState} = catch_apply(Mod, Function, Args ++ [Value, State],
- {Value,State}),
+ {Value,State}, Fallback),
{NewValue, Hook#ct_hook_config{ state = NewState } }.
%% Generic call function
@@ -257,15 +265,15 @@ remove(Key,List) when is_list(List) ->
remove(_, Else) ->
Else.
-%% Translate scopes, i.e. init_per_group,group1 -> end_per_group,group1 etc
-scope([pre_init_per_testcase, TC|_]) ->
- [post_init_per_testcase, TC];
-scope([pre_end_per_testcase, TC|_]) ->
- [post_end_per_testcase, TC];
-scope([pre_init_per_group, GroupName|_]) ->
- [post_end_per_group, GroupName];
-scope([post_init_per_group, GroupName|_]) ->
- [post_end_per_group, GroupName];
+%% Translate scopes, i.e. is_tuplenit_per_group,group1 -> end_per_group,group1 etc
+scope([pre_init_per_testcase, SuiteName, TC|_]) ->
+ [post_init_per_testcase, SuiteName, TC];
+scope([pre_end_per_testcase, SuiteName, TC|_]) ->
+ [post_end_per_testcase, SuiteName, TC];
+scope([pre_init_per_group, SuiteName, GroupName|_]) ->
+ [post_end_per_group, SuiteName, GroupName];
+scope([post_init_per_group, SuiteName, GroupName|_]) ->
+ [post_end_per_group, SuiteName, GroupName];
scope([pre_init_per_suite, SuiteName|_]) ->
[post_end_per_suite, SuiteName];
scope([post_init_per_suite, SuiteName|_]) ->
@@ -273,14 +281,29 @@ scope([post_init_per_suite, SuiteName|_]) ->
scope(init) ->
none.
-terminate_if_scope_ends(HookId, [on_tc_skip,_Suite,{end_per_group,Name}],
+strip_config([post_init_per_testcase, SuiteName, TC|_]) ->
+ [post_init_per_testcase, SuiteName, TC];
+strip_config([post_end_per_testcase, SuiteName, TC|_]) ->
+ [post_end_per_testcase, SuiteName, TC];
+strip_config([post_init_per_group, SuiteName, GroupName|_]) ->
+ [post_init_per_group, SuiteName, GroupName];
+strip_config([post_end_per_group, SuiteName, GroupName|_]) ->
+ [post_end_per_group, SuiteName, GroupName];
+strip_config([post_init_per_suite, SuiteName|_]) ->
+ [post_init_per_suite, SuiteName];
+strip_config([post_end_per_suite, SuiteName|_]) ->
+ [post_end_per_suite, SuiteName];
+strip_config(Other) ->
+ Other.
+
+
+terminate_if_scope_ends(HookId, [on_tc_skip,Suite,{end_per_group,Name}],
Hooks) ->
- terminate_if_scope_ends(HookId, [post_end_per_group, Name], Hooks);
+ terminate_if_scope_ends(HookId, [post_end_per_group, Suite, Name], Hooks);
terminate_if_scope_ends(HookId, [on_tc_skip,Suite,end_per_suite], Hooks) ->
terminate_if_scope_ends(HookId, [post_end_per_suite, Suite], Hooks);
-terminate_if_scope_ends(HookId, [Function,Tag|T], Hooks) when T =/= [] ->
- terminate_if_scope_ends(HookId,[Function,Tag],Hooks);
-terminate_if_scope_ends(HookId, Function, Hooks) ->
+terminate_if_scope_ends(HookId, Function0, Hooks) ->
+ Function = strip_config(Function0),
case lists:keyfind(HookId, #ct_hook_config.id, Hooks) of
#ct_hook_config{ id = HookId, scope = Function} = Hook ->
terminate([Hook]),
@@ -384,21 +407,29 @@ pos(Id,[_|Rest],Num) ->
catch_apply(M,F,A, Default) ->
+ catch_apply(M,F,A,Default,false).
+catch_apply(M,F,A, Default, Fallback) ->
+ not erlang:module_loaded(M) andalso (catch M:module_info()),
+ case erlang:function_exported(M,F,length(A)) of
+ false when Fallback ->
+ catch_apply(M,F,tl(A),Default,false);
+ false ->
+ Default;
+ true ->
+ catch_apply(M,F,A)
+ end.
+
+catch_apply(M,F,A) ->
try
- erlang:apply(M,F,A)
+ erlang:apply(M,F,A)
catch _:Reason ->
- case erlang:get_stacktrace() of
- %% Return the default if it was the CTH module which did not have the function.
- [{M,F,A,_}|_] when Reason == undef ->
- Default;
- Trace ->
- ct_logs:log("Suite Hook","Call to CTH failed: ~w:~p",
- [error,{Reason,Trace}]),
- throw({error_in_cth_call,
- lists:flatten(
- io_lib:format("~w:~w/~w CTH call failed",
- [M,F,length(A)]))})
- end
+ Trace = erlang:get_stacktrace(),
+ ct_logs:log("Suite Hook","Call to CTH failed: ~w:~p",
+ [error,{Reason,Trace}]),
+ throw({error_in_cth_call,
+ lists:flatten(
+ io_lib:format("~w:~w/~w CTH call failed",
+ [M,F,length(A)]))})
end.
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 53245c596a..09ad709da5 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -32,7 +32,7 @@
-export([init/2, close/2, init_tc/1, end_tc/1]).
-export([register_groupleader/2, unregister_groupleader/1]).
-export([get_log_dir/0, get_log_dir/1]).
--export([log/3, start_log/1, cont_log/2, end_log/0]).
+-export([log/3, start_log/1, cont_log/2, cont_log_no_timestamp/2, end_log/0]).
-export([set_stylesheet/2, clear_stylesheet/1]).
-export([add_external_logs/1, add_link/3]).
-export([make_last_run_index/0]).
@@ -45,8 +45,8 @@
%% Logging stuff directly from testcase
-export([tc_log/3, tc_log/4, tc_log/5, tc_log/6,
tc_log_async/3, tc_log_async/5,
- tc_print/3, tc_print/4,
- tc_pal/3, tc_pal/4, ct_log/3,
+ tc_print/3, tc_print/4, tc_print/5,
+ tc_pal/3, tc_pal/4, tc_pal/5, ct_log/3,
basic_html/0]).
%% Simulate logger process for use without ct environment running
@@ -373,6 +373,20 @@ cont_log(Format,Args) ->
ok.
%%%-----------------------------------------------------------------
+%%% @spec cont_log_no_timestamp(Format,Args) -> ok
+%%%
+%%% @doc Adds information about an activity (tool-internal use only).
+%%%
+%%% @see start_log/1
+%%% @see end_log/0
+cont_log_no_timestamp([],[]) ->
+ ok;
+cont_log_no_timestamp(Format,Args) ->
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
+ [{Format,Args}],true}),
+ ok.
+
+%%%-----------------------------------------------------------------
%%% @spec end_log() -> ok
%%%
%%% @doc Ends the logging of an activity (tool-internal use only).
@@ -433,10 +447,10 @@ tc_log(Category,Importance,Format,Args,Opts) ->
tc_log(Category,Importance,"User",Format,Args,Opts).
%%%-----------------------------------------------------------------
-%%% @spec tc_log(Category,Importance,Printer,Format,Args,Opts) -> ok
+%%% @spec tc_log(Category,Importance,Heading,Format,Args,Opts) -> ok
%%% Category = atom()
%%% Importance = integer()
-%%% Printer = string()
+%%% Heading = string()
%%% Format = string()
%%% Args = list()
%%% Opts = list()
@@ -446,13 +460,18 @@ tc_log(Category,Importance,Format,Args,Opts) ->
%%% <p>This function is called by <code>ct</code> when logging
%%% stuff directly from a testcase (i.e. not from within the CT
%%% framework).</p>
-tc_log(Category,Importance,Printer,Format,Args,Opts) ->
+tc_log(Category,Importance,Heading,Format,Args,Opts) ->
Data =
case lists:member(no_css, Opts) of
true ->
[{Format,Args}];
false ->
- [{hd,div_header(Category,Printer),[]},
+ Heading1 =
+ case proplists:get_value(heading, Opts) of
+ undefined -> Heading;
+ Str -> Str
+ end,
+ [{hd,div_header(Category,Heading1),[]},
{Format,Args},
{ft,div_footer(),[]}]
end,
@@ -470,7 +489,7 @@ tc_log_async(Category,Format,Args) ->
%%% @spec tc_log_async(Category,Importance,Format,Args) -> ok
%%% Category = atom()
%%% Importance = integer()
-%%% Printer = string()
+%%% Heading = string()
%%% Format = string()
%%% Args = list()
%%%
@@ -481,31 +500,38 @@ tc_log_async(Category,Format,Args) ->
%%% to avoid deadlocks when e.g. the hook that handles SASL printouts
%%% prints to the test case log file at the same time test server
%%% asks ct_logs for an html wrapper.</p>
-tc_log_async(Category,Importance,Printer,Format,Args) ->
+tc_log_async(Category,Importance,Heading,Format,Args) ->
cast({log,async,self(),group_leader(),Category,Importance,
- [{hd,div_header(Category,Printer),[]},
+ [{hd,div_header(Category,Heading),[]},
{Format,Args},
{ft,div_footer(),[]}],
true}),
ok.
%%%-----------------------------------------------------------------
%%% @spec tc_print(Category,Format,Args)
-%%% @equiv tc_print(Category,?STD_IMPORTANCE,Format,Args)
+%%% @equiv tc_print(Category,?STD_IMPORTANCE,Format,Args,[])
tc_print(Category,Format,Args) ->
- tc_print(Category,?STD_IMPORTANCE,Format,Args).
+ tc_print(Category,?STD_IMPORTANCE,Format,Args,[]).
%%%-----------------------------------------------------------------
-%%% @spec tc_print(Category,Importance,Format,Args) -> ok
+%%% @spec tc_print(Category,Importance,Format,Args)
+%%% @equiv tc_print(Category,Importance,Format,Args,[])
+tc_print(Category,Importance,Format,Args) ->
+ tc_print(Category,Importance,Format,Args,[]).
+
+%%%-----------------------------------------------------------------
+%%% @spec tc_print(Category,Importance,Format,Args,Opts) -> ok
%%% Category = atom()
%%% Importance = integer()
%%% Format = string()
%%% Args = list()
+%%% Opts = list()
%%%
%%% @doc Console printout from a testcase.
%%%
%%% <p>This function is called by <code>ct</code> when printing
%%% stuff from a testcase on the user console.</p>
-tc_print(Category,Importance,Format,Args) ->
+tc_print(Category,Importance,Format,Args,Opts) ->
VLvl = case ct_util:get_verbosity(Category) of
undefined ->
ct_util:get_verbosity('$unspecified');
@@ -517,50 +543,61 @@ tc_print(Category,Importance,Format,Args) ->
Val
end,
if Importance >= (100-VLvl) ->
- Head = get_heading(Category),
- io:format(user, lists:concat([Head,Format,"\n\n"]), Args),
+ Heading =
+ case proplists:get_value(heading, Opts) of
+ undefined -> atom_to_list(Category);
+ Hd -> Hd
+ end,
+ Str = lists:concat([get_header(Heading),Format,"\n\n"]),
+ try
+ io:format(?def_gl, Str, Args)
+ catch
+ %% default group leader probably not started, or has stopped
+ _:_ -> io:format(user, Str, Args)
+ end,
ok;
true ->
ok
end.
-get_heading(default) ->
+get_header("default") ->
io_lib:format("\n-----------------------------"
"-----------------------\n~s\n",
[log_timestamp(?now)]);
-get_heading(Category) ->
+get_header(Heading) ->
io_lib:format("\n-----------------------------"
- "-----------------------\n~s ~w\n",
- [log_timestamp(?now),Category]).
+ "-----------------------\n~s ~s\n",
+ [Heading,log_timestamp(?now)]).
%%%-----------------------------------------------------------------
%%% @spec tc_pal(Category,Format,Args) -> ok
-%%% @equiv tc_pal(Category,?STD_IMPORTANCE,Format,Args) -> ok
+%%% @equiv tc_pal(Category,?STD_IMPORTANCE,Format,Args,[]) -> ok
tc_pal(Category,Format,Args) ->
- tc_pal(Category,?STD_IMPORTANCE,Format,Args).
+ tc_pal(Category,?STD_IMPORTANCE,Format,Args,[]).
%%%-----------------------------------------------------------------
%%% @spec tc_pal(Category,Importance,Format,Args) -> ok
+%%% @equiv tc_pal(Category,Importance,Format,Args,[]) -> ok
+tc_pal(Category,Importance,Format,Args) ->
+ tc_pal(Category,Importance,Format,Args,[]).
+
+%%%-----------------------------------------------------------------
+%%% @spec tc_pal(Category,Importance,Format,Args,Opts) -> ok
%%% Category = atom()
%%% Importance = integer()
%%% Format = string()
%%% Args = list()
+%%% Opts = list()
%%%
%%% @doc Print and log from a testcase.
%%%
%%% <p>This function is called by <code>ct</code> when logging
%%% stuff directly from a testcase. The info is written both in the
%%% log and on the console.</p>
-tc_pal(Category,Importance,Format,Args) ->
- tc_print(Category,Importance,Format,Args),
- cast({log,sync,self(),group_leader(),Category,Importance,
- [{hd,div_header(Category),[]},
- {Format,Args},
- {ft,div_footer(),[]}],
- true}),
- ok.
-
+tc_pal(Category,Importance,Format,Args,Opts) ->
+ tc_print(Category,Importance,Format,Args,Opts),
+ tc_log(Category,Importance,"User",Format,Args,[esc_chars|Opts]).
%%%-----------------------------------------------------------------
%%% @spec ct_log(Category,Format,Args) -> ok
@@ -589,13 +626,12 @@ int_footer() ->
div_header(Class) ->
div_header(Class,"User").
-div_header(Class,Printer) ->
+div_header(Class,Heading) ->
"\n</pre>\n<div class=\"" ++ atom_to_list(Class) ++ "\"><pre><b>*** "
- ++ Printer ++ " " ++ log_timestamp(?now) ++ " ***</b>".
+ ++ Heading ++ " " ++ log_timestamp(?now) ++ " ***</b>".
div_footer() ->
"</pre></div>\n<pre>".
-
maybe_log_timestamp() ->
{MS,S,US} = ?now,
case get(log_timestamp) of
@@ -666,7 +702,7 @@ logger(Parent, Mode, Verbosity) ->
PrivFilesDestRun = [filename:join(AbsDir, F) || F <- PrivFiles],
case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of
{error,Src1,Dest1,Reason1} ->
- io:format(user, "ERROR! "++
+ io:format(?def_gl, "ERROR! "++
"Priv file ~p could not be copied to ~p. "++
"Reason: ~p~n",
[Src1,Dest1,Reason1]),
@@ -674,7 +710,7 @@ logger(Parent, Mode, Verbosity) ->
ok ->
case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of
{error,Src2,Dest2,Reason2} ->
- io:format(user,
+ io:format(?def_gl,
"ERROR! "++
"Priv file ~p could not be copied to ~p. "
++"Reason: ~p~n",
diff --git a/lib/common_test/src/ct_make.erl b/lib/common_test/src/ct_make.erl
index e7a9cfa843..f22959d457 100644
--- a/lib/common_test/src/ct_make.erl
+++ b/lib/common_test/src/ct_make.erl
@@ -342,5 +342,7 @@ check_includes2(Epp, File, ObjMTime) ->
epp:close(Epp),
false;
{error, _Error} ->
+ check_includes2(Epp, File, ObjMTime);
+ {warning, _Warning} ->
check_includes2(Epp, File, ObjMTime)
end.
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index fbb9c7ab60..10e62e18b8 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -76,8 +76,8 @@
abort_if_missing_suites,
silent_connections = [],
stylesheet,
- multiply_timetraps = 1,
- scale_timetraps = false,
+ multiply_timetraps,
+ scale_timetraps,
create_priv_dir,
testspec_files = [],
current_testspec,
@@ -264,11 +264,11 @@ script_start1(Parent, Args) ->
[], Args),
Verbosity = verbosity_args2opts(Args),
MultTT = get_start_opt(multiply_timetraps,
- fun([MT]) -> list_to_integer(MT) end, 1, Args),
+ fun([MT]) -> list_to_integer(MT) end, Args),
ScaleTT = get_start_opt(scale_timetraps,
fun([CT]) -> list_to_atom(CT);
([]) -> true
- end, false, Args),
+ end, Args),
CreatePrivDir = get_start_opt(create_priv_dir,
fun([PD]) -> list_to_atom(PD);
([]) -> auto_per_tc
@@ -1055,8 +1055,8 @@ run_test2(StartOpts) ->
CoverStop = get_start_opt(cover_stop, value, StartOpts),
%% timetrap manipulation
- MultiplyTT = get_start_opt(multiply_timetraps, value, 1, StartOpts),
- ScaleTT = get_start_opt(scale_timetraps, value, false, StartOpts),
+ MultiplyTT = get_start_opt(multiply_timetraps, value, StartOpts),
+ ScaleTT = get_start_opt(scale_timetraps, value, StartOpts),
%% create unique priv dir names
CreatePrivDir = get_start_opt(create_priv_dir, value, StartOpts),
@@ -2155,8 +2155,8 @@ continue(_MakeErrors, true) ->
false;
continue(_MakeErrors, _AbortIfMissingSuites) ->
io:nl(),
- OldGl = group_leader(),
- case set_group_leader_same_as_shell() of
+ OldGL = group_leader(),
+ case set_group_leader_same_as_shell(OldGL) of
true ->
S = self(),
io:format("Failed to compile or locate one "
@@ -2172,7 +2172,7 @@ continue(_MakeErrors, _AbortIfMissingSuites) ->
S ! false
end
end),
- group_leader(OldGl, self()),
+ group_leader(OldGL, self()),
receive R when R==true; R==false ->
R
after 15000 ->
@@ -2184,7 +2184,9 @@ continue(_MakeErrors, _AbortIfMissingSuites) ->
true
end.
-set_group_leader_same_as_shell() ->
+set_group_leader_same_as_shell(OldGL) ->
+ %% find the group leader process on the node in a dirty fashion
+ %% (check initial function call and look in the process dictionary)
GS2or3 = fun(P) ->
case process_info(P,initial_call) of
{initial_call,{group,server,X}} when X == 2 ; X == 3 ->
@@ -2197,7 +2199,10 @@ set_group_leader_same_as_shell() ->
true == lists:keymember(shell,1,
element(2,process_info(P,dictionary)))] of
[GL|_] ->
- group_leader(GL, self());
+ %% check if started from remote node (skip interaction)
+ if node(OldGL) /= node(GL) -> false;
+ true -> group_leader(GL, self())
+ end;
[] ->
false
end.
@@ -2275,8 +2280,19 @@ do_run_test(Tests, Skip, Opts0) ->
_Lower ->
ok
end,
- test_server_ctrl:multiply_timetraps(Opts0#opts.multiply_timetraps),
- test_server_ctrl:scale_timetraps(Opts0#opts.scale_timetraps),
+
+ case Opts0#opts.multiply_timetraps of
+ undefined -> MultTT = 1;
+ MultTT -> MultTT
+ end,
+ case Opts0#opts.scale_timetraps of
+ undefined -> ScaleTT = false;
+ ScaleTT -> ScaleTT
+ end,
+ ct_logs:log("TEST INFO","Timetrap time multiplier = ~w~n"
+ "Timetrap scaling enabled = ~w", [MultTT,ScaleTT]),
+ test_server_ctrl:multiply_timetraps(MultTT),
+ test_server_ctrl:scale_timetraps(ScaleTT),
test_server_ctrl:create_priv_dir(choose_val(
Opts0#opts.create_priv_dir,
diff --git a/lib/common_test/src/ct_snmp.erl b/lib/common_test/src/ct_snmp.erl
index 2c59b19196..5844909d17 100644
--- a/lib/common_test/src/ct_snmp.erl
+++ b/lib/common_test/src/ct_snmp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
+%% 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.
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index 8fb411ec4f..bff1112ab9 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -1,7 +1,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.
@@ -954,7 +954,7 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
true ->
ok;
false ->
- ct_gen_conn:cont_log(String,Args)
+ ct_gen_conn:cont_log_no_timestamp(String,Args)
end;
ForcePrint == true ->
@@ -965,7 +965,7 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
%% called
ct_gen_conn:log(heading(Action,Name1),String,Args);
false ->
- ct_gen_conn:cont_log(String,Args)
+ ct_gen_conn:cont_log_no_timestamp(String,Args)
end
end
end.
@@ -1224,7 +1224,6 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
EOMod = if TotalTO /= infinity -> EO#eo{total_timeout=trunc(TotalTO)};
true -> EO
end,
-
ExpectFun = case EOMod#eo.seq of
true -> fun() ->
seq_expect(Name,Pid,Data,Pattern,Acc,EOMod)
@@ -1247,38 +1246,34 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
true ->
IdleTO
end,
+ {PatOrPats1,Acc1,Rest1} = case NotFinished of
+ {nomatch,Rest0} ->
+ %% one expect
+ {Pattern,[],Rest0};
+ {continue,Pats0,Acc0,Rest0} ->
+ %% sequence
+ {Pats0,Acc0,Rest0}
+ end,
case timer:tc(ct_gen_conn, do_within_time, [Fun,BreakAfter]) of
- {_,{error,Reason}} ->
+ {_,{error,Reason}} ->
%% A timeout will occur when the telnet connection
%% is idle for EO#eo.idle_timeout milliseconds.
+ if Rest1 /= [] ->
+ log(name_or_pid(Name,Pid)," ~ts",[Rest1]);
+ true ->
+ ok
+ end,
{error,Reason};
{_,{ok,Data1}} when TotalTO == infinity ->
- case NotFinished of
- {nomatch,Rest} ->
- %% One expect
- teln_expect1(Name,Pid,Rest++Data1,
- Pattern,[],EOMod);
- {continue,Patterns1,Acc1,Rest} ->
- %% Sequence
- teln_expect1(Name,Pid,Rest++Data1,
- Patterns1,Acc1,EOMod)
- end;
+ teln_expect1(Name,Pid,Rest1++Data1,PatOrPats1,Acc1,EOMod);
{Elapsed,{ok,Data1}} ->
TVal = TotalTO - (Elapsed/1000),
if TVal =< 0 ->
{error,timeout};
true ->
EO1 = EO#eo{total_timeout = TVal},
- case NotFinished of
- {nomatch,Rest} ->
- %% One expect
- teln_expect1(Name,Pid,Rest++Data1,
- Pattern,[],EO1);
- {continue,Patterns1,Acc1,Rest} ->
- %% Sequence
- teln_expect1(Name,Pid,Rest++Data1,
- Patterns1,Acc1,EO1)
- end
+ teln_expect1(Name,Pid,Rest1++Data1,
+ PatOrPats1,Acc1,EO1)
end
end
end.
@@ -1416,14 +1411,14 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
case one_line(Data,[]) of
{noline,Rest} when FoundPrompt=/=false ->
%% This is the line including the prompt
- case match_line(Name,Pid,Rest,Patterns,FoundPrompt,EO) of
+ case match_line(Name,Pid,Rest,Patterns,FoundPrompt,false,EO) of
nomatch ->
{nomatch,prompt};
{Tag,Match} ->
{Tag,Match,[]}
end;
{noline,Rest} when EO#eo.prompt_check==false ->
- case match_line(Name,Pid,Rest,Patterns,false,EO) of
+ case match_line(Name,Pid,Rest,Patterns,false,false,EO) of
nomatch ->
{nomatch,Rest};
{Tag,Match} ->
@@ -1432,7 +1427,7 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
{noline,Rest} ->
{nomatch,Rest};
{Line,Rest} ->
- case match_line(Name,Pid,Line,Patterns,false,EO) of
+ case match_line(Name,Pid,Line,Patterns,false,true,EO) of
nomatch ->
match_lines(Name,Pid,Rest,Patterns,EO);
{Tag,Match} ->
@@ -1440,45 +1435,50 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
end
end.
-
%% For one line, match each pattern
-match_line(Name,Pid,Line,Patterns,FoundPrompt,EO) ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,match).
+match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO) ->
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO,match).
-match_line(Name,Pid,Line,[prompt|Patterns],false,EO,RetTag) ->
- match_line(Name,Pid,Line,Patterns,false,EO,RetTag);
-match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_EO,RetTag) ->
+match_line(Name,Pid,Line,[prompt|Patterns],false,Term,EO,RetTag) ->
+ match_line(Name,Pid,Line,Patterns,false,Term,EO,RetTag);
+match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_Term,_EO,RetTag) ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]),
{RetTag,{prompt,FoundPrompt}};
-match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_EO,RetTag)
- when PromptType==FoundPrompt ->
+match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_Term,
+ _EO,RetTag) when PromptType==FoundPrompt ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]),
{RetTag,{prompt,FoundPrompt}};
-match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,EO,RetTag)
+match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,Term,
+ EO,RetTag)
when PromptType=/=FoundPrompt ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
-match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,EO,RetTag) ->
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
+match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) ->
case re:run(Line,Pattern,[{capture,all,list}]) of
nomatch ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]),
{RetTag,{Tag,Match}}
end;
-match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,EO,RetTag) ->
+match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,Term,EO,RetTag) ->
case re:run(Line,Pattern,[{capture,all,list}]) of
nomatch ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]),
{RetTag,Match}
end;
-match_line(Name,Pid,Line,[],FoundPrompt,EO,match) ->
- match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,EO,halt);
-match_line(Name,Pid,Line,[],_FoundPrompt,_EO,halt) ->
+match_line(Name,Pid,Line,[],FoundPrompt,Term,EO,match) ->
+ match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,Term,EO,halt);
+%% print any terminated line that can not be matched
+match_line(Name,Pid,Line,[],_FoundPrompt,true,_EO,halt) ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
+ nomatch;
+%% if there's no line termination, Line is saved as Rest (above) and will
+%% be printed later
+match_line(_Name,_Pid,_Line,[],_FoundPrompt,false,_EO,halt) ->
nomatch.
one_line([$\n|Rest],Line) ->
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 991abb0666..180786273d 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -26,7 +26,8 @@
-export([prepare_tests/1, prepare_tests/2,
collect_tests_from_list/2, collect_tests_from_list/3,
- collect_tests_from_file/2, collect_tests_from_file/3]).
+ collect_tests_from_file/2, collect_tests_from_file/3,
+ get_tests/1]).
-export([testspec_rec2list/1, testspec_rec2list/2]).
@@ -803,6 +804,31 @@ list_nodes(#testspec{nodes=NodeRefs}) ->
lists:map(fun({_Ref,Node}) -> Node end, NodeRefs).
+%%%-----------------------------------------------------------------
+%%% Parse the given test specs and return the complete set of specs
+%%% and tests to run/skip.
+%%% [Spec1,Spec2,...] means create separate tests per spec
+%%% [[Spec1,Spec2,...]] means merge all specs into one
+-spec get_tests(Specs) -> {ok,[{Specs,Tests}]} | {error,Reason} when
+ Specs :: [string()] | [[string()]],
+ Tests :: {Node,Run,Skip},
+ Node :: atom(),
+ Run :: {Dir,Suites,Cases},
+ Skip :: {Dir,Suites,Comment} | {Dir,Suites,Cases,Comment},
+ Dir :: string(),
+ Suites :: atom | [atom()] | all,
+ Cases :: atom | [atom()] | all,
+ Comment :: string(),
+ Reason :: term().
+
+get_tests(Specs) ->
+ case collect_tests_from_file(Specs,true) of
+ Tests when is_list(Tests) ->
+ {ok,[{S,prepare_tests(R)} || {S,R} <- Tests]};
+ Error ->
+ Error
+ end.
+
%% -----------------------------------------------------
%% / \
%% | When adding test/config terms, remember to update |
@@ -1132,6 +1158,11 @@ handle_data(verbosity,Node,VLvls,_Spec) when is_list(VLvls) ->
VLvls1 = lists:map(fun(VLvl = {_Cat,_Lvl}) -> VLvl;
(Lvl) -> {'$unspecified',Lvl} end, VLvls),
[{Node,VLvls1}];
+handle_data(multiply_timetraps,Node,Mult,_Spec) when is_integer(Mult) ->
+ [{Node,Mult}];
+handle_data(scale_timetraps,Node,Scale,_Spec) when Scale == true;
+ Scale == false ->
+ [{Node,Scale}];
handle_data(silent_connections,Node,all,_Spec) ->
[{Node,[all]}];
handle_data(silent_connections,Node,Conn,_Spec) when is_atom(Conn) ->
@@ -1150,6 +1181,8 @@ should_be_added(Tag,Node,_Data,Spec) ->
Tag == label; Tag == auto_compile;
Tag == abort_if_missing_suites;
Tag == stylesheet; Tag == verbosity;
+ Tag == multiply_timetraps;
+ Tag == scale_timetraps;
Tag == silent_connections ->
lists:keymember(ref2node(Node,Spec#testspec.nodes),1,
read_field(Spec,Tag)) == false;
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 82a8743cf0..4d3a2ae7e3 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -188,6 +188,8 @@ do_start(Parent, Mode, LogDir, Verbosity) ->
ok
end,
+ ct_default_gl:start_link(group_leader()),
+
{StartTime,TestLogDir} = ct_logs:init(Mode, Verbosity),
ct_event:notify(#event{name=test_start,
@@ -474,6 +476,7 @@ loop(Mode,TestData,StartDir) ->
ct_logs:close(Info, StartDir),
ct_event:stop(),
ct_config:stop(),
+ ct_default_gl:stop(),
ok = file:set_cwd(StartDir),
return(From, Info);
{Ref, _Msg} when is_reference(Ref) ->
@@ -926,7 +929,8 @@ warn_duplicates(Suites) ->
[] ->
ok;
_ ->
- io:format(user,"~nWARNING! Deprecated function: ~w:sequences/0.~n"
+ io:format(?def_gl,
+ "~nWARNING! Deprecated function: ~w:sequences/0.~n"
" Use group with sequence property instead.~n",[Mod])
end
end,
@@ -980,12 +984,12 @@ get_profile_data(Profile, Key, StartDir) ->
end,
case Result of
{error,enoent} when Profile /= default ->
- io:format(user, "~nERROR! Missing profile file ~p~n", [File]),
+ io:format(?def_gl, "~nERROR! Missing profile file ~p~n", [File]),
undefined;
{error,enoent} when Profile == default ->
undefined;
{error,Reason} ->
- io:format(user,"~nERROR! Error in profile file ~p: ~p~n",
+ io:format(?def_gl,"~nERROR! Error in profile file ~p: ~p~n",
[WhichFile,Reason]),
undefined;
{ok,Data} ->
@@ -995,7 +999,7 @@ get_profile_data(Profile, Key, StartDir) ->
_ when is_list(Data) ->
Data;
_ ->
- io:format(user,
+ io:format(?def_gl,
"~nERROR! Invalid profile data in ~p~n",
[WhichFile]),
[]
@@ -1082,10 +1086,10 @@ open_url(iexplore, Args, URL) ->
Path = proplists:get_value(default, Paths),
[Cmd | _] = string:tokens(Path, "%"),
Cmd1 = Cmd ++ " " ++ Args ++ " " ++ URL,
- io:format(user, "~nOpening ~ts with command:~n ~ts~n", [URL,Cmd1]),
+ io:format(?def_gl, "~nOpening ~ts with command:~n ~ts~n", [URL,Cmd1]),
open_port({spawn,Cmd1}, []);
_ ->
- io:format("~nNo path to iexplore.exe~n",[])
+ io:format(?def_gl, "~nNo path to iexplore.exe~n",[])
end,
win32reg:close(R),
ok;
@@ -1095,6 +1099,6 @@ open_url(Prog, Args, URL) ->
is_list(Prog) -> Prog
end,
Cmd = ProgStr ++ " " ++ Args ++ " " ++ URL,
- io:format(user, "~nOpening ~ts with command:~n ~ts~n", [URL,Cmd]),
+ io:format(?def_gl, "~nOpening ~ts with command:~n ~ts~n", [URL,Cmd]),
open_port({spawn,Cmd},[]),
ok.
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index d7efa26863..039c8168ec 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -23,6 +23,7 @@
-define(board_table,ct_boards).
-define(suite_table,ct_suite_data).
-define(verbosity_table,ct_verbosity_table).
+-define(def_gl, ct_default_gl).
-record(conn, {handle,
targetref,
diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl
index 883da0da0a..ef92532969 100644
--- a/lib/common_test/src/cth_conn_log.erl
+++ b/lib/common_test/src/cth_conn_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2017. 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.
@@ -54,8 +54,8 @@
-include_lib("common_test/include/ct.hrl").
-export([init/2,
- pre_init_per_testcase/3,
- post_end_per_testcase/4]).
+ pre_init_per_testcase/4,
+ post_end_per_testcase/5]).
%%----------------------------------------------------------------------
%% Exported types
@@ -104,7 +104,7 @@ get_log_opts(Mod,Opts) ->
Hosts = proplists:get_value(hosts,Opts,[]),
{LogType,Hosts}.
-pre_init_per_testcase(TestCase,Config,CthState) ->
+pre_init_per_testcase(_Suite,TestCase,Config,CthState) ->
Logs =
lists:map(
fun({ConnMod,{LogType,Hosts}}) ->
@@ -158,7 +158,7 @@ pre_init_per_testcase(TestCase,Config,CthState) ->
ct_util:update_testdata(?MODULE, Update, [create]),
{Config,CthState}.
-post_end_per_testcase(TestCase,_Config,Return,CthState) ->
+post_end_per_testcase(_Suite,TestCase,_Config,Return,CthState) ->
Update =
fun(PrevUsers) ->
case lists:delete(TestCase, PrevUsers) of
diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl
index 6d77d7ee9e..1bc9b10d41 100644
--- a/lib/common_test/src/cth_log_redirect.erl
+++ b/lib/common_test/src/cth_log_redirect.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. 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.
@@ -28,10 +28,10 @@
%% CTH Callbacks
-export([id/1, init/2,
pre_init_per_suite/3, pre_end_per_suite/3, post_end_per_suite/4,
- pre_init_per_group/3, post_init_per_group/4,
- pre_end_per_group/3, post_end_per_group/4,
- pre_init_per_testcase/3, post_init_per_testcase/4,
- pre_end_per_testcase/3, post_end_per_testcase/4]).
+ pre_init_per_group/4, post_init_per_group/5,
+ pre_end_per_group/4, post_end_per_group/5,
+ pre_init_per_testcase/4, post_init_per_testcase/5,
+ pre_end_per_testcase/4, post_end_per_testcase/5]).
%% Event handler Callbacks
-export([init/1,
@@ -71,11 +71,11 @@ post_end_per_suite(_Suite, Config, Return, State) ->
set_curr_func(undefined, Config),
{Return, State}.
-pre_init_per_group(Group, Config, State) ->
+pre_init_per_group(_Suite, Group, Config, State) ->
set_curr_func({group,Group,init_per_group}, Config),
{Config, State}.
-post_init_per_group(Group, Config, Result, tc_log_async) when is_list(Config) ->
+post_init_per_group(_Suite, Group, Config, Result, tc_log_async) when is_list(Config) ->
case lists:member(parallel,proplists:get_value(
tc_group_properties,Config,[])) of
true ->
@@ -83,33 +83,33 @@ post_init_per_group(Group, Config, Result, tc_log_async) when is_list(Config) ->
false ->
{Result, tc_log_async}
end;
-post_init_per_group(_Group, _Config, Result, State) ->
+post_init_per_group(_Suite, _Group, _Config, Result, State) ->
{Result, State}.
-pre_init_per_testcase(TC, Config, State) ->
+pre_init_per_testcase(_Suite, TC, Config, State) ->
set_curr_func(TC, Config),
{Config, State}.
-post_init_per_testcase(_TC, _Config, Return, State) ->
+post_init_per_testcase(_Suite, _TC, _Config, Return, State) ->
{Return, State}.
-pre_end_per_testcase(_TC, Config, State) ->
+pre_end_per_testcase(_Suite, _TC, Config, State) ->
{Config, State}.
-post_end_per_testcase(_TC, _Config, Result, State) ->
+post_end_per_testcase(_Suite, _TC, _Config, Result, State) ->
%% Make sure that the event queue is flushed
%% before ending this test case.
gen_event:call(error_logger, ?MODULE, flush, 300000),
{Result, State}.
-pre_end_per_group(Group, Config, {tc_log, Group}) ->
+pre_end_per_group(_Suite, Group, Config, {tc_log, Group}) ->
set_curr_func({group,Group,end_per_group}, Config),
{Config, set_log_func(tc_log_async)};
-pre_end_per_group(Group, Config, State) ->
+pre_end_per_group(_Suite, Group, Config, State) ->
set_curr_func({group,Group,end_per_group}, Config),
{Config, State}.
-post_end_per_group(_Group, Config, Return, State) ->
+post_end_per_group(_Suite, _Group, Config, Return, State) ->
set_curr_func({group,undefined}, Config),
{Return, State}.
diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl
index 59b916851e..0bbb217275 100644
--- a/lib/common_test/src/cth_surefire.erl
+++ b/lib/common_test/src/cth_surefire.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2017. 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.
@@ -33,16 +33,16 @@
-export([pre_end_per_suite/3]).
-export([post_end_per_suite/4]).
--export([pre_init_per_group/3]).
--export([post_init_per_group/4]).
--export([pre_end_per_group/3]).
--export([post_end_per_group/4]).
+-export([pre_init_per_group/4]).
+-export([post_init_per_group/5]).
+-export([pre_end_per_group/4]).
+-export([post_end_per_group/5]).
--export([pre_init_per_testcase/3]).
--export([post_end_per_testcase/4]).
+-export([pre_init_per_testcase/4]).
+-export([post_end_per_testcase/5]).
--export([on_tc_fail/3]).
--export([on_tc_skip/3]).
+-export([on_tc_fail/4]).
+-export([on_tc_skip/4]).
-export([terminate/1]).
@@ -116,29 +116,29 @@ pre_end_per_suite(_Suite,Config,State) ->
post_end_per_suite(_Suite,Config,Result,State) ->
{Result, end_tc(end_per_suite,Config,Result,State)}.
-pre_init_per_group(Group,Config,State) ->
+pre_init_per_group(_Suite,Group,Config,State) ->
{Config, init_tc(State#state{ curr_group = [Group|State#state.curr_group]},
Config)}.
-post_init_per_group(_Group,Config,Result,State) ->
+post_init_per_group(_Suite,_Group,Config,Result,State) ->
{Result, end_tc(init_per_group,Config,Result,State)}.
-pre_end_per_group(_Group,Config,State) ->
+pre_end_per_group(_Suite,_Group,Config,State) ->
{Config, init_tc(State, Config)}.
-post_end_per_group(_Group,Config,Result,State) ->
+post_end_per_group(_Suite,_Group,Config,Result,State) ->
NewState = end_tc(end_per_group, Config, Result, State),
{Result, NewState#state{ curr_group = tl(NewState#state.curr_group)}}.
-pre_init_per_testcase(_TC,Config,State) ->
+pre_init_per_testcase(_Suite,_TC,Config,State) ->
{Config, init_tc(State, Config)}.
-post_end_per_testcase(TC,Config,Result,State) ->
+post_end_per_testcase(_Suite,TC,Config,Result,State) ->
{Result, end_tc(TC,Config, Result,State)}.
-on_tc_fail(_TC, _Res, State = #state{test_cases = []}) ->
+on_tc_fail(_Suite,_TC, _Res, State = #state{test_cases = []}) ->
State;
-on_tc_fail(_TC, Res, State) ->
+on_tc_fail(_Suite,_TC, Res, State) ->
TCs = State#state.test_cases,
TC = hd(TCs),
NewTC = TC#testcase{
@@ -146,10 +146,9 @@ on_tc_fail(_TC, Res, State) ->
{fail,lists:flatten(io_lib:format("~p",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
-on_tc_skip({ConfigFunc,_GrName},{Type,_Reason} = Res, State0)
- when Type == tc_auto_skip; Type == tc_user_skip ->
- on_tc_skip(ConfigFunc, Res, State0);
-on_tc_skip(Tc,{Type,_Reason} = Res, State0) when Type == tc_auto_skip ->
+on_tc_skip(Suite,{ConfigFunc,_GrName}, Res, State) ->
+ on_tc_skip(Suite,ConfigFunc, Res, State);
+on_tc_skip(Suite,Tc, Res, State0) ->
TcStr = atom_to_list(Tc),
State =
case State0#state.test_cases of
@@ -158,11 +157,7 @@ on_tc_skip(Tc,{Type,_Reason} = Res, State0) when Type == tc_auto_skip ->
_ ->
State0
end,
- do_tc_skip(Res, end_tc(Tc,[],Res,init_tc(State,[])));
-on_tc_skip(_Tc, _Res, State = #state{test_cases = []}) ->
- State;
-on_tc_skip(_Tc, Res, State) ->
- do_tc_skip(Res, State).
+ do_tc_skip(Res, end_tc(Tc,[],Res,init_tc(set_suite(Suite,State),[]))).
do_tc_skip(Res, State) ->
TCs = State#state.test_cases,
@@ -209,6 +204,12 @@ end_tc(Name, _Config, _Res, State = #state{ curr_suite = Suite,
result = passed }|
State#state.test_cases],
tc_log = ""}. % so old tc_log is not set if next is on_tc_skip
+
+set_suite(Suite,#state{curr_suite=undefined}=State) ->
+ State#state{curr_suite=Suite, curr_suite_ts=?now};
+set_suite(_,State) ->
+ State.
+
close_suite(#state{ test_cases = [] } = State) ->
State;
close_suite(#state{ test_cases = TCs, url_base = UrlBase } = State) ->
@@ -228,7 +229,8 @@ close_suite(#state{ test_cases = TCs, url_base = UrlBase } = State) ->
testcases = lists:reverse(TCs),
log = SuiteLog,
url = SuiteUrl},
- State#state{ test_cases = [],
+ State#state{ curr_suite = undefined,
+ test_cases = [],
test_suites = [Suite | State#state.test_suites]}.
terminate(State = #state{ test_cases = [] }) ->
diff --git a/lib/common_test/src/erl2html2.erl b/lib/common_test/src/erl2html2.erl
index e819f345de..7f3eb699de 100644
--- a/lib/common_test/src/erl2html2.erl
+++ b/lib/common_test/src/erl2html2.erl
@@ -130,6 +130,8 @@ parse_preprocessed_file(Epp, File, InCorrectFile) ->
throw({error,Reason,InCorrectFile});
{error,_Reason} ->
parse_preprocessed_file(Epp, File, InCorrectFile);
+ {warning,_} ->
+ parse_preprocessed_file(Epp, File, InCorrectFile);
{eof,_Location} ->
[]
end.
diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl
index 924086f2bd..857ff27258 100644
--- a/lib/common_test/src/test_server.erl
+++ b/lib/common_test/src/test_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -778,9 +778,9 @@ spawn_fw_call(Mod,IPTC={init_per_testcase,Func},CurrConf,Pid,
%% if init_per_testcase fails, the test case
%% should be skipped
try begin do_end_tc_call(Mod,IPTC, {Pid,Skip,[CurrConf]}, Why),
- do_init_tc_call(Mod,{end_per_testcase,Func},
+ do_init_tc_call(Mod,{end_per_testcase_not_run,Func},
[CurrConf],{ok,[CurrConf]}),
- do_end_tc_call(Mod,{end_per_testcase,Func},
+ do_end_tc_call(Mod,{end_per_testcase_not_run,Func},
{Pid,Skip,[CurrConf]}, Why) end of
_ -> ok
catch
@@ -1151,14 +1151,14 @@ do_end_tc_call(Mod, IPTC={init_per_testcase,Func}, Res, Return) ->
Args
end,
EPTCInitRes =
- case do_init_tc_call(Mod,{end_per_testcase,Func},
+ case do_init_tc_call(Mod,{end_per_testcase_not_run,Func},
IPTCEndRes,Return) of
{ok,EPTCInitConfig} when is_list(EPTCInitConfig) ->
{Return,EPTCInitConfig};
_ ->
- Return
+ {Return,IPTCEndRes}
end,
- do_end_tc_call1(Mod, {end_per_testcase,Func},
+ do_end_tc_call1(Mod, {end_per_testcase_not_run,Func},
EPTCInitRes, Return);
_Ok ->
do_end_tc_call1(Mod, IPTC, Res, Return)
diff --git a/lib/common_test/src/test_server_ctrl.erl b/lib/common_test/src/test_server_ctrl.erl
index b52e4bef9b..064e375cd5 100644
--- a/lib/common_test/src/test_server_ctrl.erl
+++ b/lib/common_test/src/test_server_ctrl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2017. 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.
@@ -2051,17 +2051,21 @@ add_init_and_end_per_suite([], _LastMod, skipped_suite, _FwMod) ->
add_init_and_end_per_suite([], LastMod, LastRef, FwMod) ->
%% we'll add end_per_suite here even if it's not exported
%% (and simply let the call fail if it's missing)
- case erlang:function_exported(LastMod, end_per_suite, 1) of
- true ->
- [{conf,LastRef,[],{LastMod,end_per_suite}}];
- false ->
+ case {erlang:function_exported(LastMod, end_per_suite, 1),
+ erlang:function_exported(LastMod, init_per_suite, 1)} of
+ {false,false} ->
%% let's call a "fake" end_per_suite if it exists
case erlang:function_exported(FwMod, end_per_suite, 1) of
true ->
[{conf,LastRef,[{suite,LastMod}],{FwMod,end_per_suite}}];
false ->
[{conf,LastRef,[],{LastMod,end_per_suite}}]
- end
+ end;
+ _ ->
+ %% If any of these exist, the other should too
+ %% (required and documented). If it isn't, it will fail
+ %% with reason 'undef'.
+ [{conf,LastRef,[],{LastMod,end_per_suite}}]
end.
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod) ->
@@ -2070,11 +2074,9 @@ do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod) ->
_ -> ok
end,
{Init,NextMod,NextRef} =
- case erlang:function_exported(Mod, init_per_suite, 1) of
- true ->
- Ref = make_ref(),
- {[{conf,Ref,[],{Mod,init_per_suite}}],Mod,Ref};
- false ->
+ case {erlang:function_exported(Mod, init_per_suite, 1),
+ erlang:function_exported(Mod, end_per_suite, 1)} of
+ {false,false} ->
%% let's call a "fake" init_per_suite if it exists
case erlang:function_exported(FwMod, init_per_suite, 1) of
true ->
@@ -2083,8 +2085,13 @@ do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod) ->
{FwMod,init_per_suite}}],Mod,Ref};
false ->
{[],Mod,undefined}
- end
-
+ end;
+ _ ->
+ %% If any of these exist, the other should too
+ %% (required and documented). If it isn't, it will fail
+ %% with reason 'undef'.
+ Ref = make_ref(),
+ {[{conf,Ref,[],{Mod,init_per_suite}}],Mod,Ref}
end,
Cases =
if LastRef==undefined ->
@@ -2094,10 +2101,9 @@ do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod) ->
true ->
%% we'll add end_per_suite here even if it's not exported
%% (and simply let the call fail if it's missing)
- case erlang:function_exported(LastMod, end_per_suite, 1) of
- true ->
- [{conf,LastRef,[],{LastMod,end_per_suite}}|Init];
- false ->
+ case {erlang:function_exported(LastMod, end_per_suite, 1),
+ erlang:function_exported(LastMod, init_per_suite, 1)} of
+ {false,false} ->
%% let's call a "fake" end_per_suite if it exists
case erlang:function_exported(FwMod, end_per_suite, 1) of
true ->
@@ -2105,8 +2111,13 @@ do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod) ->
{FwMod,end_per_suite}}|Init];
false ->
[{conf,LastRef,[],{LastMod,end_per_suite}}|Init]
- end
- end
+ end;
+ _ ->
+ %% If any of these exist, the other should too
+ %% (required and documented). If it isn't, it will fail
+ %% with reason 'undef'.
+ [{conf,LastRef,[],{LastMod,end_per_suite}}|Init]
+ end
end,
{Cases,NextMod,NextRef}.
@@ -2115,11 +2126,9 @@ do_add_end_per_suite_and_skip(LastMod, LastRef, Mod, FwMod) ->
No when No==undefined ; No==skipped_suite ->
{[],Mod,skipped_suite};
_Ref ->
- case erlang:function_exported(LastMod, end_per_suite, 1) of
- true ->
- {[{conf,LastRef,[],{LastMod,end_per_suite}}],
- Mod,skipped_suite};
- false ->
+ case {erlang:function_exported(LastMod, end_per_suite, 1),
+ erlang:function_exported(LastMod, init_per_suite, 1)} of
+ {false,false} ->
case erlang:function_exported(FwMod, end_per_suite, 1) of
true ->
%% let's call "fake" end_per_suite if it exists
@@ -2128,7 +2137,13 @@ do_add_end_per_suite_and_skip(LastMod, LastRef, Mod, FwMod) ->
false ->
{[{conf,LastRef,[],{LastMod,end_per_suite}}],
Mod,skipped_suite}
- end
+ end;
+ _ ->
+ %% If any of these exist, the other should too
+ %% (required and documented). If it isn't, it will fail
+ %% with reason 'undef'.
+ {[{conf,LastRef,[],{LastMod,end_per_suite}}],
+ Mod,skipped_suite}
end
end.
@@ -2924,22 +2939,21 @@ run_test_cases_loop([{Mod,Func,Args}|Cases], Config, TimetrapData, Mode, Status)
exit(framework_error);
%% sequential execution of test case finished
{Time,RetVal,_} ->
+ RetTag =
+ if is_tuple(RetVal) -> element(1,RetVal);
+ true -> undefined
+ end,
{Failed,Status1} =
- case Time of
- died ->
- {true,update_status(failed, Mod, Func, Status)};
- _ when is_tuple(RetVal) ->
- case element(1, RetVal) of
- R when R=='EXIT'; R==failed ->
- {true,update_status(failed, Mod, Func, Status)};
- R when R==skip; R==skipped ->
- {false,update_status(skipped, Mod, Func, Status)};
- _ ->
- {false,update_status(ok, Mod, Func, Status)}
- end;
- _ ->
- {false,update_status(ok, Mod, Func, Status)}
- end,
+ case RetTag of
+ Skip when Skip==skip; Skip==skipped ->
+ {false,update_status(skipped, Mod, Func, Status)};
+ Fail when Fail=='EXIT'; Fail==failed ->
+ {true,update_status(failed, Mod, Func, Status)};
+ _ when Time==died, RetVal=/=ok ->
+ {true,update_status(failed, Mod, Func, Status)};
+ _ ->
+ {false,update_status(ok, Mod, Func, Status)}
+ end,
case check_prop(sequence, Mode) of
false ->
stop_minor_log_file(),
@@ -3794,7 +3808,15 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
{died,{timetrap_timeout,TimetrapTimeout}} ->
progress(failed, Num, Mod, Func, GrName, Loc,
timetrap_timeout, TimetrapTimeout, Comment, Style);
- {died,Reason} ->
+ {died,{Skip,Reason}} when Skip==skip; Skip==skipped ->
+ %% died in init_per_testcase
+ progress(skip, Num, Mod, Func, GrName, Loc, Reason,
+ Time, Comment, Style);
+ {died,Reason} when Reason=/=ok ->
+ %% (If Reason==ok it means that process died in
+ %% end_per_testcase after successfully completing the
+ %% test case itself - then we shall not fail, but a
+ %% warning will be issued in the comment field.)
progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',{Skip,Reason}}} when Skip==skip; Skip==skipped;
@@ -3943,6 +3965,9 @@ progress(skip, CaseNum, Mod, Func, GrName, Loc, Reason, Time,
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},
{ReportTag,Reason1}}]),
+ TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
+ true -> "~w"
+ end, [Time]),
ReasonStr = escape_chars(reason_to_string(Reason1)),
ReasonStr1 = lists:flatten([string:strip(S,left) ||
S <- string:tokens(ReasonStr,[$\n])]),
@@ -3957,10 +3982,10 @@ progress(skip, CaseNum, Mod, Func, GrName, Loc, Reason, Time,
_ -> xhtml("<br>(","<br />(") ++ to_string(Comment) ++ ")"
end,
print(html,
- "<td>" ++ St0 ++ "~.3fs" ++ St1 ++ "</td>"
+ "<td>" ++ St0 ++ "~ts" ++ St1 ++ "</td>"
"<td><font color=\"~ts\">SKIPPED</font></td>"
"<td>~ts~ts</td></tr>\n",
- [Time,Color,ReasonStr2,Comment1]),
+ [TimeStr,Color,ReasonStr2,Comment1]),
FormatLoc = test_server_sup:format_loc(Loc),
print(minor, "=== Location: ~ts", [FormatLoc]),
print(minor, "=== Reason: ~ts", [ReasonStr1]),
@@ -4098,6 +4123,9 @@ progress(ok, _CaseNum, Mod, Func, GrName, _Loc, RetVal, Time,
Comment0, {St0,St1}) ->
print(minor, "successfully completed test case", []),
test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},ok}]),
+ TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
+ true -> "~w"
+ end, [Time]),
Comment =
case RetVal of
{comment,RetComment} ->
@@ -4116,10 +4144,10 @@ progress(ok, _CaseNum, Mod, Func, GrName, _Loc, RetVal, Time,
end,
print(major, "=elapsed ~p", [Time]),
print(html,
- "<td>" ++ St0 ++ "~.3fs" ++ St1 ++ "</td>"
+ "<td>" ++ St0 ++ "~ts" ++ St1 ++ "</td>"
"<td><font color=\"green\">Ok</font></td>"
"~ts</tr>\n",
- [Time,Comment]),
+ [TimeStr,Comment]),
print(minor,
escape_chars(io_lib:format("=== Returned value: ~tp", [RetVal])),
[]),
diff --git a/lib/common_test/src/test_server_gl.erl b/lib/common_test/src/test_server_gl.erl
index 7d6fe64b92..4845b86dd3 100644
--- a/lib/common_test/src/test_server_gl.erl
+++ b/lib/common_test/src/test_server_gl.erl
@@ -24,7 +24,7 @@
%% through the test_server_io module/process.
-module(test_server_gl).
--export([start_link/0,stop/1,set_minor_fd/3,unset_minor_fd/1,
+-export([start_link/1,stop/1,set_minor_fd/3,unset_minor_fd/1,
get_tc_supervisor/1,print/4,set_props/2]).
-export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
@@ -33,6 +33,7 @@
tc :: mfa() | 'undefined', %Current test case MFA
minor :: 'none'|pid(), %Minor fd
minor_monitor, %Monitor ref for minor fd
+ tsio_monitor, %Monitor red for controlling proc
capture :: 'none'|pid(), %Capture output
reject_io :: boolean(), %Reject I/O requests...
permit_io, %... and exceptions
@@ -45,8 +46,8 @@
%% Start a new group leader process. Only to be called by
%% the test_server_io process.
-start_link() ->
- case gen_server:start_link(?MODULE, [], []) of
+start_link(TSIO) ->
+ case gen_server:start_link(?MODULE, [TSIO], []) of
{ok,Pid} ->
{ok,Pid};
Other ->
@@ -130,14 +131,16 @@ set_props(GL, PropList) ->
%%% Internal functions.
-init([]) ->
+init([TSIO]) ->
EscChars = case application:get_env(test_server, esc_chars) of
{ok,ECBool} -> ECBool;
_ -> true
end,
+ Ref = erlang:monitor(process, TSIO),
{ok,#st{tc_supervisor=none,
minor=none,
minor_monitor=none,
+ tsio_monitor=Ref,
capture=none,
reject_io=false,
permit_io=gb_sets:empty(),
@@ -176,6 +179,9 @@ handle_info({'DOWN',Ref,process,_,Reason}=D, #st{minor_monitor=Ref}=St) ->
test_server_io:print_unexpected(Data)
end,
{noreply,St#st{minor=none,minor_monitor=none}};
+handle_info({'DOWN',Ref,process,_,_}, #st{tsio_monitor=Ref}=St) ->
+ %% controlling process (test_server_io) terminated, we're done
+ {stop,normal,St};
handle_info({permit_io,Pid}, #st{permit_io=P}=St) ->
{noreply,St#st{permit_io=gb_sets:add(Pid, P)}};
handle_info({capture,Cap0}, St) ->
diff --git a/lib/common_test/src/test_server_io.erl b/lib/common_test/src/test_server_io.erl
index 3d5238052b..fdabf17b08 100644
--- a/lib/common_test/src/test_server_io.erl
+++ b/lib/common_test/src/test_server_io.erl
@@ -185,7 +185,7 @@ reset_state() ->
init([]) ->
process_flag(trap_exit, true),
Empty = gb_trees:empty(),
- {ok,Shared} = test_server_gl:start_link(),
+ {ok,Shared} = test_server_gl:start_link(self()),
{ok,#st{fds=Empty,shared_gl=Shared,gls=gb_sets:empty(),
io_buffering=gb_sets:empty(),
buffered=Empty,
@@ -200,7 +200,7 @@ req(Req) ->
gen_server:call(?MODULE, Req, infinity).
handle_call({get_gl,false}, _From, #st{gls=Gls,gl_props=Props}=St) ->
- {ok,Pid} = test_server_gl:start_link(),
+ {ok,Pid} = test_server_gl:start_link(self()),
test_server_gl:set_props(Pid, Props),
{reply,Pid,St#st{gls=gb_sets:insert(Pid, Gls)}};
handle_call({get_gl,true}, _From, #st{shared_gl=Shared}=St) ->
@@ -285,7 +285,7 @@ handle_call(reset_state, _From, #st{fds=Fds,tags=Tags,gls=Gls,
ok
end,
Empty = gb_trees:empty(),
- {ok,Shared} = test_server_gl:start_link(),
+ {ok,Shared} = test_server_gl:start_link(self()),
{reply,ok,#st{fds=Empty,shared_gl=Shared,gls=gb_sets:empty(),
io_buffering=gb_sets:empty(),
buffered=Empty,
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index b1eddfedd7..2f0fc2e05a 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -70,7 +70,8 @@ MODULES= \
test_server_SUITE \
test_server_test_lib \
ct_release_test_SUITE \
- ct_log_SUITE
+ ct_log_SUITE \
+ ct_SUITE
ERL_FILES= $(MODULES:%=%.erl)
HRL_FILES= test_server_test_lib.hrl
diff --git a/lib/common_test/test/ct_SUITE.erl b/lib/common_test/test/ct_SUITE.erl
new file mode 100644
index 0000000000..eb98c2544f
--- /dev/null
+++ b/lib/common_test/test/ct_SUITE.erl
@@ -0,0 +1,53 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-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(ct_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{timetrap,{seconds,30}}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [app_file, appup_file].
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+
+app_file(_Config) ->
+ ok = test_server:app_test(common_test),
+ ok.
+
+appup_file(_Config) ->
+ ok = test_server:appup_test(common_test).
+
diff --git a/lib/common_test/test/ct_auto_compile_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE.erl
index e6939d23c0..dface99b8f 100644
--- a/lib/common_test/test/ct_auto_compile_SUITE.erl
+++ b/lib/common_test/test/ct_auto_compile_SUITE.erl
@@ -27,6 +27,7 @@
%%% The suites used for the test are located in the data directory.
%%%-------------------------------------------------------------------
-module(ct_auto_compile_SUITE).
+-warning("Ignore me -- testing that the debugger can handle warnings").
-compile(export_all).
diff --git a/lib/common_test/test/ct_config_SUITE.erl b/lib/common_test/test/ct_config_SUITE.erl
index 9879e0f20d..cbbfe408a8 100644
--- a/lib/common_test/test/ct_config_SUITE.erl
+++ b/lib/common_test/test/ct_config_SUITE.erl
@@ -113,10 +113,14 @@ userconfig_static(Config) when is_list(Config) ->
["config_static_SUITE"]).
userconfig_dynamic(Config) when is_list(Config) ->
- run_test(config_dynamic_SUITE,
- Config,
- {userconfig, {config_driver, "config_server"}},
- ["config_dynamic_SUITE"]).
+ case skip_dynamic() of
+ true -> {skip,"TimeWarpingOS"};
+ false ->
+ run_test(config_dynamic_SUITE,
+ Config,
+ {userconfig, {config_driver, "config_server"}},
+ ["config_dynamic_SUITE"])
+ end.
testspec_legacy(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
@@ -147,16 +151,20 @@ testspec_static(Config) when is_list(Config) ->
file:delete(filename:join(ConfigDir, "spec_static.spec")).
testspec_dynamic(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
- ConfigDir = ?config(config_dir, Config),
- make_spec(DataDir, ConfigDir, "spec_dynamic.spec",
- [config_dynamic_SUITE],
- [{userconfig, {config_driver, "config_server"}}]),
- run_test(config_dynamic_SUITE,
- Config,
- {spec, filename:join(ConfigDir, "spec_dynamic.spec")},
- []),
- file:delete(filename:join(ConfigDir, "spec_dynamic.spec")).
+ case skip_dynamic() of
+ true -> {skip,"TimeWarpingOS"};
+ false ->
+ DataDir = ?config(data_dir, Config),
+ ConfigDir = ?config(config_dir, Config),
+ make_spec(DataDir, ConfigDir, "spec_dynamic.spec",
+ [config_dynamic_SUITE],
+ [{userconfig, {config_driver, "config_server"}}]),
+ run_test(config_dynamic_SUITE,
+ Config,
+ {spec, filename:join(ConfigDir, "spec_dynamic.spec")},
+ []),
+ file:delete(filename:join(ConfigDir, "spec_dynamic.spec"))
+ end.
@@ -198,6 +206,23 @@ setup_env(Test, Config, CTConfig) ->
reformat_events(Events, EH) ->
ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% Test related to 'localtime' will often fail if the test host is
+%%% time warping, so let's just skip the 'dynamic' tests then.
+skip_dynamic() ->
+ case os:getenv("TS_EXTRA_PLATFORM_LABEL") of
+ TSExtraPlatformLabel when is_list(TSExtraPlatformLabel) ->
+ case string:str(TSExtraPlatformLabel,"TimeWarpingOS") of
+ 0 -> false;
+ _ -> true
+ end;
+ _ ->
+ false
+ end.
+
+
+
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index fae23484e6..7468ebe9d9 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2017. 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.
@@ -1531,17 +1531,17 @@ test_events(config_func_errors) ->
{?eh,tc_start,{config_func_error_1_SUITE,exit_in_iptc}},
{?eh,tc_done,{config_func_error_1_SUITE,exit_in_iptc,'_'}},
- {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,test_stats,{0,0,{0,1}}},
{?eh,tc_start,{config_func_error_1_SUITE,exit_in_eptc}},
{?eh,tc_done,{config_func_error_1_SUITE,exit_in_eptc,'_'}},
- {?eh,test_stats,{0,2,{0,0}}},
+ {?eh,test_stats,{1,0,{0,1}}},
[{?eh,tc_start,{config_func_error_1_SUITE,{init_per_group,g1,[]}}},
{?eh,tc_done,{config_func_error_1_SUITE,{init_per_group,g1,[]},ok}},
{?eh,tc_start,{config_func_error_1_SUITE,exit_in_iptc}},
{?eh,tc_done,{config_func_error_1_SUITE,exit_in_iptc,'_'}},
- {?eh,test_stats,{0,3,{0,0}}},
+ {?eh,test_stats,{1,0,{0,2}}},
{?eh,tc_start,{config_func_error_1_SUITE,{end_per_group,g1,[]}}},
{?eh,tc_done,{config_func_error_1_SUITE,{end_per_group,g1,[]},ok}}],
@@ -1549,7 +1549,7 @@ test_events(config_func_errors) ->
{?eh,tc_done,{config_func_error_1_SUITE,{init_per_group,g2,[]},ok}},
{?eh,tc_start,{config_func_error_1_SUITE,exit_in_eptc}},
{?eh,tc_done,{config_func_error_1_SUITE,exit_in_eptc,'_'}},
- {?eh,test_stats,{0,4,{0,0}}},
+ {?eh,test_stats,{2,0,{0,2}}},
{?eh,tc_start,{config_func_error_1_SUITE,{end_per_group,g2,[]}}},
{?eh,tc_done,{config_func_error_1_SUITE,{end_per_group,g2,[]},ok}}],
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
index 690d0af1bb..8ba14e63bc 100644
--- a/lib/common_test/test/ct_hooks_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2017. 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.
@@ -70,22 +70,25 @@ suite() ->
all() ->
all(suite).
-all(suite) ->
+all(suite) ->
lists:reverse(
[
one_cth, two_cth, faulty_cth_no_init, faulty_cth_id_no_init,
faulty_cth_exit_in_init, faulty_cth_exit_in_id,
- faulty_cth_exit_in_init_scope_suite, minimal_cth,
- minimal_and_maximal_cth, faulty_cth_undef,
+ faulty_cth_exit_in_init_scope_suite, minimal_cth,
+ minimal_and_maximal_cth, faulty_cth_undef,
scope_per_suite_cth, scope_per_group_cth, scope_suite_cth,
- scope_per_suite_state_cth, scope_per_group_state_cth,
+ scope_per_suite_state_cth, scope_per_group_state_cth,
scope_suite_state_cth,
fail_pre_suite_cth, double_fail_pre_suite_cth,
fail_post_suite_cth, skip_pre_suite_cth, skip_pre_end_cth,
+ skip_pre_init_tc_cth,
skip_post_suite_cth, recover_post_suite_cth, update_config_cth,
- state_update_cth, options_cth, same_id_cth,
+ state_update_cth, options_cth, same_id_cth,
fail_n_skip_with_minimal_cth, prio_cth, no_config,
- data_dir, cth_log
+ no_init_suite_config, no_init_config, no_end_config,
+ failed_sequence, repeat_force_stop, config_clash,
+ callbacks_on_skip, fallback, data_dir, cth_log
]
).
@@ -96,10 +99,10 @@ all(suite) ->
%%%-----------------------------------------------------------------
%%%
-one_cth(Config) when is_list(Config) ->
+one_cth(Config) when is_list(Config) ->
do_test(one_empty_cth, "ct_cth_empty_SUITE.erl",[empty_cth], Config).
-two_cth(Config) when is_list(Config) ->
+two_cth(Config) when is_list(Config) ->
do_test(two_empty_cth, "ct_cth_empty_SUITE.erl",[empty_cth,empty_cth],
Config).
@@ -119,13 +122,13 @@ minimal_cth(Config) when is_list(Config) ->
minimal_and_maximal_cth(Config) when is_list(Config) ->
do_test(minimal_and_maximal_cth, "ct_cth_empty_SUITE.erl",
[minimal_cth, empty_cth],Config).
-
+
faulty_cth_undef(Config) when is_list(Config) ->
do_test(faulty_cth_undef, "ct_cth_empty_SUITE.erl",
[undef_cth],Config).
faulty_cth_exit_in_init_scope_suite(Config) when is_list(Config) ->
- do_test(faulty_cth_exit_in_init_scope_suite,
+ do_test(faulty_cth_exit_in_init_scope_suite,
"ct_exit_in_init_scope_suite_cth_SUITE.erl",
[],Config).
@@ -190,6 +193,10 @@ skip_post_suite_cth(Config) when is_list(Config) ->
do_test(skip_post_suite_cth, "ct_cth_empty_SUITE.erl",
[skip_post_suite_cth],Config).
+skip_pre_init_tc_cth(Config) ->
+ do_test(skip_pre_init_tc_cth, "ct_cth_empty_SUITE.erl",
+ [skip_pre_init_tc_cth],Config).
+
recover_post_suite_cth(Config) when is_list(Config) ->
do_test(recover_post_suite_cth, "ct_cth_fail_per_suite_SUITE.erl",
[recover_post_suite_cth],Config).
@@ -205,7 +212,7 @@ state_update_cth(Config) when is_list(Config) ->
options_cth(Config) when is_list(Config) ->
do_test(options_cth, "ct_cth_empty_SUITE.erl",
[{empty_cth,[test]}],Config).
-
+
same_id_cth(Config) when is_list(Config) ->
do_test(same_id_cth, "ct_cth_empty_SUITE.erl",
[same_id_cth,same_id_cth],Config).
@@ -223,13 +230,24 @@ no_config(Config) when is_list(Config) ->
do_test(no_config, "ct_no_config_SUITE.erl",
[verify_config_cth],Config).
+no_init_suite_config(Config) when is_list(Config) ->
+ do_test(no_init_suite_config, "ct_no_init_suite_config_SUITE.erl",
+ [empty_cth],Config).
+
+no_init_config(Config) when is_list(Config) ->
+ do_test(no_init_config, "ct_no_init_config_SUITE.erl",[empty_cth],Config).
+
+no_end_config(Config) when is_list(Config) ->
+ do_test(no_end_config, "ct_no_end_config_SUITE.erl",[empty_cth],Config).
+
data_dir(Config) when is_list(Config) ->
do_test(data_dir, "ct_data_dir_SUITE.erl",
[verify_data_dir_cth],Config).
-cth_log(Config) when is_list(Config) ->
+cth_log(Config) when is_list(Config) ->
%% test that cth_log_redirect writes properly to
%% unexpected I/O log
+ ct:timetrap({minutes,10}),
StartOpts = do_test(cth_log, "cth_log_SUITE.erl", [], Config),
Logdir = proplists:get_value(logdir, StartOpts),
UnexpIoLogs =
@@ -253,29 +271,57 @@ cth_log(Config) when is_list(Config) ->
end, UnexpIoLogs),
ok.
+%% OTP-10599 adds the Suite argument as first argument to all hook
+%% callbacks that did not have a Suite argument from before. This test
+%% checks that ct_hooks will fall back to old versions of callbacks if
+%% new versions are not exported.
+fallback(Config) ->
+ do_test(fallback, "all_hook_callbacks_SUITE.erl",[fallback_cth], Config).
+
+%% Test that expected callbacks, and only those, are called when tests
+%% are skipped in different ways
+callbacks_on_skip(Config) ->
+ do_test(callbacks_on_skip, {spec,"skip.spec"},[skip_cth], Config).
+
+%% Test that expected callbacks, and only those, are called when tests
+%% are skipped due to failed sequence
+failed_sequence(Config) ->
+ do_test(failed_sequence, "seq_SUITE.erl", [skip_cth], Config).
+
+%% Test that expected callbacks, and only those, are called when tests
+%% are skipped due to {force_stop,skip_rest} option
+repeat_force_stop(Config) ->
+ do_test(repeat_force_stop, "repeat_SUITE.erl", [skip_cth], Config, ok, 2,
+ [{force_stop,skip_rest},{duration,"000009"}]).
+
+%% Test that expected callbacks, and only those, are called when a test
+%% are fails due to clash in config alias names
+config_clash(Config) ->
+ do_test(config_clash, "config_clash_SUITE.erl", [skip_cth], Config).
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
-do_test(Tag, SWC, CTHs, Config) ->
- do_test(Tag, SWC, CTHs, Config, ok).
-do_test(Tag, SWC, CTHs, Config, {error,_} = Res) ->
- do_test(Tag, SWC, CTHs, Config, Res, 1);
-do_test(Tag, SWC, CTHs, Config, Res) ->
- do_test(Tag, SWC, CTHs, Config, Res, 2).
+do_test(Tag, WTT, CTHs, Config) ->
+ do_test(Tag, WTT, CTHs, Config, ok).
+do_test(Tag, WTT, CTHs, Config, {error,_} = Res) ->
+ do_test(Tag, WTT, CTHs, Config, Res, 1,[]);
+do_test(Tag, WTT, CTHs, Config, Res) ->
+ do_test(Tag, WTT, CTHs, Config, Res, 2,[]).
-do_test(Tag, SuiteWildCard, CTHs, Config, Res, EC) ->
-
+do_test(Tag, WhatToTest, CTHs, Config, Res, EC, ExtraOpts) when is_list(WhatToTest) ->
+ do_test(Tag, {suite,WhatToTest}, CTHs, Config, Res, EC, ExtraOpts);
+do_test(Tag, {WhatTag,Wildcard}, CTHs, Config, Res, EC, ExtraOpts) ->
DataDir = ?config(data_dir, Config),
- Suites = filelib:wildcard(
- filename:join([DataDir,"cth/tests",SuiteWildCard])),
- {Opts,ERPid} = setup([{suite,Suites},
- {ct_hooks,CTHs},{label,Tag}], Config),
+ Files = filelib:wildcard(
+ filename:join([DataDir,"cth/tests",Wildcard])),
+ {Opts,ERPid} =
+ setup([{WhatTag,Files},{ct_hooks,CTHs},{label,Tag}|ExtraOpts], Config),
Res = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
- ct_test_support:log_events(Tag,
+ ct_test_support:log_events(Tag,
reformat(Events, ?eh),
?config(priv_dir, Config),
Opts),
@@ -323,12 +369,12 @@ test_events(one_empty_cth) ->
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
{?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
- {?eh,cth,{empty_cth,pre_init_per_testcase,[test_case,'$proplist',[]]}},
- {?eh,cth,{empty_cth,post_init_per_testcase,[test_case,'$proplist','_',[]]}},
- {?eh,cth,{empty_cth,pre_end_per_testcase,[test_case,'$proplist',[]]}},
- {?eh,cth,{empty_cth,post_end_per_testcase,[test_case,'$proplist','_',[]]}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist','_',[]]}},
+ {?eh,cth,{empty_cth,pre_end_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist','_',[]]}},
{?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
{?eh,cth,{empty_cth,pre_end_per_suite,
[ct_cth_empty_SUITE,'$proplist',[]]}},
@@ -355,12 +401,12 @@ test_events(two_empty_cth) ->
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
{?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',ok,[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',ok,[]]}},
{?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
{?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
@@ -402,7 +448,7 @@ test_events(minimal_cth) ->
{?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
{?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
{?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
@@ -423,10 +469,10 @@ test_events(minimal_and_maximal_cth) ->
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
{?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',ok,[]]}},
{?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
{?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{?eh,cth,{'_',post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}},
@@ -452,11 +498,11 @@ test_events(faulty_cth_undef) ->
{?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case,
{failed, FailReason}}},
{?eh,cth,{'_',on_tc_skip,'_'}},
-
+
{?eh,tc_auto_skip,{ct_cth_empty_SUITE,end_per_suite,
{failed, FailReason}}},
{?eh,cth,{'_',on_tc_skip,'_'}},
-
+
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
];
@@ -512,10 +558,10 @@ test_events(scope_per_suite_cth) ->
{?eh,tc_done,{ct_scope_per_suite_cth_SUITE,init_per_suite,ok}},
{?eh,tc_start,{ct_scope_per_suite_cth_SUITE,test_case}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_scope_per_suite_cth_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_scope_per_suite_cth_SUITE,test_case,'$proplist',ok,[]]}},
{?eh,tc_done,{ct_scope_per_suite_cth_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_scope_per_suite_cth_SUITE,end_per_suite}},
{?eh,cth,{'_',pre_end_per_suite,
[ct_scope_per_suite_cth_SUITE,'$proplist',[]]}},
@@ -538,10 +584,10 @@ test_events(scope_suite_cth) ->
{?eh,tc_done,{ct_scope_suite_cth_SUITE,init_per_suite,ok}},
{?eh,tc_start,{ct_scope_suite_cth_SUITE,test_case}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_scope_suite_cth_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_scope_suite_cth_SUITE,test_case,'$proplist',ok,[]]}},
{?eh,tc_done,{ct_scope_suite_cth_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_scope_suite_cth_SUITE,end_per_suite}},
{?eh,cth,{'_',pre_end_per_suite,[ct_scope_suite_cth_SUITE,'$proplist',[]]}},
{?eh,cth,{'_',post_end_per_suite,[ct_scope_suite_cth_SUITE,'$proplist','_',[]]}},
@@ -561,20 +607,20 @@ test_events(scope_per_group_cth) ->
[{?eh,tc_start,{ct_scope_per_group_cth_SUITE,{init_per_group,group1,[]}}},
{?eh,cth,{'_',id,[[]]}},
{?eh,cth,{'_',init,['_',[]]}},
- {?eh,cth,{'_',post_init_per_group,[group1,'$proplist','$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_group,[ct_scope_per_group_cth_SUITE,group1, '$proplist','$proplist',[]]}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,{init_per_group,group1,[]},ok}},
-
+
{?eh,tc_start,{ct_scope_per_group_cth_SUITE,test_case}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_scope_per_group_cth_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_scope_per_group_cth_SUITE,test_case,'$proplist',ok,[]]}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_scope_per_group_cth_SUITE,{end_per_group,group1,[]}}},
- {?eh,cth,{'_',pre_end_per_group,[group1,'$proplist',[]]}},
- {?eh,cth,{'_',post_end_per_group,[group1,'$proplist','_',[]]}},
+ {?eh,cth,{'_',pre_end_per_group,[ct_scope_per_group_cth_SUITE,group1,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_group,[ct_scope_per_group_cth_SUITE,group1,'$proplist','_',[]]}},
{?eh,cth,{'_',terminate,[[]]}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,{end_per_group,group1,[]},ok}}],
-
+
{?eh,tc_start,{ct_scope_per_group_cth_SUITE,end_per_suite}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
@@ -592,10 +638,10 @@ test_events(scope_per_suite_state_cth) ->
{?eh,tc_done,{ct_scope_per_suite_state_cth_SUITE,init_per_suite,ok}},
{?eh,tc_start,{ct_scope_per_suite_state_cth_SUITE,test_case}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[test]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[test]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_scope_per_suite_state_cth_SUITE,test_case,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_scope_per_suite_state_cth_SUITE,test_case,'$proplist',ok,[test]]}},
{?eh,tc_done,{ct_scope_per_suite_state_cth_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_scope_per_suite_state_cth_SUITE,end_per_suite}},
{?eh,cth,{'_',pre_end_per_suite,
[ct_scope_per_suite_state_cth_SUITE,'$proplist',[test]]}},
@@ -618,10 +664,10 @@ test_events(scope_suite_state_cth) ->
{?eh,tc_done,{ct_scope_suite_state_cth_SUITE,init_per_suite,ok}},
{?eh,tc_start,{ct_scope_suite_state_cth_SUITE,test_case}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[test]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[test]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_scope_suite_state_cth_SUITE,test_case,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_scope_suite_state_cth_SUITE,test_case,'$proplist',ok,[test]]}},
{?eh,tc_done,{ct_scope_suite_state_cth_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_scope_suite_state_cth_SUITE,end_per_suite}},
{?eh,cth,{'_',pre_end_per_suite,[ct_scope_suite_state_cth_SUITE,'$proplist',[test]]}},
{?eh,cth,{'_',post_end_per_suite,[ct_scope_suite_state_cth_SUITE,'$proplist','_',[test]]}},
@@ -641,20 +687,20 @@ test_events(scope_per_group_state_cth) ->
[{?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,{init_per_group,group1,[]}}},
{?eh,cth,{'_',id,[[test]]}},
{?eh,cth,{'_',init,['_',[test]]}},
- {?eh,cth,{'_',post_init_per_group,[group1,'$proplist','$proplist',[test]]}},
+ {?eh,cth,{'_',post_init_per_group,[ct_scope_per_group_state_cth_SUITE,group1,'$proplist','$proplist',[test]]}},
{?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,{init_per_group,group1,[]},ok}},
-
+
{?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,test_case}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[test]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[test]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_scope_per_group_state_cth_SUITE,test_case,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_scope_per_group_state_cth_SUITE,test_case,'$proplist',ok,[test]]}},
{?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,{end_per_group,group1,[]}}},
- {?eh,cth,{'_',pre_end_per_group,[group1,'$proplist',[test]]}},
- {?eh,cth,{'_',post_end_per_group,[group1,'$proplist','_',[test]]}},
+ {?eh,cth,{'_',pre_end_per_group,[ct_scope_per_group_state_cth_SUITE,group1,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_end_per_group,[ct_scope_per_group_state_cth_SUITE,group1,'$proplist','_',[test]]}},
{?eh,cth,{'_',terminate,[[test]]}},
{?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,{end_per_group,group1,[]},ok}}],
-
+
{?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,end_per_suite}},
{?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
@@ -666,7 +712,7 @@ test_events(fail_pre_suite_cth) ->
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,cth,{'_',init,['_',[]]}},
-
+
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist',
@@ -674,27 +720,27 @@ test_events(fail_pre_suite_cth) ->
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,
{failed, {error,"Test failure"}}}},
{?eh,cth,{'_',on_tc_fail,
- [init_per_suite,{failed,"Test failure"},[]]}},
+ [ct_cth_empty_SUITE,init_per_suite,"Test failure",[]]}},
+
-
{?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case,
{failed,{ct_cth_empty_SUITE,init_per_suite,
{failed,"Test failure"}}}}},
{?eh,cth,{'_',on_tc_skip,
- [test_case, {tc_auto_skip,
+ [ct_cth_empty_SUITE,test_case, {tc_auto_skip,
{failed, {ct_cth_empty_SUITE, init_per_suite,
{failed, "Test failure"}}}},[]]}},
-
+
{?eh,tc_auto_skip, {ct_cth_empty_SUITE, end_per_suite,
{failed, {ct_cth_empty_SUITE, init_per_suite,
{failed, "Test failure"}}}}},
{?eh,cth,{'_',on_tc_skip,
- [end_per_suite, {tc_auto_skip,
+ [ct_cth_empty_SUITE,end_per_suite, {tc_auto_skip,
{failed, {ct_cth_empty_SUITE, init_per_suite,
{failed, "Test failure"}}}},[]]}},
-
+
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,cth, {'_',terminate,[[]]}},
{?eh,stop_logging,[]}
@@ -727,17 +773,17 @@ test_events(fail_post_suite_cth) ->
{?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,
{failed,{error,"Test failure"}}}},
- {?eh,cth,{'_',on_tc_fail,[init_per_suite, {failed,"Test failure"}, []]}},
+ {?eh,cth,{'_',on_tc_fail,[ct_cth_empty_SUITE,init_per_suite, "Test failure", []]}},
{?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case,
{failed,{ct_cth_empty_SUITE,init_per_suite,
{failed,"Test failure"}}}}},
- {?eh,cth,{'_',on_tc_skip,[test_case,{tc_auto_skip,'_'},[]]}},
-
+ {?eh,cth,{'_',on_tc_skip,[ct_cth_empty_SUITE,test_case,{tc_auto_skip,'_'},[]]}},
+
{?eh,tc_auto_skip, {ct_cth_empty_SUITE, end_per_suite,
{failed, {ct_cth_empty_SUITE, init_per_suite,
{failed, "Test failure"}}}}},
- {?eh,cth,{'_',on_tc_skip,[end_per_suite,{tc_auto_skip,'_'},[]]}},
+ {?eh,cth,{'_',on_tc_skip,[ct_cth_empty_SUITE,end_per_suite,{tc_auto_skip,'_'},[]]}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,cth, {'_',terminate,[[]]}},
@@ -754,11 +800,11 @@ test_events(skip_pre_suite_cth) ->
{?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist',{skip,"Test skip"},[]]}},
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,{skipped,"Test skip"}}},
{?eh,cth,{'_',on_tc_skip,
- [init_per_suite,{tc_user_skip,{skipped,"Test skip"}},[]]}},
+ [ct_cth_empty_SUITE,init_per_suite,{tc_user_skip,"Test skip"},[]]}},
{?eh,tc_user_skip,{ct_cth_empty_SUITE,test_case,"Test skip"}},
- {?eh,cth,{'_',on_tc_skip,[test_case,{tc_user_skip,"Test skip"},[]]}},
-
+ {?eh,cth,{'_',on_tc_skip,[ct_cth_empty_SUITE,test_case,{tc_user_skip,"Test skip"},[]]}},
+
{?eh,tc_user_skip, {ct_cth_empty_SUITE, end_per_suite,"Test skip"}},
{?eh,test_done,{'DEF','STOP_TIME'}},
@@ -772,31 +818,33 @@ test_events(skip_pre_end_cth) ->
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,tc_start,{ct_scope_per_group_cth_SUITE,init_per_suite}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,init_per_suite,ok}},
-
+
[{?eh,tc_start,{ct_scope_per_group_cth_SUITE,{init_per_group,group1,[]}}},
{?eh,cth,{'_',id,[[]]}},
{?eh,cth,{'_',init,['_',[]]}},
- {?eh,cth,{'_',post_init_per_group,[group1,'$proplist','$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_group,[ct_scope_per_group_cth_SUITE,group1,'$proplist','$proplist',[]]}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,{init_per_group,group1,[]},ok}},
-
+
{?eh,tc_start,{ct_scope_per_group_cth_SUITE,test_case}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_scope_per_group_cth_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_scope_per_group_cth_SUITE,test_case,'$proplist',ok,[]]}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_scope_per_group_cth_SUITE,{end_per_group,group1,[]}}},
- {?eh,cth,{'_',pre_end_per_group,[group1,'$proplist',[]]}},
- {?eh,cth,{'_',post_end_per_group,[group1,'$proplist','_',[]]}},
+ {?eh,cth,{'_',pre_end_per_group,[ct_scope_per_group_cth_SUITE,group1,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_group,[ct_scope_per_group_cth_SUITE,group1,'$proplist','_',[]]}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,{end_per_group,group1,[]},
{skipped,"Test skip"}}}],
- {?eh,cth,{'_',on_tc_skip,[{end_per_group,group1},
- {tc_user_skip,{skipped,"Test skip"}},
+ {?eh,cth,{'_',on_tc_skip,[ct_scope_per_group_cth_SUITE,
+ {end_per_group,group1},
+ {tc_user_skip,"Test skip"},
[]]}},
{?eh,tc_start,{ct_scope_per_group_cth_SUITE,end_per_suite}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,end_per_suite,
{skipped,"Test skip"}}},
- {?eh,cth,{'_',on_tc_skip,[end_per_suite,
- {tc_user_skip,{skipped,"Test skip"}},
+ {?eh,cth,{'_',on_tc_skip,[ct_scope_per_group_cth_SUITE,
+ end_per_suite,
+ {tc_user_skip,"Test skip"},
[]]}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,cth,{'_',terminate,[[]]}},
@@ -808,24 +856,59 @@ test_events(skip_post_suite_cth) ->
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,cth,{'_',init,['_',[]]}},
-
+
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,{skipped,"Test skip"}}},
{?eh,cth,{'_',on_tc_skip,
- [init_per_suite,{tc_user_skip,{skipped,"Test skip"}},[]]}},
+ [ct_cth_empty_SUITE,init_per_suite,{tc_user_skip,"Test skip"},[]]}},
{?eh,tc_user_skip,{ct_cth_empty_SUITE,test_case,"Test skip"}},
- {?eh,cth,{'_',on_tc_skip,[test_case,{tc_user_skip,"Test skip"},[]]}},
-
+ {?eh,cth,{'_',on_tc_skip,[ct_cth_empty_SUITE,test_case,{tc_user_skip,"Test skip"},[]]}},
+
{?eh,tc_user_skip, {ct_cth_empty_SUITE, end_per_suite,"Test skip"}},
-
+
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,cth,{'_',terminate,[[]]}},
{?eh,stop_logging,[]}
];
+test_events(skip_pre_init_tc_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,init,['_',[]]}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [ct_cth_empty_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [ct_cth_empty_SUITE,test_case,'$proplist',
+ {skip,"Skipped in pre_init_per_testcase"},
+ []]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,test_case,
+ {skipped,"Skipped in pre_init_per_testcase"}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [ct_cth_empty_SUITE,test_case,
+ {tc_user_skip,"Skipped in pre_init_per_testcase"},
+ []]}},
+ {?eh,test_stats,{0,0,{1,0}}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,
+ [ct_cth_empty_SUITE,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
test_events(recover_post_suite_cth) ->
Suite = ct_cth_fail_per_suite_SUITE,
[
@@ -840,11 +923,11 @@ test_events(recover_post_suite_cth) ->
{?eh,tc_start,{Suite,test_case}},
{?eh,cth,{'_',pre_init_per_testcase,
- [test_case, not_contains([tc_status]),[]]}},
+ [Suite,test_case, not_contains([tc_status]),[]]}},
{?eh,cth,{'_',post_end_per_testcase,
- [test_case, contains([tc_status]),'_',[]]}},
+ [Suite,test_case, contains([tc_status]),'_',[]]}},
{?eh,tc_done,{Suite,test_case,ok}},
-
+
{?eh,tc_start,{Suite,end_per_suite}},
{?eh,cth,{'_',pre_end_per_suite,
[Suite,not_contains([tc_status]),[]]}},
@@ -861,7 +944,7 @@ test_events(update_config_cth) ->
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,cth,{'_',init,['_',[]]}},
-
+
{?eh,tc_start,{ct_update_config_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,
[ct_update_config_SUITE,contains([]),[]]}},
@@ -876,13 +959,15 @@ test_events(update_config_cth) ->
{?eh,tc_start,{ct_update_config_SUITE, {init_per_group,group1,[]}}},
{?eh,cth,{'_',pre_init_per_group,
- [group1,contains(
+ [ct_update_config_SUITE,
+ group1,contains(
[post_init_per_suite,
init_per_suite,
pre_init_per_suite]),
[]]}},
{?eh,cth,{'_',post_init_per_group,
- [group1,
+ [ct_update_config_SUITE,
+ group1,
contains(
[post_init_per_suite,
init_per_suite,
@@ -898,7 +983,8 @@ test_events(update_config_cth) ->
{?eh,tc_start,{ct_update_config_SUITE,test_case}},
{?eh,cth,{'_',pre_init_per_testcase,
- [test_case,contains(
+ [ct_update_config_SUITE,
+ test_case,contains(
[post_init_per_group,
init_per_group,
pre_init_per_group,
@@ -907,7 +993,8 @@ test_events(update_config_cth) ->
pre_init_per_suite]),
[]]}},
{?eh,cth,{'_',post_end_per_testcase,
- [test_case,contains(
+ [ct_update_config_SUITE,
+ test_case,contains(
[init_per_testcase,
pre_init_per_testcase,
post_init_per_group,
@@ -921,7 +1008,8 @@ test_events(update_config_cth) ->
{?eh,tc_start,{ct_update_config_SUITE, {end_per_group,group1,[]}}},
{?eh,cth,{'_',pre_end_per_group,
- [group1,contains(
+ [ct_update_config_SUITE,
+ group1,contains(
[post_init_per_group,
init_per_group,
pre_init_per_group,
@@ -930,7 +1018,8 @@ test_events(update_config_cth) ->
pre_init_per_suite]),
[]]}},
{?eh,cth,{'_',post_end_per_group,
- [group1,
+ [ct_update_config_SUITE,
+ group1,
contains(
[pre_end_per_group,
post_init_per_group,
@@ -941,7 +1030,7 @@ test_events(update_config_cth) ->
pre_init_per_suite]),
ok,[]]}},
{?eh,tc_done,{ct_update_config_SUITE,{end_per_group,group1,[]},ok}},
-
+
{?eh,tc_start,{ct_update_config_SUITE,end_per_suite}},
{?eh,cth,{'_',pre_end_per_suite,
[ct_update_config_SUITE,contains(
@@ -974,7 +1063,7 @@ test_events(state_update_cth) ->
{?eh,cth,{'_',init,['_',[]]}},
{?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{'_',init_per_suite}},
-
+
{?eh,tc_done,{'_',end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,cth,{'_',terminate,[contains(
@@ -1018,10 +1107,10 @@ test_events(options_cth) ->
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
{?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
- {?eh,cth,{empty_cth,pre_init_per_testcase,[test_case,'$proplist',[test]]}},
- {?eh,cth,{empty_cth,post_end_per_testcase,[test_case,'$proplist','_',[test]]}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',[test]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist','_',[test]]}},
{?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}},
-
+
{?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
{?eh,cth,{empty_cth,pre_end_per_suite,
[ct_cth_empty_SUITE,'$proplist',[test]]}},
@@ -1051,14 +1140,14 @@ test_events(same_id_cth) ->
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}}},
{?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',[]]}},
{negative,
- {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}}},
+ {?eh,cth,{'_',pre_init_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',ok,[]]}}},
{negative,
- {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',ok,[]]}},
{?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}}},
-
+
{?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
{?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{negative,
@@ -1094,11 +1183,13 @@ test_events(fail_n_skip_with_minimal_cth) ->
{?eh,tc_done,{ct_cth_fail_one_skip_one_SUITE,test_case2,{skipped,"skip it"}}},
{?eh,tc_start,{ct_cth_fail_one_skip_one_SUITE,test_case3}},
{?eh,tc_done,{ct_cth_fail_one_skip_one_SUITE,test_case3,{skipped,"skip it"}}},
- {?eh,cth,{empty_cth,on_tc_skip,[{test_case2,group2},
- {tc_user_skip,{skipped,"skip it"}},
+ {?eh,cth,{empty_cth,on_tc_skip,[ct_cth_fail_one_skip_one_SUITE,
+ {test_case2,group2},
+ {tc_user_skip,"skip it"},
[]]}},
- {?eh,cth,{empty_cth,on_tc_skip,[{test_case3,group2},
- {tc_user_skip,{skipped,"skip it"}},
+ {?eh,cth,{empty_cth,on_tc_skip,[ct_cth_fail_one_skip_one_SUITE,
+ {test_case3,group2},
+ {tc_user_skip,"skip it"},
[]]}},
{?eh,tc_start,{ct_cth_fail_one_skip_one_SUITE,{end_per_group,
group2,[parallel]}}},
@@ -1115,17 +1206,25 @@ test_events(fail_n_skip_with_minimal_cth) ->
];
test_events(prio_cth) ->
-
- GenPre = fun(Func,States) ->
- [{?eh,cth,{'_',Func,['_','_',State]}} ||
+ GenPre = fun(Func,States) when Func==pre_init_per_suite;
+ Func==pre_end_per_suite ->
+ [{?eh,cth,{'_',Func,['_','_',State]}} ||
+ State <- States];
+ (Func,States) ->
+ [{?eh,cth,{'_',Func,['_','_','_',State]}} ||
State <- States]
end,
- GenPost = fun(Func,States) ->
- [{?eh,cth,{'_',Func,['_','_','_',State]}} ||
+ GenPost = fun(Func,States) when Func==post_init_per_suite;
+ Func==post_end_per_suite ->
+ [{?eh,cth,{'_',Func,['_','_','_',State]}} ||
+ State <- States];
+ (Func,States) ->
+ [{?eh,cth,{'_',Func,['_','_','_','_',State]}} ||
State <- States]
- end,
-
+
+ end,
+
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}] ++
@@ -1136,7 +1235,7 @@ test_events(prio_cth) ->
[[1100,100],[600,200],[600,600],[700],[800],[900],[1000],
[1200,1050],[1100],[1200]]) ++
[{?eh,tc_done,{ct_cth_prio_SUITE,init_per_suite,ok}},
-
+
[{?eh,tc_start,{ct_cth_prio_SUITE,{init_per_group,'_',[]}}}] ++
GenPre(pre_init_per_group,
@@ -1147,7 +1246,7 @@ test_events(prio_cth) ->
[900],[900,900],[500,900],[1000],[1200,1050],
[1100],[1200]]) ++
[{?eh,tc_done,{ct_cth_prio_SUITE,{init_per_group,'_',[]},ok}}] ++
-
+
[{?eh,tc_start,{ct_cth_prio_SUITE,test_case}}] ++
GenPre(pre_init_per_testcase,
[[1100,100],[600,200],[600,600],[600],[700],[800],
@@ -1161,7 +1260,7 @@ test_events(prio_cth) ->
[{?eh,tc_done,{ct_cth_prio_SUITE,test_case,ok}},
{?eh,tc_start,{ct_cth_prio_SUITE,{end_per_group,'_',[]}}}] ++
- GenPre(pre_end_per_group,
+ GenPre(pre_end_per_group,
lists:reverse(
[[1100,100],[600,200],[600,600],[600],[700],[800],
[900],[900,900],[500,900],[1000],[1200,1050],
@@ -1200,30 +1299,30 @@ test_events(no_config) ->
{?eh,tc_done,{ct_framework,init_per_suite,ok}},
{?eh,tc_start,{ct_no_config_SUITE,test_case_1}},
{?eh,cth,{empty_cth,pre_init_per_testcase,
- [test_case_1,'$proplist',[]]}},
+ [ct_no_config_SUITE,test_case_1,'$proplist',[]]}},
{?eh,cth,{empty_cth,post_end_per_testcase,
- [test_case_1,'$proplist',ok,[]]}},
+ [ct_no_config_SUITE,test_case_1,'$proplist',ok,[]]}},
{?eh,tc_done,{ct_no_config_SUITE,test_case_1,ok}},
{?eh,test_stats,{1,0,{0,0}}},
[{?eh,tc_start,{ct_framework,{init_per_group,test_group,'$proplist'}}},
{?eh,cth,{empty_cth,pre_init_per_group,
- [test_group,'$proplist',[]]}},
+ [ct_no_config_SUITE,test_group,'$proplist',[]]}},
{?eh,cth,{empty_cth,post_init_per_group,
- [test_group,'$proplist','$proplist',[]]}},
+ [ct_no_config_SUITE,test_group,'$proplist','$proplist',[]]}},
{?eh,tc_done,{ct_framework,
{init_per_group,test_group,'$proplist'},ok}},
{?eh,tc_start,{ct_no_config_SUITE,test_case_2}},
{?eh,cth,{empty_cth,pre_init_per_testcase,
- [test_case_2,'$proplist',[]]}},
+ [ct_no_config_SUITE,test_case_2,'$proplist',[]]}},
{?eh,cth,{empty_cth,post_end_per_testcase,
- [test_case_2,'$proplist',ok,[]]}},
+ [ct_no_config_SUITE,test_case_2,'$proplist',ok,[]]}},
{?eh,tc_done,{ct_no_config_SUITE,test_case_2,ok}},
{?eh,test_stats,{2,0,{0,0}}},
{?eh,tc_start,{ct_framework,{end_per_group,test_group,'$proplist'}}},
{?eh,cth,{empty_cth,pre_end_per_group,
- [test_group,'$proplist',[]]}},
+ [ct_no_config_SUITE,test_group,'$proplist',[]]}},
{?eh,cth,{empty_cth,post_end_per_group,
- [test_group,'$proplist',ok,[]]}},
+ [ct_no_config_SUITE,test_group,'$proplist',ok,[]]}},
{?eh,tc_done,{ct_framework,{end_per_group,test_group,'$proplist'},ok}}],
{?eh,tc_start,{ct_framework,end_per_suite}},
{?eh,cth,{empty_cth,pre_end_per_suite,
@@ -1236,6 +1335,166 @@ test_events(no_config) ->
{?eh,stop_logging,[]}
];
+test_events(no_init_suite_config) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{ct_no_init_suite_config_SUITE,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [ct_no_init_suite_config_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [ct_no_init_suite_config_SUITE,'$proplist','_',[]]}},
+ {?eh,tc_done,{ct_no_init_suite_config_SUITE,init_per_suite,
+ {failed,{error,{undef,'_'}}}}},
+ {?eh,cth,{empty_cth,on_tc_fail,[ct_no_init_suite_config_SUITE,
+ init_per_suite,
+ {undef,'_'},[]]}},
+ {?eh,tc_auto_skip,{ct_no_init_suite_config_SUITE,test_case,
+ {failed,{ct_no_init_suite_config_SUITE,init_per_suite,
+ {'EXIT',{undef,'_'}}}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [ct_no_init_suite_config_SUITE,
+ test_case,
+ {tc_auto_skip,
+ {failed,{ct_no_init_suite_config_SUITE,init_per_suite,
+ {'EXIT',{undef,'_'}}}}},
+ []]}},
+ {?eh,test_stats,{0,0,{0,1}}},
+ {?eh,tc_auto_skip,{ct_no_init_suite_config_SUITE,end_per_suite,
+ {failed,{ct_no_init_suite_config_SUITE,init_per_suite,
+ {'EXIT',{undef,'_'}}}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [ct_no_init_suite_config_SUITE,
+ end_per_suite,
+ {tc_auto_skip,
+ {failed,{ct_no_init_suite_config_SUITE,init_per_suite,
+ {'EXIT',{undef,'_'}}}}},
+ []]}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(no_init_config) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,start_info,{1,1,2}},
+ {?eh,tc_start,{ct_no_init_config_SUITE,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [ct_no_init_config_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [ct_no_init_config_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_no_init_config_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{ct_no_init_config_SUITE,test_case_1}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [ct_no_init_config_SUITE,test_case_1,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [ct_no_init_config_SUITE,test_case_1,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_no_init_config_SUITE,test_case_1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ [{?eh,tc_start,{ct_no_init_config_SUITE,{init_per_group,test_group,[]}}},
+ {?eh,cth,{empty_cth,pre_init_per_group,
+ [ct_no_init_config_SUITE,test_group,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_group,
+ [ct_no_init_config_SUITE,test_group,'$proplist','_',[]]}},
+ {?eh,tc_done,{ct_no_init_config_SUITE,{init_per_group,test_group,[]},
+ {failed,{error,{undef,'_'}}}}},
+ {?eh,cth,{empty_cth,on_tc_fail,[ct_no_init_config_SUITE,
+ {init_per_group,test_group},
+ {undef,'_'},[]]}},
+ {?eh,tc_auto_skip,{ct_no_init_config_SUITE,{test_case_2,test_group},
+ {failed,{ct_no_init_config_SUITE,init_per_group,
+ {'EXIT',{undef,'_'}}}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,[ct_no_init_config_SUITE,
+ {test_case_2,test_group},
+ {tc_auto_skip,
+ {failed,
+ {ct_no_init_config_SUITE,init_per_group,
+ {'EXIT',{undef,'_'}}}}},
+ []]}},
+ {?eh,test_stats,{1,0,{0,1}}},
+ {?eh,tc_auto_skip,{ct_no_init_config_SUITE,{end_per_group,test_group},
+ {failed,{ct_no_init_config_SUITE,init_per_group,
+ {'EXIT',{undef,'_'}}}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,[ct_no_init_config_SUITE,
+ {end_per_group,test_group},
+ {tc_auto_skip,
+ {failed,
+ {ct_no_init_config_SUITE,init_per_group,
+ {'EXIT',{undef,'_'}}}}},
+ []]}}],
+ {?eh,tc_start,{ct_no_init_config_SUITE,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,
+ [ct_no_init_config_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,
+ [ct_no_init_config_SUITE,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_no_init_config_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(no_end_config) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,start_info,{1,1,2}},
+ {?eh,tc_start,{ct_no_end_config_SUITE,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [ct_no_end_config_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [ct_no_end_config_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_no_end_config_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{ct_no_end_config_SUITE,test_case_1}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [ct_no_end_config_SUITE,test_case_1,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [ct_no_end_config_SUITE,test_case_1,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_no_end_config_SUITE,test_case_1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ [{?eh,tc_start,{ct_no_end_config_SUITE,
+ {init_per_group,test_group,'$proplist'}}},
+ {?eh,cth,{empty_cth,pre_init_per_group,
+ [ct_no_end_config_SUITE,test_group,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_group,
+ [ct_no_end_config_SUITE,test_group,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_no_end_config_SUITE,
+ {init_per_group,test_group,'$proplist'},ok}},
+ {?eh,tc_start,{ct_no_end_config_SUITE,test_case_2}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [ct_no_end_config_SUITE,test_case_2,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [ct_no_end_config_SUITE,test_case_2,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_no_end_config_SUITE,test_case_2,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,{ct_no_end_config_SUITE,
+ {end_per_group,test_group,'$proplist'}}},
+ {?eh,cth,{empty_cth,pre_end_per_group,
+ [ct_no_end_config_SUITE,test_group,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_group,
+ [ct_no_end_config_SUITE,test_group,'$proplist','_',[]]}},
+ {?eh,tc_done,{ct_no_end_config_SUITE,{end_per_group,test_group,[]},
+ {failed,{error,{undef,'_'}}}}},
+ {?eh,cth,{empty_cth,on_tc_fail,[ct_no_end_config_SUITE,
+ {end_per_group,test_group},
+ {undef,'_'},[]]}}],
+ {?eh,tc_start,{ct_no_end_config_SUITE,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,
+ [ct_no_end_config_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,
+ [ct_no_end_config_SUITE,'$proplist','_',[]]}},
+ {?eh,tc_done,{ct_no_end_config_SUITE,end_per_suite,
+ {failed,{error,{undef,'_'}}}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
test_events(data_dir) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
@@ -1250,30 +1509,30 @@ test_events(data_dir) ->
{?eh,tc_done,{ct_framework,init_per_suite,ok}},
{?eh,tc_start,{ct_data_dir_SUITE,test_case_1}},
{?eh,cth,{empty_cth,pre_init_per_testcase,
- [test_case_1,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ [ct_data_dir_SUITE,test_case_1,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
{?eh,cth,{empty_cth,post_end_per_testcase,
- [test_case_1,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ [ct_data_dir_SUITE,test_case_1,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
{?eh,tc_done,{ct_data_dir_SUITE,test_case_1,ok}},
{?eh,test_stats,{1,0,{0,0}}},
[{?eh,tc_start,{ct_framework,{init_per_group,test_group,'$proplist'}}},
{?eh,cth,{empty_cth,pre_init_per_group,
- [test_group,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ [ct_data_dir_SUITE,test_group,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
{?eh,cth,{empty_cth,post_init_per_group,
- [test_group,'$proplist','$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ [ct_data_dir_SUITE,test_group,'$proplist','$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
{?eh,tc_done,{ct_framework,
{init_per_group,test_group,'$proplist'},ok}},
{?eh,tc_start,{ct_data_dir_SUITE,test_case_2}},
{?eh,cth,{empty_cth,pre_init_per_testcase,
- [test_case_2,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ [ct_data_dir_SUITE,test_case_2,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
{?eh,cth,{empty_cth,post_end_per_testcase,
- [test_case_2,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ [ct_data_dir_SUITE,test_case_2,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
{?eh,tc_done,{ct_data_dir_SUITE,test_case_2,ok}},
{?eh,test_stats,{2,0,{0,0}}},
{?eh,tc_start,{ct_framework,{end_per_group,test_group,'$proplist'}}},
{?eh,cth,{empty_cth,pre_end_per_group,
- [test_group,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ [ct_data_dir_SUITE,test_group,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
{?eh,cth,{empty_cth,post_end_per_group,
- [test_group,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ [ct_data_dir_SUITE,test_group,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
{?eh,tc_done,{ct_framework,{end_per_group,test_group,'$proplist'},ok}}],
{?eh,tc_start,{ct_framework,end_per_suite}},
{?eh,cth,{empty_cth,pre_end_per_suite,
@@ -1300,16 +1559,654 @@ test_events(cth_log) ->
[{suite,cth_log_SUITE},parallel]}}},
{?eh,tc_done,{ct_framework,{end_per_group,g1,
[{suite,cth_log_SUITE},parallel]},ok}}]},
-
+
{?eh,tc_done,{cth_log_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
];
+test_events(fallback) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,tc_start,{all_hook_callbacks_SUITE,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [all_hook_callbacks_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [all_hook_callbacks_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{all_hook_callbacks_SUITE,init_per_suite,ok}},
+
+ [{?eh,tc_start,{ct_framework,{init_per_group,test_group,'$proplist'}}},
+ {?eh,cth,{empty_cth,pre_init_per_group,
+ [fallback_nosuite,test_group,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_group,
+ [fallback_nosuite,test_group,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_framework,
+ {init_per_group,test_group,'$proplist'},ok}},
+ {?eh,tc_start,{all_hook_callbacks_SUITE,test_case}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [fallback_nosuite,test_case,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [fallback_nosuite,test_case,'$proplist',ok,[]]}},
+ {?eh,tc_done,{all_hook_callbacks_SUITE,test_case,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{ct_framework,{end_per_group,test_group,'$proplist'}}},
+ {?eh,cth,{empty_cth,pre_end_per_group,
+ [fallback_nosuite,test_group,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_group,
+ [fallback_nosuite,test_group,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_framework,{end_per_group,test_group,'$proplist'},ok}}],
+ {?eh,tc_start,{all_hook_callbacks_SUITE,test_case}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [fallback_nosuite,test_case,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [fallback_nosuite,test_case,'$proplist','_',[]]}},
+ {?eh,cth,{empty_cth,pre_end_per_testcase,
+ [fallback_nosuite,test_case,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [fallback_nosuite,test_case,'$proplist','_',[]]}},
+ {?eh,tc_done,{all_hook_callbacks_SUITE,test_case,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,{all_hook_callbacks_SUITE,skip_case}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [fallback_nosuite,skip_case,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [fallback_nosuite,skip_case,'$proplist',
+ {skip,"Skipped in init_per_testcase/2"},[]]}},
+ {?eh,tc_done,{all_hook_callbacks_SUITE,skip_case,
+ {skipped,"Skipped in init_per_testcase/2"}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [fallback_nosuite,skip_case,
+ {tc_user_skip,"Skipped in init_per_testcase/2"},
+ []]}},
+ {?eh,test_stats,{2,0,{1,0}}},
+ {?eh,tc_start,{all_hook_callbacks_SUITE,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,
+ [all_hook_callbacks_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,
+ [all_hook_callbacks_SUITE,'$proplist','_',[]]}},
+ {?eh,tc_done,{all_hook_callbacks_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(callbacks_on_skip) ->
+ %% skip_cth.erl will send a 'cth_error' event if a hook is
+ %% erroneously called. Therefore, all Events are changed to
+ %% {negative,{?eh,cth_error,'_'},Event}
+ %% at the end of this function.
+ Events =
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,start_info,{6,6,15}},
+
+ %% all_hook_callbacks_SUITE is skipped in spec
+ %% Only the on_tc_skip callback shall be called
+ {?eh,tc_user_skip,{all_hook_callbacks_SUITE,all,"Skipped in spec"}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [all_hook_callbacks_SUITE,all,
+ {tc_user_skip,"Skipped in spec"},
+ []]}},
+ {?eh,test_stats,{0,0,{1,0}}},
+
+ %% skip_init_SUITE is skipped in its init_per_suite function
+ %% No group- or testcase-functions shall be called.
+ {?eh,tc_start,{skip_init_SUITE,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [skip_init_SUITE,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [skip_init_SUITE,
+ '$proplist',
+ {skip,"Skipped in init_per_suite/1"},
+ []]}},
+ {?eh,tc_done,{skip_init_SUITE,init_per_suite,
+ {skipped,"Skipped in init_per_suite/1"}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_init_SUITE,init_per_suite,
+ {tc_user_skip,"Skipped in init_per_suite/1"},
+ []]}},
+ {?eh,tc_user_skip,{skip_init_SUITE,test_case,"Skipped in init_per_suite/1"}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_init_SUITE,test_case,
+ {tc_user_skip,"Skipped in init_per_suite/1"},
+ []]}},
+ {?eh,test_stats,{0,0,{2,0}}},
+ {?eh,tc_user_skip,{skip_init_SUITE,end_per_suite,
+ "Skipped in init_per_suite/1"}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_init_SUITE,end_per_suite,
+ {tc_user_skip,"Skipped in init_per_suite/1"},
+ []]}},
+
+ %% skip_req_SUITE is auto-skipped since a 'require' statement
+ %% returned by suite/0 is not fulfilled.
+ %% No group- or testcase-functions shall be called.
+ {?eh,tc_start,{skip_req_SUITE,init_per_suite}},
+ {?eh,tc_done,{skip_req_SUITE,init_per_suite,
+ {auto_skipped,{require_failed_in_suite0,
+ {not_available,whatever}}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_req_SUITE,init_per_suite,
+ {tc_auto_skip,{require_failed_in_suite0,
+ {not_available,whatever}}},
+ []]}},
+ {?eh,tc_auto_skip,{skip_req_SUITE,test_case,{require_failed_in_suite0,
+ {not_available,whatever}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_req_SUITE,test_case,
+ {tc_auto_skip,{require_failed_in_suite0,
+ {not_available,whatever}}},
+ []]}},
+ {?eh,test_stats,{0,0,{2,1}}},
+ {?eh,tc_auto_skip,{skip_req_SUITE,end_per_suite,
+ {require_failed_in_suite0,
+ {not_available,whatever}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_req_SUITE,end_per_suite,
+ {tc_auto_skip,{require_failed_in_suite0,
+ {not_available,whatever}}},
+ []]}},
+
+ %% skip_fail_SUITE is auto-skipped since the suite/0 function
+ %% retuns a faluty format.
+ %% No group- or testcase-functions shall be called.
+ {?eh,tc_start,{skip_fail_SUITE,init_per_suite}},
+ {?eh,tc_done,{skip_fail_SUITE,init_per_suite,
+ {failed,{error,{suite0_failed,bad_return_value}}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_fail_SUITE,init_per_suite,
+ {tc_auto_skip,
+ {failed,{error,{suite0_failed,bad_return_value}}}},
+ []]}},
+ {?eh,tc_auto_skip,{skip_fail_SUITE,test_case,
+ {failed,{error,{suite0_failed,bad_return_value}}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_fail_SUITE,test_case,
+ {tc_auto_skip,
+ {failed,{error,{suite0_failed,bad_return_value}}}},
+ []]}},
+ {?eh,test_stats,{0,0,{2,2}}},
+ {?eh,tc_auto_skip,{skip_fail_SUITE,end_per_suite,
+ {failed,{error,{suite0_failed,bad_return_value}}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_fail_SUITE,end_per_suite,
+ {tc_auto_skip,
+ {failed,{error,{suite0_failed,bad_return_value}}}},
+ []]}},
+
+ %% skip_group_SUITE
+ {?eh,tc_start,{skip_group_SUITE,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [skip_group_SUITE,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [skip_group_SUITE,
+ '$proplist',
+ '_',
+ []]}},
+ {?eh,tc_done,{skip_group_SUITE,init_per_suite,ok}},
+
+ %% test_group_1 - auto_skip due to require failed
+ [{?eh,tc_start,{skip_group_SUITE,{init_per_group,test_group_1,[]}}},
+ {?eh,tc_done,
+ {skip_group_SUITE,{init_per_group,test_group_1,[]},
+ {auto_skipped,{require_failed,{not_available,whatever}}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_group_SUITE,
+ {init_per_group,test_group_1},
+ {tc_auto_skip,{require_failed,{not_available,whatever}}},
+ []]}},
+ {?eh,tc_auto_skip,{skip_group_SUITE,{test_case,test_group_1},
+ {require_failed,{not_available,whatever}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_group_SUITE,
+ {test_case,test_group_1},
+ {tc_auto_skip,{require_failed,{not_available,whatever}}},
+ []]}},
+ {?eh,test_stats,{0,0,{2,3}}},
+ {?eh,tc_auto_skip,{skip_group_SUITE,{end_per_group,test_group_1},
+ {require_failed,{not_available,whatever}}}}],
+ %% The following appears to be outside of the group, but
+ %% that's only an implementation detail in
+ %% ct_test_support.erl - it does not know about events from
+ %% test suite specific hooks and regards the group ended with
+ %% the above tc_auto_skip-event for end_per_group.
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_group_SUITE,
+ {end_per_group,test_group_1},
+ {tc_auto_skip,{require_failed,{not_available,whatever}}},
+ []]}},
+
+ %% test_group_2 - auto_skip due to failed return from group/1
+ [{?eh,tc_start,{skip_group_SUITE,{init_per_group,test_group_2,[]}}},
+ {?eh,tc_done,
+ {skip_group_SUITE,{init_per_group,test_group_2,[]},
+ {auto_skipped,{group0_failed,bad_return_value}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_group_SUITE,
+ {init_per_group,test_group_2},
+ {tc_auto_skip,{group0_failed,bad_return_value}},
+ []]}},
+ {?eh,tc_auto_skip,{skip_group_SUITE,{test_case,test_group_2},
+ {group0_failed,bad_return_value}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_group_SUITE,
+ {test_case,test_group_2},
+ {tc_auto_skip,{group0_failed,bad_return_value}},
+ []]}},
+ {?eh,test_stats,{0,0,{2,4}}},
+ {?eh,tc_auto_skip,{skip_group_SUITE,{end_per_group,test_group_2},
+ {group0_failed,bad_return_value}}}],
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_group_SUITE,
+ {end_per_group,test_group_2},
+ {tc_auto_skip,{group0_failed,bad_return_value}},
+ []]}},
+ %% test_group_3 - user_skip in init_per_group/2
+ [{?eh,tc_start,
+ {skip_group_SUITE,{init_per_group,test_group_3,[]}}},
+ {?eh,cth,{empty_cth,pre_init_per_group,
+ [skip_group_SUITE,test_group_3,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_group,
+ [skip_group_SUITE,test_group_3,'$proplist',
+ {skip,"Skipped in init_per_group/2"},
+ []]}},
+ {?eh,tc_done,{skip_group_SUITE,
+ {init_per_group,test_group_3,[]},
+ {skipped,"Skipped in init_per_group/2"}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_group_SUITE,
+ {init_per_group,test_group_3},
+ {tc_user_skip,"Skipped in init_per_group/2"},
+ []]}},
+ {?eh,tc_user_skip,{skip_group_SUITE,
+ {test_case,test_group_3},
+ "Skipped in init_per_group/2"}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_group_SUITE,
+ {test_case,test_group_3},
+ {tc_user_skip,"Skipped in init_per_group/2"},
+ []]}},
+ {?eh,test_stats,{0,0,{3,4}}},
+ {?eh,tc_user_skip,{skip_group_SUITE,
+ {end_per_group,test_group_3},
+ "Skipped in init_per_group/2"}}],
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_group_SUITE,
+ {end_per_group,test_group_3},
+ {tc_user_skip,"Skipped in init_per_group/2"},
+ []]}},
+
+ {?eh,tc_start,{skip_group_SUITE,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,
+ [skip_group_SUITE,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,
+ [skip_group_SUITE,
+ '$proplist',
+ ok,[]]}},
+ {?eh,tc_done,{skip_group_SUITE,end_per_suite,ok}},
+
+
+ %% skip_case_SUITE has 4 test cases which are all skipped in
+ %% different ways
+ {?eh,tc_start,{skip_case_SUITE,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [skip_case_SUITE,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [skip_case_SUITE,
+ '$proplist',
+ '_',
+ []]}},
+ {?eh,tc_done,{skip_case_SUITE,init_per_suite,ok}},
+
+ %% Skip in spec -> only on_tc_skip shall be called
+ {?eh,tc_user_skip,{skip_case_SUITE,skip_in_spec,"Skipped in spec"}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_case_SUITE,skip_in_spec,
+ {tc_user_skip,"Skipped in spec"},
+ []]}},
+ {?eh,test_stats,{0,0,{4,4}}},
+
+ %% Skip in init_per_testcase -> pre/post_end_per_testcase
+ %% shall not be called
+ {?eh,tc_start,{skip_case_SUITE,skip_in_init}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [skip_case_SUITE,skip_in_init,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [skip_case_SUITE,skip_in_init,
+ '$proplist',
+ {skip,"Skipped in init_per_testcase/2"},
+ []]}},
+ {?eh,tc_done,{skip_case_SUITE,skip_in_init,
+ {skipped,"Skipped in init_per_testcase/2"}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_case_SUITE,skip_in_init,
+ {tc_user_skip,"Skipped in init_per_testcase/2"},
+ []]}},
+ {?eh,test_stats,{0,0,{5,4}}},
+
+ %% Fail in init_per_testcase -> pre/post_end_per_testcase
+ %% shall not be called
+ {?eh,tc_start,{skip_case_SUITE,fail_in_init}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [skip_case_SUITE,fail_in_init,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [skip_case_SUITE,fail_in_init,
+ '$proplist',
+ {skip,{failed,'_'}},
+ []]}},
+ {?eh,tc_done,{skip_case_SUITE,fail_in_init,
+ {auto_skipped,{failed,'_'}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_case_SUITE,fail_in_init,
+ {tc_auto_skip,{failed,'_'}},
+ []]}},
+ {?eh,test_stats,{0,0,{5,5}}},
+
+ %% Exit in init_per_testcase -> pre/post_end_per_testcase
+ %% shall not be called
+ {?eh,tc_start,{skip_case_SUITE,exit_in_init}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [skip_case_SUITE,exit_in_init,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [skip_case_SUITE,exit_in_init,
+ '$proplist',
+ {skip,{failed,'_'}},
+ []]}},
+ {?eh,tc_done,{skip_case_SUITE,exit_in_init,
+ {auto_skipped,{failed,'_'}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_case_SUITE,exit_in_init,
+ {tc_auto_skip,{failed,'_'}},
+ []]}},
+ {?eh,test_stats,{0,0,{5,6}}},
+
+ %% Fail in end_per_testcase -> all hooks shall be called and
+ %% test shall succeed.
+ {?eh,tc_start,{skip_case_SUITE,fail_in_end}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [skip_case_SUITE,fail_in_end,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [skip_case_SUITE,fail_in_end,
+ '$proplist',
+ ok,
+ []]}},
+ {?eh,cth,{empty_cth,pre_end_per_testcase,
+ [skip_case_SUITE,fail_in_end,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [skip_case_SUITE,fail_in_end,
+ '$proplist',
+ {failed,
+ {skip_case_SUITE,end_per_testcase,
+ {'EXIT',
+ {test_case_failed,"Failed in end_per_testcase/2"}}}},
+ []]}},
+ {?eh,tc_done,{skip_case_SUITE,fail_in_end,
+ {failed,
+ {skip_case_SUITE,end_per_testcase,
+ {'EXIT',
+ {test_case_failed,"Failed in end_per_testcase/2"}}}}}},
+ {?eh,test_stats,{1,0,{5,6}}},
+
+ %% Exit in end_per_testcase -> all hooks shall be called and
+ %% test shall succeed.
+ {?eh,tc_start,{skip_case_SUITE,exit_in_end}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [skip_case_SUITE,exit_in_end,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [skip_case_SUITE,exit_in_end,
+ '$proplist',
+ ok,
+ []]}},
+ {?eh,cth,{empty_cth,pre_end_per_testcase,
+ [skip_case_SUITE,exit_in_end,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [skip_case_SUITE,exit_in_end,
+ '$proplist',
+ {failed,
+ {skip_case_SUITE,end_per_testcase,
+ {'EXIT',"Exit in end_per_testcase/2"}}},
+ []]}},
+ {?eh,tc_done,{skip_case_SUITE,exit_in_end,
+ {failed,
+ {skip_case_SUITE,end_per_testcase,
+ {'EXIT',"Exit in end_per_testcase/2"}}}}},
+ {?eh,test_stats,{2,0,{5,6}}},
+
+ %% Skip in testcase function -> all callbacks shall be called
+ {?eh,tc_start,{skip_case_SUITE,skip_in_case}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [skip_case_SUITE,skip_in_case,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [skip_case_SUITE,skip_in_case,
+ '$proplist',
+ ok,[]]}},
+ {?eh,cth,{empty_cth,pre_end_per_testcase,
+ [skip_case_SUITE,skip_in_case,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [skip_case_SUITE,skip_in_case,
+ '$proplist',
+ {skip,"Skipped in test case function"},
+ []]}},
+ {?eh,tc_done,{skip_case_SUITE,skip_in_case,
+ {skipped,"Skipped in test case function"}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_case_SUITE,skip_in_case,
+ {tc_user_skip,"Skipped in test case function"},
+ []]}},
+ {?eh,test_stats,{2,0,{6,6}}},
+
+ %% Auto skip due to failed 'require' -> only the on_tc_skip
+ %% callback shall be called
+ {?eh,tc_start,{skip_case_SUITE,req_auto_skip}},
+ {?eh,tc_done,{skip_case_SUITE,req_auto_skip,
+ {auto_skipped,{require_failed,{not_available,whatever}}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_case_SUITE,req_auto_skip,
+ {tc_auto_skip,{require_failed,{not_available,whatever}}},
+ []]}},
+ {?eh,test_stats,{2,0,{6,7}}},
+
+ %% Auto skip due to failed testcase/0 function -> only the
+ %% on_tc_skip callback shall be called
+ {?eh,tc_start,{skip_case_SUITE,fail_auto_skip}},
+ {?eh,tc_done,{skip_case_SUITE,fail_auto_skip,
+ {auto_skipped,{testcase0_failed,bad_return_value}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [skip_case_SUITE,fail_auto_skip,
+ {tc_auto_skip,{testcase0_failed,bad_return_value}},
+ []]}},
+ {?eh,test_stats,{2,0,{6,8}}},
+
+ {?eh,tc_start,{skip_case_SUITE,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,
+ [skip_case_SUITE,
+ '$proplist',
+ []]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,
+ [skip_case_SUITE,
+ '$proplist',
+ ok,[]]}},
+ {?eh,tc_done,{skip_case_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ],
+ %% Make sure no 'cth_error' events are received!
+ [{negative,{?eh,cth_error,'_'},E} || E <- Events];
+
+test_events(failed_sequence) ->
+ %% skip_cth.erl will send a 'cth_error' event if a hook is
+ %% erroneously called. Therefore, all Events are changed to
+ %% {negative,{?eh,cth_error,'_'},Event}
+ %% at the end of this function.
+ Events =
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,start_info,{1,1,2}},
+ {?eh,tc_start,{ct_framework,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,[seq_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [seq_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_framework,init_per_suite,ok}},
+ {?eh,tc_start,{seq_SUITE,test_case_1}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [seq_SUITE,test_case_1,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [seq_SUITE,test_case_1,'$proplist',ok,[]]}},
+ {?eh,cth,{empty_cth,pre_end_per_testcase,
+ [seq_SUITE,test_case_1,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [seq_SUITE,test_case_1,'$proplist',
+ {error,failed_on_purpose},[]]}},
+ {?eh,tc_done,{seq_SUITE,test_case_1,{failed,{error,failed_on_purpose}}}},
+ {?eh,cth,{empty_cth,on_tc_fail,
+ [seq_SUITE,test_case_1,failed_on_purpose,[]]}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_start,{seq_SUITE,test_case_2}},
+ {?eh,tc_done,{seq_SUITE,test_case_2,
+ {auto_skipped,{sequence_failed,seq1,test_case_1}}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [seq_SUITE,test_case_2,
+ {tc_auto_skip,{sequence_failed,seq1,test_case_1}},
+ []]}},
+ {?eh,test_stats,{0,1,{0,1}}},
+ {?eh,tc_start,{ct_framework,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,[seq_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,[seq_SUITE,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_framework,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ],
+ %% Make sure no 'cth_error' events are received!
+ [{negative,{?eh,cth_error,'_'},E} || E <- Events];
+
+test_events(repeat_force_stop) ->
+ %% skip_cth.erl will send a 'cth_error' event if a hook is
+ %% erroneously called. Therefore, all Events are changed to
+ %% {negative,{?eh,cth_error,'_'},Event}
+ %% at the end of this function.
+ Events=
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,start_info,{1,1,2}},
+ {?eh,tc_start,{ct_framework,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,[repeat_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [repeat_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_framework,init_per_suite,ok}},
+ {?eh,tc_start,{repeat_SUITE,test_case_1}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [repeat_SUITE,test_case_1,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_testcase,
+ [repeat_SUITE,test_case_1,'$proplist',ok,[]]}},
+ {?eh,cth,{empty_cth,pre_end_per_testcase,
+ [repeat_SUITE,test_case_1,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [repeat_SUITE,test_case_1,'$proplist',ok,[]]}},
+ {?eh,tc_done,{repeat_SUITE,test_case_1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{repeat_SUITE,test_case_2}},
+ {?eh,tc_done,{repeat_SUITE,test_case_2,
+ {auto_skipped,
+ "Repeated test stopped by force_stop option"}}},
+ {?eh,cth,{empty_cth,on_tc_skip,
+ [repeat_SUITE,test_case_2,
+ {tc_auto_skip,"Repeated test stopped by force_stop option"},
+ []]}},
+ {?eh,test_stats,{1,0,{0,1}}},
+ {?eh,tc_start,{ct_framework,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,[repeat_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,
+ [repeat_SUITE,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_framework,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ],
+ %% Make sure no 'cth_error' events are received!
+ [{negative,{?eh,cth_error,'_'},E} || E <- Events];
+
+test_events(config_clash) ->
+ %% skip_cth.erl will send a 'cth_error' event if a hook is
+ %% erroneously called. Therefore, all Events are changed to
+ %% {negative,{?eh,cth_error,'_'},Event}
+ %% at the end of this function.
+ Events =
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{ct_framework,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [config_clash_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [config_clash_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_framework,init_per_suite,ok}},
+ {?eh,tc_start,{config_clash_SUITE,test_case_1}},
+ {?eh,tc_done,{config_clash_SUITE,test_case_1,
+ {failed,{error,{config_name_already_in_use,[aa]}}}}},
+ {?eh,cth,{empty_cth,on_tc_fail,
+ [config_clash_SUITE,test_case_1,
+ {config_name_already_in_use,[aa]},
+ []]}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_start,{ct_framework,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,
+ [config_clash_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,
+ [config_clash_SUITE,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_framework,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ],
+ %% Make sure no 'cth_error' events are received!
+ [{negative,{?eh,cth_error,'_'},E} || E <- Events];
+
test_events(ok) ->
ok.
-
%% test events help functions
contains(List) ->
fun(Proplist) when is_list(Proplist) ->
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_hook_callbacks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_hook_callbacks_SUITE.erl
new file mode 100644
index 0000000000..af8a85cd75
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_hook_callbacks_SUITE.erl
@@ -0,0 +1,62 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(all_hook_callbacks_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(Config) ->
+ Config.
+
+end_per_group(_Config) ->
+ ok.
+
+init_per_testcase(skip_case, Config) ->
+ {skip,"Skipped in init_per_testcase/2"};
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [{group,test_group},test_case,skip_case].
+
+groups() ->
+ [{test_group,[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) ->
+ ok.
+
+skip_case(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/config_clash_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/config_clash_SUITE.erl
new file mode 100644
index 0000000000..3853dc29da
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/config_clash_SUITE.erl
@@ -0,0 +1,43 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(config_clash_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+suite() ->
+ [{require,aa,yy},{default_config,yy,"this is a default value"}].
+
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(_,_) ->
+ ok.
+
+all() ->
+ [test_case_1].
+
+%% Test cases starts here.
+test_case_1() ->
+ [{require,aa,xx}].
+test_case_1(_Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_end_config_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_end_config_SUITE.erl
new file mode 100644
index 0000000000..0b6cabead3
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_end_config_SUITE.erl
@@ -0,0 +1,51 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(ct_no_end_config_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+%%% This suite is used to verify that all pre/post_end_per_* callbacks
+%%% are called with correct SuiteName even if no end_per_* config
+%%% function exist in the suite, and that the non-exported config
+%%% functions fail with 'undef'.
+
+init_per_suite(Config) ->
+ Config.
+
+init_per_group(_Group,Config) ->
+ Config.
+
+init_per_testcase(_TC,Config) ->
+ Config.
+
+all() ->
+ [test_case_1, {group,test_group}].
+
+groups() ->
+ [{test_group,[],[test_case_2]}].
+
+test_case_1(Config) ->
+ ok.
+
+test_case_2(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_init_config_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_init_config_SUITE.erl
new file mode 100644
index 0000000000..29cdc21ee4
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_init_config_SUITE.erl
@@ -0,0 +1,54 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(ct_no_init_config_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+%%% This suite is used to verify that all
+%%% pre/post_init_per_group/testcase callbacks are called with correct
+%%% SuiteName even if no init_per_group/testcase function exist in the
+%%% suite, and that the non-exported config functions fail with 'undef'.
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+end_per_group(_Group,Config) ->
+ Config.
+
+end_per_testcase(_TC,Config) ->
+ Config.
+
+all() ->
+ [test_case_1, {group,test_group}].
+
+groups() ->
+ [{test_group,[],[test_case_2]}].
+
+test_case_1(Config) ->
+ ok.
+
+test_case_2(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_init_suite_config_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_init_suite_config_SUITE.erl
new file mode 100644
index 0000000000..d6daff9792
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_init_suite_config_SUITE.erl
@@ -0,0 +1,39 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(ct_no_init_suite_config_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+%%% This suite is used to verify that pre/post_init_per_suite
+%%% callbacks are called with correct SuiteName even if no
+%%% init_per_suite function exist in the suite, and that the
+%%% non-exported config function fails with 'undef'.
+
+end_per_suite(Config) ->
+ Config.
+
+all() ->
+ [test_case].
+
+test_case(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 c00eb5cf93..961ea68d2d 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
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -44,18 +44,18 @@
-export([pre_end_per_suite/3]).
-export([post_end_per_suite/4]).
--export([pre_init_per_group/3]).
--export([post_init_per_group/4]).
--export([pre_end_per_group/3]).
--export([post_end_per_group/4]).
+-export([pre_init_per_group/4]).
+-export([post_init_per_group/5]).
+-export([pre_end_per_group/4]).
+-export([post_end_per_group/5]).
--export([pre_init_per_testcase/3]).
--export([post_init_per_testcase/4]).
--export([pre_end_per_testcase/3]).
--export([post_end_per_testcase/4]).
+-export([pre_init_per_testcase/4]).
+-export([post_init_per_testcase/5]).
+-export([pre_end_per_testcase/4]).
+-export([post_end_per_testcase/5]).
--export([on_tc_fail/3]).
--export([on_tc_skip/3]).
+-export([on_tc_fail/4]).
+-export([on_tc_skip/4]).
-export([terminate/1]).
@@ -154,150 +154,160 @@ post_end_per_suite(Suite,Config,Return,State) ->
%% @doc Called before each init_per_group.
%% You can change the config in this function.
--spec pre_init_per_group(Group :: atom(),
- Config :: config(),
- State :: #state{}) ->
+-spec pre_init_per_group(Suite :: atom(),
+ Group :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
{config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_group(Group,Config,State) ->
+pre_init_per_group(Suite,Group,Config,State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, pre_init_per_group,
- [Group,Config,State]}}),
- ct:log("~w:pre_init_per_group(~w) called", [?MODULE,Group]),
+ [Suite,Group,Config,State]}}),
+ ct:log("~w:pre_init_per_group(~w,~w) called", [?MODULE,Suite,Group]),
{Config, State}.
%% @doc Called after each init_per_group.
%% You can change the return value in this function.
--spec post_init_per_group(Group :: atom(),
+-spec post_init_per_group(Suite :: atom(),
+ Group :: atom(),
Config :: config(),
Return :: config() | skip_or_fail(),
State :: #state{}) ->
{config() | skip_or_fail(), NewState :: #state{}}.
-post_init_per_group(Group,Config,Return,State) ->
+post_init_per_group(Suite,Group,Config,Return,State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, post_init_per_group,
- [Group,Config,Return,State]}}),
- ct:log("~w:post_init_per_group(~w) called", [?MODULE,Group]),
+ [Suite,Group,Config,Return,State]}}),
+ ct:log("~w:post_init_per_group(~w,~w) called", [?MODULE,Suite,Group]),
{Return, State}.
%% @doc Called after each end_per_group. The config/state can be changed here,
%% though it will only affect the *end_per_group functions.
--spec pre_end_per_group(Group :: atom(),
+-spec pre_end_per_group(Suite :: atom(),
+ Group :: atom(),
Config :: config() | skip_or_fail(),
State :: #state{}) ->
{ok | skip_or_fail(), NewState :: #state{}}.
-pre_end_per_group(Group,Config,State) ->
+pre_end_per_group(Suite,Group,Config,State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, pre_end_per_group,
- [Group,Config,State]}}),
- ct:log("~w:pre_end_per_group(~w) called", [?MODULE,Group]),
+ [Suite,Group,Config,State]}}),
+ ct:log("~w:pre_end_per_group(~w~w) called", [?MODULE,Suite,Group]),
{Config, State}.
%% @doc Called after each end_per_group. Note that the config cannot be
%% changed here, only the status of the group.
--spec post_end_per_group(Group :: atom(),
+-spec post_end_per_group(Suite :: atom(),
+ Group :: atom(),
Config :: config(),
Return :: term(),
State :: #state{}) ->
{ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_group(Group,Config,Return,State) ->
+post_end_per_group(Suite,Group,Config,Return,State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, post_end_per_group,
- [Group,Config,Return,State]}}),
- ct:log("~w:post_end_per_group(~w) called", [?MODULE,Group]),
+ [Suite,Group,Config,Return,State]}}),
+ ct:log("~w:post_end_per_group(~w,~w) called", [?MODULE,Suite,Group]),
{Return, State}.
%% @doc Called before init_per_testcase/2 for each test case.
%% You can change the config in this function.
--spec pre_init_per_testcase(TC :: atom(),
- Config :: config(),
- State :: #state{}) ->
+-spec pre_init_per_testcase(Suite :: atom(),
+ TC :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
{config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_testcase(TC,Config,State) ->
+pre_init_per_testcase(Suite,TC,Config,State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, pre_init_per_testcase,
- [TC,Config,State]}}),
- ct:log("~w:pre_init_per_testcase(~w) called", [?MODULE,TC]),
+ [Suite,TC,Config,State]}}),
+ ct:log("~w:pre_init_per_testcase(~w,~w) called", [?MODULE,Suite,TC]),
{Config, State}.
%% @doc Called after init_per_testcase/2, and before the test case.
--spec post_init_per_testcase(TC :: atom(),
+-spec post_init_per_testcase(Suite :: atom(),
+ TC :: atom(),
Config :: config(),
Return :: config() | skip_or_fail(),
State :: #state{}) ->
{config() | skip_or_fail(), NewState :: #state{}}.
-post_init_per_testcase(TC,Config,Return,State) ->
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, post_init_per_testcase,
- [TC,Config,Return,State]}}),
- ct:log("~w:post_init_per_testcase(~w) called", [?MODULE,TC]),
+ [Suite,TC,Config,Return,State]}}),
+ ct:log("~w:post_init_per_testcase(~w,~w) called", [?MODULE,Suite,TC]),
{Return, State}.
%% @doc Called before end_per_testacse/2. No skip or fail allowed here,
%% only config additions.
--spec pre_end_per_testcase(TC :: atom(),
- Config :: config(),
- State :: #state{}) ->
+-spec pre_end_per_testcase(Suite :: atom(),
+ TC :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
{config(), NewState :: #state{}}.
-pre_end_per_testcase(TC,Config,State) ->
+pre_end_per_testcase(Suite,TC,Config,State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, pre_end_per_testcase,
- [TC,Config,State]}}),
- ct:log("~w:pre_end_per_testcase(~w) called", [?MODULE,TC]),
+ [Suite,TC,Config,State]}}),
+ ct:log("~w:pre_end_per_testcase(~w,~w) called", [?MODULE,Suite,TC]),
{Config, State}.
%% @doc Called after end_per_testcase/2 for each test case. Note that
%% the config cannot be changed here, only the status of the test case.
--spec post_end_per_testcase(TC :: atom(),
+-spec post_end_per_testcase(Suite :: atom(),
+ TC :: atom(),
Config :: config(),
Return :: term(),
State :: #state{}) ->
{ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_testcase(TC,Config,Return,State) ->
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, post_end_per_testcase,
- [TC,Config,Return,State]}}),
- ct:log("~w:post_end_per_testcase(~w) called", [?MODULE,TC]),
+ [Suite,TC,Config,Return,State]}}),
+ ct:log("~w:post_end_per_testcase(~w,~w) called", [?MODULE,Suite,TC]),
{Return, State}.
%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
%% post_end_per_group and post_end_per_tc if the suite, group or test case failed.
%% This function should be used for extra cleanup which might be needed.
%% It is not possible to modify the config or the status of the test run.
--spec on_tc_fail(TC :: init_per_suite | end_per_suite |
+-spec on_tc_fail(Suite :: atom(),
+ TC :: init_per_suite | end_per_suite |
init_per_group | end_per_group | atom() |
{Function :: atom(), GroupName :: atom()},
Reason :: term(), State :: #state{}) -> NewState :: #state{}.
-on_tc_fail(TC, Reason, State) ->
+on_tc_fail(Suite, TC, Reason, State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, on_tc_fail,
- [TC,Reason,State]}}),
- ct:log("~w:on_tc_fail(~w) called", [?MODULE,TC]),
+ [Suite,TC,Reason,State]}}),
+ ct:log("~w:on_tc_fail(~w,~w) called", [?MODULE,Suite,TC]),
State.
%% @doc Called when a test case is skipped by either user action
%% or due to an init function failing. Test case can be
%% end_per_suite, init_per_group, end_per_group and the actual test cases.
--spec on_tc_skip(TC :: end_per_suite |
+-spec on_tc_skip(Suite :: atom(),
+ TC :: end_per_suite |
init_per_group | end_per_group | atom() |
{Function :: atom(), GroupName :: atom()},
{tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} |
{tc_user_skip, {skipped, Reason :: term()}},
State :: #state{}) -> NewState :: #state{}.
-on_tc_skip(TC, Reason, State) ->
+on_tc_skip(Suite, TC, Reason, State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, on_tc_skip,
- [TC,Reason,State]}}),
- ct:log("~w:on_tc_skip(~w) called", [?MODULE,TC]),
+ [Suite,TC,Reason,State]}}),
+ ct:log("~w:on_tc_skip(~w,~w) called", [?MODULE,Suite,TC]),
State.
%% @doc Called when the scope of the CTH is done, this depends on
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl
index 559b22bc9f..7cb2d19414 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -45,29 +45,29 @@ pre_end_per_suite(Suite,Config,State) ->
post_end_per_suite(Suite,Config,Return,State) ->
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State).
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State).
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State).
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State).
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State).
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl
index 51202443bf..ebe6014127 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -45,35 +45,35 @@ pre_end_per_suite(Suite,Config,State) ->
post_end_per_suite(Suite,Config,Return,State) ->
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State).
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State).
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State).
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State).
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State).
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State).
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State).
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fallback_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fallback_cth.erl
new file mode 100644
index 0000000000..665efd659a
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fallback_cth.erl
@@ -0,0 +1,81 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(fallback_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+id(Opts) ->
+ empty_cth:id(Opts).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(fallback_nosuite,Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(fallback_nosuite,Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(fallback_nosuite,Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(fallback_nosuite,Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(fallback_nosuite,TC,Config,State).
+
+post_init_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(fallback_nosuite,TC,Config,Return,State).
+
+pre_end_per_testcase(TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(fallback_nosuite,TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(fallback_nosuite,TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(fallback_nosuite,TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(fallback_nosuite,TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl
index b49cbe7fb4..f5d215c69d 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -29,13 +29,13 @@
%% CT Hooks
-export([init/2]).
-export([terminate/1]).
--export([on_tc_skip/3]).
+-export([on_tc_skip/4]).
init(Id, Opts) ->
empty_cth:init(Id, Opts).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite, TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/prio_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/prio_cth.erl
index a687743641..d462e5e071 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/prio_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/prio_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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,35 +47,35 @@ pre_end_per_suite(Suite,Config,State) ->
post_end_per_suite(Suite,Config,Return,State) ->
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State).
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State).
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State).
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State).
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State).
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State).
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State).
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl
index 4d9c60f1ca..f67d152cf1 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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,35 +47,35 @@ pre_end_per_suite(Suite,Config,State) ->
post_end_per_suite(Suite,Config,Return,State) ->
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State).
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State).
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State).
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State).
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State).
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State).
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State).
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/repeat_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/repeat_SUITE.erl
new file mode 100644
index 0000000000..a1849839c9
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/repeat_SUITE.erl
@@ -0,0 +1,42 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(repeat_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(_,_) ->
+ ok.
+
+all() ->
+ [test_case_1, test_case_2].
+
+%% Test cases starts here.
+test_case_1(_Config) ->
+ timer:sleep(10000),
+ ok.
+
+test_case_2(_Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl
index 494f398fc1..a4105f5829 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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,35 +48,35 @@ pre_end_per_suite(Suite,Config,State) ->
post_end_per_suite(Suite,Config,Return,State) ->
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State).
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State).
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State).
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State).
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State).
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State).
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State).
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/seq_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/seq_SUITE.erl
new file mode 100644
index 0000000000..30eebb272a
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/seq_SUITE.erl
@@ -0,0 +1,45 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(seq_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(_,_) ->
+ ok.
+
+all() ->
+ [{sequence,seq1}].
+
+sequences() ->
+ [{seq1,[test_case_1,test_case_2]}].
+
+%% Test cases starts here.
+test_case_1(_Config) ->
+ exit(failed_on_purpose).
+
+test_case_2(_Config) ->
+ ct:fail("This test shall never be run since test_case_1 fails "
+ "and they are run in sequence").
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip.spec b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip.spec
new file mode 100644
index 0000000000..a271c5e8b2
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip.spec
@@ -0,0 +1,8 @@
+{suites,".",[all_hook_callbacks_SUITE,
+ skip_init_SUITE,
+ skip_req_SUITE,
+ skip_fail_SUITE,
+ skip_group_SUITE,
+ skip_case_SUITE]}.
+{skip_suites,".",all_hook_callbacks_SUITE,"Skipped in spec"}.
+{skip_cases,".",skip_case_SUITE,skip_in_spec,"Skipped in spec"}.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_case_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_case_SUITE.erl
new file mode 100644
index 0000000000..3c2275d55f
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_case_SUITE.erl
@@ -0,0 +1,106 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(skip_case_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+suite() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ ok.
+
+init_per_group(_,Config) ->
+ Config.
+
+end_per_group(_,_) ->
+ ok.
+
+init_per_testcase(skip_in_init,Config) ->
+ {skip,"Skipped in init_per_testcase/2"};
+init_per_testcase(fail_in_init,Config) ->
+ ct:fail("Failed in init_per_testcase/2");
+init_per_testcase(exit_in_init,Config) ->
+ exit(self(),"Exit in init_per_testcase/2");
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(fail_in_end,_) ->
+ ct:fail("Failed in end_per_testcase/2");
+end_per_testcase(exit_in_end,_) ->
+ exit(self(),"Exit in end_per_testcase/2");
+end_per_testcase(_,_) ->
+ ok.
+
+all() ->
+ [skip_in_spec,
+ skip_in_init,
+ fail_in_init,
+ exit_in_init,
+ fail_in_end,
+ exit_in_end,
+ skip_in_case,
+ req_auto_skip,
+ fail_auto_skip
+ ].
+
+%% Test cases starts here.
+skip_in_spec(Config) ->
+ ct:fail("This test shall never be run. "
+ "It shall be skipped in the test spec.").
+
+skip_in_init(Config) ->
+ ct:fail("This test shall never be run. "
+ "It shall be skipped in init_per_testcase/2.").
+
+fail_in_init(Config) ->
+ ct:fail("This test shall never be run. "
+ "It shall fail in init_per_testcase/2.").
+
+exit_in_init(Config) ->
+ ct:fail("This test shall never be run. "
+ "It shall exit in init_per_testcase/2.").
+
+fail_in_end(Config) ->
+ ok.
+
+exit_in_end(Config) ->
+ ok.
+
+skip_in_case(Config) ->
+ {skip,"Skipped in test case function"}.
+
+req_auto_skip() ->
+ [{require,whatever}].
+req_auto_skip(Config) ->
+ ct:fail("This test shall never be run due to "
+ "failed require").
+
+fail_auto_skip() ->
+ faulty_return_value.
+fail_auto_skip(Config) ->
+ ct:fail("This test shall never be run due to "
+ "faulty return from info function").
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_cth.erl
new file mode 100644
index 0000000000..7b5e93f836
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_cth.erl
@@ -0,0 +1,182 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(skip_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+%% Send a cth_error event if a callback is called with unexpected arguments
+-define(fail(Info),
+ gen_event:notify(
+ ?CT_EVMGR_REF,
+ #event{ name = cth_error,
+ node = node(),
+ data = {illegal_hook_callback,{?MODULE,?FUNCTION_NAME,Info}}})).
+
+%% CT Hooks
+-compile(export_all).
+
+id(Opts) ->
+ empty_cth:id(Opts).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ Suite==skip_init_SUITE
+ orelse Suite==skip_group_SUITE
+ orelse Suite==skip_case_SUITE
+ orelse Suite==seq_SUITE
+ orelse Suite==repeat_SUITE
+ orelse Suite==config_clash_SUITE
+ orelse ?fail(Suite),
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ Suite==skip_init_SUITE
+ orelse Suite==skip_group_SUITE
+ orelse Suite==skip_case_SUITE
+ orelse Suite==seq_SUITE
+ orelse Suite==repeat_SUITE
+ orelse Suite==config_clash_SUITE
+ orelse ?fail(Suite),
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ Suite==skip_case_SUITE
+ orelse Suite==skip_group_SUITE
+ orelse Suite==seq_SUITE
+ orelse Suite==repeat_SUITE
+ orelse Suite==config_clash_SUITE
+ orelse ?fail(Suite),
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ Suite==skip_case_SUITE
+ orelse Suite==skip_group_SUITE
+ orelse Suite==seq_SUITE
+ orelse Suite==repeat_SUITE
+ orelse Suite==config_clash_SUITE
+ orelse ?fail(Suite),
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Suite,Group,Config,State) ->
+ (Suite==skip_group_SUITE andalso Group==test_group_3)
+ orelse ?fail({Suite,Group}),
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
+
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ (Suite==skip_group_SUITE andalso Group==test_group_3)
+ orelse ?fail({Suite,Group}),
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
+
+pre_end_per_group(Suite,Group,Config,State) ->
+ ?fail({Suite,Group}),
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
+
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ ?fail({Suite,Group}),
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
+
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ (Suite==skip_case_SUITE andalso (TC==skip_in_init
+ orelse TC==fail_in_init
+ orelse TC==exit_in_init
+ orelse TC==fail_in_end
+ orelse TC==exit_in_end
+ orelse TC==skip_in_case))
+ orelse (Suite==seq_SUITE andalso TC==test_case_1)
+ orelse (Suite==repeat_SUITE andalso TC==test_case_1)
+ orelse ?fail({Suite,TC}),
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
+
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ (Suite==skip_case_SUITE andalso (TC==skip_in_init
+ orelse TC==fail_in_init
+ orelse TC==exit_in_init
+ orelse TC==fail_in_end
+ orelse TC==exit_in_end
+ orelse TC==skip_in_case))
+ orelse (Suite==seq_SUITE andalso TC==test_case_1)
+ orelse (Suite==repeat_SUITE andalso TC==test_case_1)
+ orelse ?fail({Suite,TC}),
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
+
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ (Suite==skip_case_SUITE andalso (TC==skip_in_case
+ orelse TC==fail_in_end
+ orelse TC==exit_in_end))
+ orelse (Suite==seq_SUITE andalso TC==test_case_1)
+ orelse (Suite==repeat_SUITE andalso TC==test_case_1)
+ orelse ?fail({Suite,TC}),
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
+
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ (Suite==skip_case_SUITE andalso (TC==skip_in_case
+ orelse TC==fail_in_end
+ orelse TC==exit_in_end))
+ orelse (Suite==seq_SUITE andalso TC==test_case_1)
+ orelse (Suite==repeat_SUITE andalso TC==test_case_1)
+ orelse ?fail({Suite,TC}),
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
+
+on_tc_fail(Suite,TC,Reason,State) ->
+ (Suite==seq_SUITE andalso TC==test_case_1)
+ orelse (Suite==config_clash_SUITE andalso TC==test_case_1)
+ orelse ?fail({Suite,TC}),
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
+
+on_tc_skip(all_hook_callbacks_SUITE=Suite,all=TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State);
+on_tc_skip(Suite,TC,Reason,State)
+ when (Suite==skip_init_SUITE
+ orelse Suite==skip_req_SUITE
+ orelse Suite==skip_fail_SUITE)
+ andalso
+ (TC==init_per_suite
+ orelse TC==test_case
+ orelse TC==end_per_suite) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State);
+on_tc_skip(skip_group_SUITE=Suite,TC={C,G},Reason,State)
+ when (C==init_per_group orelse C==test_case orelse C==end_per_group) andalso
+ (G==test_group_1 orelse G==test_group_2 orelse G==test_group_3) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State);
+on_tc_skip(skip_case_SUITE=Suite,TC,Reason,State)
+ when TC==skip_in_spec;
+ TC==skip_in_init;
+ TC==fail_in_init;
+ TC==exit_in_init;
+ TC==skip_in_case;
+ TC==req_auto_skip;
+ TC==fail_auto_skip ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State);
+on_tc_skip(Suite,TC,Reason,State)
+ when (Suite==seq_SUITE andalso TC==test_case_2)
+ orelse (Suite==repeat_SUITE andalso TC==test_case_2) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State);
+on_tc_skip(Suite,TC,Reason,State) ->
+ ?fail({Suite,TC}),
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_fail_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_fail_SUITE.erl
new file mode 100644
index 0000000000..df8835598e
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_fail_SUITE.erl
@@ -0,0 +1,53 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(skip_fail_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+suite() ->
+ faulty_return_value.
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ ok.
+
+init_per_group(_,Config) ->
+ Config.
+
+end_per_group(_,_) ->
+ ok.
+
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(_,_) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_group_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_group_SUITE.erl
new file mode 100644
index 0000000000..8d25701a22
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_group_SUITE.erl
@@ -0,0 +1,64 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(skip_group_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+suite() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ ok.
+
+group(test_group_1) ->
+ [{require,whatever}];
+group(test_group_2) ->
+ faulty_return_value;
+group(_) ->
+ [].
+
+init_per_group(test_group_3,Config) ->
+ {skip,"Skipped in init_per_group/2"};
+init_per_group(_,Config) ->
+ ct:fail("This shall never be run due to auto_skip from group/1").
+
+end_per_group(_,_) ->
+ ct:fail("This shall never be run").
+
+all() ->
+ [{group,test_group_1},
+ {group,test_group_2},
+ {group,test_group_3}].
+
+groups() ->
+ [{test_group_1,[test_case]},
+ {test_group_2,[test_case]},
+ {test_group_3,[test_case]}].
+
+%% Test cases starts here.
+test_case(_Config) ->
+ ct:fail("This test case shall never be run due to skip on group level").
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_init_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_init_SUITE.erl
new file mode 100644
index 0000000000..7d01ff58a9
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_init_SUITE.erl
@@ -0,0 +1,53 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(skip_init_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+suite() ->
+ [].
+
+init_per_suite(Config) ->
+ {skip,"Skipped in init_per_suite/1"}.
+
+end_per_suite(Config) ->
+ ok.
+
+init_per_group(_,Config) ->
+ Config.
+
+end_per_group(_,_) ->
+ ok.
+
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(_,_) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl
index d5b347e723..b3b5db23fb 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -45,35 +45,35 @@ pre_end_per_suite(Suite,Config,State) ->
post_end_per_suite(Suite,Config,Return,State) ->
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State).
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State).
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State).
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State).
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State).
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State).
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State).
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_end_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_end_cth.erl
index 36abac0bf8..525c376830 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_end_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_end_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -46,36 +46,36 @@ pre_end_per_suite(Suite,Config,State) ->
post_end_per_suite(Suite,Config,Return,State) ->
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State).
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State).
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State),
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State),
{{skip, "Test skip"}, State}.
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State).
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State).
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State).
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State).
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_init_tc_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_init_tc_cth.erl
new file mode 100644
index 0000000000..459608ac07
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_init_tc_cth.erl
@@ -0,0 +1,79 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(skip_pre_init_tc_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
+
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
+
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
+
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
+
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State),
+ {{skip, "Skipped in pre_init_per_testcase"}, State}.
+
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
+
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
+
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
+
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
+
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl
index fa510b2d54..1e6cab04ed 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -46,35 +46,35 @@ pre_end_per_suite(Suite,Config,State) ->
post_end_per_suite(Suite,Config,Return,State) ->
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State).
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State).
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State).
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State).
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State).
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State).
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State).
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_req_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_req_SUITE.erl
new file mode 100644
index 0000000000..c6e67d978e
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_req_SUITE.erl
@@ -0,0 +1,53 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2017. 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(skip_req_SUITE).
+
+-compile(export_all).
+
+-include("ct.hrl").
+
+suite() ->
+ [{require,whatever}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ ok.
+
+init_per_group(_,Config) ->
+ Config.
+
+end_per_group(_,_) ->
+ ok.
+
+init_per_testcase(_,Config) ->
+ Config.
+
+end_per_testcase(_,_) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl
index 7ec0d458b6..2b37116cb2 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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,44 +48,44 @@ post_end_per_suite(Suite,Config,Return,State) ->
empty_cth:post_end_per_suite(Suite,Config,Return,State),
{Return, [post_end_per_suite|State]}.
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State),
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State),
{Config, [pre_init_per_group|State]}.
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State),
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State),
{Return, [post_init_per_group|State]}.
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State),
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State),
{Config, [pre_end_per_group|State]}.
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State),
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State),
{Return, [post_end_per_group|State]}.
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State),
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State),
{Config, [pre_init_per_testcase|State]}.
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State),
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State),
{Return, [post_init_per_testcase|State]}.
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State),
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State),
{Config, [pre_end_per_testcase|State]}.
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State),
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State),
{Return, [post_end_per_testcase|State]}.
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State),
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State),
[on_tc_fail|State].
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State),
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State),
[on_tc_skip|State].
terminate(State) ->
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl
index 2b9e726819..ac7b74539a 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -44,35 +44,35 @@ pre_end_per_suite(Suite,Config,State) ->
post_end_per_suite(Suite,Config,Return,State) ->
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State).
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State).
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State).
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State).
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State).
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State).
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State).
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
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 d48981f667..7b0c1f599f 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
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -50,43 +50,43 @@ post_end_per_suite(Suite,Config,Return,State) ->
NewConfig = [{post_end_per_suite,?now}|Config],
{NewConfig,NewConfig}.
-pre_init_per_group(Group,Config,State) ->
- empty_cth:pre_init_per_group(Group,Config,State),
+pre_init_per_group(Suite, Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State),
{[{pre_init_per_group,?now}|Config],State}.
-post_init_per_group(Group,Config,Return,State) ->
- empty_cth:post_init_per_group(Group,Config,Return,State),
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State),
{[{post_init_per_group,?now}|Return],State}.
-pre_end_per_group(Group,Config,State) ->
- empty_cth:pre_end_per_group(Group,Config,State),
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State),
{[{pre_end_per_group,?now}|Config],State}.
-post_end_per_group(Group,Config,Return,State) ->
- empty_cth:post_end_per_group(Group,Config,Return,State),
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State),
{[{post_end_per_group,?now}|Config],State}.
-pre_init_per_testcase(TC,Config,State) ->
- empty_cth:pre_init_per_testcase(TC,Config,State),
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State),
{[{pre_init_per_testcase,?now}|Config],State}.
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State),
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State),
{[{post_init_per_testcase,?now}|Config],State}.
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State),
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State),
{[{pre_end_per_testcase,?now}|Config],State}.
-post_end_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_end_per_testcase(TC,Config,Return,State),
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State),
{[{post_end_per_testcase,?now}|Config],State}.
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl
index 71d84781e0..44e7c7c29d 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -60,37 +60,37 @@ post_end_per_suite(Suite,Config,Return,State) ->
ct_no_config_SUITE = ct:get_config(suite_cfg),
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
+pre_init_per_group(Suite,Group,Config,State) ->
true = ?val(post_init_per_suite, Config),
ct_no_config_SUITE = ct:get_config(suite_cfg),
test_group = ct:get_config(group_cfg),
- empty_cth:pre_init_per_group(Group,
+ empty_cth:pre_init_per_group(Suite,Group,
[{pre_init_per_group,true} | Config],
State).
-post_init_per_group(Group,Config,Return,State) ->
+post_init_per_group(Suite,Group,Config,Return,State) ->
true = ?val(pre_init_per_group, Return),
test_group = ct:get_config(group_cfg),
- empty_cth:post_init_per_group(Group,
+ empty_cth:post_init_per_group(Suite,Group,
Config,
[{post_init_per_group,true} | Return],
State).
-pre_end_per_group(Group,Config,State) ->
+pre_end_per_group(Suite,Group,Config,State) ->
true = ?val(post_init_per_group, Config),
ct_no_config_SUITE = ct:get_config(suite_cfg),
test_group = ct:get_config(group_cfg),
- empty_cth:pre_end_per_group(Group,
+ empty_cth:pre_end_per_group(Suite,Group,
[{pre_end_per_group,true} | Config],
State).
-post_end_per_group(Group,Config,Return,State) ->
+post_end_per_group(Suite,Group,Config,Return,State) ->
true = ?val(pre_end_per_group, Config),
ct_no_config_SUITE = ct:get_config(suite_cfg),
test_group = ct:get_config(group_cfg),
- empty_cth:post_end_per_group(Group,Config,Return,State).
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
+pre_init_per_testcase(Suite,TC,Config,State) ->
true = ?val(post_init_per_suite, Config),
case ?val(name, ?val(tc_group_properties, Config)) of
undefined ->
@@ -102,19 +102,19 @@ pre_init_per_testcase(TC,Config,State) ->
ct_no_config_SUITE = ct:get_config(suite_cfg),
CfgKey = list_to_atom(atom_to_list(TC) ++ "_cfg"),
TC = ct:get_config(CfgKey),
- empty_cth:pre_init_per_testcase(TC,
+ empty_cth:pre_init_per_testcase(Suite,TC,
[{pre_init_per_testcase,true} | Config],
State).
%%! TODO: Verify Config also in post_init and pre_end!
-post_init_per_testcase(TC,Config,Return,State) ->
- empty_cth:post_init_per_testcase(TC,Config,Return,State).
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
-pre_end_per_testcase(TC,Config,State) ->
- empty_cth:pre_end_per_testcase(TC,Config,State).
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
true = ?val(post_init_per_suite, Config),
true = ?val(pre_init_per_testcase, Config),
case ?val(name, ?val(tc_group_properties, Config)) of
@@ -127,13 +127,13 @@ post_end_per_testcase(TC,Config,Return,State) ->
ct_no_config_SUITE = ct:get_config(suite_cfg),
CfgKey = list_to_atom(atom_to_list(TC) ++ "_cfg"),
TC = ct:get_config(CfgKey),
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl
index 9abd2e5e83..bde6744433 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -62,43 +62,43 @@ post_end_per_suite(Suite,Config,Return,State) ->
check_dirs(State,Config),
empty_cth:post_end_per_suite(Suite,Config,Return,State).
-pre_init_per_group(Group,Config,State) ->
+pre_init_per_group(Suite,Group,Config,State) ->
check_dirs(State,Config),
- empty_cth:pre_init_per_group(Group,Config,State).
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
-post_init_per_group(Group,Config,Return,State) ->
+post_init_per_group(Suite,Group,Config,Return,State) ->
check_dirs(State,Return),
- empty_cth:post_init_per_group(Group,Config,Return,State).
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
-pre_end_per_group(Group,Config,State) ->
+pre_end_per_group(Suite,Group,Config,State) ->
check_dirs(State,Config),
- empty_cth:pre_end_per_group(Group,Config,State).
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
-post_end_per_group(Group,Config,Return,State) ->
+post_end_per_group(Suite,Group,Config,Return,State) ->
check_dirs(State,Config),
- empty_cth:post_end_per_group(Group,Config,Return,State).
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
-pre_init_per_testcase(TC,Config,State) ->
+pre_init_per_testcase(Suite,TC,Config,State) ->
check_dirs(State,Config),
- empty_cth:pre_init_per_testcase(TC,Config,State).
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
-post_init_per_testcase(TC,Config,Return,State) ->
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
check_dirs(State,Config),
- empty_cth:post_init_per_testcase(TC,Config,Return,State).
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
-pre_end_per_testcase(TC,Config,State) ->
+pre_end_per_testcase(Suite,TC,Config,State) ->
check_dirs(State,Config),
- empty_cth:pre_end_per_testcase(TC,Config,State).
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
-post_end_per_testcase(TC,Config,Return,State) ->
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
check_dirs(State,Config),
- empty_cth:post_end_per_testcase(TC,Config,Return,State).
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
-on_tc_fail(TC, Reason, State) ->
- empty_cth:on_tc_fail(TC,Reason,State).
+on_tc_fail(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
-on_tc_skip(TC, Reason, State) ->
- empty_cth:on_tc_skip(TC,Reason,State).
+on_tc_skip(Suite,TC, Reason, State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
terminate(State) ->
empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_log_SUITE.erl b/lib/common_test/test/ct_log_SUITE.erl
index 9bdd44cbdf..93affda398 100644
--- a/lib/common_test/test/ct_log_SUITE.erl
+++ b/lib/common_test/test/ct_log_SUITE.erl
@@ -86,18 +86,7 @@ print(Config) ->
io:format("5. Printing a pid: ~w~n", [Pid]),
io:format("6. Printing HTML: <pre>~s</pre>~n", [String]),
- %% --- API ---
- %% pal(Format) ->
- %% = ct:pal(default, 50, Format, []).
- %% pal(X1, X2) -> ok
- %% X1 = Category | Importance | Format
- %% X2 = Format | FormatArgs
- %% pal(X1, X2, X3) -> ok
- %% X1 = Category | Importance
- %% X2 = Importance | Format
- %% X3 = Format | FormatArgs
- %% pal(Category, Importance, Format, FormatArgs) -> ok
- %% ------
+ %% ct:pal
ct:pal("1. Printing nothing"),
ct:pal("2. Printing nothing", []),
ct:pal("3. Printing a string: ~s", [String]),
@@ -111,23 +100,16 @@ print(Config) ->
ct:pal(50, "11. Printing with ~s", ["importance"]),
ct:pal(ct_internal, 50, "12. Printing with ~s", ["category and importance"]),
- %% --- API ---
- %% log(Format) -> ok
- %% = ct:log(default, 50, Format, [], []).
- %% log(X1, X2) -> ok
- %% X1 = Category | Importance | Format
- %% X2 = Format | FormatArgs
- %% log(X1, X2, X3) -> ok
- %% X1 = Category | Importance
- %% X2 = Importance | Format
- %% X3 = Format | FormatArgs | Opts
- %% log(X1, X2, X3, X4) -> ok
- %% X1 = Category | Importance
- %% X2 = Importance | Format
- %% X3 = Format | FormatArgs
- %% X4 = FormatArgs | Opts
- %% log(Category, Importance, Format, FormatArgs, Opts) -> ok
- %% ------
+ ct:pal("13. Printing with heading", [],
+ [{heading,"This is a heading"}]),
+ ct:pal(ct_internal, "14. Printing with category and heading", [],
+ [{heading,"This is a heading"}]),
+ ct:pal(50, "15. Printing with importance and heading", [],
+ [{heading,"This is a heading"}]),
+ ct:pal(ct_internal, 50, "16. Printing with category, importance and heading", [],
+ [{heading,"This is a heading"}]),
+
+ %% ct:log
ct:log("1. Printing nothing"),
ct:log("2. Printing nothing", []),
ct:log("3. Printing a string: ~s", [String]),
@@ -153,8 +135,37 @@ print(Config) ->
ct:log(ct_internal, 50, "21. Printing a pid escaped with ~s, no_css: ~w",
["category and importance",Pid], [esc_chars,no_css]),
+ ct:log("22. Printing with heading", [],
+ [{heading,"This is a heading"}]),
+ ct:log(ct_internal, "23. Printing with category and heading", [],
+ [{heading,"This is a heading"}]),
+ ct:log(50, "24. Printing with importance and heading", [],
+ [{heading,"This is a heading"}]),
+ ct:log(ct_internal, 50, "25. Printing with category, importance and heading", [],
+ [{heading,"This is a heading"}]),
+
%% END mark
ct:log("LOGGING END", [], [no_css]),
+
+
+ %% ct:print
+ ct:print("1. Does this show??"),
+ ct:print("2. Does this ~s", ["show??"]),
+ ct:print("3. Is this a non-html pid?? ~w", [self()]),
+ ct:print(ct_internal, "4. Printing with category"),
+ ct:print(ct_internal, "5. Printing with ~s", ["category"]),
+ ct:print(50, "6. Printing with importance"),
+ ct:print(50, "7. Printing with ~s", ["importance"]),
+ ct:print(ct_internal, 50, "8. Printing with ~s", ["category and importance"]),
+ ct:print("9. Printing with heading", [],
+ [{heading,"This is a heading"}]),
+ ct:print(ct_internal, "10. Printing with category and heading", [],
+ [{heading,"This is a heading"}]),
+ ct:print(50, "11. Printing with importance and heading", [],
+ [{heading,"This is a heading"}]),
+ ct:print(ct_internal, 50, "12. Printing with category, importance and heading", [],
+ [{heading,"This is a heading"}]),
+
{save_config,[{the_logfile,TcLogFile},{the_pid,Pid},{the_string,String}]}.
@@ -169,6 +180,8 @@ verify(Config) ->
{ok,Dev} = file:open(TcLogFile, [read]),
ok = read_until(Dev, "LOGGING START\n"),
+ ct:pal("VERIFYING LOG ENTRIES...", []),
+
%% io:format
match_line(Dev, "1. Printing nothing", []),
read_nl(Dev),
@@ -182,6 +195,7 @@ verify(Config) ->
read_nl(Dev),
match_line(Dev, "6. Printing HTML: &lt;pre&gt;~s&lt;/pre&gt;", [String]),
read_nl(Dev),
+
%% ct:pal
read_header(Dev),
match_line(Dev, "1. Printing nothing", []),
@@ -219,6 +233,19 @@ verify(Config) ->
read_header(Dev, "\"ct_internal\""),
match_line(Dev, "12. Printing with ~s", ["category and importance"]),
read_footer(Dev),
+ read_header(Dev, "\"default\"", "This is a heading"),
+ match_line(Dev, "13. Printing with heading", []),
+ read_footer(Dev),
+ read_header(Dev, "\"ct_internal\"", "This is a heading"),
+ match_line(Dev, "14. Printing with category and heading", []),
+ read_footer(Dev),
+ read_header(Dev, "\"default\"", "This is a heading"),
+ match_line(Dev, "15. Printing with importance and heading", []),
+ read_footer(Dev),
+ read_header(Dev, "\"ct_internal\"", "This is a heading"),
+ match_line(Dev, "16. Printing with category, importance and heading", []),
+ read_footer(Dev),
+
%% ct:log
read_header(Dev),
match_line(Dev, "1. Printing nothing", []),
@@ -275,7 +302,18 @@ verify(Config) ->
read_footer(Dev),
match_line(Dev, "21. Printing a pid escaped with ~s, no_css: ~s",
["category and importance",EscPid]),
-
+ read_header(Dev, "\"default\"", "This is a heading"),
+ match_line(Dev, "22. Printing with heading", []),
+ read_footer(Dev),
+ read_header(Dev, "\"ct_internal\"", "This is a heading"),
+ match_line(Dev, "23. Printing with category and heading", []),
+ read_footer(Dev),
+ read_header(Dev, "\"default\"", "This is a heading"),
+ match_line(Dev, "24. Printing with importance and heading", []),
+ read_footer(Dev),
+ read_header(Dev, "\"ct_internal\"", "This is a heading"),
+ match_line(Dev, "25. Printing with category, importance and heading", []),
+ read_footer(Dev),
file:close(Dev),
ok.
@@ -298,29 +336,51 @@ read_until(Dev, Pat) ->
match_line(Dev, Format, Args) ->
Pat = lists:flatten(io_lib:format(Format, Args)),
Line = element(2, file:read_line(Dev)),
+
+ %% for debugging purposes:
+ ct:pal("L: ~tp", [Line], [no_css]),
+
case re:run(Line, Pat) of
{match,_} ->
ok;
nomatch ->
- ct:pal("ERROR! No match for ~p.\nLine = ~p", [Pat,Line]),
+ ct:pal("ERROR! No match for ~p", [Pat]),
file:close(Dev),
ct:fail({mismatch,Pat,Line})
end.
read_header(Dev) ->
- read_header(Dev, "\"default\"").
+ read_header(Dev, "\"default\"", "User").
read_header(Dev, Cat) ->
+ read_header(Dev, Cat, "User").
+
+read_header(Dev, Cat, Heading) ->
file:read_line(Dev), % \n
"</pre>\n" = element(2, file:read_line(Dev)),
- {match,_} =
- re:run(element(2, file:read_line(Dev)), "<div class="++Cat++"><pre><b>"
- "\\*\\*\\* User \\d{4}-\\d{2}-\\d{2} "
- "\\d{2}:\\d{2}:\\d{2}.\\d{1,} \\*\\*\\*</b>").
+ {ok,Hd} = file:read_line(Dev),
+
+ %% for debugging purposes:
+ ct:pal("H: ~tp", [Hd], [no_css]),
+
+ Pat = "<div class="++Cat++"><pre><b>"++
+ "\\*\\*\\* "++Heading++" \\d{4}-\\d{2}-\\d{2} "++
+ "\\d{2}:\\d{2}:\\d{2}.\\d{1,} \\*\\*\\*</b>",
+
+ case re:run(Hd, Pat) of
+ {match,_} ->
+ ok;
+ _ ->
+ ct:pal("ERROR! No match for ~p", [Pat]),
+ file:close(Dev),
+ ct:fail({mismatch,Pat,Hd})
+ end.
read_footer(Dev) ->
"</pre></div>\n" = element(2, file:read_line(Dev)),
- "<pre>\n" = element(2, file:read_line(Dev)).
+ "<pre>\n" = element(2, file:read_line(Dev)),
+ %% for debugging purposes:
+ ct:pal("F: </pre></div><pre>", [], [no_css]).
read_nl(Dev) ->
file:read_line(Dev).
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 a65275da43..f2580ad8e9 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
@@ -83,7 +83,9 @@ end_per_suite(Config) ->
%% Running the netconf server in a remote node, test that the client
%% process terminates if the remote node goes down.
remote_crash(Config) ->
- {ok,Node} = ct_slave:start(nc_remote_crash),
+ {ok,Node} = ct_slave:start(nc_remote_crash,[{boot_timeout,15},
+ {init_timeout,15},
+ {startup_timeout,15}]),
Pa = filename:dirname(code:which(?NS)),
true = rpc:call(Node,code,add_patha,[Pa]),
rpc:call(Node,code,load_file,[crypto]),
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
index e62bc617fa..2412ea6aba 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
@@ -302,10 +302,14 @@ table_trans(Fun,Args) ->
S ->
apply(Fun,Args);
Pid ->
+ Ref = erlang:monitor(process,Pid),
Pid ! {table_trans,Fun,Args,self()},
receive
{table_trans_done,Result} ->
- Result
+ erlang:demonitor(Ref,[flush]),
+ Result;
+ {'DOWN',Ref,process,Pid,Reason} ->
+ exit({main_ns_proc_died,Reason})
after 20000 ->
exit(table_trans_timeout)
end
diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
index f8b6a379f6..9382e4c011 100644
--- a/lib/common_test/test/ct_repeat_testrun_SUITE.erl
+++ b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. 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.
@@ -363,14 +363,17 @@ skip_first_tc1(Suite) ->
{?eh,tc_start,{Suite,tc1}},
{?eh,tc_done,{Suite,tc1,ok}},
{?eh,test_stats,{'_',0,{0,0}}},
+ {?eh,tc_start,{Suite,tc2}},
{?eh,tc_done,{Suite,tc2,?skipped}},
{?eh,test_stats,{'_',0,{0,1}}},
+ {?eh,tc_start,{Suite,{init_per_group,g,[]}}},
{?eh,tc_done,{Suite,{init_per_group,g,[]},?skipped}},
{?eh,tc_auto_skip,{Suite,{tc1,g},?skip_reason}},
{?eh,test_stats,{'_',0,{0,2}}},
{?eh,tc_auto_skip,{Suite,{tc2,g},?skip_reason}},
{?eh,test_stats,{'_',0,{0,3}}},
{?eh,tc_auto_skip,{Suite,{end_per_group,g},?skip_reason}},
+ {?eh,tc_start,{Suite,tc2}},
{?eh,tc_done,{Suite,tc2,?skipped}},
{?eh,test_stats,{'_',0,{0,4}}},
{?eh,tc_start,{Suite,end_per_suite}},
@@ -390,10 +393,12 @@ skip_tc1_in_group(Suite) ->
{?eh,tc_start,{Suite,tc1}},
{?eh,tc_done,{Suite,tc1,ok}},
{?eh,test_stats,{'_',0,{0,0}}},
+ {?eh,tc_start,{Suite,tc2}},
{?eh,tc_done,{Suite,tc2,?skipped}},
{?eh,test_stats,{'_',0,{0,1}}},
{?eh,tc_start,{Suite,{end_per_group,g,[]}}},
{?eh,tc_done,{Suite,{end_per_group,g,[]},ok}}],
+ {?eh,tc_start,{Suite,tc2}},
{?eh,tc_done,{Suite,tc2,?skipped}},
{?eh,test_stats,{'_',0,{0,2}}},
{?eh,tc_start,{Suite,end_per_suite}},
diff --git a/lib/common_test/test/ct_surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE.erl
index 42ec685c16..832e105517 100644
--- a/lib/common_test/test/ct_surefire_SUITE.erl
+++ b/lib/common_test/test/ct_surefire_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2017. 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.
@@ -73,7 +73,9 @@ all() ->
relative_path,
url,
logdir,
- fail_pre_init_per_suite
+ fail_pre_init_per_suite,
+ skip_case_in_spec,
+ skip_suite_in_spec
].
%%--------------------------------------------------------------------
@@ -119,6 +121,18 @@ fail_pre_init_per_suite(Config) when is_list(Config) ->
run(fail_pre_init_per_suite,[fail_pre_init_per_suite,
{cth_surefire,[{path,Path}]}],Path,Config,[],Suites).
+skip_case_in_spec(Config) ->
+ DataDir = ?config(data_dir,Config),
+ Spec = filename:join(DataDir,"skip_one_case.spec"),
+ Path = "skip_case_in_spec.xml",
+ run_spec(skip_case_in_spec,[{cth_surefire,[{path,Path}]}],Path,Config,Spec).
+
+skip_suite_in_spec(Config) ->
+ DataDir = ?config(data_dir,Config),
+ Spec = filename:join(DataDir,"skip_one_suite.spec"),
+ Path = "skip_suite_in_spec.xml",
+ run_spec(skip_suite_in_spec,[{cth_surefire,[{path,Path}]}],Path,Config,Spec).
+
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -129,8 +143,15 @@ run(Case,CTHs,Report,Config,ExtraOpts) ->
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),
+ Test = [{suite,Suite},{ct_hooks,CTHs},{label,Case}|ExtraOpts],
+ do_run(Case, Report, Test, Config).
+
+run_spec(Case,CTHs,Report,Config,Spec) ->
+ Test = [{spec,Spec},{ct_hooks,CTHs},{label,Case}],
+ do_run(Case, Report, Test, Config).
+
+do_run(Case, Report, Test, Config) ->
+ {Opts,ERPid} = setup(Test, Config),
ok = execute(Case, Opts, ERPid, Config),
LogDir =
case lists:keyfind(logdir,1,Opts) of
@@ -201,7 +222,10 @@ test_suite_events(pass_SUITE) ->
{?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(_) ->
+test_suite_events(skip_all_surefire_SUITE) ->
+ [{?eh,tc_user_skip,{skip_all_surefire_SUITE,all,"skipped in spec"}},
+ {?eh,test_stats,{0,0,{1,0}}}];
+test_suite_events(Test) ->
[{?eh,tc_start,{surefire_SUITE,init_per_suite}},
{?eh,tc_done,{surefire_SUITE,init_per_suite,ok}},
{?eh,tc_start,{surefire_SUITE,tc_ok}},
@@ -210,46 +234,55 @@ test_suite_events(_) ->
{?eh,tc_start,{surefire_SUITE,tc_fail}},
{?eh,tc_done,{surefire_SUITE,tc_fail,
{failed,{error,{test_case_failed,"this test should fail"}}}}},
- {?eh,test_stats,{1,1,{0,0}}},
- {?eh,tc_start,{surefire_SUITE,tc_skip}},
- {?eh,tc_done,{surefire_SUITE,tc_skip,{skipped,"this test is skipped"}}},
- {?eh,test_stats,{1,1,{1,0}}},
- {?eh,tc_start,{surefire_SUITE,tc_autoskip_require}},
- {?eh,tc_done,{surefire_SUITE,tc_autoskip_require,
- {auto_skipped,{require_failed,'_'}}}},
- {?eh,test_stats,{1,1,{1,1}}},
- [{?eh,tc_start,{surefire_SUITE,{init_per_group,g,[]}}},
- {?eh,tc_done,{surefire_SUITE,{init_per_group,g,[]},ok}},
- {?eh,tc_start,{surefire_SUITE,tc_ok}},
- {?eh,tc_done,{surefire_SUITE,tc_ok,ok}},
- {?eh,test_stats,{2,1,{1,1}}},
- {?eh,tc_start,{surefire_SUITE,tc_fail}},
- {?eh,tc_done,{surefire_SUITE,tc_fail,
- {failed,{error,{test_case_failed,"this test should fail"}}}}},
- {?eh,test_stats,{2,2,{1,1}}},
- {?eh,tc_start,{surefire_SUITE,tc_skip}},
- {?eh,tc_done,{surefire_SUITE,tc_skip,{skipped,"this test is skipped"}}},
- {?eh,test_stats,{2,2,{2,1}}},
- {?eh,tc_start,{surefire_SUITE,tc_autoskip_require}},
- {?eh,tc_done,{surefire_SUITE,tc_autoskip_require,
- {auto_skipped,{require_failed,'_'}}}},
- {?eh,test_stats,{2,2,{2,2}}},
- {?eh,tc_start,{surefire_SUITE,{end_per_group,g,[]}}},
- {?eh,tc_done,{surefire_SUITE,{end_per_group,g,[]},ok}}],
- [{?eh,tc_start,{surefire_SUITE,{init_per_group,g_fail,[]}}},
- {?eh,tc_done,{surefire_SUITE,{init_per_group,g_fail,[]},
- {failed,{error,all_cases_should_be_skipped}}}},
- {?eh,tc_auto_skip,{surefire_SUITE,{tc_ok,g_fail},
- {failed,
- {surefire_SUITE,init_per_group,
- {'EXIT',all_cases_should_be_skipped}}}}},
- {?eh,test_stats,{2,2,{2,3}}},
- {?eh,tc_auto_skip,{surefire_SUITE,{end_per_group,g_fail},
- {failed,
- {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,test_stats,{1,1,{0,0}}}] ++
+ tc_skip_events(Test,undefined) ++
+ [{?eh,test_stats,{1,1,{1,0}}},
+ {?eh,tc_start,{surefire_SUITE,tc_autoskip_require}},
+ {?eh,tc_done,{surefire_SUITE,tc_autoskip_require,
+ {auto_skipped,{require_failed,'_'}}}},
+ {?eh,test_stats,{1,1,{1,1}}},
+ [{?eh,tc_start,{surefire_SUITE,{init_per_group,g,[]}}},
+ {?eh,tc_done,{surefire_SUITE,{init_per_group,g,[]},ok}},
+ {?eh,tc_start,{surefire_SUITE,tc_ok}},
+ {?eh,tc_done,{surefire_SUITE,tc_ok,ok}},
+ {?eh,test_stats,{2,1,{1,1}}},
+ {?eh,tc_start,{surefire_SUITE,tc_fail}},
+ {?eh,tc_done,{surefire_SUITE,tc_fail,
+ {failed,{error,{test_case_failed,"this test should fail"}}}}},
+ {?eh,test_stats,{2,2,{1,1}}}] ++
+ tc_skip_events(Test,g) ++
+ [{?eh,test_stats,{2,2,{2,1}}},
+ {?eh,tc_start,{surefire_SUITE,tc_autoskip_require}},
+ {?eh,tc_done,{surefire_SUITE,tc_autoskip_require,
+ {auto_skipped,{require_failed,'_'}}}},
+ {?eh,test_stats,{2,2,{2,2}}},
+ {?eh,tc_start,{surefire_SUITE,{end_per_group,g,[]}}},
+ {?eh,tc_done,{surefire_SUITE,{end_per_group,g,[]},ok}}],
+ [{?eh,tc_start,{surefire_SUITE,{init_per_group,g_fail,[]}}},
+ {?eh,tc_done,{surefire_SUITE,{init_per_group,g_fail,[]},
+ {failed,{error,all_cases_should_be_skipped}}}},
+ {?eh,tc_auto_skip,{surefire_SUITE,{tc_ok,g_fail},
+ {failed,
+ {surefire_SUITE,init_per_group,
+ {'EXIT',all_cases_should_be_skipped}}}}},
+ {?eh,test_stats,{2,2,{2,3}}},
+ {?eh,tc_auto_skip,{surefire_SUITE,{end_per_group,g_fail},
+ {failed,
+ {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}}].
+
+tc_skip_events(skip_case_in_spec,Group) ->
+ [{?eh,tc_user_skip,{surefire_SUITE,tc_skip_name(Group),"skipped in spec"}}];
+tc_skip_events(_Test,_Group) ->
+ [{?eh,tc_start,{surefire_SUITE,tc_skip}},
+ {?eh,tc_done,{surefire_SUITE,tc_skip,{skipped,"this test is skipped"}}}].
+
+tc_skip_name(undefined) ->
+ tc_skip;
+tc_skip_name(Group) ->
+ {tc_skip,Group}.
test_events(fail_pre_init_per_suite) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
@@ -257,6 +290,10 @@ test_events(fail_pre_init_per_suite) ->
test_suite_events(pass_SUITE) ++
test_suite_events(fail_SUITE, {1,0,{0,1}}) ++
[{?eh,stop_logging,[]}];
+test_events(skip_suite_in_spec) ->
+ [{?eh,start_logging,'_'},{?eh,start_info,{1,1,0}}] ++
+ test_suite_events(skip_all_surefire_SUITE) ++
+ [{?eh,stop_logging,[]}];
test_events(Test) ->
[{?eh,start_logging,'_'}, {?eh,start_info,{1,1,9}}] ++
test_suite_events(Test) ++
@@ -364,6 +401,8 @@ failed_or_skipped([]) ->
events_to_result(E) ->
events_to_result(E, []).
+events_to_result([{?eh,tc_user_skip,{_Suite,all,_}}|E], Result) ->
+ events_to_result(E, [[[s]]|Result]);
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]);
@@ -382,7 +421,7 @@ events_to_result1([{?eh,tc_done,{_Suite, end_per_suite,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]) ->
+events_to_result1([{?eh,Skip,_}|E]) when Skip==tc_auto_skip; Skip==tc_user_skip ->
{Suite,Rest} = events_to_result1(E),
{[[s]|Suite],Rest};
events_to_result1([_|E]) ->
diff --git a/lib/common_test/test/ct_surefire_SUITE_data/skip_one_case.spec b/lib/common_test/test/ct_surefire_SUITE_data/skip_one_case.spec
new file mode 100644
index 0000000000..42df8a7d1a
--- /dev/null
+++ b/lib/common_test/test/ct_surefire_SUITE_data/skip_one_case.spec
@@ -0,0 +1,2 @@
+{suites,".",surefire_SUITE}.
+{skip_cases,".",surefire_SUITE,tc_skip,"skipped in spec"}.
diff --git a/lib/common_test/test/ct_surefire_SUITE_data/skip_one_suite.spec b/lib/common_test/test/ct_surefire_SUITE_data/skip_one_suite.spec
new file mode 100644
index 0000000000..57966328ab
--- /dev/null
+++ b/lib/common_test/test/ct_surefire_SUITE_data/skip_one_suite.spec
@@ -0,0 +1,2 @@
+{suites,".",[skip_all_surefire_SUITE]}.
+{skip_suites,".",skip_all_surefire_SUITE,"skipped in spec"}.
diff --git a/lib/common_test/test/ct_test_server_if_1_SUITE.erl b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
index 228d900545..0455313669 100644
--- a/lib/common_test/test/ct_test_server_if_1_SUITE.erl
+++ b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2017. 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.
@@ -161,6 +161,7 @@ test_events(ts_if_1) ->
{?eh,tc_start,{ts_if_1_SUITE,tc4}},
{?eh,tc_done,{ts_if_1_SUITE,tc4,{failed,{error,failed_on_purpose}}}},
{?eh,test_stats,{1,2,{0,1}}},
+ {?eh,tc_start,{ts_if_1_SUITE,tc5}},
{?eh,tc_done,{ts_if_1_SUITE,tc5,{auto_skipped,{sequence_failed,seq1,tc4}}}},
{?eh,test_stats,{1,2,{0,2}}},
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index e926abd885..de4eb00686 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -765,23 +765,23 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
{Done,RemEvs2,length(RemEvs2)}
end;
%% end_per_group auto- or user skipped
- (TEv={TEH,AutoOrUserSkip,{M,end_per_group,R}}, {Done,RemEvs,_RemSize})
+ (TEv={TEH,AutoOrUserSkip,{M,{end_per_group,G},R}}, {Done,RemEvs,_RemSize})
when AutoOrUserSkip == tc_auto_skip;
AutoOrUserSkip == tc_user_skip ->
RemEvs1 =
lists:dropwhile(
fun({EH,#event{name=tc_auto_skip,
node=EvNode,
- data={Mod,end_per_group,Reason}}}) when
- EH == TEH, EvNode == Node, Mod == M ->
+ data={Mod,{end_per_group,EvGroupName},Reason}}}) when
+ EH == TEH, EvNode == Node, Mod == M, EvGroupName == G ->
case match_data(R, Reason) of
match -> false;
_ -> true
end;
({EH,#event{name=tc_user_skip,
node=EvNode,
- data={Mod,end_per_group,Reason}}}) when
- EH == TEH, EvNode == Node, Mod == M ->
+ data={Mod,{end_per_group,EvGroupName},Reason}}}) when
+ EH == TEH, EvNode == Node, Mod == M, EvGroupName == G ->
case match_data(R, Reason) of
match -> false;
_ -> true
@@ -1008,20 +1008,20 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
{Done,RemEvs2,length(RemEvs2)}
end;
%% end_per_group auto-or user skipped
- (TEv={TEH,AutoOrUserSkip,{M,end_per_group,R}}, {Done,RemEvs,_RemSize})
+ (TEv={TEH,AutoOrUserSkip,{M,{end_per_group,G},R}}, {Done,RemEvs,_RemSize})
when AutoOrUserSkip == tc_auto_skip;
AutoOrUserSkip == tc_user_skip ->
RemEvs1 =
lists:dropwhile(
fun({EH,#event{name=tc_auto_skip,
node=EvNode,
- data={Mod,end_per_group,Reason}}}) when
- EH == TEH, EvNode == Node, Mod == M, Reason == R ->
+ data={Mod,{end_per_group,EvGroupName},Reason}}}) when
+ EH == TEH, EvNode == Node, Mod == M, EvGroupName == G, Reason == R ->
false;
({EH,#event{name=tc_user_skip,
node=EvNode,
- data={Mod,end_per_group,Reason}}}) when
- EH == TEH, EvNode == Node, Mod == M, Reason == R ->
+ data={Mod,{end_per_group,EvGroupName},Reason}}}) when
+ EH == TEH, EvNode == Node, Mod == M, EvGroupName == G, Reason == R ->
false;
({EH,#event{name=stop_logging,
node=EvNode,data=_}}) when
@@ -1264,10 +1264,10 @@ log_events1([E={_EH,tc_done,{_M,{end_per_group,_GrName,Props},_R}} | Evs], Dev,
io:format(Dev, "~s~p]},~n", [Ind,E]),
log_events1(Evs, Dev, Ind--" ")
end;
-log_events1([E={_EH,tc_auto_skip,{_M,end_per_group,_Reason}} | Evs], Dev, Ind) ->
+log_events1([E={_EH,tc_auto_skip,{_M,{end_per_group,_GrName},_Reason}} | Evs], Dev, Ind) ->
io:format(Dev, "~s~p],~n", [Ind,E]),
log_events1(Evs, Dev, Ind--" ");
-log_events1([E={_EH,tc_user_skip,{_M,end_per_group,_Reason}} | Evs], Dev, Ind) ->
+log_events1([E={_EH,tc_user_skip,{_M,{end_per_group,_GrName},_Reason}} | Evs], Dev, Ind) ->
io:format(Dev, "~s~p],~n", [Ind,E]),
log_events1(Evs, Dev, Ind--" ");
log_events1([E], Dev, Ind) ->
diff --git a/lib/common_test/test/ct_testspec_2_SUITE.erl b/lib/common_test/test/ct_testspec_2_SUITE.erl
index 1a941df185..1bab80942a 100644
--- a/lib/common_test/test/ct_testspec_2_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_2_SUITE.erl
@@ -220,7 +220,24 @@ basic_compatible_no_nodes(_Config) ->
{tc2,{skip,"skipped"}}]}]}],
merge_tests = true},
- verify_result(Verify,ListResult,FileResult).
+ verify_result(Verify,ListResult,FileResult),
+
+ {ok,Tests} = ct_testspec:get_tests([SpecFile]),
+ ct:pal("ct_testspec:get_tests/1:~n~p~n", [Tests]),
+ [{[SpecFile],[{Node,Run,Skip}]}] = Tests,
+ [{Alias1V,x_SUITE,all},
+ {Alias1V,y_SUITE,[{g1,all},{g2,all},tc1,tc2]},
+ {Alias1V,z_SUITE,all},
+ {Alias2V,x_SUITE,all},
+ {Alias2V,y_SUITE,all}] = lists:sort(Run),
+ [{Alias1V,z_SUITE,"skipped"},
+ {Alias2V,x_SUITE,{g1,all},"skipped"},
+ {Alias2V,x_SUITE,{g2,all},"skipped"},
+ {Alias2V,y_SUITE,tc1,"skipped"},
+ {Alias2V,y_SUITE,tc2,"skipped"}] = lists:sort(Skip),
+
+ ok.
+
%%%-----------------------------------------------------------------
%%%
@@ -346,7 +363,25 @@ basic_compatible_nodes(_Config) ->
{tc2,{skip,"skipped"}}]}]}],
merge_tests = true},
- verify_result(Verify,ListResult,FileResult).
+ verify_result(Verify,ListResult,FileResult),
+
+ {ok,Tests} = ct_testspec:get_tests([SpecFile]),
+ ct:pal("ct_testspec:get_tests/1:~n~p~n", [Tests]),
+ [{[SpecFile],[{Node,[],[]},
+ {Node1,Run1,Skip1},
+ {Node2,Run2,Skip2}]}] = Tests,
+ [{TO1V,x_SUITE,all},
+ {TO1V,y_SUITE,[{g1,all},{g2,all},tc1,tc2]},
+ {TO1V,z_SUITE,all}] = lists:sort(Run1),
+ [{TO2V,x_SUITE,all},
+ {TO2V,y_SUITE,all}] = lists:sort(Run2),
+ [{TO1V,z_SUITE,"skipped"}] = lists:sort(Skip1),
+ [{TO2V,x_SUITE,{g1,all},"skipped"},
+ {TO2V,x_SUITE,{g2,all},"skipped"},
+ {TO2V,y_SUITE,tc1,"skipped"},
+ {TO2V,y_SUITE,tc2,"skipped"}] = lists:sort(Skip2),
+
+ ok.
%%%-----------------------------------------------------------------
%%%
@@ -439,7 +474,28 @@ no_merging(_Config) ->
[{y_SUITE,[{tc1,{skip,"skipped"}},
{tc2,{skip,"skipped"}}]}]}]},
- verify_result(Verify,ListResult,FileResult).
+ verify_result(Verify,ListResult,FileResult),
+
+ {ok,Tests} = ct_testspec:get_tests([SpecFile]),
+ ct:pal("ct_testspec:get_tests/1:~n~p~n", [Tests]),
+ [{[SpecFile],[{Node,[],[]},
+ {Node1,Run1,Skip1},
+ {Node2,Run2,Skip2}]}] = Tests,
+ [{TO1V,x_SUITE,all},
+ {TO1V,y_SUITE,[tc1,tc2]},
+ {TO1V,y_SUITE,[{g1,all},{g2,all}]},
+ {TO1V,z_SUITE,all}] = lists:sort(Run1),
+ [{TO2V,x_SUITE,all},
+ {TO2V,x_SUITE,[{skipped,g1,all},{skipped,g2,all}]},
+ {TO2V,y_SUITE,all},
+ {TO2V,y_SUITE,[{skipped,tc1},{skipped,tc2}]}] = lists:sort(Run2),
+ [{TO1V,z_SUITE,"skipped"}] = lists:sort(Skip1),
+ [{TO2V,x_SUITE,{g1,all},"skipped"},
+ {TO2V,x_SUITE,{g2,all},"skipped"},
+ {TO2V,y_SUITE,tc1,"skipped"},
+ {TO2V,y_SUITE,tc2,"skipped"}] = lists:sort(Skip2),
+
+ ok.
%%%-----------------------------------------------------------------
%%%
@@ -510,7 +566,25 @@ multiple_specs(_Config) ->
{y_SUITE,[all,{tc1,{skip,"skipped"}},
{tc2,{skip,"skipped"}}]}]}]},
- verify_result(Verify,FileResult,FileResult).
+ verify_result(Verify,FileResult,FileResult),
+
+ {ok,Tests} = ct_testspec:get_tests([[SpecFile1,SpecFile2]]),
+ ct:pal("ct_testspec:get_tests/1:~n~p~n", [Tests]),
+ [{[SpecFile1,SpecFile2],[{Node,[],[]},
+ {Node1,Run1,Skip1},
+ {Node2,Run2,Skip2}]}] = Tests,
+ [{TO1V,x_SUITE,all},
+ {TO1V,y_SUITE,[{g1,all},{g2,all},tc1,tc2]},
+ {TO1V,z_SUITE,all}] = lists:sort(Run1),
+ [{TO2V,x_SUITE,all},
+ {TO2V,y_SUITE,all}] = lists:sort(Run2),
+ [{TO1V,z_SUITE,"skipped"}] = lists:sort(Skip1),
+ [{TO2V,x_SUITE,{g1,all},"skipped"},
+ {TO2V,x_SUITE,{g2,all},"skipped"},
+ {TO2V,y_SUITE,tc1,"skipped"},
+ {TO2V,y_SUITE,tc2,"skipped"}] = lists:sort(Skip2),
+
+ ok.
%%%-----------------------------------------------------------------
%%%
diff --git a/lib/common_test/test_server/ts_run.erl b/lib/common_test/test_server/ts_run.erl
index 66db1ff9a7..82ae44ec06 100644
--- a/lib/common_test/test_server/ts_run.erl
+++ b/lib/common_test/test_server/ts_run.erl
@@ -258,7 +258,7 @@ make_command(Vars, Spec, State) ->
run_batch(Vars, _Spec, State) ->
process_flag(trap_exit, true),
- Command = State#state.command ++ " -noinput -s erlang halt",
+ Command = State#state.command ++ " -noinput -eval \"erlang:halt(0,[{flush,false}]).\"",
ts_lib:progress(Vars, 1, "Command: ~ts~n", [Command]),
io:format(user, "Command: ~ts~n",[Command]),
Port = open_port({spawn, Command}, [stream, in, eof, exit_status]),
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index c68750886a..e6ae8b2e7a 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.12.2
+COMMON_TEST_VSN = 1.14
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index 61e214294e..9ac16af814 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2016</year>
+ <year>1996</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -136,7 +136,7 @@
(see
<seealso marker="erts:absform">The Abstract Format</seealso>
in ERTS User's Guide) in the compiled beam module. Tools
- such as <c>Debugger</c>, <c>Xref</c>, and <c>Cover</c> require
+ such as Debugger, Xref, and Cover require
the debug information to be included.</p>
<p><em>Warning</em>: Source code can be reconstructed from
@@ -544,7 +544,7 @@ module.beam: module.erl \
compiler to be deprecated. Notice that the compiler does not know
about attribute <c>-deprecated()</c>, but uses an
assembled list of deprecated functions in Erlang/OTP. To
- do a more general check, the <c>Xref</c> tool can be used.
+ do a more general check, the Xref tool can be used.
See also
<seealso marker="tools:xref#deprecated_function">xref(3)</seealso>
and the function
@@ -669,7 +669,7 @@ module.beam: module.erl \
<fsummary>Compiles a list of forms.</fsummary>
<desc>
<p>Is the same as
- <c>forms(File, [verbose,report_errors,report_warnings])</c>.
+ <c>forms(Forms, [verbose,report_errors,report_warnings])</c>.
</p>
</desc>
</func>
@@ -846,7 +846,7 @@ pi() -> 3.1416.
<section>
<title>Inlining of List Functions</title>
<p>The compiler can also inline various list manipulation functions
- from the module <c>list</c> in <c>STDLIB</c>.</p>
+ from the module <c>list</c> in STDLIB.</p>
<p>This feature must be explicitly enabled with a compiler option or a
<c>-compile()</c> attribute in the source module.</p>
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index e25cdc580c..db50cba094 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -32,6 +32,128 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 7.0.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fail labels on guard BIFs weren't taken into account
+ during an optimization pass, and a bug in the validation
+ pass sometimes prevented this from being noticed when a
+ fault occurred.</p>
+ <p>
+ Own Id: OTP-14522 Aux Id: ERIERL-48 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 7.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Minor internal changes. A typo in the documentation was
+ also fixed.</p>
+ <p>
+ Own Id: OTP-14240</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 7.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a compiler crash when maps were matched.</p>
+ <p>
+ Own Id: OTP-13931 Aux Id: ERL-266 </p>
+ </item>
+ <item>
+ <p>
+ Fixed a compiler crash having to with the delayed
+ sub-creation optimization. (Thanks to Jose Valim for
+ reporting this bug.)</p>
+ <p>
+ Own Id: OTP-13947 Aux Id: ERL-268 </p>
+ </item>
+ <item>
+ <p>The compiler option <c>inline_list_funcs</c>
+ accidentally turned off some other optimizations.</p>
+ <p>
+ Own Id: OTP-13985</p>
+ </item>
+ <item>
+ <p>The compiler could sometimes generate spurious
+ warnings when inlining was enabled.</p>
+ <p>
+ Own Id: OTP-14040 Aux Id: ERL-301 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 7.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If the compiler fails to write the BEAM file, it will now
+ report the reason of the error for the write operation.</p>
+ <p>
+ Own Id: OTP-13701</p>
+ </item>
+ <item>
+ <p>
+ Fixed an internal compiler error. (Thanks to Svilen
+ Ivanov for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-13780 Aux Id: ERL-202 </p>
+ </item>
+ <item>
+ <p>
+ The compiler could crash when trying to compile a
+ complicated expression with multiple catches all on one
+ line . (Thanks to Thomas Arts for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-13804 Aux Id: ERL-209 </p>
+ </item>
+ <item>
+ <p>
+ Eliminated a few internal compiler failures.</p>
+ <p>
+ Own Id: OTP-13863</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 7.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A literal binary matching regression was introduced in
+ 19.0 where a match could fail to resolve to the right
+ clause. This has now been fixed.</p>
+ <p>
+ Own Id: OTP-13738</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 7.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -263,7 +385,7 @@
<item>
<p>
The <c>cerl</c> and <c>cerl_trees</c> modules in the
- <c>compiler</c> application are now documented.</p>
+ Compiler application are now documented.</p>
<p>
Own Id: OTP-11978</p>
</item>
@@ -1965,7 +2087,7 @@
<c>RightExpr</c> or vice versa. The evaluation order is
only important if the expressions contains and/or depends
on operations with side-effects, such as message passing
- or <c>ets</c> operations.</p>
+ or ETS operations.</p>
<p>
Own Id: OTP-7206</p>
</item>
diff --git a/lib/compiler/doc/src/ref_man.xml b/lib/compiler/doc/src/ref_man.xml
index f5466553c0..c32c499008 100644
--- a/lib/compiler/doc/src/ref_man.xml
+++ b/lib/compiler/doc/src/ref_man.xml
@@ -30,7 +30,7 @@
<file>application.sgml</file>
</header>
<description>
- <p>The <c>Compiler</c> application compiles Erlang
+ <p>The Compiler application compiles Erlang
code to byte-code. The highly compact byte-code is executed by
the Erlang emulator.</p>
</description>
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 85d332c56e..6a35191f6e 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -58,13 +58,6 @@ blockify(Is) ->
blockify([{loop_rec,{f,Fail},{x,0}},{loop_rec_end,_Lbl},{label,Fail}|Is], Acc) ->
%% Useless instruction sequence.
blockify(Is, Acc);
-blockify([{get_map_elements,F,S,{list,Gets}}|Is0], Acc) ->
- %% A get_map_elements instruction is only safe at the beginning of
- %% a block because of the failure label.
- {Ss,Ds} = beam_utils:split_even(Gets),
- I = {set,Ds,[S|Ss],{get_map_elements,F}},
- {Block,Is} = collect_block(Is0, [I]),
- blockify(Is, [{block,Block}|Acc]);
blockify([I|Is0]=IsAll, Acc) ->
case collect(I) of
error -> blockify(Is0, [I|Acc]);
@@ -159,14 +152,43 @@ find_fixpoint(OptFun, Is0) ->
end.
%% move_allocates(Is0) -> Is
-%% Move allocate instructions upwards in the instruction stream, in the
-%% hope of getting more possibilities for optimizing away moves later.
+%% Move allocate instructions upwards in the instruction stream
+%% (within the same block), in the hope of getting more possibilities
+%% for optimizing away moves later.
%%
-%% NOTE: Moving allocation instructions is only safe because it is done
-%% immediately after code generation so that we KNOW that if {x,X} is
-%% initialized, all x registers with lower numbers are also initialized.
-%% That assumption may not be true after other optimizations, such as
-%% the beam_utils:live_opt/1 optimization.
+%% For example, we can transform the following instructions:
+%%
+%% get_tuple_element x(1) Element => x(2)
+%% allocate_zero StackSize 3 %% x(0), x(1), x(2) are live
+%%
+%% to the following instructions:
+%%
+%% allocate_zero StackSize 2 %% x(0) and x(1) are live
+%% get_tuple_element x(1) Element => x(2)
+%%
+%% NOTE: Since the beam_reorder pass has been run, it is no longer
+%% safe to assume that if x(N) is initialized, then all lower-numbered
+%% x registers are also initialized.
+%%
+%% For example, in general it is not safe to transform the following
+%% instructions:
+%%
+%% get_tuple_element x(0) Element => x(1)
+%% allocate_zero StackSize 3 %x(0), x(1), x(2) are live
+%%
+%% to the following instructions:
+%%
+%% allocate_zero StackSize 3
+%% get_tuple_element x(0) Element => x(1)
+%%
+%% The transformation is safe if and only if x(1) has been
+%% initialized previously. Unfortunately, beam_reorder may have moved
+%% a get_tuple_element instruction so that x(1) is not always
+%% initialized when this code is reached. To find whether or not x(1)
+%% is initialized, we would need to analyze all code preceding these
+%% two instructions (across branches). Since we currently don't have
+%% any practical mechanism for doing that, we will have to
+%% conservatively assume that the transformation is unsafe.
move_allocates([{block,Bl0}|Is]) ->
Bl = move_allocates_1(reverse(Bl0), []),
@@ -175,38 +197,26 @@ move_allocates([I|Is]) ->
[I|move_allocates(Is)];
move_allocates([]) -> [].
-move_allocates_1([{set,[],[],{alloc,_,_}=Alloc}|Is0], Acc0) ->
- {Is,Acc} = move_allocates_2(Alloc, Is0, Acc0),
- move_allocates_1(Is, Acc);
+move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info}}|Acc]=Acc0) ->
+ case {alloc_may_pass(I),alloc_live_regs(I, Live0)} of
+ {false,_} ->
+ move_allocates_1(Is, [I|Acc0]);
+ {true,not_possible} ->
+ move_allocates_1(Is, [I|Acc0]);
+ {true,Live} when is_integer(Live) ->
+ A = {set,[],[],{alloc,Live,Info}},
+ move_allocates_1(Is, [A,I|Acc])
+ end;
move_allocates_1([I|Is], Acc) ->
move_allocates_1(Is, [I|Acc]);
-move_allocates_1([], Is) -> Is.
-
-move_allocates_2({alloc,Live,Info}, [{set,[],[],{alloc,Live0,Info0}}|Is], Acc) ->
- Live = Live0, % Assertion.
- Alloc = {alloc,Live,combine_alloc(Info0, Info)},
- move_allocates_2(Alloc, Is, Acc);
-move_allocates_2({alloc,Live,Info}=Alloc0, [I|Is]=Is0, Acc) ->
- case alloc_may_pass(I) of
- false ->
- {Is0,[{set,[],[],Alloc0}|Acc]};
- true ->
- Alloc = {alloc,alloc_live_regs(I, Live),Info},
- move_allocates_2(Alloc, Is, [I|Acc])
- end;
-move_allocates_2(Alloc, [], Acc) ->
- {[],[{set,[],[],Alloc}|Acc]}.
+move_allocates_1([], Acc) -> Acc.
alloc_may_pass({set,_,_,{alloc,_,_}}) -> false;
alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false;
-alloc_may_pass({set,_,_,{get_map_elements,_}}) -> false;
alloc_may_pass({set,_,_,put_list}) -> false;
alloc_may_pass({set,_,_,put}) -> false;
alloc_may_pass({set,_,_,_}) -> true.
-combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
- {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
-
%% opt([Instruction]) -> [Instruction]
%% Optimize the instruction stream inside a basic block.
@@ -217,8 +227,6 @@ opt([{set,_,_,{line,_}}=Line1,
{set,[D2],[{integer,Idx2},Reg],{bif,element,{f,0}}}=I2|Is])
when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg ->
opt([Line2,I2,Line1,I1|Is]);
-opt([{set,[_|_],_Ss,{get_map_elements,_F}}=I|Is]) ->
- [I|opt(Is)];
opt([{set,Ds0,Ss,Op}|Is0]) ->
{Ds,Is} = opt_moves(Ds0, Is0),
[{set,Ds,Ss,Op}|opt(Is)];
@@ -393,10 +401,19 @@ eliminate_use_of_from_reg([I]=Is, From, _To, Acc) ->
%% opt_alloc(Instructions) -> Instructions'
%% Optimises all allocate instructions.
+opt_alloc([{set,[],[],{alloc,Live0,Info0}},
+ {set,[],[],{alloc,Live,Info}}|Is]) ->
+ Live = Live0, %Assertion.
+ Alloc = combine_alloc(Info0, Info),
+ I = {set,[],[],{alloc,Live,Alloc}},
+ opt_alloc([I|Is]);
opt_alloc([{set,[],[],{alloc,R,{_,Ns,Nh,[]}}}|Is]) ->
[{set,[],[],opt_alloc(Is, Ns, Nh, R)}|Is];
opt_alloc([I|Is]) -> [I|opt_alloc(Is)];
opt_alloc([]) -> [].
+
+combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
+ {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
%% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr]
%% Generates the optimal sequence of instructions for
@@ -445,13 +462,14 @@ count_ones(Bits, Acc) ->
alloc_live_regs({set,Ds,Ss,_}, Regs0) ->
Rset = x_live(Ss, x_dead(Ds, (1 bsl Regs0)-1)),
- live_regs(Rset).
+ live_regs(0, Rset).
-live_regs(Regs) ->
- live_regs_1(0, Regs).
-
-live_regs_1(N, 0) -> N;
-live_regs_1(N, Regs) -> live_regs_1(N+1, Regs bsr 1).
+live_regs(N, 0) ->
+ N;
+live_regs(N, Regs) when Regs band 1 =:= 1 ->
+ live_regs(N+1, Regs bsr 1);
+live_regs(_, _) ->
+ not_possible.
x_dead([{x,N}|Rs], Regs) -> x_dead(Rs, Regs band (bnot (1 bsl N)));
x_dead([_|Rs], Regs) -> x_dead(Rs, Regs);
diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl
index 286307a4be..ae1b34ba49 100644
--- a/lib/compiler/src/beam_bsm.erl
+++ b/lib/compiler/src/beam_bsm.erl
@@ -205,8 +205,15 @@ btb_reaches_match_1(Is, Regs, D) ->
btb_reaches_match_2([{block,Bl}|Is], Regs0, D) ->
Regs = btb_reaches_match_block(Bl, Regs0),
btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs, D) ->
- btb_call(Arity, Lbl, Regs, Is, D);
+btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs0, D) ->
+ case is_tail_call(Is) of
+ true ->
+ Regs1 = btb_kill_not_live(Arity, Regs0),
+ Regs = btb_kill_yregs(Regs1),
+ btb_tail_call(Lbl, Regs, D);
+ false ->
+ btb_call(Arity, Lbl, Regs0, Is, D)
+ end;
btb_reaches_match_2([{apply,Arity}|Is], Regs, D) ->
btb_call(Arity+2, apply, Regs, Is, D);
btb_reaches_match_2([{call_fun,Live}=I|Is], Regs, D) ->
@@ -360,6 +367,10 @@ btb_reaches_match_2([{line,_}|Is], Regs, D) ->
btb_reaches_match_2([I|_], Regs, _) ->
btb_error({btb_context_regs(Regs),I,not_handled}).
+is_tail_call([{deallocate,_}|_]) -> true;
+is_tail_call([return|_]) -> true;
+is_tail_call(_) -> false.
+
btb_call(Arity, Lbl, Regs0, Is, D0) ->
Regs = btb_kill_not_live(Arity, Regs0),
case btb_are_x_registers_empty(Regs) of
@@ -369,15 +380,15 @@ btb_call(Arity, Lbl, Regs0, Is, D0) ->
D = btb_tail_call(Lbl, Regs, D0),
%% No problem so far (the called function can handle a
- %% match context). Now we must make sure that the rest
- %% of this function following the call does not attempt
- %% to use the match context in case there is a copy
- %% tucked away in a y register.
+ %% match context). Now we must make sure that we don't
+ %% have any copies of the match context tucked away in an
+ %% y register.
RegList = btb_context_regs(Regs),
- YRegs = [R || {y,_}=R <- RegList],
- case btb_are_all_unused(YRegs, Is, D) of
- true -> D;
- false -> btb_error({multiple_uses,RegList})
+ case [R || {y,_}=R <- RegList] of
+ [] ->
+ D;
+ [_|_] ->
+ btb_error({multiple_uses,RegList})
end;
true ->
%% No match context in any x register. It could have been
diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl
index b01f58f683..6f6d742293 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -272,17 +272,14 @@ backward([{jump,{f,To}}=J|[{bif,Op,_,Ops,Reg}|Is]=Is0], D, Acc) ->
catch
throw:not_possible -> backward(Is0, D, [J|Acc])
end;
-backward([{test,bs_start_match2,F,Live,[R,_]=Args,Ctxt}|Is], D,
+backward([{test,bs_start_match2,F,_,[R,_],Ctxt}=I|Is], D,
[{test,bs_match_string,F,[Ctxt,Bs]},
{test,bs_test_tail2,F,[Ctxt,0]}|Acc0]=Acc) ->
- {f,To0} = F,
- To = shortcut_bs_start_match(To0, R, D),
case beam_utils:is_killed(Ctxt, Acc0, D) of
true ->
- Eq = {test,is_eq_exact,{f,To},[R,{literal,Bs}]},
+ Eq = {test,is_eq_exact,F,[R,{literal,Bs}]},
backward(Is, D, [Eq|Acc0]);
false ->
- I = {test,bs_start_match2,{f,To},Live,Args,Ctxt},
backward(Is, D, [I|Acc])
end;
backward([{test,bs_start_match2,{f,To0},Live,[Src|_]=Info,Dst}|Is], D, Acc) ->
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index 36369bd0b4..c9ff07b496 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -64,7 +64,6 @@ norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2};
norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) ->
{put_map,F,Op,S,D,R,{list,Puts}};
-%% get_map_elements is always handled in beam_split (moved out of block)
norm({set,[],[],remove_message}) -> remove_message;
norm({set,[],[],fclearerror}) -> fclearerror;
norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}}.
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 09cd3aa2d4..48b5a32814 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -167,12 +167,18 @@ share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->
end;
share_1([{func_info,_,_,_}=I|Is], _, [], Acc) ->
reverse(Is, [I|Acc]);
+share_1([{'catch',_,_}=I|Is], Dict0, Seq, Acc) ->
+ Dict = clean_non_sharable(Dict0),
+ share_1(Is, Dict, [I|Seq], Acc);
share_1([{'try',_,_}=I|Is], Dict0, Seq, Acc) ->
Dict = clean_non_sharable(Dict0),
share_1(Is, Dict, [I|Seq], Acc);
share_1([{try_case,_}=I|Is], Dict0, Seq, Acc) ->
Dict = clean_non_sharable(Dict0),
share_1(Is, Dict, [I|Seq], Acc);
+share_1([{catch_end,_}=I|Is], Dict0, Seq, Acc) ->
+ Dict = clean_non_sharable(Dict0),
+ share_1(Is, Dict, [I|Seq], Acc);
share_1([I|Is], Dict, Seq, Acc) ->
case is_unreachable_after(I) of
false ->
@@ -182,18 +188,18 @@ share_1([I|Is], Dict, Seq, Acc) ->
end.
clean_non_sharable(Dict) ->
- %% We are passing in or out of a 'try' block. Remove
- %% sequences that should not shared over the boundaries
- %% of a 'try' block. Since the end of the sequence must match,
- %% the only possible match between a sequence outside and
- %% a sequence inside the 'try' block is a sequence that ends
- %% with an instruction that causes an exception. Any sequence
- %% that causes an exception must contain a line/1 instruction.
+ %% We are passing in or out of a 'catch' or 'try' block. Remove
+ %% sequences that should not be shared over the boundaries of the
+ %% block. Since the end of the sequence must match, the only
+ %% possible match between a sequence outside and a sequence inside
+ %% the 'catch'/'try' block is a sequence that ends with an
+ %% instruction that causes an exception. Any sequence that causes
+ %% an exception must contain a line/1 instruction.
maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict).
sharable_with_try([{line,_}|_]) ->
%% This sequence may cause an exception and may potentially
- %% match a sequence on the other side of the 'try' block
+ %% match a sequence on the other side of the 'catch'/'try' block
%% boundary.
false;
sharable_with_try([_|Is]) ->
diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl
index c83c686953..feeab0af50 100644
--- a/lib/compiler/src/beam_split.erl
+++ b/lib/compiler/src/beam_split.erl
@@ -56,9 +56,6 @@ split_block([{set,[D],[S|Puts],{alloc,R,{put_map,Op,{f,Lbl}=Fail}}}|Is],
Bl, Acc) when Lbl =/= 0 ->
split_block(Is, [], [{put_map,Fail,Op,S,D,R,{list,Puts}}|
make_block(Bl, Acc)]);
-split_block([{set,Ds,[S|Ss],{get_map_elements,Fail}}|Is], Bl, Acc) ->
- Gets = beam_utils:join_even(Ss,Ds),
- split_block(Is, [], [{get_map_elements,Fail,S,{list,Gets}}|make_block(Bl, Acc)]);
split_block([{set,[R],[],{try_catch,Op,L}}|Is], Bl, Acc) ->
split_block(Is, [], [{Op,R,L}|make_block(Bl, Acc)]);
split_block([{set,[],[],{line,_}=Line}|Is], Bl, Acc) ->
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index a15ecf633e..e743abc773 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -26,7 +26,7 @@
empty_label_index/0,index_label/3,index_labels/1,
code_at/2,bif_to_test/3,is_pure_test/1,
live_opt/1,delete_live_annos/1,combine_heap_needs/2,
- join_even/2,split_even/1]).
+ split_even/1]).
-import(lists, [member/2,sort/1,reverse/1,splitwith/2]).
@@ -233,11 +233,6 @@ combine_heap_needs(H1, H2) when is_integer(H1), is_integer(H2) ->
split_even(Rs) -> split_even(Rs, [], []).
-%% join_even/1
-%% {[1,3,5],[2,4,6]} -> [1,2,3,4,5,6]
-
-join_even([], []) -> [];
-join_even([S|Ss], [D|Ds]) -> [S,D|join_even(Ss, Ds)].
%%%
%%% Local functions.
@@ -753,6 +748,11 @@ live_opt([timeout=I|Is], _, D, Acc) ->
live_opt(Is, 0, D, [I|Acc]);
live_opt([{wait,_}=I|Is], _, D, Acc) ->
live_opt(Is, 0, D, [I|Acc]);
+live_opt([{get_map_elements,Fail,Src,{list,List}}=I|Is], Regs0, D, Acc) ->
+ {Ss,Ds} = split_even(List),
+ Regs1 = x_live([Src|Ss], x_dead(Ds, Regs0)),
+ Regs = live_join_label(Fail, D, Regs1),
+ live_opt(Is, Regs, D, [I|Acc]);
%% Transparent instructions - they neither use nor modify x registers.
live_opt([{deallocate,_}=I|Is], Regs, D, Acc) ->
@@ -783,37 +783,46 @@ live_opt([{recv_mark,_}=I|Is], Regs, D, Acc) ->
live_opt([], _, _, Acc) -> Acc.
-live_opt_block([{set,Ds,Ss,Op}=I0|Is], Regs0, D, Acc) ->
+live_opt_block([{set,Ds,Ss,Op0}|Is], Regs0, D, Acc) ->
Regs1 = x_live(Ss, x_dead(Ds, Regs0)),
- {I,Regs} = case Op of
- {alloc,Live0,Alloc} ->
- %% The life-time analysis used by the code generator
- %% is sometimes too conservative, so it may be
- %% possible to lower the number of live registers
- %% based on the exact liveness information.
- %% The main benefit is that more optimizations that
- %% depend on liveness information (such as the
- %% beam_bool and beam_dead passes) may be applied.
- Live = live_regs(Regs1),
- true = Live =< Live0, %Assertion.
- I1 = {set,Ds,Ss,{alloc,Live,Alloc}},
- {I1,live_call(Live)};
- _ ->
- {I0,Regs1}
- end,
+ {Op, Regs} = live_opt_block_op(Op0, Regs1, D),
+ I = {set, Ds, Ss, Op},
+
case Ds of
- [{x,X}] ->
- case (not is_live(X, Regs0)) andalso Op =:= move of
- true ->
- live_opt_block(Is, Regs0, D, Acc);
- false ->
- live_opt_block(Is, Regs, D, [I|Acc])
- end;
- _ ->
- live_opt_block(Is, Regs, D, [I|Acc])
+ [{x,X}] ->
+ case (not is_live(X, Regs0)) andalso Op =:= move of
+ true ->
+ live_opt_block(Is, Regs0, D, Acc);
+ false ->
+ live_opt_block(Is, Regs, D, [I|Acc])
+ end;
+ _ ->
+ live_opt_block(Is, Regs, D, [I|Acc])
end;
live_opt_block([], Regs, _, Acc) -> {Acc,Regs}.
+live_opt_block_op({alloc,Live0,AllocOp}, Regs0, D) ->
+ Regs =
+ case AllocOp of
+ {Kind, _N, Fail} when Kind =:= gc_bif; Kind =:= put_map ->
+ live_join_label(Fail, D, Regs0);
+ _ ->
+ Regs0
+ end,
+
+ %% The life-time analysis used by the code generator is sometimes too
+ %% conservative, so it may be possible to lower the number of live
+ %% registers based on the exact liveness information. The main benefit is
+ %% that more optimizations that depend on liveness information (such as the
+ %% beam_bool and beam_dead passes) may be applied.
+ Live = live_regs(Regs),
+ true = Live =< Live0,
+ {{alloc,Live,AllocOp}, live_call(Live)};
+live_opt_block_op({bif,_N,Fail} = Op, Regs, D) ->
+ {Op, live_join_label(Fail, D, Regs)};
+live_opt_block_op(Op, Regs, _D) ->
+ {Op, Regs}.
+
live_join_labels([{f,L}|T], D, Regs0) when L =/= 0 ->
Regs = gb_trees:get(L, D) bor Regs0,
live_join_labels(T, D, Regs);
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 4c0cb6780a..69e34f76b0 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -808,9 +808,11 @@ validate_bs_skip_utf(Fail, Ctx, Live, Vst0) ->
%% A possibility for garbage collection must not occur between setelement/3 and
%% set_tuple_element/3.
%%
+%% Note that #vst.current will be 'none' if the instruction is unreachable.
+%%
val_dsetel({move,_,_}, Vst) ->
Vst;
-val_dsetel({call_ext,3,{extfunc,erlang,setelement,3}}, #vst{current=St}=Vst) ->
+val_dsetel({call_ext,3,{extfunc,erlang,setelement,3}}, #vst{current=#st{}=St}=Vst) ->
Vst#vst{current=St#st{setelem=true}};
val_dsetel({set_tuple_element,_,_,_}, #vst{current=#st{setelem=false}}) ->
error(illegal_context_for_set_tuple_element);
@@ -919,9 +921,9 @@ verify_call_match_context(Lbl, Ctx, #vst{ft=Ft}) ->
error({unsuitable_bs_start_match2,I})
end.
-allocate(Zero, Stk, Heap, Live, #vst{current=#st{numy=none}=St}=Vst0) ->
+allocate(Zero, Stk, Heap, Live, #vst{current=#st{numy=none}}=Vst0) ->
verify_live(Live, Vst0),
- Vst = prune_x_regs(Live, Vst0),
+ Vst = #vst{current=St} = prune_x_regs(Live, Vst0),
Ys = init_regs(Stk, case Zero of
true -> initialized;
false -> uninitialized
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 82ff8a95f3..e37ca31704 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.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,6 +43,10 @@
-type abstract_code() :: [erl_parse:abstract_form()].
+%% Internal representations used for 'from_asm' and 'from_beam' compilation can
+%% also be valid, but have no relevant types defined.
+-type forms() :: abstract_code() | cerl:c_module().
+
-type option() :: atom() | {atom(), term()} | {'d', atom(), term()}.
-type err_info() :: {erl_anno:line() | 'none',
@@ -88,7 +92,7 @@ file(File, Opt) ->
forms(Forms) -> forms(Forms, ?DEFAULT_OPTIONS).
--spec forms(abstract_code(), [option()] | option()) -> comp_ret().
+-spec forms(forms(), [option()] | option()) -> comp_ret().
forms(Forms, Opts) when is_list(Opts) ->
do_compile({forms,Forms}, [binary|Opts++env_default_opts()]);
@@ -116,7 +120,7 @@ noenv_file(File, Opts) when is_list(Opts) ->
noenv_file(File, Opt) ->
noenv_file(File, [Opt|?DEFAULT_OPTIONS]).
--spec noenv_forms(abstract_code(), [option()] | option()) -> comp_ret().
+-spec noenv_forms(forms(), [option()] | option()) -> comp_ret().
noenv_forms(Forms, Opts) when is_list(Opts) ->
do_compile({forms,Forms}, [binary|Opts]);
@@ -169,17 +173,25 @@ env_default_opts() ->
do_compile(Input, Opts0) ->
Opts = expand_opts(Opts0),
- {Pid,Ref} =
- spawn_monitor(fun() ->
- exit(try
- internal(Input, Opts)
- catch
- error:Reason ->
- {error,Reason}
- end)
- end),
- receive
- {'DOWN',Ref,process,Pid,Rep} -> Rep
+ IntFun = fun() -> try
+ internal(Input, Opts)
+ catch
+ error:Reason ->
+ {error,Reason}
+ end
+ end,
+ %% Dialyzer has already spawned workers.
+ case lists:member(dialyzer, Opts) of
+ true ->
+ IntFun();
+ false ->
+ {Pid,Ref} =
+ spawn_monitor(fun() ->
+ exit(IntFun())
+ end),
+ receive
+ {'DOWN',Ref,process,Pid,Rep} -> Rep
+ end
end.
expand_opts(Opts0) ->
@@ -236,6 +248,8 @@ format_error({epp,E}) ->
epp:format_error(E);
format_error(write_error) ->
"error writing file";
+format_error({write_error, Error}) ->
+ io_lib:format("error writing file: ~ts", [file:format_error(Error)]);
format_error({rename,From,To,Error}) ->
io_lib:format("failed to rename ~ts to ~ts: ~ts",
[From,To,file:format_error(Error)]);
@@ -678,7 +692,7 @@ kernel_passes() ->
{iff,core,?pass(save_core_code)},
%% Kernel Erlang and code generation.
- {pass,v3_kernel},
+ ?pass(v3_kernel),
{iff,dkern,{listing,"kernel"}},
{iff,'to_kernel',{done,"kernel"}},
{pass,v3_life},
@@ -1206,7 +1220,7 @@ makedep_output(#compile{code=Code,options=Opts,ofile=Ofile}=St) ->
end,
{ok,St}
catch
- exit:_ ->
+ error:_ ->
%% Couldn't write to output Makefile.
Err = {St#compile.ifile,[{none,?MODULE,write_error}]},
{error,St#compile{errors=St#compile.errors++[Err]}}
@@ -1235,6 +1249,17 @@ core_fold_module_after_inlining(#compile{code=Code0,options=Opts}=St) ->
{ok,Code,_Ws} = sys_core_fold:module(Code0, Opts),
{ok,St#compile{code=Code}}.
+v3_kernel(#compile{code=Code0,options=Opts,warnings=Ws0}=St) ->
+ {ok,Code,Ws} = v3_kernel:module(Code0, Opts),
+ case Ws =:= [] orelse test_core_inliner(St) of
+ false ->
+ {ok,St#compile{code=Code,warnings=Ws0++Ws}};
+ true ->
+ %% cerl_inline may produce code that generates spurious
+ %% warnings. Ignore any such warnings.
+ {ok,St#compile{code=Code}}
+ end.
+
test_old_inliner(#compile{options=Opts}) ->
%% The point of this test is to avoid loading the old inliner
%% if we know that it will not be used.
@@ -1479,8 +1504,8 @@ save_binary_1(St) ->
end,
{error,St#compile{errors=St#compile.errors ++ Es}}
end;
- {error,_Error} ->
- Es = [{Tfile,[{none,compile,write_error}]}],
+ {error,Error} ->
+ Es = [{Tfile,[{none,compile,{write_error,Error}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
end.
@@ -1628,8 +1653,8 @@ listing(LFun, Ext, St) ->
LFun(Lf, Code),
ok = file:close(Lf),
{ok,St};
- {error,_Error} ->
- Es = [{Lfile,[{none,compile,write_error}]}],
+ {error,Error} ->
+ Es = [{Lfile,[{none,compile,{write_error,Error}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
end.
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index e0de50f3ae..4922953407 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -468,7 +468,8 @@ bitstr(#c_bitstr{val=Val,size=Size}=BinSeg, Sub) ->
%% Currently, we don't attempt to check binaries because they
%% are difficult to check.
-is_safe_simple(#c_var{}, _) -> true;
+is_safe_simple(#c_var{}=Var, _) ->
+ not cerl:is_c_fname(Var);
is_safe_simple(#c_cons{hd=H,tl=T}, Sub) ->
is_safe_simple(H, Sub) andalso is_safe_simple(T, Sub);
is_safe_simple(#c_tuple{es=Es}, Sub) -> is_safe_simple_list(Es, Sub);
@@ -733,7 +734,7 @@ call(#c_call{args=As}=Call, #c_literal{val=M}=M0, #c_literal{val=N}=N0, Sub) ->
false ->
case sys_core_fold_lists:call(Call, M, N, As) of
none ->
- call_1(Call, M, N, As, Sub);
+ call_1(Call, M0, N0, As, Sub);
Core ->
expr(Core, Sub)
end
@@ -1129,7 +1130,13 @@ clause_1(#c_clause{guard=G0,body=B0}=Cl, Ps1, Cexpr, Ctxt, Sub1) ->
%%
%% case A of NewVar when true -> ...
%%
- sub_set_var(Var, Cexpr, Sub2);
+ case cerl:is_c_fname(Cexpr) of
+ false ->
+ sub_set_var(Var, Cexpr, Sub2);
+ true ->
+ %% We must not copy funs, and especially not into guards.
+ Sub2
+ end;
_ ->
Sub2
end,
diff --git a/lib/compiler/test/beam_block_SUITE.erl b/lib/compiler/test/beam_block_SUITE.erl
index 4bcb252833..55d5f2dbe8 100644
--- a/lib/compiler/test/beam_block_SUITE.erl
+++ b/lib/compiler/test/beam_block_SUITE.erl
@@ -21,7 +21,8 @@
-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,otp_7345/1,move_opt_across_gc_bif/1]).
+ get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1,
+ erl_202/1,repro/1]).
%% The only test for the following functions is that
%% the code compiles and is accepted by beam_validator.
@@ -37,7 +38,9 @@ groups() ->
[{p,[parallel],
[get_map_elements,
otp_7345,
- move_opt_across_gc_bif
+ move_opt_across_gc_bif,
+ erl_202,
+ repro
]}].
init_per_suite(Config) ->
@@ -135,6 +138,48 @@ positive(speaking) ->
paris([], P) -> P + 1.
+
+%% See https://bugs.erlang.org/browse/ERL-202.
+%% Test that move_allocates/1 in beam_block doesn't move allocate
+%% when it would not be safe.
+
+-record(erl_202_r1, {y}).
+-record(erl_202_r2, {x}).
+
+erl_202(_Config) ->
+ Ref = make_ref(),
+ Ref = erl_202({{1,2},Ref}, 42),
+
+ {Ref} = erl_202({7,8}, #erl_202_r1{y=#erl_202_r2{x=Ref}}),
+
+ ok.
+
+erl_202({{_, _},X}, _) ->
+ X;
+erl_202({_, _}, #erl_202_r1{y=R2}) ->
+ {R2#erl_202_r2.x}.
+
+%% See https://bugs.erlang.org/browse/ERL-266.
+%% Instructions with failure labels are not safe to include
+%% in a block. Including get_map_elements in a block would
+%% lead to unsafe code.
+
+repro(_Config) ->
+ [] = maps:to_list(repro([], #{}, #{})),
+ [{tmp1,n}] = maps:to_list(repro([{tmp1,0}], #{}, #{})),
+ [{tmp1,name}] = maps:to_list(repro([{tmp1,0}], #{}, #{0=>name})),
+ ok.
+
+repro([], TempNames, _Slots) ->
+ TempNames;
+repro([{Temp, Slot}|Xs], TempNames, Slots0) ->
+ {Name, Slots} =
+ case Slots0 of
+ #{Slot := Name0} -> {Name0, Slots0};
+ #{} -> {n, Slots0#{Slot => n}}
+ end,
+ repro(Xs, TempNames#{Temp => Name}, Slots).
+
%%%
%%% The only test of the following code is that it compiles.
%%%
diff --git a/lib/compiler/test/beam_jump_SUITE.erl b/lib/compiler/test/beam_jump_SUITE.erl
index 0b13adaff2..088f63606c 100644
--- a/lib/compiler/test/beam_jump_SUITE.erl
+++ b/lib/compiler/test/beam_jump_SUITE.erl
@@ -21,7 +21,7 @@
-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
- undefined_label/1]).
+ undefined_label/1,ambiguous_catch_try_state/1]).
suite() ->
[{ct_hooks,[ts_install_cth]}].
@@ -32,7 +32,8 @@ all() ->
groups() ->
[{p,[parallel],
- [undefined_label
+ [undefined_label,
+ ambiguous_catch_try_state
]}].
init_per_suite(Config) ->
@@ -57,3 +58,17 @@ flights(0, [], []) when [], 0; 0.0, [], false ->
clark;
flights(_, Reproduction, introduction) when false, Reproduction ->
responsible.
+
+%% [ERL-209] beam_jump would share 'catch' blocks, causing an
+%% ambiguous_catch_try_state error in beam_validator.
+
+ambiguous_catch_try_state(_Config) ->
+ {{'EXIT',{{case_clause,song},_}},{'EXIT',{{case_clause,song},_}}} =
+ checks(42),
+ ok.
+
+river() -> song.
+
+checks(Wanted) ->
+ %% Must be one line to cause the unsafe optimization.
+ {catch case river() of sheet -> begin +Wanted, if "da" -> Wanted end end end, catch case river() of sheet -> begin + Wanted, if "da" -> Wanted end end end}.
diff --git a/lib/compiler/test/beam_reorder_SUITE.erl b/lib/compiler/test/beam_reorder_SUITE.erl
index ff31f2d3bd..27ce51eec3 100644
--- a/lib/compiler/test/beam_reorder_SUITE.erl
+++ b/lib/compiler/test/beam_reorder_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-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.
diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl
index 69e2f1838d..492067ef00 100644
--- a/lib/compiler/test/beam_type_SUITE.erl
+++ b/lib/compiler/test/beam_type_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-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.
diff --git a/lib/compiler/test/beam_utils_SUITE.erl b/lib/compiler/test/beam_utils_SUITE.erl
index f6d4a311bb..ac734a70be 100644
--- a/lib/compiler/test/beam_utils_SUITE.erl
+++ b/lib/compiler/test/beam_utils_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-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.
@@ -260,6 +260,14 @@ otp_8949_b(A, B) ->
liveopt(_Config) ->
F = liveopt_fun(42, pebkac, user),
void = F(42, #alarmInfo{type=sctp,cause=pebkac,origin=user}),
+
+
+ A = {#alarmInfo{cause = {abc, def}}, ghi},
+ A = liveopt_guard_bif(A),
+
+ B = {#alarmInfo{cause = {abc}}, def},
+ {#alarmInfo{cause = {{abc}}}, def} = liveopt_guard_bif(B),
+
ok.
liveopt_fun(Peer, Cause, Origin) ->
@@ -271,6 +279,15 @@ liveopt_fun(Peer, Cause, Origin) ->
void
end.
+liveopt_guard_bif({#alarmInfo{cause=F}=R, X}=A) ->
+ %% ERIERL-48
+ if
+ is_tuple(F), tuple_size(F) == 2 -> A;
+ true ->
+ R2 = R#alarmInfo{cause={F}},
+ {R2,X}
+ end.
+
%% Thanks to QuickCheck.
coverage(_Config) ->
42+7 = merchant([[],7,false]),
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 263fd2ca7e..ca85eef688 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -32,7 +32,8 @@
bad_bin_match/1,bad_dsetel/1,
state_after_fault_in_catch/1,no_exception_in_catch/1,
undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1,
- map_field_lists/1,cover_bin_opt/1]).
+ map_field_lists/1,cover_bin_opt/1,
+ val_dsetel/1]).
-include_lib("common_test/include/ct.hrl").
@@ -60,7 +61,7 @@ groups() ->
freg_state,bad_bin_match,bad_dsetel,
state_after_fault_in_catch,no_exception_in_catch,
undef_label,illegal_instruction,failing_gc_guard_bif,
- map_field_lists,cover_bin_opt]}].
+ map_field_lists,cover_bin_opt,val_dsetel]}].
init_per_suite(Config) ->
Config.
@@ -546,3 +547,23 @@ beam_val(M) ->
_ = [io:put_chars(beam_validator:format_error(E)) ||
E <- Errors],
Errors.
+
+%%%-------------------------------------------------------------------------
+
+val_dsetel(_Config) ->
+ self() ! 13,
+ {'EXIT',{{try_clause,participating},_}} = (catch night(0)),
+ ok.
+
+night(Turned) ->
+ receive
+ 13 ->
+ try participating of engine -> 16 after false end
+ end,
+ %% The setelement/3 call is unreachable.
+ Turned(setelement(#{true => Turned},
+ participating(Turned, "suit", 40, []),
+ Turned < Turned)),
+ ok.
+
+participating(_, _, _, _) -> ok.
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 224abf6c29..a9bee888d9 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -38,7 +38,8 @@
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,constant_propagation/1]).
+ bad_literals/1,good_literals/1,constant_propagation/1
+ ]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -768,6 +769,11 @@ multiple_uses(Config) when is_list(Config) ->
{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 = first_after(<<>>, 42),
+ <<1>> = first_after(<<1,2,3>>, 0),
+ <<2>> = first_after(<<1,2,3>>, 1),
+
ok.
multiple_uses_1(<<X:16,Tail/binary>>) ->
@@ -789,6 +795,24 @@ multiple_uses_match(<<Y:16,Z:16>>) ->
multiple_uses_cmp(<<Y:16>>, <<Y:16>>) -> true;
multiple_uses_cmp(<<_:16>>, <<_:16>>) -> false.
+first_after(Data, Offset) ->
+ case byte_size(Data) > Offset of
+ false ->
+ {First, Rest} = {ok, ok},
+ ok;
+ true ->
+ <<_:Offset/binary, Rest/binary>> = Data,
+ %% 'Rest' saved in y(0) before the call.
+ {First, _} = match_first(Data, Rest),
+ %% When beam_bsm sees the code, the following line
+ %% which uses y(0) has been optimized away.
+ {First, Rest} = {First, Rest},
+ First
+ end.
+
+match_first(_, <<First:1/binary, Rest/binary>>) ->
+ {First, Rest}.
+
zero_label(Config) when is_list(Config) ->
<<"nosemouth">> = read_pols(<<"FACE","nose","mouth">>),
<<"CE">> = read_pols(<<"noFACE">>),
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index 376d2c8e9a..ced0e39d06 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -26,7 +26,7 @@
unused_multiple_values_error/1,unused_multiple_values/1,
multiple_aliases/1,redundant_boolean_clauses/1,
mixed_matching_clauses/1,unnecessary_building/1,
- no_no_file/1]).
+ no_no_file/1,configuration/1]).
-export([foo/0,foo/1,foo/2,foo/3]).
@@ -45,7 +45,7 @@ groups() ->
unused_multiple_values_error,unused_multiple_values,
multiple_aliases,redundant_boolean_clauses,
mixed_matching_clauses,unnecessary_building,
- no_no_file]}].
+ no_no_file,configuration]}].
init_per_suite(Config) ->
@@ -499,4 +499,16 @@ experiment() ->
end,
ok.
+
+%% Make sure we don't try to move a fun into a guard.
+configuration(_Config) ->
+ {'EXIT',_} = (catch configuration()),
+ ok.
+
+configuration() ->
+ [forgotten || Components <- enemy, is_tuple(fun art/0)].
+
+art() ->
+ creating.
+
id(I) -> I.
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index 6302f82f29..429d6b79e0 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -87,6 +87,7 @@ misc(Config) when is_list(Config) ->
{ok,buf,<<>>} = get_data({o,true,0}, 42, buf),
{ok,buf,<<>>} = get_data({o,false,0}, 0, buf),
error = get_data({o,false,0}, 42, buf),
+
ok.
@@ -343,6 +344,11 @@ complex_semicolon(Config) when is_list(Config) ->
ok = csemi7(#{a=>1}, 3, 3),
ok = csemi7(#{a=>1, b=>3}, 0, 0),
+ %% 8: Make sure that funs cannot be copied into guards.
+ ok = csemi8(true),
+ error = csemi8(false),
+ error = csemi8(42),
+
ok.
csemi1(Type, Val) when is_list(Val), Type == float;
@@ -457,6 +463,13 @@ csemi6(_, _) -> error.
csemi7(A, B, C) when A#{a:=B} > #{a=>1}; abs(C) > 2 -> ok;
csemi7(_, _, _) -> error.
+csemi8(Together) ->
+ case fun csemi8/1 of
+ Typically when Together; Typically, Together -> ok;
+ _ -> error
+ end.
+
+
comma(Config) when is_list(Config) ->
%% ',' combinations of literal true/false.
diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl
index 3cb49433ce..adb96fb87d 100644
--- a/lib/compiler/test/lc_SUITE.erl
+++ b/lib/compiler/test/lc_SUITE.erl
@@ -19,7 +19,7 @@
%%
-module(lc_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+-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,
basic/1,deeply_nested/1,no_generator/1,
@@ -32,11 +32,11 @@ suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,1}}].
-all() ->
+all() ->
test_lib:recompile(?MODULE),
[{group,p}].
-groups() ->
+groups() ->
[{p,test_lib:parallel(),
[basic,
deeply_nested,
@@ -214,6 +214,7 @@ shadow(Config) when is_list(Config) ->
ok.
effect(Config) when is_list(Config) ->
+ ct:timetrap({minutes,10}),
[{42,{a,b,c}}] =
do_effect(fun(F, L) ->
[F({V1,V2}) ||
@@ -240,7 +241,7 @@ do_effect(Lc, L) ->
lists:reverse(erase(?MODULE)).
id(I) -> I.
-
+
fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Args,_}|_]}}) -> ok;
fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Arity,_}|_]}})
when length(Args) =:= Arity ->
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index 31402ac717..127679ba69 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,non_matching_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,grab_bag/1]).
+ coverage/1,grab_bag/1,literal_binary/1]).
-include_lib("common_test/include/ct.hrl").
@@ -40,7 +40,7 @@ groups() ->
match_in_call,untuplify,
shortcut_boolean,letify_guard,selectify,
underscore,match_map,map_vars_used,coverage,
- grab_bag]}].
+ grab_bag,literal_binary]}].
init_per_suite(Config) ->
@@ -574,6 +574,15 @@ grab_bag_remove_failure([{stretch,_,Mi}=Stretch | Specs], Unit, _MaxFailure) ->
ok
end.
+%% Regression in 19.0, reported by Alexei Sholik
+literal_binary(_Config) ->
+ 3 = literal_binary_match(bar,<<"y">>),
+ ok.
+
+literal_binary_match(bar, <<"x">>) -> 1;
+literal_binary_match(_, <<"x">>) -> 2;
+literal_binary_match(_, <<"y">>) -> 3;
+literal_binary_match(_, _) -> fail.
id(I) -> I.
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index f884e6e7d6..ff9bddc1e2 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -42,7 +42,7 @@
comprehensions/1,maps/1,maps_bin_opt_info/1,
redundant_boolean_clauses/1,
latin1_fallback/1,underscore/1,no_warnings/1,
- bit_syntax/1]).
+ bit_syntax/1,inlining/1]).
init_per_testcase(_Case, Config) ->
Config.
@@ -65,7 +65,7 @@ groups() ->
bin_opt_info,bin_construction,comprehensions,maps,
maps_bin_opt_info,
redundant_boolean_clauses,latin1_fallback,
- underscore,no_warnings,bit_syntax]}].
+ underscore,no_warnings,bit_syntax,inlining]}].
init_per_suite(Config) ->
Config.
@@ -823,6 +823,30 @@ bit_syntax(Config) ->
run(Config, Ts),
ok.
+inlining(Config) ->
+ %% Make sure that no spurious warnings are generated
+ %% when inlining.
+ Ts = [{inlining_1,
+ <<"-compile(inline).
+ compute1(X) -> add(X, 0).
+ add(1, 0) -> 1;
+ add(1, Y) -> 1 + Y;
+ add(X, Y) -> X + Y.
+ ">>,
+ [],
+ []},
+ {inlining_2,
+ <<"-compile({inline,[add/2]}).
+ compute1(X) -> add(X, 0).
+ add(1, 0) -> 1;
+ add(1, Y) -> 1 + Y;
+ add(X, Y) -> X + Y.
+ ">>,
+ [],
+ []}
+ ],
+ run(Config, Ts),
+ ok.
%%%
%%% End of test cases.
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index 23dd4bd4b1..f3b9d3e9fd 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 7.0
+COMPILER_VSN = 7.0.4.1
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 7183c395ae..d4264335b6 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2017. 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.
@@ -37,7 +37,9 @@
#include <openssl/opensslconf.h>
#include <openssl/crypto.h>
+#ifndef OPENSSL_NO_DES
#include <openssl/des.h>
+#endif /* #ifndef OPENSSL_NO_DES */
/* #include <openssl/idea.h> This is not supported on the openssl OTP requires */
#include <openssl/dsa.h>
#include <openssl/rsa.h>
@@ -48,14 +50,17 @@
#include <openssl/ripemd.h>
#include <openssl/bn.h>
#include <openssl/objects.h>
-#include <openssl/rc4.h>
-#include <openssl/rc2.h>
+#ifndef OPENSSL_NO_RC4
+ #include <openssl/rc4.h>
+#endif /* OPENSSL_NO_RC4 */
+#ifndef OPENSSL_NO_RC2
+ #include <openssl/rc2.h>
+#endif
#include <openssl/blowfish.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
-
/* Helper macro to construct a OPENSSL_VERSION_NUMBER.
* See openssl/opensslv.h
*/
@@ -226,6 +231,7 @@ static ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
static ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -296,6 +302,7 @@ static ErlNifFunc nif_funcs[] = {
{"hmac_final_nif", 2, hmac_final_nif},
{"block_crypt_nif", 5, block_crypt_nif},
{"block_crypt_nif", 4, block_crypt_nif},
+ {"aes_cfb_128_crypt_nif", 4, aes_cfb_128_crypt_nif},
{"aes_ige_crypt_nif", 4, aes_ige_crypt_nif},
{"aes_ctr_encrypt", 3, aes_ctr_encrypt},
@@ -320,7 +327,7 @@ static ErlNifFunc nif_funcs[] = {
{"rsa_private_crypt", 4, rsa_private_crypt},
{"dh_generate_parameters_nif", 2, dh_generate_parameters_nif},
{"dh_check", 1, dh_check},
- {"dh_generate_key_nif", 3, dh_generate_key_nif},
+ {"dh_generate_key_nif", 4, dh_generate_key_nif},
{"dh_compute_key_nif", 3, dh_compute_key_nif},
{"srp_value_B_nif", 5, srp_value_B_nif},
{"srp_user_secret_nif", 7, srp_user_secret_nif},
@@ -458,16 +465,35 @@ struct cipher_type_t {
const size_t key_len; /* != 0 to also match on key_len */
};
+#ifdef OPENSSL_NO_DES
+#define COND_NO_DES_PTR(Ptr) (NULL)
+#else
+#define COND_NO_DES_PTR(Ptr) (Ptr)
+#endif
+
struct cipher_type_t cipher_types[] =
{
- {{"rc2_cbc"}, {&EVP_rc2_cbc}},
- {{"des_cbc"}, {&EVP_des_cbc}},
- {{"des_cfb"}, {&EVP_des_cfb8}},
- {{"des_ecb"}, {&EVP_des_ecb}},
- {{"des_ede3_cbc"}, {&EVP_des_ede3_cbc}},
- {{"des_ede3_cbf"},
+ {{"rc2_cbc"},
+#ifndef OPENSSL_NO_RC2
+ {&EVP_rc2_cbc}
+#else
+ {NULL}
+#endif
+ },
+ {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}},
+ {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}},
+ {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}},
+ {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}},
+ {{"des_ede3_cbf"}, /* Misspelled, retained */
+#ifdef HAVE_DES_ede3_cfb_encrypt
+ {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)}
+#else
+ {NULL}
+#endif
+ },
+ {{"des_ede3_cfb"},
#ifdef HAVE_DES_ede3_cfb_encrypt
- {&EVP_des_ede3_cfb8}
+ {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)}
#else
{NULL}
#endif
@@ -563,7 +589,7 @@ static void error_handler(void* null, const char* errstr)
}
#endif /* HAVE_DYNAMIC_CRYPTO_LIB */
-static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
+static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
{
#ifdef OPENSSL_THREADS
ErlNifSysInfo sys_info;
@@ -578,7 +604,7 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
char lib_buf[1000];
if (!verify_lib_version())
- return 0;
+ return __LINE__;
/* load_info: {301, <<"/full/path/of/this/library">>} */
if (!enif_get_tuple(env, load_info, &tpl_arity, &tpl_array)
@@ -588,7 +614,7 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
|| !enif_inspect_binary(env, tpl_array[1], &lib_bin)) {
PRINTF_ERR1("CRYPTO: Invalid load_info '%T'", load_info);
- return 0;
+ return __LINE__;
}
hmac_context_rtype = enif_open_resource_type(env, NULL, "hmac_context",
@@ -597,7 +623,7 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
NULL);
if (!hmac_context_rtype) {
PRINTF_ERR0("CRYPTO: Could not open resource type 'hmac_context'");
- return 0;
+ return __LINE__;
}
#if OPENSSL_VERSION_NUMBER >= OpenSSL_version_plain(1,0,0)
evp_md_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_MD_CTX",
@@ -606,7 +632,7 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
NULL);
if (!evp_md_ctx_rtype) {
PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_MD_CTX'");
- return 0;
+ return __LINE__;
}
#endif
#ifdef HAVE_EVP_AES_CTR
@@ -616,14 +642,14 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
NULL);
if (!evp_cipher_ctx_rtype) {
PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_CIPHER_CTX'");
- return 0;
+ return __LINE__;
}
#endif
if (library_refc > 0) {
/* Repeated loading of this library (module upgrade).
* Atoms and callbacks are already set, we are done.
*/
- return 1;
+ return 0;
}
atom_true = enif_make_atom(env,"true");
@@ -669,14 +695,14 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
{
void* handle;
if (!change_basename(&lib_bin, lib_buf, sizeof(lib_buf), crypto_callback_name)) {
- return 0;
+ return __LINE__;
}
if (!(handle = enif_dlopen(lib_buf, &error_handler, NULL))) {
- return 0;
+ return __LINE__;
}
if (!(funcp = (get_crypto_callbacks_t*) enif_dlsym(handle, "get_crypto_callbacks",
&error_handler, NULL))) {
- return 0;
+ return __LINE__;
}
}
#else /* !HAVE_DYNAMIC_CRYPTO_LIB */
@@ -695,7 +721,7 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
if (!ccb || ccb->sizeof_me != sizeof(*ccb)) {
PRINTF_ERR0("Invalid 'crypto_callbacks'");
- return 0;
+ return __LINE__;
}
CRYPTO_set_mem_functions(ccb->crypto_alloc, ccb->crypto_realloc, ccb->crypto_free);
@@ -709,13 +735,14 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
CRYPTO_set_dynlock_destroy_callback(ccb->dyn_destroy_function);
}
#endif /* OPENSSL_THREADS */
- return 1;
+ return 0;
}
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
- if (!init(env, load_info)) {
- return -1;
+ int errline = initialize(env, load_info);
+ if (errline) {
+ return errline;
}
*priv_data = NULL;
@@ -726,14 +753,16 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
ERL_NIF_TERM load_info)
{
+ int errline;
if (*old_priv_data != NULL) {
- return -1; /* Don't know how to do that */
+ return __LINE__; /* Don't know how to do that */
}
if (*priv_data != NULL) {
- return -1; /* Don't know how to do that */
+ return __LINE__; /* Don't know how to do that */
}
- if (!init(env, load_info)) {
- return -1;
+ errline = initialize(env, load_info);
+ if (errline) {
+ return errline;
}
library_refc++;
return 0;
@@ -749,7 +778,7 @@ static ERL_NIF_TERM algo_hash[8]; /* increase when extending the list */
static int algo_pubkey_cnt;
static ERL_NIF_TERM algo_pubkey[7]; /* increase when extending the list */
static int algo_cipher_cnt;
-static ERL_NIF_TERM algo_cipher[20]; /* increase when extending the list */
+static ERL_NIF_TERM algo_cipher[23]; /* increase when extending the list */
static void init_algorithms_types(ErlNifEnv* env)
{
@@ -785,10 +814,13 @@ static void init_algorithms_types(ErlNifEnv* env)
algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp");
algo_cipher_cnt = 0;
+#ifndef OPENSSL_NO_DES
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbc");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des_ede3");
#ifdef HAVE_DES_ede3_cfb_encrypt
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbf");
+ algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cfb");
+#endif
#endif
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc128");
@@ -800,14 +832,21 @@ static void init_algorithms_types(ErlNifEnv* env)
#ifdef HAVE_AES_IGE
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ige256");
#endif
+#ifndef OPENSSL_NO_DES
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cbc");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cfb");
+ algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_ecb");
+#endif
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cbc");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cfb64");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ofb64");
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ecb");
+#ifndef OPENSSL_NO_RC2
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc2_cbc");
+#endif
+#ifndef OPENSSL_NO_RC4
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc4");
+#endif
#if defined(HAVE_GCM)
algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_gcm");
#endif
@@ -1366,13 +1405,20 @@ static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
return enif_raise_exception(env, atom_notsup);
}
- if ((argv[0] == atom_aes_cfb8 || argv[0] == atom_aes_cfb128)
+ if (argv[0] == atom_aes_cfb8
&& (key.size == 24 || key.size == 32)) {
/* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes?
* Fall back on low level API
*/
return aes_cfb_8_crypt(env, argc-1, argv+1);
}
+ else if (argv[0] == atom_aes_cfb128
+ && (key.size == 24 || key.size == 32)) {
+ /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes?
+ * Fall back on low level API
+ */
+ return aes_cfb_128_crypt_nif(env, argc-1, argv+1);
+ }
ivec_size = EVP_CIPHER_iv_length(cipher);
@@ -1446,6 +1492,31 @@ static ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM
return ret;
}
+static ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, IVec, Data, IsEncrypt) */
+ ErlNifBinary key, ivec, text;
+ AES_KEY aes_key;
+ unsigned char ivec_clone[16]; /* writable copy */
+ int new_ivlen = 0;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || !(key.size == 16 || key.size == 24 || key.size == 32)
+ || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16
+ || !enif_inspect_iolist_as_binary(env, argv[2], &text)) {
+ return enif_make_badarg(env);
+ }
+
+ memcpy(ivec_clone, ivec.data, 16);
+ AES_set_encrypt_key(key.data, key.size * 8, &aes_key);
+ AES_cfb128_encrypt((unsigned char *) text.data,
+ enif_make_new_binary(env, text.size, &ret),
+ text.size, &aes_key, ivec_clone, &new_ivlen,
+ (argv[3] != atom_true));
+ CONSUME_REDS(env,text);
+ return ret;
+}
+
static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key, IVec, Data, IsEncrypt) */
#ifdef HAVE_AES_IGE
@@ -2141,7 +2212,7 @@ static ERL_NIF_TERM dss_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
DSA *dsa;
int i;
- if (!argv[0] == atom_sha
+ if (argv[0] != atom_sha
|| !enif_inspect_binary(env, argv[1], &digest_bin)
|| digest_bin.size != SHA_DIGEST_LENGTH
|| !enif_inspect_binary(env, argv[2], &sign_bin)
@@ -2306,6 +2377,7 @@ static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ERL_NIF_TERM rc4_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key, Data) */
+#ifndef OPENSSL_NO_RC4
ErlNifBinary key, data;
RC4_KEY rc4_key;
ERL_NIF_TERM ret;
@@ -2319,10 +2391,14 @@ static ERL_NIF_TERM rc4_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
enif_make_new_binary(env, data.size, &ret));
CONSUME_REDS(env,data);
return ret;
-}
+#else
+ return enif_raise_exception(env, atom_notsup);
+#endif
+}
static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key) */
+#ifndef OPENSSL_NO_RC4
ErlNifBinary key;
ERL_NIF_TERM ret;
@@ -2332,11 +2408,14 @@ static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
RC4_set_key((RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &ret),
key.size, key.data);
return ret;
+#else
+ return enif_raise_exception(env, atom_notsup);
+#endif
}
static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (State, Data) */
-
+#ifndef OPENSSL_NO_RC4
ErlNifBinary state, data;
RC4_KEY* rc4_key;
ERL_NIF_TERM new_state, new_data;
@@ -2352,7 +2431,10 @@ static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_N
enif_make_new_binary(env, data.size, &new_data));
CONSUME_REDS(env,data);
return enif_make_tuple2(env,new_state,new_data);
-}
+#else
+ return enif_raise_exception(env, atom_notsup);
+#endif
+}
static int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa)
{
@@ -2467,7 +2549,7 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
DSA* dsa;
int i;
- if (!argv[0] == atom_sha
+ if (argv[0] != atom_sha
|| !enif_inspect_binary(env, argv[1], &digest_bin)
|| digest_bin.size != SHA_DIGEST_LENGTH) {
return enif_make_badarg(env);
@@ -2678,12 +2760,13 @@ static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
}
static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (PrivKey, DHParams=[P,G], Mpint) */
+{/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */
DH* dh_params;
int pub_len, prv_len;
unsigned char *pub_ptr, *prv_ptr;
ERL_NIF_TERM ret, ret_pub, ret_prv, head, tail;
int mpint; /* 0 or 4 */
+ unsigned long len = 0;
dh_params = DH_new();
@@ -2694,11 +2777,21 @@ static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_
|| !enif_get_list_cell(env, tail, &head, &tail)
|| !get_bn_from_bin(env, head, &dh_params->g)
|| !enif_is_empty_list(env, tail)
- || !enif_get_int(env, argv[2], &mpint) || (mpint & ~4)) {
+ || !enif_get_int(env, argv[2], &mpint) || (mpint & ~4)
+ || !enif_get_ulong(env, argv[3], &len) ) {
DH_free(dh_params);
return enif_make_badarg(env);
}
+ if (len) {
+ if (len < BN_num_bits(dh_params->p))
+ dh_params->length = len;
+ else {
+ DH_free(dh_params);
+ return enif_make_badarg(env);
+ }
+ }
+
if (DH_generate_key(dh_params)) {
pub_len = BN_num_bytes(dh_params->pub_key);
prv_len = BN_num_bytes(dh_params->priv_key);
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 5a5627747c..82e450ec21 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2016</year>
+ <year>1999</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -100,7 +100,7 @@
<code>dh_private() = key_value() </code>
- <code>dh_params() = [key_value()] = [P, G] </code>
+ <code>dh_params() = [key_value()] = [P, G] | [P, G, PrivateKeyBitLength]</code>
<code>ecdh_public() = key_value() </code>
@@ -136,7 +136,7 @@
<code>stream_cipher() = rc4 | aes_ctr </code>
<code>block_cipher() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ige256 | blowfish_cbc |
- blowfish_cfb64 | des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | rc2_cbc </code>
+ blowfish_cfb64 | des_cbc | des_cfb | des3_cbc | des3_cfb | des_ede3 | rc2_cbc </code>
<code>aead_cipher() = aes_gcm | chacha20_poly1305 </code>
@@ -161,7 +161,7 @@
</p>
<code> cipher_algorithms() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ctr | aes_gcm |
aes_ige256 | blowfish_cbc | blowfish_cfb64 | chacha20_poly1305 | des_cbc | des_cfb |
- des3_cbc | des3_cbf | des_ede3 | rc2_cbc | rc4 </code>
+ des3_cbc | des3_cfb | des_ede3 | rc2_cbc | rc4 </code>
<code> public_key_algorithms() = rsa |dss | ecdsa | dh | ecdh | ec_gf2m</code>
<p>Note that ec_gf2m is not strictly a public key algorithm, but a restriction on what curves are supported
with ecdsa and ecdh.
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 7d3a85326f..887aeca680 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -31,6 +31,106 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 3.7.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a bug with AES CFB 128 for 192 and 256 bit keys.
+ Thanks to kellymclaughlin !</p>
+ <p>
+ Own Id: OTP-14313 Aux Id: PR-1393 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Crypto 3.7.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The implementation of the key exchange algorithms
+ diffie-hellman-group-exchange-sha* are optimized, up to a
+ factor of 11 for the slowest ( = biggest and safest)
+ group size.</p>
+ <p>
+ Own Id: OTP-14169 Aux Id: seq-13261 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Crypto 3.7.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The crypto application has been fixed to not use RC2
+ against OpenSSL built with RC2 disabled.</p>
+ <p>
+ Own Id: OTP-13895 Aux Id: PR-1163 </p>
+ </item>
+ <item>
+ <p>
+ The crypto application has been fixed to not use RC4
+ against OpenSSL built with RC4 disabled.</p>
+ <p>
+ Own Id: OTP-13896 Aux Id: PR-1169 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ To ease troubleshooting, <c>erlang:load_nif/2</c> now
+ includes the return value from a failed call to
+ load/reload/upgrade in the text part of the error tuple.
+ The <c>crypto</c> NIF makes use of this feature by
+ returning the source line where/if the initialization
+ fails.</p>
+ <p>
+ Own Id: OTP-13951</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Crypto 3.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Crypto has been fixed to work against OpenSSL versions
+ with disabled DES ciphers. Correct spelling of cipher
+ algorithm 'des3_cfb' has been introduced; the previous
+ misspeling still works.</p>
+ <p>
+ Own Id: OTP-13783 Aux Id: ERL-203 </p>
+ </item>
+ <item>
+ <p>
+ The size of an internal array in crypto has been fixed to
+ not segfault when having all possible ciphers. Bug fix by
+ Duncan Overbruck.</p>
+ <p>
+ Own Id: OTP-13789 Aux Id: PR-1140 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 3.7</title>
<section><title>Improvements and New Features</title>
@@ -815,7 +915,7 @@
also been extended. </item><item> The <c>configure</c>
scripts of <c>erl_interface</c> and <c>odbc</c> now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 025d57e9c5..60e0affda0 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2017. 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.
@@ -274,13 +274,14 @@ hmac_final_n(Context, HashLen) ->
%% Ecrypt/decrypt %%%
-spec block_encrypt(des_cbc | des_cfb |
- des3_cbc | des3_cbf | des_ede3 |
+ des3_cbc | des3_cbf | des3_cfb | des_ede3 |
blowfish_cbc | blowfish_cfb64 | blowfish_ofb64 |
aes_cbc128 | aes_cfb8 | aes_cfb128 | aes_cbc256 | aes_ige256 |
- aes_cbc |
+ aes_cbc |
rc2_cbc,
- Key::iodata(), Ivec::binary(), Data::iodata()) -> binary();
- (aes_gcm | chacha20_poly1305, Key::iodata(), Ivec::binary(), {AAD::binary(), Data::iodata()}) -> {binary(), binary()}.
+ Key::iodata(), Ivec::binary(), Data::iodata()) -> binary();
+ (aes_gcm | chacha20_poly1305, Key::iodata(), Ivec::binary(), {AAD::binary(), Data::iodata()}) -> {binary(), binary()};
+ (aes_gcm, Key::iodata(), Ivec::binary(), {AAD::binary(), Data::iodata(), TagLength::1..16}) -> {binary(), binary()}.
block_encrypt(Type, Key, Ivec, Data) when Type =:= des_cbc;
Type =:= des_cfb;
@@ -301,6 +302,9 @@ block_encrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc;
block_encrypt(des3_cbf, Key0, Ivec, Data) ->
Key = check_des3_key(Key0),
block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, true);
+block_encrypt(des3_cfb, Key0, Ivec, Data) ->
+ Key = check_des3_key(Key0),
+ block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, true);
block_encrypt(aes_ige256, Key, Ivec, Data) ->
aes_ige_crypt_nif(Key, Ivec, Data, true);
block_encrypt(aes_gcm, Key, Ivec, {AAD, Data}) ->
@@ -311,7 +315,7 @@ block_encrypt(chacha20_poly1305, Key, Ivec, {AAD, Data}) ->
chacha20_poly1305_encrypt(Key, Ivec, AAD, Data).
-spec block_decrypt(des_cbc | des_cfb |
- des3_cbc | des3_cbf | des_ede3 |
+ des3_cbc | des3_cbf | des3_cfb | des_ede3 |
blowfish_cbc | blowfish_cfb64 | blowfish_ofb64 |
aes_cbc128 | aes_cfb8 | aes_cfb128 | aes_cbc256 | aes_ige256 |
aes_cbc |
@@ -338,6 +342,9 @@ block_decrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc;
block_decrypt(des3_cbf, Key0, Ivec, Data) ->
Key = check_des3_key(Key0),
block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, false);
+block_decrypt(des3_cfb, Key0, Ivec, Data) ->
+ Key = check_des3_key(Key0),
+ block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, false);
block_decrypt(aes_ige256, Key, Ivec, Data) ->
notsup_to_error(aes_ige_crypt_nif(Key, Ivec, Data, false));
block_decrypt(aes_gcm, Key, Ivec, {AAD, Data, Tag}) ->
@@ -540,9 +547,15 @@ exor(Bin1, Bin2) ->
generate_key(Type, Params) ->
generate_key(Type, Params, undefined).
-generate_key(dh, DHParameters, PrivateKey) ->
+generate_key(dh, DHParameters0, PrivateKey) ->
+ {DHParameters, Len} =
+ case DHParameters0 of
+ [P,G,L] -> {[P,G], L};
+ [P,G] -> {[P,G], 0}
+ end,
dh_generate_key_nif(ensure_int_as_bin(PrivateKey),
- map_ensure_int_as_bin(DHParameters), 0);
+ map_ensure_int_as_bin(DHParameters),
+ 0, Len);
generate_key(srp, {host, [Verifier, Generator, Prime, Version]}, PrivArg)
when is_binary(Verifier), is_binary(Generator), is_binary(Prime), is_atom(Version) ->
@@ -809,6 +822,8 @@ sha_mac_96(Key, Data) -> hmac(sha, Key, Data, 12).
block_crypt_nif(_Type, _Key, _Ivec, _Text, _IsEncrypt) -> ?nif_stub.
block_crypt_nif(_Type, _Key, _Text, _IsEncrypt) -> ?nif_stub.
+aes_cfb_128_crypt_nif(_Key, _Ivec, _Text, _IsEncrypt) -> ?nif_stub.
+
check_des3_key(Key) ->
case lists:map(fun erlang:iolist_to_binary/1, Key) of
ValidKey = [B1, B2, B3] when byte_size(B1) =:= 8,
@@ -857,10 +872,10 @@ des_ede3_cbc_decrypt(Key1, Key2, Key3, IVec, Data) ->
binary().
des3_cfb_encrypt(Key1, Key2, Key3, IVec, Data) ->
- block_encrypt(des3_cbf, [Key1, Key2, Key3], IVec, Data).
+ block_encrypt(des3_cfb, [Key1, Key2, Key3], IVec, Data).
des3_cfb_decrypt(Key1, Key2, Key3, IVec, Data) ->
- block_decrypt(des3_cbf, [Key1, Key2, Key3], IVec, Data).
+ block_decrypt(des3_cfb, [Key1, Key2, Key3], IVec, Data).
%%
%% Blowfish
@@ -902,7 +917,9 @@ blowfish_ofb64_encrypt(Key, IVec, Data) ->
-spec aes_cfb_128_decrypt(iodata(), binary(), iodata()) -> binary().
aes_cfb_128_encrypt(Key, IVec, Data) ->
- block_encrypt(aes_cfb128, Key, IVec, Data).
+ %% block_encrypt(aes_cfb128, Key, IVec, Data).
+ aes_cfb_128_crypt_nif(Key, IVec, Data, true).
+
aes_cfb_128_decrypt(Key, IVec, Data) ->
block_decrypt(aes_cfb128, Key, IVec, Data).
@@ -1194,11 +1211,11 @@ dh_check([_Prime,_Gen]) -> ?nif_stub.
{binary(),binary()}.
dh_generate_key(DHParameters) ->
- dh_generate_key_nif(undefined, map_mpint_to_bin(DHParameters), 4).
+ dh_generate_key_nif(undefined, map_mpint_to_bin(DHParameters), 4, 0).
dh_generate_key(PrivateKey, DHParameters) ->
- dh_generate_key_nif(mpint_to_bin(PrivateKey), map_mpint_to_bin(DHParameters), 4).
+ dh_generate_key_nif(mpint_to_bin(PrivateKey), map_mpint_to_bin(DHParameters), 4, 0).
-dh_generate_key_nif(_PrivateKey, _DHParameters, _Mpint) -> ?nif_stub.
+dh_generate_key_nif(_PrivateKey, _DHParameters, _Mpint, _Length) -> ?nif_stub.
%% DHParameters = [P (Prime)= mpint(), G(Generator) = mpint()]
%% MyPrivKey, OthersPublicKey = mpint()
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 6732f27824..dbd335c693 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -50,6 +50,7 @@ all() ->
{group, des_cfb},
{group, des3_cbc},
{group, des3_cbf},
+ {group, des3_cfb},
{group, des_ede3},
{group, blowfish_cbc},
{group, blowfish_ecb},
@@ -94,6 +95,7 @@ groups() ->
{des3_cbc,[], [block]},
{des_ede3,[], [block]},
{des3_cbf,[], [block]},
+ {des3_cfb,[], [block]},
{rc2_cbc,[], [block]},
{aes_cbc128,[], [block]},
{aes_cfb8,[], [block]},
@@ -356,6 +358,16 @@ block_cipher({Type, Key, PlainText}) ->
ct:fail({{crypto, block_decrypt, [Type, Key, CipherText]}, {expected, Plain}, {got, Other}})
end;
+block_cipher({aes_cfb128, Key, IV, PlainText}) ->
+ Plain = iolist_to_binary(PlainText),
+ CipherText = crypto:aes_cfb_128_encrypt(Key, IV, PlainText),
+ case crypto:block_decrypt(aes_cfb128, Key, IV, CipherText) of
+ Plain ->
+ ok;
+ Other ->
+ ct:fail({{crypto, block_decrypt, [aes_cfb128, Key, IV, CipherText]}, {expected, Plain}, {got, Other}})
+ end;
+
block_cipher({Type, Key, IV, PlainText}) ->
Plain = iolist_to_binary(PlainText),
CipherText = crypto:block_encrypt(Type, Key, IV, PlainText),
@@ -381,11 +393,8 @@ block_cipher({Type, Key, IV, PlainText, CipherText}) ->
ct:fail({{crypto, block_decrypt, [Type, Key, IV, CipherText]}, {expected, Plain}, {got, Other1}})
end.
-block_cipher_increment({Type, Key, IV, PlainTexts}) when Type == des_cbc;
- Type == des3_cbc;
- Type == aes_cbc;
- Type == des_cbf
- ->
+block_cipher_increment({Type, Key, IV, PlainTexts})
+ when Type == des_cbc; Type == aes_cbc; Type == des3_cbc ->
block_cipher_increment(Type, Key, IV, IV, PlainTexts, iolist_to_binary(PlainTexts), []);
block_cipher_increment({Type, Key, IV, PlainTexts, _CipherText}) when Type == aes_cbc ->
Plain = iolist_to_binary(PlainTexts),
@@ -582,6 +591,8 @@ do_block_iolistify({des3_cbc = Type, Key, IV, PlainText}) ->
{Type, Key, IV, des_iolistify(PlainText)};
do_block_iolistify({des3_cbf = Type, Key, IV, PlainText}) ->
{Type, Key, IV, des_iolistify(PlainText)};
+do_block_iolistify({des3_cfb = Type, Key, IV, PlainText}) ->
+ {Type, Key, IV, des_iolistify(PlainText)};
do_block_iolistify({des_ede3 = Type, Key, IV, PlainText}) ->
{Type, Key, IV, des_iolistify(PlainText)};
do_block_iolistify({Type, Key, PlainText}) ->
@@ -792,6 +803,9 @@ group_config(des3_cbc, Config) ->
group_config(des3_cbf, Config) ->
Block = des3_cbf(),
[{block, Block} | Config];
+group_config(des3_cfb, Config) ->
+ Block = des3_cfb(),
+ [{block, Block} | Config];
group_config(des_ede3, Config) ->
Block = des_ede3(),
[{block, Block} | Config];
@@ -1193,7 +1207,16 @@ des_ede3() ->
des3_cbf() ->
[{des3_cbf,
- [hexstr2bin("0123456789abcdef"),
+ [hexstr2bin("0123456789abcdef"),
+ hexstr2bin("fedcba9876543210"),
+ hexstr2bin("0f2d4b6987a5c3e1")],
+ hexstr2bin("1234567890abcdef"),
+ <<"Now is the time for all ">>
+ }].
+
+des3_cfb() ->
+ [{des3_cfb,
+ [hexstr2bin("0123456789abcdef"),
hexstr2bin("fedcba9876543210"),
hexstr2bin("0f2d4b6987a5c3e1")],
hexstr2bin("1234567890abcdef"),
diff --git a/lib/crypto/test/old_crypto_SUITE.erl b/lib/crypto/test/old_crypto_SUITE.erl
index 0d97290d10..324ed39c6d 100644
--- a/lib/crypto/test/old_crypto_SUITE.erl
+++ b/lib/crypto/test/old_crypto_SUITE.erl
@@ -58,6 +58,7 @@
des_cfb_iter/1,
des_ecb/1,
des3_cbc/1,
+ des3_cbf/1,
des3_cfb/1,
rc2_cbc/1,
aes_cfb/1,
@@ -102,7 +103,7 @@ groups() ->
hmac_rfc2202, hmac_rfc4231_sha224, hmac_rfc4231_sha256,
hmac_rfc4231_sha384, hmac_rfc4231_sha512,
des_cbc, aes_cfb, aes_cbc,
- des_cfb, des_cfb_iter, des3_cbc, des3_cfb, rc2_cbc,
+ des_cfb, des_cfb_iter, des3_cbc, des3_cbf, des3_cfb, rc2_cbc,
aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_ecb,
rand_uniform_test, strong_rand_test,
rsa_verify_test, dsa_verify_test, rsa_sign_test,
@@ -969,6 +970,9 @@ des_cbc(doc) ->
des_cbc(suite) ->
[];
des_cbc(Config) when is_list(Config) ->
+ if_supported(des_cbc, fun des_cbc_do/0).
+
+des_cbc_do() ->
?line Key = hexstr2bin("0123456789abcdef"),
?line IVec = hexstr2bin("1234567890abcdef"),
?line Plain = "Now is the time for all ",
@@ -992,6 +996,9 @@ des_cbc_iter(doc) ->
des_cbc_iter(suite) ->
[];
des_cbc_iter(Config) when is_list(Config) ->
+ if_supported(des_cbc, fun des_cbc_iter_do/0).
+
+des_cbc_iter_do() ->
?line Key = hexstr2bin("0123456789abcdef"),
?line IVec = hexstr2bin("1234567890abcdef"),
?line Plain1 = "Now is the time ",
@@ -1011,6 +1018,9 @@ des_cfb(doc) ->
des_cfb(suite) ->
[];
des_cfb(Config) when is_list(Config) ->
+ if_supported(des_cfb, fun des_cfb_do/0).
+
+des_cfb_do() ->
?line Key = hexstr2bin("0123456789abcdef"),
?line IVec = hexstr2bin("1234567890abcdef"),
?line Plain = "Now is the",
@@ -1027,6 +1037,9 @@ des_cfb_iter(doc) ->
des_cfb_iter(suite) ->
[];
des_cfb_iter(Config) when is_list(Config) ->
+ if_supported(des_cfb, fun des_cfb_iter_do/0).
+
+des_cfb_iter_do() ->
?line Key = hexstr2bin("0123456789abcdef"),
?line IVec = hexstr2bin("1234567890abcdef"),
?line Plain1 = "Now i",
@@ -1045,6 +1058,9 @@ des_ecb(doc) ->
des_ecb(suite) ->
[];
des_ecb(Config) when is_list(Config) ->
+ if_supported(des_ecb, fun des_ecb_do/0).
+
+des_ecb_do() ->
?line Key = hexstr2bin("0123456789abcdef"),
?line Cipher1 = crypto:des_ecb_encrypt(Key, "Now is t"),
?line m(Cipher1, hexstr2bin("3fa40e8a984d4815")),
@@ -1064,7 +1080,9 @@ rc2_cbc(doc) ->
"Encrypt and decrypt according to RC2 CBC and check the result. "
"Example stripped out from public_key application test";
rc2_cbc(Config) when is_list(Config) ->
-
+ if_supported(rc2_cbc, fun rc2_cbc_do/0).
+
+rc2_cbc_do() ->
Key = <<146,210,160,124,215,227,153,239,227,17,222,140,3,93,27,191>>,
IV = <<72,91,135,182,25,42,35,210>>,
@@ -1081,6 +1099,9 @@ des3_cbc(doc) ->
des3_cbc(suite) ->
[];
des3_cbc(Config) when is_list(Config) ->
+ if_supported(des3_cbc, fun des3_cbc_do/0).
+
+des3_cbc_do() ->
?line Key1 = hexstr2bin("0123456789abcdef"),
?line Key2 = hexstr2bin("fedcba9876543210"),
?line Key3 = hexstr2bin("0f2d4b6987a5c3e1"),
@@ -1112,6 +1133,19 @@ des3_cbc(Config) when is_list(Config) ->
%%
%%
+des3_cbf(doc) ->
+ "Encrypt and decrypt according to CFB 3DES, and check the result.";
+des3_cbf(suite) ->
+ [];
+des3_cbf(Config) when is_list(Config) ->
+ case openssl_version() of
+ V when V < 16#90705F -> {skipped,"OpenSSL version too old"};
+ _ ->
+ if_supported(des3_cbf, fun des3_cfb_do/0)
+ end.
+
+%%
+%%
des3_cfb(doc) ->
"Encrypt and decrypt according to CFB 3DES, and check the result.";
des3_cfb(suite) ->
@@ -1119,7 +1153,8 @@ des3_cfb(suite) ->
des3_cfb(Config) when is_list(Config) ->
case openssl_version() of
V when V < 16#90705F -> {skipped,"OpenSSL version too old"};
- _ -> des3_cfb_do()
+ _ ->
+ if_supported(des3_cfb, fun des3_cfb_do/0)
end.
des3_cfb_do() ->
@@ -2084,6 +2119,9 @@ rc4_test(doc) ->
rc4_test(suite) ->
[];
rc4_test(Config) when is_list(Config) ->
+ if_supported(rc4, fun rc4_test_do/0).
+
+rc4_test_do() ->
CT1 = <<"Yo baby yo">>,
R1 = <<118,122,68,110,157,166,141,212,139,39>>,
K = "apaapa",
@@ -2099,6 +2137,9 @@ rc4_stream_test(doc) ->
rc4_stream_test(suite) ->
[];
rc4_stream_test(Config) when is_list(Config) ->
+ if_supported(rc4, fun rc4_stream_test_do/0).
+
+rc4_stream_test_do() ->
CT1 = <<"Yo ">>,
CT2 = <<"baby yo">>,
K = "apaapa",
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 96466869d1..f3e0623ac9 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 3.7
+CRYPTO_VSN = 3.7.4
diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile
index 6c9617ca69..0f724b6f17 100644
--- a/lib/debugger/doc/src/Makefile
+++ b/lib/debugger/doc/src/Makefile
@@ -114,7 +114,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
diff --git a/lib/debugger/doc/src/i.xml b/lib/debugger/doc/src/i.xml
index db89f23494..628b91e9e4 100644
--- a/lib/debugger/doc/src/i.xml
+++ b/lib/debugger/doc/src/i.xml
@@ -45,7 +45,7 @@
attached manually or automatically.</p>
<p>By preference, these functions can be included in module
- <seealso marker="stdlib:shell_default"><c>stdlib:shell_default</c></seealso>.
+ <seealso marker="stdlib:shell_default"><c>shell_default</c></seealso>.
By default, they are included in that module.</p>
</description>
@@ -372,7 +372,7 @@
</fsummary>
<desc>
<p>Returns the current version number of the interpreter.
- Same as the version number of the <c>Debugger</c> application.</p>
+ Same as the version number of the Debugger application.</p>
</desc>
</func>
diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml
index 2e0d834269..93bc46ddbe 100644
--- a/lib/debugger/doc/src/notes.xml
+++ b/lib/debugger/doc/src/notes.xml
@@ -33,6 +33,22 @@
<p>This document describes the changes made to the Debugger
application.</p>
+<section><title>Debugger 4.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Debugger 4.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl
index 3561454685..3e959e8e30 100644
--- a/lib/debugger/src/dbg_iserver.erl
+++ b/lib/debugger/src/dbg_iserver.erl
@@ -214,7 +214,6 @@ handle_call({new_process, Pid, Meta, Function}, _From, State) ->
%% Code loading
handle_call({load, Mod, Src, Bin}, _From, State) ->
-
%% Create an ETS table for storing information about the module
Db = State#state.db,
ModDb = ets:new(Mod, [ordered_set, public]),
@@ -285,23 +284,28 @@ handle_call({contents, Mod, Pid}, _From, State) ->
{reply, {ok, Bin}, State};
handle_call({raw_contents, Mod, Pid}, _From, State) ->
Db = State#state.db,
- [{{Mod, refs}, ModDbs}] = ets:lookup(Db, {Mod, refs}),
- ModDb = if
- Pid =:= any -> hd(ModDbs);
- true ->
- lists:foldl(fun(T, not_found) ->
- [{T, Pids}] = ets:lookup(Db, T),
- case lists:member(Pid, Pids) of
- true -> T;
- false -> not_found
- end;
- (_T, T) -> T
- end,
- not_found,
- ModDbs)
- end,
- [{mod_raw, Bin}] = ets:lookup(ModDb, mod_raw),
- {reply, {ok, Bin}, State};
+ case ets:lookup(Db, {Mod, refs}) of
+ [{{Mod, refs}, ModDbs}] ->
+ ModDb =
+ if
+ Pid =:= any -> hd(ModDbs);
+ true ->
+ lists:foldl(fun(T, not_found) ->
+ [{T, Pids}] = ets:lookup(Db, T),
+ case lists:member(Pid, Pids) of
+ true -> T;
+ false -> not_found
+ end;
+ (_T, T) -> T
+ end,
+ not_found,
+ ModDbs)
+ end,
+ [{mod_raw, Bin}] = ets:lookup(ModDb, mod_raw),
+ {reply, {ok, Bin}, State};
+ [] -> % code not interpreted
+ {reply, not_found, State}
+ end;
handle_call({is_interpreted, Mod, Name, Arity}, _From, State) ->
Db = State#state.db,
Reply = case ets:lookup(Db, {Mod, refs}) of
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index 6af19af33b..29c8e8cefb 100644
--- a/lib/debugger/src/dbg_wx_trace.erl
+++ b/lib/debugger/src/dbg_wx_trace.erl
@@ -818,11 +818,14 @@ gui_show_module(Win, Mod, Line, _Cm, Pid, How) ->
gui_load_module(Win, Mod, _Pid) ->
dbg_wx_trace_win:display(Win,{text, "Loading module..."}),
- %% Contents = int:contents(Mod, Pid),
- {ok, Contents} = dbg_iserver:call({raw_contents, Mod, any}),
- Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents),
- dbg_wx_trace_win:display(Win,{text, ""}),
- Win2.
+ case dbg_iserver:call({raw_contents, Mod, any}) of
+ {ok, Contents} ->
+ Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents),
+ dbg_wx_trace_win:display(Win,{text, ""}),
+ Win2;
+ not_found ->
+ dbg_wx_trace_win:show_no_code(Win)
+ end.
gui_update_bindings(Win,Meta) ->
Bs = int:meta(Meta, bindings, nostack),
diff --git a/lib/debugger/src/dbg_wx_view.erl b/lib/debugger/src/dbg_wx_view.erl
index 91fc0d08cb..86d009238f 100644
--- a/lib/debugger/src/dbg_wx_view.erl
+++ b/lib/debugger/src/dbg_wx_view.erl
@@ -263,7 +263,11 @@ shortcut(_) -> false.
gui_load_module(Win, Mod) ->
dbg_wx_trace_win:display(Win,{text, "Loading module..."}),
- {ok, Contents} = dbg_iserver:call({raw_contents, Mod, any}),
- Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents),
- dbg_wx_trace_win:display(Win,{text, ""}),
- Win2.
+ case dbg_iserver:call({raw_contents, Mod, any}) of
+ {ok, Contents} ->
+ Win2 = dbg_wx_trace_win:show_code(Win, Mod, Contents),
+ dbg_wx_trace_win:display(Win,{text, ""}),
+ Win2;
+ not_found ->
+ dbg_wx_trace_win:show_no_code(Win)
+ end.
diff --git a/lib/debugger/src/dbg_wx_win.erl b/lib/debugger/src/dbg_wx_win.erl
index 25ffc5054c..d302423077 100644
--- a/lib/debugger/src/dbg_wx_win.erl
+++ b/lib/debugger/src/dbg_wx_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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.
diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk
index dd496013cd..f5440865ef 100644
--- a/lib/debugger/vsn.mk
+++ b/lib/debugger/vsn.mk
@@ -1 +1 @@
-DEBUGGER_VSN = 4.2
+DEBUGGER_VSN = 4.2.1
diff --git a/lib/dialyzer/RELEASE_NOTES b/lib/dialyzer/RELEASE_NOTES
index 4e311bb543..2457faa07a 100644
--- a/lib/dialyzer/RELEASE_NOTES
+++ b/lib/dialyzer/RELEASE_NOTES
@@ -135,7 +135,7 @@ Version 1.9.1 (in Erlang/OTP R13B)
Version 1.9.0 (in Erlang/OTP R13A)
----------------------------------
- The analysis accepts opaque type declarations and detects violations of
- opaqueness of terms of such types. Starting with R13, many Erlang/OTP
+ opacity of terms of such types. Starting with R13, many Erlang/OTP
standard libraries (array, dict, digraph, ets, gb_sets, gb_trees, queue,
and sets) contain opaque type declarations of their main data types.
Dialyzer will spit out warnings in code that explicitly depends on the
diff --git a/lib/dialyzer/doc/manual.txt b/lib/dialyzer/doc/manual.txt
index be1fd2f8bc..a571cd2e2b 100644
--- a/lib/dialyzer/doc/manual.txt
+++ b/lib/dialyzer/doc/manual.txt
@@ -255,7 +255,7 @@ Warning options:
-Wno_match
Suppress warnings for patterns that are unused or cannot match.
-Wno_opaque
- Suppress warnings for violations of opaqueness of data types.
+ Suppress warnings for violations of opacity of data types.
-Wunmatched_returns ***
Include warnings for function calls which ignore a structured return
value or do not match against one of many possible return value(s).
diff --git a/lib/dialyzer/doc/src/book.xml b/lib/dialyzer/doc/src/book.xml
index aecc0e5bfa..46df8b81b8 100644
--- a/lib/dialyzer/doc/src/book.xml
+++ b/lib/dialyzer/doc/src/book.xml
@@ -25,7 +25,7 @@
<title>Dialyzer</title>
<prepared></prepared>
<docno></docno>
- <date></date>
+ <date>2016-09-19</date>
<rev></rev>
<file>book.xml</file>
</header>
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 619db125b1..4b7eb4ad68 100644
--- a/lib/dialyzer/doc/src/dialyzer.xml
+++ b/lib/dialyzer/doc/src/dialyzer.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2006</year><year>2015</year>
+ <year>2006</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -25,341 +25,477 @@
<title>dialyzer</title>
<prepared></prepared>
<docno></docno>
- <date></date>
+ <date>2016-09-20</date>
<rev></rev>
+ <file>dialyzer.xml</file>
</header>
<module>dialyzer</module>
- <modulesummary>The Dialyzer, a DIscrepancy AnalYZer for ERlang programs</modulesummary>
+ <modulesummary>Dialyzer, a DIscrepancy AnaLYZer for ERlang programs.
+ </modulesummary>
<description>
- <p>The Dialyzer is a static analysis tool that identifies software
- discrepancies such as definite type errors, code which has become
- dead or unreachable due to some programming error, unnecessary
- tests, etc. in single Erlang modules or entire (sets of)
- applications. Dialyzer starts its analysis from either
- debug-compiled BEAM bytecode or from Erlang source code. The file
- and line number of a discrepancy is reported along with an
- indication of what the discrepancy is about. Dialyzer bases its
- analysis on the concept of success typings which allows for sound
- warnings (no false positives).</p>
- <p>Read more about Dialyzer and about how to use it from the GUI
- in <seealso marker="dialyzer_chapter">Dialyzer User's
- Guide</seealso>.</p>
+ <p>Dialyzer is a static analysis tool that identifies software
+ discrepancies, such as definite type errors, code that has become dead
+ or unreachable because of programming error, and unnecessary tests,
+ in single Erlang modules or entire (sets of) applications.</p>
+
+ <p>Dialyzer starts its analysis from either
+ debug-compiled BEAM bytecode or from Erlang source code. The file
+ and line number of a discrepancy is reported along with an
+ indication of what the discrepancy is about. Dialyzer bases its
+ analysis on the concept of success typings, which allows for sound
+ warnings (no false positives).</p>
</description>
<section>
- <title>Using the Dialyzer from the command line</title>
- <p>Dialyzer also has a command line version for automated use. Below is a
- brief description of the list of its options. The same information can
- be obtained by writing</p>
- <code type="none">
- dialyzer --help</code>
- <p>in a shell. Please refer to the GUI description for more details on
- the operation of Dialyzer.</p>
- <p>The exit status of the command line version is:</p>
+ <marker id="command_line"></marker>
+ <title>Using Dialyzer from the Command Line</title>
+ <p>Dialyzer has a command-line version for automated use. This
+ section provides a brief description of the options. The same information
+ can be obtained by writing the following in a shell:</p>
+
<code type="none">
- 0 - No problems were encountered during the analysis and no
- warnings were emitted.
- 1 - Problems were encountered during the analysis.
- 2 - No problems were encountered, but warnings were emitted.</code>
- <p>Usage:</p>
+dialyzer --help</code>
+
+ <p>For more details about the operation of Dialyzer, see section
+ <seealso marker="dialyzer_chapter#dialyzer_gui">
+ Using Dialyzer from the GUI</seealso> in the User's Guide.</p>
+
+ <p><em>Exit status of the command-line version:</em></p>
+
+ <taglist>
+ <tag><c>0</c></tag>
+ <item>
+ <p>No problems were found during the analysis and no warnings were
+ emitted.</p>
+ </item>
+ <tag><c>1</c></tag>
+ <item>
+ <p>Problems were found during the analysis.</p>
+ </item>
+ <tag><c>2</c></tag>
+ <item>
+ <p>No problems were found during the analysis, but warnings were
+ emitted.</p>
+ </item>
+ </taglist>
+
+ <p><em>Usage:</em></p>
+
<code type="none">
- dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
- [-pa dir]* [--plt plt] [--plts plt*] [-Ddefine]*
- [-I include_dir]* [--output_plt file] [-Wwarn]* [--raw]
- [--src] [--gui] [files_or_dirs] [-r dirs]
- [--apps applications] [-o outfile]
- [--build_plt] [--add_to_plt] [--remove_from_plt]
- [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
- [--dump_callgraph file] [--no_native] [--fullpath]
- [--statistics] [--no_native_cache]</code>
- <p>Options:</p>
+dialyzer [--add_to_plt] [--apps applications] [--build_plt]
+ [--check_plt] [-Ddefine]* [-Dname] [--dump_callgraph file]
+ [files_or_dirs] [--fullpath] [--get_warnings] [--gui] [--help]
+ [-I include_dir]* [--no_check_plt] [--no_native]
+ [--no_native_cache] [-o outfile] [--output_plt file] [-pa dir]*
+ [--plt plt] [--plt_info] [--plts plt*] [--quiet] [-r dirs]
+ [--raw] [--remove_from_plt] [--shell] [--src] [--statistics]
+ [--verbose] [--version] [-Wwarn]*</code>
+
+ <note>
+ <p>* denotes that multiple occurrences of the option are possible.</p>
+ </note>
+
+ <p><em>Options:</em></p>
+
<taglist>
- <tag><c><![CDATA[files_or_dirs]]></c> (for backwards compatibility also
- as: <c><![CDATA[-c files_or_dirs]]></c>)</tag>
- <item>Use Dialyzer from the command line to detect defects in the
- specified files or directories containing <c><![CDATA[.erl]]></c> or
- <c><![CDATA[.beam]]></c> files, depending on the type of the
- analysis.</item>
- <tag><c><![CDATA[-r dirs]]></c></tag>
- <item>Same as the previous but the specified directories are searched
- recursively for subdirectories containing <c><![CDATA[.erl]]></c> or
- <c><![CDATA[.beam]]></c> files in them, depending on the type of
- analysis.</item>
- <tag><c><![CDATA[--apps applications]]></c></tag>
- <item>Option typically used when building or modifying a plt as in:
+ <tag><c>--add_to_plt</c></tag>
+ <item>
+ <p>The PLT is extended to also include the files specified with
+ <c>-c</c> and <c>-r</c>. Use
+ <c>--plt</c> to specify which PLT to start from,
+ and <c>--output_plt</c> to specify where to put the PLT.
+ Notice that the analysis possibly can include files from the PLT if
+ they depend on the new files. This option only works for BEAM
+ files.</p>
+ </item>
+ <tag><c>--apps applications</c></tag>
+ <item>
+ <p>This option is typically used when building or modifying a PLT as
+ in:</p>
<code type="none">
- dialyzer --build_plt --apps erts kernel stdlib mnesia ...</code>
- to conveniently refer to library applications corresponding to the
- Erlang/OTP installation. However, the option is general and can also
- be used during analysis in order to refer to Erlang/OTP applications.
- In addition, file or directory names can also be included, as in:
+dialyzer --build_plt --apps erts kernel stdlib mnesia ...</code>
+ <p>to refer conveniently to library applications corresponding to the
+ Erlang/OTP installation. However, this option is general and can also
+ be used during analysis to refer to Erlang/OTP applications.
+ File or directory names can also be included, as in:</p>
<code type="none">
- dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam</code></item>
- <tag><c><![CDATA[-o outfile]]></c> (or
- <c><![CDATA[--output outfile]]></c>)</tag>
- <item>When using Dialyzer from the command line, send the analysis
- results to the specified outfile rather than to stdout.</item>
- <tag><c><![CDATA[--raw]]></c></tag>
- <item>When using Dialyzer from the command line, output the raw analysis
- results (Erlang terms) instead of the formatted result. The raw format
- is easier to post-process (for instance, to filter warnings or to
- output HTML pages).</item>
- <tag><c><![CDATA[--src]]></c></tag>
- <item>Override the default, which is to analyze BEAM files, and
- analyze starting from Erlang source code instead.</item>
- <tag><c><![CDATA[-Dname]]></c> (or <c><![CDATA[-Dname=value]]></c>)</tag>
- <item>When analyzing from source, pass the define to Dialyzer. (**)</item>
- <tag><c><![CDATA[-I include_dir]]></c></tag>
- <item>When analyzing from source, pass the <c><![CDATA[include_dir]]></c>
- to Dialyzer. (**)</item>
- <tag><c><![CDATA[-pa dir]]></c></tag>
- <item>Include <c><![CDATA[dir]]></c> in the path for Erlang (useful when
- analyzing files that have <c><![CDATA['-include_lib()']]></c>
- directives).</item>
- <tag><c><![CDATA[--output_plt file]]></c></tag>
- <item>Store the plt at the specified file after building it.</item>
- <tag><c><![CDATA[--plt plt]]></c></tag>
- <item>Use the specified plt as the initial plt (if the plt was built
- during setup the files will be checked for consistency).</item>
- <tag><c><![CDATA[--plts plt*]]></c></tag>
- <item>Merge the specified plts to create the initial plt -- requires
- that the plts are disjoint (i.e., do not have any module
- appearing in more than one plt).
- The plts are created in the usual way:
+dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam</code>
+ </item>
+ <tag><c>--build_plt</c></tag>
+ <item>
+ <p>The analysis starts from an empty PLT and creates a new one from
+ the files specified with <c>-c</c> and
+ <c>-r</c>. This option only works for BEAM files.
+ To override the default PLT location, use
+ <c>--plt</c> or <c>--output_plt</c>.</p>
+ </item>
+ <tag><c>--check_plt</c></tag>
+ <item>
+ <p>Check the PLT for consistency and rebuild it if it is not
+ up-to-date.</p>
+ </item>
+ <tag><c>-Dname</c> (or <c>-Dname=value</c>)</tag>
+ <item>
+ <p>When analyzing from source, pass the define to Dialyzer.
+ (**)</p>
+ </item>
+ <tag><c>--dump_callgraph file</c></tag>
+ <item>
+ <p>Dump the call graph into the specified file whose format is
+ determined by the filename extension. Supported extensions are:
+ <c>raw</c>, <c>dot</c>, and <c>ps</c>. If something else is used as
+ filename extension, default format <c>.raw</c> is used.</p>
+ </item>
+ <tag><c>files_or_dirs</c> (for backward compatibility also
+ as <c>-c files_or_dirs</c>)</tag>
+ <item>
+ <p>Use Dialyzer from the command line to detect defects in the
+ specified files or directories containing <c>.erl</c> or
+ <c>.beam</c> files, depending on the type of the
+ analysis.</p>
+ </item>
+ <tag><c>--fullpath</c></tag>
+ <item>
+ <p>Display the full path names of files for which warnings are
+ emitted.</p>
+ </item>
+ <tag><c>--get_warnings</c></tag>
+ <item>
+ <p>Make Dialyzer emit warnings even when manipulating the PLT.
+ Warnings are only emitted for files that are analyzed.</p>
+ </item>
+ <tag><c>--gui</c></tag>
+ <item>
+ <p>Use the GUI.</p></item>
+ <tag><c>--help</c> (or <c>-h</c>)</tag>
+ <item>
+ <p>Print this message and exit.</p>
+ </item>
+ <tag><c>-I include_dir</c></tag>
+ <item>
+ <p>When analyzing from source, pass the <c>include_dir</c>
+ to Dialyzer. (**)</p>
+ </item>
+ <tag><c>--no_check_plt</c></tag>
+ <item>
+ <p>Skip the PLT check when running Dialyzer. This is useful when
+ working with installed PLTs that never change.</p>
+ </item>
+ <tag><c>--no_native</c> (or <c>-nn</c>)</tag>
+ <item>
+ <p>Bypass the native code compilation of some key files that
+ Dialyzer heuristically performs when dialyzing many files.
+ This avoids the compilation time, but can result in (much) longer
+ analysis time.</p>
+ </item>
+ <tag><c>--no_native_cache</c></tag>
+ <item>
+ <p>By default, Dialyzer caches the results of native compilation
+ in directory <c>$XDG_CACHE_HOME/erlang/dialyzer_hipe_cache</c>.
+ <c>XDG_CACHE_HOME</c> defaults to <c>$HOME/.cache</c>.
+ Use this option to disable caching.</p>
+ </item>
+ <tag><c>-o outfile</c> (or
+ <c>--output outfile</c>)</tag>
+ <item>
+ <p>When using Dialyzer from the command line, send the analysis
+ results to the specified outfile rather than to <c>stdout</c>.</p>
+ </item>
+ <tag><c>--output_plt file</c></tag>
+ <item>
+ <p>Store the PLT at the specified file after building it.</p>
+ </item>
+ <tag><c>-pa dir</c></tag>
+ <item>
+ <p>Include <c>dir</c> in the path for Erlang. This is useful
+ when analyzing files that have <c>-include_lib()</c>
+ directives.</p>
+ </item>
+ <tag><c>--plt plt</c></tag>
+ <item>
+ <p>Use the specified PLT as the initial PLT. If the PLT was built
+ during setup, the files are checked for consistency.</p>
+ </item>
+ <tag><c>--plt_info</c></tag>
+ <item>
+ <p>Make Dialyzer print information about the PLT and then quit.
+ The PLT can be specified with <c>--plt(s)</c>.</p>
+ </item>
+ <tag><c>--plts plt*</c></tag>
+ <item>
+ <p>Merge the specified PLTs to create the initial PLT. This requires
+ that the PLTs are disjoint (that is, do not have any module
+ appearing in more than one PLT).
+ The PLTs are created in the usual way:</p>
<code type="none">
- dialyzer --build_plt --output_plt plt_1 files_to_include
- ...
- dialyzer --build_plt --output_plt plt_n files_to_include</code>
- and then can be used in either of the following ways:
+dialyzer --build_plt --output_plt plt_1 files_to_include
+...
+dialyzer --build_plt --output_plt plt_n files_to_include</code>
+ <p>They can then be used in either of the following ways:</p>
<code type="none">
- dialyzer files_to_analyze --plts plt_1 ... plt_n</code>
- or:
+dialyzer files_to_analyze --plts plt_1 ... plt_n</code>
+ <p>or</p>
<code type="none">
- dialyzer --plts plt_1 ... plt_n -- files_to_analyze</code>
- (Note the -- delimiter in the second case)</item>
- <tag><c><![CDATA[-Wwarn]]></c></tag>
- <item>A family of options which selectively turn on/off warnings
- (for help on the names of warnings use
- <c><![CDATA[dialyzer -Whelp]]></c>).
- Note that the options can also be given in the file with a
- <c>-dialyzer()</c> attribute. See <seealso
- marker="#suppression">Requesting or Suppressing Warnings in
- Source Files</seealso> below for details.</item>
- <tag><c><![CDATA[--shell]]></c></tag>
- <item>Do not disable the Erlang shell while running the GUI.</item>
- <tag><c><![CDATA[--version]]></c> (or <c><![CDATA[-v]]></c>)</tag>
- <item>Print the Dialyzer version and some more information and
- exit.</item>
- <tag><c><![CDATA[--help]]></c> (or <c><![CDATA[-h]]></c>)</tag>
- <item>Print this message and exit.</item>
- <tag><c><![CDATA[--quiet]]></c> (or <c><![CDATA[-q]]></c>)</tag>
- <item>Make Dialyzer a bit more quiet.</item>
- <tag><c><![CDATA[--verbose]]></c></tag>
- <item>Make Dialyzer a bit more verbose.</item>
- <tag><c><![CDATA[--statistics]]></c></tag>
- <item>Prints information about the progress of execution (analysis phases,
- time spent in each and size of the relative input).</item>
- <tag><c><![CDATA[--build_plt]]></c></tag>
- <item>The analysis starts from an empty plt and creates a new one from
- the files specified with <c><![CDATA[-c]]></c> and
- <c><![CDATA[-r]]></c>. Only works for beam files. Use
- <c><![CDATA[--plt]]></c> or <c><![CDATA[--output_plt]]></c> to
- override the default plt location.</item>
- <tag><c><![CDATA[--add_to_plt]]></c></tag>
- <item>The plt is extended to also include the files specified with
- <c><![CDATA[-c]]></c> and <c><![CDATA[-r]]></c>. Use
- <c><![CDATA[--plt]]></c> to specify which plt to start from,
- and <c><![CDATA[--output_plt]]></c> to specify where to put the plt.
- Note that the analysis might include files from the plt if they depend
- on the new files. This option only works with beam files.</item>
- <tag><c><![CDATA[--remove_from_plt]]></c></tag>
- <item>The information from the files specified with
- <c><![CDATA[-c]]></c> and <c><![CDATA[-r]]></c> is removed
- from the plt. Note that this may cause a re-analysis of the remaining
- dependent files.</item>
- <tag><c><![CDATA[--check_plt]]></c></tag>
- <item>Check the plt for consistency and rebuild it if it is not
- up-to-date.</item>
- <tag><c><![CDATA[--no_check_plt]]></c></tag>
- <item>Skip the plt check when running Dialyzer. Useful when working with
- installed plts that never change.</item>
- <tag><c><![CDATA[--plt_info]]></c></tag>
- <item>Make Dialyzer print information about the plt and then quit. The
- plt can be specified with <c><![CDATA[--plt(s)]]></c>.</item>
- <tag><c><![CDATA[--get_warnings]]></c></tag>
- <item>Make Dialyzer emit warnings even when manipulating the plt.
- Warnings are only emitted for files that are actually analyzed.</item>
- <tag><c><![CDATA[--dump_callgraph file]]></c></tag>
- <item>Dump the call graph into the specified file whose format is
- determined by the file name extension. Supported extensions are: raw,
- dot, and ps. If something else is used as file name extension, default
- format '.raw' will be used.</item>
- <tag><c><![CDATA[--no_native]]></c> (or <c><![CDATA[-nn]]></c>)</tag>
- <item>Bypass the native code compilation of some key files that Dialyzer
- heuristically performs when dialyzing many files; this avoids the
- compilation time but it may result in (much) longer analysis
- time.</item>
- <tag><c><![CDATA[--no_native_cache]]></c></tag>
- <item>By default, Dialyzer caches the results of native compilation in the
- <c>$XDG_CACHE_HOME/erlang/dialyzer_hipe_cache</c> directory.
- <c>XDG_CACHE_HOME</c> defaults to <c>$HOME/.cache</c>.
- Use this option to disable caching.</item>
- <tag><c><![CDATA[--fullpath]]></c></tag>
- <item>Display the full path names of files for which warnings are emitted.</item>
- <tag><c><![CDATA[--gui]]></c></tag>
- <item>Use the GUI.</item>
+dialyzer --plts plt_1 ... plt_n -- files_to_analyze</code>
+ <p>Notice the <c>--</c> delimiter in the second case.</p>
+ </item>
+ <tag><c>--quiet</c> (or <c>-q</c>)</tag>
+ <item>
+ <p>Make Dialyzer a bit more quiet.</p>
+ </item>
+ <tag><c>-r dirs</c></tag>
+ <item>
+ <p>Same as <c>files_or_dirs</c>, but the specified
+ directories are searched
+ recursively for subdirectories containing <c>.erl</c> or
+ <c>.beam</c> files in them, depending on the type of
+ analysis.</p>
+ </item>
+ <tag><c>--raw</c></tag>
+ <item>
+ <p>When using Dialyzer from the command line, output the raw
+ analysis results (Erlang terms) instead of the formatted result.
+ The raw format
+ is easier to post-process (for example, to filter warnings or to
+ output HTML pages).</p>
+ </item>
+ <tag><c>--remove_from_plt</c></tag>
+ <item>
+ <p>The information from the files specified with
+ <c>-c</c> and <c>-r</c> is removed from
+ the PLT. Notice that this can cause a reanalysis of the remaining
+ dependent files.</p>
+ </item>
+ <tag><c>--shell</c></tag>
+ <item>
+ <p>Do not disable the Erlang shell while running the GUI.</p>
+ </item>
+ <tag><c>--src</c></tag>
+ <item>
+ <p>Override the default, which is to analyze BEAM files, and
+ analyze starting from Erlang source code instead.</p>
+ </item>
+ <tag><c>--statistics</c></tag>
+ <item>
+ <p>Print information about the progress of execution (analysis phases,
+ time spent in each, and size of the relative input).</p>
+ </item>
+ <tag><c>--verbose</c></tag>
+ <item>
+ <p>Make Dialyzer a bit more verbose.</p>
+ </item>
+ <tag><c>--version</c> (or <c>-v</c>)</tag>
+ <item>
+ <p>Print the Dialyzer version and some more information and
+ exit.</p>
+ </item>
+ <tag><c>-Wwarn</c></tag>
+ <item>
+ <p>A family of options that selectively turn on/off warnings.
+ (For help on the names of warnings, use
+ <c>dialyzer -Whelp</c>.)
+ Notice that the options can also be specified in the file with a
+ <c>-dialyzer()</c> attribute. For details, see section <seealso
+ marker="#suppression">Requesting or Suppressing Warnings in
+ Source Files</seealso>.</p>
+ </item>
</taglist>
+
<note>
- <p>* denotes that multiple occurrences of these options are possible.</p>
- <p>** options <c><![CDATA[-D]]></c> and <c><![CDATA[-I]]></c> work both from command-line and in the Dialyzer GUI;
- the syntax of defines and includes is the same as that used by <c><![CDATA[erlc]]></c>.</p>
+ <p>** options <c>-D</c> and <c>-I</c> work both
+ from the command line and in the Dialyzer GUI; the syntax of
+ defines and includes is the same as that used by
+ <seealso marker="erts:erlc">erlc(1)</seealso>.</p>
</note>
- <p>Warning options:</p>
+
+ <p><em>Warning options:</em></p>
+
<taglist>
- <tag><c><![CDATA[-Wno_return]]></c></tag>
- <item>Suppress warnings for functions that will never return a
- value.</item>
- <tag><c><![CDATA[-Wno_unused]]></c></tag>
- <item>Suppress warnings for unused functions.</item>
- <tag><c><![CDATA[-Wno_improper_lists]]></c></tag>
- <item>Suppress warnings for construction of improper lists.</item>
- <tag><c><![CDATA[-Wno_fun_app]]></c></tag>
- <item>Suppress warnings for fun applications that will fail.</item>
- <tag><c><![CDATA[-Wno_match]]></c></tag>
- <item>Suppress warnings for patterns that are unused or cannot
- match.</item>
- <tag><c><![CDATA[-Wno_opaque]]></c></tag>
- <item>Suppress warnings for violations of opaqueness of data types.</item>
- <tag><c><![CDATA[-Wno_fail_call]]></c></tag>
- <item>Suppress warnings for failing calls.</item>
- <tag><c><![CDATA[-Wno_contracts]]></c></tag>
- <item>Suppress warnings about invalid contracts.</item>
- <tag><c><![CDATA[-Wno_behaviours]]></c></tag>
- <item>Suppress warnings about behaviour callbacks which drift from the
- published recommended interfaces.</item>
- <tag><c><![CDATA[-Wno_missing_calls]]></c></tag>
- <item>Suppress warnings about calls to missing functions.</item>
- <tag><c><![CDATA[-Wno_undefined_callbacks]]></c></tag>
- <item>Suppress warnings about behaviours that have no
- <c>-callback</c> attributes for their callbacks.</item>
- <tag><c><![CDATA[-Wunmatched_returns]]></c>***</tag>
- <item>Include warnings for function calls which ignore a structured return
- value or do not match against one of many possible return
- value(s).</item>
- <tag><c><![CDATA[-Werror_handling]]></c>***</tag>
- <item>Include warnings for functions that only return by means of an
- exception.</item>
- <tag><c><![CDATA[-Wrace_conditions]]></c>***</tag>
- <item>Include warnings for possible race conditions. Note that the
- analysis that finds data races performs intra-procedural data flow analysis
- and can sometimes explode in time. Enable it at your own risk.
- </item>
- <tag><c><![CDATA[-Wunderspecs]]></c>***</tag>
- <item>Warn about underspecified functions
- (the -spec is strictly more allowing than the success typing).</item>
- <tag><c><![CDATA[-Wunknown]]></c>***</tag>
- <item>Let warnings about unknown functions and types affect the
- exit status of the command line version. The default is to ignore
- warnings about unknown functions and types when setting the exit
- status. When using the Dialyzer from Erlang, warnings about unknown
- functions and types are returned; the default is not to return
- these warnings.</item>
+ <tag><c>-Werror_handling</c> (***)</tag>
+ <item>
+ <p>Include warnings for functions that only return by an exception.</p>
+ </item>
+ <tag><c>-Wno_behaviours</c></tag>
+ <item>
+ <p>Suppress warnings about behavior callbacks that drift from the
+ published recommended interfaces.</p>
+ </item>
+ <tag><c>-Wno_contracts</c></tag>
+ <item>
+ <p>Suppress warnings about invalid contracts.</p>
+ </item>
+ <tag><c>-Wno_fail_call</c></tag>
+ <item>
+ <p>Suppress warnings for failing calls.</p>
+ </item>
+ <tag><c>-Wno_fun_app</c></tag>
+ <item>
+ <p>Suppress warnings for fun applications that will fail.</p>
+ </item>
+ <tag><c>-Wno_improper_lists</c></tag>
+ <item>
+ <p>Suppress warnings for construction of improper lists.</p>
+ </item>
+ <tag><c>-Wno_match</c></tag>
+ <item>
+ <p>Suppress warnings for patterns that are unused or cannot match.</p>
+ </item>
+ <tag><c>-Wno_missing_calls</c></tag>
+ <item>
+ <p>Suppress warnings about calls to missing functions.</p>
+ </item>
+ <tag><c>-Wno_opaque</c></tag>
+ <item>
+ <p>Suppress warnings for violations of opacity of data types.</p>
+ </item>
+ <tag><c>-Wno_return</c></tag>
+ <item>
+ <p>Suppress warnings for functions that will never return a value.</p>
+ </item>
+ <tag><c>-Wno_undefined_callbacks</c></tag>
+ <item>
+ <p>Suppress warnings about behaviors that have no
+ <c>-callback</c> attributes for their callbacks.</p>
+ </item>
+ <tag><c>-Wno_unused</c></tag>
+ <item>
+ <p>Suppress warnings for unused functions.</p>
+ </item>
+ <tag><c>-Wrace_conditions</c> (***)</tag>
+ <item>
+ <p>Include warnings for possible race conditions. Notice that the
+ analysis that finds data races performs intra-procedural data flow
+ analysis and can sometimes explode in time. Enable it at your own
+ risk.</p>
+ </item>
+ <tag><c>-Wunderspecs</c> (***)</tag>
+ <item>
+ <p>Warn about underspecified functions (the specification is strictly
+ more allowing than the success typing).</p>
+ </item>
+ <tag><c>-Wunknown</c> (***)</tag>
+ <item>
+ <p>Let warnings about unknown functions and types affect the
+ exit status of the command-line version. The default is to ignore
+ warnings about unknown functions and types when setting the exit
+ status. When using Dialyzer from Erlang, warnings about unknown
+ functions and types are returned; the default is not to return
+ these warnings.</p>
+ </item>
+ <tag><c>-Wunmatched_returns</c> (***)</tag>
+ <item>
+ <p>Include warnings for function calls that ignore a structured return
+ value or do not match against one of many possible return
+ value(s).</p>
+ </item>
</taglist>
- <p>The following options are also available but their use is not
- recommended: (they are mostly for Dialyzer developers and internal
- debugging)</p>
+
+ <p>The following options are also available, but their use is not
+ recommended (they are mostly for Dialyzer developers and internal
+ debugging):</p>
+
<taglist>
- <tag><c><![CDATA[-Woverspecs]]></c>***</tag>
- <item>Warn about overspecified functions
- (the -spec is strictly less allowing than the success typing).</item>
- <tag><c><![CDATA[-Wspecdiffs]]></c>***</tag>
- <item>Warn when the -spec is different than the success typing.</item>
+ <tag><c>-Woverspecs</c> (***)</tag>
+ <item>
+ <p>Warn about overspecified functions (the specification is strictly
+ less allowing than the success typing).</p>
+ </item>
+ <tag><c>-Wspecdiffs</c> (***)</tag>
+ <item>
+ <p>Warn when the specification is different than the success typing.</p>
+ </item>
</taglist>
+
<note>
- <p>*** Identifies options that turn on warnings rather than
- turning them off.</p>
+ <p>*** denotes options that turn on warnings rather than
+ turning them off.</p>
</note>
</section>
<section>
- <title>Using the Dialyzer from Erlang</title>
- <p>You can also use Dialyzer directly from Erlang. Both the GUI and the
- command line versions are available. The options are similar to the ones
- given from the command line, so please refer to the sections above for
- a description of these.</p>
+ <title>Using Dialyzer from Erlang</title>
+ <p>Dialyzer can be used directly from Erlang. Both the GUI and the
+ command-line versions are also available. The options are similar to the
+ ones given from the command line, see section
+ <seealso marker="#command_line">
+ Using Dialyzer from the Command Line</seealso>.</p>
</section>
<section>
<marker id="suppression"></marker>
<title>Requesting or Suppressing Warnings in Source Files</title>
- <p>
- The <c>-dialyzer()</c> attribute can be used for turning off
+ <p>Attribute <c>-dialyzer()</c> can be used for turning off
warnings in a module by specifying functions or warning options.
For example, to turn off all warnings for the function
- <c>f/0</c>, include the following line:
- </p>
-<code type="none">
--dialyzer({nowarn_function, f/0}).
-</code>
+ <c>f/0</c>, include the following line:</p>
+
+ <code type="none">
+-dialyzer({nowarn_function, f/0}).</code>
+
<p>To turn off warnings for improper lists, add the following line
- to the source file:
- </p>
-<code type="none">
--dialyzer(no_improper_lists).
-</code>
- <p>The <c>-dialyzer()</c> attribute is allowed after function
- declarations. Lists of warning options or functions are allowed:
- </p>
-<code type="none">
--dialyzer([{nowarn_function, [f/0]}, no_improper_lists]).
-</code>
- <p>
- Warning options can be restricted to functions:
- </p>
-<code type="none">
--dialyzer({no_improper_lists, g/0}).
-</code>
-<code type="none">
--dialyzer({[no_return, no_match], [g/0, h/0]}).
-</code>
- <p>
- For help on the warning options use <c>dialyzer -Whelp</c>. The
- options are also enumerated <seealso
- marker="#gui/1">below</seealso> (<c>WarnOpts</c>).
- </p>
+ to the source file:</p>
+
+ <code type="none">
+-dialyzer(no_improper_lists).</code>
+
+ <p>Attribute <c>-dialyzer()</c> is allowed after function
+ declarations. Lists of warning options or functions are allowed:</p>
+
+ <code type="none">
+-dialyzer([{nowarn_function, [f/0]}, no_improper_lists]).</code>
+
+ <p>Warning options can be restricted to functions:</p>
+
+ <code type="none">
+-dialyzer({no_improper_lists, g/0}).</code>
+
+ <code type="none">
+-dialyzer({[no_return, no_match], [g/0, h/0]}).</code>
+
+ <p>For help on the warning options, use <c>dialyzer -Whelp</c>. The
+ options are also enumerated, see function <seealso marker="#gui/1">
+ <c>gui/1</c></seealso> below (<c>WarnOpts</c>).</p>
+
<note>
- <p>
- The <c>-dialyzer()</c> attribute is not checked by the Erlang
- Compiler, but by the Dialyzer itself.
- </p>
+ <p>Attribute <c>-dialyzer()</c> is not checked by the Erlang
+ compiler, but by Dialyzer itself.</p>
</note>
+
<note>
- <p>
- The warning option <c>-Wrace_conditions</c> has no effect when
- set in source files.
- </p>
+ <p>Warning option <c>-Wrace_conditions</c> has no effect when
+ set in source files.</p>
</note>
- <p>
- The <c>-dialyzer()</c> attribute can also be used for turning on
- warnings. For instance, if a module has been fixed regarding
- unmatched returns, adding the line
- </p>
-<code type="none">
--dialyzer(unmatched_returns).
-</code>
- <p>
- can help in assuring that no new unmatched return warnings are
- introduced.
- </p>
+
+ <p>Attribute <c>-dialyzer()</c> can also be used for turning on
+ warnings. For example, if a module has been fixed regarding
+ unmatched returns, adding the following line can help in assuring
+ that no new unmatched return warnings are introduced:</p>
+
+ <code type="none">
+-dialyzer(unmatched_returns).</code>
</section>
<funcs>
<func>
+ <name>format_warning(Msg) -> string()</name>
+ <fsummary>Get the string version of a warning message.</fsummary>
+ <type>
+ <v>Msg = {Tag, Id, msg()}</v>
+ <d>See <c>run/1</c>.</d>
+ </type>
+ <desc>
+ <p>Get a string from warnings as returned by
+ <seealso marker="#run/1"><c>run/1</c></seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
<name>gui() -> ok | {error, Msg}</name>
<name>gui(OptList) -> ok | {error, Msg}</name>
- <fsummary>Dialyzer GUI version</fsummary>
+ <fsummary>Dialyzer GUI version.</fsummary>
<type>
- <v>OptList -- see below</v>
+ <v>OptList</v>
+ <d>See below.</d>
</type>
<desc>
<p>Dialyzer GUI version.</p>
@@ -368,9 +504,12 @@ OptList :: [Option]
Option :: {files, [Filename :: string()]}
| {files_rec, [DirName :: string()]}
| {defines, [{Macro :: atom(), Value :: term()}]}
- | {from, src_code | byte_code} %% Defaults to byte_code
- | {init_plt, FileName :: string()} %% If changed from default
- | {plts, [FileName :: string()]} %% If changed from default
+ | {from, src_code | byte_code}
+ %% Defaults to byte_code
+ | {init_plt, FileName :: string()}
+ %% If changed from default
+ | {plts, [FileName :: string()]}
+ %% If changed from default
| {include_dirs, [DirName :: string()]}
| {output_file, FileName :: string()}
| {output_plt, FileName :: string()}
@@ -383,76 +522,71 @@ Option :: {files, [Filename :: string()]}
| {warnings, [WarnOpts]}
| {get_warnings, bool()}
-WarnOpts :: no_return
- | no_unused
- | no_improper_lists
+WarnOpts :: error_handling
+ | no_behaviours
+ | no_contracts
+ | no_fail_call
| no_fun_app
+ | no_improper_lists
| no_match
+ | no_missing_calls
| no_opaque
- | no_fail_call
- | no_contracts
- | no_behaviours
+ | no_return
| no_undefined_callbacks
- | unmatched_returns
- | error_handling
+ | no_unused
| race_conditions
- | overspecs
| underspecs
- | specdiffs
- | unknown</code>
+ | unknown
+ | unmatched_returns
+ | overspecs
+ | specdiffs</code>
</desc>
</func>
+
<func>
- <name>run(OptList) -> Warnings</name>
- <fsummary>Dialyzer command line version</fsummary>
- <type>
- <v>OptList -- see gui/0,1</v>
- <v>Warnings -- see below </v>
- </type>
+ <name>plt_info(string()) -> {'ok', [{atom(), any()}]} | {'error', atom()}</name>
+ <fsummary>Return information about the specified PLT.</fsummary>
<desc>
- <p>Dialyzer command line version.</p>
- <code type="none">
-Warnings :: [{Tag, Id, Msg}]
-Tag :: 'warn_behaviour'
- | 'warn_bin_construction'
- | 'warn_callgraph'
- | 'warn_contract_not_equal'
- | 'warn_contract_range'
- | 'warn_contract_subtype'
- | 'warn_contract_supertype'
- | 'warn_contract_syntax'
- | 'warn_contract_types'
- | 'warn_failing_call'
- | 'warn_fun_app'
- | 'warn_matching'
- | 'warn_non_proper_list'
- | 'warn_not_called'
- | 'warn_opaque'
- | 'warn_race_condition'
- | 'warn_return_no_exit'
- | 'warn_return_only_exit'
- | 'warn_umatched_return'
- | 'warn_undefined_callbacks'
- | 'warn_unknown'
-Id = {File :: string(), Line :: integer()}
-Msg = msg() -- Undefined</code>
+ <p>Returns information about the specified PLT.</p>
</desc>
</func>
+
<func>
- <name>format_warning(Msg) -> string()</name>
- <fsummary>Get the string version of a warning message.</fsummary>
+ <name>run(OptList) -> Warnings</name>
+ <fsummary>Dialyzer command-line version.</fsummary>
<type>
- <v>Msg = {Tag, Id, msg()} -- See run/1</v>
+ <v>OptList</v>
+ <d>See <c>gui/0,1</c>.</d>
+ <v>Warnings</v>
+ <d>See below.</d>
</type>
<desc>
- <p>Get a string from warnings as returned by dialyzer:run/1.</p>
- </desc>
- </func>
- <func>
- <name>plt_info(string()) -> {'ok', [{atom(), any()}]} | {'error', atom()}</name>
- <fsummary>Returns information about the specified plt.</fsummary>
- <desc>
- <p>Returns information about the specified plt.</p>
+ <p>Dialyzer command-line version.</p>
+ <code type="none">
+Warnings :: [{Tag, Id, Msg}]
+Tag :: 'warn_behaviour'
+ | 'warn_bin_construction'
+ | 'warn_callgraph'
+ | 'warn_contract_not_equal'
+ | 'warn_contract_range'
+ | 'warn_contract_subtype'
+ | 'warn_contract_supertype'
+ | 'warn_contract_syntax'
+ | 'warn_contract_types'
+ | 'warn_failing_call'
+ | 'warn_fun_app'
+ | 'warn_matching'
+ | 'warn_non_proper_list'
+ | 'warn_not_called'
+ | 'warn_opaque'
+ | 'warn_race_condition'
+ | 'warn_return_no_exit'
+ | 'warn_return_only_exit'
+ | 'warn_umatched_return'
+ | 'warn_undefined_callbacks'
+ | 'warn_unknown'
+Id = {File :: string(), Line :: integer()}
+Msg = msg() -- Undefined</code>
</desc>
</func>
</funcs>
diff --git a/lib/dialyzer/doc/src/dialyzer_chapter.xml b/lib/dialyzer/doc/src/dialyzer_chapter.xml
index c445f2633f..b5acf3732e 100644
--- a/lib/dialyzer/doc/src/dialyzer_chapter.xml
+++ b/lib/dialyzer/doc/src/dialyzer_chapter.xml
@@ -25,196 +25,211 @@
<title>Dialyzer</title>
<prepared></prepared>
<docno></docno>
- <date></date>
+ <date>2016-09-19</date>
<rev></rev>
<file>dialyzer_chapter.xml</file>
</header>
<section>
<title>Introduction</title>
- <p><em>Dialyzer</em> is a static analysis tool that identifies software discrepancies
- such as type errors, unreachable code, unnecessary tests, etc in single Erlang modules
- or entire (sets of) applications.</p>
- </section>
-
- <section>
- <title>Using the Dialyzer from the GUI</title>
-
<section>
- <title>Choosing the applications or modules</title>
- <p>In the "File" window you will find a listing of the current directory.
- Click your way to the directories/modules you want to add or type the
- correct path in the entry.</p>
- <p>Mark the directories/modules you want to analyze for discrepancies and
- click "Add". You can either add the <c><![CDATA[.beam]]></c> and <c><![CDATA[.erl]]></c>-files directly, or
- you can add directories that contain these kinds of files. Note that
- you are only allowed to add the type of files that can be analyzed in
- the current mode of operation (see below), and that you cannot mix
- <c><![CDATA[.beam]]></c> and <c><![CDATA[.erl]]></c>-files.</p>
+ <title>Scope</title>
+ <p>Dialyzer is a static analysis tool that identifies software
+ discrepancies, such as definite type errors, code that has become dead
+ or unreachable because of programming error, and unnecessary tests,
+ in single Erlang modules or entire (sets of) applications.</p>
+
+ <p>Dialyzer can be called from the command line, from Erlang,
+ and from a GUI.</p>
</section>
<section>
- <title>The analysis modes</title>
- <p>Dialyzer has two modes of analysis, "Byte Code" or "Source Code".
- These are controlled by the buttons in the top-middle part of the
- main window, under "Analysis Options".</p>
- </section>
-
- <section>
- <title>Controlling the discrepancies reported by the Dialyzer</title>
- <p>Under the "Warnings" pull-down menu, there are buttons that control
- which discrepancies are reported to the user in the "Warnings" window.
- By clicking on these buttons, one can enable/disable a whole class of
- warnings. Information about the classes of warnings can be found on
- the "Warnings" item under the "Help" menu (at the rightmost top corner).</p>
- <p>If modules are compiled with inlining, spurious warnings may be emitted.
- In the "Options" menu you can choose to ignore inline-compiled modules
- when analyzing byte code. When starting from source code this is not a
- problem since the inlining is explicitly turned off by Dialyzer. The
- option causes Dialyzer to suppress all warnings from inline-compiled
- modules, since there is currently no way for Dialyzer to find what
- parts of the code have been produced by inlining. </p>
+ <title>Prerequisites</title>
+ <p>It is assumed that the reader is familiar with the Erlang programming
+ language.</p>
</section>
+ </section>
- <section>
- <title>Running the analysis</title>
- <p>Once you have chosen the modules or directories you want to analyze,
- click the "Run" button to start the analysis. If for some reason you
- want to stop the analysis while it is running, push the "Stop" button.</p>
- <p>The information from the analysis will be displayed in the Log and the
- Warnings windows.</p>
- </section>
+ <section>
+ <marker id="plt"/>
+ <title>The Persistent Lookup Table</title>
+ <p>Dialyzer stores the result of an analysis in a Persistent
+ Lookup Table (PLT). The PLT can then be used as a starting
+ point for later analyses. It is recommended to build a PLT with the
+ Erlang/OTP applications that you are using, but also to include your
+ own applications that you are using frequently.</p>
+
+ <p>The PLT is built using option <c>--build_plt</c> to Dialyzer.
+ The following command builds the recommended minimal PLT for
+ Erlang/OTP:</p>
- <section>
- <title>Include directories and macro definitions</title>
- <p>When analyzing from source you might have to supply Dialyzer with a
- list of include directories and macro definitions (as you can do with
- the <c><![CDATA[erlc]]></c> flags <c><![CDATA[-I]]></c> and <c><![CDATA[-D]]></c>). This can be done either by starting Dialyzer
- with these flags from the command line as in:</p>
- <code type="none">
+ <code type="none">
+dialyzer --build_plt --apps erts kernel stdlib mnesia</code>
- dialyzer -I my_includes -DDEBUG -Dvsn=42 -I one_more_dir
- </code>
- <p>or by adding these explicitly using the "Manage Macro Definitions" or
- "Manage Include Directories" sub-menus in the "Options" menu.</p>
- </section>
+ <p>Dialyzer looks if there is an environment variable called
+ <c>DIALYZER_PLT</c> and places the PLT at this location. If no such
+ variable is set, Dialyzer places the PLT at
+ <c>$HOME/.dialyzer_plt</c>. The placement can also be specified using
+ the options <c>--plt</c> or <c>--output_plt</c>.</p>
- <section>
- <title>Saving the information on the Log and Warnings windows</title>
- <p>In the "File" menu there are options to save the contents of the Log
- and the Warnings window. Just choose the options and enter the file to
- save the contents in.</p>
- <p>There are also buttons to clear the contents of each window.</p>
- </section>
+ <p>Information can be added to an existing PLT using option
+ <c>--add_to_plt</c>. If you also want to include the Erlang compiler in
+ the PLT and place it in a new PLT, then use the following command:</p>
- <section>
- <title>Inspecting the inferred types of the analyzed functions</title>
- <p>Dialyzer stores the information of the analyzed functions in a
- Persistent Lookup Table (PLT). After an analysis you can inspect this
- information. In the PLT menu you can choose to either search the PLT
- or inspect the contents of the whole PLT. The information is presented
- in edoc format.</p>
- </section>
- </section>
+ <code type="none">
+dialyzer --add_to_plt --apps compiler --output_plt my.plt</code>
- <section>
- <title>Using the Dialyzer from the command line</title>
- <p>See <seealso marker="dialyzer">dialyzer(3)</seealso>.</p>
- </section>
+ <p>Then you can add your favorite application my_app to the new
+ PLT:</p>
- <section>
- <title>Using the Dialyzer from Erlang</title>
- <p>See <seealso marker="dialyzer">dialyzer(3)</seealso>.</p>
- </section>
+ <code type="none">
+dialyzer --add_to_plt --plt my.plt -r my_app/ebin</code>
- <section>
- <title>More on the Persistent Lookup Table (PLT)</title>
+ <p>But you realize that it is unnecessary to have the Erlang compiler in this
+ one:</p>
- <p> The persistent lookup table, or PLT, is used to store the
- result of an analysis. The PLT can then be used as a starting
- point for later analyses. It is recommended to build a PLT with
- the otp applications that you are using, but also to include your
- own applications that you are using frequently.</p>
+ <code type="none">
+dialyzer --remove_from_plt --plt my.plt --apps compiler</code>
- <p>The PLT is built using the --build_plt option to dialyzer. The
- following command builds the recommended minimal PLT for OTP.</p>
+ <p>Later, when you have fixed a bug in your application my_app,
+ you want to update the PLT so that it becomes fresh the next time
+ you run Dialyzer. In this case, run the following command:</p>
<code type="none">
+dialyzer --check_plt --plt my.plt</code>
- dialyzer --build_plt -r $ERL_TOP/lib/stdlib/ebin\
- $ERL_TOP/lib/kernel/ebin \
- $ERL_TOP/lib/mnesia/ebin
- </code>
+ <p>Dialyzer then reanalyzes the changed files
+ and the files that depend on these files. Notice that this
+ consistency check is performed automatically the next time you
+ run Dialyzer with this PLT. Option <c>--check_plt</c> is only
+ for doing so without doing any other analysis.</p>
- <p>Dialyzer will look if there is an environment variable called
- $DIALYZER_PLT and place the PLT at this location. If no such
- variable is set, Dialyzer will place the PLT at
- $HOME/.dialyzer_plt. The placement can also be specified using the
- --plt, or --output_plt options.</p>
-
- <p>You can also add information to an existing plt using the
- --add_to_plt option. Suppose you want to also include the compiler
- in the PLT and place it in a new PLT, then give the command</p>
+ <p>To get information about a PLT, use the following option:</p>
<code type="none">
+dialyzer --plt_info</code>
- dialyzer --add_to_plt -r $ERL_TOP/lib/compiler/ebin --output_plt my.plt
- </code>
+ <p>To specify which PLT, use option <c>--plt</c>.</p>
- <p>Then you would like to add your favorite application my_app to
- the new plt.</p>
+ <p>To get the output printed to a file, use option <c>--output_file</c>.</p>
- <code type="none">
+ <p>Notice that when manipulating the PLT, no warnings are
+ emitted. To turn on warnings during (re)analysis of the PLT, use
+ option <c>--get_warnings</c>.</p>
+ </section>
- dialyzer --add_to_plt --plt my.plt -r my_app/ebin
- </code>
+ <section>
+ <title>Using Dialyzer from the Command Line</title>
+ <p>Dialyzer has a command-line version for automated use.
+ See <seealso marker="dialyzer"><c>dialyzer(3)</c></seealso>.</p>
+ </section>
- <p>But you realize that it is unnecessary to have compiler in this one.</p>
+ <section>
+ <title>Using Dialyzer from Erlang</title>
+ <p>Dialyzer can also be used directly from Erlang.
+ See <seealso marker="dialyzer"><c>dialyzer(3)</c></seealso>.</p>
+ </section>
- <code type="none">
+ <section>
+ <marker id="dialyzer_gui"/>
+ <title>Using Dialyzer from the GUI</title>
+ <section>
+ <title>Choosing the Applications or Modules</title>
+ <p>The <em>File</em> window displays a listing of the current directory.
+ Click your way to the directories/modules you want to add or type the
+ correct path in the entry.</p>
- dialyzer --remove_from_plt --plt my.plt -r $ERL_TOP/lib/compiler/ebin
- </code>
+ <p>Mark the directories/modules you want to analyze for discrepancies and
+ click <em>Add</em>. You can either add the <c>.beam</c> and
+ <c>.erl</c> files directly, or add directories that contain
+ these kind of files. Notice that
+ you are only allowed to add the type of files that can be analyzed in
+ the current mode of operation (see below), and that you cannot mix
+ <c>.beam</c> and <c>.erl</c> files.</p>
+ </section>
- <p> Later, when you have fixed a bug in your application my_app,
- you want to update the plt so that it will be fresh the next time
- you run Dialyzer, run the command</p>
+ <section>
+ <title>Analysis Modes</title>
+ <p>Dialyzer has two analysis modes: "Byte Code" and "Source Code".
+ They are controlled by the buttons in the top-middle part of the
+ main window, under <em>Analysis Options</em>.</p>
+ </section>
- <code type="none">
+ <section>
+ <title>Controlling the Discrepancies Reported by Dialyzer</title>
+ <p>Under the <em>Warnings</em> pull-down menu, there are buttons that
+ control which discrepancies are reported to the user in the
+ <em>Warnings</em> window. By clicking these buttons, you can
+ enable/disable a whole class of warnings. Information about the classes
+ of warnings is found on the "Warnings" item under the <em>Help</em>
+ menu (in the rightmost top corner).</p>
+
+ <p>If modules are compiled with inlining, spurious warnings can be
+ emitted. In the <em>Options</em> menu you can choose to ignore
+ inline-compiled modules when analyzing byte code.
+ When starting from source code, this is not a problem because
+ inlining is explicitly turned off by Dialyzer. The option causes
+ Dialyzer to suppress all warnings from inline-compiled
+ modules, as there is currently no way for Dialyzer to find what
+ parts of the code have been produced by inlining.</p>
+ </section>
- dialyzer --check_plt --plt my.plt
- </code>
+ <section>
+ <title>Running the Analysis</title>
+ <p>Once you have chosen the modules or directories you want to analyze,
+ click the <em>Run</em> button to start the analysis. If you for some
+ reason want to stop the analysis while it is running, click the
+ <em>Stop</em> button.</p>
- <p> Dialyzer will then reanalyze the files that have been changed,
- and the files that depend on these files. Note that this
- consistency check will be performed automatically the next time
- you run Dialyzer with this plt. The --check_plt option is merely
- for doing so without doing any other analysis.</p>
+ <p>The information from the analysis is displayed in the <em>Log</em>
+ window and the <em>Warnings</em> window.</p>
+ </section>
- <p> To get some information about a plt use the option</p>
- <code type="none">
+ <section>
+ <title>Include Directories and Macro Definitions</title>
+ <p>When analyzing from source, you might have to supply Dialyzer
+ with a list of include directories and macro definitions (as you can do
+ with the <seealso marker="erts:erlc"><c>erlc</c></seealso> flags
+ <c>-I</c> and <c>-D</c>). This can be done
+ either by starting Dialyzer with these flags from the command
+ line as in:</p>
+
+ <code type="none">
+dialyzer -I my_includes -DDEBUG -Dvsn=42 -I one_more_dir</code>
- dialyzer --plt_info
- </code>
+ <p>or by adding these explicitly using submenu
+ <em>Manage Macro Definitions</em> or
+ <em>Manage Include Directories</em> in the <em>Options</em> menu.</p>
+ </section>
- <p>You can also specify which plt with the --plt option, and get the
- output printed to a file with --output_file</p>
+ <section>
+ <title>Saving the Information on the Log and Warnings Windows</title>
+ <p>The <em>File</em> menu includes options to save the contents of the
+ <em>Log</em> window and the <em>Warnings</em> window. Simply choose the
+ options and enter the file to save the contents in.</p>
- <p>Note that when manipulating the plt, no warnings are
- emitted. To turn on warnings during (re)analysis of the plt, use
- the option --get_warnings.</p>
+ <p>There are also buttons to clear the contents of each window.</p>
+ </section>
+ <section>
+ <title>Inspecting the Inferred Types of the Analyzed Functions</title>
+ <p>Dialyzer stores the information of the analyzed functions in a
+ Persistent Lookup Table (PLT), see section
+ <seealso marker="#plt">The Persistent Lookup Table</seealso>.</p>
+
+ <p>After an analysis, you can inspect this information.
+ In the <em>PLT</em> menu you can choose to either search the PLT
+ or inspect the contents of the whole PLT. The information is presented
+ in <seealso marker="edoc:edoc"><c>EDoc</c></seealso> format.</p>
+ </section>
</section>
<section>
- <title>Feedback and bug reports</title>
- <p>At this point, we very much welcome user feedback (even wish-lists!).
- If you notice something weird, especially if the Dialyzer reports any
- discrepancy that is a false positive, please send an error report
- describing the symptoms and how to reproduce them to:</p>
- <code type="none"><![CDATA[
- ]]></code>
+ <title>Feedback and Bug Reports</title>
+ <p>We very much welcome user feedback - even wishlists!
+ If you notice anything weird, especially if Dialyzer reports
+ any discrepancy that is a false positive, please send an error report
+ describing the symptoms and how to reproduce them.</p>
</section>
</chapter>
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index a5a52fee61..0dbee1ce34 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -32,6 +32,132 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 3.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Report unknown types properly. A bug was introduced
+ in Erlang/OTP 19.3, where warnings about unknown types
+ were simply discarded. </p>
+ <p>
+ Own Id: OTP-14368</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Dialyzer 3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix a bug concerning parameterized opaque types. </p>
+ <p>
+ Own Id: OTP-14130</p>
+ </item>
+ <item>
+ <p> Improve a few warnings. One of them could cause a
+ crash. </p>
+ <p>
+ Own Id: OTP-14177</p>
+ </item>
+ <item>
+ <p>The dialyzer and observer applications will now use a
+ portable way to find the home directory. That means that
+ there is no longer any need to manually set the HOME
+ environment variable on Windows.</p>
+ <p>
+ Own Id: OTP-14249 Aux Id: ERL-161 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The peak memory consumption is reduced. </p><p> The
+ evaluation of huge SCCs in <c>dialyzer_typesig</c> is
+ optimized. </p><p> Analyzing modules with binary
+ construction with huge strings is now much faster. </p>
+ <p>
+ Own Id: OTP-14126 Aux Id: ERL-308 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Dialyzer 3.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix bugs regarding opaque types. </p>
+ <p>
+ Own Id: OTP-13693</p>
+ </item>
+ <item>
+ <p> Fix error handling of bad <c>-dialyzer()</c>
+ attributes. </p>
+ <p>
+ Own Id: OTP-13979 Aux Id: ERL-283 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> A few warning messages have been improved. </p>
+ <p>
+ Own Id: OTP-11403</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Dialyzer 3.0.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The translation of forms to types is improved for
+ opaque types in a few cases. </p>
+ <p>
+ Own Id: OTP-13682</p>
+ </item>
+ <item>
+ <p> Add warning suppression to compiler-generated case
+ statements. Warnings about clauses that cannot match and
+ are also compiler generated are suppressed unless none of
+ the clauses return. </p>
+ <p>
+ Own Id: OTP-13723 Aux Id: ERL-159, PR-1121 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Dialyzer 3.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fix a map related bug.</p>
+ <p>
+ Own Id: OTP-13709 Aux Id: ERL-177, PR-1115 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 3.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -485,7 +611,7 @@
or modifying opaque types within the scope of a module.
</p> <p> Hitherto the shape of terms (tuple, list, etc.)
has been used to determine the opaque terms, but now the
- contracts are used for decorating types with opaqueness.
+ contracts are used for decorating types with opacity.
</p>
<p>
Own Id: OTP-10397</p>
@@ -1468,7 +1594,7 @@
<list>
<item>
<p>The analysis accepts opaque type declarations and
- detects violations of opaqueness of terms of such types.
+ detects violations of opacity of terms of such types.
Starting with R13, many Erlang/OTP standard libraries
(array, dict, digraph, ets, gb_sets, gb_trees, queue, and
sets) contain opaque type declarations of their main data
diff --git a/lib/dialyzer/doc/src/part.xml b/lib/dialyzer/doc/src/part.xml
index 575f77549a..9bfcf21a66 100644
--- a/lib/dialyzer/doc/src/part.xml
+++ b/lib/dialyzer/doc/src/part.xml
@@ -25,12 +25,11 @@
<title>Dialyzer User's Guide</title>
<prepared></prepared>
<docno></docno>
- <date></date>
+ <date>2016-09-19</date>
<rev></rev>
<file>part.xml</file>
</header>
<description>
- <p><em>Dialyzer</em> is a static analysis tool that identifies software discrepancies such as type errors, unreachable code, unnecessary tests, etc in single Erlang modules or entire (sets of) applications.</p>
</description>
<xi:include href="dialyzer_chapter.xml"/>
</part>
diff --git a/lib/dialyzer/doc/src/ref_man.xml b/lib/dialyzer/doc/src/ref_man.xml
index 01478cfb40..ddac047f2e 100644
--- a/lib/dialyzer/doc/src/ref_man.xml
+++ b/lib/dialyzer/doc/src/ref_man.xml
@@ -25,11 +25,10 @@
<title>Dialyzer Reference Manual</title>
<prepared></prepared>
<docno></docno>
- <date></date>
+ <date>2016-09-19</date>
<rev></rev>
</header>
<description>
- <p><em>Dialyzer</em> is a static analysis tool that identifies software discrepancies such as type errors, unreachable code, unnecessary tests, etc in single Erlang modules or entire (sets of) applications.</p>
</description>
<xi:include href="dialyzer.xml"/>
</application>
diff --git a/lib/dialyzer/src/dialyzer.app.src b/lib/dialyzer/src/dialyzer.app.src
index 5b28f7ae86..f517c51ec1 100644
--- a/lib/dialyzer/src/dialyzer.app.src
+++ b/lib/dialyzer/src/dialyzer.app.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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,5 +48,5 @@
{applications, [compiler, hipe, kernel, stdlib, wx]},
{env, []},
{runtime_dependencies, ["wx-1.2","syntax_tools-2.0","stdlib-3.0",
- "kernel-5.0","hipe-3.15.1","erts-8.0",
+ "kernel-5.0","hipe-3.15.4","erts-8.0",
"compiler-7.0"]}]}.
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index bcac8afe64..d25ffd02a2 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.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.
@@ -282,7 +282,7 @@ cl_check_log(none) ->
cl_check_log(Output) ->
io:format(" Check output file `~s' for details\n", [Output]).
--spec format_warning(raw_warning()) -> string().
+-spec format_warning(raw_warning() | dial_warning()) -> string().
format_warning(W) ->
format_warning(W, basename).
@@ -407,6 +407,10 @@ message_to_string({contract_range, [Contract, M, F, ArgStrings, Line, CRet]}) ->
message_to_string({invalid_contract, [M, F, A, Sig]}) ->
io_lib:format("Invalid type specification for function ~w:~w/~w."
" The success typing is ~s\n", [M, F, A, Sig]);
+message_to_string({contract_with_opaque, [M, F, A, OpaqueType, SigType]}) ->
+ io_lib:format("The specification for ~w:~w/~w"
+ " has an opaque subtype ~s which is violated by the"
+ " success typing ~s\n", [M, F, A, OpaqueType, SigType]);
message_to_string({extra_range, [M, F, A, ExtraRanges, SigRange]}) ->
io_lib:format("The specification for ~w:~w/~w states that the function"
" might also return ~s but the inferred return is ~s\n",
@@ -432,25 +436,25 @@ message_to_string({opaque_guard, [Arg1, Infix, Arg2, ArgNs]}) ->
io_lib:format("Guard test ~s ~s ~s contains ~s\n",
[Arg1, Infix, Arg2, form_positions(ArgNs)]);
message_to_string({opaque_guard, [Guard, Args]}) ->
- io_lib:format("Guard test ~w~s breaks the opaqueness of its argument\n",
+ io_lib:format("Guard test ~w~s breaks the opacity of its argument\n",
[Guard, Args]);
message_to_string({opaque_match, [Pat, OpaqueType, OpaqueTerm]}) ->
Term = if OpaqueType =:= OpaqueTerm -> "the term";
true -> OpaqueTerm
end,
io_lib:format("The attempt to match a term of type ~s against the ~s"
- " breaks the opaqueness of ~s\n", [OpaqueType, Pat, Term]);
+ " breaks the opacity of ~s\n", [OpaqueType, Pat, Term]);
message_to_string({opaque_neq, [Type, _Op, OpaqueType]}) ->
io_lib:format("Attempt to test for inequality between a term of type ~s"
" and a term of opaque type ~s\n", [Type, OpaqueType]);
message_to_string({opaque_type_test, [Fun, Args, Arg, ArgType]}) ->
- io_lib:format("The type test ~s~s breaks the opaqueness of the term ~s~s\n",
+ io_lib:format("The type test ~s~s breaks the opacity of the term ~s~s\n",
[Fun, Args, Arg, ArgType]);
message_to_string({opaque_size, [SizeType, Size]}) ->
- io_lib:format("The size ~s breaks the opaqueness of ~s\n",
+ io_lib:format("The size ~s breaks the opacity of ~s\n",
[SizeType, Size]);
message_to_string({opaque_call, [M, F, Args, Culprit, OpaqueType]}) ->
- io_lib:format("The call ~s:~s~s breaks the opaqueness of the term ~s :: ~s\n",
+ io_lib:format("The call ~s:~s~s breaks the opacity of the term ~s :: ~s\n",
[M, F, Args, Culprit, OpaqueType]);
%%----- Warnings for concurrency errors --------------------
message_to_string({race_condition, [M, F, Args, Reason]}) ->
diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl
index ea6a71217c..1139044ec9 100644
--- a/lib/dialyzer/src/dialyzer.hrl
+++ b/lib/dialyzer/src/dialyzer.hrl
@@ -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.
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index 50fc1d8471..7f8e68908e 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -101,9 +101,9 @@ loop(#server_state{parent = Parent} = State,
{AnalPid, cserver, CServer, Plt} ->
send_codeserver_plt(Parent, CServer, Plt),
loop(State, Analysis, ExtCalls);
- {AnalPid, done, Plt, DocPlt} ->
+ {AnalPid, done, MiniPlt, DocPlt} ->
send_ext_calls(Parent, ExtCalls),
- send_analysis_done(Parent, Plt, DocPlt);
+ send_analysis_done(Parent, MiniPlt, DocPlt);
{AnalPid, ext_calls, NewExtCalls} ->
loop(State, Analysis, NewExtCalls);
{AnalPid, ext_types, ExtTypes} ->
@@ -121,6 +121,7 @@ loop(#server_state{parent = Parent} = State,
%% The Analysis
%%--------------------------------------------------------------------
+%% Calls to erlang:garbage_collect() help to reduce the heap size.
analysis_start(Parent, Analysis, LegalWarnings) ->
CServer = dialyzer_codeserver:new(),
Plt = Analysis#analysis.plt,
@@ -157,12 +158,9 @@ analysis_start(Parent, Analysis, LegalWarnings) ->
TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer0),
TmpCServer2 =
dialyzer_codeserver:finalize_exported_types(MergedExpTypes, TmpCServer1),
+ erlang:garbage_collect(),
?timing(State#analysis_state.timing_server, "remote",
- begin
- TmpCServer3 =
- dialyzer_utils:process_record_remote_types(TmpCServer2),
- dialyzer_contracts:process_contract_remote_types(TmpCServer3)
- end)
+ contracts_and_records(TmpCServer2, Parent))
catch
throw:{error, _ErrorMsg} = Error -> exit(Error)
end,
@@ -171,48 +169,75 @@ analysis_start(Parent, Analysis, LegalWarnings) ->
NewPlt1 = dialyzer_plt:insert_exported_types(NewPlt0, ExpTypes),
State0 = State#analysis_state{plt = NewPlt1},
dump_callgraph(Callgraph, State0, Analysis),
- State1 = State0#analysis_state{codeserver = NewCServer},
%% Remove all old versions of the files being analyzed
AllNodes = dialyzer_callgraph:all_nodes(Callgraph),
- Plt1 = dialyzer_plt:delete_list(NewPlt1, AllNodes),
+ Plt1_a = dialyzer_plt:delete_list(NewPlt1, AllNodes),
+ Plt1 = dialyzer_plt:insert_callbacks(Plt1_a, NewCServer),
+ State1 = State0#analysis_state{codeserver = NewCServer, plt = Plt1},
Exports = dialyzer_codeserver:get_exports(NewCServer),
+ NonExports = sets:subtract(sets:from_list(AllNodes), Exports),
+ NonExportsList = sets:to_list(NonExports),
NewCallgraph =
case Analysis#analysis.race_detection of
true -> dialyzer_callgraph:put_race_detection(true, Callgraph);
false -> Callgraph
end,
- State2 = analyze_callgraph(NewCallgraph, State1#analysis_state{plt = Plt1}),
+ State2 = analyze_callgraph(NewCallgraph, State1),
+ #analysis_state{plt = MiniPlt2, doc_plt = DocPlt} = State2,
dialyzer_callgraph:dispose_race_server(NewCallgraph),
- rcv_and_send_ext_types(Parent),
- NonExports = sets:subtract(sets:from_list(AllNodes), Exports),
- NonExportsList = sets:to_list(NonExports),
- Plt2 = dialyzer_plt:delete_list(State2#analysis_state.plt, NonExportsList),
- send_codeserver_plt(Parent, CServer, State2#analysis_state.plt),
- send_analysis_done(Parent, Plt2, State2#analysis_state.doc_plt).
+ %% Since the PLT is never used, a dummy is sent:
+ DummyPlt = dialyzer_plt:new(),
+ send_codeserver_plt(Parent, CServer, DummyPlt),
+ MiniPlt3 = dialyzer_plt:delete_list(MiniPlt2, NonExportsList),
+ send_analysis_done(Parent, MiniPlt3, DocPlt).
+
+contracts_and_records(CodeServer, Parent) ->
+ Fun = contrs_and_recs(CodeServer, Parent),
+ {Pid, Ref} = erlang:spawn_monitor(Fun),
+ dialyzer_codeserver:give_away(CodeServer, Pid),
+ Pid ! {self(), go},
+ receive {'DOWN', Ref, process, Pid, Return} ->
+ Return
+ end.
+
+-spec contrs_and_recs(dialyzer_codeserver:codeserver(), pid()) ->
+ fun(() -> no_return()).
+
+contrs_and_recs(TmpCServer2, Parent) ->
+ fun() ->
+ Caller = receive {Pid, go} -> Pid end,
+ {TmpCServer3, RecordDict} =
+ dialyzer_utils:process_record_remote_types(TmpCServer2),
+ TmpServer4 =
+ dialyzer_contracts:process_contract_remote_types(TmpCServer3,
+ RecordDict),
+ dialyzer_codeserver:give_away(TmpServer4, Parent),
+ rcv_and_send_ext_types(Caller, Parent),
+ exit(TmpServer4)
+ end.
analyze_callgraph(Callgraph, #analysis_state{codeserver = Codeserver,
doc_plt = DocPlt,
+ plt = Plt,
timing_server = TimingServer,
parent = Parent,
solvers = Solvers} = State) ->
- Plt = dialyzer_plt:insert_callbacks(State#analysis_state.plt, Codeserver),
- {NewPlt, NewDocPlt} =
- case State#analysis_state.analysis_type of
- plt_build ->
- NewPlt0 =
- dialyzer_succ_typings:analyze_callgraph(Callgraph, Plt, Codeserver,
- TimingServer, Solvers, Parent),
- {NewPlt0, DocPlt};
- succ_typings ->
- {Warnings, NewPlt0, NewDocPlt0} =
- dialyzer_succ_typings:get_warnings(Callgraph, Plt, DocPlt, Codeserver,
- TimingServer, Solvers, Parent),
- Warnings1 = filter_warnings(Warnings, Codeserver),
- send_warnings(State#analysis_state.parent, Warnings1),
- {NewPlt0, NewDocPlt0}
- end,
- dialyzer_callgraph:delete(Callgraph),
- State#analysis_state{plt = NewPlt, doc_plt = NewDocPlt}.
+ case State#analysis_state.analysis_type of
+ plt_build ->
+ NewMiniPlt =
+ dialyzer_succ_typings:analyze_callgraph(Callgraph, Plt, Codeserver,
+ TimingServer, Solvers, Parent),
+ dialyzer_callgraph:delete(Callgraph),
+ State#analysis_state{plt = NewMiniPlt, doc_plt = DocPlt};
+ succ_typings ->
+ {Warnings, NewMiniPlt, NewDocPlt} =
+ dialyzer_succ_typings:get_warnings(Callgraph, Plt, DocPlt, Codeserver,
+ TimingServer, Solvers, Parent),
+ dialyzer_callgraph:delete(Callgraph),
+ Warnings1 = filter_warnings(Warnings, Codeserver),
+ send_warnings(State#analysis_state.parent, Warnings1),
+ State#analysis_state{plt = NewMiniPlt, doc_plt = NewDocPlt}
+ end.
%%--------------------------------------------------------------------
%% Build the callgraph and fill the codeserver.
@@ -406,24 +431,28 @@ compile_common(File, AbstrCode, CompOpts, Callgraph, CServer,
{ok, RecInfo} ->
CServer1 =
dialyzer_codeserver:store_temp_records(Mod, RecInfo, CServer),
- MetaFunInfo =
- dialyzer_utils:get_fun_meta_info(Mod, AbstrCode, LegalWarnings),
- CServer2 =
- dialyzer_codeserver:insert_fun_meta_info(MetaFunInfo, CServer1),
- case UseContracts of
- true ->
- case dialyzer_utils:get_spec_info(Mod, AbstrCode, RecInfo) of
- {error, _} = Error -> Error;
- {ok, SpecInfo, CallbackInfo} ->
- CServer3 =
- dialyzer_codeserver:store_temp_contracts(Mod, SpecInfo,
- CallbackInfo,
- CServer2),
- store_core(Mod, Core, Callgraph, CServer3)
- end;
- false ->
- store_core(Mod, Core, Callgraph, CServer2)
- end
+ case
+ dialyzer_utils:get_fun_meta_info(Mod, AbstrCode, LegalWarnings)
+ of
+ {error, _} = Error -> Error;
+ MetaFunInfo ->
+ CServer2 =
+ dialyzer_codeserver:insert_fun_meta_info(MetaFunInfo, CServer1),
+ case UseContracts of
+ true ->
+ case dialyzer_utils:get_spec_info(Mod, AbstrCode, RecInfo) of
+ {error, _} = Error -> Error;
+ {ok, SpecInfo, CallbackInfo} ->
+ CServer3 =
+ dialyzer_codeserver:store_temp_contracts(Mod, SpecInfo,
+ CallbackInfo,
+ CServer2),
+ store_core(Mod, Core, Callgraph, CServer3)
+ end;
+ false ->
+ store_core(Mod, Core, Callgraph, CServer2)
+ end
+ end
end
end.
@@ -525,13 +554,13 @@ default_includes(Dir) ->
%% Handle Messages
%%-------------------------------------------------------------------
-rcv_and_send_ext_types(Parent) ->
+rcv_and_send_ext_types(SendTo, Parent) ->
Self = self(),
Self ! {Self, done},
case rcv_ext_types(Self, []) of
[] -> ok;
ExtTypes ->
- Parent ! {Self, ext_types, ExtTypes},
+ Parent ! {SendTo, ext_types, ExtTypes},
ok
end.
@@ -565,8 +594,9 @@ is_ok_fun({_Filename, _Line, {_M, _F, _A} = MFA}, Codeserver) ->
is_ok_tag(Tag, {_F, _L, MorMFA}, Codeserver) ->
not dialyzer_utils:is_suppressed_tag(MorMFA, Tag, Codeserver).
-send_analysis_done(Parent, Plt, DocPlt) ->
- Parent ! {self(), done, Plt, DocPlt},
+send_analysis_done(Parent, MiniPlt, DocPlt) ->
+ ok = dialyzer_plt:give_away(MiniPlt, Parent),
+ Parent ! {self(), done, MiniPlt, DocPlt},
ok.
send_ext_calls(_Parent, none) ->
@@ -579,7 +609,7 @@ send_ext_types(Parent, ExtTypes) ->
Parent ! {self(), ext_types, ExtTypes},
ok.
-send_codeserver_plt(Parent, CServer, Plt ) ->
+send_codeserver_plt(Parent, CServer, Plt) ->
Parent ! {self(), cserver, CServer, Plt},
ok.
@@ -598,14 +628,14 @@ format_bad_calls([{{_, _, _}, {_, module_info, A}}|Left], CodeServer, Acc)
format_bad_calls([{FromMFA, {M, F, A} = To}|Left], CodeServer, Acc) ->
{_Var, FunCode} = dialyzer_codeserver:lookup_mfa_code(FromMFA, CodeServer),
Msg = {call_to_missing, [M, F, A]},
- {File, Line} = find_call_file_and_line(FunCode, To),
+ {File, Line} = find_call_file_and_line(FromMFA, FunCode, To, CodeServer),
WarningInfo = {File, Line, FromMFA},
NewAcc = [{?WARN_CALLGRAPH, WarningInfo, Msg}|Acc],
format_bad_calls(Left, CodeServer, NewAcc);
format_bad_calls([], _CodeServer, Acc) ->
Acc.
-find_call_file_and_line(Tree, MFA) ->
+find_call_file_and_line({Module, _, _}, Tree, MFA, CodeServer) ->
Fun =
fun(SubTree, Acc) ->
case cerl:is_c_call(SubTree) of
@@ -618,7 +648,7 @@ find_call_file_and_line(Tree, MFA) ->
case {cerl:concrete(M), cerl:concrete(F), A} of
MFA ->
Ann = cerl:get_ann(SubTree),
- [{get_file(Ann), get_line(Ann)}|Acc];
+ [{get_file(CodeServer, Module, Ann), get_line(Ann)}|Acc];
{erlang, make_fun, 3} ->
[CA1, CA2, CA3] = cerl:call_args(SubTree),
case
@@ -634,7 +664,8 @@ find_call_file_and_line(Tree, MFA) ->
of
MFA ->
Ann = cerl:get_ann(SubTree),
- [{get_file(Ann), get_line(Ann)}|Acc];
+ [{get_file(CodeServer, Module, Ann),
+ get_line(Ann)}|Acc];
_ ->
Acc
end;
@@ -654,8 +685,10 @@ get_line([Line|_]) when is_integer(Line) -> Line;
get_line([_|Tail]) -> get_line(Tail);
get_line([]) -> -1.
-get_file([{file, File}|_]) -> File;
-get_file([_|Tail]) -> get_file(Tail).
+get_file(Codeserver, Module, [{file, FakeFile}|_]) ->
+ dialyzer_codeserver:translate_fake_file(Codeserver, Module, FakeFile);
+get_file(Codeserver, Module, [_|Tail]) ->
+ get_file(Codeserver, Module, Tail).
-spec dump_callgraph(dialyzer_callgraph:callgraph(), #analysis_state{}, #analysis{}) ->
'ok'.
diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl
index 5623929a43..a8af5debb9 100644
--- a/lib/dialyzer/src/dialyzer_behaviours.erl
+++ b/lib/dialyzer/src/dialyzer_behaviours.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -62,9 +62,9 @@ check_callbacks(Module, Attrs, Records, Plt, Codeserver) ->
_ ->
MFA = {Module,module_info,0},
{_Var,Code} = dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver),
- File = get_file(cerl:get_ann(Code)),
+ File = get_file(Codeserver, Module, cerl:get_ann(Code)),
State = #state{plt = Plt, filename = File, behlines = BehLines,
- codeserver = Codeserver, records = Records},
+ codeserver = Codeserver, records = Records},
Warnings = get_warnings(Module, Behaviours, State),
[add_tag_warning_info(Module, W, State) || W <- Warnings]
end.
@@ -213,12 +213,15 @@ add_tag_warning_info(Module, {_Tag, [_B, Fun, Arity|_R]} = Warn, State) ->
dialyzer_codeserver:lookup_mfa_code({Module, Fun, Arity},
State#state.codeserver),
Anns = cerl:get_ann(FunCode),
- WarningInfo = {get_file(Anns), get_line(Anns), {Module, Fun, Arity}},
+ File = get_file(State#state.codeserver, Module, Anns),
+ WarningInfo = {File, get_line(Anns), {Module, Fun, Arity}},
{?WARN_BEHAVIOUR, WarningInfo, Warn}.
get_line([Line|_]) when is_integer(Line) -> Line;
get_line([_|Tail]) -> get_line(Tail);
get_line([]) -> -1.
-get_file([{file, File}|_]) -> File;
-get_file([_|Tail]) -> get_file(Tail).
+get_file(Codeserver, Module, [{file, FakeFile}|_]) ->
+ dialyzer_codeserver:translate_fake_file(Codeserver, Module, FakeFile);
+get_file(Codeserver, Module, [_|Tail]) ->
+ get_file(Codeserver, Module, Tail).
diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl
index 50abb22009..b079cd3090 100644
--- a/lib/dialyzer/src/dialyzer_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_callgraph.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -119,7 +119,11 @@
-opaque callgraph() :: #callgraph{}.
--type active_digraph() :: {'d', digraph:graph()} | {'e', ets:tid(), ets:tid()}.
+-type active_digraph() :: {'d', digraph:graph()}
+ | {'e',
+ Out :: ets:tid(),
+ In :: ets:tid(),
+ Map :: ets:tid()}.
%%----------------------------------------------------------------------
@@ -248,24 +252,30 @@ find_non_local_calls([], Set) ->
-spec get_depends_on(scc() | module(), callgraph()) -> [scc()].
-get_depends_on(SCC, #callgraph{active_digraph = {'e', Out, _In}}) ->
- case ets_lookup_dict(SCC, Out) of
- {ok, Value} -> Value;
- error -> []
- end;
+get_depends_on(SCC, #callgraph{active_digraph = {'e', Out, _In, Maps}}) ->
+ lookup_scc(SCC, Out, Maps);
get_depends_on(SCC, #callgraph{active_digraph = {'d', DG}}) ->
digraph:out_neighbours(DG, SCC).
-spec get_required_by(scc() | module(), callgraph()) -> [scc()].
-get_required_by(SCC, #callgraph{active_digraph = {'e', _Out, In}}) ->
- case ets_lookup_dict(SCC, In) of
- {ok, Value} -> Value;
- error -> []
- end;
+get_required_by(SCC, #callgraph{active_digraph = {'e', _Out, In, Maps}}) ->
+ lookup_scc(SCC, In, Maps);
get_required_by(SCC, #callgraph{active_digraph = {'d', DG}}) ->
digraph:in_neighbours(DG, SCC).
+lookup_scc(SCC, Table, Maps) ->
+ case ets_lookup_dict({'scc', SCC}, Maps) of
+ {ok, SCCInt} ->
+ case ets_lookup_dict(SCCInt, Table) of
+ {ok, Ints} ->
+ [ets:lookup_element(Maps, Int, 2) || Int <- Ints];
+ error ->
+ []
+ end;
+ error -> []
+ end.
+
%%----------------------------------------------------------------------
%% Handling of modules & SCCs
%%----------------------------------------------------------------------
@@ -582,9 +592,10 @@ digraph_delete(DG) ->
active_digraph_delete({'d', DG}) ->
digraph:delete(DG);
-active_digraph_delete({'e', Out, In}) ->
+active_digraph_delete({'e', Out, In, Maps}) ->
ets:delete(Out),
- ets:delete(In).
+ ets:delete(In),
+ ets:delete(Maps).
digraph_edges(DG) ->
digraph:edges(DG).
@@ -758,37 +769,28 @@ to_ps(#callgraph{} = CG, File, Args) ->
ok.
condensation(G) ->
- SCs = digraph_utils:strong_components(G),
- V2I = ets:new(condensation_v2i, []),
- I2C = ets:new(condensation_i2c, []),
- I2I = ets:new(condensation_i2i, [bag]),
- CFun =
- fun(SC, N) ->
- lists:foreach(fun(V) -> true = ets:insert(V2I, {V,N}) end, SC),
- true = ets:insert(I2C, {N, SC}),
- N + 1
- end,
- lists:foldl(CFun, 1, SCs),
- Fun1 =
- fun({V1, V2}) ->
- I1 = ets:lookup_element(V2I, V1, 2),
- I2 = ets:lookup_element(V2I, V2, 2),
- I1 =:= I2 orelse ets:insert(I2I, {I1, I2})
- end,
- lists:foreach(Fun1, digraph:edges(G)),
- Fun3 =
- fun({I1, I2}, {Out, In}) ->
- SC1 = ets:lookup_element(I2C, I1, 2),
- SC2 = ets:lookup_element(I2C, I2, 2),
- {dict:append(SC1, SC2, Out), dict:append(SC2, SC1, In)}
- end,
- {OutDict, InDict} = ets:foldl(Fun3, {dict:new(), dict:new()}, I2I),
- [OutETS, InETS] =
+ SCCs = digraph_utils:strong_components(G),
+ %% Assign unique numbers to SCCs:
+ Ints = lists:seq(1, length(SCCs)),
+ IntToSCC = lists:zip(Ints, SCCs),
+ IntScc = sofs:relation(IntToSCC, [{int, scc}]),
+ %% Subsitute strong components for vertices in edges using the
+ %% unique numbers:
+ C2V = sofs:relation([{SC, V} || SC <- SCCs, V <- SC], [{scc, v}]),
+ I2V = sofs:relative_product(IntScc, C2V), % [{v, int}]
+ Es = sofs:relation(digraph:edges(G), [{v, v}]),
+ R1 = sofs:relative_product(I2V, Es),
+ R2 = sofs:relative_product(I2V, sofs:converse(R1)),
+ %% Create in- and out-neighbours:
+ In = sofs:relation_to_family(sofs:strict_relation(R2)),
+ R3 = sofs:converse(R2),
+ Out = sofs:relation_to_family(sofs:strict_relation(R3)),
+ [OutETS, InETS, MapsETS] =
[ets:new(Name,[{read_concurrency, true}]) ||
- Name <- [callgraph_deps_out, callgraph_deps_in]],
- ets:insert(OutETS, dict:to_list(OutDict)),
- ets:insert(InETS, dict:to_list(InDict)),
- ets:delete(V2I),
- ets:delete(I2C),
- ets:delete(I2I),
- {{'e', OutETS, InETS}, SCs}.
+ Name <- [callgraph_deps_out, callgraph_deps_in, callgraph_scc_map]],
+ ets:insert(OutETS, sofs:to_external(Out)),
+ ets:insert(InETS, sofs:to_external(In)),
+ %% Create mappings from SCCs to unique integers, and the inverse:
+ ets:insert(MapsETS, lists:zip([{'scc', SCC} || SCC<- SCCs], Ints)),
+ ets:insert(MapsETS, IntToSCC),
+ {{'e', OutETS, InETS, MapsETS}, SCCs}.
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index fc56693ea3..e8c1613a33 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -637,8 +637,8 @@ cl_loop(State, LogCache) ->
{BackendPid, warnings, Warnings} ->
NewState = store_warnings(State, Warnings),
cl_loop(NewState, LogCache);
- {BackendPid, done, NewPlt, _NewDocPlt} ->
- return_value(State, NewPlt);
+ {BackendPid, done, NewMiniPlt, _NewDocPlt} ->
+ return_value(State, NewMiniPlt);
{BackendPid, ext_calls, ExtCalls} ->
cl_loop(State#cl_state{external_calls = ExtCalls}, LogCache);
{BackendPid, ext_types, ExtTypes} ->
@@ -654,6 +654,7 @@ cl_loop(State, LogCache) ->
cl_error(State, Msg);
_Other ->
%% io:format("Received ~p\n", [_Other]),
+ %% Note: {BackendPid, cserver, CodeServer, Plt} is ignored.
cl_loop(State, LogCache)
end.
@@ -699,10 +700,13 @@ return_value(State = #cl_state{erlang_mode = ErlangMode,
output_plt = OutputPlt,
plt_info = PltInfo,
stored_warnings = StoredWarnings},
- Plt) ->
+ MiniPlt) ->
case OutputPlt =:= none of
- true -> ok;
- false -> dialyzer_plt:to_file(OutputPlt, Plt, ModDeps, PltInfo)
+ true ->
+ dialyzer_plt:delete(MiniPlt);
+ false ->
+ Plt = dialyzer_plt:restore_full_plt(MiniPlt),
+ dialyzer_plt:to_file(OutputPlt, Plt, ModDeps, PltInfo)
end,
UnknownWarnings = unknown_warnings(State),
RetValue =
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index 934351aeeb..f668b81cd3 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.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.
@@ -510,7 +510,7 @@ warning_options_msg() ->
-Wno_match
Suppress warnings for patterns that are unused or cannot match.
-Wno_opaque
- Suppress warnings for violations of opaqueness of data types.
+ Suppress warnings for violations of opacity of data types.
-Wno_fail_call
Suppress warnings for failing calls.
-Wno_contracts
diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl
index 03cd9671af..543934193d 100644
--- a/lib/dialyzer/src/dialyzer_codeserver.erl
+++ b/lib/dialyzer/src/dialyzer_codeserver.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -29,7 +29,9 @@
-module(dialyzer_codeserver).
-export([delete/1,
- finalize_contracts/3,
+ store_temp_contracts/4,
+ give_away/2,
+ finalize_contracts/1,
finalize_exported_types/2,
finalize_records/2,
get_contracts/1,
@@ -38,7 +40,9 @@
get_exports/1,
get_records/1,
get_next_core_label/1,
- get_temp_contracts/1,
+ get_temp_contracts/2,
+ contracts_modules/1,
+ store_contracts/4,
get_temp_exported_types/1,
get_temp_records/1,
insert/3,
@@ -48,6 +52,7 @@
is_exported/2,
lookup_mod_code/2,
lookup_mfa_code/2,
+ lookup_mfa_var_label/2,
lookup_mod_records/2,
lookup_mod_contracts/2,
lookup_mfa_contract/2,
@@ -56,21 +61,22 @@
set_next_core_label/2,
set_temp_records/2,
store_temp_records/3,
- store_temp_contracts/4]).
+ translate_fake_file/3]).
--export_type([codeserver/0, fun_meta_info/0]).
+-export_type([codeserver/0, fun_meta_info/0, contracts/0]).
-include("dialyzer.hrl").
%%--------------------------------------------------------------------
-type dict_ets() :: ets:tid().
+-type map_ets() :: ets:tid().
-type set_ets() :: ets:tid().
-type types() :: erl_types:type_table().
--type mod_records() :: dict:dict(module(), types()).
+-type mod_records() :: erl_types:mod_records().
--type contracts() :: dict:dict(mfa(),dialyzer_contracts:file_contract()).
+-type contracts() :: #{mfa() => dialyzer_contracts:file_contract()}.
-type mod_contracts() :: dict:dict(module(), contracts()).
%% A property-list of data compiled from -compile and -dialyzer attributes.
@@ -81,16 +87,16 @@
-record(codeserver, {next_core_label = 0 :: label(),
code :: dict_ets(),
- exported_types :: set_ets() | 'undefined', % set(mfa())
- records :: dict_ets() | 'undefined',
- contracts :: dict_ets() | 'undefined',
- callbacks :: dict_ets() | 'undefined',
+ exported_types :: set_ets(), % set(mfa())
+ records :: map_ets(),
+ contracts :: map_ets(),
+ callbacks :: map_ets(),
fun_meta_info :: dict_ets(), % {mfa(), meta_info()}
exports :: 'clean' | set_ets(), % set(mfa())
temp_exported_types :: 'clean' | set_ets(), % set(mfa())
- temp_records :: 'clean' | dict_ets(),
- temp_contracts :: 'clean' | dict_ets(),
- temp_callbacks :: 'clean' | dict_ets()
+ temp_records :: 'clean' | map_ets(),
+ temp_contracts :: 'clean' | map_ets(),
+ temp_callbacks :: 'clean' | map_ets()
}).
-opaque codeserver() :: #codeserver{}.
@@ -104,7 +110,7 @@ ets_dict_find(Key, Table) ->
_:_ -> error
end.
-ets_dict_store(Key, Element, Table) ->
+ets_map_store(Key, Element, Table) ->
true = ets:insert(Table, {Key, Element}),
Table.
@@ -128,9 +134,6 @@ ets_set_to_set(Table) ->
Fold = fun({E}, Set) -> sets:add_element(E, Set) end,
ets:foldl(Fold, sets:new(), Table).
-ets_read_concurrent_table(Name) ->
- ets:new(Name, [{read_concurrency, true}]).
-
%%--------------------------------------------------------------------
-spec new() -> codeserver().
@@ -138,6 +141,13 @@ ets_read_concurrent_table(Name) ->
new() ->
CodeOptions = [compressed, public, {read_concurrency, true}],
Code = ets:new(dialyzer_codeserver_code, CodeOptions),
+ ReadOptions = [compressed, {read_concurrency, true}],
+ [Contracts, Callbacks, Records, ExportedTypes] =
+ [ets:new(Name, ReadOptions) ||
+ Name <- [dialyzer_codeserver_contracts,
+ dialyzer_codeserver_callbacks,
+ dialyzer_codeserver_records,
+ dialyzer_codeserver_exported_types]],
TempOptions = [public, {write_concurrency, true}],
[Exports, FunMetaInfo, TempExportedTypes, TempRecords, TempContracts,
TempCallbacks] =
@@ -150,6 +160,10 @@ new() ->
#codeserver{code = Code,
exports = Exports,
fun_meta_info = FunMetaInfo,
+ exported_types = ExportedTypes,
+ records = Records,
+ contracts = Contracts,
+ callbacks = Callbacks,
temp_exported_types = TempExportedTypes,
temp_records = TempRecords,
temp_contracts = TempContracts,
@@ -170,13 +184,15 @@ insert(Mod, ModCode, CS) ->
Exports = cerl:module_exports(ModCode),
Attrs = cerl:module_attrs(ModCode),
Defs = cerl:module_defs(ModCode),
+ {Files, SmallDefs} = compress_file_anno(Defs),
As = cerl:get_ann(ModCode),
Funs =
[{{Mod, cerl:fname_id(Var), cerl:fname_arity(Var)},
- Val} || Val = {Var, _Fun} <- Defs],
- Keys = [Key || {Key, _Value} <- Funs],
+ Val, {Var, cerl_trees:get_label(Fun)}} || Val = {Var, Fun} <- SmallDefs],
+ Keys = [Key || {Key, _Value, _Label} <- Funs],
ModEntry = {Mod, {Name, Exports, Attrs, Keys, As}},
- true = ets:insert(CS#codeserver.code, [ModEntry|Funs]),
+ ModFileEntry = {{mod, Mod}, Files},
+ true = ets:insert(CS#codeserver.code, [ModEntry, ModFileEntry|Funs]),
CS.
-spec get_temp_exported_types(codeserver()) -> sets:set(mfa()).
@@ -220,12 +236,12 @@ get_exports(#codeserver{exports = Exports}) ->
-spec finalize_exported_types(sets:set(mfa()), codeserver()) -> codeserver().
-finalize_exported_types(Set, CS) ->
- ExportedTypes = ets_read_concurrent_table(dialyzer_codeserver_exported_types),
+finalize_exported_types(Set,
+ #codeserver{exported_types = ExportedTypes,
+ temp_exported_types = TempETypes} = CS) ->
true = ets_set_insert_set(Set, ExportedTypes),
- TempExpTypes = CS#codeserver.temp_exported_types,
- true = ets:delete(TempExpTypes),
- CS#codeserver{exported_types = ExportedTypes, temp_exported_types = clean}.
+ true = ets:delete(TempETypes),
+ CS#codeserver{temp_exported_types = clean}.
-spec lookup_mod_code(atom(), codeserver()) -> cerl:c_module().
@@ -237,6 +253,11 @@ lookup_mod_code(Mod, CS) when is_atom(Mod) ->
lookup_mfa_code({_M, _F, _A} = MFA, CS) ->
table__lookup(CS#codeserver.code, MFA).
+-spec lookup_mfa_var_label(mfa(), codeserver()) -> {cerl:c_var(), label()}.
+
+lookup_mfa_var_label({_M, _F, _A} = MFA, CS) ->
+ ets:lookup_element(CS#codeserver.code, MFA, 3).
+
-spec get_next_core_label(codeserver()) -> label().
get_next_core_label(#codeserver{next_core_label = NCL}) ->
@@ -251,8 +272,8 @@ set_next_core_label(NCL, CS) ->
lookup_mod_records(Mod, #codeserver{records = RecDict}) when is_atom(Mod) ->
case ets_dict_find(Mod, RecDict) of
- error -> dict:new();
- {ok, Dict} -> Dict
+ error -> maps:new();
+ {ok, Map} -> Map
end.
-spec get_records(codeserver()) -> mod_records().
@@ -262,11 +283,11 @@ get_records(#codeserver{records = RecDict}) ->
-spec store_temp_records(module(), types(), codeserver()) -> codeserver().
-store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS)
+store_temp_records(Mod, Map, #codeserver{temp_records = TempRecDict} = CS)
when is_atom(Mod) ->
- case dict:size(Dict) =:= 0 of
+ case maps:size(Map) =:= 0 of
true -> CS;
- false -> CS#codeserver{temp_records = ets_dict_store(Mod, Dict, TempRecDict)}
+ false -> CS#codeserver{temp_records = ets_map_store(Mod, Map, TempRecDict)}
end.
-spec get_temp_records(codeserver()) -> mod_records().
@@ -284,20 +305,20 @@ set_temp_records(Dict, CS) ->
-spec finalize_records(mod_records(), codeserver()) -> codeserver().
-finalize_records(Dict, CS) ->
- true = ets:delete(CS#codeserver.temp_records),
- Records = ets_read_concurrent_table(dialyzer_codeserver_records),
+finalize_records(Dict, #codeserver{temp_records = TmpRecords,
+ records = Records} = CS) ->
+ true = ets:delete(TmpRecords),
true = ets_dict_store_dict(Dict, Records),
- CS#codeserver{records = Records, temp_records = clean}.
+ CS#codeserver{temp_records = clean}.
-spec lookup_mod_contracts(atom(), codeserver()) -> contracts().
lookup_mod_contracts(Mod, #codeserver{contracts = ContDict})
when is_atom(Mod) ->
case ets_dict_find(Mod, ContDict) of
- error -> dict:new();
+ error -> maps:new();
{ok, Keys} ->
- dict:from_list([get_file_contract(Key, ContDict)|| Key <- Keys])
+ maps:from_list([get_file_contract(Key, ContDict)|| Key <- Keys])
end.
get_file_contract(Key, ContDict) ->
@@ -330,48 +351,69 @@ get_callbacks(#codeserver{callbacks = CallbDict}) ->
-spec store_temp_contracts(module(), contracts(), contracts(), codeserver()) ->
codeserver().
-store_temp_contracts(Mod, SpecDict, CallbackDict,
+store_temp_contracts(Mod, SpecMap, CallbackMap,
#codeserver{temp_contracts = Cn,
temp_callbacks = Cb} = CS)
when is_atom(Mod) ->
- CS1 =
- case dict:size(SpecDict) =:= 0 of
- true -> CS;
- false ->
- CS#codeserver{temp_contracts = ets_dict_store(Mod, SpecDict, Cn)}
- end,
- case dict:size(CallbackDict) =:= 0 of
- true -> CS1;
- false ->
- CS1#codeserver{temp_callbacks = ets_dict_store(Mod, CallbackDict, Cb)}
- end.
-
--spec get_temp_contracts(codeserver()) -> {mod_contracts(), mod_contracts()}.
+ CS1 = CS#codeserver{temp_contracts = ets_map_store(Mod, SpecMap, Cn)},
+ CS1#codeserver{temp_callbacks = ets_map_store(Mod, CallbackMap, Cb)}.
-get_temp_contracts(#codeserver{temp_contracts = TempContDict,
- temp_callbacks = TempCallDict}) ->
- {ets_dict_to_dict(TempContDict), ets_dict_to_dict(TempCallDict)}.
+-spec contracts_modules(codeserver()) -> [module()].
--spec finalize_contracts(mod_contracts(), mod_contracts(), codeserver()) ->
- codeserver().
+contracts_modules(#codeserver{temp_contracts = TempContTable}) ->
+ ets:select(TempContTable, [{{'$1', '$2'}, [], ['$1']}]).
-finalize_contracts(SpecDict, CallbackDict, CS) ->
- Contracts = ets_read_concurrent_table(dialyzer_codeserver_contracts),
- Callbacks = ets_read_concurrent_table(dialyzer_codeserver_callbacks),
- Contracts = dict:fold(fun decompose_spec_dict/3, Contracts, SpecDict),
- Callbacks = dict:fold(fun decompose_cb_dict/3, Callbacks, CallbackDict),
- CS#codeserver{contracts = Contracts, callbacks = Callbacks,
- temp_contracts = clean, temp_callbacks = clean}.
+-spec store_contracts(module(), contracts(), contracts(), codeserver()) ->
+ codeserver().
-decompose_spec_dict(Mod, Dict, Table) ->
- Keys = dict:fetch_keys(Dict),
- true = ets:insert(Table, dict:to_list(Dict)),
- true = ets:insert(Table, {Mod, Keys}),
- Table.
+store_contracts(Mod, SpecMap, CallbackMap, CS) ->
+ #codeserver{contracts = SpecDict, callbacks = CallbackDict} = CS,
+ Keys = maps:keys(SpecMap),
+ true = ets:insert(SpecDict, maps:to_list(SpecMap)),
+ true = ets:insert(SpecDict, {Mod, Keys}),
+ true = ets:insert(CallbackDict, maps:to_list(CallbackMap)),
+ CS.
-decompose_cb_dict(_Mod, Dict, Table) ->
- true = ets:insert(Table, dict:to_list(Dict)),
- Table.
+-spec get_temp_contracts(module(), codeserver()) ->
+ {contracts(), contracts()}.
+
+get_temp_contracts(Mod, #codeserver{temp_contracts = TempContDict,
+ temp_callbacks = TempCallDict}) ->
+ [{Mod, Contracts}] = ets:lookup(TempContDict, Mod),
+ true = ets:delete(TempContDict, Mod),
+ [{Mod, Callbacks}] = ets:lookup(TempCallDict, Mod),
+ true = ets:delete(TempCallDict, Mod),
+ {Contracts, Callbacks}.
+
+-spec give_away(codeserver(), pid()) -> 'ok'.
+
+give_away(#codeserver{temp_records = TempRecords,
+ temp_contracts = TempContracts,
+ temp_callbacks = TempCallbacks,
+ records = Records,
+ contracts = Contracts,
+ callbacks = Callbacks}, Pid) ->
+ _ = [true = ets:give_away(Table, Pid, any) ||
+ Table <- [TempRecords, TempContracts, TempCallbacks,
+ Records, Contracts, Callbacks],
+ Table =/= clean],
+ ok.
+
+-spec finalize_contracts(codeserver()) -> codeserver().
+
+finalize_contracts(#codeserver{temp_contracts = TempContDict,
+ temp_callbacks = TempCallDict} = CS) ->
+ true = ets:delete(TempContDict),
+ true = ets:delete(TempCallDict),
+ CS#codeserver{temp_contracts = clean, temp_callbacks = clean}.
+
+-spec translate_fake_file(codeserver(), module(), file:filename()) ->
+ file:filename().
+
+translate_fake_file(#codeserver{code = Code}, Module, FakeFile) ->
+ Files = ets:lookup_element(Code, {mod, Module}, 2),
+ {FakeFile, File} = lists:keyfind(FakeFile, 1, Files),
+ File.
table__lookup(TablePid, M) when is_atom(M) ->
{Name, Exports, Attrs, Keys, As} = ets:lookup_element(TablePid, M, 2),
@@ -379,3 +421,25 @@ table__lookup(TablePid, M) when is_atom(M) ->
cerl:ann_c_module(As, Name, Exports, Attrs, Defs);
table__lookup(TablePid, MFA) ->
ets:lookup_element(TablePid, MFA, 2).
+
+compress_file_anno(Term) ->
+ {Files, SmallTerm} = compress_file_anno(Term, []),
+ {[{FakeFile, File} || {File, {file, FakeFile}} <- Files], SmallTerm}.
+
+compress_file_anno({file, F}, Fs) when is_list(F) ->
+ case lists:keyfind(F, 1, Fs) of
+ false ->
+ I = integer_to_list(length(Fs)),
+ FileI = {file, I},
+ NFs = [{F, FileI}|Fs],
+ {NFs, FileI};
+ {F, FileI} -> {Fs, FileI}
+ end;
+compress_file_anno(T, Fs) when is_tuple(T) ->
+ {NFs, NL} = compress_file_anno(tuple_to_list(T), Fs),
+ {NFs, list_to_tuple(NL)};
+compress_file_anno([E|L], Fs) ->
+ {Fs1, NE} = compress_file_anno(E, Fs),
+ {NFs, NL} = compress_file_anno(L, Fs1),
+ {NFs, [NE|NL]};
+compress_file_anno(T, Fs) -> {Fs, T}.
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 976a2b8955..b101f710e7 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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.
@@ -31,7 +31,7 @@
get_contract_return/2,
%% get_contract_signature/1,
is_overloaded/1,
- process_contract_remote_types/1,
+ process_contract_remote_types/2,
store_tmp_contract/5]).
-export_type([file_contract/0, plt_contracts/0]).
@@ -146,14 +146,13 @@ sequence([], _Delimiter) -> "";
sequence([H], _Delimiter) -> H;
sequence([H|T], Delimiter) -> H ++ Delimiter ++ sequence(T, Delimiter).
--spec process_contract_remote_types(dialyzer_codeserver:codeserver()) ->
- dialyzer_codeserver:codeserver().
+-spec process_contract_remote_types(dialyzer_codeserver:codeserver(),
+ erl_types:mod_records()) ->
+ dialyzer_codeserver:codeserver().
-process_contract_remote_types(CodeServer) ->
- {TmpContractDict, TmpCallbackDict} =
- dialyzer_codeserver:get_temp_contracts(CodeServer),
+process_contract_remote_types(CodeServer, RecordDict) ->
+ Mods = dialyzer_codeserver:contracts_modules(CodeServer),
ExpTypes = dialyzer_codeserver:get_exported_types(CodeServer),
- RecordDict = dialyzer_codeserver:get_records(CodeServer),
ContractFun =
fun({{_M, _F, _A}=MFA, {File, TmpContract, Xtra}}, C0) ->
#tmp_contract{contract_funs = CFuns, forms = Forms} = TmpContract,
@@ -165,20 +164,21 @@ process_contract_remote_types(CodeServer) ->
{{MFA, {File, Contract, Xtra}}, C2}
end,
ModuleFun =
- fun({ModuleName, ContractDict}, C3) ->
- {NewContractList, C4} =
- lists:mapfoldl(ContractFun, C3, dict:to_list(ContractDict)),
- {{ModuleName, dict:from_list(NewContractList)}, C4}
+ fun(ModuleName) ->
+ Cache = erl_types:cache__new(),
+ {ContractMap, CallbackMap} =
+ dialyzer_codeserver:get_temp_contracts(ModuleName, CodeServer),
+ {NewContractList, Cache1} =
+ lists:mapfoldl(ContractFun, Cache, maps:to_list(ContractMap)),
+ {NewCallbackList, _NewCache} =
+ lists:mapfoldl(ContractFun, Cache1, maps:to_list(CallbackMap)),
+ dialyzer_codeserver:store_contracts(ModuleName,
+ maps:from_list(NewContractList),
+ maps:from_list(NewCallbackList),
+ CodeServer)
end,
- Cache = erl_types:cache__new(),
- {NewContractList, C5} =
- lists:mapfoldl(ModuleFun, Cache, dict:to_list(TmpContractDict)),
- {NewCallbackList, _C6} =
- lists:mapfoldl(ModuleFun, C5, dict:to_list(TmpCallbackDict)),
- NewContractDict = dict:from_list(NewContractList),
- NewCallbackDict = dict:from_list(NewCallbackList),
- dialyzer_codeserver:finalize_contracts(NewContractDict, NewCallbackDict,
- CodeServer).
+ lists:foreach(ModuleFun, Mods),
+ dialyzer_codeserver:finalize_contracts(CodeServer).
-type opaques_fun() :: fun((module()) -> [erl_types:erl_type()]).
@@ -232,7 +232,7 @@ check_contract(#contract{contracts = Contracts}, SuccType, Opaques) ->
error ->
{error, {overlapping_contract, []}};
ok ->
- InfList = [erl_types:t_inf(Contract, SuccType, Opaques)
+ InfList = [{Contract, erl_types:t_inf(Contract, SuccType, Opaques)}
|| Contract <- Contracts2],
case check_contract_inf_list(InfList, SuccType, Opaques) of
{error, _} = Invalid -> Invalid;
@@ -256,10 +256,21 @@ check_domains([Dom|Doms]) ->
%% Allow a contract if one of the overloaded contracts is possible.
%% We used to be more strict, e.g., all overloaded contracts had to be
%% possible.
-check_contract_inf_list([FunType|Left], SuccType, Opaques) ->
+check_contract_inf_list(List, SuccType, Opaques) ->
+ case check_contract_inf_list(List, SuccType, Opaques, []) of
+ ok -> ok;
+ {error, []} -> {error, invalid_contract};
+ {error, [{SigRange, ContrRange}|_]} ->
+ case erl_types:t_find_opaque_mismatch(SigRange, ContrRange, Opaques) of
+ error -> {error, invalid_contract};
+ {ok, _T1, T2} -> {error, {opaque_mismatch, T2}}
+ end
+ end.
+
+check_contract_inf_list([{Contract, FunType}|Left], SuccType, Opaques, OM) ->
FunArgs = erl_types:t_fun_args(FunType),
case lists:any(fun erl_types:t_is_none_or_unit/1, FunArgs) of
- true -> check_contract_inf_list(Left, SuccType, Opaques);
+ true -> check_contract_inf_list(Left, SuccType, Opaques, OM);
false ->
STRange = erl_types:t_fun_range(SuccType),
case erl_types:t_is_none_or_unit(STRange) of
@@ -267,13 +278,16 @@ check_contract_inf_list([FunType|Left], SuccType, Opaques) ->
false ->
Range = erl_types:t_fun_range(FunType),
case erl_types:t_is_none(erl_types:t_inf(STRange, Range)) of
- true -> check_contract_inf_list(Left, SuccType, Opaques);
+ true ->
+ CR = erl_types:t_fun_range(Contract),
+ NewOM = [{STRange, CR}|OM],
+ check_contract_inf_list(Left, SuccType, Opaques, NewOM);
false -> ok
end
end
end;
-check_contract_inf_list([], _SuccType, _Opaques) ->
- {error, invalid_contract}.
+check_contract_inf_list([], _SuccType, _Opaques, OM) ->
+ {error, OM}.
check_extraneous([], _SuccType) -> ok;
check_extraneous([C|Cs], SuccType) ->
@@ -383,7 +397,7 @@ solve_constraints(Contract, Call, Constraints) ->
%% ?debug("Inf: ~s\n", [erl_types:t_to_string(Inf)]),
%% erl_types:t_assign_variables_to_subtype(Contract, Inf).
--type contracts() :: dict:dict(mfa(),dialyzer_contracts:file_contract()).
+-type contracts() :: dialyzer_codeserver:contracts().
%% Checks the contracts for functions that are not implemented
-spec contracts_without_fun(contracts(), [_], dialyzer_callgraph:callgraph()) ->
@@ -393,12 +407,12 @@ contracts_without_fun(Contracts, AllFuns0, Callgraph) ->
AllFuns1 = [{dialyzer_callgraph:lookup_name(Label, Callgraph), Arity}
|| {Label, Arity} <- AllFuns0],
AllFuns2 = [{M, F, A} || {{ok, {M, F, _}}, A} <- AllFuns1],
- AllContractMFAs = dict:fetch_keys(Contracts),
+ AllContractMFAs = maps:keys(Contracts),
ErrorContractMFAs = AllContractMFAs -- AllFuns2,
[warn_spec_missing_fun(MFA, Contracts) || MFA <- ErrorContractMFAs].
warn_spec_missing_fun({M, F, A} = MFA, Contracts) ->
- {{File, Line}, _Contract, _Xtra} = dict:fetch(MFA, Contracts),
+ {{File, Line}, _Contract, _Xtra} = maps:get(MFA, Contracts),
WarningInfo = {File, Line, MFA},
{?WARN_CONTRACT_SYNTAX, WarningInfo, {spec_missing_fun, [M, F, A]}}.
@@ -431,11 +445,11 @@ insert_constraints([], Map) -> Map.
-spec store_tmp_contract(mfa(), file_line(), spec_data(), contracts(), types()) ->
contracts().
-store_tmp_contract(MFA, FileLine, {TypeSpec, Xtra}, SpecDict, RecordsDict) ->
+store_tmp_contract(MFA, FileLine, {TypeSpec, Xtra}, SpecMap, RecordsDict) ->
%% io:format("contract from form: ~p\n", [TypeSpec]),
TmpContract = contract_from_form(TypeSpec, MFA, RecordsDict, FileLine),
%% io:format("contract: ~p\n", [TmpContract]),
- dict:store(MFA, {FileLine, TmpContract, Xtra}, SpecDict).
+ maps:put(MFA, {FileLine, TmpContract, Xtra}, SpecMap).
contract_from_form(Forms, MFA, RecDict, FileLine) ->
{CFuns, Forms1} = contract_from_form(Forms, MFA, RecDict, FileLine, [], []),
@@ -663,7 +677,7 @@ get_invalid_contract_warnings(Modules, CodeServer, Plt, FindOpaques) ->
get_invalid_contract_warnings_modules([Mod|Mods], CodeServer, Plt, FindOpaques, Acc) ->
Contracts1 = dialyzer_codeserver:lookup_mod_contracts(Mod, CodeServer),
- Contracts2 = dict:to_list(Contracts1),
+ Contracts2 = maps:to_list(Contracts1),
Records = dialyzer_codeserver:lookup_mod_records(Mod, CodeServer),
NewAcc = get_invalid_contract_warnings_funs(Contracts2, Plt, Records, FindOpaques, Acc),
get_invalid_contract_warnings_modules(Mods, CodeServer, Plt, FindOpaques, NewAcc);
@@ -687,6 +701,9 @@ get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract, _Xtra}}|Left],
case check_contract(Contract, Sig, Opaques) of
{error, invalid_contract} ->
[invalid_contract_warning(MFA, WarningInfo, Sig, RecDict)|Acc];
+ {error, {opaque_mismatch, T2}} ->
+ W = contract_opaque_warning(MFA, WarningInfo, T2, Sig, RecDict),
+ [W|Acc];
{error, {overlapping_contract, []}} ->
[overlapping_contract_warning(MFA, WarningInfo)|Acc];
{error, {extra_range, ExtraRanges, STRange}} ->
@@ -740,6 +757,12 @@ invalid_contract_warning({M, F, A}, WarningInfo, SuccType, RecDict) ->
SuccTypeStr = dialyzer_utils:format_sig(SuccType, RecDict),
{?WARN_CONTRACT_TYPES, WarningInfo, {invalid_contract, [M, F, A, SuccTypeStr]}}.
+contract_opaque_warning({M, F, A}, WarningInfo, OpType, SuccType, RecDict) ->
+ OpaqueStr = erl_types:t_to_string(OpType),
+ SuccTypeStr = dialyzer_utils:format_sig(SuccType, RecDict),
+ {?WARN_CONTRACT_TYPES, WarningInfo,
+ {contract_with_opaque, [M, F, A, OpaqueStr, SuccTypeStr]}}.
+
overlapping_contract_warning({M, F, A}, WarningInfo) ->
{?WARN_CONTRACT_TYPES, WarningInfo, {overlapping_contract, [M, F, A]}}.
@@ -768,19 +791,9 @@ picky_contract_check(CSig0, Sig0, MFA, WarningInfo, Contract, RecDict, Acc) ->
end.
extra_contract_warning(MFA, WarningInfo, Contract, CSig, Sig, RecDict) ->
- %% We do not want to depend upon erl_types:t_to_string() possibly
- %% hiding the contents of opaque types.
- SigUnopaque = erl_types:t_unopaque(Sig),
- CSigUnopaque = erl_types:t_unopaque(CSig),
- SigString0 =
- lists:flatten(dialyzer_utils:format_sig(SigUnopaque, RecDict)),
- ContractString0 =
- lists:flatten(dialyzer_utils:format_sig(CSigUnopaque, RecDict)),
- %% The only difference is in record fields containing 'undefined' or not.
- IsUndefRecordFieldsRelated = SigString0 =:= ContractString0,
{IsRemoteTypesRelated, SubtypeRelation} =
is_remote_types_related(Contract, CSig, Sig, MFA, RecDict),
- case IsUndefRecordFieldsRelated orelse IsRemoteTypesRelated of
+ case IsRemoteTypesRelated of
true ->
no_warning;
false ->
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 9399789464..83ce875280 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -529,7 +529,7 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
case is_race_analysis_enabled(State) of
true ->
Ann = cerl:get_ann(Tree),
- File = get_file(Ann),
+ File = get_file(Ann, State),
Line = abs(get_line(Ann)),
dialyzer_races:store_race_call(Fun, ArgTypes, Args,
{File, Line}, State);
@@ -978,12 +978,21 @@ handle_case(Tree, Map, State) ->
false -> State1
end,
Map2 = join_maps_begin(Map1),
- {MapList, State3, Type} =
+ {MapList, State3, Type, Warns} =
handle_clauses(Clauses, Arg, ArgType, ArgType, State2,
- [], Map2, [], []),
+ [], Map2, [], [], []),
+ %% Non-Erlang BEAM languages, such as Elixir, expand language constructs
+ %% into case statements. In that case, we do not want to warn on
+ %% individual clauses not matching unless none of them can.
+ SupressForced = is_compiler_generated(cerl:get_ann(Tree))
+ andalso not (t_is_none(Type)),
+ State4 = lists:foldl(fun({T,R,M,F}, S) ->
+ state__add_warning(
+ S,T,R,M,F andalso (not SupressForced))
+ end, State3, Warns),
Map3 = join_maps_end(MapList, Map2),
debug_pp_map(Map3),
- {State3, Map3, Type}
+ {State4, Map3, Type}
end.
%%----------------------------------------
@@ -1082,22 +1091,24 @@ handle_receive(Tree, Map, State) ->
RaceListSize + 1, State);
false -> State
end,
- {MapList, State2, ReceiveType} =
+ {MapList, State2, ReceiveType, Warns} =
handle_clauses(Clauses, ?no_arg, t_any(), t_any(), State1, [], Map,
- [], []),
+ [], [], []),
+ State3 = lists:foldl(fun({T,R,M,F}, S) -> state__add_warning(S,T,R,M,F) end,
+ State2, Warns),
Map1 = join_maps(MapList, Map),
- {State3, Map2, TimeoutType} = traverse(Timeout, Map1, State2),
- Opaques = State3#state.opaques,
+ {State4, Map2, TimeoutType} = traverse(Timeout, Map1, State3),
+ Opaques = State4#state.opaques,
case (t_is_atom(TimeoutType, Opaques) andalso
(t_atom_vals(TimeoutType, Opaques) =:= ['infinity'])) of
true ->
- {State3, Map2, ReceiveType};
+ {State4, Map2, ReceiveType};
false ->
Action = cerl:receive_action(Tree),
- {State4, Map3, ActionType} = traverse(Action, Map, State3),
+ {State5, Map3, ActionType} = traverse(Action, Map, State4),
Map4 = join_maps([Map3, Map1], Map),
Type = t_sup(ReceiveType, ActionType),
- {State4, Map4, Type}
+ {State5, Map4, Type}
end.
%%----------------------------------------
@@ -1200,7 +1211,7 @@ handle_tuple(Tree, Map, State) ->
TagVal = cerl:atom_val(Tag),
case state__lookup_record(TagVal, length(Left), State1) of
error -> {State1, Map1, TupleType};
- {ok, RecType} ->
+ {ok, RecType, FieldNames} ->
InfTupleType = t_inf(RecType, TupleType),
case t_is_none(InfTupleType) of
true ->
@@ -1221,10 +1232,13 @@ handle_tuple(Tree, Map, State) ->
Tree, Msg),
{State2, Map1, t_none()};
{error, opaque, ErrorPat, ErrorType, OpaqueType} ->
+ OpaqueStr = format_type(OpaqueType, State1),
+ Name = field_name(Elements, ErrorPat, FieldNames),
Msg = {opaque_match,
- [format_patterns(ErrorPat),
- format_type(ErrorType, State1),
- format_type(OpaqueType, State1)]},
+ ["record field" ++ Name ++
+ " declared to be of type " ++
+ format_type(ErrorType, State1),
+ OpaqueStr, OpaqueStr]},
State2 = state__add_warning(State1, ?WARN_OPAQUE,
Tree, Msg),
{State2, Map1, t_none()};
@@ -1241,11 +1255,20 @@ handle_tuple(Tree, Map, State) ->
end
end.
+field_name(Elements, ErrorPat, FieldNames) ->
+ try
+ [Pat] = ErrorPat,
+ Take = lists:takewhile(fun(X) -> X =/= Pat end, Elements),
+ " " ++ format_atom(lists:nth(length(Take), FieldNames))
+ catch
+ _:_ -> ""
+ end.
+
%%----------------------------------------
%% Clauses
%%
handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn,
- Acc, ClauseAcc) ->
+ Acc, ClauseAcc, WarnAcc0) ->
IsRaceAnalysisEnabled = is_race_analysis_enabled(State),
State1 =
case IsRaceAnalysisEnabled of
@@ -1258,8 +1281,8 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn,
State);
false -> State
end,
- {State2, ClauseMap, BodyType, NewArgType} =
- do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1),
+ {State2, ClauseMap, BodyType, NewArgType, WarnAcc} =
+ do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1, WarnAcc0),
{NewClauseAcc, State3} =
case IsRaceAnalysisEnabled of
true ->
@@ -1277,9 +1300,9 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn,
false -> {[BodyType|CaseTypes], [ClauseMap|Acc]}
end,
handle_clauses(Left, Arg, NewArgType, OrigArgType, State3,
- NewCaseTypes, MapIn, NewAcc, NewClauseAcc);
+ NewCaseTypes, MapIn, NewAcc, NewClauseAcc, WarnAcc);
handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc,
- ClauseAcc) ->
+ ClauseAcc, WarnAcc) ->
State1 =
case is_race_analysis_enabled(State) of
true ->
@@ -1289,9 +1312,9 @@ handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc,
RaceListSize + 1, State);
false -> State
end,
- {lists:reverse(Acc), State1, t_sup(CaseTypes)}.
+ {lists:reverse(Acc), State1, t_sup(CaseTypes), WarnAcc}.
-do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
+do_clause(C, Arg, ArgType0, OrigArgType, Map, State, Warns) ->
Pats = cerl:clause_pats(C),
Guard = cerl:clause_guard(C),
Body = cerl:clause_body(C),
@@ -1323,13 +1346,11 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
[cerl_prettypr:format(C), format_type(ArgType0, State1)]),
case state__warning_mode(State1) of
false ->
- {State1, Map, t_none(), ArgType0};
+ {State1, Map, t_none(), ArgType0, Warns};
true ->
{Msg, Force} =
case t_is_none(ArgType0) of
true ->
- PatString = format_patterns(Pats),
- PatTypes = [PatString, format_type(OrigArgType, State1)],
%% See if this is covered by an earlier clause or if it
%% simply cannot match
OrigArgTypes =
@@ -1337,14 +1358,24 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
true -> Any = t_any(), [Any || _ <- Pats];
false -> t_to_tlist(OrigArgType)
end,
+ PatString = format_patterns(Pats),
+ ArgTypeString = format_type(OrigArgType, State1),
+ BindResOrig =
+ bind_pat_vars(Pats, OrigArgTypes, [], Map1, State1),
Tag =
- case bind_pat_vars(Pats, OrigArgTypes, [], Map1, State1) of
+ case BindResOrig of
{error, bind, _, _, _} -> pattern_match;
{error, record, _, _, _} -> record_match;
{error, opaque, _, _, _} -> opaque_match;
{_, _} -> pattern_match_cov
end,
- {{Tag, PatTypes}, false};
+ PatTypes = case BindResOrig of
+ {error, opaque, _, _, OpaqueType} ->
+ [PatString, ArgTypeString,
+ format_type(OpaqueType, State1)];
+ _ -> [PatString, ArgTypeString]
+ end,
+ {{Tag, PatTypes}, false};
false ->
%% Try to find out if this is a default clause in a list
%% comprehension and supress this. A real Hack(tm)
@@ -1403,8 +1434,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
{record_match, _} -> ?WARN_MATCHING;
{pattern_match_cov, _} -> ?WARN_MATCHING
end,
- {state__add_warning(State1, WarnType, C, Msg, Force),
- Map, t_none(), ArgType0}
+ {State1, Map, t_none(), ArgType0, [{WarnType, C, Msg, Force}|Warns]}
end;
{Map2, PatTypes} ->
Map3 =
@@ -1437,9 +1467,9 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
false ->
{guard_fail_pat, [PatString, format_type(ArgType0, State1)]}
end,
- State2 =
+ Warn =
case Reason of
- none -> state__add_warning(State1, ?WARN_MATCHING, C, DefaultMsg);
+ none -> {?WARN_MATCHING, C, DefaultMsg, false};
{FailGuard, Msg} ->
case is_compiler_generated(cerl:get_ann(FailGuard)) of
false ->
@@ -1448,15 +1478,15 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
{neg_guard_fail, _} -> ?WARN_MATCHING;
{opaque_guard, _} -> ?WARN_OPAQUE
end,
- state__add_warning(State1, WarnType, FailGuard, Msg);
+ {WarnType, FailGuard, Msg, false};
true ->
- state__add_warning(State1, ?WARN_MATCHING, C, Msg)
+ {?WARN_MATCHING, C, Msg, false}
end
end,
- {State2, Map, t_none(), NewArgType};
+ {State1, Map, t_none(), NewArgType, [Warn|Warns]};
Map4 ->
{RetState, RetMap, BodyType} = traverse(Body, Map4, State1),
- {RetState, RetMap, BodyType, NewArgType}
+ {RetState, RetMap, BodyType, NewArgType, Warns}
end
end.
@@ -1622,7 +1652,7 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
TagAtom = cerl:atom_val(Tag),
case state__lookup_record(TagAtom, length(Left), State) of
error -> {false, t_tuple(length(Es))};
- {ok, Record} ->
+ {ok, Record, _FieldNames} ->
[_Head|AnyTail] = [t_any() || _ <- Es],
UntypedRecord = t_tuple([t_atom(TagAtom)|AnyTail]),
{not t_is_equal(Record, UntypedRecord), Record}
@@ -2150,7 +2180,7 @@ handle_guard_is_record(Guard, Map, Env, Eval, State) ->
TupleType =
case state__lookup_record(Tag, ArityMin1, State) of
error -> Tuple;
- {ok, Prototype} -> Prototype
+ {ok, Prototype, _FieldNames} -> Prototype
end,
Type = t_inf(TupleType, RecType, State#state.opaques),
case t_is_none(Type) of
@@ -2600,7 +2630,7 @@ bind_guard_case_clauses(Arg, Clauses, Map0, Env, Eval, State) ->
Map = join_maps_begin(Map0),
{GenMap, GenArgType} = bind_guard(Arg, Map, Env, dont_know, State),
bind_guard_case_clauses(GenArgType, GenMap, Arg, Clauses1, Map, Env, Eval,
- t_none(), [], State).
+ t_none(), [], [], State).
filter_fail_clauses([Clause|Left]) ->
case (cerl:clause_pats(Clause) =:= []) of
@@ -2619,7 +2649,7 @@ filter_fail_clauses([]) ->
[].
bind_guard_case_clauses(GenArgType, GenMap, ArgExpr, [Clause|Left],
- Map, Env, Eval, AccType, AccMaps, State) ->
+ Map, Env, Eval, AccType, AccMaps, Throws, State) ->
Pats = cerl:clause_pats(Clause),
{NewMap0, ArgType} =
case Pats of
@@ -2663,9 +2693,9 @@ bind_guard_case_clauses(GenArgType, GenMap, ArgExpr, [Clause|Left],
case (NewMap1 =:= none) orelse t_is_none(GenArgType) of
true ->
bind_guard_case_clauses(NewGenArgType, GenMap, ArgExpr, Left, Map, Env,
- Eval, AccType, AccMaps, State);
+ Eval, AccType, AccMaps, Throws, State);
false ->
- {NewAccType, NewAccMaps} =
+ {NewAccType, NewAccMaps, NewThrows} =
try
{NewMap2, GuardType} = bind_guard(Guard, NewMap1, Env, pos, State),
case t_is_none(t_inf(t_atom(true), GuardType)) of
@@ -2689,17 +2719,26 @@ bind_guard_case_clauses(GenArgType, GenMap, ArgExpr, [Clause|Left],
dont_know ->
ok
end,
- {t_sup(AccType, CType), [NewMap3|AccMaps]}
+ {t_sup(AccType, CType), [NewMap3|AccMaps], Throws}
catch
- throw:{fail, _What} -> {AccType, AccMaps}
+ throw:{fail, Reason} ->
+ Throws1 = case Reason of
+ none -> Throws;
+ _ -> Throws ++ [Reason]
+ end,
+ {AccType, AccMaps, Throws1}
end,
bind_guard_case_clauses(NewGenArgType, GenMap, ArgExpr, Left, Map, Env,
- Eval, NewAccType, NewAccMaps, State)
+ Eval, NewAccType, NewAccMaps, NewThrows, State)
end;
bind_guard_case_clauses(_GenArgType, _GenMap, _ArgExpr, [], Map, _Env, _Eval,
- AccType, AccMaps, _State) ->
+ AccType, AccMaps, Throws, _State) ->
case t_is_none(AccType) of
- true -> throw({fail, none});
+ true ->
+ case Throws of
+ [Throw|_] -> throw({fail, Throw});
+ [] -> throw({fail, none})
+ end;
false -> {join_maps_end(AccMaps, Map), AccType}
end.
@@ -3059,7 +3098,7 @@ state__add_warning(#state{warnings = Warnings, warning_mode = true} = State,
Ann = cerl:get_ann(Tree),
case Force of
true ->
- WarningInfo = {get_file(Ann),
+ WarningInfo = {get_file(Ann, State),
abs(get_line(Ann)),
State#state.curr_fun},
Warn = {Tag, WarningInfo, Msg},
@@ -3069,7 +3108,9 @@ state__add_warning(#state{warnings = Warnings, warning_mode = true} = State,
case is_compiler_generated(Ann) of
true -> State;
false ->
- WarningInfo = {get_file(Ann), get_line(Ann), State#state.curr_fun},
+ WarningInfo = {get_file(Ann, State),
+ get_line(Ann),
+ State#state.curr_fun},
Warn = {Tag, WarningInfo, Msg},
case Tag of
?WARN_CONTRACT_RANGE -> ok;
@@ -3197,7 +3238,8 @@ state__lookup_record(Tag, Arity, #state{records = Records}) ->
RecType =
t_tuple([t_atom(Tag)|
[FieldType || {_FieldName, _Abstr, FieldType} <- Fields]]),
- {ok, RecType};
+ FieldNames = [FieldName || {FieldName, _Abstr, _FieldType} <- Fields],
+ {ok, RecType, FieldNames};
error ->
error
end.
@@ -3467,6 +3509,12 @@ state__put_races(Races, State) ->
state__records_only(#state{records = Records}) ->
#state{records = Records}.
+-spec state__translate_file(file:filename(), state()) -> file:filename().
+
+state__translate_file(FakeFile, State) ->
+ #state{codeserver = CodeServer, module = Module} = State,
+ dialyzer_codeserver:translate_fake_file(CodeServer, Module, FakeFile).
+
%%% ===========================================================================
%%%
%%% Races
@@ -3538,9 +3586,11 @@ get_line([Line|_]) when is_integer(Line) -> Line;
get_line([_|Tail]) -> get_line(Tail);
get_line([]) -> -1.
-get_file([]) -> [];
-get_file([{file, File}|_]) -> File;
-get_file([_|Tail]) -> get_file(Tail).
+get_file([], _State) -> [];
+get_file([{file, FakeFile}|_], State) ->
+ state__translate_file(FakeFile, State);
+get_file([_|Tail], State) ->
+ get_file(Tail, State).
is_compiler_generated(Ann) ->
lists:member(compiler_generated, Ann) orelse (get_line(Ann) < 1).
@@ -3650,6 +3700,9 @@ map_pats(Pats) ->
fold_literals(TreeList) ->
[cerl:fold_literal(Tree) || Tree <- TreeList].
+format_atom(A) ->
+ format_cerl(cerl:c_atom(A)).
+
type(Tree) ->
Folded = cerl:fold_literal(Tree),
case cerl:type(Folded) of
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index 30d2bdeca4..1701aff2f2 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -2,7 +2,7 @@
%%------------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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.
@@ -310,7 +310,7 @@ createWarningsMenu() ->
addCheckedItem(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS,
"Failing function calls"),
addCheckedItem(WarningsMenu, ?menuID_WARN_BAD_FUN, "Bad fun applications"),
- addCheckedItem(WarningsMenu, ?menuID_WARN_OPAQUE, "Opaqueness violations"),
+ addCheckedItem(WarningsMenu, ?menuID_WARN_OPAQUE, "Opacity violations"),
addCheckedItem(WarningsMenu, ?menuID_WARN_LIST_CONSTR,
"Improper list constructions"),
addCheckedItem(WarningsMenu, ?menuID_WARN_UNUSED_FUN, "Unused functions"),
@@ -505,8 +505,9 @@ gui_loop(#gui_state{backend_pid = BackendPid, doc_plt = DocPlt,
end,
ExplanationPid = spawn_link(Fun),
gui_loop(State#gui_state{expl_pid = ExplanationPid});
- {BackendPid, done, _NewPlt, NewDocPlt} ->
+ {BackendPid, done, NewMiniPlt, NewDocPlt} ->
message(State, "Analysis done"),
+ dialyzer_plt:delete(NewMiniPlt),
config_gui_stop(State),
gui_loop(State#gui_state{doc_plt = NewDocPlt});
{'EXIT', BackendPid, {error, Reason}} ->
diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl
index add660eae9..e71a0fca9d 100644
--- a/lib/dialyzer/src/dialyzer_options.erl
+++ b/lib/dialyzer/src/dialyzer_options.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.
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl
index cf2f0e919e..d054d5a5a0 100644
--- a/lib/dialyzer/src/dialyzer_plt.erl
+++ b/lib/dialyzer/src/dialyzer_plt.erl
@@ -2,7 +2,7 @@
%%----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -58,7 +58,9 @@
get_specs/4,
to_file/4,
get_mini_plt/1,
- restore_full_plt/2
+ restore_full_plt/1,
+ delete/1,
+ give_away/2
]).
%% Debug utilities
@@ -82,14 +84,16 @@
%%----------------------------------------------------------------------
-record(plt, {info = table_new() :: dict:dict(),
- types = table_new() :: dict:dict(),
+ types = table_new() :: erl_types:mod_records(),
contracts = table_new() :: dict:dict(),
callbacks = table_new() :: dict:dict(),
exported_types = sets:new() :: sets:set()}).
-record(mini_plt, {info :: ets:tid(),
+ types :: ets:tid(),
contracts :: ets:tid(),
- callbacks :: ets:tid()
+ callbacks :: ets:tid(),
+ exported_types :: ets:tid()
}).
-opaque plt() :: #plt{} | #mini_plt{}.
@@ -130,6 +134,10 @@ delete_module(#plt{info = Info, types = Types,
-spec delete_list(plt(), [mfa() | integer()]) -> plt().
+delete_list(#mini_plt{info = Info,
+ contracts = Contracts}=Plt, List) ->
+ Plt#mini_plt{info = ets_table_delete_list(Info, List),
+ contracts = ets_table_delete_list(Contracts, List)};
delete_list(#plt{info = Info, types = Types,
contracts = Contracts,
callbacks = Callbacks,
@@ -183,7 +191,7 @@ lookup(Plt, Label) when is_integer(Label) ->
lookup_1(#mini_plt{info = Info}, MFAorLabel) ->
ets_table_lookup(Info, MFAorLabel).
--spec insert_types(plt(), dict:dict()) -> plt().
+-spec insert_types(plt(), erl_types:mod_records()) -> plt().
insert_types(PLT, Rec) ->
PLT#plt{types = Rec}.
@@ -193,7 +201,7 @@ insert_types(PLT, Rec) ->
insert_exported_types(PLT, Set) ->
PLT#plt{exported_types = Set}.
--spec get_types(plt()) -> dict:dict().
+-spec get_types(plt()) -> erl_types:mod_records().
get_types(#plt{types = Types}) ->
Types.
@@ -226,12 +234,8 @@ contains_mfa(#plt{info = Info, contracts = Contracts}, MFA) ->
get_default_plt() ->
case os:getenv("DIALYZER_PLT") of
false ->
- case os:getenv("HOME") of
- false ->
- plt_error("The HOME environment variable needs to be set " ++
- "so that Dialyzer knows where to find the default PLT");
- HomeDir -> filename:join(HomeDir, ".dialyzer_plt")
- end;
+ {ok,[[HomeDir]]} = init:get_argument(home),
+ filename:join(HomeDir, ".dialyzer_plt");
UserSpecPlt -> UserSpecPlt
end.
@@ -253,8 +257,10 @@ from_file(FileName, ReturnInfo) ->
Msg = io_lib:format("Old PLT file ~s\n", [FileName]),
plt_error(Msg);
ok ->
+ Types = [{Mod, maps:from_list(dict:to_list(Types))} ||
+ {Mod, Types} <- dict:to_list(Rec#file_plt.types)],
Plt = #plt{info = Rec#file_plt.info,
- types = Rec#file_plt.types,
+ types = dict:from_list(Types),
contracts = Rec#file_plt.contracts,
callbacks = Rec#file_plt.callbacks,
exported_types = Rec#file_plt.exported_types},
@@ -371,12 +377,14 @@ to_file(FileName,
end,
OldModDeps, ModDeps),
ImplMd5 = compute_implementation_md5(),
+ FileTypes = dict:from_list([{Mod, dict:from_list(maps:to_list(MTypes))} ||
+ {Mod, MTypes} <- dict:to_list(Types)]),
Record = #file_plt{version = ?VSN,
file_md5_list = MD5,
info = Info,
contracts = Contracts,
callbacks = Callbacks,
- types = Types,
+ types = FileTypes,
exported_types = ExpTypes,
mod_deps = NewModDeps,
implementation_md5 = ImplMd5},
@@ -510,32 +518,100 @@ init_md5_list_1(Md5List, [], Acc) ->
-spec get_mini_plt(plt()) -> plt().
-get_mini_plt(#plt{info = Info, contracts = Contracts, callbacks = Callbacks}) ->
- [ETSInfo, ETSContracts, ETSCallbacks] =
- [ets:new(Name, [public]) || Name <- [plt_info, plt_contracts, plt_callbacks]],
+get_mini_plt(#plt{info = Info,
+ types = Types,
+ contracts = Contracts,
+ callbacks = Callbacks,
+ exported_types = ExpTypes}) ->
+ [ETSInfo, ETSTypes, ETSContracts, ETSCallbacks, ETSExpTypes] =
+ [ets:new(Name, [public]) ||
+ Name <- [plt_info, plt_types, plt_contracts, plt_callbacks,
+ plt_exported_types]],
CallbackList = dict:to_list(Callbacks),
CallbacksByModule =
[{M, [Cb || {{M1,_,_},_} = Cb <- CallbackList, M1 =:= M]} ||
M <- lists:usort([M || {{M,_,_},_} <- CallbackList])],
- [true, true] =
+ [true, true, true] =
[ets:insert(ETS, dict:to_list(Data)) ||
- {ETS, Data} <- [{ETSInfo, Info}, {ETSContracts, Contracts}]],
+ {ETS, Data} <- [{ETSInfo, Info},
+ {ETSTypes, Types},
+ {ETSContracts, Contracts}]],
true = ets:insert(ETSCallbacks, CallbacksByModule),
- #mini_plt{info = ETSInfo, contracts = ETSContracts, callbacks = ETSCallbacks};
+ true = ets:insert(ETSExpTypes, [{ET} || ET <- sets:to_list(ExpTypes)]),
+ #mini_plt{info = ETSInfo,
+ types = ETSTypes,
+ contracts = ETSContracts,
+ callbacks = ETSCallbacks,
+ exported_types = ETSExpTypes};
get_mini_plt(undefined) ->
undefined.
--spec restore_full_plt(plt(), plt()) -> plt().
-
-restore_full_plt(#mini_plt{info = ETSInfo, contracts = ETSContracts}, Plt) ->
- Info = dict:from_list(ets:tab2list(ETSInfo)),
- Contracts = dict:from_list(ets:tab2list(ETSContracts)),
- ets:delete(ETSContracts),
- ets:delete(ETSInfo),
- Plt#plt{info = Info, contracts = Contracts};
-restore_full_plt(undefined, undefined) ->
+-spec restore_full_plt(plt()) -> plt().
+
+restore_full_plt(#mini_plt{info = ETSInfo,
+ types = ETSTypes,
+ contracts = ETSContracts,
+ callbacks = ETSCallbacks,
+ exported_types = ETSExpTypes} = MiniPlt) ->
+ Info = dict:from_list(tab2list(ETSInfo)),
+ Contracts = dict:from_list(tab2list(ETSContracts)),
+ Types = dict:from_list(tab2list(ETSTypes)),
+ Callbacks =
+ dict:from_list([Cb || {_M, Cbs} <- tab2list(ETSCallbacks), Cb <- Cbs]),
+ ExpTypes = sets:from_list([E || {E} <- tab2list(ETSExpTypes)]),
+ ok = delete(MiniPlt),
+ #plt{info = Info,
+ types = Types,
+ contracts = Contracts,
+ callbacks = Callbacks,
+ exported_types = ExpTypes};
+restore_full_plt(undefined) ->
undefined.
+-spec delete(plt()) -> 'ok'.
+
+delete(#mini_plt{info = ETSInfo,
+ types = ETSTypes,
+ contracts = ETSContracts,
+ callbacks = ETSCallbacks,
+ exported_types = ETSExpTypes}) ->
+ true = ets:delete(ETSContracts),
+ true = ets:delete(ETSTypes),
+ true = ets:delete(ETSInfo),
+ true = ets:delete(ETSCallbacks),
+ true = ets:delete(ETSExpTypes),
+ ok.
+
+-spec give_away(plt(), pid()) -> 'ok'.
+
+give_away(#mini_plt{info = ETSInfo,
+ types = ETSTypes,
+ contracts = ETSContracts,
+ callbacks = ETSCallbacks,
+ exported_types = ETSExpTypes},
+ Pid) ->
+ true = ets:give_away(ETSContracts, Pid, any),
+ true = ets:give_away(ETSTypes, Pid, any),
+ true = ets:give_away(ETSInfo, Pid, any),
+ true = ets:give_away(ETSCallbacks, Pid, any),
+ true = ets:give_away(ETSExpTypes, Pid, any),
+ ok.
+
+%% Somewhat slower than ets:tab2list(), but uses less memory.
+tab2list(T) ->
+ tab2list(ets:first(T), T, []).
+
+tab2list('$end_of_table', T, A) ->
+ case ets:first(T) of % no safe_fixtable()...
+ '$end_of_table' -> A;
+ Key -> tab2list(Key, T, A)
+ end;
+tab2list(Key, T, A) ->
+ Vs = ets:lookup(T, Key),
+ Key1 = ets:next(T, Key),
+ ets:delete(T, Key),
+ tab2list(Key1, T, Vs ++ A).
+
%%---------------------------------------------------------------------------
%% Edoc
@@ -607,6 +683,12 @@ table_delete_module1(Plt, Mod) ->
table_delete_module2(Plt, Mod) ->
dict:filter(fun(M, _Val) -> M =/= Mod end, Plt).
+ets_table_delete_list(Tab, [H|T]) ->
+ ets:delete(Tab, H),
+ ets_table_delete_list(Tab, T);
+ets_table_delete_list(Tab, []) ->
+ Tab.
+
table_delete_list(Plt, [H|T]) ->
table_delete_list(dict:erase(H, Plt), T);
table_delete_list(Plt, []) ->
diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl
index 987da3aecf..705a870786 100644
--- a/lib/dialyzer/src/dialyzer_succ_typings.erl
+++ b/lib/dialyzer/src/dialyzer_succ_typings.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -96,7 +96,7 @@ analyze_callgraph(Callgraph, Plt, Codeserver, TimingServer, Solvers, Parent) ->
NewState =
init_state_and_get_success_typings(Callgraph, Plt, Codeserver,
TimingServer, Solvers, Parent),
- dialyzer_plt:restore_full_plt(NewState#st.plt, Plt).
+ NewState#st.plt.
%%--------------------------------------------------------------------
@@ -111,6 +111,7 @@ init_state_and_get_success_typings(Callgraph, Plt, Codeserver,
get_refined_success_typings(SCCs, #st{callgraph = Callgraph,
timing_server = TimingServer} = State) ->
+ erlang:garbage_collect(),
case find_succ_typings(SCCs, State) of
{fixpoint, State1} -> State1;
{not_fixpoint, NotFixpoint1, State1} ->
@@ -155,8 +156,8 @@ get_warnings(Callgraph, Plt, DocPlt, Codeserver,
?timing(TimingServer, "warning",
get_warnings_from_modules(Mods, InitState, MiniDocPlt)),
{postprocess_warnings(CWarns ++ ModWarns, Codeserver),
- dialyzer_plt:restore_full_plt(MiniPlt, Plt),
- dialyzer_plt:restore_full_plt(MiniDocPlt, DocPlt)}.
+ MiniPlt,
+ dialyzer_plt:restore_full_plt(MiniDocPlt)}.
get_warnings_from_modules(Mods, State, DocPlt) ->
#st{callgraph = Callgraph, codeserver = Codeserver,
@@ -174,10 +175,10 @@ collect_warnings(M, {Codeserver, Callgraph, Plt, DocPlt}) ->
%% Check if there are contracts for functions that do not exist
Warnings1 =
dialyzer_contracts:contracts_without_fun(Contracts, AllFuns, Callgraph),
+ Attrs = cerl:module_attrs(ModCode),
{Warnings2, FunTypes} =
dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, Codeserver,
Records),
- Attrs = cerl:module_attrs(ModCode),
Warnings3 =
dialyzer_behaviours:check_callbacks(M, Attrs, Records, Plt, Codeserver),
DocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt),
@@ -262,7 +263,7 @@ refine_one_module(M, {CodeServer, Callgraph, Plt, _Solvers}) ->
NewFunTypes =
dialyzer_dataflow:get_fun_types(ModCode, Plt, Callgraph, CodeServer, Records),
Contracts1 = dialyzer_codeserver:lookup_mod_contracts(M, CodeServer),
- Contracts = orddict:from_list(dict:to_list(Contracts1)),
+ Contracts = orddict:from_list(maps:to_list(Contracts1)),
FindOpaques = find_opaques_fun(Records),
DecoratedFunTypes =
decorate_succ_typings(Contracts, Callgraph, NewFunTypes, FindOpaques),
@@ -348,21 +349,25 @@ find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph,
-spec find_succ_types_for_scc(scc(), typesig_init_data()) -> [mfa_or_funlbl()].
-find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt, Solvers}) ->
- SCC_Info = [{MFA,
- dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver),
- dialyzer_codeserver:lookup_mod_records(M, Codeserver)}
- || {M, _, _} = MFA <- SCC],
+find_succ_types_for_scc(SCC0, {Codeserver, Callgraph, Plt, Solvers}) ->
+ SCC = [MFA || {_, _, _} = MFA <- SCC0],
Contracts1 = [{MFA, dialyzer_codeserver:lookup_mfa_contract(MFA, Codeserver)}
- || {_, _, _} = MFA <- SCC],
+ || MFA <- SCC],
Contracts2 = [{MFA, Contract} || {MFA, {ok, Contract}} <- Contracts1],
Contracts3 = orddict:from_list(Contracts2),
Label = dialyzer_codeserver:get_next_core_label(Codeserver),
- AllFuns = collect_fun_info([Fun || {_MFA, {_Var, Fun}, _Rec} <- SCC_Info]),
+ AllFuns = lists:append(
+ [begin
+ {_Var, Fun} =
+ dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver),
+ collect_fun_info([Fun])
+ end || MFA <- SCC]),
+ erlang:garbage_collect(),
PropTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt),
%% Assume that the PLT contains the current propagated types
- FunTypes = dialyzer_typesig:analyze_scc(SCC_Info, Label, Callgraph,
- Plt, PropTypes, Solvers),
+ FunTypes = dialyzer_typesig:analyze_scc(SCC, Label, Callgraph,
+ Codeserver, Plt, PropTypes,
+ Solvers),
AllFunSet = sets:from_list([X || {X, _} <- AllFuns]),
FilteredFunTypes =
dict:filter(fun(X, _) -> sets:is_element(X, AllFunSet) end, FunTypes),
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index 1787b66192..4d650592e2 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -29,7 +29,7 @@
-module(dialyzer_typesig).
--export([analyze_scc/6]).
+-export([analyze_scc/7]).
-export([get_safe_underapprox/2]).
%%-import(helper, %% 'helper' could be any module doing sanity checks...
@@ -101,10 +101,9 @@
-type types() :: erl_types:type_table().
--type typesig_scc() :: [{mfa(), {cerl:c_var(), cerl:c_fun()}, types()}].
-type typesig_funmap() :: #{type_var() => type_var()}.
--type prop_types() :: dict:dict(label(), types()).
+-type prop_types() :: dict:dict(label(), erl_types:erl_type()).
-record(state, {callgraph :: dialyzer_callgraph:callgraph()
| 'undefined',
@@ -121,7 +120,7 @@
plt :: dialyzer_plt:plt()
| 'undefined',
prop_types = dict:new() :: prop_types(),
- records = dict:new() :: types(),
+ records = maps:new() :: types(),
scc = [] :: ordsets:ordset(type_var()),
mfas :: [mfa()],
solvers = [] :: [solver()]
@@ -160,11 +159,10 @@
%%-----------------------------------------------------------------------------
%% Analysis of strongly connected components.
%%
-%% analyze_scc(SCC, NextLabel, CallGraph, PLT, PropTypes, Solvers) -> FunTypes
+%% analyze_scc(SCC, NextLabel, CallGraph, CodeServer,
+%% PLT, PropTypes, Solvers) -> FunTypes
%%
-%% SCC - [{MFA, Def, Records}]
-%% where Def = {Var, Fun} as in the Core Erlang module definitions.
-%% Records = dict(RecName, {Arity, [{FieldName, FieldType}]})
+%% SCC - [{MFA}]
%% NextLabel - An integer that is higher than any label in the code.
%% CallGraph - A callgraph as produced by dialyzer_callgraph.erl
%% Note: The callgraph must have been built with all the
@@ -176,28 +174,27 @@
%% Solvers - User specified solvers.
%%-----------------------------------------------------------------------------
--spec analyze_scc(typesig_scc(), label(),
+-spec analyze_scc([mfa()], label(),
dialyzer_callgraph:callgraph(),
+ dialyzer_codeserver:codeserver(),
dialyzer_plt:plt(), prop_types(), [solver()]) -> prop_types().
-analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers0) ->
+analyze_scc(SCC, NextLabel, CallGraph, CServer, Plt, PropTypes, Solvers0) ->
Solvers = solvers(Solvers0),
- assert_format_of_scc(SCC),
- State1 = new_state(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers),
- DefSet = add_def_list([Var || {_MFA, {Var, _Fun}, _Rec} <- SCC], sets:new()),
- State2 = traverse_scc(SCC, DefSet, State1),
+ State1 = new_state(SCC, NextLabel, CallGraph, CServer, Plt, PropTypes,
+ Solvers),
+ DefSet = add_def_list(maps:values(State1#state.name_map), sets:new()),
+ ModRecs = [{M, dialyzer_codeserver:lookup_mod_records(M, CServer)} ||
+ M <- lists:usort([M || {M, _, _} <- SCC])],
+ State2 = traverse_scc(SCC, CServer, DefSet, ModRecs, State1),
State3 = state__finalize(State2),
+ erlang:garbage_collect(),
Funs = state__scc(State3),
pp_constrs_scc(Funs, State3),
constraints_to_dot_scc(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);
-assert_format_of_scc([]) ->
- ok.
-
solvers([]) -> [v2];
solvers(Solvers) -> Solvers.
@@ -207,12 +204,15 @@ solvers(Solvers) -> Solvers.
%%
%% ============================================================================
-traverse_scc([{_MFA, Def, Rec}|Left], DefSet, AccState) ->
+traverse_scc([{M,_,_}=MFA|Left], Codeserver, DefSet, ModRecs, AccState) ->
+ Def = dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver),
+ {M, Rec} = lists:keyfind(M, 1, ModRecs),
TmpState1 = state__set_rec_dict(AccState, Rec),
DummyLetrec = cerl:c_letrec([Def], cerl:c_atom(foo)),
- {NewAccState, _} = traverse(DummyLetrec, DefSet, TmpState1),
- traverse_scc(Left, DefSet, NewAccState);
-traverse_scc([], _DefSet, AccState) ->
+ TmpState2 = state__new_constraint_context(TmpState1),
+ {NewAccState, _} = traverse(DummyLetrec, DefSet, TmpState2),
+ traverse_scc(Left, Codeserver, DefSet, ModRecs, NewAccState);
+traverse_scc([], _Codeserver, _DefSet, _ModRecs, AccState) ->
AccState.
traverse(Tree, DefinedVars, State) ->
@@ -2106,6 +2106,12 @@ v2_solve_disj([I|Is], [C|Cs], I, Map0, V2State0, UL, MapL, Eval, Uneval,
end;
v2_solve_disj([], [], _I, _Map, V2State, UL, MapL, Eval, Uneval, Failed) ->
{ok, V2State, lists:reverse(Eval), UL, MapL, lists:reverse(Uneval), Failed};
+v2_solve_disj([every_i], Cs, I, Map, V2State, UL, MapL, Eval, Uneval, Failed) ->
+ NewIs = case Cs of
+ [] -> [];
+ _ -> [I, every_i]
+ end,
+ v2_solve_disj(NewIs, Cs, I, Map, V2State, UL, MapL, Eval, Uneval, Failed);
v2_solve_disj(Is, [C|Cs], I, Map, V2State, UL, MapL, Eval, Uneval0, Failed) ->
Uneval = [{I,C#constraint_list.id} ||
not is_failed_list(C, V2State)] ++ Uneval0,
@@ -2177,7 +2183,7 @@ v2_solve_conj([I|Is], [Cs|Tail], I, Map0, Conj, IsFlat, V2State0,
M = lists:keydelete(I, 1, vars_per_child(U, Masks)),
{V2State2, NewF0} = save_updated_vars_list(AllCs, M, V2State1),
{NewF, F} = lists:splitwith(fun(J) -> J < I end, NewF0),
- Is1 = lists:umerge(Is, F),
+ Is1 = umerge_mask(Is, F),
NewFs = [NewF|NewFs0],
v2_solve_conj(Is1, Tail, I+1, Map, Conj, IsFlat, V2State2,
[U|UL], NewFs, VarsUp, LastMap, LastFlags)
@@ -2199,6 +2205,14 @@ v2_solve_conj([], _Cs, _I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
v2_solve_conj(NewFlags, Cs, 1, Map, Conj, IsFlat, V2State,
[], [], [U|VarsUp], Map, NewFlags)
end;
+v2_solve_conj([every_i], Cs, I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
+ LastMap, LastFlags) ->
+ NewIs = case Cs of
+ [] -> [];
+ _ -> [I, every_i]
+ end,
+ v2_solve_conj(NewIs, Cs, I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
+ LastMap, LastFlags);
v2_solve_conj(Is, [_|Tail], I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
LastMap, LastFlags) ->
v2_solve_conj(Is, Tail, I+1, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
@@ -2215,7 +2229,12 @@ 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(M, Flags), lists:reverse(L)}.
+ {umerge_mask(Flags, M), lists:reverse(L)}.
+
+umerge_mask([every_i]=Is, _F) ->
+ Is;
+umerge_mask(Is, F) ->
+ lists:umerge(Is, F).
get_mask(V, Masks) ->
case maps:find(V, Masks) of
@@ -2229,7 +2248,7 @@ get_flags(#v2_state{constr_data = ConData}=V2State0, C) ->
error ->
?debug("get_flags Id=~w Flags=all ~w\n", [Id, length(Cs)]),
V2State = V2State0#v2_state{constr_data = maps:put(Id, {[],[]}, ConData)},
- {V2State, lists:seq(1, length(Cs))};
+ {V2State, [every_i]};
{ok, failed} ->
{V2State0, failed_list};
{ok, {Part,U}} when U =/= [] ->
@@ -2702,11 +2721,14 @@ pp_map(_S, _Map) ->
%%
%% ============================================================================
-new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes, Solvers) ->
- List = [{MFA, Var} || {MFA, {Var, _Fun}, _Rec} <- SCC0],
+new_state(MFAs, NextLabel, CallGraph, CServer, Plt, PropTypes, Solvers) ->
+ List_SCC =
+ [begin
+ {Var, Label} = dialyzer_codeserver:lookup_mfa_var_label(MFA, CServer),
+ {{MFA, Var}, t_var(Label)}
+ end || MFA <- MFAs],
+ {List, SCC} = lists:unzip(List_SCC),
NameMap = maps:from_list(List),
- MFAs = [MFA || {MFA, _Var} <- List],
- SCC = [mk_var(Fun) || {_MFA, {_Var, Fun}, _Rec} <- SCC0],
SelfRec =
case SCC of
[OneF] ->
@@ -2906,8 +2928,9 @@ state__get_rec_var(Fun, #state{fun_map = Map}) ->
maps:find(Fun, Map).
state__finalize(State) ->
- State1 = enumerate_constraints(State),
- order_fun_constraints(State1).
+ State1 = state__new_constraint_context(State),
+ State2 = enumerate_constraints(State1),
+ order_fun_constraints(State2).
%% ============================================================================
%%
@@ -2987,7 +3010,7 @@ find_constraint_deps([Type|Tail], Acc) ->
NewAcc = [[t_var_name(D) || D <- t_collect_vars(Type)]|Acc],
find_constraint_deps(Tail, NewAcc);
find_constraint_deps([], Acc) ->
- lists:flatten(Acc).
+ lists:append(Acc).
mk_constraint_1(Lhs, eq, Rhs, Deps) when Lhs < Rhs ->
#constraint{lhs = Lhs, op = eq, rhs = Rhs, deps = Deps};
@@ -3095,8 +3118,8 @@ expand_to_conjunctions(#constraint_list{type = disj, list = List}) ->
List1 = [C || C <- List, is_simple_constraint(C)],
%% Just an assert.
[] = [C || #constraint{} = C <- List1],
- Expanded = lists:flatten([expand_to_conjunctions(C)
- || #constraint_list{} = C <- List]),
+ Expanded = lists:append([expand_to_conjunctions(C)
+ || #constraint_list{} = C <- List]),
ReturnList = Expanded ++ List1,
if length(ReturnList) > ?DISJ_NORM_FORM_LIMIT -> throw(too_many_disj);
true -> ReturnList
@@ -3121,8 +3144,10 @@ calculate_deps(List) ->
calculate_deps([H|Tail], Acc) ->
Deps = get_deps(H),
calculate_deps(Tail, [Deps|Acc]);
+calculate_deps([], []) -> [];
+calculate_deps([], [L]) -> L;
calculate_deps([], Acc) ->
- ordsets:from_list(lists:flatten(Acc)).
+ lists:umerge(Acc).
mk_conj_constraint_list(List) ->
mk_constraint_list(conj, List).
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 76a5cf3d0b..7a395666cd 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -202,7 +202,7 @@ get_core_from_abstract_code(AbstrCode, Opts) ->
get_record_and_type_info(AbstractCode) ->
Module = get_module(AbstractCode),
- get_record_and_type_info(AbstractCode, Module, dict:new()).
+ get_record_and_type_info(AbstractCode, Module, maps:new()).
-spec get_record_and_type_info(abstract_code(), module(), type_table()) ->
{'ok', type_table()} | {'error', string()}.
@@ -215,7 +215,7 @@ get_record_and_type_info([{attribute, A, record, {Name, Fields0}}|Left],
{ok, Fields} = get_record_fields(Fields0, RecDict),
Arity = length(Fields),
FN = {File, erl_anno:line(A)},
- NewRecDict = dict:store({record, Name}, {FN, [{Arity,Fields}]}, RecDict),
+ NewRecDict = maps:put({record, Name}, {FN, [{Arity,Fields}]}, RecDict),
get_record_and_type_info(Left, Module, NewRecDict, File);
get_record_and_type_info([{attribute, A, type, {{record, Name}, Fields0, []}}
|Left], Module, RecDict, File) ->
@@ -223,7 +223,7 @@ get_record_and_type_info([{attribute, A, type, {{record, Name}, Fields0, []}}
{ok, Fields} = get_record_fields(Fields0, RecDict),
Arity = length(Fields),
FN = {File, erl_anno:line(A)},
- NewRecDict = dict:store({record, Name}, {FN, [{Arity, Fields}]}, RecDict),
+ NewRecDict = maps:put({record, Name}, {FN, [{Arity, Fields}]}, RecDict),
get_record_and_type_info(Left, Module, NewRecDict, File);
get_record_and_type_info([{attribute, A, Attr, {Name, TypeForm}}|Left],
Module, RecDict, File)
@@ -263,9 +263,9 @@ add_new_type(TypeOrOpaque, Name, TypeForm, ArgForms, Module, FN,
false ->
try erl_types:t_var_names(ArgForms) of
ArgNames ->
- dict:store({TypeOrOpaque, Name, Arity},
- {{Module, FN, TypeForm, ArgNames},
- erl_types:t_any()}, RecDict)
+ maps:put({TypeOrOpaque, Name, Arity},
+ {{Module, FN, TypeForm, ArgNames},
+ erl_types:t_any()}, RecDict)
catch
_:_ ->
throw({error, flat_format("Type declaration for ~w does not "
@@ -296,19 +296,18 @@ get_record_fields([{record_field, _Line, Name, _Init}|Left], RecDict, Acc) ->
get_record_fields([], _RecDict, Acc) ->
lists:reverse(Acc).
--spec process_record_remote_types(codeserver()) -> codeserver().
+-spec process_record_remote_types(codeserver()) ->
+ {codeserver(), mod_records()}.
%% The field types are cached. Used during analysis when handling records.
process_record_remote_types(CServer) ->
TempRecords = dialyzer_codeserver:get_temp_records(CServer),
ExpTypes = dialyzer_codeserver:get_exported_types(CServer),
- Cache = erl_types:cache__new(),
- {TempRecords1, Cache1} =
- process_opaque_types0(TempRecords, ExpTypes, Cache),
+ TempRecords1 = process_opaque_types0(TempRecords, ExpTypes),
%% A cache (not the field type cache) is used for speeding things up a bit.
VarTable = erl_types:var_table__new(),
ModuleFun =
- fun({Module, Record}, C0) ->
+ fun({Module, Record}) ->
RecordFun =
fun({Key, Value}, C2) ->
case Key of
@@ -334,24 +333,27 @@ process_record_remote_types(CServer) ->
_Other -> {{Key, Value}, C2}
end
end,
- {RecordList, C1} =
- lists:mapfoldl(RecordFun, C0, dict:to_list(Record)),
- {{Module, dict:from_list(RecordList)}, C1}
+ Cache = erl_types:cache__new(),
+ {RecordList, _NewCache} =
+ lists:mapfoldl(RecordFun, Cache, maps:to_list(Record)),
+ {Module, maps:from_list(RecordList)}
end,
- {NewRecordsList, C1} =
- lists:mapfoldl(ModuleFun, Cache1, dict:to_list(TempRecords1)),
+ NewRecordsList = lists:map(ModuleFun, dict:to_list(TempRecords1)),
NewRecords = dict:from_list(NewRecordsList),
- _C8 = check_record_fields(NewRecords, ExpTypes, C1),
- dialyzer_codeserver:finalize_records(NewRecords, CServer).
+ check_record_fields(NewRecords, ExpTypes),
+ {dialyzer_codeserver:finalize_records(NewRecords, CServer), NewRecords}.
%% erl_types:t_from_form() substitutes the declaration of opaque types
%% for the expanded type in some cases. To make sure the initial type,
%% any(), is not used, the expansion is done twice.
%% XXX: Recursive opaque types are not handled well.
-process_opaque_types0(TempRecords0, TempExpTypes, Cache) ->
- {TempRecords1, NewCache} =
+process_opaque_types0(TempRecords0, TempExpTypes) ->
+ Cache = erl_types:cache__new(),
+ {TempRecords1, Cache1} =
process_opaque_types(TempRecords0, TempExpTypes, Cache),
- process_opaque_types(TempRecords1, TempExpTypes, NewCache).
+ {TempRecords, _NewCache} =
+ process_opaque_types(TempRecords1, TempExpTypes, Cache1),
+ TempRecords.
process_opaque_types(TempRecords, TempExpTypes, Cache) ->
VarTable = erl_types:var_table__new(),
@@ -371,8 +373,8 @@ process_opaque_types(TempRecords, TempExpTypes, Cache) ->
end
end,
{RecordList, C1} =
- lists:mapfoldl(RecordFun, C0, dict:to_list(Record)),
- {{Module, dict:from_list(RecordList)}, C1}
+ lists:mapfoldl(RecordFun, C0, maps:to_list(Record)),
+ {{Module, maps:from_list(RecordList)}, C1}
%% dict:map(RecordFun, Record)
end,
{TempRecordList, NewCache} =
@@ -380,7 +382,8 @@ process_opaque_types(TempRecords, TempExpTypes, Cache) ->
{dict:from_list(TempRecordList), NewCache}.
%% dict:map(ModuleFun, TempRecords).
-check_record_fields(Records, TempExpTypes, Cache) ->
+check_record_fields(Records, TempExpTypes) ->
+ Cache = erl_types:cache__new(),
VarTable = erl_types:var_table__new(),
CheckFun =
fun({Module, Element}, C0) ->
@@ -410,9 +413,10 @@ check_record_fields(Records, TempExpTypes, Cache) ->
msg_with_position(Fun, FileLine)
end
end,
- lists:foldl(ElemFun, C0, dict:to_list(Element))
+ lists:foldl(ElemFun, C0, maps:to_list(Element))
end,
- lists:foldl(CheckFun, Cache, dict:to_list(Records)).
+ _NewCache = lists:foldl(CheckFun, Cache, dict:to_list(Records)),
+ ok.
msg_with_position(Fun, FileLine) ->
try Fun()
@@ -435,17 +439,17 @@ merge_records(NewRecords, OldRecords) ->
%%
%% ============================================================================
--type spec_dict() :: dict:dict().
--type callback_dict() :: dict:dict().
+-type spec_map() :: dialyzer_codeserver:contracts().
+-type callback_map() :: dialyzer_codeserver:contracts().
-spec get_spec_info(module(), abstract_code(), type_table()) ->
- {'ok', spec_dict(), callback_dict()} | {'error', string()}.
+ {'ok', spec_map(), callback_map()} | {'error', string()}.
-get_spec_info(ModName, AbstractCode, RecordsDict) ->
+get_spec_info(ModName, AbstractCode, RecordsMap) ->
OptionalCallbacks0 = get_optional_callbacks(AbstractCode, ModName),
OptionalCallbacks = gb_sets:from_list(OptionalCallbacks0),
- get_spec_info(AbstractCode, dict:new(), dict:new(),
- RecordsDict, ModName, OptionalCallbacks, "nofile").
+ get_spec_info(AbstractCode, maps:new(), maps:new(),
+ RecordsMap, ModName, OptionalCallbacks, "nofile").
get_optional_callbacks(Abs, ModName) ->
[{ModName, F, A} || {F, A} <- get_optional_callbacks(Abs)].
@@ -463,7 +467,7 @@ get_optional_callbacks(Abs) ->
%% are erl_types:erl_type()
get_spec_info([{attribute, Anno, Contract, {Id, TypeSpec}}|Left],
- SpecDict, CallbackDict, RecordsDict, ModName, OptCb, File)
+ SpecMap, CallbackMap, RecordsMap, ModName, OptCb, File)
when ((Contract =:= 'spec') or (Contract =:= 'callback')),
is_list(TypeSpec) ->
Ln = erl_anno:line(Anno),
@@ -472,24 +476,24 @@ get_spec_info([{attribute, Anno, Contract, {Id, TypeSpec}}|Left],
{F, A} -> {ModName, F, A}
end,
Xtra = [optional_callback || gb_sets:is_member(MFA, OptCb)],
- ActiveDict =
+ ActiveMap =
case Contract of
- spec -> SpecDict;
- callback -> CallbackDict
+ spec -> SpecMap;
+ callback -> CallbackMap
end,
- try dict:find(MFA, ActiveDict) of
+ try maps:find(MFA, ActiveMap) of
error ->
SpecData = {TypeSpec, Xtra},
- NewActiveDict =
+ NewActiveMap =
dialyzer_contracts:store_tmp_contract(MFA, {File, Ln}, SpecData,
- ActiveDict, RecordsDict),
- {NewSpecDict, NewCallbackDict} =
+ ActiveMap, RecordsMap),
+ {NewSpecMap, NewCallbackMap} =
case Contract of
- spec -> {NewActiveDict, CallbackDict};
- callback -> {SpecDict, NewActiveDict}
+ spec -> {NewActiveMap, CallbackMap};
+ callback -> {SpecMap, NewActiveMap}
end,
- get_spec_info(Left, NewSpecDict, NewCallbackDict,
- RecordsDict, ModName, OptCb, File);
+ get_spec_info(Left, NewSpecMap, NewCallbackMap,
+ RecordsMap, ModName, OptCb, File);
{ok, {{OtherFile, L}, _D}} ->
{Mod, Fun, Arity} = MFA,
Msg = flat_format(" Contract/callback for function ~w:~w/~w "
@@ -502,28 +506,33 @@ get_spec_info([{attribute, Anno, Contract, {Id, TypeSpec}}|Left],
[Ln, Error])}
end;
get_spec_info([{attribute, _, file, {IncludeFile, _}}|Left],
- SpecDict, CallbackDict, RecordsDict, ModName, OptCb, _File) ->
- get_spec_info(Left, SpecDict, CallbackDict,
- RecordsDict, ModName, OptCb, IncludeFile);
-get_spec_info([_Other|Left], SpecDict, CallbackDict,
- RecordsDict, ModName, OptCb, File) ->
- get_spec_info(Left, SpecDict, CallbackDict,
- RecordsDict, ModName, OptCb, File);
-get_spec_info([], SpecDict, CallbackDict,
- _RecordsDict, _ModName, _OptCb, _File) ->
- {ok, SpecDict, CallbackDict}.
+ SpecMap, CallbackMap, RecordsMap, ModName, OptCb, _File) ->
+ get_spec_info(Left, SpecMap, CallbackMap,
+ RecordsMap, ModName, OptCb, IncludeFile);
+get_spec_info([_Other|Left], SpecMap, CallbackMap,
+ RecordsMap, ModName, OptCb, File) ->
+ get_spec_info(Left, SpecMap, CallbackMap,
+ RecordsMap, ModName, OptCb, File);
+get_spec_info([], SpecMap, CallbackMap,
+ _RecordsMap, _ModName, _OptCb, _File) ->
+ {ok, SpecMap, CallbackMap}.
-spec get_fun_meta_info(module(), abstract_code(), [dial_warn_tag()]) ->
- dialyzer_codeserver:fun_meta_info().
+ dialyzer_codeserver:fun_meta_info() | {'error', string()}.
get_fun_meta_info(M, Abs, LegalWarnings) ->
- NoWarn = get_nowarn_unused_function(M, Abs),
- FuncSupp = get_func_suppressions(M, Abs),
- Warnings0 = get_options(Abs, LegalWarnings),
- Warnings = ordsets:to_list(Warnings0),
- ModuleWarnings = [{M, W} || W <- Warnings],
- RawProps = lists:append([NoWarn, FuncSupp, ModuleWarnings]),
- process_options(dialyzer_utils:family(RawProps), Warnings0).
+ try
+ {get_nowarn_unused_function(M, Abs), get_func_suppressions(M, Abs)}
+ of
+ {NoWarn, FuncSupp} ->
+ Warnings0 = get_options(Abs, LegalWarnings),
+ Warnings = ordsets:to_list(Warnings0),
+ ModuleWarnings = [{M, W} || W <- Warnings],
+ RawProps = lists:append([NoWarn, FuncSupp, ModuleWarnings]),
+ process_options(dialyzer_utils:family(RawProps), Warnings0)
+ catch throw:{error, _} = Error ->
+ Error
+ end.
process_options([{M, _}=Mod|Left], Warnings) when is_atom(M) ->
[Mod|process_options(Left, Warnings)];
@@ -702,7 +711,7 @@ format_errors([]) ->
-spec format_sig(erl_types:erl_type()) -> string().
format_sig(Type) ->
- format_sig(Type, dict:new()).
+ format_sig(Type, maps:new()).
-spec format_sig(erl_types:erl_type(), type_table()) -> string().
@@ -954,9 +963,7 @@ label(Tree) ->
-spec parallelism() -> integer().
parallelism() ->
- CPUs = erlang:system_info(logical_processors_available),
- Schedulers = erlang:system_info(schedulers),
- min(CPUs, Schedulers).
+ erlang:system_info(schedulers_online).
-spec family([{K,V}]) -> [{K,[V]}].
diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile
index f43e04dd59..0d8fba438c 100644
--- a/lib/dialyzer/test/Makefile
+++ b/lib/dialyzer/test/Makefile
@@ -12,6 +12,7 @@ AUXILIARY_FILES=\
dialyzer_common.erl\
file_utils.erl\
dialyzer_SUITE.erl\
+ abstract_SUITE.erl\
plt_SUITE.erl
# ----------------------------------------------------
diff --git a/lib/dialyzer/test/abstract_SUITE.erl b/lib/dialyzer/test/abstract_SUITE.erl
new file mode 100644
index 0000000000..0e84dfab24
--- /dev/null
+++ b/lib/dialyzer/test/abstract_SUITE.erl
@@ -0,0 +1,106 @@
+%% This suite contains cases that cannot be written
+%% in Erlang itself and must be done via the abstract
+%% format.
+
+-module(abstract_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, all/0, init_per_suite/0, init_per_suite/1, end_per_suite/1]).
+-export([generated_case/1]).
+
+suite() ->
+ [{timetrap, {minutes, 1}}].
+all() ->
+ [generated_case].
+
+init_per_suite() ->
+ [{timetrap, ?plt_timeout}].
+init_per_suite(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, []}|Config]
+ end.
+
+end_per_suite(_Config) ->
+ %% This function is required since init_per_suite/1 exists.
+ ok.
+
+generated_case(Config) when is_list(Config) ->
+ %% Equivalent to:
+ %%
+ %% -module(foo).
+ %% -export(bar).
+ %% bar() ->
+ %% Arg = sample,
+ %% case Arg of
+ %% #{} -> map;
+ %% _ -> Arg:fn()
+ %% end.
+ %%
+ %% Except the case statement and its clauses are marked as autogenerated.
+ [] =
+ test([{attribute,1,module,foo},
+ {attribute,2,export,[{bar,0}]},
+ {function,3,bar,0,
+ [{clause,3,[],[],
+ [{match,4,{var,4,'Arg'},{atom,4,sample}},
+ {'case',[{location,5},{generated,true}],{var,5,'Arg'},
+ [{clause,[{location,6},{generated,true}],[{map,6,[]}],[],
+ [{atom,6,map}]},
+ {clause,[{location,7},{generated,true}],[{var,7,'_'}],[],
+ [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}],
+ Config, [], []),
+ %% With the first clause not auto-generated
+ [{warn_matching,{_,6},_}] =
+ test([{attribute,1,module,foo},
+ {attribute,2,export,[{bar,0}]},
+ {function,3,bar,0,
+ [{clause,3,[],[],
+ [{match,4,{var,4,'Arg'},{atom,4,sample}},
+ {'case',[{location,5},{generated,true}],{var,5,'Arg'},
+ [{clause,6,[{map,6,[]}],[],
+ [{atom,6,map}]},
+ {clause,[{location,7},{generated,true}],[{var,7,'_'}],[],
+ [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}],
+ Config, [], []),
+ %% With Arg set to [] so neither clause matches
+ [{warn_return_no_exit,{_,3},_},
+ {warn_matching,{_,6},_},
+ {warn_failing_call,{_,7},_}] =
+ test([{attribute,1,module,foo},
+ {attribute,2,export,[{bar,0}]},
+ {function,3,bar,0,
+ [{clause,3,[],[],
+ [{match,4,{var,4,'Arg'},{nil,4}},
+ {'case',[{location,5},{generated,true}],{var,5,'Arg'},
+ [{clause,[{location,6},{generated,true}],[{map,6,[]}],[],
+ [{atom,6,map}]},
+ {clause,[{location,7},{generated,true}],[{var,7,'_'}],[],
+ [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}],
+ Config, [], []),
+ ok.
+
+test(Prog, Config, COpts, DOpts) ->
+ {ok, BeamFile} = compile(Config, Prog, COpts),
+ run_dialyzer(Config, succ_typings, [BeamFile], DOpts).
+
+compile(Config, Prog, CompileOpts) ->
+ OutDir = ?config(priv_dir, Config),
+ Opts = [{outdir, OutDir}, debug_info, return_errors | CompileOpts],
+ {ok, Module, Source} = compile:forms(Prog, Opts),
+ BeamFile = filename:join([OutDir, lists:concat([Module, ".beam"])]),
+ ok = file:write_file(BeamFile, Source),
+ {ok, BeamFile}.
+
+run_dialyzer(Config, Analysis, Files, Opts) ->
+ OutDir = ?config(priv_dir, Config),
+ PltFilename = dialyzer_common:plt_file(OutDir),
+ dialyzer:run([{analysis_type, Analysis},
+ {files, Files},
+ {init_plt, PltFilename},
+ {check_plt, false},
+ {from, byte_code} |
+ Opts]).
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options b/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options
index 50991c9bc5..365b4798c5 100644
--- a/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options
@@ -1 +1,2 @@
{dialyzer_options, []}.
+{time_limit, 5}.
diff --git a/lib/dialyzer/test/dialyzer_common.erl b/lib/dialyzer/test/dialyzer_common.erl
index d2b1026c06..48083a2731 100644
--- a/lib/dialyzer/test/dialyzer_common.erl
+++ b/lib/dialyzer/test/dialyzer_common.erl
@@ -7,7 +7,7 @@
-module(dialyzer_common).
--export([check_plt/1, check/4, create_all_suites/0, new_tests/2]).
+-export([check_plt/1, check/4, create_all_suites/0, new_tests/2, plt_file/1]).
-include_lib("kernel/include/file.hrl").
@@ -39,7 +39,7 @@
check_plt(OutDir) ->
io:format("Checking plt:"),
- PltFilename = filename:join(OutDir, ?plt_filename),
+ PltFilename = plt_file(OutDir),
case file:read_file_info(PltFilename) of
{ok, _} -> dialyzer_check_plt(PltFilename);
{error, _ } ->
@@ -63,6 +63,11 @@ check_plt(OutDir) ->
end
end.
+-spec plt_file(string()) -> string().
+
+plt_file(OutDir) ->
+ filename:join(OutDir, ?plt_filename).
+
dialyzer_check_plt(PltFilename) ->
try dialyzer:run([{analysis_type, plt_check},
{init_plt, PltFilename}]) of
@@ -119,7 +124,7 @@ build_plt(PltFilename) ->
'same' | {differ, [term()]}.
check(TestCase, Opts, Dir, OutDir) ->
- PltFilename = filename:join(OutDir, ?plt_filename),
+ PltFilename = plt_file(OutDir),
SrcDir = filename:join(Dir, ?input_files_directory),
ResDir = filename:join(Dir, ?result_files_directory),
Filename = filename:join(SrcDir, atom_to_list(TestCase)),
diff --git a/lib/dialyzer/test/map_SUITE_data/dialyzer_options b/lib/dialyzer/test/map_SUITE_data/dialyzer_options
index 50991c9bc5..02425c33f2 100644
--- a/lib/dialyzer/test/map_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/map_SUITE_data/dialyzer_options
@@ -1 +1,2 @@
{dialyzer_options, []}.
+{time_limit, 30}.
diff --git a/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2 b/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2
index f6fb98a863..46e2e8d36c 100644
--- a/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2
+++ b/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2
@@ -2,11 +2,11 @@
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:17: Guard test is_map(M::'not_a_map') can never succeed
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:22: Guard test is_map(M::'not_a_map') can never succeed
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
diff --git a/lib/dialyzer/test/map_SUITE_data/results/opaque_key b/lib/dialyzer/test/map_SUITE_data/results/opaque_key
index fb7080cdc5..2ae0e0c5c6 100644
--- a/lib/dialyzer/test/map_SUITE_data/results/opaque_key
+++ b/lib/dialyzer/test/map_SUITE_data/results/opaque_key
@@ -6,10 +6,10 @@ opaque_key_adt.erl:59: Invalid type specification for function opaque_key_adt:sm
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:40: The attempt to match a term of type opaque_key_adt:m() against the pattern #{A:=R} breaks the opacity 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:51: Guard test is_map(M::opaque_key_adt:m()) breaks the opacity 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:56: Guard test is_map(M::opaque_key_adt:m()) breaks the opacity 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
+opaque_key_use.erl:60: Guard test is_map(M::opaque_key_adt:m()) breaks the opacity of its argument
diff --git a/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl
new file mode 100644
index 0000000000..827984b20b
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl
@@ -0,0 +1,9 @@
+-module(a).
+-export([to_map/1, to_map/2]).
+-type t() :: #{type := b:t()}.
+
+-spec to_map(t()) -> map().
+to_map(Resource) -> to_map(Resource, #{}).
+
+-spec to_map(t(), map()) -> map().
+to_map(_, Map) when is_map(Map) -> #{}.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl
new file mode 100644
index 0000000000..31f9bb6b3e
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl
@@ -0,0 +1,3 @@
+-module(b).
+-export_type([t/0]).
+-type t() :: binary().
diff --git a/lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl b/lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl
new file mode 100644
index 0000000000..40214a1887
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/opaque_bif.erl
@@ -0,0 +1,13 @@
+-module(opaque_bif).
+-export([o1/1]).
+-export_type([opaque_any_map/0]).
+-opaque opaque_any_map() :: map().
+
+%% ERL-249: A bug with opaque arguments to maps:merge/2
+%% Reported by Felipe Ripoll on 6/9/2016
+-spec o1(opaque_any_map()) -> opaque_any_map().
+o1(Map) ->
+ maps:merge(o1_c(), Map).
+
+-spec o1_c() -> opaque_any_map().
+o1_c() -> #{}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
index 3ff26b87db..cb301ff6a1 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
@@ -1 +1,2 @@
{dialyzer_options, [{warnings, [no_unused, no_return]}]}.
+{time_limit, 40}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/array b/lib/dialyzer/test/opaque_SUITE_data/results/array
index 9921b61669..6f1aa1ce3d 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/array
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/array
@@ -1,3 +1,3 @@
-array_use.erl:12: The type test is_tuple(array:array(_)) breaks the opaqueness of the term array:array(_)
-array_use.erl:9: The attempt to match a term of type array:array(_) against the pattern {'array', _, _, 'undefined', _} breaks the opaqueness of the term
+array_use.erl:12: The type test is_tuple(array:array(_)) breaks the opacity of the term array:array(_)
+array_use.erl:9: The attempt to match a term of type array:array(_) against the pattern {'array', _, _, 'undefined', _} breaks the opacity of the term
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/dict b/lib/dialyzer/test/opaque_SUITE_data/results/dict
index 42f6663191..3f8242c72d 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/dict
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/dict
@@ -1,15 +1,15 @@
-dict_use.erl:41: The attempt to match a term of type dict:dict(_,_) against the pattern 'gazonk' breaks the opaqueness of the term
-dict_use.erl:45: The attempt to match a term of type dict:dict(_,_) against the pattern [] breaks the opaqueness of the term
-dict_use.erl:46: The attempt to match a term of type dict:dict(_,_) against the pattern 42 breaks the opaqueness of the term
-dict_use.erl:51: The attempt to match a term of type dict:dict(_,_) against the pattern [] breaks the opaqueness of the term
-dict_use.erl:52: The attempt to match a term of type dict:dict(_,_) against the pattern 42 breaks the opaqueness of the term
+dict_use.erl:41: The attempt to match a term of type dict:dict(_,_) against the pattern 'gazonk' breaks the opacity of the term
+dict_use.erl:45: The attempt to match a term of type dict:dict(_,_) against the pattern [] breaks the opacity of the term
+dict_use.erl:46: The attempt to match a term of type dict:dict(_,_) against the pattern 42 breaks the opacity of the term
+dict_use.erl:51: The attempt to match a term of type dict:dict(_,_) against the pattern [] breaks the opacity of the term
+dict_use.erl:52: The attempt to match a term of type dict:dict(_,_) against the pattern 42 breaks the opacity of the term
dict_use.erl:58: Attempt to test for equality between a term of type maybe_improper_list() and a term of opaque type dict:dict(_,_)
dict_use.erl:60: Attempt to test for inequality between a term of type atom() and a term of opaque type dict:dict(_,_)
-dict_use.erl:64: Guard test length(D::dict:dict(_,_)) breaks the opaqueness of its argument
-dict_use.erl:65: Guard test is_atom(D::dict:dict(_,_)) breaks the opaqueness of its argument
-dict_use.erl:66: Guard test is_list(D::dict:dict(_,_)) breaks the opaqueness of its argument
-dict_use.erl:70: The type test is_list(dict:dict(_,_)) breaks the opaqueness of the term dict:dict(_,_)
+dict_use.erl:64: Guard test length(D::dict:dict(_,_)) breaks the opacity of its argument
+dict_use.erl:65: Guard test is_atom(D::dict:dict(_,_)) breaks the opacity of its argument
+dict_use.erl:66: Guard test is_list(D::dict:dict(_,_)) breaks the opacity of its argument
+dict_use.erl:70: The type test is_list(dict:dict(_,_)) breaks the opacity of the term dict:dict(_,_)
dict_use.erl:73: The call dict:fetch('foo',[1 | 2 | 3,...]) does not have an opaque term of type dict:dict(_,_) as 2nd argument
dict_use.erl:76: The call dict:merge(Fun::any(),42,[1 | 2,...]) does not have opaque terms as 2nd and 3rd arguments
dict_use.erl:79: The call dict:store(42,'elli',{'dict',0,16,16,8,80,48,{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}) does not have an opaque term of type dict:dict(_,_) as 3rd argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/ets b/lib/dialyzer/test/opaque_SUITE_data/results/ets
index e11c7a8352..5dde23fb15 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/ets
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/ets
@@ -1,4 +1,4 @@
-ets_use.erl:12: Guard test is_integer(T::atom() | ets:tid()) breaks the opaqueness of its argument
-ets_use.erl:20: The type test is_integer(atom() | ets:tid()) breaks the opaqueness of the term atom() | ets:tid()
-ets_use.erl:7: Guard test is_integer(T::ets:tid()) breaks the opaqueness of its argument
+ets_use.erl:12: Guard test is_integer(T::atom() | ets:tid()) breaks the opacity of its argument
+ets_use.erl:20: The type test is_integer(atom() | ets:tid()) breaks the opacity of the term atom() | ets:tid()
+ets_use.erl:7: Guard test is_integer(T::ets:tid()) breaks the opacity of its argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/my_queue b/lib/dialyzer/test/opaque_SUITE_data/results/my_queue
index 1f25a6f9c3..67999b0e20 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/my_queue
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/my_queue
@@ -1,7 +1,7 @@
my_queue_use.erl:15: The call my_queue_adt:is_empty([]) does not have an opaque term of type my_queue_adt:my_queue() as 1st argument
my_queue_use.erl:19: The call my_queue_adt:add(42,Q0::[]) does not have an opaque term of type my_queue_adt:my_queue() as 2nd argument
-my_queue_use.erl:24: The attempt to match a term of type my_queue_adt:my_queue() against the pattern [42 | Q2] breaks the opaqueness of the term
+my_queue_use.erl:24: The attempt to match a term of type my_queue_adt:my_queue() against the pattern [42 | Q2] breaks the opacity of the term
my_queue_use.erl:30: Attempt to test for equality between a term of type [] and a term of opaque type my_queue_adt:my_queue()
my_queue_use.erl:34: Cons will produce an improper list since its 2nd argument is my_queue_adt:my_queue()
my_queue_use.erl:34: The call my_queue_adt:dequeue(nonempty_maybe_improper_list(42,my_queue_adt:my_queue())) does not have an opaque term of type my_queue_adt:my_queue() as 1st argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/opaque b/lib/dialyzer/test/opaque_SUITE_data/results/opaque
index 5747f9061f..864e0d853c 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/opaque
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/opaque
@@ -1,3 +1,3 @@
opaque_bug3.erl:19: The pattern 'a' can never match the type #c{}
-opaque_bug4.erl:20: The attempt to match a term of type opaque_adt:abc() against the pattern 'a' breaks the opaqueness of the term
+opaque_bug4.erl:20: The attempt to match a term of type opaque_adt:abc() against the pattern 'a' breaks the opacity of the term
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/para b/lib/dialyzer/test/opaque_SUITE_data/results/para
index 8fe67e39ad..37b5b7b44e 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/para
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/para
@@ -16,12 +16,12 @@ para2.erl:88: The test para2:circ(integer()) =:= para2:circ(integer(),integer())
para3.erl:28: Invalid type specification for function para3:ot2/0. The success typing is () -> 'foo'
para3.erl:36: The pattern {{{17}}} can never match the type {{{{{{_,_,_,_,_}}}}}}
para3.erl:55: Invalid type specification for function para3:t2/0. The success typing is () -> 'foo'
-para3.erl:65: The attempt to match a term of type {{{{{para3_adt:ot1(_,_,_,_,_)}}}}} against the pattern {{{{{17}}}}} breaks the opaqueness of para3_adt:ot1(_,_,_,_,_)
+para3.erl:65: The attempt to match a term of type {{{{{para3_adt:ot1(_,_,_,_,_)}}}}} against the pattern {{{{{17}}}}} breaks the opacity of para3_adt:ot1(_,_,_,_,_)
para3.erl:68: The pattern {{{{17}}}} can never match the type {{{{{para3_adt:ot1(_,_,_,_,_)}}}}}
-para3.erl:74: Invalid type specification for function para3:exp_adt/0. The success typing is () -> 3
-para4.erl:21: Invalid type specification for function para4:a/1. The success typing is (dict:dict(atom() | integer(),atom() | integer()) | para4:d_all()) -> [{atom() | integer(),atom() | integer()}]
-para4.erl:26: Invalid type specification for function para4:i/1. The success typing is (dict:dict(atom() | integer(),atom() | integer()) | para4:d_all()) -> [{atom() | integer(),atom() | integer()}]
-para4.erl:31: Invalid type specification for function para4:t/1. The success typing is (dict:dict(atom() | integer(),atom() | integer()) | para4:d_all()) -> [{atom() | integer(),atom() | integer()}]
+para3.erl:74: The specification for para3:exp_adt/0 has an opaque subtype para3_adt:exp1(para3_adt:exp2()) which is violated by the success typing () -> 3
+para4.erl:21: Invalid type specification for function para4:a/1. The success typing is (para4:d_all() | para4:d_atom()) -> [{atom() | integer(),atom() | integer()}]
+para4.erl:26: Invalid type specification for function para4:i/1. The success typing is (para4:d_all() | para4:d_integer()) -> [{atom() | integer(),atom() | integer()}]
+para4.erl:31: Invalid type specification for function para4:t/1. The success typing is (para4:d_all() | para4:d_tuple()) -> [{atom() | integer(),atom() | integer()}]
para4.erl:59: Attempt to test for equality between a term of type para4_adt:t(atom() | integer()) and a term of opaque type para4_adt:t(integer())
para4.erl:64: Attempt to test for equality between a term of type para4_adt:t(atom() | integer()) and a term of opaque type para4_adt:t(atom())
para4.erl:69: Attempt to test for equality between a term of type para4_adt:int(1 | 2 | 3 | 4) and a term of opaque type para4_adt:int(1 | 2)
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/queue b/lib/dialyzer/test/opaque_SUITE_data/results/queue
index 5b3813c418..9822b7168f 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/queue
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/queue
@@ -1,11 +1,11 @@
queue_use.erl:18: The call queue:is_empty({[],[]}) does not have an opaque term of type queue:queue(_) as 1st argument
queue_use.erl:22: The call queue:in(42,Q0::{[],[]}) does not have an opaque term of type queue:queue(_) as 2nd argument
-queue_use.erl:27: The attempt to match a term of type queue:queue(_) against the pattern {"*", Q2} breaks the opaqueness of the term
+queue_use.erl:27: The attempt to match a term of type queue:queue(_) against the pattern {"*", Q2} breaks the opacity of the term
queue_use.erl:33: Attempt to test for equality between a term of type {[42,...],[]} and a term of opaque type queue:queue(_)
-queue_use.erl:36: The attempt to match a term of type queue:queue(_) against the pattern {F, _R} breaks the opaqueness of the term
+queue_use.erl:36: The attempt to match a term of type queue:queue(_) against the pattern {F, _R} breaks the opacity of the term
queue_use.erl:40: The call queue:out({[42,...],[]}) does not have an opaque term of type queue:queue(_) as 1st argument
queue_use.erl:51: The call queue_use:is_in_queue(E::42,DB::#db{p::[],q::queue:queue(_)}) contains an opaque term as 2nd argument when terms of different types are expected in these positions
-queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue:queue(_)} against the pattern {'db', _, {L1, L2}} breaks the opaqueness of queue:queue(_)
+queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue:queue(_)} against the pattern {'db', _, {L1, L2}} breaks the opacity of queue:queue(_)
queue_use.erl:62: The call queue_use:tuple_queue({42,'gazonk'}) does not have a term of type {_,queue:queue(_)} (with opaque subterms) as 1st argument
queue_use.erl:65: The call queue:in(F::42,Q::'gazonk') does not have an opaque term of type queue:queue(_) as 2nd argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/rec b/lib/dialyzer/test/opaque_SUITE_data/results/rec
index 72736b3b3c..e9b217a93f 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/rec
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/rec
@@ -1,6 +1,6 @@
-rec_use.erl:17: The attempt to match a term of type rec_adt:rec() against the pattern {'rec', _, 42} breaks the opaqueness of the term
-rec_use.erl:18: Guard test tuple_size(R::rec_adt:rec()) breaks the opaqueness of its argument
+rec_use.erl:17: The attempt to match a term of type rec_adt:rec() against the pattern {'rec', _, 42} breaks the opacity of the term
+rec_use.erl:18: Guard test tuple_size(R::rec_adt:rec()) breaks the opacity of its argument
rec_use.erl:23: The call rec_adt:get_a(R::tuple()) does not have an opaque term of type rec_adt:rec() as 1st argument
rec_use.erl:27: Attempt to test for equality between a term of type {'rec','gazonk',42} and a term of opaque type rec_adt:rec()
rec_use.erl:30: The call erlang:tuple_size(rec_adt:rec()) contains an opaque term as 1st argument when a structured term of type tuple() is expected
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/simple b/lib/dialyzer/test/opaque_SUITE_data/results/simple
index 391c37664e..5cd8916aee 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/simple
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/simple
@@ -1,29 +1,29 @@
exact_api.erl:17: The call exact_api:set_type(A::#digraph{vtab::'notable',etab::'notable',ntab::'notable',cyclic::'true'}) does not have an opaque term of type digraph:graph() as 1st argument
exact_api.erl:23: The call digraph:delete(G::#digraph{vtab::'notable',etab::'notable',ntab::'notable',cyclic::'true'}) does not have an opaque term of type digraph:graph() as 1st argument
-exact_api.erl:55: The attempt to match a term of type exact_adt:exact_adt() against the pattern {'exact_adt'} breaks the opaqueness of the term
+exact_api.erl:55: The attempt to match a term of type exact_adt:exact_adt() against the pattern {'exact_adt'} breaks the opacity of the term
exact_api.erl:59: The call exact_adt:exact_adt_set_type2(A::#exact_adt{}) does not have an opaque term of type exact_adt:exact_adt() as 1st argument
is_rec.erl:10: The call erlang:is_record(simple1_adt:d1(),'r',2) contains an opaque term as 1st argument when terms of different types are expected in these positions
is_rec.erl:15: The call erlang:is_record(A::simple1_adt:d1(),'r',I::1 | 2 | 3) contains an opaque term as 1st argument when terms of different types are expected in these positions
-is_rec.erl:19: Guard test is_record(A::simple1_adt:d1(),'r',2) breaks the opaqueness of its argument
-is_rec.erl:23: Guard test is_record({simple1_adt:d1(),1},'r',2) breaks the opaqueness of its argument
+is_rec.erl:19: Guard test is_record(A::simple1_adt:d1(),'r',2) breaks the opacity of its argument
+is_rec.erl:23: Guard test is_record({simple1_adt:d1(),1},'r',2) breaks the opacity of its argument
is_rec.erl:41: The call erlang:is_record(A::simple1_adt:d1(),R::'a') contains an opaque term as 1st argument when terms of different types are expected in these positions
is_rec.erl:45: The call erlang:is_record(A::simple1_adt:d1(),A::simple1_adt:d1(),1) contains an opaque term as 2nd argument when terms of different types are expected in these positions
is_rec.erl:49: The call erlang:is_record(A::simple1_adt:d1(),any(),1) contains an opaque term as 1st argument when terms of different types are expected in these positions
is_rec.erl:53: The call erlang:is_record(A::simple1_adt:d1(),A::simple1_adt:d1(),any()) contains an opaque term as 2nd argument when terms of different types are expected in these positions
-is_rec.erl:57: Guard test is_record(A::simple1_adt:d1(),'r',2) breaks the opaqueness of its argument
+is_rec.erl:57: Guard test is_record(A::simple1_adt:d1(),'r',2) breaks the opacity of its argument
is_rec.erl:61: The record #r{f1::simple1_adt:d1()} violates the declared type for #r{}
is_rec.erl:65: The call erlang:is_record({simple1_adt:d1(),1},'r',2) contains an opaque term as 1st argument when terms of different types are expected in these positions
rec_api.erl:104: Matching of pattern {'r2', 10} tagged with a record name violates the declared type of #r2{f1::10}
-rec_api.erl:113: The attempt to match a term of type #r3{f1::queue:queue(_)} against the pattern {'r3', 'a'} breaks the opaqueness of queue:queue(_)
+rec_api.erl:113: The attempt to match a term of type #r3{f1::queue:queue(_)} against the pattern {'r3', 'a'} breaks the opacity of queue:queue(_)
rec_api.erl:118: Record construction #r3{f1::10} violates the declared type of field f1::queue:queue(_)
-rec_api.erl:123: The attempt to match a term of type #r3{f1::10} against the pattern {'r3', 10} breaks the opaqueness of queue:queue(_)
+rec_api.erl:123: The attempt to match a term of type #r3{f1::10} against the pattern {'r3', 10} breaks the opacity of queue:queue(_)
rec_api.erl:24: Record construction #r1{f1::10} violates the declared type of field f1::rec_api:a()
rec_api.erl:29: Matching of pattern {'r1', 10} tagged with a record name violates the declared type of #r1{f1::10}
-rec_api.erl:33: The attempt to match a term of type rec_adt:r1() against the pattern {'r1', 'a'} breaks the opaqueness of the term
+rec_api.erl:33: The attempt to match a term of type rec_adt:r1() against the pattern {'r1', 'a'} breaks the opacity of the term
rec_api.erl:35: Invalid type specification for function rec_api:adt_t1/1. The success typing is (#r1{f1::'a'}) -> #r1{f1::'a'}
-rec_api.erl:40: Invalid type specification for function rec_api:adt_r1/0. The success typing is () -> #r1{f1::'a'}
-rec_api.erl:85: The attempt to match a term of type rec_api:f() against the variable _ breaks the opaqueness of rec_adt:f()
+rec_api.erl:40: The specification for rec_api:adt_r1/0 has an opaque subtype rec_adt:r1() which is violated by the success typing () -> #r1{f1::'a'}
+rec_api.erl:85: The attempt to match a term of type rec_adt:f() against the record field 'f' declared to be of type rec_api:f() breaks the opacity of the term
rec_api.erl:99: Record construction #r2{f1::10} violates the declared type of field f1::rec_api:a()
simple1_api.erl:113: The test simple1_api:d1() =:= simple1_api:d2() can never evaluate to 'true'
simple1_api.erl:118: Guard test simple1_api:d2() =:= A::simple1_api:d1() can never succeed
@@ -35,20 +35,20 @@ simple1_api.erl:165: Attempt to test for equality between a term of type simple1
simple1_api.erl:181: Guard test A::simple1_adt:d1() =< B::simple1_adt:d2() contains opaque terms as 1st and 2nd arguments
simple1_api.erl:185: Guard test 'a' =< B::simple1_adt:d2() contains an opaque term as 2nd argument
simple1_api.erl:189: Guard test A::simple1_adt:d1() =< 'd' contains an opaque term as 1st argument
-simple1_api.erl:197: The type test is_integer(A::simple1_adt:d1()) breaks the opaqueness of the term A::simple1_adt:d1()
+simple1_api.erl:197: The type test is_integer(A::simple1_adt:d1()) breaks the opacity of the term A::simple1_adt:d1()
simple1_api.erl:221: Guard test A::simple1_api:i1() > 3 can never succeed
simple1_api.erl:225: Guard test A::simple1_adt:i1() > 3 contains an opaque term as 1st argument
simple1_api.erl:233: Guard test A::simple1_adt:i1() < 3 contains an opaque term as 1st argument
simple1_api.erl:239: Guard test A::1 > 3 can never succeed
simple1_api.erl:243: Guard test A::1 > 3 can never succeed
simple1_api.erl:257: Guard test is_function(T::simple1_api:o1()) can never succeed
-simple1_api.erl:265: Guard test is_function(T::simple1_adt:o1()) breaks the opaqueness of its argument
-simple1_api.erl:269: The type test is_function(T::simple1_adt:o1()) breaks the opaqueness of the term T::simple1_adt:o1()
+simple1_api.erl:265: Guard test is_function(T::simple1_adt:o1()) breaks the opacity of its argument
+simple1_api.erl:269: The type test is_function(T::simple1_adt:o1()) breaks the opacity of the term T::simple1_adt:o1()
simple1_api.erl:274: Guard test is_function(T::simple1_api:o1(),A::simple1_api:i1()) can never succeed
-simple1_api.erl:284: Guard test is_function(T::simple1_adt:o1(),A::simple1_adt:i1()) breaks the opaqueness of its argument
-simple1_api.erl:289: The type test is_function(T::simple1_adt:o1(),A::simple1_adt:i1()) breaks the opaqueness of the term T::simple1_adt:o1()
+simple1_api.erl:284: Guard test is_function(T::simple1_adt:o1(),A::simple1_adt:i1()) breaks the opacity of its argument
+simple1_api.erl:289: The type test is_function(T::simple1_adt:o1(),A::simple1_adt:i1()) breaks the opacity of the term T::simple1_adt:o1()
simple1_api.erl:294: The call erlang:is_function(T::simple1_api:o1(),A::simple1_adt:i1()) contains an opaque term as 2nd argument when terms of different types are expected in these positions
-simple1_api.erl:300: The type test is_function(T::simple1_adt:o1(),A::simple1_api:i1()) breaks the opaqueness of the term T::simple1_adt:o1()
+simple1_api.erl:300: The type test is_function(T::simple1_adt:o1(),A::simple1_api:i1()) breaks the opacity of the term T::simple1_adt:o1()
simple1_api.erl:306: Guard test B::simple1_api:b2() =:= 'true' can never succeed
simple1_api.erl:315: Guard test A::simple1_api:b1() =:= 'false' can never succeed
simple1_api.erl:319: Guard test not('and'('true','true')) can never succeed
@@ -60,14 +60,14 @@ simple1_api.erl:365: Clause guard cannot succeed.
simple1_api.erl:368: Invalid type specification for function simple1_api:bool_adt_t8/2. The success typing is (boolean(),boolean()) -> 1
simple1_api.erl:378: Clause guard cannot succeed.
simple1_api.erl:381: Invalid type specification for function simple1_api:bool_adt_t9/2. The success typing is ('false','false') -> 1
-simple1_api.erl:407: The size simple1_adt:i1() breaks the opaqueness of A
-simple1_api.erl:418: The attempt to match a term of type non_neg_integer() against the variable A breaks the opaqueness of simple1_adt:i1()
-simple1_api.erl:425: The attempt to match a term of type non_neg_integer() against the variable B breaks the opaqueness of simple1_adt:i1()
+simple1_api.erl:407: The size simple1_adt:i1() breaks the opacity of A
+simple1_api.erl:418: The attempt to match a term of type non_neg_integer() against the variable A breaks the opacity of simple1_adt:i1()
+simple1_api.erl:425: The attempt to match a term of type non_neg_integer() against the variable B breaks the opacity of simple1_adt:i1()
simple1_api.erl:432: The pattern <<_:B/integer-unit:1>> can never match the type any()
-simple1_api.erl:448: The attempt to match a term of type non_neg_integer() against the variable Sz breaks the opaqueness of simple1_adt:i1()
-simple1_api.erl:460: The attempt to match a term of type simple1_adt:bit1() against the pattern <<_/binary-unit:8>> breaks the opaqueness of the term
-simple1_api.erl:478: The call 'foo':A(A::simple1_adt:a()) breaks the opaqueness of the term A :: simple1_adt:a()
-simple1_api.erl:486: The call A:'foo'(A::simple1_adt:a()) breaks the opaqueness of the term A :: simple1_adt:a()
+simple1_api.erl:448: The attempt to match a term of type non_neg_integer() against the variable Sz breaks the opacity of simple1_adt:i1()
+simple1_api.erl:460: The attempt to match a term of type simple1_adt:bit1() against the pattern <<_/binary-unit:8>> breaks the opacity of the term
+simple1_api.erl:478: The call 'foo':A(A::simple1_adt:a()) breaks the opacity of the term A :: simple1_adt:a()
+simple1_api.erl:486: The call A:'foo'(A::simple1_adt:a()) breaks the opacity of the term A :: simple1_adt:a()
simple1_api.erl:499: The call 'foo':A(A::simple1_api:i()) requires that A is of type atom() not simple1_api:i()
simple1_api.erl:503: The call 'foo':A(A::simple1_adt:i()) requires that A is of type atom() not simple1_adt:i()
simple1_api.erl:507: The call A:'foo'(A::simple1_api:i()) requires that A is of type atom() not simple1_api:i()
@@ -79,7 +79,7 @@ simple1_api.erl:538: Guard test A::simple1_adt:d1() =:= 3 contains an opaque ter
simple1_api.erl:548: The call erlang:'<'(A::simple1_adt:d1(),3) contains an opaque term as 1st argument when terms of different types are expected in these positions
simple1_api.erl:558: The call erlang:'=<'(A::simple1_adt:d1(),B::simple1_adt:d2()) contains opaque terms as 1st and 2nd arguments when terms of different types are expected in these positions
simple1_api.erl:565: Guard test {digraph:graph(),3} > {digraph:graph(),atom() | ets:tid()} contains an opaque term as 2nd argument
-simple1_api.erl:91: Invalid type specification for function simple1_api:tup/0. The success typing is () -> {'a','b'}
+simple1_api.erl:91: The specification for simple1_api:tup/0 has an opaque subtype simple1_adt:tuple1() which is violated by the success typing () -> {'a','b'}
simple2_api.erl:100: The call lists:flatten(A::simple1_adt:tuple1()) contains an opaque term as 1st argument when a structured term of type [any()] is expected
simple2_api.erl:116: The call lists:flatten({simple1_adt:tuple1()}) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
simple2_api.erl:121: Guard test {simple1_adt:d1(),3} > {simple1_adt:d1(),simple1_adt:tuple1()} contains an opaque term as 2nd argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/timer b/lib/dialyzer/test/opaque_SUITE_data/results/timer
index b1cfcd4e9f..46c5a86307 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/timer
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/timer
@@ -1,4 +1,4 @@
timer_use.erl:16: The pattern 'gazonk' can never match the type {'error',_} | {'ok',timer:tref()}
-timer_use.erl:17: The attempt to match a term of type {'error',_} | {'ok',timer:tref()} against the pattern {'ok', 42} breaks the opaqueness of timer:tref()
-timer_use.erl:18: The attempt to match a term of type {'error',_} | {'ok',timer:tref()} against the pattern {Tag, 'gazonk'} breaks the opaqueness of timer:tref()
+timer_use.erl:17: The attempt to match a term of type {'error',_} | {'ok',timer:tref()} against the pattern {'ok', 42} breaks the opacity of timer:tref()
+timer_use.erl:18: The attempt to match a term of type {'error',_} | {'ok',timer:tref()} against the pattern {Tag, 'gazonk'} breaks the opacity of timer:tref()
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/union b/lib/dialyzer/test/opaque_SUITE_data/results/union
index 98829b424a..8763088bf0 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/union
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/union
@@ -1,5 +1,5 @@
-union_use.erl:12: The attempt to match a term of type union_adt:u() against the pattern 'aaa' breaks the opaqueness of the term
-union_use.erl:16: The type test is_tuple(union_adt:u()) breaks the opaqueness of the term union_adt:u()
-union_use.erl:7: Guard test is_atom(A::union_adt:u()) breaks the opaqueness of its argument
-union_use.erl:8: Guard test is_tuple(T::union_adt:u()) breaks the opaqueness of its argument
+union_use.erl:12: The attempt to match a term of type union_adt:u() against the pattern 'aaa' breaks the opacity of the term
+union_use.erl:16: The type test is_tuple(union_adt:u()) breaks the opacity of the term union_adt:u()
+union_use.erl:7: Guard test is_atom(A::union_adt:u()) breaks the opacity of its argument
+union_use.erl:8: Guard test is_tuple(T::union_adt:u()) breaks the opacity of its argument
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/weird b/lib/dialyzer/test/opaque_SUITE_data/results/weird
new file mode 100644
index 0000000000..d7f57cd152
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/weird
@@ -0,0 +1,6 @@
+
+weird_warning1.erl:15: Matching of pattern {'a', Dict} tagged with a record name violates the declared type of #b{q::queue:queue(_)}
+weird_warning2.erl:13: Matching of pattern <{'b', Queue}, Key, Value> tagged with a record name violates the declared type of <#a{d::dict:dict(_,_)},'my_key','my_value'>
+weird_warning3.erl:14: The call weird_warning3:add_element(#a{d::queue:queue(_)},'my_key','my_value') does not have a term of type #a{d::dict:dict(_,_)} | #b{q::queue:queue(_)} (with opaque subterms) as 1st argument
+weird_warning3.erl:16: The attempt to match a term of type #a{d::queue:queue(_)} against the pattern {'a', Dict} breaks the opacity of queue:queue(_)
+weird_warning3.erl:18: Matching of pattern {'b', Queue} tagged with a record name violates the declared type of #a{d::queue:queue(_)}
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/wings b/lib/dialyzer/test/opaque_SUITE_data/results/wings
index 511263b70a..391501d86f 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/results/wings
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/wings
@@ -1,11 +1,11 @@
-wings_dissolve.erl:103: Guard test is_list(List::gb_sets:set(_)) breaks the opaqueness of its argument
-wings_dissolve.erl:19: Guard test is_list(Faces::gb_sets:set(_)) breaks the opaqueness of its argument
-wings_dissolve.erl:272: Guard test is_list(Faces::gb_sets:set(_)) breaks the opaqueness of its argument
+wings_dissolve.erl:103: Guard test is_list(List::gb_sets:set(_)) breaks the opacity of its argument
+wings_dissolve.erl:19: Guard test is_list(Faces::gb_sets:set(_)) breaks the opacity of its argument
+wings_dissolve.erl:272: Guard test is_list(Faces::gb_sets:set(_)) breaks the opacity of its argument
wings_dissolve.erl:31: The call gb_sets:is_empty(Faces::[any(),...]) does not have an opaque term of type gb_sets:set(_) as 1st argument
wings_edge.erl:205: The pattern <Edge, 'hard', Htab> can never match the type <_,'soft',_>
wings_edge_cmd.erl:30: The call gb_trees:size(P::gb_sets:set(_)) does not have an opaque term of type gb_trees:tree(_,_) as 1st argument
wings_edge_cmd.erl:32: The pattern [_ | Parts] can never match the type []
wings_edge_cmd.erl:32: The pattern [{_, P} | _] can never match the type []
-wings_io.erl:30: The attempt to match a term of type {'empty',queue:queue(_)} against the pattern {'empty', {In, Out}} breaks the opaqueness of queue:queue(_)
+wings_io.erl:30: The attempt to match a term of type {'empty',queue:queue(_)} against the pattern {'empty', {In, Out}} breaks the opacity of queue:queue(_)
wings_we.erl:155: The call wings_util:gb_trees_largest_key(Etab::gb_trees:tree(_,_)) contains an opaque term as 1st argument when a structured term of type {_,{_,_,_,'nil' | {_,_,_,'nil' | {_,_,_,_}}}} is expected
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl
index a4cec065ab..2527f166f2 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/dict/dict_use.erl
@@ -34,7 +34,7 @@ middle() ->
{w1(), w2()}.
%%---------------------------------------------------------------------
-%% Cases that are problematic w.r.t. opaqueness of types
+%% Cases that are problematic w.r.t. opacity of types
%%---------------------------------------------------------------------
w1() ->
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/hipe_vectors/hipe_ig_moves.erl b/lib/dialyzer/test/opaque_SUITE_data/src/hipe_vectors/hipe_ig_moves.erl
new file mode 100644
index 0000000000..2a70606dab
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/hipe_vectors/hipe_ig_moves.erl
@@ -0,0 +1,83 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% %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(hipe_ig_moves).
+-export([new/1,
+ new_move/3,
+ get_moves/1]).
+
+%%-----------------------------------------------------------------------------
+%% The main data structure; its fields are:
+%% - movelist : mapping from temp to set of associated move numbers
+%% - nrmoves : number of distinct move instructions seen so far
+%% - moveinsns : list of move instructions, in descending move number order
+%% - moveset : set of move instructions
+
+-record(ig_moves, {movelist :: movelist(),
+ nrmoves = 0 :: non_neg_integer(),
+ moveinsns = [] :: [{_,_}],
+ moveset = gb_sets:empty() :: gb_sets:set()}).
+
+-type movelist() :: hipe_vectors:vector(ordsets:ordset(non_neg_integer())).
+
+%%-----------------------------------------------------------------------------
+
+-spec new(non_neg_integer()) -> #ig_moves{}.
+
+new(NrTemps) ->
+ MoveList = hipe_vectors:new(NrTemps, ordsets:new()),
+ #ig_moves{movelist = MoveList}.
+
+-spec new_move(_, _, #ig_moves{}) -> #ig_moves{}.
+
+new_move(Dst, Src, IG_moves) ->
+ MoveSet = IG_moves#ig_moves.moveset,
+ MoveInsn = {Dst, Src},
+ case gb_sets:is_member(MoveInsn, MoveSet) of
+ true ->
+ IG_moves;
+ false ->
+ MoveNr = IG_moves#ig_moves.nrmoves,
+ Movelist0 = IG_moves#ig_moves.movelist,
+ Movelist1 = add_movelist(MoveNr, Dst,
+ add_movelist(MoveNr, Src, Movelist0)),
+ IG_moves#ig_moves{nrmoves = MoveNr+1,
+ movelist = Movelist1,
+ moveinsns = [MoveInsn|IG_moves#ig_moves.moveinsns],
+ moveset = gb_sets:insert(MoveInsn, MoveSet)}
+ end.
+
+-spec add_movelist(non_neg_integer(), non_neg_integer(), movelist())
+ -> movelist().
+
+add_movelist(MoveNr, Temp, MoveList) ->
+ AssocMoves = hipe_vectors:get(MoveList, Temp),
+ %% XXX: MoveNr does not occur in moveList[Temp], but the new list must be an
+ %% ordset due to the ordsets:union in hipe_coalescing_regalloc:combine().
+ hipe_vectors:set(MoveList, Temp, ordsets:add_element(MoveNr, AssocMoves)).
+
+-spec get_moves(#ig_moves{}) -> {movelist(), non_neg_integer(), tuple()}.
+
+get_moves(IG_moves) -> % -> {MoveList, NrMoves, MoveInsns}
+ {IG_moves#ig_moves.movelist,
+ IG_moves#ig_moves.nrmoves,
+ list_to_tuple(lists:reverse(IG_moves#ig_moves.moveinsns))}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/hipe_vectors/hipe_vectors.erl b/lib/dialyzer/test/opaque_SUITE_data/src/hipe_vectors/hipe_vectors.erl
new file mode 100644
index 0000000000..279f244586
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/hipe_vectors/hipe_vectors.erl
@@ -0,0 +1,136 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% %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%
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% VECTORS IN ERLANG
+%%
+%% Abstract interface to vectors, indexed from 0 to size-1.
+
+-module(hipe_vectors).
+-export([new/2,
+ set/3,
+ get/2,
+ size/1,
+ vector_to_list/1,
+ %% list_to_vector/1,
+ list/1]).
+
+%%-define(USE_TUPLES, true).
+%%-define(USE_GBTREES, true).
+-define(USE_ARRAYS, true).
+
+-type vector() :: vector(_).
+-export_type([vector/0, vector/1]).
+
+-spec new(non_neg_integer(), V) -> vector(E) when V :: E.
+-spec set(vector(E), non_neg_integer(), V :: E) -> vector(E).
+-spec get(vector(E), non_neg_integer()) -> E.
+-spec size(vector(_)) -> non_neg_integer().
+-spec vector_to_list(vector(E)) -> [E].
+%% -spec list_to_vector([E]) -> vector(E).
+-spec list(vector(E)) -> [{non_neg_integer(), E}].
+
+%% ---------------------------------------------------------------------
+
+-ifdef(USE_TUPLES).
+-opaque vector(_) :: tuple().
+
+new(N, V) ->
+ erlang:make_tuple(N, V).
+
+size(V) -> erlang:tuple_size(V).
+
+list(Vec) ->
+ index(tuple_to_list(Vec), 0).
+
+index([X|Xs],N) ->
+ [{N,X} | index(Xs,N+1)];
+index([],_) ->
+ [].
+
+%% list_to_vector(Xs) ->
+%% list_to_tuple(Xs).
+
+vector_to_list(V) ->
+ tuple_to_list(V).
+
+set(Vec, Ix, V) ->
+ setelement(Ix+1, Vec, V).
+
+get(Vec, Ix) -> element(Ix+1, Vec).
+
+-endif. %% ifdef USE_TUPLES
+
+%% ---------------------------------------------------------------------
+
+-ifdef(USE_GBTREES).
+-opaque vector(E) :: gb_trees:tree(non_neg_integer(), E).
+
+new(N, V) when is_integer(N), N >= 0 ->
+ gb_trees:from_orddict(mklist(N, V)).
+
+mklist(N, V) ->
+ mklist(0, N, V).
+
+mklist(M, N, V) when M < N ->
+ [{M, V} | mklist(M+1, N, V)];
+mklist(_, _, _) ->
+ [].
+
+size(V) -> gb_trees:size(V).
+
+list(Vec) ->
+ gb_trees:to_list(Vec).
+
+%% list_to_vector(Xs) ->
+%% gb_trees:from_orddict(index(Xs, 0)).
+%%
+%% index([X|Xs], N) ->
+%% [{N, X} | index(Xs, N+1)];
+%% index([],_) ->
+%% [].
+
+vector_to_list(V) ->
+ gb_trees:values(V).
+
+set(Vec, Ix, V) ->
+ gb_trees:update(Ix, V, Vec).
+
+get(Vec, Ix) ->
+ gb_trees:get(Ix, Vec).
+
+-endif. %% ifdef USE_GBTREES
+
+%% ---------------------------------------------------------------------
+
+-ifdef(USE_ARRAYS).
+-opaque vector(E) :: array:array(E).
+%%-type vector(E) :: array:array(E). % Work around dialyzer bug
+
+new(N, V) -> array:new(N, {default, V}).
+size(V) -> array:size(V).
+list(Vec) -> array:to_orddict(Vec).
+%% list_to_vector(Xs) -> array:from_list(Xs).
+vector_to_list(V) -> array:to_list(V).
+set(Vec, Ix, V) -> array:set(Ix, V, Vec).
+get(Vec, Ix) -> array:get(Ix, Vec).
+
+-endif. %% ifdef USE_ARRAYS
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl
index 102215b28d..d8c1f561f7 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para3.erl
@@ -62,7 +62,7 @@ t2() ->
%% Shows that the list TypeNames in t_from_form must include ArgsLen.
t1_adt() ->
- {{{{{17}}}}} = para3_adt:t1(3). % breaks the opaqueness
+ {{{{{17}}}}} = para3_adt:t1(3). % breaks the opacity
t2_adt() ->
{{{{17}}}} = para3_adt:t1(3). % can never match
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/recrec/cerl.erl b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/cerl.erl
new file mode 100644
index 0000000000..a4fdbfd5f0
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/cerl.erl
@@ -0,0 +1,4602 @@
+%%
+%% %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%
+
+%% =====================================================================
+%% @doc Core Erlang abstract syntax trees.
+%%
+%% <p> This module defines an abstract data type for representing Core
+%% Erlang source code as syntax trees.</p>
+%%
+%% <p>A recommended starting point for the first-time user is the
+%% documentation of the function <a
+%% href="#type-1"><code>type/1</code></a>.</p>
+%%
+%% <h3><b>NOTES:</b></h3>
+%%
+%% <p>This module deals with the composition and decomposition of
+%% <em>syntactic</em> entities (as opposed to semantic ones); its
+%% purpose is to hide all direct references to the data structures
+%% used to represent these entities. With few exceptions, the
+%% functions in this module perform no semantic interpretation of
+%% their inputs, and in general, the user is assumed to pass
+%% type-correct arguments - if this is not done, the effects are not
+%% defined.</p>
+%%
+%% <p>Currently, the internal data structure used is the same as
+%% the record-based data structures used traditionally in the Beam
+%% compiler.</p>
+%%
+%% <p>The internal representations of abstract syntax trees are
+%% subject to change without notice, and should not be documented
+%% outside this module. Furthermore, we do not give any guarantees on
+%% how an abstract syntax tree may or may not be represented, <em>with
+%% the following exceptions</em>: no syntax tree is represented by a
+%% single atom, such as <code>none</code>, by a list constructor
+%% <code>[X | Y]</code>, or by the empty list <code>[]</code>. This
+%% can be relied on when writing functions that operate on syntax
+%% trees.</p>
+%%
+%% @type cerl(). An abstract Core Erlang syntax tree.
+%%
+%% <p>Every abstract syntax tree has a <em>type</em>, given by the
+%% function <a href="#type-1"><code>type/1</code></a>. In addition,
+%% each syntax tree has a list of <em>user annotations</em> (cf. <a
+%% href="#get_ann-1"><code>get_ann/1</code></a>), which are included
+%% in the Core Erlang syntax.</p>
+
+-module(cerl).
+
+-export([abstract/1, add_ann/2, alias_pat/1, alias_var/1,
+ ann_abstract/2, ann_c_alias/3, ann_c_apply/3, ann_c_atom/2,
+ ann_c_call/4, ann_c_case/3, ann_c_catch/2, ann_c_char/2,
+ ann_c_clause/3, ann_c_clause/4, ann_c_cons/3, ann_c_float/2,
+ ann_c_fname/3, ann_c_fun/3, ann_c_int/2, ann_c_let/4,
+ ann_c_letrec/3, ann_c_module/4, ann_c_module/5, ann_c_nil/1,
+ ann_c_cons_skel/3, ann_c_tuple_skel/2, ann_c_primop/3,
+ ann_c_receive/2, ann_c_receive/4, ann_c_seq/3, ann_c_string/2,
+ ann_c_try/6, ann_c_tuple/2, ann_c_values/2, ann_c_var/2,
+ ann_make_data/3, ann_make_list/2, ann_make_list/3,
+ ann_make_data_skel/3, ann_make_tree/3, apply_args/1,
+ apply_arity/1, apply_op/1, atom_lit/1, atom_name/1, atom_val/1,
+ c_alias/2, c_apply/2, c_atom/1, c_call/3, c_case/2, c_catch/1,
+ c_char/1, c_clause/2, c_clause/3, c_cons/2, c_float/1,
+ c_fname/2, c_fun/2, c_int/1, c_let/3, c_letrec/2, c_module/3,
+ c_module/4, c_nil/0, c_cons_skel/2, c_tuple_skel/1, c_primop/2,
+ c_receive/1, c_receive/3, c_seq/2, c_string/1, c_try/5,
+ c_tuple/1, c_values/1, c_var/1, call_args/1, call_arity/1,
+ call_module/1, call_name/1, case_arg/1, case_arity/1,
+ case_clauses/1, catch_body/1, char_lit/1, char_val/1,
+ clause_arity/1, clause_body/1, clause_guard/1, clause_pats/1,
+ clause_vars/1, concrete/1, cons_hd/1, cons_tl/1, copy_ann/2,
+ data_arity/1, data_es/1, data_type/1, float_lit/1, float_val/1,
+ fname_arity/1, fname_id/1, fold_literal/1, from_records/1,
+ fun_arity/1, fun_body/1, fun_vars/1, get_ann/1, int_lit/1,
+ int_val/1, is_c_alias/1, is_c_apply/1, is_c_atom/1,
+ is_c_call/1, is_c_case/1, is_c_catch/1, is_c_char/1,
+ is_c_clause/1, is_c_cons/1, is_c_float/1, is_c_fname/1,
+ is_c_fun/1, is_c_int/1, is_c_let/1, is_c_letrec/1, is_c_list/1,
+ is_c_module/1, is_c_nil/1, is_c_primop/1, is_c_receive/1,
+ is_c_seq/1, is_c_string/1, is_c_try/1, is_c_tuple/1,
+ is_c_values/1, is_c_var/1, is_data/1, is_leaf/1, is_literal/1,
+ is_literal_term/1, is_print_char/1, is_print_string/1,
+ let_arg/1, let_arity/1, let_body/1, let_vars/1, letrec_body/1,
+ letrec_defs/1, letrec_vars/1, list_elements/1, list_length/1,
+ make_data/2, make_list/1, make_list/2, make_data_skel/2,
+ make_tree/2, meta/1, module_attrs/1, module_defs/1,
+ module_exports/1, module_name/1, module_vars/1,
+ pat_list_vars/1, pat_vars/1, primop_args/1, primop_arity/1,
+ primop_name/1, receive_action/1, receive_clauses/1,
+ receive_timeout/1, seq_arg/1, seq_body/1, set_ann/2,
+ string_lit/1, string_val/1, subtrees/1, to_records/1,
+ try_arg/1, try_body/1, try_vars/1, try_evars/1, try_handler/1,
+ tuple_arity/1, tuple_es/1, type/1, unfold_literal/1,
+ update_c_alias/3, update_c_apply/3, update_c_call/4,
+ update_c_case/3, update_c_catch/2, update_c_clause/4,
+ update_c_cons/3, update_c_cons_skel/3, update_c_fname/2,
+ update_c_fname/3, update_c_fun/3, update_c_let/4,
+ update_c_letrec/3, update_c_module/5, update_c_primop/3,
+ update_c_receive/4, update_c_seq/3, update_c_try/6,
+ update_c_tuple/2, update_c_tuple_skel/2, update_c_values/2,
+ update_c_var/2, update_data/3, update_list/2, update_list/3,
+ update_data_skel/3, update_tree/2, update_tree/3,
+ values_arity/1, values_es/1, var_name/1, c_binary/1,
+ update_c_binary/2, ann_c_binary/2, is_c_binary/1,
+ binary_segments/1, c_bitstr/3, c_bitstr/4, c_bitstr/5,
+ update_c_bitstr/5, update_c_bitstr/6, ann_c_bitstr/5,
+ ann_c_bitstr/6, is_c_bitstr/1, bitstr_val/1, bitstr_size/1,
+ bitstr_bitsize/1, bitstr_unit/1, bitstr_type/1, bitstr_flags/1,
+
+ %% keep map exports here for now
+ c_map_pattern/1,
+ is_c_map/1,
+ is_c_map_pattern/1,
+ map_es/1,
+ map_arg/1,
+ update_c_map/3,
+ c_map/1, is_c_map_empty/1,
+ ann_c_map/2, ann_c_map/3,
+ 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_exact/2,
+ ann_c_map_pair/4
+ ]).
+
+-export_type([c_binary/0, c_bitstr/0, c_call/0, c_clause/0, c_cons/0, c_fun/0,
+ c_let/0, c_literal/0, c_map/0, c_map_pair/0,
+ c_module/0, c_tuple/0,
+ c_values/0, c_var/0, cerl/0,
+ anns/0, attrs/0, defs/0, litval/0, var_name/0]).
+
+-include("core_parse.hrl").
+
+-type c_alias() :: #c_alias{}.
+-type c_apply() :: #c_apply{}.
+-type c_binary() :: #c_binary{}.
+-type c_bitstr() :: #c_bitstr{}.
+-type c_call() :: #c_call{}.
+-type c_case() :: #c_case{}.
+-type c_catch() :: #c_catch{}.
+-type c_clause() :: #c_clause{}.
+-type c_cons() :: #c_cons{}.
+-type c_fun() :: #c_fun{}.
+-type c_let() :: #c_let{}.
+-type c_letrec() :: #c_letrec{}.
+-type c_literal() :: #c_literal{}.
+-type c_map() :: #c_map{}.
+-type c_map_pair() :: #c_map_pair{}.
+-type c_module() :: #c_module{}.
+-type c_primop() :: #c_primop{}.
+-type c_receive() :: #c_receive{}.
+-type c_seq() :: #c_seq{}.
+-type c_try() :: #c_try{}.
+-type c_tuple() :: #c_tuple{}.
+-type c_values() :: #c_values{}.
+-type c_var() :: #c_var{}.
+
+-type cerl() :: c_alias() | c_apply() | c_binary() | c_bitstr()
+ | c_call() | c_case() | c_catch() | c_clause() | c_cons()
+ | c_fun() | c_let() | c_letrec() | c_literal()
+ | c_map() | c_map_pair()
+ | c_module() | c_primop() | c_receive() | c_seq()
+ | c_try() | c_tuple() | c_values() | c_var().
+
+-type anns() :: [term()].
+-type attr() :: {c_literal(), c_literal()}.
+-type attrs() :: [attr()].
+-type def() :: {c_var(), c_fun()}.
+-type defs() :: [def()].
+
+-type litval() :: atom() | bitstring() | map() | number()
+ | string() | tuple() | [litval()].
+
+-type var_name() :: integer() | atom() | {atom(), arity()}.
+
+
+%% =====================================================================
+%% Representation (general)
+%%
+%% All nodes are represented by tuples of arity 2 or (generally)
+%% greater, whose first element is an atom which uniquely identifies the
+%% type of the node, and whose second element is a (proper) list of
+%% annotation terms associated with the node - this is by default empty.
+%%
+%% For most node constructor functions, there are analogous functions
+%% named 'ann_...', taking one extra argument 'As' (always the first
+%% argument), specifying an annotation list at node creation time.
+%% Similarly, there are also functions named 'update_...', taking one
+%% extra argument 'Old', specifying a node from which all fields not
+%% explicitly given as arguments should be copied (generally, this is
+%% the annotation field only).
+%% =====================================================================
+
+%% @spec type(Node::cerl()) -> atom()
+%%
+%% @doc Returns the type tag of <code>Node</code>. Current node types
+%% are:
+%%
+%% <p><center><table border="1">
+%% <tr>
+%% <td>alias</td>
+%% <td>apply</td>
+%% <td>binary</td>
+%% <td>bitstr</td>
+%% <td>call</td>
+%% <td>case</td>
+%% <td>catch</td>
+%% <td>clause</td>
+%% </tr><tr>
+%% <td>cons</td>
+%% <td>fun</td>
+%% <td>let</td>
+%% <td>letrec</td>
+%% <td>literal</td>
+%% <td>map</td>
+%% <td>map_pair</td>
+%% <td>module</td>
+%% </tr><tr>
+%% <td>primop</td>
+%% <td>receive</td>
+%% <td>seq</td>
+%% <td>try</td>
+%% <td>tuple</td>
+%% <td>values</td>
+%% <td>var</td>
+%% </tr>
+%% </table></center></p>
+%%
+%% <p>Note: The name of the primary constructor function for a node
+%% type is always the name of the type itself, prefixed by
+%% "<code>c_</code>"; recognizer predicates are correspondingly
+%% prefixed by "<code>is_c_</code>". Furthermore, to simplify
+%% preservation of annotations (cf. <code>get_ann/1</code>), there are
+%% analogous constructor functions prefixed by "<code>ann_c_</code>"
+%% and "<code>update_c_</code>", for setting the annotation list of
+%% the new node to either a specific value or to the annotations of an
+%% existing node, respectively.</p>
+%%
+%% @see abstract/1
+%% @see c_alias/2
+%% @see c_apply/2
+%% @see c_binary/1
+%% @see c_bitstr/5
+%% @see c_call/3
+%% @see c_case/2
+%% @see c_catch/1
+%% @see c_clause/3
+%% @see c_cons/2
+%% @see c_fun/2
+%% @see c_let/3
+%% @see c_letrec/2
+%% @see c_module/3
+%% @see c_primop/2
+%% @see c_receive/1
+%% @see c_seq/2
+%% @see c_try/5
+%% @see c_tuple/1
+%% @see c_values/1
+%% @see c_var/1
+%% @see get_ann/1
+%% @see to_records/1
+%% @see from_records/1
+%% @see data_type/1
+%% @see subtrees/1
+%% @see meta/1
+
+-type ctype() :: 'alias' | 'apply' | 'binary' | 'bitrst' | 'call' | 'case'
+ | 'catch' | 'clause' | 'cons' | 'fun' | 'let' | 'letrec'
+ | 'literal' | 'map' | 'map_pair' | 'module' | 'primop'
+ | 'receive' | 'seq' | 'try' | 'tuple' | 'values' | 'var'.
+
+-spec type(cerl()) -> ctype().
+
+type(#c_alias{}) -> alias;
+type(#c_apply{}) -> apply;
+type(#c_binary{}) -> binary;
+type(#c_bitstr{}) -> bitstr;
+type(#c_call{}) -> call;
+type(#c_case{}) -> 'case';
+type(#c_catch{}) -> 'catch';
+type(#c_clause{}) -> clause;
+type(#c_cons{}) -> cons;
+type(#c_fun{}) -> 'fun';
+type(#c_let{}) -> 'let';
+type(#c_letrec{}) -> letrec;
+type(#c_literal{}) -> literal;
+type(#c_map{}) -> map;
+type(#c_map_pair{}) -> map_pair;
+type(#c_module{}) -> module;
+type(#c_primop{}) -> primop;
+type(#c_receive{}) -> 'receive';
+type(#c_seq{}) -> seq;
+type(#c_try{}) -> 'try';
+type(#c_tuple{}) -> tuple;
+type(#c_values{}) -> values;
+type(#c_var{}) -> var.
+
+
+%% @spec is_leaf(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is a leaf node,
+%% otherwise <code>false</code>. The current leaf node types are
+%% <code>literal</code> and <code>var</code>.
+%%
+%% <p>Note: all literals (cf. <code>is_literal/1</code>) are leaf
+%% nodes, even if they represent structured (constant) values such as
+%% <code>{foo, [bar, baz]}</code>. Also note that variables are leaf
+%% nodes but not literals.</p>
+%%
+%% @see type/1
+%% @see is_literal/1
+
+-spec is_leaf(cerl()) -> boolean().
+
+is_leaf(Node) ->
+ case type(Node) of
+ literal -> true;
+ var -> true;
+ _ -> false
+ end.
+
+
+%% @spec get_ann(cerl()) -> anns()
+%%
+%% @doc Returns the list of user annotations associated with a syntax
+%% tree node. For a newly created node, this is the empty list. The
+%% annotations may be any terms.
+%%
+%% @see set_ann/2
+
+-spec get_ann(cerl()) -> anns().
+
+get_ann(Node) ->
+ element(2, Node).
+
+
+%% @spec set_ann(Node::cerl(), Annotations::anns()) -> cerl()
+%%
+%% @doc Sets the list of user annotations of <code>Node</code> to
+%% <code>Annotations</code>.
+%%
+%% @see get_ann/1
+%% @see add_ann/2
+%% @see copy_ann/2
+
+-spec set_ann(cerl(), anns()) -> cerl().
+
+set_ann(Node, List) ->
+ setelement(2, Node, List).
+
+
+%% @spec add_ann(Annotations::anns(), Node::cerl()) -> cerl()
+%%
+%% @doc Appends <code>Annotations</code> to the list of user
+%% annotations of <code>Node</code>.
+%%
+%% <p>Note: this is equivalent to <code>set_ann(Node, Annotations ++
+%% get_ann(Node))</code>, but potentially more efficient.</p>
+%%
+%% @see get_ann/1
+%% @see set_ann/2
+
+-spec add_ann(anns(), cerl()) -> cerl().
+
+add_ann(Terms, Node) ->
+ set_ann(Node, Terms ++ get_ann(Node)).
+
+
+%% @spec copy_ann(Source::cerl(), Target::cerl()) -> cerl()
+%%
+%% @doc Copies the list of user annotations from <code>Source</code>
+%% to <code>Target</code>.
+%%
+%% <p>Note: this is equivalent to <code>set_ann(Target,
+%% get_ann(Source))</code>, but potentially more efficient.</p>
+%%
+%% @see get_ann/1
+%% @see set_ann/2
+
+-spec copy_ann(cerl(), cerl()) -> cerl().
+
+copy_ann(Source, Target) ->
+ set_ann(Target, get_ann(Source)).
+
+
+%% @spec abstract(Term::litval()) -> cerl()
+%%
+%% @doc Creates a syntax tree corresponding to an Erlang term.
+%% <code>Term</code> must be a literal term, i.e., one that can be
+%% represented as a source code literal. Thus, it may not contain a
+%% process identifier, port, reference or function value as a subterm.
+%%
+%% <p>Note: This is a constant time operation.</p>
+%%
+%% @see ann_abstract/2
+%% @see concrete/1
+%% @see is_literal/1
+%% @see is_literal_term/1
+
+-spec abstract(litval()) -> c_literal().
+
+abstract(T) ->
+ #c_literal{val = T}.
+
+
+%% @spec ann_abstract(Annotations::anns(), Term::litval()) -> cerl()
+%% @see abstract/1
+
+-spec ann_abstract(anns(), litval()) -> c_literal().
+
+ann_abstract(As, T) ->
+ #c_literal{val = T, anno = As}.
+
+
+%% @spec is_literal_term(Term::term()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Term</code> can be
+%% represented as a literal, otherwise <code>false</code>. This
+%% function takes time proportional to the size of <code>Term</code>.
+%%
+%% @see abstract/1
+
+-spec is_literal_term(term()) -> boolean().
+
+is_literal_term(T) when is_integer(T) -> true;
+is_literal_term(T) when is_float(T) -> true;
+is_literal_term(T) when is_atom(T) -> true;
+is_literal_term([]) -> true;
+is_literal_term([H | T]) ->
+ is_literal_term(H) andalso is_literal_term(T);
+is_literal_term(T) when is_tuple(T) ->
+ is_literal_term_list(tuple_to_list(T));
+is_literal_term(B) when is_bitstring(B) -> true;
+is_literal_term(M) when is_map(M) ->
+ is_literal_term_list(maps:to_list(M));
+is_literal_term(_) ->
+ false.
+
+-spec is_literal_term_list([term()]) -> boolean().
+
+is_literal_term_list([T | Ts]) ->
+ case is_literal_term(T) of
+ true ->
+ is_literal_term_list(Ts);
+ false ->
+ false
+ end;
+is_literal_term_list([]) ->
+ true.
+
+
+%% @spec concrete(Node::c_literal()) -> litval()
+%%
+%% @doc Returns the Erlang term represented by a syntax tree. An
+%% exception is thrown if <code>Node</code> does not represent a
+%% literal term.
+%%
+%% <p>Note: This is a constant time operation.</p>
+%%
+%% @see abstract/1
+%% @see is_literal/1
+
+%% Because the normal tuple and list constructor operations always
+%% return a literal if the arguments are literals, 'concrete' and
+%% 'is_literal' never need to traverse the structure.
+
+-spec concrete(c_literal()) -> litval().
+
+concrete(#c_literal{val = V}) ->
+ V.
+
+
+%% @spec is_literal(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents a
+%% literal term, otherwise <code>false</code>. This function returns
+%% <code>true</code> if and only if the value of
+%% <code>concrete(Node)</code> is defined.
+%%
+%% <p>Note: This is a constant time operation.</p>
+%%
+%% @see abstract/1
+%% @see concrete/1
+%% @see fold_literal/1
+
+-spec is_literal(cerl()) -> boolean().
+
+is_literal(#c_literal{}) ->
+ true;
+is_literal(_) ->
+ false.
+
+
+%% @spec fold_literal(Node::cerl()) -> cerl()
+%%
+%% @doc Assures that literals have a compact representation. This is
+%% occasionally useful if <code>c_cons_skel/2</code>,
+%% <code>c_tuple_skel/1</code> or <code>unfold_literal/1</code> were
+%% used in the construction of <code>Node</code>, and you want to revert
+%% to the normal "folded" representation of literals. If
+%% <code>Node</code> represents a tuple or list constructor, its
+%% elements are rewritten recursively, and the node is reconstructed
+%% using <code>c_cons/2</code> or <code>c_tuple/1</code>, respectively;
+%% otherwise, <code>Node</code> is not changed.
+%%
+%% @see is_literal/1
+%% @see c_cons_skel/2
+%% @see c_tuple_skel/1
+%% @see c_cons/2
+%% @see c_tuple/1
+%% @see unfold_literal/1
+
+-spec fold_literal(cerl()) -> cerl().
+
+fold_literal(Node) ->
+ case type(Node) of
+ tuple ->
+ update_c_tuple(Node, fold_literal_list(tuple_es(Node)));
+ cons ->
+ update_c_cons(Node, fold_literal(cons_hd(Node)),
+ fold_literal(cons_tl(Node)));
+ _ ->
+ Node
+ end.
+
+fold_literal_list([E | Es]) ->
+ [fold_literal(E) | fold_literal_list(Es)];
+fold_literal_list([]) ->
+ [].
+
+
+%% @spec unfold_literal(Node::cerl()) -> cerl()
+%%
+%% @doc Assures that literals have a fully expanded representation. If
+%% <code>Node</code> represents a literal tuple or list constructor, its
+%% elements are rewritten recursively, and the node is reconstructed
+%% using <code>c_cons_skel/2</code> or <code>c_tuple_skel/1</code>,
+%% respectively; otherwise, <code>Node</code> is not changed. The {@link
+%% fold_literal/1} can be used to revert to the normal compact
+%% representation.
+%%
+%% @see is_literal/1
+%% @see c_cons_skel/2
+%% @see c_tuple_skel/1
+%% @see c_cons/2
+%% @see c_tuple/1
+%% @see fold_literal/1
+
+-spec unfold_literal(cerl()) -> cerl().
+
+unfold_literal(Node) ->
+ case type(Node) of
+ literal ->
+ copy_ann(Node, unfold_concrete(concrete(Node)));
+ _ ->
+ Node
+ end.
+
+unfold_concrete(Val) ->
+ case Val of
+ _ when is_tuple(Val) ->
+ c_tuple_skel(unfold_concrete_list(tuple_to_list(Val)));
+ [H|T] ->
+ c_cons_skel(unfold_concrete(H), unfold_concrete(T));
+ _ ->
+ abstract(Val)
+ end.
+
+unfold_concrete_list([E | Es]) ->
+ [unfold_concrete(E) | unfold_concrete_list(Es)];
+unfold_concrete_list([]) ->
+ [].
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_module(Name::c_literal(), Exports, Definitions) -> c_module()
+%%
+%% Exports = [c_var()]
+%% Definitions = defs()
+%%
+%% @equiv c_module(Name, Exports, [], Definitions)
+
+-spec c_module(c_literal(), [c_var()], defs()) -> c_module().
+
+c_module(Name, Exports, Defs) ->
+ #c_module{name = Name, exports = Exports, attrs = [], defs = Defs}.
+
+
+%% @spec c_module(Name::c_literal(), Exports, Attributes, Definitions) ->
+%% c_module()
+%%
+%% Exports = [c_var()]
+%% Attributes = attrs()
+%% Definitions = defs()
+%%
+%% @doc Creates an abstract module definition. The result represents
+%% <pre>
+%% module <em>Name</em> [<em>E1</em>, ..., <em>Ek</em>]
+%% attributes [<em>K1</em> = <em>T1</em>, ...,
+%% <em>Km</em> = <em>Tm</em>]
+%% <em>V1</em> = <em>F1</em>
+%% ...
+%% <em>Vn</em> = <em>Fn</em>
+%% end</pre>
+%%
+%% if <code>Exports</code> = <code>[E1, ..., Ek]</code>,
+%% <code>Attributes</code> = <code>[{K1, T1}, ..., {Km, Tm}]</code>,
+%% and <code>Definitions</code> = <code>[{V1, F1}, ..., {Vn,
+%% Fn}]</code>.
+%%
+%% <p><code>Name</code> and all the <code>Ki</code> must be atom
+%% literals, and all the <code>Ti</code> must be constant literals. All
+%% the <code>Vi</code> and <code>Ei</code> must have type
+%% <code>var</code> and represent function names. All the
+%% <code>Fi</code> must have type <code>'fun'</code>.</p>
+%%
+%% @see c_module/3
+%% @see module_name/1
+%% @see module_exports/1
+%% @see module_attrs/1
+%% @see module_defs/1
+%% @see module_vars/1
+%% @see ann_c_module/4
+%% @see ann_c_module/5
+%% @see update_c_module/5
+%% @see c_atom/1
+%% @see c_var/1
+%% @see c_fun/2
+%% @see is_literal/1
+
+-spec c_module(c_literal(), [c_var()], attrs(), defs()) -> c_module().
+
+c_module(Name, Exports, Attrs, Defs) ->
+ #c_module{name = Name, exports = Exports, attrs = Attrs, defs = Defs}.
+
+
+%% @spec ann_c_module(As::anns(), Name::c_literal(), Exports,
+%% Definitions) -> c_module()
+%%
+%% Exports = [c_var()]
+%% Definitions = defs()
+%%
+%% @see c_module/3
+%% @see ann_c_module/5
+
+-spec ann_c_module(anns(), c_literal(), [c_var()], defs()) -> c_module().
+
+ann_c_module(As, Name, Exports, Defs) ->
+ #c_module{name = Name, exports = Exports, attrs = [], defs = Defs,
+ anno = As}.
+
+
+%% @spec ann_c_module(As::anns(), Name::c_literal(), Exports,
+%% Attributes, Definitions) -> c_module()
+%%
+%% Exports = [c_var()]
+%% Attributes = attrs()
+%% Definitions = defs()
+%%
+%% @see c_module/4
+%% @see ann_c_module/4
+
+-spec ann_c_module(anns(), c_literal(), [c_var()], attrs(), defs()) ->
+ c_module().
+
+ann_c_module(As, Name, Exports, Attrs, Defs) ->
+ #c_module{name = Name, exports = Exports, attrs = Attrs, defs = Defs,
+ anno = As}.
+
+
+%% @spec update_c_module(Old::cerl(), Name::c_literal(), Exports,
+%% Attributes, Definitions) -> c_module()
+%%
+%% Exports = [c_var()]
+%% Attributes = attrs()
+%% Definitions = defs()
+%%
+%% @see c_module/4
+
+-spec update_c_module(c_module(), c_literal(), [c_var()], attrs(), defs()) ->
+ c_module().
+
+update_c_module(Node, Name, Exports, Attrs, Defs) ->
+ #c_module{name = Name, exports = Exports, attrs = Attrs, defs = Defs,
+ anno = get_ann(Node)}.
+
+
+%% @spec is_c_module(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% module definition, otherwise <code>false</code>.
+%%
+%% @see type/1
+
+-spec is_c_module(cerl()) -> boolean().
+
+is_c_module(#c_module{}) ->
+ true;
+is_c_module(_) ->
+ false.
+
+
+%% @spec module_name(Node::c_module()) -> c_literal()
+%%
+%% @doc Returns the name subtree of an abstract module definition.
+%%
+%% @see c_module/4
+
+-spec module_name(c_module()) -> c_literal().
+
+module_name(Node) ->
+ Node#c_module.name.
+
+
+%% @spec module_exports(Node::c_module()) -> [c_var()]
+%%
+%% @doc Returns the list of exports subtrees of an abstract module
+%% definition.
+%%
+%% @see c_module/4
+
+-spec module_exports(c_module()) -> [c_var()].
+
+module_exports(Node) ->
+ Node#c_module.exports.
+
+
+%% @spec module_attrs(Node::c_module()) -> [{cerl(), cerl()}]
+%%
+%% @doc Returns the list of pairs of attribute key/value subtrees of
+%% an abstract module definition.
+%%
+%% @see c_module/4
+
+-spec module_attrs(c_module()) -> attrs().
+
+module_attrs(Node) ->
+ Node#c_module.attrs.
+
+
+%% @spec module_defs(Node::c_module()) -> defs()
+%%
+%% @doc Returns the list of function definitions of an abstract module
+%% definition.
+%%
+%% @see c_module/4
+
+-spec module_defs(c_module()) -> defs().
+
+module_defs(Node) ->
+ Node#c_module.defs.
+
+
+%% @spec module_vars(Node::c_module()) -> [c_var()]
+%%
+%% @doc Returns the list of left-hand side function variable subtrees
+%% of an abstract module definition.
+%%
+%% @see c_module/4
+
+-spec module_vars(c_module()) -> [c_var()].
+
+module_vars(Node) ->
+ [F || {F, _} <- module_defs(Node)].
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_int(Value::integer()) -> c_literal()
+%%
+%% @doc Creates an abstract integer literal. The lexical
+%% representation is the canonical decimal numeral of
+%% <code>Value</code>.
+%%
+%% @see ann_c_int/2
+%% @see is_c_int/1
+%% @see int_val/1
+%% @see int_lit/1
+%% @see c_char/1
+
+-spec c_int(integer()) -> c_literal().
+
+c_int(Value) ->
+ #c_literal{val = Value}.
+
+
+%% @spec ann_c_int(As::anns(), Value::integer()) -> c_literal()
+%% @see c_int/1
+
+-spec ann_c_int(anns(), integer()) -> c_literal().
+
+ann_c_int(As, Value) ->
+ #c_literal{val = Value, anno = As}.
+
+
+%% @spec is_c_int(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents an
+%% integer literal, otherwise <code>false</code>.
+%% @see c_int/1
+
+-spec is_c_int(cerl()) -> boolean().
+
+is_c_int(#c_literal{val = V}) when is_integer(V) ->
+ true;
+is_c_int(_) ->
+ false.
+
+
+%% @spec int_val(c_literal()) -> integer()
+%%
+%% @doc Returns the value represented by an integer literal node.
+%% @see c_int/1
+
+-spec int_val(c_literal()) -> integer().
+
+int_val(Node) ->
+ Node#c_literal.val.
+
+
+%% @spec int_lit(c_literal()) -> string()
+%%
+%% @doc Returns the numeral string represented by an integer literal
+%% node.
+%% @see c_int/1
+
+-spec int_lit(c_literal()) -> string().
+
+int_lit(Node) ->
+ integer_to_list(int_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_float(Value::float()) -> c_literal()
+%%
+%% @doc Creates an abstract floating-point literal. The lexical
+%% representation is the decimal floating-point numeral of
+%% <code>Value</code>.
+%%
+%% @see ann_c_float/2
+%% @see is_c_float/1
+%% @see float_val/1
+%% @see float_lit/1
+
+%% Note that not all floating-point numerals can be represented with
+%% full precision.
+
+-spec c_float(float()) -> c_literal().
+
+c_float(Value) ->
+ #c_literal{val = Value}.
+
+
+%% @spec ann_c_float(As::anns(), Value::float()) -> c_literal()
+%% @see c_float/1
+
+-spec ann_c_float(anns(), float()) -> c_literal().
+
+ann_c_float(As, Value) ->
+ #c_literal{val = Value, anno = As}.
+
+
+%% @spec is_c_float(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents a
+%% floating-point literal, otherwise <code>false</code>.
+%% @see c_float/1
+
+-spec is_c_float(cerl()) -> boolean().
+
+is_c_float(#c_literal{val = V}) when is_float(V) ->
+ true;
+is_c_float(_) ->
+ false.
+
+
+%% @spec float_val(c_literal()) -> float()
+%%
+%% @doc Returns the value represented by a floating-point literal
+%% node.
+%% @see c_float/1
+
+-spec float_val(c_literal()) -> float().
+
+float_val(Node) ->
+ Node#c_literal.val.
+
+
+%% @spec float_lit(c_literal()) -> string()
+%%
+%% @doc Returns the numeral string represented by a floating-point
+%% literal node.
+%% @see c_float/1
+
+-spec float_lit(c_literal()) -> string().
+
+float_lit(Node) ->
+ float_to_list(float_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_atom(Name) -> c_literal()
+%% Name = atom() | string()
+%%
+%% @doc Creates an abstract atom literal. The print name of the atom
+%% is the character sequence represented by <code>Name</code>.
+%%
+%% <p>Note: passing a string as argument to this function causes a
+%% corresponding atom to be created for the internal representation.</p>
+%%
+%% @see ann_c_atom/2
+%% @see is_c_atom/1
+%% @see atom_val/1
+%% @see atom_name/1
+%% @see atom_lit/1
+
+-spec c_atom(atom() | string()) -> c_literal().
+
+c_atom(Name) when is_atom(Name) ->
+ #c_literal{val = Name};
+c_atom(Name) ->
+ #c_literal{val = list_to_atom(Name)}.
+
+
+%% @spec ann_c_atom(As::anns(), Name) -> cerl()
+%% Name = atom() | string()
+%% @see c_atom/1
+
+-spec ann_c_atom(anns(), atom() | string()) -> c_literal().
+
+ann_c_atom(As, Name) when is_atom(Name) ->
+ #c_literal{val = Name, anno = As};
+ann_c_atom(As, Name) ->
+ #c_literal{val = list_to_atom(Name), anno = As}.
+
+
+%% @spec is_c_atom(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents an
+%% atom literal, otherwise <code>false</code>.
+%%
+%% @see c_atom/1
+
+-spec is_c_atom(cerl()) -> boolean().
+
+is_c_atom(#c_literal{val = V}) when is_atom(V) ->
+ true;
+is_c_atom(_) ->
+ false.
+
+%% @spec atom_val(c_literal()) -> atom()
+%%
+%% @doc Returns the value represented by an abstract atom.
+%%
+%% @see c_atom/1
+
+-spec atom_val(c_literal()) -> atom().
+
+atom_val(Node) ->
+ Node#c_literal.val.
+
+
+%% @spec atom_name(c_literal()) -> string()
+%%
+%% @doc Returns the printname of an abstract atom.
+%%
+%% @see c_atom/1
+
+-spec atom_name(c_literal()) -> string().
+
+atom_name(Node) ->
+ atom_to_list(atom_val(Node)).
+
+
+%% @spec atom_lit(cerl()) -> string()
+%%
+%% @doc Returns the literal string represented by an abstract
+%% atom. This always includes surrounding single-quote characters.
+%%
+%% <p>Note that an abstract atom may have several literal
+%% representations, and that the representation yielded by this
+%% function is not fixed; e.g.,
+%% <code>atom_lit(c_atom("a\012b"))</code> could yield the string
+%% <code>"\'a\\nb\'"</code>.</p>
+%%
+%% @see c_atom/1
+
+%% TODO: replace the use of the unofficial 'write_string/2'.
+
+-spec atom_lit(cerl()) -> nonempty_string().
+
+atom_lit(Node) ->
+ io_lib:write_string(atom_name(Node), $'). %' stupid Emacs.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_char(Value) -> c_literal()
+%%
+%% Value = char() | integer()
+%%
+%% @doc Creates an abstract character literal. If the local
+%% implementation of Erlang defines <code>char()</code> as a subset of
+%% <code>integer()</code>, this function is equivalent to
+%% <code>c_int/1</code>. Otherwise, if the given value is an integer,
+%% it will be converted to the character with the corresponding
+%% code. The lexical representation of a character is
+%% "<code>$<em>Char</em></code>", where <code>Char</code> is a single
+%% printing character or an escape sequence.
+%%
+%% @see c_int/1
+%% @see c_string/1
+%% @see ann_c_char/2
+%% @see is_c_char/1
+%% @see char_val/1
+%% @see char_lit/1
+%% @see is_print_char/1
+
+-spec c_char(non_neg_integer()) -> c_literal().
+
+c_char(Value) when is_integer(Value), Value >= 0 ->
+ #c_literal{val = Value}.
+
+
+%% @spec ann_c_char(As::anns(), Value::char()) -> c_literal()
+%% @see c_char/1
+
+-spec ann_c_char(anns(), char()) -> c_literal().
+
+ann_c_char(As, Value) ->
+ #c_literal{val = Value, anno = As}.
+
+
+%% @spec is_c_char(Node::c_literal()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> may represent a
+%% character literal, otherwise <code>false</code>.
+%%
+%% <p>If the local implementation of Erlang defines
+%% <code>char()</code> as a subset of <code>integer()</code>, then
+%% <code>is_c_int(<em>Node</em>)</code> will also yield
+%% <code>true</code>.</p>
+%%
+%% @see c_char/1
+%% @see is_print_char/1
+
+-spec is_c_char(c_literal()) -> boolean().
+
+is_c_char(#c_literal{val = V}) when is_integer(V), V >= 0 ->
+ is_char_value(V);
+is_c_char(_) ->
+ false.
+
+
+%% @spec is_print_char(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> may represent a
+%% "printing" character, otherwise <code>false</code>. (Cf.
+%% <code>is_c_char/1</code>.) A "printing" character has either a
+%% given graphical representation, or a "named" escape sequence such
+%% as "<code>\n</code>". Currently, only ISO 8859-1 (Latin-1)
+%% character values are recognized.
+%%
+%% @see c_char/1
+%% @see is_c_char/1
+
+-spec is_print_char(cerl()) -> boolean().
+
+is_print_char(#c_literal{val = V}) when is_integer(V), V >= 0 ->
+ is_print_char_value(V);
+is_print_char(_) ->
+ false.
+
+
+%% @spec char_val(c_literal()) -> char()
+%%
+%% @doc Returns the value represented by an abstract character literal.
+%%
+%% @see c_char/1
+
+-spec char_val(c_literal()) -> char().
+
+char_val(Node) ->
+ Node#c_literal.val.
+
+
+%% @spec char_lit(c_literal()) -> string()
+%%
+%% @doc Returns the literal string represented by an abstract
+%% character. This includes a leading <code>$</code>
+%% character. Currently, all characters that are not in the set of ISO
+%% 8859-1 (Latin-1) "printing" characters will be escaped.
+%%
+%% @see c_char/1
+
+-spec char_lit(c_literal()) -> nonempty_string().
+
+char_lit(Node) ->
+ io_lib:write_char(char_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_string(Value::string()) -> c_literal()
+%%
+%% @doc Creates an abstract string literal. Equivalent to creating an
+%% abstract list of the corresponding character literals
+%% (cf. <code>is_c_string/1</code>), but is typically more
+%% efficient. The lexical representation of a string is
+%% "<code>"<em>Chars</em>"</code>", where <code>Chars</code> is a
+%% sequence of printing characters or spaces.
+%%
+%% @see c_char/1
+%% @see ann_c_string/2
+%% @see is_c_string/1
+%% @see string_val/1
+%% @see string_lit/1
+%% @see is_print_string/1
+
+-spec c_string(string()) -> c_literal().
+
+c_string(Value) ->
+ #c_literal{val = Value}.
+
+
+%% @spec ann_c_string(As::anns(), Value::string()) -> c_literal()
+%% @see c_string/1
+
+-spec ann_c_string(anns(), string()) -> c_literal().
+
+ann_c_string(As, Value) ->
+ #c_literal{val = Value, anno = As}.
+
+
+%% @spec is_c_string(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> may represent a
+%% string literal, otherwise <code>false</code>. Strings are defined
+%% as lists of characters; see <code>is_c_char/1</code> for details.
+%%
+%% @see c_string/1
+%% @see is_c_char/1
+%% @see is_print_string/1
+
+-spec is_c_string(cerl()) -> boolean().
+
+is_c_string(#c_literal{val = V}) ->
+ is_char_list(V);
+is_c_string(_) ->
+ false.
+
+
+%% @spec is_print_string(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> may represent a
+%% string literal containing only "printing" characters, otherwise
+%% <code>false</code>. See <code>is_c_string/1</code> and
+%% <code>is_print_char/1</code> for details. Currently, only ISO
+%% 8859-1 (Latin-1) character values are recognized.
+%%
+%% @see c_string/1
+%% @see is_c_string/1
+%% @see is_print_char/1
+
+-spec is_print_string(cerl()) -> boolean().
+
+is_print_string(#c_literal{val = V}) ->
+ is_print_char_list(V);
+is_print_string(_) ->
+ false.
+
+
+%% @spec string_val(cerl()) -> string()
+%%
+%% @doc Returns the value represented by an abstract string literal.
+%%
+%% @see c_string/1
+
+-spec string_val(c_literal()) -> string().
+
+string_val(Node) ->
+ Node#c_literal.val.
+
+
+%% @spec string_lit(cerl()) -> string()
+%%
+%% @doc Returns the literal string represented by an abstract string.
+%% This includes surrounding double-quote characters
+%% <code>"..."</code>. Currently, characters that are not in the set
+%% of ISO 8859-1 (Latin-1) "printing" characters will be escaped,
+%% except for spaces.
+%%
+%% @see c_string/1
+
+-spec string_lit(c_literal()) -> nonempty_string().
+
+string_lit(Node) ->
+ io_lib:write_string(string_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_nil() -> cerl()
+%%
+%% @doc Creates an abstract empty list. The result represents
+%% "<code>[]</code>". The empty list is traditionally called "nil".
+%%
+%% @see ann_c_nil/1
+%% @see is_c_list/1
+%% @see c_cons/2
+
+-spec c_nil() -> c_literal().
+
+c_nil() ->
+ #c_literal{val = []}.
+
+
+%% @spec ann_c_nil(As::anns()) -> cerl()
+%% @see c_nil/0
+
+-spec ann_c_nil(anns()) -> c_literal().
+
+ann_c_nil(As) ->
+ #c_literal{val = [], anno = As}.
+
+
+%% @spec is_c_nil(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% empty list, otherwise <code>false</code>.
+
+-spec is_c_nil(cerl()) -> boolean().
+
+is_c_nil(#c_literal{val = []}) ->
+ true;
+is_c_nil(_) ->
+ false.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_cons(Head::cerl(), Tail::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract list constructor. The result represents
+%% "<code>[<em>Head</em> | <em>Tail</em>]</code>". Note that if both
+%% <code>Head</code> and <code>Tail</code> have type
+%% <code>literal</code>, then the result will also have type
+%% <code>literal</code>, and annotations on <code>Head</code> and
+%% <code>Tail</code> are lost.
+%%
+%% <p>Recall that in Erlang, the tail element of a list constructor is
+%% not necessarily a list.</p>
+%%
+%% @see ann_c_cons/3
+%% @see update_c_cons/3
+%% @see c_cons_skel/2
+%% @see is_c_cons/1
+%% @see cons_hd/1
+%% @see cons_tl/1
+%% @see is_c_list/1
+%% @see c_nil/0
+%% @see list_elements/1
+%% @see list_length/1
+%% @see make_list/2
+
+%% *Always* collapse literals.
+
+-spec c_cons(cerl(), cerl()) -> c_literal() | c_cons().
+
+c_cons(#c_literal{val = Head}, #c_literal{val = Tail}) ->
+ #c_literal{val = [Head | Tail]};
+c_cons(Head, Tail) ->
+ #c_cons{hd = Head, tl = Tail}.
+
+
+%% @spec ann_c_cons(As::anns(), Head::cerl(), Tail::cerl()) -> cerl()
+%% @see c_cons/2
+
+-spec ann_c_cons(anns(), cerl(), cerl()) -> c_literal() | c_cons().
+
+ann_c_cons(As, #c_literal{val = Head}, #c_literal{val = Tail}) ->
+ #c_literal{val = [Head | Tail], anno = As};
+ann_c_cons(As, Head, Tail) ->
+ #c_cons{hd = Head, tl = Tail, anno = As}.
+
+
+%% @spec update_c_cons(Old::cerl(), Head::cerl(), Tail::cerl()) ->
+%% cerl()
+%% @see c_cons/2
+
+-spec update_c_cons(c_literal() | c_cons(), cerl(), cerl()) ->
+ c_literal() | c_cons().
+
+update_c_cons(Node, #c_literal{val = Head}, #c_literal{val = Tail}) ->
+ #c_literal{val = [Head | Tail], anno = get_ann(Node)};
+update_c_cons(Node, Head, Tail) ->
+ #c_cons{hd = Head, tl = Tail, anno = get_ann(Node)}.
+
+
+%% @spec c_cons_skel(Head::cerl(), Tail::cerl()) -> c_cons()
+%%
+%% @doc Creates an abstract list constructor skeleton. Does not fold
+%% constant literals, i.e., the result always has type
+%% <code>cons</code>, representing "<code>[<em>Head</em> |
+%% <em>Tail</em>]</code>".
+%%
+%% <p>This function is occasionally useful when it is necessary to have
+%% annotations on the subnodes of a list constructor node, even when the
+%% subnodes are constant literals. Note however that
+%% <code>is_literal/1</code> will yield <code>false</code> and
+%% <code>concrete/1</code> will fail if passed the result from this
+%% function.</p>
+%%
+%% <p><code>fold_literal/1</code> can be used to revert a node to the
+%% normal-form representation.</p>
+%%
+%% @see ann_c_cons_skel/3
+%% @see update_c_cons_skel/3
+%% @see c_cons/2
+%% @see is_c_cons/1
+%% @see is_c_list/1
+%% @see c_nil/0
+%% @see is_literal/1
+%% @see fold_literal/1
+%% @see concrete/1
+
+%% *Never* collapse literals.
+
+-spec c_cons_skel(cerl(), cerl()) -> c_cons().
+
+c_cons_skel(Head, Tail) ->
+ #c_cons{hd = Head, tl = Tail}.
+
+
+%% @spec ann_c_cons_skel(As::anns(), Head::cerl(), Tail::cerl()) ->
+%% c_cons()
+%% @see c_cons_skel/2
+
+-spec ann_c_cons_skel(anns(), cerl(), cerl()) -> c_cons().
+
+ann_c_cons_skel(As, Head, Tail) ->
+ #c_cons{hd = Head, tl = Tail, anno = As}.
+
+
+%% @spec update_c_cons_skel(Old::cerl(), Head::cerl(), Tail::cerl()) ->
+%% c_cons()
+%% @see c_cons_skel/2
+
+-spec update_c_cons_skel(c_cons() | c_literal(), cerl(), cerl()) -> c_cons().
+
+update_c_cons_skel(Node, Head, Tail) ->
+ #c_cons{hd = Head, tl = Tail, anno = get_ann(Node)}.
+
+
+%% @spec is_c_cons(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% list constructor, otherwise <code>false</code>.
+
+-spec is_c_cons(cerl()) -> boolean().
+
+is_c_cons(#c_cons{}) ->
+ true;
+is_c_cons(#c_literal{val = [_ | _]}) ->
+ true;
+is_c_cons(_) ->
+ false.
+
+
+%% @spec cons_hd(cerl()) -> cerl()
+%%
+%% @doc Returns the head subtree of an abstract list constructor.
+%%
+%% @see c_cons/2
+
+-spec cons_hd(c_cons() | c_literal()) -> cerl().
+
+cons_hd(#c_cons{hd = Head}) ->
+ Head;
+cons_hd(#c_literal{val = [Head | _]}) ->
+ #c_literal{val = Head}.
+
+
+%% @spec cons_tl(c_cons() | c_literal()) -> cerl()
+%%
+%% @doc Returns the tail subtree of an abstract list constructor.
+%%
+%% <p>Recall that the tail does not necessarily represent a proper
+%% list.</p>
+%%
+%% @see c_cons/2
+
+-spec cons_tl(c_cons() | c_literal()) -> cerl().
+
+cons_tl(#c_cons{tl = Tail}) ->
+ Tail;
+cons_tl(#c_literal{val = [_ | Tail]}) ->
+ #c_literal{val = Tail}.
+
+
+%% @spec is_c_list(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents a
+%% proper list, otherwise <code>false</code>. A proper list is either
+%% the empty list <code>[]</code>, or a cons cell <code>[<em>Head</em> |
+%% <em>Tail</em>]</code>, where recursively <code>Tail</code> is a
+%% proper list.
+%%
+%% <p>Note: Because <code>Node</code> is a syntax tree, the actual
+%% run-time values corresponding to its subtrees may often be partially
+%% or completely unknown. Thus, if <code>Node</code> represents e.g.
+%% "<code>[... | Ns]</code>" (where <code>Ns</code> is a variable), then
+%% the function will return <code>false</code>, because it is not known
+%% whether <code>Ns</code> will be bound to a list at run-time. If
+%% <code>Node</code> instead represents e.g. "<code>[1, 2, 3]</code>" or
+%% "<code>[A | []]</code>", then the function will return
+%% <code>true</code>.</p>
+%%
+%% @see c_cons/2
+%% @see c_nil/0
+%% @see list_elements/1
+%% @see list_length/1
+
+-spec is_c_list(cerl()) -> boolean().
+
+is_c_list(#c_cons{tl = Tail}) ->
+ is_c_list(Tail);
+is_c_list(#c_literal{val = V}) ->
+ is_proper_list(V);
+is_c_list(_) ->
+ false.
+
+is_proper_list([_ | Tail]) ->
+ is_proper_list(Tail);
+is_proper_list([]) ->
+ true;
+is_proper_list(_) ->
+ false.
+
+%% @spec list_elements(c_cons() | c_literal()) -> [cerl()]
+%%
+%% @doc Returns the list of element subtrees of an abstract list.
+%% <code>Node</code> must represent a proper list. E.g., if
+%% <code>Node</code> represents "<code>[<em>X1</em>, <em>X2</em> |
+%% [<em>X3</em>, <em>X4</em> | []]</code>", then
+%% <code>list_elements(Node)</code> yields the list <code>[X1, X2, X3,
+%% X4]</code>.
+%%
+%% @see c_cons/2
+%% @see c_nil/0
+%% @see is_c_list/1
+%% @see list_length/1
+%% @see make_list/2
+
+-spec list_elements(c_cons() | c_literal()) -> [cerl()].
+
+list_elements(#c_cons{hd = Head, tl = Tail}) ->
+ [Head | list_elements(Tail)];
+list_elements(#c_literal{val = V}) ->
+ abstract_list(V).
+
+abstract_list([X | Xs]) ->
+ [abstract(X) | abstract_list(Xs)];
+abstract_list([]) ->
+ [].
+
+
+%% @spec list_length(Node::c_cons() | c_literal()) -> integer()
+%%
+%% @doc Returns the number of element subtrees of an abstract list.
+%% <code>Node</code> must represent a proper list. E.g., if
+%% <code>Node</code> represents "<code>[X1 | [X2, X3 | [X4, X5,
+%% X6]]]</code>", then <code>list_length(Node)</code> returns the
+%% integer 6.
+%%
+%% <p>Note: this is equivalent to
+%% <code>length(list_elements(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_cons/2
+%% @see c_nil/0
+%% @see is_c_list/1
+%% @see list_elements/1
+
+-spec list_length(c_cons() | c_literal()) -> non_neg_integer().
+
+list_length(L) ->
+ list_length(L, 0).
+
+list_length(#c_cons{tl = Tail}, A) ->
+ list_length(Tail, A + 1);
+list_length(#c_literal{val = V}, A) ->
+ A + length(V).
+
+
+%% @spec make_list(List) -> Node
+%% @equiv make_list(List, none)
+
+-spec make_list([cerl()]) -> cerl().
+
+make_list(List) ->
+ ann_make_list([], List).
+
+
+%% @spec make_list(List::[cerl()], Tail) -> cerl()
+%%
+%% Tail = cerl() | none
+%%
+%% @doc Creates an abstract list from the elements in <code>List</code>
+%% and the optional <code>Tail</code>. If <code>Tail</code> is
+%% <code>none</code>, the result will represent a nil-terminated list,
+%% otherwise it represents "<code>[... | <em>Tail</em>]</code>".
+%%
+%% @see c_cons/2
+%% @see c_nil/0
+%% @see ann_make_list/3
+%% @see update_list/3
+%% @see list_elements/1
+
+-spec make_list([cerl()], cerl() | 'none') -> cerl().
+
+make_list(List, Tail) ->
+ ann_make_list([], List, Tail).
+
+
+%% @spec update_list(Old::cerl(), List::[cerl()]) -> cerl()
+%% @equiv update_list(Old, List, none)
+
+-spec update_list(cerl(), [cerl()]) -> cerl().
+
+update_list(Node, List) ->
+ ann_make_list(get_ann(Node), List).
+
+
+%% @spec update_list(Old::cerl(), List::[cerl()], Tail) -> cerl()
+%%
+%% Tail = cerl() | none
+%%
+%% @see make_list/2
+%% @see update_list/2
+
+-spec update_list(cerl(), [cerl()], cerl() | 'none') -> cerl().
+
+update_list(Node, List, Tail) ->
+ ann_make_list(get_ann(Node), List, Tail).
+
+
+%% @spec ann_make_list(As::anns(), List::[cerl()]) -> cerl()
+%% @equiv ann_make_list(As, List, none)
+
+-spec ann_make_list(anns(), [cerl()]) -> cerl().
+
+ann_make_list(As, List) ->
+ ann_make_list(As, List, none).
+
+
+%% @spec ann_make_list(As::anns(), List::[cerl()], Tail) -> cerl()
+%%
+%% Tail = cerl() | none
+%%
+%% @see make_list/2
+%% @see ann_make_list/2
+
+-spec ann_make_list(anns(), [cerl()], cerl() | 'none') -> cerl().
+
+ann_make_list(As, [H | T], Tail) ->
+ ann_c_cons(As, H, make_list(T, Tail)); % `c_cons' folds literals
+ann_make_list(As, [], none) ->
+ ann_c_nil(As);
+ann_make_list(_, [], Node) ->
+ Node.
+
+
+%% ---------------------------------------------------------------------
+%% maps
+
+%% @spec is_c_map(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% map constructor, otherwise <code>false</code>.
+
+-spec is_c_map(cerl()) -> boolean().
+
+is_c_map(#c_map{}) ->
+ true;
+is_c_map(#c_literal{val = V}) when is_map(V) ->
+ true;
+is_c_map(_) ->
+ false.
+
+-spec map_es(c_map() | c_literal()) -> [c_map_pair()].
+
+map_es(#c_literal{anno=As,val=M}) when is_map(M) ->
+ [ann_c_map_pair(As,
+ #c_literal{anno=As,val='assoc'},
+ #c_literal{anno=As,val=K},
+ #c_literal{anno=As,val=V}) || {K,V} <- maps:to_list(M)];
+map_es(#c_map{es = Es}) ->
+ Es.
+
+-spec map_arg(c_map() | c_literal()) -> c_map() | c_literal().
+
+map_arg(#c_literal{anno=As,val=M}) when is_map(M) ->
+ #c_literal{anno=As,val=#{}};
+map_arg(#c_map{arg=M}) ->
+ M.
+
+-spec c_map([c_map_pair()]) -> c_map().
+
+c_map(Pairs) ->
+ ann_c_map([], Pairs).
+
+-spec c_map_pattern([c_map_pair()]) -> c_map().
+
+c_map_pattern(Pairs) ->
+ #c_map{es=Pairs, is_pat=true}.
+
+-spec ann_c_map_pattern([term()], [c_map_pair()]) -> c_map().
+
+ann_c_map_pattern(As, Pairs) ->
+ #c_map{anno=As, es=Pairs, is_pat=true}.
+
+-spec is_c_map_empty(c_map() | c_literal()) -> boolean().
+
+is_c_map_empty(#c_map{ es=[] }) -> true;
+is_c_map_empty(#c_literal{val=M}) when is_map(M),map_size(M) =:= 0 -> true;
+is_c_map_empty(_) -> false.
+
+-spec is_c_map_pattern(c_map()) -> boolean().
+
+is_c_map_pattern(#c_map{is_pat=IsPat}) ->
+ IsPat.
+
+-spec ann_c_map([term()], [c_map_pair()]) -> c_map() | c_literal().
+
+ann_c_map(As, Es) ->
+ ann_c_map(As, #c_literal{val=#{}}, Es).
+
+-spec ann_c_map(anns(), c_map() | c_literal(), [c_map_pair()]) -> c_map() | c_literal().
+
+ann_c_map(As, #c_literal{val=M}, Es) when is_map(M) ->
+ fold_map_pairs(As,Es,M);
+ann_c_map(As, M, Es) ->
+ #c_map{arg=M, es=Es, anno=As}.
+
+fold_map_pairs(As,[],M) -> #c_literal{anno=As,val=M};
+%% M#{ K => V}
+fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=assoc},key=Ck,val=Cv}=E|Es],M) ->
+ case is_lit_list([Ck,Cv]) of
+ true ->
+ [K,V] = lit_list_vals([Ck,Cv]),
+ fold_map_pairs(As,Es,maps:put(K,V,M));
+ false ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As}
+ end;
+%% M#{ K := V}
+fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=exact},key=Ck,val=Cv}=E|Es],M) ->
+ case is_lit_list([Ck,Cv]) of
+ true ->
+ [K,V] = lit_list_vals([Ck,Cv]),
+ case maps:is_key(K,M) of
+ true -> fold_map_pairs(As,Es,maps:put(K,V,M));
+ false ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As }
+ end;
+ false ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As }
+ end.
+
+-spec update_c_map(c_map(), cerl(), [cerl()]) -> c_map() | c_literal().
+
+update_c_map(#c_map{is_pat=true}=Old, M, Es) ->
+ Old#c_map{arg=M, es=Es};
+update_c_map(#c_map{is_pat=false}=Old, M, Es) ->
+ ann_c_map(get_ann(Old), M, Es).
+
+map_pair_key(#c_map_pair{key=K}) -> K.
+map_pair_val(#c_map_pair{val=V}) -> V.
+map_pair_op(#c_map_pair{op=Op}) -> Op.
+
+-spec c_map_pair(cerl(), cerl()) -> c_map_pair().
+
+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(anns(), cerl(), cerl(), cerl()) ->
+ c_map_pair().
+
+ann_c_map_pair(As,Op,K,V) ->
+ #c_map_pair{op = Op, key = K, val = V, anno = As}.
+
+update_c_map_pair(Old,Op,K,V) ->
+ #c_map_pair{op = Op, key = K, val = V, anno = get_ann(Old)}.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_tuple(Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract tuple. If <code>Elements</code> is
+%% <code>[E1, ..., En]</code>, the result represents
+%% "<code>{<em>E1</em>, ..., <em>En</em>}</code>". Note that if all
+%% nodes in <code>Elements</code> have type <code>literal</code>, or if
+%% <code>Elements</code> is empty, then the result will also have type
+%% <code>literal</code> and annotations on nodes in
+%% <code>Elements</code> are lost.
+%%
+%% <p>Recall that Erlang has distinct 1-tuples, i.e., <code>{X}</code>
+%% is always distinct from <code>X</code> itself.</p>
+%%
+%% @see ann_c_tuple/2
+%% @see update_c_tuple/2
+%% @see is_c_tuple/1
+%% @see tuple_es/1
+%% @see tuple_arity/1
+%% @see c_tuple_skel/1
+
+%% *Always* collapse literals.
+
+-spec c_tuple([cerl()]) -> c_tuple() | c_literal().
+
+c_tuple(Es) ->
+ case is_lit_list(Es) of
+ false ->
+ #c_tuple{es = Es};
+ true ->
+ #c_literal{val = list_to_tuple(lit_list_vals(Es))}
+ end.
+
+
+%% @spec ann_c_tuple(As::anns(), Elements::[cerl()]) -> cerl()
+%% @see c_tuple/1
+
+-spec ann_c_tuple(anns(), [cerl()]) -> c_tuple() | c_literal().
+
+ann_c_tuple(As, Es) ->
+ case is_lit_list(Es) of
+ false ->
+ #c_tuple{es = Es, anno = As};
+ true ->
+ #c_literal{val = list_to_tuple(lit_list_vals(Es)), anno = As}
+ end.
+
+
+%% @spec update_c_tuple(Old::cerl(), Elements::[cerl()]) -> cerl()
+%% @see c_tuple/1
+
+-spec update_c_tuple(c_tuple() | c_literal(), [cerl()]) -> c_tuple() | c_literal().
+
+update_c_tuple(Node, Es) ->
+ case is_lit_list(Es) of
+ false ->
+ #c_tuple{es = Es, anno = get_ann(Node)};
+ true ->
+ #c_literal{val = list_to_tuple(lit_list_vals(Es)),
+ anno = get_ann(Node)}
+ end.
+
+
+%% @spec c_tuple_skel(Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract tuple skeleton. Does not fold constant
+%% literals, i.e., the result always has type <code>tuple</code>,
+%% representing "<code>{<em>E1</em>, ..., <em>En</em>}</code>", if
+%% <code>Elements</code> is <code>[E1, ..., En]</code>.
+%%
+%% <p>This function is occasionally useful when it is necessary to have
+%% annotations on the subnodes of a tuple node, even when all the
+%% subnodes are constant literals. Note however that
+%% <code>is_literal/1</code> will yield <code>false</code> and
+%% <code>concrete/1</code> will fail if passed the result from this
+%% function.</p>
+%%
+%% <p><code>fold_literal/1</code> can be used to revert a node to the
+%% normal-form representation.</p>
+%%
+%% @see ann_c_tuple_skel/2
+%% @see update_c_tuple_skel/2
+%% @see c_tuple/1
+%% @see tuple_es/1
+%% @see is_c_tuple/1
+%% @see is_literal/1
+%% @see fold_literal/1
+%% @see concrete/1
+
+%% *Never* collapse literals.
+
+-spec c_tuple_skel([cerl()]) -> c_tuple().
+
+c_tuple_skel(Es) ->
+ #c_tuple{es = Es}.
+
+
+%% @spec ann_c_tuple_skel(As::anns(), Elements::[cerl()]) -> cerl()
+%% @see c_tuple_skel/1
+
+-spec ann_c_tuple_skel(anns(), [cerl()]) -> c_tuple().
+
+ann_c_tuple_skel(As, Es) ->
+ #c_tuple{es = Es, anno = As}.
+
+
+%% @spec update_c_tuple_skel(Old::cerl(), Elements::[cerl()]) -> cerl()
+%% @see c_tuple_skel/1
+
+-spec update_c_tuple_skel(c_tuple(), [cerl()]) -> c_tuple().
+
+update_c_tuple_skel(Old, Es) ->
+ #c_tuple{es = Es, anno = get_ann(Old)}.
+
+
+%% @spec is_c_tuple(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% tuple, otherwise <code>false</code>.
+%%
+%% @see c_tuple/1
+
+-spec is_c_tuple(cerl()) -> boolean().
+
+is_c_tuple(#c_tuple{}) ->
+ true;
+is_c_tuple(#c_literal{val = V}) when is_tuple(V) ->
+ true;
+is_c_tuple(_) ->
+ false.
+
+
+%% @spec tuple_es(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of element subtrees of an abstract tuple.
+%%
+%% @see c_tuple/1
+
+-spec tuple_es(c_tuple() | c_literal()) -> [cerl()].
+
+tuple_es(#c_tuple{es = Es}) ->
+ Es;
+tuple_es(#c_literal{val = V}) ->
+ make_lit_list(tuple_to_list(V)).
+
+
+%% @spec tuple_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of element subtrees of an abstract tuple.
+%%
+%% <p>Note: this is equivalent to <code>length(tuple_es(Node))</code>,
+%% but potentially more efficient.</p>
+%%
+%% @see tuple_es/1
+%% @see c_tuple/1
+
+-spec tuple_arity(c_tuple() | c_literal()) -> non_neg_integer().
+
+tuple_arity(#c_tuple{es = Es}) ->
+ length(Es);
+tuple_arity(#c_literal{val = V}) when is_tuple(V) ->
+ tuple_size(V).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_var(Name::var_name()) -> cerl()
+%%
+%% var_name() = integer() | atom() | {atom(), arity()}
+%%
+%% @doc Creates an abstract variable. A variable is identified by its
+%% name, given by the <code>Name</code> parameter.
+%%
+%% <p>If a name is given by a single atom, it should either be a
+%% "simple" atom which does not need to be single-quoted in Erlang, or
+%% otherwise its print name should correspond to a proper Erlang
+%% variable, i.e., begin with an uppercase character or an
+%% underscore. Names on the form <code>{A, N}</code> represent
+%% function name variables "<code><em>A</em>/<em>N</em></code>"; these
+%% are special variables which may be bound only in the function
+%% definitions of a module or a <code>letrec</code>. They may not be
+%% bound in <code>let</code> expressions and cannot occur in clause
+%% patterns. The atom <code>A</code> in a function name may be any
+%% atom; the integer <code>N</code> must be nonnegative. The functions
+%% <code>c_fname/2</code> etc. are utilities for handling function
+%% name variables.</p>
+%%
+%% <p>When printing variable names, they must have the form of proper
+%% Core Erlang variables and function names. E.g., a name represented
+%% by an integer such as <code>42</code> could be formatted as
+%% "<code>_42</code>", an atom <code>'Xxx'</code> simply as
+%% "<code>Xxx</code>", and an atom <code>foo</code> as
+%% "<code>_foo</code>". However, one must assure that any two valid
+%% distinct names are never mapped to the same strings. Tuples such
+%% as <code>{foo, 2}</code> representing function names can simply by
+%% formatted as "<code>'foo'/2</code>", with no risk of conflicts.</p>
+%%
+%% @see ann_c_var/2
+%% @see update_c_var/2
+%% @see is_c_var/1
+%% @see var_name/1
+%% @see c_fname/2
+%% @see c_module/4
+%% @see c_letrec/2
+
+-spec c_var(var_name()) -> c_var().
+
+c_var(Name) ->
+ #c_var{name = Name}.
+
+
+%% @spec ann_c_var(As::anns(), Name::var_name()) -> c_var()
+%%
+%% @see c_var/1
+
+-spec ann_c_var(anns(), var_name()) -> c_var().
+
+ann_c_var(As, Name) ->
+ #c_var{name = Name, anno = As}.
+
+%% @spec update_c_var(Old::cerl(), Name::var_name()) -> c_var()
+%%
+%% @see c_var/1
+
+-spec update_c_var(c_var(), var_name()) -> c_var().
+
+update_c_var(Node, Name) ->
+ #c_var{name = Name, anno = get_ann(Node)}.
+
+
+%% @spec is_c_var(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% variable, otherwise <code>false</code>.
+%%
+%% @see c_var/1
+
+-spec is_c_var(cerl()) -> boolean().
+
+is_c_var(#c_var{}) ->
+ true;
+is_c_var(_) ->
+ false.
+
+
+%% @spec c_fname(Name::atom(), Arity::arity()) -> c_var()
+%% @equiv c_var({Name, Arity})
+%% @see fname_id/1
+%% @see fname_arity/1
+%% @see is_c_fname/1
+%% @see ann_c_fname/3
+%% @see update_c_fname/3
+
+-spec c_fname(atom(), arity()) -> c_var().
+
+c_fname(Atom, Arity) ->
+ c_var({Atom, Arity}).
+
+
+%% @spec ann_c_fname(As::anns(), Name::atom(), Arity::arity()) -> c_var()
+%%
+%% @equiv ann_c_var(As, {Atom, Arity})
+%% @see c_fname/2
+
+-spec ann_c_fname(anns(), atom(), arity()) -> c_var().
+
+ann_c_fname(As, Atom, Arity) ->
+ ann_c_var(As, {Atom, Arity}).
+
+
+%% @spec update_c_fname(Old::c_var(), Name::atom()) -> c_var()
+%% @doc Like <code>update_c_fname/3</code>, but takes the arity from
+%% <code>Node</code>.
+%% @see update_c_fname/3
+%% @see c_fname/2
+
+-spec update_c_fname(c_var(), atom()) -> c_var().
+
+update_c_fname(#c_var{name = {_, Arity}, anno = As}, Atom) ->
+ #c_var{name = {Atom, Arity}, anno = As}.
+
+
+%% @spec update_c_fname(Old::var(), Name::atom(), Arity::arity()) -> c_var()
+%%
+%% @equiv update_c_var(Old, {Atom, Arity})
+%% @see update_c_fname/2
+%% @see c_fname/2
+
+-spec update_c_fname(c_var(), atom(), arity()) -> c_var().
+
+update_c_fname(Node, Atom, Arity) ->
+ update_c_var(Node, {Atom, Arity}).
+
+
+%% @spec is_c_fname(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% function name variable, otherwise <code>false</code>.
+%%
+%% @see c_fname/2
+%% @see c_var/1
+%% @see var_name/1
+
+-spec is_c_fname(cerl()) -> boolean().
+
+is_c_fname(#c_var{name = {A, N}}) when is_atom(A), is_integer(N), N >= 0 ->
+ true;
+is_c_fname(_) ->
+ false.
+
+
+%% @spec var_name(c_var()) -> var_name()
+%%
+%% @doc Returns the name of an abstract variable.
+%%
+%% @see c_var/1
+
+-spec var_name(c_var()) -> var_name().
+
+var_name(Node) ->
+ Node#c_var.name.
+
+
+%% @spec fname_id(c_var()) -> atom()
+%%
+%% @doc Returns the identifier part of an abstract function name
+%% variable.
+%%
+%% @see fname_arity/1
+%% @see c_fname/2
+
+-spec fname_id(c_var()) -> atom().
+
+fname_id(#c_var{name={A,_}}) ->
+ A.
+
+
+%% @spec fname_arity(c_var()) -> arity()
+%%
+%% @doc Returns the arity part of an abstract function name variable.
+%%
+%% @see fname_id/1
+%% @see c_fname/2
+
+-spec fname_arity(c_var()) -> arity().
+
+fname_arity(#c_var{name={_,N}}) ->
+ N.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_values(Elements::[cerl()]) -> c_values()
+%%
+%% @doc Creates an abstract value list. If <code>Elements</code> is
+%% <code>[E1, ..., En]</code>, the result represents
+%% "<code>&lt;<em>E1</em>, ..., <em>En</em>&gt;</code>".
+%%
+%% @see ann_c_values/2
+%% @see update_c_values/2
+%% @see is_c_values/1
+%% @see values_es/1
+%% @see values_arity/1
+
+-spec c_values([cerl()]) -> c_values().
+
+c_values(Es) ->
+ #c_values{es = Es}.
+
+
+%% @spec ann_c_values(As::anns(), Elements::[cerl()]) -> c_values()
+%% @see c_values/1
+
+-spec ann_c_values(anns(), [cerl()]) -> c_values().
+
+ann_c_values(As, Es) ->
+ #c_values{es = Es, anno = As}.
+
+
+%% @spec update_c_values(Old::cerl(), Elements::[cerl()]) -> c_values()
+%% @see c_values/1
+
+-spec update_c_values(c_values(), [cerl()]) -> c_values().
+
+update_c_values(Node, Es) ->
+ #c_values{es = Es, anno = get_ann(Node)}.
+
+
+%% @spec is_c_values(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% value list; otherwise <code>false</code>.
+%%
+%% @see c_values/1
+
+-spec is_c_values(cerl()) -> boolean().
+
+is_c_values(#c_values{}) ->
+ true;
+is_c_values(_) ->
+ false.
+
+
+%% @spec values_es(c_values()) -> [cerl()]
+%%
+%% @doc Returns the list of element subtrees of an abstract value
+%% list.
+%%
+%% @see c_values/1
+%% @see values_arity/1
+
+-spec values_es(c_values()) -> [cerl()].
+
+values_es(Node) ->
+ Node#c_values.es.
+
+
+%% @spec values_arity(Node::c_values()) -> non_neg_integer()
+%%
+%% @doc Returns the number of element subtrees of an abstract value
+%% list.
+%%
+%% <p>Note: This is equivalent to
+%% <code>length(values_es(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_values/1
+%% @see values_es/1
+
+-spec values_arity(c_values()) -> non_neg_integer().
+
+values_arity(Node) ->
+ length(values_es(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_binary(Segments::[c_bitstr()]) -> c_binary()
+%%
+%% @doc Creates an abstract binary-template. A binary object is a
+%% sequence of 8-bit bytes. It is specified by zero or more bit-string
+%% template <em>segments</em> of arbitrary lengths (in number of bits),
+%% such that the sum of the lengths is evenly divisible by 8. If
+%% <code>Segments</code> is <code>[S1, ..., Sn]</code>, the result
+%% represents "<code>#{<em>S1</em>, ..., <em>Sn</em>}#</code>". All the
+%% <code>Si</code> must have type <code>bitstr</code>.
+%%
+%% @see ann_c_binary/2
+%% @see update_c_binary/2
+%% @see is_c_binary/1
+%% @see binary_segments/1
+%% @see c_bitstr/5
+
+-spec c_binary([c_bitstr()]) -> c_binary().
+
+c_binary(Segments) ->
+ #c_binary{segments = Segments}.
+
+
+%% @spec ann_c_binary(As::anns(), Segments::[c_bitstr()]) -> c_binary()
+%% @see c_binary/1
+
+-spec ann_c_binary(anns(), [c_bitstr()]) -> c_binary().
+
+ann_c_binary(As, Segments) ->
+ #c_binary{segments = Segments, anno = As}.
+
+
+%% @spec update_c_binary(Old::cerl(), Segments::[c_bitstr()]) -> cerl()
+%% @see c_binary/1
+
+-spec update_c_binary(c_binary(), [c_bitstr()]) -> c_binary().
+
+update_c_binary(Node, Segments) ->
+ #c_binary{segments = Segments, anno = get_ann(Node)}.
+
+
+%% @spec is_c_binary(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% binary-template; otherwise <code>false</code>.
+%%
+%% @see c_binary/1
+
+-spec is_c_binary(cerl()) -> boolean().
+
+is_c_binary(#c_binary{}) ->
+ true;
+is_c_binary(_) ->
+ false.
+
+
+%% @spec binary_segments(cerl()) -> [c_bitstr()]
+%%
+%% @doc Returns the list of segment subtrees of an abstract
+%% binary-template.
+%%
+%% @see c_binary/1
+%% @see c_bitstr/5
+
+-spec binary_segments(c_binary()) -> [c_bitstr()].
+
+binary_segments(Node) ->
+ Node#c_binary.segments.
+
+
+%% @spec c_bitstr(Value::cerl(), Size::cerl(), Unit::cerl(),
+%% Type::cerl(), Flags::cerl()) -> c_bitstr()
+%%
+%% @doc Creates an abstract bit-string template. These can only occur as
+%% components of an abstract binary-template (see {@link c_binary/1}).
+%% The result represents "<code>#&lt;<em>Value</em>&gt;(<em>Size</em>,
+%% <em>Unit</em>, <em>Type</em>, <em>Flags</em>)</code>", where
+%% <code>Unit</code> must represent a positive integer constant,
+%% <code>Type</code> must represent a constant atom (one of
+%% <code>'integer'</code>, <code>'float'</code>, or
+%% <code>'binary'</code>), and <code>Flags</code> must represent a
+%% constant list <code>"[<em>F1</em>, ..., <em>Fn</em>]"</code> where
+%% all the <code>Fi</code> are atoms.
+%%
+%% @see c_binary/1
+%% @see ann_c_bitstr/6
+%% @see update_c_bitstr/6
+%% @see is_c_bitstr/1
+%% @see bitstr_val/1
+%% @see bitstr_size/1
+%% @see bitstr_unit/1
+%% @see bitstr_type/1
+%% @see bitstr_flags/1
+
+-spec c_bitstr(cerl(), cerl(), cerl(), cerl(), cerl()) -> c_bitstr().
+
+c_bitstr(Val, Size, Unit, Type, Flags) ->
+ #c_bitstr{val = Val, size = Size, unit = Unit, type = Type,
+ flags = Flags}.
+
+
+%% @spec c_bitstr(Value::cerl(), Size::cerl(), Type::cerl(),
+%% Flags::cerl()) -> c_bitstr()
+%% @equiv c_bitstr(Value, Size, abstract(1), Type, Flags)
+
+-spec c_bitstr(cerl(), cerl(), cerl(), cerl()) -> c_bitstr().
+
+c_bitstr(Val, Size, Type, Flags) ->
+ c_bitstr(Val, Size, abstract(1), Type, Flags).
+
+
+%% @spec c_bitstr(Value::cerl(), Type::cerl(),
+%% Flags::cerl()) -> c_bitstr()
+%% @equiv c_bitstr(Value, abstract(all), abstract(1), Type, Flags)
+
+-spec c_bitstr(cerl(), cerl(), cerl()) -> c_bitstr().
+
+c_bitstr(Val, Type, Flags) ->
+ c_bitstr(Val, abstract(all), abstract(1), Type, Flags).
+
+
+%% @spec ann_c_bitstr(As::anns(), Value::cerl(), Size::cerl(),
+%% Unit::cerl(), Type::cerl(), Flags::cerl()) -> cerl()
+%% @see c_bitstr/5
+%% @see ann_c_bitstr/5
+
+-spec ann_c_bitstr(anns(), cerl(), cerl(), cerl(), cerl(), cerl()) ->
+ c_bitstr().
+
+ann_c_bitstr(As, Val, Size, Unit, Type, Flags) ->
+ #c_bitstr{val = Val, size = Size, unit = Unit, type = Type,
+ flags = Flags, anno = As}.
+
+%% @spec ann_c_bitstr(As::anns(), Value::cerl(), Size::cerl(),
+%% Type::cerl(), Flags::cerl()) -> c_bitstr()
+%% @equiv ann_c_bitstr(As, Value, Size, abstract(1), Type, Flags)
+
+-spec ann_c_bitstr(anns(), cerl(), cerl(), cerl(), cerl()) -> c_bitstr().
+
+ann_c_bitstr(As, Value, Size, Type, Flags) ->
+ ann_c_bitstr(As, Value, Size, abstract(1), Type, Flags).
+
+
+%% @spec update_c_bitstr(Old::c_bitstr(), Value::cerl(), Size::cerl(),
+%% Unit::cerl(), Type::cerl(), Flags::cerl()) -> c_bitstr()
+%% @see c_bitstr/5
+%% @see update_c_bitstr/5
+
+-spec update_c_bitstr(c_bitstr(), cerl(), cerl(), cerl(), cerl(), cerl()) ->
+ c_bitstr().
+
+update_c_bitstr(Node, Val, Size, Unit, Type, Flags) ->
+ #c_bitstr{val = Val, size = Size, unit = Unit, type = Type,
+ flags = Flags, anno = get_ann(Node)}.
+
+
+%% @spec update_c_bitstr(Old::c_bitstr(), Value::cerl(), Size::cerl(),
+%% Type::cerl(), Flags::cerl()) -> c_bitstr()
+%% @equiv update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags)
+
+-spec update_c_bitstr(c_bitstr(), cerl(), cerl(), cerl(), cerl()) -> c_bitstr().
+
+update_c_bitstr(Node, Value, Size, Type, Flags) ->
+ update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags).
+
+%% @spec is_c_bitstr(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% bit-string template; otherwise <code>false</code>.
+%%
+%% @see c_bitstr/5
+
+-spec is_c_bitstr(cerl()) -> boolean().
+
+is_c_bitstr(#c_bitstr{}) ->
+ true;
+is_c_bitstr(_) ->
+ false.
+
+
+%% @spec bitstr_val(c_bitstr()) -> cerl()
+%%
+%% @doc Returns the value subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+-spec bitstr_val(c_bitstr()) -> cerl().
+
+bitstr_val(Node) ->
+ Node#c_bitstr.val.
+
+
+%% @spec bitstr_size(c_bitstr()) -> cerl()
+%%
+%% @doc Returns the size subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+-spec bitstr_size(c_bitstr()) -> cerl().
+
+bitstr_size(Node) ->
+ Node#c_bitstr.size.
+
+
+%% @spec bitstr_bitsize(c_bitstr()) -> any | all | utf | integer()
+%%
+%% @doc Returns the total size in bits of an abstract bit-string
+%% template. If the size field is an integer literal, the result is the
+%% product of the size and unit values; if the size field is the atom
+%% literal <code>all</code>, the atom <code>all</code> is returned.
+%% If the size is not a literal, the atom <code>any</code> is returned.
+%%
+%% @see c_bitstr/5
+
+-spec bitstr_bitsize(c_bitstr()) -> 'all' | 'any' | 'utf' | non_neg_integer().
+
+bitstr_bitsize(Node) ->
+ Size = Node#c_bitstr.size,
+ case is_literal(Size) of
+ true ->
+ case concrete(Size) of
+ all ->
+ all;
+ undefined ->
+ %% just an assertion below
+ "utf" ++ _ = atom_to_list(concrete(Node#c_bitstr.type)),
+ utf;
+ S when is_integer(S) ->
+ S * concrete(Node#c_bitstr.unit)
+ end;
+ false ->
+ any
+ end.
+
+
+%% @spec bitstr_unit(c_bitstr()) -> cerl()
+%%
+%% @doc Returns the unit subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+-spec bitstr_unit(c_bitstr()) -> cerl().
+
+bitstr_unit(Node) ->
+ Node#c_bitstr.unit.
+
+
+%% @spec bitstr_type(c_bitstr()) -> cerl()
+%%
+%% @doc Returns the type subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+-spec bitstr_type(c_bitstr()) -> cerl().
+
+bitstr_type(Node) ->
+ Node#c_bitstr.type.
+
+
+%% @spec bitstr_flags(c_bitstr()) -> cerl()
+%%
+%% @doc Returns the flags subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+-spec bitstr_flags(c_bitstr()) -> cerl().
+
+bitstr_flags(Node) ->
+ Node#c_bitstr.flags.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_fun(Variables::[c_var()], Body::cerl()) -> c_fun()
+%%
+%% @doc Creates an abstract fun-expression. If <code>Variables</code>
+%% is <code>[V1, ..., Vn]</code>, the result represents "<code>fun
+%% (<em>V1</em>, ..., <em>Vn</em>) -> <em>Body</em></code>". All the
+%% <code>Vi</code> must have type <code>var</code>.
+%%
+%% @see ann_c_fun/3
+%% @see update_c_fun/3
+%% @see is_c_fun/1
+%% @see fun_vars/1
+%% @see fun_body/1
+%% @see fun_arity/1
+
+-spec c_fun([c_var()], cerl()) -> c_fun().
+
+c_fun(Variables, Body) ->
+ #c_fun{vars = Variables, body = Body}.
+
+
+%% @spec ann_c_fun(As::anns(), Variables::[c_var()], Body::cerl()) ->
+%% c_fun()
+%% @see c_fun/2
+
+-spec ann_c_fun(anns(), [c_var()], cerl()) -> c_fun().
+
+ann_c_fun(As, Variables, Body) ->
+ #c_fun{vars = Variables, body = Body, anno = As}.
+
+
+%% @spec update_c_fun(Old::c_fun(), Variables::[c_var()],
+%% Body::cerl()) -> c_fun()
+%% @see c_fun/2
+
+-spec update_c_fun(c_fun(), [c_var()], cerl()) -> c_fun().
+
+update_c_fun(Node, Variables, Body) ->
+ #c_fun{vars = Variables, body = Body, anno = get_ann(Node)}.
+
+
+%% @spec is_c_fun(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% fun-expression, otherwise <code>false</code>.
+%%
+%% @see c_fun/2
+
+-spec is_c_fun(cerl()) -> boolean().
+
+is_c_fun(#c_fun{}) ->
+ true; % Now this is fun!
+is_c_fun(_) ->
+ false.
+
+
+%% @spec fun_vars(c_fun()) -> [c_var()]
+%%
+%% @doc Returns the list of parameter subtrees of an abstract
+%% fun-expression.
+%%
+%% @see c_fun/2
+%% @see fun_arity/1
+
+-spec fun_vars(c_fun()) -> [c_var()].
+
+fun_vars(Node) ->
+ Node#c_fun.vars.
+
+
+%% @spec fun_body(c_fun()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract fun-expression.
+%%
+%% @see c_fun/2
+
+-spec fun_body(c_fun()) -> cerl().
+
+fun_body(Node) ->
+ Node#c_fun.body.
+
+
+%% @spec fun_arity(Node::c_fun()) -> arity()
+%%
+%% @doc Returns the number of parameter subtrees of an abstract
+%% fun-expression.
+%%
+%% <p>Note: this is equivalent to <code>length(fun_vars(Node))</code>,
+%% but potentially more efficient.</p>
+%%
+%% @see c_fun/2
+%% @see fun_vars/1
+
+-spec fun_arity(c_fun()) -> arity().
+
+fun_arity(Node) ->
+ length(fun_vars(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_seq(Argument::cerl(), Body::cerl()) -> c_seq()
+%%
+%% @doc Creates an abstract sequencing expression. The result
+%% represents "<code>do <em>Argument</em> <em>Body</em></code>".
+%%
+%% @see ann_c_seq/3
+%% @see update_c_seq/3
+%% @see is_c_seq/1
+%% @see seq_arg/1
+%% @see seq_body/1
+
+-spec c_seq(cerl(), cerl()) -> c_seq().
+
+c_seq(Argument, Body) ->
+ #c_seq{arg = Argument, body = Body}.
+
+
+%% @spec ann_c_seq(As::anns(), Argument::cerl(), Body::cerl()) -> c_seq()
+%%
+%% @see c_seq/2
+
+-spec ann_c_seq(anns(), cerl(), cerl()) -> c_seq().
+
+ann_c_seq(As, Argument, Body) ->
+ #c_seq{arg = Argument, body = Body, anno = As}.
+
+
+%% @spec update_c_seq(Old::c_seq(), Argument::cerl(), Body::cerl()) ->
+%% c_seq()
+%% @see c_seq/2
+
+-spec update_c_seq(c_seq(), cerl(), cerl()) -> c_seq().
+
+update_c_seq(Node, Argument, Body) ->
+ #c_seq{arg = Argument, body = Body, anno = get_ann(Node)}.
+
+
+%% @spec is_c_seq(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% sequencing expression, otherwise <code>false</code>.
+%%
+%% @see c_seq/2
+
+-spec is_c_seq(cerl()) -> boolean().
+
+is_c_seq(#c_seq{}) ->
+ true;
+is_c_seq(_) ->
+ false.
+
+
+%% @spec seq_arg(c_seq()) -> cerl()
+%%
+%% @doc Returns the argument subtree of an abstract sequencing
+%% expression.
+%%
+%% @see c_seq/2
+
+-spec seq_arg(c_seq()) -> cerl().
+
+seq_arg(Node) ->
+ Node#c_seq.arg.
+
+
+%% @spec seq_body(c_seq()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract sequencing expression.
+%%
+%% @see c_seq/2
+
+-spec seq_body(c_seq()) -> cerl().
+
+seq_body(Node) ->
+ Node#c_seq.body.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_let(Variables::[c_var()], Argument::cerl(), Body::cerl()) ->
+%% c_let()
+%%
+%% @doc Creates an abstract let-expression. If <code>Variables</code>
+%% is <code>[V1, ..., Vn]</code>, the result represents "<code>let
+%% &lt;<em>V1</em>, ..., <em>Vn</em>&gt; = <em>Argument</em> in
+%% <em>Body</em></code>". All the <code>Vi</code> must have type
+%% <code>var</code>.
+%%
+%% @see ann_c_let/4
+%% @see update_c_let/4
+%% @see is_c_let/1
+%% @see let_vars/1
+%% @see let_arg/1
+%% @see let_body/1
+%% @see let_arity/1
+
+-spec c_let([c_var()], cerl(), cerl()) -> c_let().
+
+c_let(Variables, Argument, Body) ->
+ #c_let{vars = Variables, arg = Argument, body = Body}.
+
+
+%% ann_c_let(As, Variables, Argument, Body) -> c_let()
+%% @see c_let/3
+
+-spec ann_c_let(anns(), [c_var()], cerl(), cerl()) -> c_let().
+
+ann_c_let(As, Variables, Argument, Body) ->
+ #c_let{vars = Variables, arg = Argument, body = Body, anno = As}.
+
+
+%% update_c_let(Old, Variables, Argument, Body) -> c_let()
+%% @see c_let/3
+
+-spec update_c_let(c_let(), [c_var()], cerl(), cerl()) -> c_let().
+
+update_c_let(Node, Variables, Argument, Body) ->
+ #c_let{vars = Variables, arg = Argument, body = Body,
+ anno = get_ann(Node)}.
+
+
+%% @spec is_c_let(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% let-expression, otherwise <code>false</code>.
+%%
+%% @see c_let/3
+
+-spec is_c_let(cerl()) -> boolean().
+
+is_c_let(#c_let{}) ->
+ true;
+is_c_let(_) ->
+ false.
+
+
+%% @spec let_vars(c_let()) -> [c_var()]
+%%
+%% @doc Returns the list of left-hand side variables of an abstract
+%% let-expression.
+%%
+%% @see c_let/3
+%% @see let_arity/1
+
+-spec let_vars(c_let()) -> [c_var()].
+
+let_vars(Node) ->
+ Node#c_let.vars.
+
+
+%% @spec let_arg(c_let()) -> cerl()
+%%
+%% @doc Returns the argument subtree of an abstract let-expression.
+%%
+%% @see c_let/3
+
+-spec let_arg(c_let()) -> cerl().
+
+let_arg(Node) ->
+ Node#c_let.arg.
+
+
+%% @spec let_body(c_let()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract let-expression.
+%%
+%% @see c_let/3
+
+-spec let_body(c_let()) -> cerl().
+
+let_body(Node) ->
+ Node#c_let.body.
+
+
+%% @spec let_arity(Node::c_let()) -> non_neg_integer()
+%%
+%% @doc Returns the number of left-hand side variables of an abstract
+%% let-expression.
+%%
+%% <p>Note: this is equivalent to <code>length(let_vars(Node))</code>,
+%% but potentially more efficient.</p>
+%%
+%% @see c_let/3
+%% @see let_vars/1
+
+-spec let_arity(c_let()) -> non_neg_integer().
+
+let_arity(Node) ->
+ length(let_vars(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_letrec(Definitions::defs(), Body::cerl()) -> c_letrec()
+%%
+%% @doc Creates an abstract letrec-expression. If
+%% <code>Definitions</code> is <code>[{V1, F1}, ..., {Vn, Fn}]</code>,
+%% the result represents "<code>letrec <em>V1</em> = <em>F1</em>
+%% ... <em>Vn</em> = <em>Fn</em> in <em>Body</em></code>. All the
+%% <code>Vi</code> must have type <code>var</code> and represent
+%% function names. All the <code>Fi</code> must have type
+%% <code>'fun'</code>.
+%%
+%% @see ann_c_letrec/3
+%% @see update_c_letrec/3
+%% @see is_c_letrec/1
+%% @see letrec_defs/1
+%% @see letrec_body/1
+%% @see letrec_vars/1
+
+-spec c_letrec(defs(), cerl()) -> c_letrec().
+
+c_letrec(Defs, Body) ->
+ #c_letrec{defs = Defs, body = Body}.
+
+
+%% @spec ann_c_letrec(As::anns(), Definitions::defs(),
+%% Body::cerl()) -> c_letrec()
+%% @see c_letrec/2
+
+-spec ann_c_letrec(anns(), defs(), cerl()) -> c_letrec().
+
+ann_c_letrec(As, Defs, Body) ->
+ #c_letrec{defs = Defs, body = Body, anno = As}.
+
+
+%% @spec update_c_letrec(Old::c_letrec(), Definitions::defs(),
+%% Body::cerl()) -> c_letrec()
+%% @see c_letrec/2
+
+-spec update_c_letrec(c_letrec(), defs(), cerl()) -> c_letrec().
+
+update_c_letrec(Node, Defs, Body) ->
+ #c_letrec{defs = Defs, body = Body, anno = get_ann(Node)}.
+
+
+%% @spec is_c_letrec(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% letrec-expression, otherwise <code>false</code>.
+%%
+%% @see c_letrec/2
+
+-spec is_c_letrec(cerl()) -> boolean().
+
+is_c_letrec(#c_letrec{}) ->
+ true;
+is_c_letrec(_) ->
+ false.
+
+
+%% @spec letrec_defs(Node::c_letrec()) -> defs()
+%%
+%% @doc Returns the list of definitions of an abstract
+%% letrec-expression. If <code>Node</code> represents "<code>letrec
+%% <em>V1</em> = <em>F1</em> ... <em>Vn</em> = <em>Fn</em> in
+%% <em>Body</em></code>", the returned value is <code>[{V1, F1}, ...,
+%% {Vn, Fn}]</code>.
+%%
+%% @see c_letrec/2
+
+-spec letrec_defs(c_letrec()) -> defs().
+
+letrec_defs(Node) ->
+ Node#c_letrec.defs.
+
+
+%% @spec letrec_body(c_letrec()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract letrec-expression.
+%%
+%% @see c_letrec/2
+
+-spec letrec_body(c_letrec()) -> cerl().
+
+letrec_body(Node) ->
+ Node#c_letrec.body.
+
+
+%% @spec letrec_vars(c_letrec()) -> [cerl()]
+%%
+%% @doc Returns the list of left-hand side function variable subtrees
+%% of a letrec-expression. If <code>Node</code> represents
+%% "<code>letrec <em>V1</em> = <em>F1</em> ... <em>Vn</em> =
+%% <em>Fn</em> in <em>Body</em></code>", the returned value is
+%% <code>[V1, ..., Vn]</code>.
+%%
+%% @see c_letrec/2
+
+-spec letrec_vars(c_letrec()) -> [cerl()].
+
+letrec_vars(Node) ->
+ [F || {F, _} <- letrec_defs(Node)].
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_case(Argument::cerl(), Clauses::[cerl()]) -> c_case()
+%%
+%% @doc Creates an abstract case-expression. If <code>Clauses</code>
+%% is <code>[C1, ..., Cn]</code>, the result represents "<code>case
+%% <em>Argument</em> of <em>C1</em> ... <em>Cn</em>
+%% end</code>". <code>Clauses</code> must not be empty.
+%%
+%% @see ann_c_case/3
+%% @see update_c_case/3
+%% @see is_c_case/1
+%% @see c_clause/3
+%% @see case_arg/1
+%% @see case_clauses/1
+%% @see case_arity/1
+
+-spec c_case(cerl(), [cerl()]) -> c_case().
+
+c_case(Expr, Clauses) ->
+ #c_case{arg = Expr, clauses = Clauses}.
+
+
+%% @spec ann_c_case(As::anns(), Argument::cerl(),
+%% Clauses::[cerl()]) -> c_case()
+%% @see c_case/2
+
+-spec ann_c_case(anns(), cerl(), [cerl()]) -> c_case().
+
+ann_c_case(As, Expr, Clauses) ->
+ #c_case{arg = Expr, clauses = Clauses, anno = As}.
+
+
+%% @spec update_c_case(Old::cerl(), Argument::cerl(),
+%% Clauses::[cerl()]) -> c_case()
+%% @see c_case/2
+
+-spec update_c_case(c_case(), cerl(), [cerl()]) -> c_case().
+
+update_c_case(Node, Expr, Clauses) ->
+ #c_case{arg = Expr, clauses = Clauses, anno = get_ann(Node)}.
+
+
+%% is_c_case(Node) -> boolean()
+%%
+%% Node = cerl()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% case-expression; otherwise <code>false</code>.
+%%
+%% @see c_case/2
+
+-spec is_c_case(cerl()) -> boolean().
+
+is_c_case(#c_case{}) ->
+ true;
+is_c_case(_) ->
+ false.
+
+
+%% @spec case_arg(c_case()) -> cerl()
+%%
+%% @doc Returns the argument subtree of an abstract case-expression.
+%%
+%% @see c_case/2
+
+-spec case_arg(c_case()) -> cerl().
+
+case_arg(Node) ->
+ Node#c_case.arg.
+
+
+%% @spec case_clauses(c_case()) -> [cerl()]
+%%
+%% @doc Returns the list of clause subtrees of an abstract
+%% case-expression.
+%%
+%% @see c_case/2
+%% @see case_arity/1
+
+-spec case_clauses(c_case()) -> [cerl()].
+
+case_clauses(Node) ->
+ Node#c_case.clauses.
+
+
+%% @spec case_arity(Node::c_case()) -> non_neg_integer()
+%%
+%% @doc Equivalent to
+%% <code>clause_arity(hd(case_clauses(Node)))</code>, but potentially
+%% more efficient.
+%%
+%% @see c_case/2
+%% @see case_clauses/1
+%% @see clause_arity/1
+
+-spec case_arity(c_case()) -> non_neg_integer().
+
+case_arity(Node) ->
+ clause_arity(hd(case_clauses(Node))).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_clause(Patterns::[cerl()], Body::cerl()) -> c_clause()
+%% @equiv c_clause(Patterns, c_atom(true), Body)
+%% @see c_atom/1
+
+-spec c_clause([cerl()], cerl()) -> c_clause().
+
+c_clause(Patterns, Body) ->
+ c_clause(Patterns, c_atom(true), Body).
+
+
+%% @spec c_clause(Patterns::[cerl()], Guard::cerl(), Body::cerl()) ->
+%% c_clause()
+%%
+%% @doc Creates an an abstract clause. If <code>Patterns</code> is
+%% <code>[P1, ..., Pn]</code>, the result represents
+%% "<code>&lt;<em>P1</em>, ..., <em>Pn</em>&gt; when <em>Guard</em> ->
+%% <em>Body</em></code>".
+%%
+%% @see c_clause/2
+%% @see ann_c_clause/4
+%% @see update_c_clause/4
+%% @see is_c_clause/1
+%% @see c_case/2
+%% @see c_receive/3
+%% @see clause_pats/1
+%% @see clause_guard/1
+%% @see clause_body/1
+%% @see clause_arity/1
+%% @see clause_vars/1
+
+-spec c_clause([cerl()], cerl(), cerl()) -> c_clause().
+
+c_clause(Patterns, Guard, Body) ->
+ #c_clause{pats = Patterns, guard = Guard, body = Body}.
+
+
+%% @spec ann_c_clause(As::anns(), Patterns::[cerl()],
+%% Body::cerl()) -> c_clause()
+%% @equiv ann_c_clause(As, Patterns, c_atom(true), Body)
+%% @see c_clause/3
+
+-spec ann_c_clause(anns(), [cerl()], cerl()) -> c_clause().
+
+ann_c_clause(As, Patterns, Body) ->
+ ann_c_clause(As, Patterns, c_atom(true), Body).
+
+
+%% @spec ann_c_clause(As::anns(), Patterns::[cerl()], Guard::cerl(),
+%% Body::cerl()) -> c_clause()
+%% @see ann_c_clause/3
+%% @see c_clause/3
+
+-spec ann_c_clause(anns(), [cerl()], cerl(), cerl()) -> c_clause().
+
+ann_c_clause(As, Patterns, Guard, Body) ->
+ #c_clause{pats = Patterns, guard = Guard, body = Body, anno = As}.
+
+
+%% @spec update_c_clause(Old::c_clause(), Patterns::[cerl()],
+%% Guard::cerl(), Body::cerl()) -> c_clause()
+%% @see c_clause/3
+
+-spec update_c_clause(c_clause(), [cerl()], cerl(), cerl()) -> c_clause().
+
+update_c_clause(Node, Patterns, Guard, Body) ->
+ #c_clause{pats = Patterns, guard = Guard, body = Body,
+ anno = get_ann(Node)}.
+
+
+%% @spec is_c_clause(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% clause, otherwise <code>false</code>.
+%%
+%% @see c_clause/3
+
+-spec is_c_clause(cerl()) -> boolean().
+
+is_c_clause(#c_clause{}) ->
+ true;
+is_c_clause(_) ->
+ false.
+
+
+%% @spec clause_pats(c_clause()) -> [cerl()]
+%%
+%% @doc Returns the list of pattern subtrees of an abstract clause.
+%%
+%% @see c_clause/3
+%% @see clause_arity/1
+
+-spec clause_pats(c_clause()) -> [cerl()].
+
+clause_pats(Node) ->
+ Node#c_clause.pats.
+
+
+%% @spec clause_guard(c_clause()) -> cerl()
+%%
+%% @doc Returns the guard subtree of an abstract clause.
+%%
+%% @see c_clause/3
+
+-spec clause_guard(c_clause()) -> cerl().
+
+clause_guard(Node) ->
+ Node#c_clause.guard.
+
+
+%% @spec clause_body(c_clause()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract clause.
+%%
+%% @see c_clause/3
+
+-spec clause_body(c_clause()) -> cerl().
+
+clause_body(Node) ->
+ Node#c_clause.body.
+
+
+%% @spec clause_arity(Node::c_clause()) -> non_neg_integer()
+%%
+%% @doc Returns the number of pattern subtrees of an abstract clause.
+%%
+%% <p>Note: this is equivalent to
+%% <code>length(clause_pats(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_clause/3
+%% @see clause_pats/1
+
+-spec clause_arity(c_clause()) -> non_neg_integer().
+
+clause_arity(Node) ->
+ length(clause_pats(Node)).
+
+
+%% @spec clause_vars(c_clause()) -> [cerl()]
+%%
+%% @doc Returns the list of all abstract variables in the patterns of
+%% an abstract clause. The order of listing is not defined.
+%%
+%% @see c_clause/3
+%% @see pat_list_vars/1
+
+-spec clause_vars(c_clause()) -> [cerl()].
+
+clause_vars(Clause) ->
+ pat_list_vars(clause_pats(Clause)).
+
+
+%% @spec pat_vars(Pattern::cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of all abstract variables in a pattern. An
+%% exception is thrown if <code>Node</code> does not represent a
+%% well-formed Core Erlang clause pattern. The order of listing is not
+%% defined.
+%%
+%% @see pat_list_vars/1
+%% @see clause_vars/1
+
+-spec pat_vars(cerl()) -> [cerl()].
+
+pat_vars(Node) ->
+ pat_vars(Node, []).
+
+pat_vars(Node, Vs) ->
+ case type(Node) of
+ var ->
+ [Node | Vs];
+ literal ->
+ Vs;
+ cons ->
+ pat_vars(cons_hd(Node), pat_vars(cons_tl(Node), Vs));
+ tuple ->
+ pat_list_vars(tuple_es(Node), Vs);
+ map ->
+ pat_list_vars(map_es(Node), Vs);
+ map_pair ->
+ %% map_pair_key is not a pattern var, excluded
+ pat_list_vars([map_pair_op(Node),map_pair_val(Node)],Vs);
+ binary ->
+ pat_list_vars(binary_segments(Node), Vs);
+ bitstr ->
+ %% bitstr_size is not a pattern var, excluded
+ pat_vars(bitstr_val(Node), Vs);
+ alias ->
+ pat_vars(alias_pat(Node), [alias_var(Node) | Vs])
+ end.
+
+
+%% @spec pat_list_vars(Patterns::[cerl()]) -> [cerl()]
+%%
+%% @doc Returns the list of all abstract variables in the given
+%% patterns. An exception is thrown if some element in
+%% <code>Patterns</code> does not represent a well-formed Core Erlang
+%% clause pattern. The order of listing is not defined.
+%%
+%% @see pat_vars/1
+%% @see clause_vars/1
+
+-spec pat_list_vars([cerl()]) -> [cerl()].
+
+pat_list_vars(Ps) ->
+ pat_list_vars(Ps, []).
+
+pat_list_vars([P | Ps], Vs) ->
+ pat_list_vars(Ps, pat_vars(P, Vs));
+pat_list_vars([], Vs) ->
+ Vs.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_alias(Variable::c_var(), Pattern::cerl()) -> c_alias()
+%%
+%% @doc Creates an abstract pattern alias. The result represents
+%% "<code><em>Variable</em> = <em>Pattern</em></code>".
+%%
+%% @see ann_c_alias/3
+%% @see update_c_alias/3
+%% @see is_c_alias/1
+%% @see alias_var/1
+%% @see alias_pat/1
+%% @see c_clause/3
+
+-spec c_alias(c_var(), cerl()) -> c_alias().
+
+c_alias(Var, Pattern) ->
+ #c_alias{var = Var, pat = Pattern}.
+
+
+%% @spec ann_c_alias(As::anns(), Variable::c_var(),
+%% Pattern::cerl()) -> c_alias()
+%% @see c_alias/2
+
+-spec ann_c_alias(anns(), c_var(), cerl()) -> c_alias().
+
+ann_c_alias(As, Var, Pattern) ->
+ #c_alias{var = Var, pat = Pattern, anno = As}.
+
+
+%% @spec update_c_alias(Old::cerl(), Variable::c_var(),
+%% Pattern::cerl()) -> c_alias()
+%% @see c_alias/2
+
+-spec update_c_alias(c_alias(), c_var(), cerl()) -> c_alias().
+
+update_c_alias(Node, Var, Pattern) ->
+ #c_alias{var = Var, pat = Pattern, anno = get_ann(Node)}.
+
+
+%% @spec is_c_alias(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% pattern alias, otherwise <code>false</code>.
+%%
+%% @see c_alias/2
+
+-spec is_c_alias(cerl()) -> boolean().
+
+is_c_alias(#c_alias{}) ->
+ true;
+is_c_alias(_) ->
+ false.
+
+
+%% @spec alias_var(c_alias()) -> c_var()
+%%
+%% @doc Returns the variable subtree of an abstract pattern alias.
+%%
+%% @see c_alias/2
+
+-spec alias_var(c_alias()) -> c_var().
+
+alias_var(Node) ->
+ Node#c_alias.var.
+
+
+%% @spec alias_pat(c_alias()) -> cerl()
+%%
+%% @doc Returns the pattern subtree of an abstract pattern alias.
+%%
+%% @see c_alias/2
+
+-spec alias_pat(c_alias()) -> cerl().
+
+alias_pat(Node) ->
+ Node#c_alias.pat.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_receive(Clauses::[cerl()]) -> c_receive()
+%% @equiv c_receive(Clauses, c_atom(infinity), c_atom(true))
+%% @see c_atom/1
+
+-spec c_receive([cerl()]) -> c_receive().
+
+c_receive(Clauses) ->
+ c_receive(Clauses, c_atom(infinity), c_atom(true)).
+
+
+%% @spec c_receive(Clauses::[cerl()], Timeout::cerl(),
+%% Action::cerl()) -> c_receive()
+%%
+%% @doc Creates an abstract receive-expression. If
+%% <code>Clauses</code> is <code>[C1, ..., Cn]</code>, the result
+%% represents "<code>receive <em>C1</em> ... <em>Cn</em> after
+%% <em>Timeout</em> -> <em>Action</em> end</code>".
+%%
+%% @see c_receive/1
+%% @see ann_c_receive/4
+%% @see update_c_receive/4
+%% @see is_c_receive/1
+%% @see receive_clauses/1
+%% @see receive_timeout/1
+%% @see receive_action/1
+
+-spec c_receive([cerl()], cerl(), cerl()) -> c_receive().
+
+c_receive(Clauses, Timeout, Action) ->
+ #c_receive{clauses = Clauses, timeout = Timeout, action = Action}.
+
+
+%% @spec ann_c_receive(As::anns(), Clauses::[cerl()]) -> c_receive()
+%% @equiv ann_c_receive(As, Clauses, c_atom(infinity), c_atom(true))
+%% @see c_receive/3
+%% @see c_atom/1
+
+-spec ann_c_receive(anns(), [cerl()]) -> c_receive().
+
+ann_c_receive(As, Clauses) ->
+ ann_c_receive(As, Clauses, c_atom(infinity), c_atom(true)).
+
+
+%% @spec ann_c_receive(As::anns(), Clauses::[cerl()],
+%% Timeout::cerl(), Action::cerl()) -> c_receive()
+%% @see ann_c_receive/2
+%% @see c_receive/3
+
+-spec ann_c_receive(anns(), [cerl()], cerl(), cerl()) -> c_receive().
+
+ann_c_receive(As, Clauses, Timeout, Action) ->
+ #c_receive{clauses = Clauses, timeout = Timeout, action = Action,
+ anno = As}.
+
+
+%% @spec update_c_receive(Old::cerl(), Clauses::[cerl()],
+%% Timeout::cerl(), Action::cerl()) -> c_receive()
+%% @see c_receive/3
+
+-spec update_c_receive(c_receive(), [cerl()], cerl(), cerl()) -> c_receive().
+
+update_c_receive(Node, Clauses, Timeout, Action) ->
+ #c_receive{clauses = Clauses, timeout = Timeout, action = Action,
+ anno = get_ann(Node)}.
+
+
+%% @spec is_c_receive(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% receive-expression, otherwise <code>false</code>.
+%%
+%% @see c_receive/3
+
+-spec is_c_receive(cerl()) -> boolean().
+
+is_c_receive(#c_receive{}) ->
+ true;
+is_c_receive(_) ->
+ false.
+
+
+%% @spec receive_clauses(c_receive()) -> [cerl()]
+%%
+%% @doc Returns the list of clause subtrees of an abstract
+%% receive-expression.
+%%
+%% @see c_receive/3
+
+-spec receive_clauses(c_receive()) -> [cerl()].
+
+receive_clauses(Node) ->
+ Node#c_receive.clauses.
+
+
+%% @spec receive_timeout(c_receive()) -> cerl()
+%%
+%% @doc Returns the timeout subtree of an abstract receive-expression.
+%%
+%% @see c_receive/3
+
+-spec receive_timeout(c_receive()) -> cerl().
+
+receive_timeout(Node) ->
+ Node#c_receive.timeout.
+
+
+%% @spec receive_action(c_receive()) -> cerl()
+%%
+%% @doc Returns the action subtree of an abstract receive-expression.
+%%
+%% @see c_receive/3
+
+-spec receive_action(c_receive()) -> cerl().
+
+receive_action(Node) ->
+ Node#c_receive.action.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_apply(Operator::c_var(), Arguments::[cerl()]) -> c_apply()
+%%
+%% @doc Creates an abstract function application. If
+%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result
+%% represents "<code>apply <em>Operator</em>(<em>A1</em>, ...,
+%% <em>An</em>)</code>".
+%%
+%% @see ann_c_apply/3
+%% @see update_c_apply/3
+%% @see is_c_apply/1
+%% @see apply_op/1
+%% @see apply_args/1
+%% @see apply_arity/1
+%% @see c_call/3
+%% @see c_primop/2
+
+-spec c_apply(c_var(), [cerl()]) -> c_apply().
+
+c_apply(Operator, Arguments) ->
+ #c_apply{op = Operator, args = Arguments}.
+
+
+%% @spec ann_c_apply(As::anns(), Operator::c_var(),
+%% Arguments::[cerl()]) -> c_apply()
+%% @see c_apply/2
+
+-spec ann_c_apply(anns(), c_var(), [cerl()]) -> c_apply().
+
+ann_c_apply(As, Operator, Arguments) ->
+ #c_apply{op = Operator, args = Arguments, anno = As}.
+
+
+%% @spec update_c_apply(Old::c_apply(), Operator::cerl(),
+%% Arguments::[cerl()]) -> c_apply()
+%% @see c_apply/2
+
+-spec update_c_apply(c_apply(), c_var(), [cerl()]) -> c_apply().
+
+update_c_apply(Node, Operator, Arguments) ->
+ #c_apply{op = Operator, args = Arguments, anno = get_ann(Node)}.
+
+
+%% @spec is_c_apply(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% function application, otherwise <code>false</code>.
+%%
+%% @see c_apply/2
+
+-spec is_c_apply(cerl()) -> boolean().
+
+is_c_apply(#c_apply{}) ->
+ true;
+is_c_apply(_) ->
+ false.
+
+
+%% @spec apply_op(c_apply()) -> c_var()
+%%
+%% @doc Returns the operator subtree of an abstract function
+%% application.
+%%
+%% @see c_apply/2
+
+-spec apply_op(c_apply()) -> c_var().
+
+apply_op(Node) ->
+ Node#c_apply.op.
+
+
+%% @spec apply_args(c_apply()) -> [cerl()]
+%%
+%% @doc Returns the list of argument subtrees of an abstract function
+%% application.
+%%
+%% @see c_apply/2
+%% @see apply_arity/1
+
+-spec apply_args(c_apply()) -> [cerl()].
+
+apply_args(Node) ->
+ Node#c_apply.args.
+
+
+%% @spec apply_arity(Node::c_apply()) -> arity()
+%%
+%% @doc Returns the number of argument subtrees of an abstract
+%% function application.
+%%
+%% <p>Note: this is equivalent to
+%% <code>length(apply_args(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_apply/2
+%% @see apply_args/1
+
+-spec apply_arity(c_apply()) -> arity().
+
+apply_arity(Node) ->
+ length(apply_args(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_call(Module::cerl(), Name::cerl(), Arguments::[cerl()]) ->
+%% c_call()
+%%
+%% @doc Creates an abstract inter-module call. If
+%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result
+%% represents "<code>call <em>Module</em>:<em>Name</em>(<em>A1</em>,
+%% ..., <em>An</em>)</code>".
+%%
+%% @see ann_c_call/4
+%% @see update_c_call/4
+%% @see is_c_call/1
+%% @see call_module/1
+%% @see call_name/1
+%% @see call_args/1
+%% @see call_arity/1
+%% @see c_apply/2
+%% @see c_primop/2
+
+-spec c_call(cerl(), cerl(), [cerl()]) -> c_call().
+
+c_call(Module, Name, Arguments) ->
+ #c_call{module = Module, name = Name, args = Arguments}.
+
+
+%% @spec ann_c_call(As::anns(), Module::cerl(), Name::cerl(),
+%% Arguments::[cerl()]) -> c_call()
+%% @see c_call/3
+
+-spec ann_c_call(anns(), cerl(), cerl(), [cerl()]) -> c_call().
+
+ann_c_call(As, Module, Name, Arguments) ->
+ #c_call{module = Module, name = Name, args = Arguments, anno = As}.
+
+
+%% @spec update_c_call(Old::cerl(), Module::cerl(), Name::cerl(),
+%% Arguments::[cerl()]) -> c_call()
+%% @see c_call/3
+
+-spec update_c_call(cerl(), cerl(), cerl(), [cerl()]) -> c_call().
+
+update_c_call(Node, Module, Name, Arguments) ->
+ #c_call{module = Module, name = Name, args = Arguments,
+ anno = get_ann(Node)}.
+
+
+%% @spec is_c_call(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% inter-module call expression; otherwise <code>false</code>.
+%%
+%% @see c_call/3
+
+-spec is_c_call(cerl()) -> boolean().
+
+is_c_call(#c_call{}) ->
+ true;
+is_c_call(_) ->
+ false.
+
+
+%% @spec call_module(c_call()) -> cerl()
+%%
+%% @doc Returns the module subtree of an abstract inter-module call.
+%%
+%% @see c_call/3
+
+-spec call_module(c_call()) -> cerl().
+
+call_module(Node) ->
+ Node#c_call.module.
+
+
+%% @spec call_name(c_call()) -> cerl()
+%%
+%% @doc Returns the name subtree of an abstract inter-module call.
+%%
+%% @see c_call/3
+
+-spec call_name(c_call()) -> cerl().
+
+call_name(Node) ->
+ Node#c_call.name.
+
+
+%% @spec call_args(c_call()) -> [cerl()]
+%%
+%% @doc Returns the list of argument subtrees of an abstract
+%% inter-module call.
+%%
+%% @see c_call/3
+%% @see call_arity/1
+
+-spec call_args(c_call()) -> [cerl()].
+
+call_args(Node) ->
+ Node#c_call.args.
+
+
+%% @spec call_arity(Node::c_call()) -> arity()
+%%
+%% @doc Returns the number of argument subtrees of an abstract
+%% inter-module call.
+%%
+%% <p>Note: this is equivalent to
+%% <code>length(call_args(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_call/3
+%% @see call_args/1
+
+-spec call_arity(c_call()) -> arity().
+
+call_arity(Node) ->
+ length(call_args(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_primop(Name::c_literal(), Arguments::[cerl()]) -> c_primop()
+%%
+%% @doc Creates an abstract primitive operation call. If
+%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result
+%% represents "<code>primop <em>Name</em>(<em>A1</em>, ...,
+%% <em>An</em>)</code>". <code>Name</code> must be an atom literal.
+%%
+%% @see ann_c_primop/3
+%% @see update_c_primop/3
+%% @see is_c_primop/1
+%% @see primop_name/1
+%% @see primop_args/1
+%% @see primop_arity/1
+%% @see c_apply/2
+%% @see c_call/3
+
+-spec c_primop(c_literal(), [cerl()]) -> c_primop().
+
+c_primop(Name, Arguments) ->
+ #c_primop{name = Name, args = Arguments}.
+
+
+%% @spec ann_c_primop(As::anns(), Name::c_literal(),
+%% Arguments::[cerl()]) -> c_primop()
+%% @see c_primop/2
+
+-spec ann_c_primop(anns(), c_literal(), [cerl()]) -> c_primop().
+
+ann_c_primop(As, Name, Arguments) ->
+ #c_primop{name = Name, args = Arguments, anno = As}.
+
+
+%% @spec update_c_primop(Old::cerl(), Name::c_literal(),
+%% Arguments::[cerl()]) -> c_primop()
+%% @see c_primop/2
+
+-spec update_c_primop(cerl(), c_literal(), [cerl()]) -> c_primop().
+
+update_c_primop(Node, Name, Arguments) ->
+ #c_primop{name = Name, args = Arguments, anno = get_ann(Node)}.
+
+
+%% @spec is_c_primop(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% primitive operation call, otherwise <code>false</code>.
+%%
+%% @see c_primop/2
+
+-spec is_c_primop(cerl()) -> boolean().
+
+is_c_primop(#c_primop{}) ->
+ true;
+is_c_primop(_) ->
+ false.
+
+
+%% @spec primop_name(c_primop()) -> c_literal()
+%%
+%% @doc Returns the name subtree of an abstract primitive operation
+%% call.
+%%
+%% @see c_primop/2
+
+-spec primop_name(c_primop()) -> c_literal().
+
+primop_name(Node) ->
+ Node#c_primop.name.
+
+
+%% @spec primop_args(c_primop()) -> [cerl()]
+%%
+%% @doc Returns the list of argument subtrees of an abstract primitive
+%% operation call.
+%%
+%% @see c_primop/2
+%% @see primop_arity/1
+
+-spec primop_args(c_primop()) -> [cerl()].
+
+primop_args(Node) ->
+ Node#c_primop.args.
+
+
+%% @spec primop_arity(Node::c_primop()) -> arity()
+%%
+%% @doc Returns the number of argument subtrees of an abstract
+%% primitive operation call.
+%%
+%% <p>Note: this is equivalent to
+%% <code>length(primop_args(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_primop/2
+%% @see primop_args/1
+
+-spec primop_arity(c_primop()) -> arity().
+
+primop_arity(Node) ->
+ length(primop_args(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_try(Argument::cerl(), Variables::[c_var()], Body::cerl(),
+%% ExceptionVars::[c_var()], Handler::cerl()) -> c_try()
+%%
+%% @doc Creates an abstract try-expression. If <code>Variables</code> is
+%% <code>[V1, ..., Vn]</code> and <code>ExceptionVars</code> is
+%% <code>[X1, ..., Xm]</code>, the result represents "<code>try
+%% <em>Argument</em> of &lt;<em>V1</em>, ..., <em>Vn</em>&gt; ->
+%% <em>Body</em> catch &lt;<em>X1</em>, ..., <em>Xm</em>&gt; ->
+%% <em>Handler</em></code>". All the <code>Vi</code> and <code>Xi</code>
+%% must have type <code>var</code>.
+%%
+%% @see ann_c_try/6
+%% @see update_c_try/6
+%% @see is_c_try/1
+%% @see try_arg/1
+%% @see try_vars/1
+%% @see try_body/1
+%% @see c_catch/1
+
+-spec c_try(cerl(), [c_var()], cerl(), [c_var()], cerl()) -> c_try().
+
+c_try(Expr, Vs, Body, Evs, Handler) ->
+ #c_try{arg = Expr, vars = Vs, body = Body,
+ evars = Evs, handler = Handler}.
+
+
+%% @spec ann_c_try(As::[term()], Expression::cerl(),
+%% Variables::[c_var()], Body::cerl(),
+%% EVars::[c_var()], Handler::cerl()) -> c_try()
+%% @see c_try/5
+
+-spec ann_c_try(anns(), cerl(), [c_var()], cerl(), [c_var()], cerl()) ->
+ c_try().
+
+ann_c_try(As, Expr, Vs, Body, Evs, Handler) ->
+ #c_try{arg = Expr, vars = Vs, body = Body,
+ evars = Evs, handler = Handler, anno = As}.
+
+
+%% @spec update_c_try(Old::c_try(), Expression::cerl(),
+%% Variables::[c_var()], Body::cerl(),
+%% EVars::[c_var()], Handler::cerl()) -> cerl()
+%% @see c_try/5
+
+-spec update_c_try(c_try(), cerl(), [c_var()], cerl(), [c_var()], cerl()) ->
+ c_try().
+
+update_c_try(Node, Expr, Vs, Body, Evs, Handler) ->
+ #c_try{arg = Expr, vars = Vs, body = Body,
+ evars = Evs, handler = Handler, anno = get_ann(Node)}.
+
+
+%% @spec is_c_try(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% try-expression, otherwise <code>false</code>.
+%%
+%% @see c_try/5
+
+-spec is_c_try(cerl()) -> boolean().
+
+is_c_try(#c_try{}) ->
+ true;
+is_c_try(_) ->
+ false.
+
+
+%% @spec try_arg(c_try()) -> cerl()
+%%
+%% @doc Returns the expression subtree of an abstract try-expression.
+%%
+%% @see c_try/5
+
+-spec try_arg(c_try()) -> cerl().
+
+try_arg(Node) ->
+ Node#c_try.arg.
+
+
+%% @spec try_vars(c_try()) -> [c_var()]
+%%
+%% @doc Returns the list of success variable subtrees of an abstract
+%% try-expression.
+%%
+%% @see c_try/5
+
+-spec try_vars(c_try()) -> [c_var()].
+
+try_vars(Node) ->
+ Node#c_try.vars.
+
+
+%% @spec try_body(c_try()) -> cerl()
+%%
+%% @doc Returns the success body subtree of an abstract try-expression.
+%%
+%% @see c_try/5
+
+-spec try_body(c_try()) -> cerl().
+
+try_body(Node) ->
+ Node#c_try.body.
+
+
+%% @spec try_evars(c_try()) -> [c_var()]
+%%
+%% @doc Returns the list of exception variable subtrees of an abstract
+%% try-expression.
+%%
+%% @see c_try/5
+
+-spec try_evars(c_try()) -> [c_var()].
+
+try_evars(Node) ->
+ Node#c_try.evars.
+
+
+%% @spec try_handler(c_try()) -> cerl()
+%%
+%% @doc Returns the exception body subtree of an abstract
+%% try-expression.
+%%
+%% @see c_try/5
+
+-spec try_handler(c_try()) -> cerl().
+
+try_handler(Node) ->
+ Node#c_try.handler.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_catch(Body::cerl()) -> c_catch()
+%%
+%% @doc Creates an abstract catch-expression. The result represents
+%% "<code>catch <em>Body</em></code>".
+%%
+%% <p>Note: catch-expressions can be rewritten as try-expressions, and
+%% will eventually be removed from Core Erlang.</p>
+%%
+%% @see ann_c_catch/2
+%% @see update_c_catch/2
+%% @see is_c_catch/1
+%% @see catch_body/1
+%% @see c_try/5
+
+-spec c_catch(cerl()) -> c_catch().
+
+c_catch(Body) ->
+ #c_catch{body = Body}.
+
+
+%% @spec ann_c_catch(As::anns(), Body::cerl()) -> c_catch()
+%% @see c_catch/1
+
+-spec ann_c_catch(anns(), cerl()) -> c_catch().
+
+ann_c_catch(As, Body) ->
+ #c_catch{body = Body, anno = As}.
+
+
+%% @spec update_c_catch(Old::c_catch(), Body::cerl()) -> c_catch()
+%% @see c_catch/1
+
+-spec update_c_catch(c_catch(), cerl()) -> c_catch().
+
+update_c_catch(Node, Body) ->
+ #c_catch{body = Body, anno = get_ann(Node)}.
+
+
+%% @spec is_c_catch(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% catch-expression, otherwise <code>false</code>.
+%%
+%% @see c_catch/1
+
+-spec is_c_catch(cerl()) -> boolean().
+
+is_c_catch(#c_catch{}) ->
+ true;
+is_c_catch(_) ->
+ false.
+
+
+%% @spec catch_body(Node::c_catch()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract catch-expression.
+%%
+%% @see c_catch/1
+
+-spec catch_body(c_catch()) -> cerl().
+
+catch_body(Node) ->
+ Node#c_catch.body.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec to_records(Tree::cerl()) -> record(record_types())
+%%
+%% @doc Translates an abstract syntax tree to a corresponding explicit
+%% record representation. The records are defined in the file
+%% "<code>cerl.hrl</code>".
+%%
+%% @see type/1
+%% @see from_records/1
+
+-spec to_records(cerl()) -> cerl().
+
+to_records(Node) ->
+ Node.
+
+%% @spec from_records(Tree::record(record_types())) -> cerl()
+%%
+%% record_types() = c_alias | c_apply | c_binary | c_bitstr | c_call |
+%% c_case | c_catch | c_clause | c_cons | c_fun |
+%% c_let | c_letrec | c_literal | c_map | c_map_pair |
+%% c_module | c_primop | c_receive | c_seq |
+%% c_try | c_tuple | c_values | c_var
+%%
+%% @doc Translates an explicit record representation to a
+%% corresponding abstract syntax tree. The records are defined in the
+%% file "<code>core_parse.hrl</code>".
+%%
+%% @see type/1
+%% @see to_records/1
+
+-spec from_records(cerl()) -> cerl().
+
+from_records(Node) ->
+ Node.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec is_data(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents a
+%% data constructor, otherwise <code>false</code>. Data constructors
+%% are cons cells, tuples, and atomic literals.
+%%
+%% @see data_type/1
+%% @see data_es/1
+%% @see data_arity/1
+
+-spec is_data(cerl()) -> boolean().
+
+is_data(#c_literal{}) ->
+ true;
+is_data(#c_cons{}) ->
+ true;
+is_data(#c_tuple{}) ->
+ true;
+is_data(_) ->
+ false.
+
+
+%% @spec data_type(Node::cerl()) -> dtype()
+%%
+%% dtype() = cons | tuple | {atomic, Value}
+%% Value = integer() | float() | atom() | []
+%%
+%% @doc Returns a type descriptor for a data constructor
+%% node. (Cf. <code>is_data/1</code>.) This is mainly useful for
+%% comparing types and for constructing new nodes of the same type
+%% (cf. <code>make_data/2</code>). If <code>Node</code> represents an
+%% integer, floating-point number, atom or empty list, the result is
+%% <code>{atomic, Value}</code>, where <code>Value</code> is the value
+%% of <code>concrete(Node)</code>, otherwise the result is either
+%% <code>cons</code> or <code>tuple</code>.
+%%
+%% <p>Type descriptors can be compared for equality or order (in the
+%% Erlang term order), but remember that floating-point values should
+%% in general never be tested for equality.</p>
+%%
+%% @see is_data/1
+%% @see make_data/2
+%% @see type/1
+%% @see concrete/1
+
+-type value() :: integer() | float() | atom() | [].
+-type dtype() :: 'cons' | 'tuple' | {'atomic', value()}.
+-type c_lct() :: c_literal() | c_cons() | c_tuple().
+
+-spec data_type(c_lct()) -> dtype().
+
+data_type(#c_literal{val = V}) ->
+ case V of
+ [_ | _] ->
+ cons;
+ _ when is_tuple(V) ->
+ tuple;
+ _ ->
+ {atomic, V}
+ end;
+data_type(#c_cons{}) ->
+ cons;
+data_type(#c_tuple{}) ->
+ tuple.
+
+%% @spec data_es(Node::cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of subtrees of a data constructor node. If
+%% the arity of the constructor is zero, the result is the empty list.
+%%
+%% <p>Note: if <code>data_type(Node)</code> is <code>cons</code>, the
+%% number of subtrees is exactly two. If <code>data_type(Node)</code>
+%% is <code>{atomic, Value}</code>, the number of subtrees is
+%% zero.</p>
+%%
+%% @see is_data/1
+%% @see data_type/1
+%% @see data_arity/1
+%% @see make_data/2
+
+-spec data_es(c_lct()) -> [cerl()].
+
+data_es(#c_literal{val = V}) ->
+ case V of
+ [Head | Tail] ->
+ [#c_literal{val = Head}, #c_literal{val = Tail}];
+ _ when is_tuple(V) ->
+ make_lit_list(tuple_to_list(V));
+ _ ->
+ []
+ end;
+data_es(#c_cons{hd = H, tl = T}) ->
+ [H, T];
+data_es(#c_tuple{es = Es}) ->
+ Es.
+
+%% @spec data_arity(Node::cerl()) -> non_neg_integer()
+%%
+%% @doc Returns the number of subtrees of a data constructor
+%% node. This is equivalent to <code>length(data_es(Node))</code>, but
+%% potentially more efficient.
+%%
+%% @see is_data/1
+%% @see data_es/1
+
+-spec data_arity(c_lct()) -> non_neg_integer().
+
+data_arity(#c_literal{val = V}) ->
+ case V of
+ [_ | _] ->
+ 2;
+ _ when is_tuple(V) ->
+ tuple_size(V);
+ _ ->
+ 0
+ end;
+data_arity(#c_cons{}) ->
+ 2;
+data_arity(#c_tuple{es = Es}) ->
+ length(Es).
+
+
+%% @spec make_data(Type::dtype(), Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates a data constructor node with the specified type and
+%% subtrees. (Cf. <code>data_type/1</code>.) An exception is thrown
+%% if the length of <code>Elements</code> is invalid for the given
+%% <code>Type</code>; see <code>data_es/1</code> for arity constraints
+%% on constructor types.
+%%
+%% @see data_type/1
+%% @see data_es/1
+%% @see ann_make_data/3
+%% @see update_data/3
+%% @see make_data_skel/2
+
+-spec make_data(dtype(), [cerl()]) -> c_lct().
+
+make_data(CType, Es) ->
+ ann_make_data([], CType, Es).
+
+
+%% @spec ann_make_data(As::anns(), Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data/2
+
+-spec ann_make_data(anns(), dtype(), [cerl()]) -> c_lct().
+
+ann_make_data(As, {atomic, V}, []) -> #c_literal{val = V, anno = As};
+ann_make_data(As, cons, [H, T]) -> ann_c_cons(As, H, T);
+ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es).
+
+%% @spec update_data(Old::cerl(), Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data/2
+
+-spec update_data(cerl(), dtype(), [cerl()]) -> c_lct().
+
+update_data(Node, CType, Es) ->
+ ann_make_data(get_ann(Node), CType, Es).
+
+
+%% @spec make_data_skel(Type::dtype(), Elements::[cerl()]) -> cerl()
+%%
+%% @doc Like <code>make_data/2</code>, but analogous to
+%% <code>c_tuple_skel/1</code> and <code>c_cons_skel/2</code>.
+%%
+%% @see ann_make_data_skel/3
+%% @see update_data_skel/3
+%% @see make_data/2
+%% @see c_tuple_skel/1
+%% @see c_cons_skel/2
+
+-spec make_data_skel(dtype(), [cerl()]) -> c_lct().
+
+make_data_skel(CType, Es) ->
+ ann_make_data_skel([], CType, Es).
+
+
+%% @spec ann_make_data_skel(As::anns(), Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data_skel/2
+
+-spec ann_make_data_skel(anns(), dtype(), [cerl()]) -> c_lct().
+
+ann_make_data_skel(As, {atomic, V}, []) -> #c_literal{val = V, anno = As};
+ann_make_data_skel(As, cons, [H, T]) -> ann_c_cons_skel(As, H, T);
+ann_make_data_skel(As, tuple, Es) -> ann_c_tuple_skel(As, Es).
+
+
+%% @spec update_data_skel(Old::cerl(), Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data_skel/2
+
+-spec update_data_skel(cerl(), dtype(), [cerl()]) -> c_lct().
+
+update_data_skel(Node, CType, Es) ->
+ ann_make_data_skel(get_ann(Node), CType, Es).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec subtrees(Node::cerl()) -> [[cerl()]]
+%%
+%% @doc Returns the grouped list of all subtrees of a node. If
+%% <code>Node</code> is a leaf node (cf. <code>is_leaf/1</code>), this
+%% is the empty list, otherwise the result is always a nonempty list,
+%% containing the lists of subtrees of <code>Node</code>, in
+%% left-to-right order as they occur in the printed program text, and
+%% grouped by category. Often, each group contains only a single
+%% subtree.
+%%
+%% <p>Depending on the type of <code>Node</code>, the size of some
+%% groups may be variable (e.g., the group consisting of all the
+%% elements of a tuple), while others always contain the same number
+%% of elements - usually exactly one (e.g., the group containing the
+%% argument expression of a case-expression). Note, however, that the
+%% exact structure of the returned list (for a given node type) should
+%% in general not be depended upon, since it might be subject to
+%% change without notice.</p>
+%%
+%% <p>The function <code>subtrees/1</code> and the constructor functions
+%% <code>make_tree/2</code> and <code>update_tree/2</code> can be a
+%% great help if one wants to traverse a syntax tree, visiting all its
+%% subtrees, but treat nodes of the tree in a uniform way in most or all
+%% cases. Using these functions makes this simple, and also assures that
+%% your code is not overly sensitive to extensions of the syntax tree
+%% data type, because any node types not explicitly handled by your code
+%% can be left to a default case.</p>
+%%
+%% <p>For example:
+%% <pre>
+%% postorder(F, Tree) ->
+%% F(case subtrees(Tree) of
+%% [] -> Tree;
+%% List -> update_tree(Tree,
+%% [[postorder(F, Subtree)
+%% || Subtree &lt;- Group]
+%% || Group &lt;- List])
+%% end).
+%% </pre>
+%% maps the function <code>F</code> on <code>Tree</code> and all its
+%% subtrees, doing a post-order traversal of the syntax tree. (Note
+%% the use of <code>update_tree/2</code> to preserve annotations.) For
+%% a simple function like:
+%% <pre>
+%% f(Node) ->
+%% case type(Node) of
+%% atom -> atom("a_" ++ atom_name(Node));
+%% _ -> Node
+%% end.
+%% </pre>
+%% the call <code>postorder(fun f/1, Tree)</code> will yield a new
+%% representation of <code>Tree</code> in which all atom names have
+%% been extended with the prefix "a_", but nothing else (including
+%% annotations) has been changed.</p>
+%%
+%% @see is_leaf/1
+%% @see make_tree/2
+%% @see update_tree/2
+
+-spec subtrees(cerl()) -> [[cerl()]].
+
+subtrees(T) ->
+ case is_leaf(T) of
+ true ->
+ [];
+ false ->
+ case type(T) of
+ values ->
+ [values_es(T)];
+ binary ->
+ [binary_segments(T)];
+ bitstr ->
+ [[bitstr_val(T)], [bitstr_size(T)],
+ [bitstr_unit(T)], [bitstr_type(T)],
+ [bitstr_flags(T)]];
+ cons ->
+ [[cons_hd(T)], [cons_tl(T)]];
+ tuple ->
+ [tuple_es(T)];
+ map ->
+ [map_es(T)];
+ map_pair ->
+ [[map_pair_op(T)],[map_pair_key(T)],[map_pair_val(T)]];
+ 'let' ->
+ [let_vars(T), [let_arg(T)], [let_body(T)]];
+ seq ->
+ [[seq_arg(T)], [seq_body(T)]];
+ apply ->
+ [[apply_op(T)], apply_args(T)];
+ call ->
+ [[call_module(T)], [call_name(T)],
+ call_args(T)];
+ primop ->
+ [[primop_name(T)], primop_args(T)];
+ 'case' ->
+ [[case_arg(T)], case_clauses(T)];
+ clause ->
+ [clause_pats(T), [clause_guard(T)],
+ [clause_body(T)]];
+ alias ->
+ [[alias_var(T)], [alias_pat(T)]];
+ 'fun' ->
+ [fun_vars(T), [fun_body(T)]];
+ 'receive' ->
+ [receive_clauses(T), [receive_timeout(T)],
+ [receive_action(T)]];
+ 'try' ->
+ [[try_arg(T)], try_vars(T), [try_body(T)],
+ try_evars(T), [try_handler(T)]];
+ 'catch' ->
+ [[catch_body(T)]];
+ letrec ->
+ Es = unfold_tuples(letrec_defs(T)),
+ [Es, [letrec_body(T)]];
+ module ->
+ As = unfold_tuples(module_attrs(T)),
+ Es = unfold_tuples(module_defs(T)),
+ [[module_name(T)], module_exports(T), As, Es]
+ end
+ end.
+
+
+%% @spec update_tree(Old::cerl(), Groups::[[cerl()]]) -> cerl()
+%%
+%% @doc Creates a syntax tree with the given subtrees, and the same
+%% type and annotations as the <code>Old</code> node. This is
+%% equivalent to <code>ann_make_tree(get_ann(Node), type(Node),
+%% Groups)</code>, but potentially more efficient.
+%%
+%% @see update_tree/3
+%% @see ann_make_tree/3
+%% @see get_ann/1
+%% @see type/1
+
+-spec update_tree(cerl(), [[cerl()],...]) -> cerl().
+
+update_tree(Node, Gs) ->
+ ann_make_tree(get_ann(Node), type(Node), Gs).
+
+
+%% @spec update_tree(Old::cerl(), Type::ctype(), Groups::[[cerl()]]) ->
+%% cerl()
+%%
+%% @doc Creates a syntax tree with the given type and subtrees, and
+%% the same annotations as the <code>Old</code> node. This is
+%% equivalent to <code>ann_make_tree(get_ann(Node), Type,
+%% Groups)</code>, but potentially more efficient.
+%%
+%% @see update_tree/2
+%% @see ann_make_tree/3
+%% @see get_ann/1
+
+-spec update_tree(cerl(), ctype(), [[cerl()],...]) -> cerl().
+
+update_tree(Node, Type, Gs) ->
+ ann_make_tree(get_ann(Node), Type, Gs).
+
+
+%% @spec make_tree(Type::ctype(), Groups::[[cerl()]]) -> cerl()
+%%
+%% @doc Creates a syntax tree with the given type and subtrees.
+%% <code>Type</code> must be a node type name
+%% (cf. <code>type/1</code>) that does not denote a leaf node type
+%% (cf. <code>is_leaf/1</code>). <code>Groups</code> must be a
+%% <em>nonempty</em> list of groups of syntax trees, representing the
+%% subtrees of a node of the given type, in left-to-right order as
+%% they would occur in the printed program text, grouped by category
+%% as done by <code>subtrees/1</code>.
+%%
+%% <p>The result of <code>ann_make_tree(get_ann(Node), type(Node),
+%% subtrees(Node))</code> (cf. <code>update_tree/2</code>) represents
+%% the same source code text as the original <code>Node</code>,
+%% assuming that <code>subtrees(Node)</code> yields a nonempty
+%% list. However, it does not necessarily have the exact same data
+%% representation as <code>Node</code>.</p>
+%%
+%% @see ann_make_tree/3
+%% @see type/1
+%% @see is_leaf/1
+%% @see subtrees/1
+%% @see update_tree/2
+
+-spec make_tree(ctype(), [[cerl()],...]) -> cerl().
+
+make_tree(Type, Gs) ->
+ ann_make_tree([], Type, Gs).
+
+
+%% @spec ann_make_tree(As::anns(), Type::ctype(),
+%% Groups::[[cerl()]]) -> cerl()
+%%
+%% @doc Creates a syntax tree with the given annotations, type and
+%% subtrees. See <code>make_tree/2</code> for details.
+%%
+%% @see make_tree/2
+
+-spec ann_make_tree(anns(), ctype(), [[cerl()],...]) -> cerl().
+
+ann_make_tree(As, values, [Es]) -> ann_c_values(As, Es);
+ann_make_tree(As, binary, [Ss]) -> ann_c_binary(As, Ss);
+ann_make_tree(As, bitstr, [[V],[S],[U],[T],[Fs]]) ->
+ ann_c_bitstr(As, V, S, U, T, Fs);
+ann_make_tree(As, cons, [[H], [T]]) -> ann_c_cons(As, H, T);
+ann_make_tree(As, tuple, [Es]) -> ann_c_tuple(As, Es);
+ann_make_tree(As, map, [Es]) -> ann_c_map(As, Es);
+ann_make_tree(As, map, [[A], Es]) -> ann_c_map(As, A, Es);
+ann_make_tree(As, map_pair, [[Op], [K], [V]]) -> ann_c_map_pair(As, Op, K, V);
+ann_make_tree(As, 'let', [Vs, [A], [B]]) -> ann_c_let(As, Vs, A, B);
+ann_make_tree(As, seq, [[A], [B]]) -> ann_c_seq(As, A, B);
+ann_make_tree(As, apply, [[Op], Es]) -> ann_c_apply(As, Op, Es);
+ann_make_tree(As, call, [[M], [N], Es]) -> ann_c_call(As, M, N, Es);
+ann_make_tree(As, primop, [[N], Es]) -> ann_c_primop(As, N, Es);
+ann_make_tree(As, 'case', [[A], Cs]) -> ann_c_case(As, A, Cs);
+ann_make_tree(As, clause, [Ps, [G], [B]]) -> ann_c_clause(As, Ps, G, B);
+ann_make_tree(As, alias, [[V], [P]]) -> ann_c_alias(As, V, P);
+ann_make_tree(As, 'fun', [Vs, [B]]) -> ann_c_fun(As, Vs, B);
+ann_make_tree(As, 'receive', [Cs, [T], [A]]) ->
+ ann_c_receive(As, Cs, T, A);
+ann_make_tree(As, 'try', [[E], Vs, [B], Evs, [H]]) ->
+ ann_c_try(As, E, Vs, B, Evs, H);
+ann_make_tree(As, 'catch', [[B]]) -> ann_c_catch(As, B);
+ann_make_tree(As, letrec, [Es, [B]]) ->
+ ann_c_letrec(As, fold_tuples(Es), B);
+ann_make_tree(As, module, [[N], Xs, Es, Ds]) ->
+ ann_c_module(As, N, Xs, fold_tuples(Es), fold_tuples(Ds)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec meta(Tree::cerl()) -> cerl()
+%%
+%% @doc Creates a meta-representation of a syntax tree. The result
+%% represents an Erlang expression "<code><em>MetaTree</em></code>"
+%% which, if evaluated, will yield a new syntax tree representing the
+%% same source code text as <code>Tree</code> (although the actual
+%% data representation may be different). The expression represented
+%% by <code>MetaTree</code> is <em>implementation independent</em>
+%% with regard to the data structures used by the abstract syntax tree
+%% implementation.
+%%
+%% <p>Any node in <code>Tree</code> whose node type is
+%% <code>var</code> (cf. <code>type/1</code>), and whose list of
+%% annotations (cf. <code>get_ann/1</code>) contains the atom
+%% <code>meta_var</code>, will remain unchanged in the resulting tree,
+%% except that exactly one occurrence of <code>meta_var</code> is
+%% removed from its annotation list.</p>
+%%
+%% <p>The main use of the function <code>meta/1</code> is to transform
+%% a data structure <code>Tree</code>, which represents a piece of
+%% program code, into a form that is <em>representation independent
+%% when printed</em>. E.g., suppose <code>Tree</code> represents a
+%% variable named "V". Then (assuming a function <code>print/1</code>
+%% for printing syntax trees), evaluating
+%% <code>print(abstract(Tree))</code> - simply using
+%% <code>abstract/1</code> to map the actual data structure onto a
+%% syntax tree representation - would output a string that might look
+%% something like "<code>{var, ..., 'V'}</code>", which is obviously
+%% dependent on the implementation of the abstract syntax trees. This
+%% could e.g. be useful for caching a syntax tree in a file. However,
+%% in some situations like in a program generator generator (with two
+%% "generator"), it may be unacceptable. Using
+%% <code>print(meta(Tree))</code> instead would output a
+%% <em>representation independent</em> syntax tree generating
+%% expression; in the above case, something like
+%% "<code>cerl:c_var('V')</code>".</p>
+%%
+%% <p>The implementation tries to generate compact code with respect
+%% to literals and lists.</p>
+%%
+%% @see abstract/1
+%% @see type/1
+%% @see get_ann/1
+
+-spec meta(cerl()) -> cerl().
+
+meta(Node) ->
+ %% First of all we check for metavariables:
+ case type(Node) of
+ var ->
+ case lists:member(meta_var, get_ann(Node)) of
+ false ->
+ meta_0(var, Node);
+ true ->
+ %% A meta-variable: remove the first found
+ %% 'meta_var' annotation, but otherwise leave
+ %% the node unchanged.
+ set_ann(Node, lists:delete(meta_var, get_ann(Node)))
+ end;
+ Type ->
+ meta_0(Type, Node)
+ end.
+
+meta_0(Type, Node) ->
+ case get_ann(Node) of
+ [] ->
+ meta_1(Type, Node);
+ As ->
+ meta_call(set_ann, [meta_1(Type, Node), abstract(As)])
+ end.
+
+meta_1(literal, Node) ->
+ %% We handle atomic literals separately, to get a bit
+ %% more compact code. For the rest, we use 'abstract'.
+ case concrete(Node) of
+ V when is_atom(V) ->
+ meta_call(c_atom, [Node]);
+ V when is_integer(V) ->
+ meta_call(c_int, [Node]);
+ V when is_float(V) ->
+ meta_call(c_float, [Node]);
+ [] ->
+ meta_call(c_nil, []);
+ _ ->
+ meta_call(abstract, [Node])
+ end;
+meta_1(var, Node) ->
+ %% A normal variable or function name.
+ meta_call(c_var, [abstract(var_name(Node))]);
+meta_1(values, Node) ->
+ meta_call(c_values,
+ [make_list(meta_list(values_es(Node)))]);
+meta_1(binary, Node) ->
+ meta_call(c_binary,
+ [make_list(meta_list(binary_segments(Node)))]);
+meta_1(bitstr, Node) ->
+ meta_call(c_bitstr,
+ [meta(bitstr_val(Node)),
+ meta(bitstr_size(Node)),
+ meta(bitstr_unit(Node)),
+ meta(bitstr_type(Node)),
+ meta(bitstr_flags(Node))]);
+meta_1(cons, Node) ->
+ %% The list is split up if some sublist has annotatations. If
+ %% we get exactly one element, we generate a 'c_cons' call
+ %% instead of 'make_list' to reconstruct the node.
+ case split_list(Node) of
+ {[H], Node1} ->
+ meta_call(c_cons, [meta(H), meta(Node1)]);
+ {L, Node1} ->
+ meta_call(make_list,
+ [make_list(meta_list(L)), meta(Node1)])
+ end;
+meta_1(tuple, Node) ->
+ meta_call(c_tuple,
+ [make_list(meta_list(tuple_es(Node)))]);
+meta_1('let', Node) ->
+ meta_call(c_let,
+ [make_list(meta_list(let_vars(Node))),
+ meta(let_arg(Node)), meta(let_body(Node))]);
+meta_1(seq, Node) ->
+ meta_call(c_seq,
+ [meta(seq_arg(Node)), meta(seq_body(Node))]);
+meta_1(apply, Node) ->
+ meta_call(c_apply,
+ [meta(apply_op(Node)),
+ make_list(meta_list(apply_args(Node)))]);
+meta_1(call, Node) ->
+ meta_call(c_call,
+ [meta(call_module(Node)), meta(call_name(Node)),
+ make_list(meta_list(call_args(Node)))]);
+meta_1(primop, Node) ->
+ meta_call(c_primop,
+ [meta(primop_name(Node)),
+ make_list(meta_list(primop_args(Node)))]);
+meta_1('case', Node) ->
+ meta_call(c_case,
+ [meta(case_arg(Node)),
+ make_list(meta_list(case_clauses(Node)))]);
+meta_1(clause, Node) ->
+ meta_call(c_clause,
+ [make_list(meta_list(clause_pats(Node))),
+ meta(clause_guard(Node)),
+ meta(clause_body(Node))]);
+meta_1(alias, Node) ->
+ meta_call(c_alias,
+ [meta(alias_var(Node)), meta(alias_pat(Node))]);
+meta_1('fun', Node) ->
+ meta_call(c_fun,
+ [make_list(meta_list(fun_vars(Node))),
+ meta(fun_body(Node))]);
+meta_1('receive', Node) ->
+ meta_call(c_receive,
+ [make_list(meta_list(receive_clauses(Node))),
+ meta(receive_timeout(Node)),
+ meta(receive_action(Node))]);
+meta_1('try', Node) ->
+ meta_call(c_try,
+ [meta(try_arg(Node)),
+ make_list(meta_list(try_vars(Node))),
+ meta(try_body(Node)),
+ make_list(meta_list(try_evars(Node))),
+ meta(try_handler(Node))]);
+meta_1('catch', Node) ->
+ meta_call(c_catch, [meta(catch_body(Node))]);
+meta_1(letrec, Node) ->
+ meta_call(c_letrec,
+ [make_list([c_tuple([meta(N), meta(F)])
+ || {N, F} <- letrec_defs(Node)]),
+ meta(letrec_body(Node))]);
+meta_1(module, Node) ->
+ meta_call(c_module,
+ [meta(module_name(Node)),
+ make_list(meta_list(module_exports(Node))),
+ make_list([c_tuple([meta(A), meta(V)])
+ || {A, V} <- module_attrs(Node)]),
+ make_list([c_tuple([meta(N), meta(F)])
+ || {N, F} <- module_defs(Node)])]).
+
+meta_call(F, As) ->
+ c_call(c_atom(?MODULE), c_atom(F), As).
+
+meta_list([T | Ts]) ->
+ [meta(T) | meta_list(Ts)];
+meta_list([]) ->
+ [].
+
+split_list(Node) ->
+ split_list(set_ann(Node, []), []).
+
+split_list(Node, L) ->
+ A = get_ann(Node),
+ case type(Node) of
+ cons when A =:= [] ->
+ split_list(cons_tl(Node), [cons_hd(Node) | L]);
+ _ ->
+ {lists:reverse(L), Node}
+ end.
+
+
+%% ---------------------------------------------------------------------
+
+%% General utilities
+
+is_lit_list([#c_literal{} | Es]) ->
+ is_lit_list(Es);
+is_lit_list([_ | _]) ->
+ false;
+is_lit_list([]) ->
+ true.
+
+lit_list_vals([#c_literal{val = V} | Es]) ->
+ [V | lit_list_vals(Es)];
+lit_list_vals([]) ->
+ [].
+
+-spec make_lit_list([litval()]) -> [c_literal()].
+
+make_lit_list([V | Vs]) ->
+ [#c_literal{val = V} | make_lit_list(Vs)];
+make_lit_list([]) ->
+ [].
+
+%% The following tests are the same as done by 'io_lib:char_list' and
+%% 'io_lib:printable_list', respectively, but for a single character.
+
+is_char_value(V) when V >= $\000, V =< $\377 -> true;
+is_char_value(_) -> false.
+
+is_print_char_value(V) when V >= $\040, V =< $\176 -> true;
+is_print_char_value(V) when V >= $\240, V =< $\377 -> true;
+is_print_char_value(V) when V =:= $\b -> true;
+is_print_char_value(V) when V =:= $\d -> true;
+is_print_char_value(V) when V =:= $\e -> true;
+is_print_char_value(V) when V =:= $\f -> true;
+is_print_char_value(V) when V =:= $\n -> true;
+is_print_char_value(V) when V =:= $\r -> true;
+is_print_char_value(V) when V =:= $\s -> true;
+is_print_char_value(V) when V =:= $\t -> true;
+is_print_char_value(V) when V =:= $\v -> true;
+is_print_char_value(V) when V =:= $\" -> true;
+is_print_char_value(V) when V =:= $\' -> true; %' stupid Emacs.
+is_print_char_value(V) when V =:= $\\ -> true;
+is_print_char_value(_) -> false.
+
+is_char_list([V | Vs]) when is_integer(V) ->
+ is_char_value(V) andalso is_char_list(Vs);
+is_char_list([]) ->
+ true;
+is_char_list(_) ->
+ false.
+
+is_print_char_list([V | Vs]) when is_integer(V) ->
+ is_print_char_value(V) andalso is_print_char_list(Vs);
+is_print_char_list([]) ->
+ true;
+is_print_char_list(_) ->
+ false.
+
+unfold_tuples([{X, Y} | Ps]) ->
+ [X, Y | unfold_tuples(Ps)];
+unfold_tuples([]) ->
+ [].
+
+fold_tuples([X, Y | Es]) ->
+ [{X, Y} | fold_tuples(Es)];
+fold_tuples([]) ->
+ [].
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/recrec/core_parse.hrl b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/core_parse.hrl
new file mode 100644
index 0000000000..5823622f05
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/core_parse.hrl
@@ -0,0 +1,122 @@
+%%
+%% %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%
+%%
+%% Purpose : Core Erlang syntax trees as records.
+
+%% It would be nice to incorporate some generic functions as well but
+%% this could make including this file difficult.
+
+%% Note: the annotation list is *always* the first record field.
+%% Thus it is possible to define the macros:
+%% -define(get_ann(X), element(2, X)).
+%% -define(set_ann(X, Y), setelement(2, X, Y)).
+
+%% The record definitions appear alphabetically
+
+-record(c_alias, {anno=[] :: cerl:anns(),
+ var :: cerl:c_var(),
+ pat :: cerl:cerl()}).
+
+-record(c_apply, {anno=[] :: cerl:anns(),
+ op :: cerl:c_var(),
+ args :: [cerl:cerl()]}).
+
+-record(c_binary, {anno=[] :: cerl:anns(),
+ segments :: [cerl:c_bitstr()]}).
+
+-record(c_bitstr, {anno=[], val, % val :: Tree,
+ size, % size :: Tree,
+ unit, % unit :: Tree,
+ type, % type :: Tree,
+ flags}). % flags :: Tree
+
+-record(c_call, {anno=[], module, % module :: cerl:cerl(),
+ name, % name :: cerl:cerl(),
+ args}). % args :: [cerl:cerl()]
+
+-record(c_case, {anno=[] :: cerl:anns(),
+ arg :: cerl:cerl(),
+ clauses :: [cerl:cerl()]}).
+
+-record(c_catch, {anno=[] :: cerl:anns(), body :: cerl:cerl()}).
+
+-record(c_clause, {anno=[] :: cerl:anns(),
+ pats, % :: [cerl:cerl()], % pats :: [Tree],
+ guard, % :: cerl:cerl(), % guard :: Tree,
+ body}). % :: cerl:cerl()}). % body :: Tree
+
+-record(c_cons, {anno=[] :: cerl:anns(),
+ hd :: cerl:cerl(),
+ tl :: cerl:cerl()}).
+
+-record(c_fun, {anno=[] :: cerl:anns(),
+ vars :: [cerl:c_var()],
+ body :: cerl:cerl()}).
+
+-record(c_let, {anno=[] :: cerl:anns(),
+ vars :: [cerl:c_var()],
+ arg :: cerl:cerl(),
+ body :: cerl:cerl()}).
+
+-record(c_letrec, {anno=[] :: cerl:anns(),
+ defs :: cerl:defs(),
+ body :: cerl:cerl()}).
+
+-record(c_literal, {anno=[] :: cerl:anns(), val :: cerl:litval()}).
+
+-record(c_map, {anno=[] :: cerl:anns(),
+ arg=#c_literal{val=#{}} :: cerl:c_var() | cerl:c_literal(),
+ es :: [cerl:c_map_pair()],
+ is_pat=false :: boolean()}).
+
+-record(c_map_pair, {anno=[] :: cerl:anns(),
+ op, %:: #c_literal{val::'assoc'} | #c_literal{val::'exact'},
+ key,
+ val}).
+
+-record(c_module, {anno=[] :: cerl:anns(),
+ name :: cerl:c_literal(),
+ exports :: [cerl:c_var()],
+ attrs :: cerl:attrs(),
+ defs :: cerl:defs()}).
+
+-record(c_primop, {anno=[] :: cerl:anns(),
+ name :: cerl:c_literal(),
+ args :: [cerl:cerl()]}).
+
+-record(c_receive, {anno=[]:: cerl:anns(),
+ clauses, % clauses :: [Tree],
+ timeout, % timeout :: Tree,
+ action}). % action :: Tree
+
+-record(c_seq, {anno=[] :: cerl:anns(),
+ arg, % arg :: cerl:cerl(),
+ body}). % body :: cerl:cerl()
+
+-record(c_try, {anno=[], arg, % arg :: cerl:cerl(),
+ vars, % vars :: [cerl:c_var()],
+ body, % body :: cerl:cerl(),
+ evars, % evars :: [cerl:c_var()],
+ handler}). % handler :: cerl:cerl()
+
+-record(c_tuple, {anno=[] :: cerl:anns(), es :: [cerl:cerl()]}).
+
+-record(c_values, {anno=[] :: cerl:anns(), es :: [cerl:cerl()]}).
+
+-record(c_var, {anno=[] :: cerl:anns(), name :: cerl:var_name()}).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer.hrl b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer.hrl
new file mode 100644
index 0000000000..1139044ec9
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer.hrl
@@ -0,0 +1,180 @@
+%%% This is an -*- Erlang -*- file.
+%%%
+%%% %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%
+%%%
+%%%-------------------------------------------------------------------
+%%% File : dialyzer.hrl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Kostis Sagonas <[email protected]>
+%%% Description : Header file for Dialyzer.
+%%%
+%%% Created : 1 Oct 2004 by Kostis Sagonas <[email protected]>
+%%%-------------------------------------------------------------------
+
+-define(RET_NOTHING_SUSPICIOUS, 0).
+-define(RET_INTERNAL_ERROR, 1).
+-define(RET_DISCREPANCIES, 2).
+
+-type dial_ret() :: ?RET_NOTHING_SUSPICIOUS
+ | ?RET_INTERNAL_ERROR
+ | ?RET_DISCREPANCIES.
+
+%%--------------------------------------------------------------------
+%% Warning classification
+%%--------------------------------------------------------------------
+
+-define(WARN_RETURN_NO_RETURN, warn_return_no_exit).
+-define(WARN_RETURN_ONLY_EXIT, warn_return_only_exit).
+-define(WARN_NOT_CALLED, warn_not_called).
+-define(WARN_NON_PROPER_LIST, warn_non_proper_list).
+-define(WARN_FUN_APP, warn_fun_app).
+-define(WARN_MATCHING, warn_matching).
+-define(WARN_OPAQUE, warn_opaque).
+-define(WARN_FAILING_CALL, warn_failing_call).
+-define(WARN_BIN_CONSTRUCTION, warn_bin_construction).
+-define(WARN_CONTRACT_TYPES, warn_contract_types).
+-define(WARN_CONTRACT_SYNTAX, warn_contract_syntax).
+-define(WARN_CONTRACT_NOT_EQUAL, warn_contract_not_equal).
+-define(WARN_CONTRACT_SUBTYPE, warn_contract_subtype).
+-define(WARN_CONTRACT_SUPERTYPE, warn_contract_supertype).
+-define(WARN_CONTRACT_RANGE, warn_contract_range).
+-define(WARN_CALLGRAPH, warn_callgraph).
+-define(WARN_UNMATCHED_RETURN, warn_umatched_return).
+-define(WARN_RACE_CONDITION, warn_race_condition).
+-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:
+%% 1. It is the set of warnings that will be collected.
+%% 2. It is also the set of tags for warnings that will be returned.
+%%
+-type dial_warn_tag() :: ?WARN_RETURN_NO_RETURN | ?WARN_RETURN_ONLY_EXIT
+ | ?WARN_NOT_CALLED | ?WARN_NON_PROPER_LIST
+ | ?WARN_MATCHING | ?WARN_OPAQUE | ?WARN_FUN_APP
+ | ?WARN_FAILING_CALL | ?WARN_BIN_CONSTRUCTION
+ | ?WARN_CONTRACT_TYPES | ?WARN_CONTRACT_SYNTAX
+ | ?WARN_CONTRACT_NOT_EQUAL | ?WARN_CONTRACT_SUBTYPE
+ | ?WARN_CONTRACT_SUPERTYPE | ?WARN_CALLGRAPH
+ | ?WARN_UNMATCHED_RETURN | ?WARN_RACE_CONDITION
+ | ?WARN_BEHAVIOUR | ?WARN_CONTRACT_RANGE
+ | ?WARN_UNDEFINED_CALLBACK | ?WARN_UNKNOWN
+ | ?WARN_MAP_CONSTRUCTION.
+
+%%
+%% This is the representation of each warning as they will be returned
+%% to dialyzer's callers
+%%
+-type file_line() :: {file:filename(), non_neg_integer()}.
+-type dial_warning() :: {dial_warn_tag(), file_line(), {atom(), [term()]}}.
+
+%%
+%% This is the representation of each warning before suppressions have
+%% been applied
+%%
+-type m_or_mfa() :: module() % warnings not associated with any function
+ | mfa().
+-type warning_info() :: {file:filename(), non_neg_integer(), m_or_mfa()}.
+-type raw_warning() :: {dial_warn_tag(), warning_info(), {atom(), [term()]}}.
+
+%%
+%% This is the representation of dialyzer's internal errors
+%%
+-type dial_error() :: any(). %% XXX: underspecified
+
+%%--------------------------------------------------------------------
+%% Basic types used either in the record definitions below or in other
+%% parts of the application
+%%--------------------------------------------------------------------
+
+-type anal_type() :: 'succ_typings' | 'plt_build'.
+-type anal_type1() :: anal_type() | 'plt_add' | 'plt_check' | 'plt_remove'.
+-type contr_constr() :: {'subtype', erl_types:erl_type(), erl_types:erl_type()}.
+-type contract_pair() :: {erl_types:erl_type(), [contr_constr()]}.
+-type dial_define() :: {atom(), term()}.
+-type dial_option() :: {atom(), term()}.
+-type dial_options() :: [dial_option()].
+-type fopt() :: 'basename' | 'fullpath'.
+-type format() :: 'formatted' | 'raw'.
+-type label() :: non_neg_integer().
+-type dial_warn_tags():: ordsets:ordset(dial_warn_tag()).
+-type rep_mode() :: 'quiet' | 'normal' | 'verbose'.
+-type start_from() :: 'byte_code' | 'src_code'.
+-type mfa_or_funlbl() :: label() | mfa().
+-type solver() :: 'v1' | 'v2'.
+
+%%--------------------------------------------------------------------
+%% Record declarations used by various files
+%%--------------------------------------------------------------------
+
+-type doc_plt() :: 'undefined' | dialyzer_plt:plt().
+
+-record(analysis, {analysis_pid :: pid() | 'undefined',
+ type = succ_typings :: anal_type(),
+ defines = [] :: [dial_define()],
+ doc_plt :: doc_plt(),
+ files = [] :: [file:filename()],
+ include_dirs = [] :: [file:filename()],
+ start_from = byte_code :: start_from(),
+ plt :: dialyzer_plt:plt(),
+ use_contracts = true :: boolean(),
+ race_detection = false :: boolean(),
+ behaviours_chk = false :: boolean(),
+ timing = false :: boolean() | 'debug',
+ timing_server = none :: dialyzer_timing:timing_server(),
+ callgraph_file = "" :: file:filename(),
+ solvers :: [solver()]}).
+
+-record(options, {files = [] :: [file:filename()],
+ files_rec = [] :: [file:filename()],
+ analysis_type = succ_typings :: anal_type1(),
+ timing = false :: boolean() | 'debug',
+ defines = [] :: [dial_define()],
+ from = byte_code :: start_from(),
+ get_warnings = maybe :: boolean() | 'maybe',
+ init_plts = [] :: [file:filename()],
+ include_dirs = [] :: [file:filename()],
+ output_plt = none :: 'none' | file:filename(),
+ legal_warnings = ordsets:new() :: dial_warn_tags(),
+ report_mode = normal :: rep_mode(),
+ erlang_mode = false :: boolean(),
+ use_contracts = true :: boolean(),
+ output_file = none :: 'none' | file:filename(),
+ output_format = formatted :: format(),
+ filename_opt = basename :: fopt(),
+ callgraph_file = "" :: file:filename(),
+ check_plt = true :: boolean(),
+ solvers = [] :: [solver()]}).
+
+-record(contract, {contracts = [] :: [contract_pair()],
+ args = [] :: [erl_types:erl_type()],
+ forms = [] :: [{_, _}]}).
+
+%%--------------------------------------------------------------------
+
+-define(timing(Server, Msg, Var, Expr),
+ begin
+ dialyzer_timing:start_stamp(Server, Msg),
+ Var = Expr,
+ dialyzer_timing:end_stamp(Server),
+ Var
+ end).
+-define(timing(Server, Msg, Expr), ?timing(Server, Msg, _T, Expr)).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer_dataflow.erl b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer_dataflow.erl
new file mode 100644
index 0000000000..9399789464
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer_dataflow.erl
@@ -0,0 +1,3802 @@
+%% -*- erlang-indent-level: 2 -*-
+%%--------------------------------------------------------------------
+%% %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%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File : dialyzer_dataflow.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 19 Apr 2005 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+
+-module(dialyzer_dataflow).
+
+-export([get_fun_types/5, get_warnings/5, format_args/3]).
+
+%% Data structure interfaces.
+-export([state__add_warning/2, state__cleanup/1,
+ state__duplicate/1, dispose_state/1,
+ state__get_callgraph/1, state__get_races/1,
+ state__get_records/1, state__put_callgraph/2,
+ state__put_races/2, state__records_only/1,
+ state__find_function/2]).
+
+-export_type([state/0]).
+
+-include("dialyzer.hrl").
+
+-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,
+ t_sup/1, t_sup/2]).
+
+-import(erl_types,
+ [any_none/1, t_any/0, t_atom/0, t_atom/1, t_atom_vals/1, t_atom_vals/2,
+ t_binary/0, t_boolean/0,
+ t_bitstr/0, t_bitstr/2, t_bitstr_concat/1, t_bitstr_match/2,
+ t_cons/0, t_cons/2, t_cons_hd/2, t_cons_tl/2,
+ t_contains_opaque/2,
+ t_find_opaque_mismatch/3, t_float/0, t_from_range/2, t_from_term/1,
+ t_fun/0, t_fun/2, t_fun_args/1, t_fun_args/2, t_fun_range/1,
+ t_fun_range/2, t_integer/0, t_integers/1,
+ t_is_any/1, t_is_atom/1, t_is_atom/2, t_is_any_atom/3,
+ t_is_boolean/2,
+ t_is_integer/2, t_is_list/1,
+ t_is_nil/2, t_is_none/1, t_is_none_or_unit/1,
+ t_is_number/2, t_is_reference/2, t_is_pid/2, t_is_port/2,
+ t_is_unit/1,
+ t_limit/2, t_list/0, t_list_elements/2,
+ t_maybe_improper_list/0, t_module/0,
+ t_none/0, t_non_neg_integer/0, t_number/0, t_number_vals/2,
+ t_pid/0, t_port/0, t_product/1, t_reference/0,
+ t_to_string/2, t_to_tlist/1,
+ 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/0, t_map/1, t_is_singleton/2
+ ]).
+
+%%-define(DEBUG, true).
+%%-define(DEBUG_PP, true).
+%%-define(DEBUG_TIME, true).
+
+-ifdef(DEBUG).
+-import(erl_types, [t_to_string/1]).
+-define(debug(S_, L_), io:format(S_, L_)).
+-else.
+-define(debug(S_, L_), ok).
+-endif.
+
+%%--------------------------------------------------------------------
+
+-type type() :: erl_types:erl_type().
+-type types() :: erl_types:type_table().
+
+-type curr_fun() :: 'undefined' | 'top' | mfa_or_funlbl().
+
+-define(no_arg, no_arg).
+
+-define(TYPE_LIMIT, 3).
+
+-define(BITS, 128).
+
+%% Types with comment 'race' are due to dialyzer_races.erl.
+-record(state, {callgraph :: dialyzer_callgraph:callgraph()
+ | 'undefined', % race
+ codeserver :: dialyzer_codeserver:codeserver()
+ | 'undefined', % race
+ envs :: env_tab()
+ | 'undefined', % race
+ fun_tab :: fun_tab()
+ | 'undefined', % race
+ fun_homes :: dict:dict(label(), mfa())
+ | 'undefined', % race
+ plt :: dialyzer_plt:plt()
+ | 'undefined', % race
+ opaques :: [type()]
+ | 'undefined', % race
+ races = dialyzer_races:new() :: dialyzer_races:races(),
+ records = dict:new() :: types(),
+ tree_map :: dict:dict(label(), cerl:cerl())
+ | 'undefined', % race
+ warning_mode = false :: boolean(),
+ warnings = [] :: [raw_warning()],
+ work :: {[_], [_], sets:set()}
+ | 'undefined', % race
+ module :: module(),
+ curr_fun :: curr_fun()
+ }).
+
+-record(map, {map = maps:new() :: type_tab(),
+ subst = maps:new() :: subst_tab(),
+ modified = [] :: [Key :: term()],
+ modified_stack = [] :: [{[Key :: term()],reference()}],
+ ref = undefined :: reference() | undefined}).
+
+-type env_tab() :: dict:dict(label(), #map{}).
+-type fun_entry() :: {Args :: [type()], RetType :: type()}.
+-type fun_tab() :: dict:dict('top' | label(),
+ {'not_handled', fun_entry()} | fun_entry()).
+-type key() :: label() | cerl:cerl().
+-type type_tab() :: #{key() => type()}.
+-type subst_tab() :: #{key() => cerl:cerl()}.
+
+%% Exported Types
+
+-opaque state() :: #state{}.
+
+%%--------------------------------------------------------------------
+
+-type fun_types() :: dict:dict(label(), type()).
+
+-spec get_warnings(cerl:c_module(), dialyzer_plt:plt(),
+ dialyzer_callgraph:callgraph(),
+ dialyzer_codeserver:codeserver(),
+ types()) ->
+ {[raw_warning()], fun_types()}.
+
+get_warnings(Tree, Plt, Callgraph, Codeserver, Records) ->
+ State1 = analyze_module(Tree, Plt, Callgraph, Codeserver, Records, true),
+ State2 = state__renew_warnings(state__get_warnings(State1), State1),
+ State3 = state__get_race_warnings(State2),
+ {State3#state.warnings, state__all_fun_types(State3)}.
+
+-spec get_fun_types(cerl:c_module(), dialyzer_plt:plt(),
+ dialyzer_callgraph:callgraph(),
+ dialyzer_codeserver:codeserver(),
+ types()) -> fun_types().
+
+get_fun_types(Tree, Plt, Callgraph, Codeserver, Records) ->
+ State = analyze_module(Tree, Plt, Callgraph, Codeserver, Records, false),
+ state__all_fun_types(State).
+
+%%% ===========================================================================
+%%%
+%%% The analysis.
+%%%
+%%% ===========================================================================
+
+analyze_module(Tree, Plt, Callgraph, Codeserver, Records, GetWarnings) ->
+ debug_pp(Tree, false),
+ Module = cerl:atom_val(cerl:module_name(Tree)),
+ TopFun = cerl:ann_c_fun([{label, top}], [], Tree),
+ State = state__new(Callgraph, Codeserver, TopFun, Plt, Module, Records),
+ State1 = state__race_analysis(not GetWarnings, State),
+ State2 = analyze_loop(State1),
+ case GetWarnings of
+ true ->
+ State3 = state__set_warning_mode(State2),
+ State4 = analyze_loop(State3),
+ dialyzer_races:race(State4);
+ false ->
+ State2
+ end.
+
+analyze_loop(State) ->
+ case state__get_work(State) of
+ none -> state__set_curr_fun(undefined, State);
+ {Fun, NewState0} ->
+ NewState1 = state__set_curr_fun(get_label(Fun), NewState0),
+ {ArgTypes, IsCalled} = state__get_args_and_status(Fun, NewState1),
+ case not IsCalled of
+ true ->
+ ?debug("Not handling (not called) ~w: ~s\n",
+ [NewState1#state.curr_fun,
+ t_to_string(t_product(ArgTypes))]),
+ analyze_loop(NewState1);
+ false ->
+ case state__fun_env(Fun, NewState1) of
+ none ->
+ ?debug("Not handling (no env) ~w: ~s\n",
+ [NewState1#state.curr_fun,
+ t_to_string(t_product(ArgTypes))]),
+ analyze_loop(NewState1);
+ Map ->
+ ?debug("Handling fun ~p: ~s\n",
+ [NewState1#state.curr_fun,
+ t_to_string(state__fun_type(Fun, NewState1))]),
+ Vars = cerl:fun_vars(Fun),
+ Map1 = enter_type_lists(Vars, ArgTypes, Map),
+ Body = cerl:fun_body(Fun),
+ FunLabel = get_label(Fun),
+ IsRaceAnalysisEnabled = is_race_analysis_enabled(State),
+ NewState3 =
+ case IsRaceAnalysisEnabled of
+ true ->
+ NewState2 = state__renew_curr_fun(
+ state__lookup_name(FunLabel, NewState1), FunLabel,
+ NewState1),
+ state__renew_race_list([], 0, NewState2);
+ false -> NewState1
+ end,
+ {NewState4, _Map2, BodyType} =
+ traverse(Body, Map1, NewState3),
+ ?debug("Done analyzing: ~w:~s\n",
+ [NewState1#state.curr_fun,
+ t_to_string(t_fun(ArgTypes, BodyType))]),
+ NewState5 =
+ case IsRaceAnalysisEnabled of
+ true -> renew_race_code(NewState4);
+ false -> NewState4
+ end,
+ NewState6 =
+ state__update_fun_entry(Fun, ArgTypes, BodyType, NewState5),
+ ?debug("done adding stuff for ~w\n",
+ [state__lookup_name(get_label(Fun), State)]),
+ analyze_loop(NewState6)
+ end
+ end
+ end.
+
+traverse(Tree, Map, State) ->
+ ?debug("Handling ~p\n", [cerl:type(Tree)]),
+ %% debug_pp_map(Map),
+ case cerl:type(Tree) of
+ alias ->
+ %% This only happens when checking for illegal record patterns
+ %% so the handling is a bit rudimentary.
+ traverse(cerl:alias_pat(Tree), Map, State);
+ apply ->
+ handle_apply(Tree, Map, State);
+ binary ->
+ Segs = cerl:binary_segments(Tree),
+ {State1, Map1, SegTypes} = traverse_list(Segs, Map, State),
+ {State1, Map1, t_bitstr_concat(SegTypes)};
+ bitstr ->
+ handle_bitstr(Tree, Map, State);
+ call ->
+ handle_call(Tree, Map, State);
+ 'case' ->
+ handle_case(Tree, Map, State);
+ 'catch' ->
+ {State1, _Map1, _} = traverse(cerl:catch_body(Tree), Map, State),
+ {State1, Map, t_any()};
+ cons ->
+ handle_cons(Tree, Map, State);
+ 'fun' ->
+ Type = state__fun_type(Tree, State),
+ case state__warning_mode(State) of
+ true -> {State, Map, Type};
+ false ->
+ State2 = state__add_work(get_label(Tree), State),
+ State3 = state__update_fun_env(Tree, Map, State2),
+ {State3, Map, Type}
+ end;
+ 'let' ->
+ handle_let(Tree, Map, State);
+ letrec ->
+ Defs = cerl:letrec_defs(Tree),
+ Body = cerl:letrec_body(Tree),
+ %% By not including the variables in scope we can assure that we
+ %% will get the current function type when using the variables.
+ FoldFun = fun({Var, Fun}, {AccState, AccMap}) ->
+ {NewAccState, NewAccMap0, FunType} =
+ traverse(Fun, AccMap, AccState),
+ NewAccMap = enter_type(Var, FunType, NewAccMap0),
+ {NewAccState, NewAccMap}
+ end,
+ {State1, Map1} = lists:foldl(FoldFun, {State, Map}, Defs),
+ traverse(Body, Map1, State1);
+ literal ->
+ Type = literal_type(Tree),
+ {State, Map, Type};
+ module ->
+ handle_module(Tree, Map, State);
+ primop ->
+ Type =
+ case cerl:atom_val(cerl:primop_name(Tree)) of
+ match_fail -> t_none();
+ raise -> t_none();
+ bs_init_writable -> t_from_term(<<>>);
+ Other -> erlang:error({'Unsupported primop', Other})
+ end,
+ {State, Map, Type};
+ 'receive' ->
+ handle_receive(Tree, Map, State);
+ seq ->
+ Arg = cerl:seq_arg(Tree),
+ Body = cerl:seq_body(Tree),
+ {State1, Map1, ArgType} = SMA = traverse(Arg, Map, State),
+ case t_is_none_or_unit(ArgType) of
+ true ->
+ SMA;
+ false ->
+ State2 =
+ case
+ t_is_any(ArgType)
+ orelse t_is_simple(ArgType, State)
+ orelse is_call_to_send(Arg)
+ orelse is_lc_simple_list(Arg, ArgType, State)
+ of
+ true -> % do not warn in these cases
+ State1;
+ false ->
+ state__add_warning(State1, ?WARN_UNMATCHED_RETURN, Arg,
+ {unmatched_return,
+ [format_type(ArgType, State1)]})
+ end,
+ traverse(Body, Map1, State2)
+ end;
+ 'try' ->
+ handle_try(Tree, Map, State);
+ tuple ->
+ handle_tuple(Tree, Map, State);
+ map ->
+ handle_map(Tree, Map, State);
+ values ->
+ Elements = cerl:values_es(Tree),
+ {State1, Map1, EsType} = traverse_list(Elements, Map, State),
+ Type = t_product(EsType),
+ {State1, Map1, Type};
+ var ->
+ ?debug("Looking up unknown variable: ~p\n", [Tree]),
+ case state__lookup_type_for_letrec(Tree, State) of
+ error ->
+ LType = lookup_type(Tree, Map),
+ {State, Map, LType};
+ {ok, Type} -> {State, Map, Type}
+ end;
+ Other ->
+ erlang:error({'Unsupported type', Other})
+ end.
+
+traverse_list(Trees, Map, State) ->
+ traverse_list(Trees, Map, State, []).
+
+traverse_list([Tree|Tail], Map, State, Acc) ->
+ {State1, Map1, Type} = traverse(Tree, Map, State),
+ traverse_list(Tail, Map1, State1, [Type|Acc]);
+traverse_list([], Map, State, Acc) ->
+ {State, Map, lists:reverse(Acc)}.
+
+%%________________________________________
+%%
+%% Special instructions
+%%
+
+handle_apply(Tree, Map, State) ->
+ Args = cerl:apply_args(Tree),
+ Op = cerl:apply_op(Tree),
+ {State0, Map1, ArgTypes} = traverse_list(Args, Map, State),
+ {State1, Map2, OpType} = traverse(Op, Map1, State0),
+ case any_none(ArgTypes) of
+ true ->
+ {State1, Map2, t_none()};
+ false ->
+ FunList =
+ case state__lookup_call_site(Tree, State) of
+ error -> [external]; %% so that we go directly in the fallback
+ {ok, List} -> List
+ end,
+ FunInfoList = [{local, state__fun_info(Fun, State)} || Fun <- FunList],
+ case
+ handle_apply_or_call(FunInfoList, Args, ArgTypes, Map2, Tree, State1)
+ of
+ {had_external, State2} ->
+ %% Fallback: use whatever info we collected from traversing the op
+ %% instead of the result that has been generalized to t_any().
+ Arity = length(Args),
+ OpType1 = t_inf(OpType, t_fun(Arity, t_any())),
+ case t_is_none(OpType1) of
+ true ->
+ Msg = {fun_app_no_fun,
+ [format_cerl(Op), format_type(OpType, State2), Arity]},
+ State3 = state__add_warning(State2, ?WARN_FAILING_CALL,
+ Tree, Msg),
+ {State3, Map2, t_none()};
+ false ->
+ NewArgs = t_inf_lists(ArgTypes,
+ t_fun_args(OpType1, 'universe')),
+ case any_none(NewArgs) of
+ true ->
+ Msg = {fun_app_args,
+ [format_args(Args, ArgTypes, State),
+ format_type(OpType, State)]},
+ State3 = state__add_warning(State2, ?WARN_FAILING_CALL,
+ Tree, Msg),
+ {State3, enter_type(Op, OpType1, Map2), t_none()};
+ false ->
+ Map3 = enter_type_lists(Args, NewArgs, Map2),
+ Range0 = t_fun_range(OpType1, 'universe'),
+ Range =
+ case t_is_unit(Range0) of
+ true -> t_none();
+ false -> Range0
+ end,
+ {State2, enter_type(Op, OpType1, Map3), Range}
+ end
+ end;
+ Normal -> Normal
+ end
+ end.
+
+handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State) ->
+ None = t_none(),
+ %% Call-site analysis may be inaccurate and consider more funs than those that
+ %% are actually possible. If all of them are incorrect, then warnings can be
+ %% emitted. If at least one fun is ok, however, then no warning is emitted,
+ %% just in case the bad ones are not really possible. The last argument is
+ %% used for this, with the following encoding:
+ %% Initial value: {none, []}
+ %% First fun checked: {one, <List of warns>}
+ %% More funs checked: {many, <List of warns>}
+ %% A '{one, []}' can only become '{many, []}'.
+ %% If at any point an fun does not add warnings, then the list is also
+ %% replaced with an empty list.
+ handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State,
+ [None || _ <- ArgTypes], None, false, {none, []}).
+
+handle_apply_or_call([{local, external}|Left], Args, ArgTypes, Map, Tree, State,
+ _AccArgTypes, _AccRet, _HadExternal, Warns) ->
+ {HowMany, _} = Warns,
+ NewHowMany =
+ case HowMany of
+ none -> one;
+ _ -> many
+ end,
+ NewWarns = {NewHowMany, []},
+ handle_apply_or_call(Left, Args, ArgTypes, Map, Tree, State,
+ ArgTypes, t_any(), true, NewWarns);
+handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
+ Args, ArgTypes, Map, Tree,
+ #state{opaques = Opaques} = State,
+ AccArgTypes, AccRet, HadExternal, Warns) ->
+ Any = t_any(),
+ AnyArgs = [Any || _ <- Args],
+ GenSig = {AnyArgs, fun(_) -> t_any() end},
+ {CArgs, CRange} =
+ case Contr of
+ {value, #contract{args = As} = C} ->
+ {As, fun(FunArgs) ->
+ dialyzer_contracts:get_contract_return(C, FunArgs)
+ end};
+ none -> GenSig
+ end,
+ {BifArgs, BifRange} =
+ case TypeOfApply of
+ remote ->
+ {M, F, A} = Fun,
+ case erl_bif_types:is_known(M, F, A) of
+ true ->
+ BArgs = erl_bif_types:arg_types(M, F, A),
+ BRange =
+ fun(FunArgs) ->
+ erl_bif_types:type(M, F, A, FunArgs, Opaques)
+ end,
+ {BArgs, BRange};
+ false ->
+ GenSig
+ end;
+ local -> GenSig
+ end,
+ {SigArgs, SigRange} =
+ case Sig of
+ {value, {SR, SA}} -> {SA, SR};
+ none -> {AnyArgs, t_any()}
+ end,
+
+ ?debug("--------------------------------------------------------\n", []),
+ ?debug("Fun: ~p\n", [state__lookup_name(Fun, State)]),
+ ?debug("Module ~p\n", [State#state.module]),
+ ?debug("CArgs ~s\n", [erl_types:t_to_string(t_product(CArgs))]),
+ ?debug("ArgTypes ~s\n", [erl_types:t_to_string(t_product(ArgTypes))]),
+ ?debug("BifArgs ~p\n", [erl_types:t_to_string(t_product(BifArgs))]),
+
+ NewArgsSig = t_inf_lists(SigArgs, ArgTypes, Opaques),
+ ?debug("SigArgs ~s\n", [erl_types:t_to_string(t_product(SigArgs))]),
+ ?debug("NewArgsSig: ~s\n", [erl_types:t_to_string(t_product(NewArgsSig))]),
+ NewArgsContract = t_inf_lists(CArgs, ArgTypes, Opaques),
+ ?debug("NewArgsContract: ~s\n",
+ [erl_types:t_to_string(t_product(NewArgsContract))]),
+ NewArgsBif = t_inf_lists(BifArgs, ArgTypes, Opaques),
+ ?debug("NewArgsBif: ~s\n", [erl_types:t_to_string(t_product(NewArgsBif))]),
+ NewArgTypes0 = t_inf_lists(NewArgsSig, NewArgsContract),
+ NewArgTypes = t_inf_lists(NewArgTypes0, NewArgsBif, Opaques),
+ ?debug("NewArgTypes ~s\n", [erl_types:t_to_string(t_product(NewArgTypes))]),
+ ?debug("\n", []),
+
+ BifRet = BifRange(NewArgTypes),
+ ContrRet = CRange(NewArgTypes),
+ RetWithoutContr = t_inf(SigRange, BifRet),
+ RetWithoutLocal = t_inf(ContrRet, RetWithoutContr),
+
+ ?debug("RetWithoutContr: ~s\n",[erl_types:t_to_string(RetWithoutContr)]),
+ ?debug("RetWithoutLocal: ~s\n", [erl_types:t_to_string(RetWithoutLocal)]),
+ ?debug("BifRet: ~s\n", [erl_types:t_to_string(BifRange(NewArgTypes))]),
+ ?debug("SigRange: ~s\n", [erl_types:t_to_string(SigRange)]),
+ ?debug("ContrRet: ~s\n", [erl_types:t_to_string(ContrRet)]),
+ ?debug("LocalRet: ~s\n", [erl_types:t_to_string(LocalRet)]),
+
+ State1 =
+ case is_race_analysis_enabled(State) of
+ true ->
+ Ann = cerl:get_ann(Tree),
+ File = get_file(Ann),
+ Line = abs(get_line(Ann)),
+ dialyzer_races:store_race_call(Fun, ArgTypes, Args,
+ {File, Line}, State);
+ false -> State
+ end,
+ FailedConj = any_none([RetWithoutLocal|NewArgTypes]),
+ IsFailBif = t_is_none(BifRange(BifArgs)),
+ IsFailSig = t_is_none(SigRange),
+ ?debug("FailedConj: ~p~n", [FailedConj]),
+ ?debug("IsFailBif: ~p~n", [IsFailBif]),
+ ?debug("IsFailSig: ~p~n", [IsFailSig]),
+ State2 =
+ case FailedConj andalso not (IsFailBif orelse IsFailSig) of
+ true ->
+ case t_is_none(RetWithoutLocal) andalso
+ not t_is_none(RetWithoutContr) andalso
+ not any_none(NewArgTypes) of
+ true ->
+ {value, C1} = Contr,
+ Contract = dialyzer_contracts:contract_to_string(C1),
+ {M1, F1, A1} = state__lookup_name(Fun, State),
+ ArgStrings = format_args(Args, ArgTypes, State),
+ CRet = erl_types:t_to_string(RetWithoutContr),
+ %% This Msg will be post_processed by dialyzer_succ_typings
+ Msg =
+ {contract_range, [Contract, M1, F1, A1, ArgStrings, CRet]},
+ state__add_warning(State1, ?WARN_CONTRACT_RANGE, Tree, Msg);
+ false ->
+ FailedSig = any_none(NewArgsSig),
+ FailedContract =
+ any_none([CRange(NewArgsContract)|NewArgsContract]),
+ FailedBif = any_none([BifRange(NewArgsBif)|NewArgsBif]),
+ InfSig = t_inf(t_fun(SigArgs, SigRange),
+ t_fun(BifArgs, BifRange(BifArgs))),
+ FailReason =
+ apply_fail_reason(FailedSig, FailedBif, FailedContract),
+ Msg = get_apply_fail_msg(Fun, Args, ArgTypes, NewArgTypes, InfSig,
+ Contr, CArgs, State1, FailReason, Opaques),
+ WarnType = case Msg of
+ {call, _} -> ?WARN_FAILING_CALL;
+ {apply, _} -> ?WARN_FAILING_CALL;
+ {call_with_opaque, _} -> ?WARN_OPAQUE;
+ {call_without_opaque, _} -> ?WARN_OPAQUE;
+ {opaque_type_test, _} -> ?WARN_OPAQUE
+ end,
+ Frc = {erlang, is_record, 3} =:= state__lookup_name(Fun, State),
+ state__add_warning(State1, WarnType, Tree, Msg, Frc)
+ end;
+ false -> State1
+ end,
+ State3 =
+ case TypeOfApply of
+ local ->
+ case state__is_escaping(Fun, State2) of
+ true -> State2;
+ false ->
+ ForwardArgs = [t_limit(X, ?TYPE_LIMIT) || X <- ArgTypes],
+ forward_args(Fun, ForwardArgs, State2)
+ end;
+ remote ->
+ add_bif_warnings(Fun, NewArgTypes, Tree, State2)
+ end,
+ NewAccArgTypes =
+ case FailedConj of
+ true -> AccArgTypes;
+ false -> [t_sup(X, Y) || {X, Y} <- lists:zip(NewArgTypes, AccArgTypes)]
+ end,
+ TotalRet =
+ case t_is_none(LocalRet) andalso t_is_unit(RetWithoutLocal) of
+ true -> RetWithoutLocal;
+ false -> t_inf(RetWithoutLocal, LocalRet)
+ end,
+ NewAccRet = t_sup(AccRet, TotalRet),
+ ?debug("NewAccRet: ~s\n", [t_to_string(NewAccRet)]),
+ {NewWarnings, State4} = state__remove_added_warnings(State, State3),
+ {HowMany, OldWarnings} = Warns,
+ NewWarns =
+ case HowMany of
+ none -> {one, NewWarnings};
+ _ ->
+ case OldWarnings =:= [] of
+ true -> {many, []};
+ false ->
+ case NewWarnings =:= [] of
+ true -> {many, []};
+ false -> {many, NewWarnings ++ OldWarnings}
+ end
+ end
+ end,
+ handle_apply_or_call(Left, Args, ArgTypes, Map, Tree,
+ State4, NewAccArgTypes, NewAccRet, HadExternal, NewWarns);
+handle_apply_or_call([], Args, _ArgTypes, Map, _Tree, State,
+ AccArgTypes, AccRet, HadExternal, {_, Warnings}) ->
+ State1 = state__add_warnings(Warnings, State),
+ case HadExternal of
+ false ->
+ NewMap = enter_type_lists(Args, AccArgTypes, Map),
+ {State1, NewMap, AccRet};
+ true ->
+ {had_external, State1}
+ end.
+
+apply_fail_reason(FailedSig, FailedBif, FailedContract) ->
+ if
+ (FailedSig orelse FailedBif) andalso (not FailedContract) -> only_sig;
+ FailedContract andalso (not (FailedSig orelse FailedBif)) -> only_contract;
+ true -> both
+ end.
+
+get_apply_fail_msg(Fun, Args, ArgTypes, NewArgTypes,
+ Sig, Contract, ContrArgs, State, FailReason, Opaques) ->
+ ArgStrings = format_args(Args, ArgTypes, State),
+ ContractInfo =
+ case Contract of
+ {value, #contract{} = C} ->
+ {dialyzer_contracts:is_overloaded(C),
+ dialyzer_contracts:contract_to_string(C)};
+ none -> {false, none}
+ end,
+ EnumArgTypes = lists:zip(lists:seq(1, length(NewArgTypes)), NewArgTypes),
+ ArgNs = [Arg || {Arg, Type} <- EnumArgTypes, t_is_none(Type)],
+ case state__lookup_name(Fun, State) of
+ {M, F, A} ->
+ case is_opaque_type_test_problem(Fun, Args, NewArgTypes, State) of
+ {yes, Arg, ArgType} ->
+ {opaque_type_test, [atom_to_list(F), ArgStrings,
+ format_arg(Arg), format_type(ArgType, State)]};
+ no ->
+ SigArgs = t_fun_args(Sig),
+ BadOpaque =
+ opaque_problems([SigArgs, ContrArgs], ArgTypes, Opaques, ArgNs),
+ %% In fact *both* 'call_with_opaque' and
+ %% 'call_without_opaque' are possible.
+ case lists:keyfind(decl, 1, BadOpaque) of
+ {decl, BadArgs} ->
+ %% a structured term is used where an opaque is expected
+ ExpectedTriples =
+ case FailReason of
+ only_sig -> expected_arg_triples(BadArgs, SigArgs, State);
+ _ -> expected_arg_triples(BadArgs, ContrArgs, State)
+ end,
+ {call_without_opaque, [M, F, ArgStrings, ExpectedTriples]};
+ false ->
+ case lists:keyfind(use, 1, BadOpaque) of
+ {use, BadArgs} ->
+ %% an opaque term is used where a structured term is expected
+ ExpectedArgs =
+ case FailReason of
+ only_sig -> SigArgs;
+ _ -> ContrArgs
+ end,
+ {call_with_opaque, [M, F, ArgStrings, BadArgs, ExpectedArgs]};
+ false ->
+ case
+ erl_bif_types:opaque_args(M, F, A, ArgTypes, Opaques)
+ of
+ [] -> %% there is a structured term clash in some argument
+ {call, [M, F, ArgStrings,
+ ArgNs, FailReason,
+ format_sig_args(Sig, State),
+ format_type(t_fun_range(Sig), State),
+ ContractInfo]};
+ Ns ->
+ {call_with_opaque, [M, F, ArgStrings, Ns, ContrArgs]}
+ end
+ end
+ end
+ end;
+ Label when is_integer(Label) ->
+ {apply, [ArgStrings,
+ ArgNs, FailReason,
+ format_sig_args(Sig, State),
+ format_type(t_fun_range(Sig), State),
+ ContractInfo]}
+ end.
+
+%% -> [{ElementI, [ArgN]}] where [ArgN] is a non-empty list of
+%% arguments containing unknown opaque types and Element is 1 or 2.
+opaque_problems(ContractOrSigList, ArgTypes, Opaques, ArgNs) ->
+ ArgElementList = find_unknown(ContractOrSigList, ArgTypes, Opaques, ArgNs),
+ F = fun(1) -> decl; (2) -> use end,
+ [{F(ElementI), lists:usort([ArgN || {ArgN, EI} <- ArgElementList,
+ EI =:= ElementI])} ||
+ ElementI <- lists:usort([EI || {_, EI} <- ArgElementList])].
+
+%% -> [{ArgN, ElementI}] where ElementI = 1 means there is an unknown
+%% opaque type in argument ArgN of the the contract/signature,
+%% and ElementI = 2 means that there is an unknown opaque type in
+%% argument ArgN of the the (current) argument types.
+find_unknown(ContractOrSigList, ArgTypes, Opaques, NoneArgNs) ->
+ ArgNs = lists:seq(1, length(ArgTypes)),
+ [{ArgN, ElementI} ||
+ ContractOrSig <- ContractOrSigList,
+ {E1, E2, ArgN} <- lists:zip3(ContractOrSig, ArgTypes, ArgNs),
+ lists:member(ArgN, NoneArgNs),
+ ElementI <- erl_types:t_find_unknown_opaque(E1, E2, Opaques)].
+
+is_opaque_type_test_problem(Fun, Args, ArgTypes, State) ->
+ case Fun of
+ {erlang, FN, 1} when FN =:= is_atom; FN =:= is_boolean;
+ FN =:= is_binary; FN =:= is_bitstring;
+ FN =:= is_float; FN =:= is_function;
+ FN =:= is_integer; FN =:= is_list;
+ FN =:= is_number; FN =:= is_pid; FN =:= is_port;
+ FN =:= is_reference; FN =:= is_tuple;
+ FN =:= is_map ->
+ type_test_opaque_arg(Args, ArgTypes, State#state.opaques);
+ {erlang, FN, 2} when FN =:= is_function ->
+ type_test_opaque_arg(Args, ArgTypes, State#state.opaques);
+ _ -> no
+ end.
+
+type_test_opaque_arg([], [], _Opaques) ->
+ no;
+type_test_opaque_arg([Arg|Args], [ArgType|ArgTypes], Opaques) ->
+ case erl_types:t_has_opaque_subtype(ArgType, Opaques) of
+ true -> {yes, Arg, ArgType};
+ false -> type_test_opaque_arg(Args, ArgTypes, Opaques)
+ end.
+
+expected_arg_triples(ArgNs, ArgTypes, State) ->
+ [begin
+ Arg = lists:nth(N, ArgTypes),
+ {N, Arg, format_type(Arg, State)}
+ end || N <- ArgNs].
+
+add_bif_warnings({erlang, Op, 2}, [T1, T2] = Ts, Tree, State)
+ when Op =:= '=:='; Op =:= '==' ->
+ Opaques = State#state.opaques,
+ Inf = t_inf(T1, T2, Opaques),
+ case
+ t_is_none(Inf) andalso (not any_none(Ts))
+ andalso (not is_int_float_eq_comp(T1, Op, T2, Opaques))
+ of
+ true ->
+ %% Give priority to opaque warning (as usual).
+ case erl_types:t_find_unknown_opaque(T1, T2, Opaques) of
+ [] ->
+ Args = comp_format_args([], T1, Op, T2, State),
+ state__add_warning(State, ?WARN_MATCHING, Tree, {exact_eq, Args});
+ Ns ->
+ Args = comp_format_args(Ns, T1, Op, T2, State),
+ state__add_warning(State, ?WARN_OPAQUE, Tree, {opaque_eq, Args})
+ end;
+ false ->
+ State
+ end;
+add_bif_warnings({erlang, Op, 2}, [T1, T2] = Ts, Tree, State)
+ when Op =:= '=/='; Op =:= '/=' ->
+ Opaques = State#state.opaques,
+ case
+ (not any_none(Ts))
+ andalso (not is_int_float_eq_comp(T1, Op, T2, Opaques))
+ of
+ true ->
+ case erl_types:t_find_unknown_opaque(T1, T2, Opaques) of
+ [] -> State;
+ Ns ->
+ Args = comp_format_args(Ns, T1, Op, T2, State),
+ state__add_warning(State, ?WARN_OPAQUE, Tree, {opaque_neq, Args})
+ end;
+ false ->
+ State
+ end;
+add_bif_warnings(_, _, _, State) ->
+ State.
+
+is_int_float_eq_comp(T1, Op, T2, Opaques) ->
+ (Op =:= '==' orelse Op =:= '/=') andalso
+ ((erl_types:t_is_float(T1, Opaques)
+ andalso t_is_integer(T2, Opaques)) orelse
+ (t_is_integer(T1, Opaques)
+ andalso erl_types:t_is_float(T2, Opaques))).
+
+comp_format_args([1|_], T1, Op, T2, State) ->
+ [format_type(T2, State), Op, format_type(T1, State)];
+comp_format_args(_, T1, Op, T2, State) ->
+ [format_type(T1, State), Op, format_type(T2, State)].
+
+%%----------------------------------------
+
+handle_bitstr(Tree, Map, State) ->
+ %% Construction of binaries.
+ Size = cerl:bitstr_size(Tree),
+ Val = cerl:bitstr_val(Tree),
+ BitstrType = cerl:concrete(cerl:bitstr_type(Tree)),
+ {State1, Map1, SizeType0} = traverse(Size, Map, State),
+ {State2, Map2, ValType0} = traverse(Val, Map1, State1),
+ case cerl:bitstr_bitsize(Tree) of
+ BitSz when BitSz =:= all orelse BitSz =:= utf ->
+ ValType =
+ case BitSz of
+ all ->
+ true = (BitstrType =:= binary),
+ t_inf(ValType0, t_bitstr());
+ utf ->
+ true = lists:member(BitstrType, [utf8, utf16, utf32]),
+ t_inf(ValType0, t_integer())
+ end,
+ Map3 = enter_type(Val, ValType, Map2),
+ case t_is_none(ValType) of
+ true ->
+ Msg = {bin_construction, ["value",
+ format_cerl(Val), format_cerl(Tree),
+ format_type(ValType0, State2)]},
+ State3 = state__add_warning(State2, ?WARN_BIN_CONSTRUCTION, Val, Msg),
+ {State3, Map3, t_none()};
+ false ->
+ {State2, Map3, t_bitstr()}
+ end;
+ BitSz when is_integer(BitSz) orelse BitSz =:= any ->
+ SizeType = t_inf(SizeType0, t_non_neg_integer()),
+ ValType =
+ case BitstrType of
+ binary -> t_inf(ValType0, t_bitstr());
+ float -> t_inf(ValType0, t_number());
+ integer -> t_inf(ValType0, t_integer())
+ end,
+ case any_none([SizeType, ValType]) of
+ true ->
+ {Msg, Offending} =
+ case t_is_none(SizeType) of
+ true ->
+ {{bin_construction,
+ ["size", format_cerl(Size), format_cerl(Tree),
+ format_type(SizeType0, State2)]},
+ Size};
+ false ->
+ {{bin_construction,
+ ["value", format_cerl(Val), format_cerl(Tree),
+ format_type(ValType0, State2)]},
+ Val}
+ end,
+ State3 = state__add_warning(State2, ?WARN_BIN_CONSTRUCTION,
+ Offending, Msg),
+ {State3, Map2, t_none()};
+ false ->
+ UnitVal = cerl:concrete(cerl:bitstr_unit(Tree)),
+ Opaques = State2#state.opaques,
+ NumberVals = t_number_vals(SizeType, Opaques),
+ {State3, Type} =
+ case t_contains_opaque(SizeType, Opaques) of
+ true ->
+ Msg = {opaque_size, [format_type(SizeType, State2),
+ format_cerl(Size)]},
+ {state__add_warning(State2, ?WARN_OPAQUE, Size, Msg),
+ t_none()};
+ false ->
+ case NumberVals of
+ [OneSize] -> {State2, t_bitstr(0, OneSize * UnitVal)};
+ unknown -> {State2, t_bitstr()};
+ _ ->
+ MinSize = erl_types:number_min(SizeType, Opaques),
+ {State2, t_bitstr(UnitVal, UnitVal * MinSize)}
+ end
+ end,
+ Map3 = enter_type_lists([Val, Size, Tree],
+ [ValType, SizeType, Type], Map2),
+ {State3, Map3, Type}
+ end
+ end.
+
+%%----------------------------------------
+
+handle_call(Tree, Map, State) ->
+ M = cerl:call_module(Tree),
+ F = cerl:call_name(Tree),
+ Args = cerl:call_args(Tree),
+ MFAList = [M, F|Args],
+ {State1, Map1, [MType0, FType0|As]} = traverse_list(MFAList, Map, State),
+ Opaques = State#state.opaques,
+ MType = t_inf(t_module(), MType0, Opaques),
+ FType = t_inf(t_atom(), FType0, Opaques),
+ Map2 = enter_type_lists([M, F], [MType, FType], Map1),
+ MOpaque = t_is_none(MType) andalso (not t_is_none(MType0)),
+ FOpaque = t_is_none(FType) andalso (not t_is_none(FType0)),
+ case any_none([MType, FType|As]) of
+ true ->
+ State2 =
+ if
+ MOpaque -> % This is a problem we just detected; not a known one
+ MS = format_cerl(M),
+ case t_is_none(t_inf(t_module(), MType0)) of
+ true ->
+ Msg = {app_call, [MS, format_cerl(F),
+ format_args(Args, As, State1),
+ MS, format_type(t_module(), State1),
+ format_type(MType0, State1)]},
+ state__add_warning(State1, ?WARN_FAILING_CALL, Tree, Msg);
+ false ->
+ Msg = {opaque_call, [MS, format_cerl(F),
+ format_args(Args, As, State1),
+ MS, format_type(MType0, State1)]},
+ state__add_warning(State1, ?WARN_FAILING_CALL, Tree, Msg)
+ end;
+ FOpaque ->
+ FS = format_cerl(F),
+ case t_is_none(t_inf(t_atom(), FType0)) of
+ true ->
+ Msg = {app_call, [format_cerl(M), FS,
+ format_args(Args, As, State1),
+ FS, format_type(t_atom(), State1),
+ format_type(FType0, State1)]},
+ state__add_warning(State1, ?WARN_FAILING_CALL, Tree, Msg);
+ false ->
+ Msg = {opaque_call, [format_cerl(M), FS,
+ format_args(Args, As, State1),
+ FS, format_type(FType0, State1)]},
+ state__add_warning(State1, ?WARN_FAILING_CALL, Tree, Msg)
+ end;
+ true -> State1
+ end,
+ {State2, Map2, t_none()};
+ false ->
+ case t_is_atom(MType) of
+ true ->
+ %% XXX: Consider doing this for all combinations of MF
+ case {t_atom_vals(MType), t_atom_vals(FType)} of
+ {[MAtom], [FAtom]} ->
+ FunInfo = [{remote, state__fun_info({MAtom, FAtom, length(Args)},
+ State1)}],
+ handle_apply_or_call(FunInfo, Args, As, Map2, Tree, State1);
+ {_MAtoms, _FAtoms} ->
+ {State1, Map2, t_any()}
+ end;
+ false ->
+ {State1, Map2, t_any()}
+ end
+ end.
+
+%%----------------------------------------
+
+handle_case(Tree, Map, State) ->
+ Arg = cerl:case_arg(Tree),
+ Clauses = filter_match_fail(cerl:case_clauses(Tree)),
+ {State1, Map1, ArgType} = SMA = traverse(Arg, Map, State),
+ case t_is_none_or_unit(ArgType) of
+ true -> SMA;
+ false ->
+ State2 =
+ case is_race_analysis_enabled(State) of
+ true ->
+ {RaceList, RaceListSize} = get_race_list_and_size(State1),
+ state__renew_race_list([beg_case|RaceList],
+ RaceListSize + 1, State1);
+ false -> State1
+ end,
+ Map2 = join_maps_begin(Map1),
+ {MapList, State3, Type} =
+ handle_clauses(Clauses, Arg, ArgType, ArgType, State2,
+ [], Map2, [], []),
+ Map3 = join_maps_end(MapList, Map2),
+ debug_pp_map(Map3),
+ {State3, Map3, Type}
+ end.
+
+%%----------------------------------------
+
+handle_cons(Tree, Map, State) ->
+ Hd = cerl:cons_hd(Tree),
+ Tl = cerl:cons_tl(Tree),
+ {State1, Map1, HdType} = traverse(Hd, Map, State),
+ {State2, Map2, TlType} = traverse(Tl, Map1, State1),
+ State3 =
+ case t_is_none(t_inf(TlType, t_list(), State2#state.opaques)) of
+ true ->
+ Msg = {improper_list_constr, [format_type(TlType, State2)]},
+ state__add_warning(State2, ?WARN_NON_PROPER_LIST, Tree, Msg);
+ false ->
+ State2
+ end,
+ Type = t_cons(HdType, TlType),
+ {State3, Map2, Type}.
+
+%%----------------------------------------
+
+handle_let(Tree, Map, State) ->
+ IsRaceAnalysisEnabled = is_race_analysis_enabled(State),
+ Arg = cerl:let_arg(Tree),
+ Vars = cerl:let_vars(Tree),
+ {Map0, State0} =
+ case cerl:is_c_var(Arg) of
+ true ->
+ [Var] = Vars,
+ {enter_subst(Var, Arg, Map),
+ case IsRaceAnalysisEnabled of
+ true ->
+ {RaceList, RaceListSize} = get_race_list_and_size(State),
+ state__renew_race_list(
+ [dialyzer_races:let_tag_new(Var, Arg)|RaceList],
+ RaceListSize + 1, State);
+ false -> State
+ end};
+ false -> {Map, State}
+ end,
+ Body = cerl:let_body(Tree),
+ {State1, Map1, ArgTypes} = SMA = traverse(Arg, Map0, State0),
+ State2 =
+ case IsRaceAnalysisEnabled andalso cerl:is_c_call(Arg) of
+ true ->
+ Mod = cerl:call_module(Arg),
+ Name = cerl:call_name(Arg),
+ case cerl:is_literal(Mod) andalso
+ cerl:concrete(Mod) =:= ets andalso
+ cerl:is_literal(Name) andalso
+ cerl:concrete(Name) =:= new of
+ true -> renew_race_public_tables(Vars, State1);
+ false -> State1
+ end;
+ false -> State1
+ end,
+ case t_is_none_or_unit(ArgTypes) of
+ true -> SMA;
+ false ->
+ Map2 = enter_type_lists(Vars, t_to_tlist(ArgTypes), Map1),
+ traverse(Body, Map2, State2)
+ end.
+
+%%----------------------------------------
+
+handle_module(Tree, Map, State) ->
+ %% By not including the variables in scope we can assure that we
+ %% will get the current function type when using the variables.
+ Defs = cerl:module_defs(Tree),
+ PartFun = fun({_Var, Fun}) ->
+ state__is_escaping(get_label(Fun), State)
+ end,
+ {Defs1, Defs2} = lists:partition(PartFun, Defs),
+ Letrec = cerl:c_letrec(Defs1, cerl:c_int(42)),
+ {State1, Map1, _FunTypes} = traverse(Letrec, Map, State),
+ %% Also add environments for the other top-level functions.
+ VarTypes = [{Var, state__fun_type(Fun, State1)} || {Var, Fun} <- Defs],
+ EnvMap = enter_type_list(VarTypes, Map),
+ FoldFun = fun({_Var, Fun}, AccState) ->
+ state__update_fun_env(Fun, EnvMap, AccState)
+ end,
+ State2 = lists:foldl(FoldFun, State1, Defs2),
+ {State2, Map1, t_any()}.
+
+%%----------------------------------------
+
+handle_receive(Tree, Map, State) ->
+ Clauses = filter_match_fail(cerl:receive_clauses(Tree)),
+ Timeout = cerl:receive_timeout(Tree),
+ State1 =
+ case is_race_analysis_enabled(State) of
+ true ->
+ {RaceList, RaceListSize} = get_race_list_and_size(State),
+ state__renew_race_list([beg_case|RaceList],
+ RaceListSize + 1, State);
+ false -> State
+ end,
+ {MapList, State2, ReceiveType} =
+ handle_clauses(Clauses, ?no_arg, t_any(), t_any(), State1, [], Map,
+ [], []),
+ Map1 = join_maps(MapList, Map),
+ {State3, Map2, TimeoutType} = traverse(Timeout, Map1, State2),
+ Opaques = State3#state.opaques,
+ case (t_is_atom(TimeoutType, Opaques) andalso
+ (t_atom_vals(TimeoutType, Opaques) =:= ['infinity'])) of
+ true ->
+ {State3, Map2, ReceiveType};
+ false ->
+ Action = cerl:receive_action(Tree),
+ {State4, Map3, ActionType} = traverse(Action, Map, State3),
+ Map4 = join_maps([Map3, Map1], Map),
+ Type = t_sup(ReceiveType, ActionType),
+ {State4, Map4, Type}
+ end.
+
+%%----------------------------------------
+
+handle_try(Tree, Map, State) ->
+ Arg = cerl:try_arg(Tree),
+ EVars = cerl:try_evars(Tree),
+ Vars = cerl:try_vars(Tree),
+ Body = cerl:try_body(Tree),
+ Handler = cerl:try_handler(Tree),
+ {State1, Map1, ArgType} = traverse(Arg, Map, State),
+ Map2 = mark_as_fresh(Vars, Map1),
+ {SuccState, SuccMap, SuccType} =
+ case bind_pat_vars(Vars, t_to_tlist(ArgType), [], Map2, State1) of
+ {error, _, _, _, _} ->
+ {State1, map__new(), t_none()};
+ {SuccMap1, VarTypes} ->
+ %% Try to bind the argument. Will only succeed if
+ %% it is a simple structured term.
+ SuccMap2 =
+ case bind_pat_vars_reverse([Arg], [t_product(VarTypes)], [],
+ SuccMap1, State1) of
+ {error, _, _, _, _} -> SuccMap1;
+ {SM, _} -> SM
+ end,
+ traverse(Body, SuccMap2, State1)
+ end,
+ ExcMap1 = mark_as_fresh(EVars, Map),
+ {State2, ExcMap2, HandlerType} = traverse(Handler, ExcMap1, SuccState),
+ TryType = t_sup(SuccType, HandlerType),
+ {State2, join_maps([ExcMap2, SuccMap], Map1), TryType}.
+
+%%----------------------------------------
+
+handle_map(Tree,Map,State) ->
+ 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.
+
+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),
+ 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).
+
+%%----------------------------------------
+
+handle_tuple(Tree, Map, State) ->
+ Elements = cerl:tuple_es(Tree),
+ {State1, Map1, EsType} = traverse_list(Elements, Map, State),
+ TupleType = t_tuple(EsType),
+ case t_is_none(TupleType) of
+ true ->
+ {State1, Map1, t_none()};
+ false ->
+ %% Let's find out if this is a record
+ case Elements of
+ [Tag|Left] ->
+ case cerl:is_c_atom(Tag) andalso is_literal_record(Tree) of
+ true ->
+ TagVal = cerl:atom_val(Tag),
+ case state__lookup_record(TagVal, length(Left), State1) of
+ error -> {State1, Map1, TupleType};
+ {ok, RecType} ->
+ InfTupleType = t_inf(RecType, TupleType),
+ case t_is_none(InfTupleType) of
+ true ->
+ RecC = format_type(TupleType, State1),
+ FieldDiffs = format_field_diffs(TupleType, State1),
+ Msg = {record_constr, [RecC, FieldDiffs]},
+ State2 = state__add_warning(State1, ?WARN_MATCHING,
+ Tree, Msg),
+ {State2, Map1, t_none()};
+ false ->
+ case bind_pat_vars(Elements, t_tuple_args(RecType),
+ [], Map1, State1) of
+ {error, bind, ErrorPat, ErrorType, _} ->
+ Msg = {record_constr,
+ [TagVal, format_patterns(ErrorPat),
+ format_type(ErrorType, State1)]},
+ State2 = state__add_warning(State1, ?WARN_MATCHING,
+ Tree, Msg),
+ {State2, Map1, t_none()};
+ {error, opaque, ErrorPat, ErrorType, OpaqueType} ->
+ Msg = {opaque_match,
+ [format_patterns(ErrorPat),
+ format_type(ErrorType, State1),
+ format_type(OpaqueType, State1)]},
+ State2 = state__add_warning(State1, ?WARN_OPAQUE,
+ Tree, Msg),
+ {State2, Map1, t_none()};
+ {Map2, ETypes} ->
+ {State1, Map2, t_tuple(ETypes)}
+ end
+ end
+ end;
+ false ->
+ {State1, Map1, t_tuple(EsType)}
+ end;
+ [] ->
+ {State1, Map1, t_tuple([])}
+ end
+ end.
+
+%%----------------------------------------
+%% Clauses
+%%
+handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn,
+ Acc, ClauseAcc) ->
+ IsRaceAnalysisEnabled = is_race_analysis_enabled(State),
+ State1 =
+ case IsRaceAnalysisEnabled of
+ true ->
+ {RaceList, RaceListSize} = get_race_list_and_size(State),
+ state__renew_race_list(
+ [dialyzer_races:beg_clause_new(Arg, cerl:clause_pats(C),
+ cerl:clause_guard(C))|
+ RaceList], RaceListSize + 1,
+ State);
+ false -> State
+ end,
+ {State2, ClauseMap, BodyType, NewArgType} =
+ do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1),
+ {NewClauseAcc, State3} =
+ case IsRaceAnalysisEnabled of
+ true ->
+ {RaceList1, RaceListSize1} = get_race_list_and_size(State2),
+ EndClause = dialyzer_races:end_clause_new(Arg, cerl:clause_pats(C),
+ cerl:clause_guard(C)),
+ {[EndClause|ClauseAcc],
+ state__renew_race_list([EndClause|RaceList1],
+ RaceListSize1 + 1, State2)};
+ false -> {ClauseAcc, State2}
+ end,
+ {NewCaseTypes, NewAcc} =
+ case t_is_none(BodyType) of
+ true -> {CaseTypes, Acc};
+ false -> {[BodyType|CaseTypes], [ClauseMap|Acc]}
+ end,
+ handle_clauses(Left, Arg, NewArgType, OrigArgType, State3,
+ NewCaseTypes, MapIn, NewAcc, NewClauseAcc);
+handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc,
+ ClauseAcc) ->
+ State1 =
+ case is_race_analysis_enabled(State) of
+ true ->
+ {RaceList, RaceListSize} = get_race_list_and_size(State),
+ state__renew_race_list(
+ [dialyzer_races:end_case_new(ClauseAcc)|RaceList],
+ RaceListSize + 1, State);
+ false -> State
+ end,
+ {lists:reverse(Acc), State1, t_sup(CaseTypes)}.
+
+do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
+ Pats = cerl:clause_pats(C),
+ Guard = cerl:clause_guard(C),
+ Body = cerl:clause_body(C),
+ State1 =
+ case is_race_analysis_enabled(State) of
+ true ->
+ state__renew_fun_args(Pats, State);
+ false -> State
+ end,
+ Map0 = mark_as_fresh(Pats, Map),
+ Map1 = if Arg =:= ?no_arg -> Map0;
+ true -> bind_subst(Arg, Pats, Map0)
+ end,
+ BindRes =
+ case t_is_none(ArgType0) of
+ true ->
+ {error, bind, Pats, ArgType0, ArgType0};
+ false ->
+ ArgTypes =
+ case t_is_any(ArgType0) of
+ true -> [ArgType0 || _ <- Pats];
+ false -> t_to_tlist(ArgType0)
+ end,
+ bind_pat_vars(Pats, ArgTypes, [], Map1, State1)
+ end,
+ case BindRes of
+ {error, ErrorType, NewPats, Type, OpaqueTerm} ->
+ ?debug("Failed binding pattern: ~s\nto ~s\n",
+ [cerl_prettypr:format(C), format_type(ArgType0, State1)]),
+ case state__warning_mode(State1) of
+ false ->
+ {State1, Map, t_none(), ArgType0};
+ true ->
+ {Msg, Force} =
+ case t_is_none(ArgType0) of
+ true ->
+ PatString = format_patterns(Pats),
+ PatTypes = [PatString, format_type(OrigArgType, State1)],
+ %% See if this is covered by an earlier clause or if it
+ %% simply cannot match
+ OrigArgTypes =
+ case t_is_any(OrigArgType) of
+ true -> Any = t_any(), [Any || _ <- Pats];
+ false -> t_to_tlist(OrigArgType)
+ end,
+ Tag =
+ case bind_pat_vars(Pats, OrigArgTypes, [], Map1, State1) of
+ {error, bind, _, _, _} -> pattern_match;
+ {error, record, _, _, _} -> record_match;
+ {error, opaque, _, _, _} -> opaque_match;
+ {_, _} -> pattern_match_cov
+ end,
+ {{Tag, PatTypes}, false};
+ false ->
+ %% Try to find out if this is a default clause in a list
+ %% comprehension and supress this. A real Hack(tm)
+ Force0 =
+ case is_compiler_generated(cerl:get_ann(C)) of
+ true ->
+ case Pats of
+ [Pat] ->
+ case cerl:is_c_cons(Pat) of
+ true ->
+ not (cerl:is_c_var(cerl:cons_hd(Pat)) andalso
+ cerl:is_c_var(cerl:cons_tl(Pat)) andalso
+ cerl:is_literal(Guard) andalso
+ (cerl:concrete(Guard) =:= true));
+ false ->
+ true
+ end;
+ [Pat0, Pat1] -> % binary comprehension
+ case cerl:is_c_cons(Pat0) of
+ true ->
+ not (cerl:is_c_var(cerl:cons_hd(Pat0)) andalso
+ cerl:is_c_var(cerl:cons_tl(Pat0)) andalso
+ cerl:is_c_var(Pat1) andalso
+ cerl:is_literal(Guard) andalso
+ (cerl:concrete(Guard) =:= true));
+ false ->
+ true
+ end;
+ _ -> true
+ end;
+ false ->
+ true
+ end,
+ PatString =
+ case ErrorType of
+ bind -> format_patterns(Pats);
+ record -> format_patterns(NewPats);
+ opaque -> format_patterns(NewPats)
+ end,
+ PatTypes = case ErrorType of
+ bind -> [PatString, format_type(ArgType0, State1)];
+ record -> [PatString, format_type(Type, State1)];
+ opaque -> [PatString, format_type(Type, State1),
+ format_type(OpaqueTerm, State1)]
+ end,
+ FailedTag = case ErrorType of
+ bind -> pattern_match;
+ record -> record_match;
+ opaque -> opaque_match
+ end,
+ {{FailedTag, PatTypes}, Force0}
+ end,
+ WarnType = case Msg of
+ {opaque_match, _} -> ?WARN_OPAQUE;
+ {pattern_match, _} -> ?WARN_MATCHING;
+ {record_match, _} -> ?WARN_MATCHING;
+ {pattern_match_cov, _} -> ?WARN_MATCHING
+ end,
+ {state__add_warning(State1, WarnType, C, Msg, Force),
+ Map, t_none(), ArgType0}
+ end;
+ {Map2, PatTypes} ->
+ Map3 =
+ case Arg =:= ?no_arg of
+ true -> Map2;
+ false ->
+ %% Try to bind the argument. Will only succeed if
+ %% it is a simple structured term.
+ case bind_pat_vars_reverse([Arg], [t_product(PatTypes)],
+ [], Map2, State1) of
+ {error, _, _, _, _} -> Map2;
+ {NewMap, _} -> NewMap
+ end
+ end,
+ NewArgType =
+ case Arg =:= ?no_arg of
+ true -> ArgType0;
+ false ->
+ GenType = dialyzer_typesig:get_safe_underapprox(Pats, Guard),
+ t_subtract(t_product(t_to_tlist(ArgType0)), GenType)
+ end,
+ case bind_guard(Guard, Map3, State1) of
+ {error, Reason} ->
+ ?debug("Failed guard: ~s\n",
+ [cerl_prettypr:format(C, [{hook, cerl_typean:pp_hook()}])]),
+ PatString = format_patterns(Pats),
+ DefaultMsg =
+ case Pats =:= [] of
+ true -> {guard_fail, []};
+ false ->
+ {guard_fail_pat, [PatString, format_type(ArgType0, State1)]}
+ end,
+ State2 =
+ case Reason of
+ none -> state__add_warning(State1, ?WARN_MATCHING, C, DefaultMsg);
+ {FailGuard, Msg} ->
+ case is_compiler_generated(cerl:get_ann(FailGuard)) of
+ false ->
+ WarnType = case Msg of
+ {guard_fail, _} -> ?WARN_MATCHING;
+ {neg_guard_fail, _} -> ?WARN_MATCHING;
+ {opaque_guard, _} -> ?WARN_OPAQUE
+ end,
+ state__add_warning(State1, WarnType, FailGuard, Msg);
+ true ->
+ state__add_warning(State1, ?WARN_MATCHING, C, Msg)
+ end
+ end,
+ {State2, Map, t_none(), NewArgType};
+ Map4 ->
+ {RetState, RetMap, BodyType} = traverse(Body, Map4, State1),
+ {RetState, RetMap, BodyType, NewArgType}
+ end
+ end.
+
+bind_subst(Arg, Pats, Map) ->
+ case cerl:type(Arg) of
+ values ->
+ bind_subst_list(cerl:values_es(Arg), Pats, Map);
+ var ->
+ [Pat] = Pats,
+ enter_subst(Arg, Pat, Map);
+ _ ->
+ Map
+ end.
+
+bind_subst_list([Arg|ArgLeft], [Pat|PatLeft], Map) ->
+ NewMap =
+ case {cerl:type(Arg), cerl:type(Pat)} of
+ {var, var} -> enter_subst(Arg, Pat, Map);
+ {var, alias} -> enter_subst(Arg, cerl:alias_pat(Pat), Map);
+ {literal, literal} -> Map;
+ {T, T} -> bind_subst_list(lists:flatten(cerl:subtrees(Arg)),
+ lists:flatten(cerl:subtrees(Pat)),
+ Map);
+ _ -> Map
+ end,
+ bind_subst_list(ArgLeft, PatLeft, NewMap);
+bind_subst_list([], [], Map) ->
+ Map.
+
+%%----------------------------------------
+%% Patterns
+%%
+
+bind_pat_vars(Pats, Types, Acc, Map, State) ->
+ try
+ bind_pat_vars(Pats, Types, Acc, Map, State, false)
+ catch
+ throw:Error ->
+ %% Error = {error, bind | opaque | record, ErrorPats, ErrorType}
+ Error
+ end.
+
+bind_pat_vars_reverse(Pats, Types, Acc, Map, State) ->
+ try
+ bind_pat_vars(Pats, Types, Acc, Map, State, true)
+ catch
+ throw:Error ->
+ %% Error = {error, bind | opaque | record, ErrorPats, ErrorType}
+ Error
+ end.
+
+bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
+ ?debug("Binding pat: ~w to ~s\n", [cerl:type(Pat), format_type(Type, State)]
+),
+ Opaques = State#state.opaques,
+ {NewMap, TypeOut} =
+ case cerl:type(Pat) of
+ alias ->
+ %% 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], [],
+ Map1, State, Rev),
+ {enter_type(Var, PatType, Map2), PatType};
+ binary ->
+ %% Cannot bind the binary if we are in reverse match since
+ %% binary patterns and binary construction are not symmetric.
+ case Rev of
+ true -> {Map, t_bitstr()};
+ false ->
+ BinType = t_inf(t_bitstr(), Type, Opaques),
+ case t_is_none(BinType) of
+ true ->
+ case t_find_opaque_mismatch(t_bitstr(), Type, Opaques) of
+ {ok, T1, T2} ->
+ bind_error([Pat], T1, T2, opaque);
+ error ->
+ bind_error([Pat], Type, t_none(), bind)
+ end;
+ false ->
+ Segs = cerl:binary_segments(Pat),
+ {Map1, SegTypes} = bind_bin_segs(Segs, BinType, Map, State),
+ {Map1, t_bitstr_concat(SegTypes)}
+ end
+ end;
+ cons ->
+ Cons = t_inf(Type, t_cons(), Opaques),
+ case t_is_none(Cons) of
+ true ->
+ bind_opaque_pats(t_cons(), Type, Pat, State);
+ false ->
+ {Map1, [HdType, TlType]} =
+ bind_pat_vars([cerl:cons_hd(Pat), cerl:cons_tl(Pat)],
+ [t_cons_hd(Cons, Opaques),
+ t_cons_tl(Cons, Opaques)],
+ [], Map, State, Rev),
+ {Map1, t_cons(HdType, TlType)}
+ end;
+ literal ->
+ Pat0 = dialyzer_utils:refold_pattern(Pat),
+ case cerl:is_literal(Pat0) of
+ true ->
+ 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 ->
+ 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} =
+ case Es of
+ [] -> {false, t_tuple([])};
+ [Tag|Left] ->
+ case cerl:is_c_atom(Tag) andalso is_literal_record(Pat) of
+ true ->
+ TagAtom = cerl:atom_val(Tag),
+ case state__lookup_record(TagAtom, length(Left), State) of
+ error -> {false, t_tuple(length(Es))};
+ {ok, Record} ->
+ [_Head|AnyTail] = [t_any() || _ <- Es],
+ UntypedRecord = t_tuple([t_atom(TagAtom)|AnyTail]),
+ {not t_is_equal(Record, UntypedRecord), Record}
+ end;
+ false -> {false, t_tuple(length(Es))}
+ end
+ end,
+ Tuple = t_inf(Prototype, Type, Opaques),
+ case t_is_none(Tuple) of
+ true ->
+ bind_opaque_pats(Prototype, Type, Pat, State);
+ false ->
+ SubTuples = t_tuple_subtypes(Tuple, Opaques),
+ %% Need to call the top function to get the try-catch wrapper
+ MapJ = join_maps_begin(Map),
+ Results =
+ case Rev of
+ true ->
+ [bind_pat_vars_reverse(Es, t_tuple_args(SubTuple, Opaques),
+ [], MapJ, State)
+ || SubTuple <- SubTuples];
+ false ->
+ [bind_pat_vars(Es, t_tuple_args(SubTuple, Opaques), [],
+ MapJ, State)
+ || SubTuple <- SubTuples]
+ end,
+ case lists:keyfind(opaque, 2, Results) of
+ {error, opaque, _PatList, _Type, Opaque} ->
+ bind_error([Pat], Tuple, Opaque, opaque);
+ false ->
+ case [M || {M, _} <- Results, M =/= error] of
+ [] ->
+ case TypedRecord of
+ true -> bind_error([Pat], Tuple, Prototype, record);
+ false -> bind_error([Pat], Tuple, t_none(), bind)
+ end;
+ Maps ->
+ Map1 = join_maps_end(Maps, MapJ),
+ TupleType = t_sup([t_tuple(EsTypes)
+ || {M, EsTypes} <- Results, M =/= error]),
+ {Map1, TupleType}
+ end
+ end
+ end;
+ values ->
+ Es = cerl:values_es(Pat),
+ {Map1, EsTypes} =
+ bind_pat_vars(Es, t_to_tlist(Type), [], Map, State, Rev),
+ {Map1, t_product(EsTypes)};
+ var ->
+ VarType1 =
+ case state__lookup_type_for_letrec(Pat, State) of
+ error -> lookup_type(Pat, Map);
+ {ok, RecType} -> RecType
+ end,
+ %% Must do inf when binding args to pats. Vars in pats are fresh.
+ VarType2 = t_inf(VarType1, Type, Opaques),
+ case t_is_none(VarType2) of
+ true ->
+ case t_find_opaque_mismatch(VarType1, Type, Opaques) of
+ {ok, T1, T2} ->
+ bind_error([Pat], T1, T2, opaque);
+ error ->
+ bind_error([Pat], Type, t_none(), bind)
+ end;
+ false ->
+ Map1 = enter_type(Pat, VarType2, Map),
+ {Map1, VarType2}
+ end;
+ _Other ->
+ %% Catch all is needed when binding args to pats
+ ?debug("Failed match for ~p\n", [_Other]),
+ bind_error([Pat], Type, t_none(), bind)
+ end,
+ bind_pat_vars(PatLeft, TypeLeft, [TypeOut|Acc], NewMap, State, Rev);
+bind_pat_vars([], [], Acc, Map, _State, _Rev) ->
+ {Map, lists:reverse(Acc)}.
+
+bind_bin_segs(BinSegs, BinType, Map, State) ->
+ bind_bin_segs(BinSegs, BinType, [], Map, State).
+
+bind_bin_segs([Seg|Segs], BinType, Acc, Map, State) ->
+ Val = cerl:bitstr_val(Seg),
+ SegType = cerl:concrete(cerl:bitstr_type(Seg)),
+ UnitVal = cerl:concrete(cerl:bitstr_unit(Seg)),
+ case cerl:bitstr_bitsize(Seg) of
+ all ->
+ binary = SegType, [] = Segs, %% just an assert
+ T = t_inf(t_bitstr(UnitVal, 0), BinType),
+ {Map1, [Type]} = bind_pat_vars([Val], [T], [], Map, State, false),
+ Type1 = remove_local_opaque_types(Type, State#state.opaques),
+ bind_bin_segs(Segs, t_bitstr(0, 0), [Type1|Acc], Map1, State);
+ utf -> % XXX: possibly can be strengthened
+ true = lists:member(SegType, [utf8, utf16, utf32]),
+ {Map1, [_]} = bind_pat_vars([Val], [t_integer()], [], Map, State, false),
+ Type = t_binary(),
+ bind_bin_segs(Segs, BinType, [Type|Acc], Map1, State);
+ BitSz when is_integer(BitSz) orelse BitSz =:= any ->
+ Size = cerl:bitstr_size(Seg),
+ {Map1, [SizeType]} =
+ bind_pat_vars([Size], [t_non_neg_integer()], [], Map, State, false),
+ Opaques = State#state.opaques,
+ NumberVals = t_number_vals(SizeType, Opaques),
+ case t_contains_opaque(SizeType, Opaques) of
+ true -> bind_error([Seg], SizeType, t_none(), opaque);
+ false -> ok
+ end,
+ Type =
+ case NumberVals of
+ [OneSize] -> t_bitstr(0, UnitVal * OneSize);
+ _ -> % 'unknown' too
+ MinSize = erl_types:number_min(SizeType, Opaques),
+ t_bitstr(UnitVal, UnitVal * MinSize)
+ end,
+ ValConstr =
+ case SegType of
+ binary -> Type; %% The same constraints as for the whole bitstr
+ float -> t_float();
+ integer ->
+ case NumberVals of
+ unknown -> t_integer();
+ List ->
+ SizeVal = lists:max(List),
+ Flags = cerl:concrete(cerl:bitstr_flags(Seg)),
+ N = SizeVal * UnitVal,
+ case N >= ?BITS of
+ true ->
+ case lists:member(signed, Flags) of
+ true -> t_from_range(neg_inf, pos_inf);
+ false -> t_from_range(0, pos_inf)
+ end;
+ false ->
+ case lists:member(signed, Flags) of
+ true -> t_from_range(-(1 bsl (N - 1)), 1 bsl (N - 1) - 1);
+ false -> t_from_range(0, 1 bsl N - 1)
+ end
+ end
+ end
+ end,
+ {Map2, [_]} = bind_pat_vars([Val], [ValConstr], [], Map1, State, false),
+ NewBinType = t_bitstr_match(Type, BinType),
+ case t_is_none(NewBinType) of
+ true -> bind_error([Seg], BinType, t_none(), bind);
+ false -> bind_bin_segs(Segs, NewBinType, [Type|Acc], Map2, State)
+ end
+ end;
+bind_bin_segs([], _BinType, Acc, Map, _State) ->
+ {Map, lists:reverse(Acc)}.
+
+bind_error(Pats, Type, OpaqueType, Error0) ->
+ Error = case {Error0, Pats} of
+ {bind, [Pat]} ->
+ case is_literal_record(Pat) of
+ true -> record;
+ false -> Error0
+ end;
+ _ -> Error0
+ end,
+ throw({error, Error, Pats, Type, OpaqueType}).
+
+-spec bind_opaque_pats(type(), type(), cerl:c_literal(), state()) ->
+ no_return().
+
+bind_opaque_pats(GenType, Type, Pat, State) ->
+ case t_find_opaque_mismatch(GenType, Type, State#state.opaques) of
+ {ok, T1, T2} ->
+ bind_error([Pat], T1, T2, opaque);
+ error ->
+ bind_error([Pat], Type, t_none(), bind)
+ end.
+
+%%----------------------------------------
+%% Guards
+%%
+
+bind_guard(Guard, Map, State) ->
+ try bind_guard(Guard, Map, maps:new(), pos, State) of
+ {Map1, _Type} -> Map1
+ catch
+ throw:{fail, Warning} -> {error, Warning};
+ throw:{fatal_fail, Warning} -> {error, Warning}
+ end.
+
+bind_guard(Guard, Map, Env, Eval, State) ->
+ ?debug("Handling ~w guard: ~s\n",
+ [Eval, cerl_prettypr:format(Guard, [{noann, true}])]),
+ case cerl:type(Guard) of
+ binary ->
+ {Map, t_binary()};
+ 'case' ->
+ Arg = cerl:case_arg(Guard),
+ Clauses = cerl:case_clauses(Guard),
+ bind_guard_case_clauses(Arg, Clauses, Map, Env, Eval, State);
+ cons ->
+ Hd = cerl:cons_hd(Guard),
+ Tl = cerl:cons_tl(Guard),
+ {Map1, HdType} = bind_guard(Hd, Map, Env, dont_know, State),
+ {Map2, TlType} = bind_guard(Tl, Map1, Env, dont_know, State),
+ {Map2, t_cons(HdType, TlType)};
+ literal ->
+ {Map, literal_type(Guard)};
+ 'try' ->
+ Arg = cerl:try_arg(Guard),
+ [Var] = cerl:try_vars(Guard),
+ EVars = cerl:try_evars(Guard),
+ %%?debug("Storing: ~w\n", [Var]),
+ 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 ->
+ 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 = maps:put(get_label(Var), Arg, Env),
+ bind_guard(cerl:let_body(Guard), Map, NewEnv, Eval, State);
+ values ->
+ Es = cerl:values_es(Guard),
+ List = [bind_guard(V, Map, Env, dont_know, State) || V <- Es],
+ Type = t_product([T || {_, T} <- List]),
+ {Map, Type};
+ var ->
+ ?debug("Looking for var(~w)...", [cerl_trees:get_label(Guard)]),
+ case maps:find(get_label(Guard), Env) of
+ error ->
+ ?debug("Did not find it\n", []),
+ Type = lookup_type(Guard, Map),
+ Constr =
+ case Eval of
+ pos -> t_atom(true);
+ neg -> t_atom(false);
+ dont_know -> Type
+ end,
+ Inf = t_inf(Constr, Type),
+ {enter_type(Guard, Inf, Map), Inf};
+ {ok, Tree} ->
+ ?debug("Found it\n", []),
+ {Map1, Type} = bind_guard(Tree, Map, Env, Eval, State),
+ {enter_type(Guard, Type, Map1), Type}
+ end;
+ call ->
+ handle_guard_call(Guard, Map, Env, Eval, State)
+ end.
+
+handle_guard_call(Guard, Map, Env, Eval, State) ->
+ MFA = {cerl:atom_val(cerl:call_module(Guard)),
+ cerl:atom_val(cerl:call_name(Guard)),
+ cerl:call_arity(Guard)},
+ case MFA of
+ {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_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);
+ {erlang, is_function, 2} ->
+ handle_guard_is_function(Guard, Map, Env, Eval, State);
+ MFA when (MFA =:= {erlang, internal_is_record, 3}) or
+ (MFA =:= {erlang, is_record, 3}) ->
+ handle_guard_is_record(Guard, Map, Env, Eval, State);
+ {erlang, '=:=', 2} ->
+ handle_guard_eqeq(Guard, Map, Env, Eval, State);
+ {erlang, '==', 2} ->
+ handle_guard_eq(Guard, Map, Env, Eval, State);
+ {erlang, 'and', 2} ->
+ handle_guard_and(Guard, Map, Env, Eval, State);
+ {erlang, 'or', 2} ->
+ handle_guard_or(Guard, Map, Env, Eval, State);
+ {erlang, 'not', 1} ->
+ handle_guard_not(Guard, Map, Env, Eval, State);
+ {erlang, Comp, 2} when Comp =:= '<'; Comp =:= '=<';
+ Comp =:= '>'; Comp =:= '>=' ->
+ handle_guard_comp(Guard, Comp, Map, Env, Eval, State);
+ _ ->
+ handle_guard_gen_fun(MFA, Guard, Map, Env, Eval, State)
+ end.
+
+handle_guard_gen_fun({M, F, A}, Guard, Map, Env, Eval, State) ->
+ Args = cerl:call_args(Guard),
+ {Map1, As} = bind_guard_list(Args, Map, Env, dont_know, State),
+ Opaques = State#state.opaques,
+ BifRet = erl_bif_types:type(M, F, A, As, Opaques),
+ case t_is_none(BifRet) of
+ true ->
+ %% Is this an error-bif?
+ case t_is_none(erl_bif_types:type(M, F, A)) of
+ true -> signal_guard_fail(Eval, Guard, As, State);
+ false -> signal_guard_fatal_fail(Eval, Guard, As, State)
+ end;
+ false ->
+ BifArgs = bif_args(M, F, A),
+ Map2 = enter_type_lists(Args, t_inf_lists(BifArgs, As, Opaques), Map1),
+ Ret =
+ case Eval of
+ pos -> t_inf(t_atom(true), BifRet);
+ neg -> t_inf(t_atom(false), BifRet);
+ dont_know -> BifRet
+ end,
+ case t_is_none(Ret) of
+ true ->
+ case Eval =:= pos of
+ true -> signal_guard_fail(Eval, Guard, As, State);
+ false -> throw({fail, none})
+ end;
+ false -> {Map2, Ret}
+ end
+ end.
+
+handle_guard_type_test(Guard, F, Map, Env, Eval, State) ->
+ [Arg] = cerl:call_args(Guard),
+ {Map1, ArgType} = bind_guard(Arg, Map, Env, dont_know, State),
+ case bind_type_test(Eval, F, ArgType, State) of
+ error ->
+ ?debug("Type test: ~w failed\n", [F]),
+ signal_guard_fail(Eval, Guard, [ArgType], State);
+ {ok, NewArgType, Ret} ->
+ ?debug("Type test: ~w succeeded, NewType: ~s, Ret: ~s\n",
+ [F, t_to_string(NewArgType), t_to_string(Ret)]),
+ {enter_type(Arg, NewArgType, Map1), Ret}
+ end.
+
+bind_type_test(Eval, TypeTest, ArgType, State) ->
+ Type = case TypeTest of
+ is_atom -> t_atom();
+ is_boolean -> t_boolean();
+ is_binary -> t_binary();
+ is_bitstring -> t_bitstr();
+ is_float -> t_float();
+ 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();
+ is_reference -> t_reference();
+ is_tuple -> t_tuple()
+ end,
+ case Eval of
+ pos ->
+ Inf = t_inf(Type, ArgType, State#state.opaques),
+ case t_is_none(Inf) of
+ true -> error;
+ false -> {ok, Inf, t_atom(true)}
+ end;
+ neg ->
+ Sub = t_subtract(ArgType, Type),
+ case t_is_none(Sub) of
+ true -> error;
+ false -> {ok, Sub, t_atom(false)}
+ end;
+ dont_know ->
+ {ok, ArgType, t_boolean()}
+ end.
+
+handle_guard_comp(Guard, Comp, Map, Env, Eval, State) ->
+ Args = cerl:call_args(Guard),
+ [Arg1, Arg2] = Args,
+ {Map1, ArgTypes} = bind_guard_list(Args, Map, Env, dont_know, State),
+ Opaques = State#state.opaques,
+ [Type1, Type2] = ArgTypes,
+ IsInt1 = t_is_integer(Type1, Opaques),
+ IsInt2 = t_is_integer(Type2, Opaques),
+ case {type(Arg1), type(Arg2)} of
+ {{literal, Lit1}, {literal, Lit2}} ->
+ case erlang:Comp(cerl:concrete(Lit1), cerl:concrete(Lit2)) of
+ true when Eval =:= pos -> {Map, t_atom(true)};
+ true when Eval =:= dont_know -> {Map, t_atom(true)};
+ true when Eval =:= neg -> {Map, t_atom(true)};
+ false when Eval =:= pos ->
+ signal_guard_fail(Eval, Guard, ArgTypes, State);
+ false when Eval =:= dont_know -> {Map, t_atom(false)};
+ false when Eval =:= neg -> {Map, t_atom(false)}
+ end;
+ {{literal, Lit1}, var} when IsInt1 andalso IsInt2 andalso (Eval =:= pos) ->
+ case bind_comp_literal_var(Lit1, Arg2, Type2, Comp, Map1, Opaques) of
+ error -> signal_guard_fail(Eval, Guard, ArgTypes, State);
+ {ok, NewMap} -> {NewMap, t_atom(true)}
+ end;
+ {var, {literal, Lit2}} when IsInt1 andalso IsInt2 andalso (Eval =:= pos) ->
+ case bind_comp_literal_var(Lit2, Arg1, Type1, invert_comp(Comp),
+ Map1, Opaques) of
+ error -> signal_guard_fail(Eval, Guard, ArgTypes, State);
+ {ok, NewMap} -> {NewMap, t_atom(true)}
+ end;
+ {_, _} ->
+ handle_guard_gen_fun({erlang, Comp, 2}, Guard, Map, Env, Eval, State)
+ end.
+
+invert_comp('=<') -> '>=';
+invert_comp('<') -> '>';
+invert_comp('>=') -> '=<';
+invert_comp('>') -> '<'.
+
+bind_comp_literal_var(Lit, Var, VarType, CompOp, Map, Opaques) ->
+ LitVal = cerl:concrete(Lit),
+ NewVarType =
+ case t_number_vals(VarType, Opaques) of
+ unknown ->
+ Range =
+ case CompOp of
+ '=<' -> t_from_range(LitVal, pos_inf);
+ '<' -> t_from_range(LitVal + 1, pos_inf);
+ '>=' -> t_from_range(neg_inf, LitVal);
+ '>' -> t_from_range(neg_inf, LitVal - 1)
+ end,
+ t_inf(Range, VarType, Opaques);
+ NumberVals ->
+ NewNumberVals = [X || X <- NumberVals, erlang:CompOp(LitVal, X)],
+ t_integers(NewNumberVals)
+ end,
+ case t_is_none(NewVarType) of
+ true -> error;
+ false -> {ok, enter_type(Var, NewVarType, Map)}
+ end.
+
+handle_guard_is_function(Guard, Map, Env, Eval, State) ->
+ Args = cerl:call_args(Guard),
+ {Map1, ArgTypes0} = bind_guard_list(Args, Map, Env, dont_know, State),
+ [FunType0, ArityType0] = ArgTypes0,
+ Opaques = State#state.opaques,
+ ArityType = t_inf(ArityType0, t_integer(), Opaques),
+ case t_is_none(ArityType) of
+ true -> signal_guard_fail(Eval, Guard, ArgTypes0, State);
+ false ->
+ FunTypeConstr =
+ case t_number_vals(ArityType, State#state.opaques) of
+ unknown -> t_fun();
+ Vals ->
+ t_sup([t_fun(lists:duplicate(X, t_any()), t_any()) || X <- Vals])
+ end,
+ FunType = t_inf(FunType0, FunTypeConstr, Opaques),
+ case t_is_none(FunType) of
+ true ->
+ case Eval of
+ pos -> signal_guard_fail(Eval, Guard, ArgTypes0, State);
+ neg -> {Map1, t_atom(false)};
+ dont_know -> {Map1, t_atom(false)}
+ end;
+ false ->
+ case Eval of
+ pos -> {enter_type_lists(Args, [FunType, ArityType], Map1),
+ t_atom(true)};
+ neg -> {Map1, t_atom(false)};
+ dont_know -> {Map1, t_boolean()}
+ end
+ end
+ end.
+
+handle_guard_is_record(Guard, Map, Env, Eval, State) ->
+ Args = cerl:call_args(Guard),
+ [Rec, Tag0, Arity0] = Args,
+ Tag = cerl:atom_val(Tag0),
+ Arity = cerl:int_val(Arity0),
+ {Map1, RecType} = bind_guard(Rec, Map, Env, dont_know, State),
+ ArityMin1 = Arity - 1,
+ Opaques = State#state.opaques,
+ Tuple = t_tuple([t_atom(Tag)|lists:duplicate(ArityMin1, t_any())]),
+ case t_is_none(t_inf(Tuple, RecType, Opaques)) of
+ true ->
+ case erl_types:t_has_opaque_subtype(RecType, Opaques) of
+ true ->
+ signal_guard_fail(Eval, Guard,
+ [RecType, t_from_term(Tag),
+ t_from_term(Arity)],
+ State);
+ false ->
+ case Eval of
+ pos -> signal_guard_fail(Eval, Guard,
+ [RecType, t_from_term(Tag),
+ t_from_term(Arity)],
+ State);
+ neg -> {Map1, t_atom(false)};
+ dont_know -> {Map1, t_atom(false)}
+ end
+ end;
+ false ->
+ TupleType =
+ case state__lookup_record(Tag, ArityMin1, State) of
+ error -> Tuple;
+ {ok, Prototype} -> Prototype
+ end,
+ Type = t_inf(TupleType, RecType, State#state.opaques),
+ case t_is_none(Type) of
+ true ->
+ %% No special handling of opaque errors.
+ FArgs = "record " ++ format_type(RecType, State),
+ Msg = {record_matching, [FArgs, Tag]},
+ throw({fail, {Guard, Msg}});
+ false ->
+ case Eval of
+ pos -> {enter_type(Rec, Type, Map1), t_atom(true)};
+ neg -> {Map1, t_atom(false)};
+ dont_know -> {Map1, t_boolean()}
+ end
+ end
+ end.
+
+handle_guard_eq(Guard, Map, Env, Eval, State) ->
+ [Arg1, Arg2] = cerl:call_args(Guard),
+ case {type(Arg1), type(Arg2)} of
+ {{literal, Lit1}, {literal, Lit2}} ->
+ case cerl:concrete(Lit1) =:= cerl:concrete(Lit2) of
+ true ->
+ if
+ Eval =:= pos -> {Map, t_atom(true)};
+ Eval =:= neg ->
+ ArgTypes = [t_from_term(cerl:concrete(Lit1)),
+ t_from_term(cerl:concrete(Lit2))],
+ signal_guard_fail(Eval, Guard, ArgTypes, State);
+ Eval =:= dont_know -> {Map, t_atom(true)}
+ end;
+ false ->
+ if
+ Eval =:= neg -> {Map, t_atom(false)};
+ Eval =:= dont_know -> {Map, t_atom(false)};
+ Eval =:= pos ->
+ ArgTypes = [t_from_term(cerl:concrete(Lit1)),
+ t_from_term(cerl:concrete(Lit2))],
+ signal_guard_fail(Eval, Guard, ArgTypes, State)
+ end
+ end;
+ {{literal, Lit1}, _} when Eval =:= pos ->
+ case cerl:concrete(Lit1) of
+ Atom when is_atom(Atom) ->
+ bind_eqeq_guard_lit_other(Guard, Lit1, Arg2, Map, Env, State);
+ [] ->
+ bind_eqeq_guard_lit_other(Guard, Lit1, Arg2, Map, Env, State);
+ _ ->
+ bind_eq_guard(Guard, Lit1, Arg2, Map, Env, Eval, State)
+ end;
+ {_, {literal, Lit2}} when Eval =:= pos ->
+ case cerl:concrete(Lit2) of
+ Atom when is_atom(Atom) ->
+ bind_eqeq_guard_lit_other(Guard, Lit2, Arg1, Map, Env, State);
+ [] ->
+ bind_eqeq_guard_lit_other(Guard, Lit2, Arg1, Map, Env, State);
+ _ ->
+ bind_eq_guard(Guard, Arg1, Lit2, Map, Env, Eval, State)
+ end;
+ {_, _} ->
+ bind_eq_guard(Guard, Arg1, Arg2, Map, Env, Eval, State)
+ end.
+
+bind_eq_guard(Guard, Arg1, Arg2, Map, Env, Eval, State) ->
+ {Map1, Type1} = bind_guard(Arg1, Map, Env, dont_know, State),
+ {Map2, Type2} = bind_guard(Arg2, Map1, Env, dont_know, State),
+ Opaques = State#state.opaques,
+ case
+ t_is_nil(Type1, Opaques) orelse t_is_nil(Type2, Opaques)
+ orelse t_is_atom(Type1, Opaques) orelse t_is_atom(Type2, Opaques)
+ of
+ true -> bind_eqeq_guard(Guard, Arg1, Arg2, Map, Env, Eval, State);
+ false ->
+ %% XXX. Is this test OK?
+ OpArgs = erl_types:t_find_unknown_opaque(Type1, Type2, Opaques),
+ case OpArgs =:= [] of
+ true ->
+ case Eval of
+ pos -> {Map2, t_atom(true)};
+ neg -> {Map2, t_atom(false)};
+ dont_know -> {Map2, t_boolean()}
+ end;
+ false ->
+ signal_guard_fail(Eval, Guard, [Type1, Type2], State)
+ end
+ end.
+
+handle_guard_eqeq(Guard, Map, Env, Eval, State) ->
+ [Arg1, Arg2] = cerl:call_args(Guard),
+ case {type(Arg1), type(Arg2)} of
+ {{literal, Lit1}, {literal, Lit2}} ->
+
+ case cerl:concrete(Lit1) =:= cerl:concrete(Lit2) of
+ true ->
+ if Eval =:= neg ->
+ ArgTypes = [t_from_term(cerl:concrete(Lit1)),
+ t_from_term(cerl:concrete(Lit2))],
+ signal_guard_fail(Eval, Guard, ArgTypes, State);
+ Eval =:= pos -> {Map, t_atom(true)};
+ Eval =:= dont_know -> {Map, t_atom(true)}
+ end;
+ false ->
+ if Eval =:= neg -> {Map, t_atom(false)};
+ Eval =:= dont_know -> {Map, t_atom(false)};
+ Eval =:= pos ->
+ ArgTypes = [t_from_term(cerl:concrete(Lit1)),
+ t_from_term(cerl:concrete(Lit2))],
+ signal_guard_fail(Eval, Guard, ArgTypes, State)
+ end
+ end;
+ {{literal, Lit1}, _} when Eval =:= pos ->
+ bind_eqeq_guard_lit_other(Guard, Lit1, Arg2, Map, Env, State);
+ {_, {literal, Lit2}} when Eval =:= pos ->
+ bind_eqeq_guard_lit_other(Guard, Lit2, Arg1, Map, Env, State);
+ {_, _} ->
+ bind_eqeq_guard(Guard, Arg1, Arg2, Map, Env, Eval, State)
+ end.
+
+bind_eqeq_guard(Guard, Arg1, Arg2, Map, Env, Eval, State) ->
+ {Map1, Type1} = bind_guard(Arg1, Map, Env, dont_know, State),
+ {Map2, Type2} = bind_guard(Arg2, Map1, Env, dont_know, State),
+ ?debug("Types are:~s =:= ~s\n", [t_to_string(Type1),
+ t_to_string(Type2)]),
+ Opaques = State#state.opaques,
+ Inf = t_inf(Type1, Type2, Opaques),
+ case t_is_none(Inf) of
+ true ->
+ OpArgs = erl_types:t_find_unknown_opaque(Type1, Type2, Opaques),
+ case OpArgs =:= [] of
+ true ->
+ case Eval of
+ neg -> {Map2, t_atom(false)};
+ dont_know -> {Map2, t_atom(false)};
+ pos -> signal_guard_fail(Eval, Guard, [Type1, Type2], State)
+ end;
+ false ->
+ signal_guard_fail(Eval, Guard, [Type1, Type2], State)
+ end;
+ false ->
+ case Eval of
+ pos ->
+ case {cerl:type(Arg1), cerl:type(Arg2)} of
+ {var, var} ->
+ Map3 = enter_subst(Arg1, Arg2, Map2),
+ Map4 = enter_type(Arg2, Inf, Map3),
+ {Map4, t_atom(true)};
+ {var, _} ->
+ Map3 = enter_type(Arg1, Inf, Map2),
+ {Map3, t_atom(true)};
+ {_, var} ->
+ Map3 = enter_type(Arg2, Inf, Map2),
+ {Map3, t_atom(true)};
+ {_, _} ->
+ {Map2, t_atom(true)}
+ end;
+ neg ->
+ {Map2, t_atom(false)};
+ dont_know ->
+ {Map2, t_boolean()}
+ end
+ end.
+
+bind_eqeq_guard_lit_other(Guard, Arg1, Arg2, Map, Env, State) ->
+ Eval = dont_know,
+ Opaques = State#state.opaques,
+ case cerl:concrete(Arg1) of
+ true ->
+ {_, Type} = MT = bind_guard(Arg2, Map, Env, pos, State),
+ case t_is_any_atom(true, Type, Opaques) of
+ true -> MT;
+ false ->
+ {_, Type0} = bind_guard(Arg2, Map, Env, Eval, State),
+ signal_guard_fail(Eval, Guard, [Type0, t_atom(true)], State)
+ end;
+ false ->
+ {Map1, Type} = bind_guard(Arg2, Map, Env, neg, State),
+ case t_is_any_atom(false, Type, Opaques) of
+ true -> {Map1, t_atom(true)};
+ false ->
+ {_, Type0} = bind_guard(Arg2, Map, Env, Eval, State),
+ signal_guard_fail(Eval, Guard, [Type0, t_atom(false)], State)
+ end;
+ Term ->
+ LitType = t_from_term(Term),
+ {Map1, Type} = bind_guard(Arg2, Map, Env, Eval, State),
+ case t_is_subtype(LitType, Type) of
+ false -> signal_guard_fail(Eval, Guard, [Type, LitType], State);
+ true ->
+ case cerl:is_c_var(Arg2) of
+ true -> {enter_type(Arg2, LitType, Map1), t_atom(true)};
+ false -> {Map1, t_atom(true)}
+ end
+ end
+ end.
+
+handle_guard_and(Guard, Map, Env, Eval, State) ->
+ [Arg1, Arg2] = cerl:call_args(Guard),
+ Opaques = State#state.opaques,
+ case Eval of
+ pos ->
+ {Map1, Type1} = bind_guard(Arg1, Map, Env, Eval, State),
+ case t_is_any_atom(true, Type1, Opaques) of
+ false -> signal_guard_fail(Eval, Guard, [Type1, t_any()], State);
+ true ->
+ {Map2, Type2} = bind_guard(Arg2, Map1, Env, Eval, State),
+ case t_is_any_atom(true, Type2, Opaques) of
+ false -> signal_guard_fail(Eval, Guard, [Type1, Type2], State);
+ true -> {Map2, t_atom(true)}
+ end
+ end;
+ neg ->
+ MapJ = join_maps_begin(Map),
+ {Map1, Type1} =
+ try bind_guard(Arg1, MapJ, Env, neg, State)
+ catch throw:{fail, _} -> bind_guard(Arg2, MapJ, Env, pos, State)
+ end,
+ {Map2, Type2} =
+ try bind_guard(Arg2, MapJ, Env, neg, State)
+ catch throw:{fail, _} -> bind_guard(Arg1, MapJ, Env, pos, State)
+ end,
+ case
+ t_is_any_atom(false, Type1, Opaques)
+ orelse t_is_any_atom(false, Type2, Opaques)
+ of
+ true -> {join_maps_end([Map1, Map2], MapJ), t_atom(false)};
+ false -> signal_guard_fail(Eval, Guard, [Type1, Type2], State)
+ end;
+ dont_know ->
+ MapJ = join_maps_begin(Map),
+ {Map1, Type1} = bind_guard(Arg1, MapJ, Env, dont_know, State),
+ {Map2, Type2} = bind_guard(Arg2, MapJ, Env, dont_know, State),
+ Bool1 = t_inf(Type1, t_boolean()),
+ Bool2 = t_inf(Type2, t_boolean()),
+ case t_is_none(Bool1) orelse t_is_none(Bool2) of
+ true -> throw({fatal_fail, none});
+ false ->
+ NewMap = join_maps_end([Map1, Map2], MapJ),
+ NewType =
+ case {t_atom_vals(Bool1, Opaques), t_atom_vals(Bool2, Opaques)} of
+ {['true'] , ['true'] } -> t_atom(true);
+ {['false'], _ } -> t_atom(false);
+ {_ , ['false']} -> t_atom(false);
+ {unknown , _ } ->
+ signal_guard_fail(Eval, Guard, [Type1, Type2], State);
+ {_ , unknown } ->
+ signal_guard_fail(Eval, Guard, [Type1, Type2], State);
+ {_ , _ } -> t_boolean()
+
+ end,
+ {NewMap, NewType}
+ end
+ end.
+
+handle_guard_or(Guard, Map, Env, Eval, State) ->
+ [Arg1, Arg2] = cerl:call_args(Guard),
+ Opaques = State#state.opaques,
+ case Eval of
+ pos ->
+ MapJ = join_maps_begin(Map),
+ {Map1, Bool1} =
+ try bind_guard(Arg1, MapJ, Env, pos, State)
+ catch
+ throw:{fail,_} -> bind_guard(Arg1, MapJ, Env, dont_know, State)
+ end,
+ {Map2, Bool2} =
+ try bind_guard(Arg2, MapJ, Env, pos, State)
+ catch
+ throw:{fail,_} -> bind_guard(Arg2, MapJ, Env, dont_know, State)
+ end,
+ case
+ ((t_is_any_atom(true, Bool1, Opaques)
+ andalso t_is_boolean(Bool2, Opaques))
+ orelse
+ (t_is_any_atom(true, Bool2, Opaques)
+ andalso t_is_boolean(Bool1, Opaques)))
+ of
+ true -> {join_maps_end([Map1, Map2], MapJ), t_atom(true)};
+ false -> signal_guard_fail(Eval, Guard, [Bool1, Bool2], State)
+ end;
+ neg ->
+ {Map1, Type1} = bind_guard(Arg1, Map, Env, neg, State),
+ case t_is_any_atom(false, Type1, Opaques) of
+ false -> signal_guard_fail(Eval, Guard, [Type1, t_any()], State);
+ true ->
+ {Map2, Type2} = bind_guard(Arg2, Map1, Env, neg, State),
+ case t_is_any_atom(false, Type2, Opaques) of
+ false -> signal_guard_fail(Eval, Guard, [Type1, Type2], State);
+ true -> {Map2, t_atom(false)}
+ end
+ end;
+ dont_know ->
+ MapJ = join_maps_begin(Map),
+ {Map1, Type1} = bind_guard(Arg1, MapJ, Env, dont_know, State),
+ {Map2, Type2} = bind_guard(Arg2, MapJ, Env, dont_know, State),
+ Bool1 = t_inf(Type1, t_boolean()),
+ Bool2 = t_inf(Type2, t_boolean()),
+ case t_is_none(Bool1) orelse t_is_none(Bool2) of
+ true -> throw({fatal_fail, none});
+ false ->
+ NewMap = join_maps_end([Map1, Map2], MapJ),
+ NewType =
+ case {t_atom_vals(Bool1, Opaques), t_atom_vals(Bool2, Opaques)} of
+ {['false'], ['false']} -> t_atom(false);
+ {['true'] , _ } -> t_atom(true);
+ {_ , ['true'] } -> t_atom(true);
+ {unknown , _ } ->
+ signal_guard_fail(Eval, Guard, [Type1, Type2], State);
+ {_ , unknown } ->
+ signal_guard_fail(Eval, Guard, [Type1, Type2], State);
+ {_ , _ } -> t_boolean()
+ end,
+ {NewMap, NewType}
+ end
+ end.
+
+handle_guard_not(Guard, Map, Env, Eval, State) ->
+ [Arg] = cerl:call_args(Guard),
+ Opaques = State#state.opaques,
+ case Eval of
+ neg ->
+ {Map1, Type} = bind_guard(Arg, Map, Env, pos, State),
+ case t_is_any_atom(true, Type, Opaques) of
+ true -> {Map1, t_atom(false)};
+ false ->
+ {_, Type0} = bind_guard(Arg, Map, Env, Eval, State),
+ signal_guard_fail(Eval, Guard, [Type0], State)
+ end;
+ pos ->
+ {Map1, Type} = bind_guard(Arg, Map, Env, neg, State),
+ case t_is_any_atom(false, Type, Opaques) of
+ true -> {Map1, t_atom(true)};
+ false ->
+ {_, Type0} = bind_guard(Arg, Map, Env, Eval, State),
+ signal_guard_fail(Eval, Guard, [Type0], State)
+ end;
+ dont_know ->
+ {Map1, Type} = bind_guard(Arg, Map, Env, dont_know, State),
+ Bool = t_inf(Type, t_boolean()),
+ case t_is_none(Bool) of
+ true -> throw({fatal_fail, none});
+ false ->
+ case t_atom_vals(Bool, Opaques) of
+ ['true'] -> {Map1, t_atom(false)};
+ ['false'] -> {Map1, t_atom(true)};
+ [_, _] -> {Map1, Bool};
+ unknown -> signal_guard_fail(Eval, Guard, [Type], State)
+ end
+ end
+ end.
+
+bind_guard_list(Guards, Map, Env, Eval, State) ->
+ bind_guard_list(Guards, Map, Env, Eval, State, []).
+
+bind_guard_list([G|Gs], Map, Env, Eval, State, Acc) ->
+ {Map1, T} = bind_guard(G, Map, Env, Eval, State),
+ bind_guard_list(Gs, Map1, Env, Eval, State, [T|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()],
+ state()) -> no_return().
+
+signal_guard_fail(Eval, Guard, ArgTypes, State) ->
+ signal_guard_failure(Eval, Guard, ArgTypes, fail, State).
+
+-spec signal_guard_fatal_fail(eval(), cerl:c_call(), [erl_types:erl_type()],
+ state()) -> no_return().
+
+signal_guard_fatal_fail(Eval, Guard, ArgTypes, State) ->
+ signal_guard_failure(Eval, Guard, ArgTypes, fatal_fail, State).
+
+signal_guard_failure(Eval, Guard, ArgTypes, Tag, State) ->
+ Args = cerl:call_args(Guard),
+ F = cerl:atom_val(cerl:call_name(Guard)),
+ {M, F, A} = MFA = {cerl:atom_val(cerl:call_module(Guard)), F, length(Args)},
+ Opaques = State#state.opaques,
+ {Kind, XInfo} =
+ case erl_bif_types:opaque_args(M, F, A, ArgTypes, Opaques) of
+ [] ->
+ {case Eval of
+ neg -> neg_guard_fail;
+ pos -> guard_fail;
+ dont_know -> guard_fail
+ end,
+ []};
+ Ns -> {opaque_guard, [Ns]}
+ end,
+ FArgs =
+ case is_infix_op(MFA) of
+ true ->
+ [ArgType1, ArgType2] = ArgTypes,
+ [Arg1, Arg2] = Args,
+ [format_args_1([Arg1], [ArgType1], State),
+ atom_to_list(F),
+ format_args_1([Arg2], [ArgType2], State)] ++ XInfo;
+ false ->
+ [F, format_args(Args, ArgTypes, State)]
+ end,
+ Msg = {Kind, FArgs},
+ throw({Tag, {Guard, Msg}}).
+
+is_infix_op({erlang, '=:=', 2}) -> true;
+is_infix_op({erlang, '==', 2}) -> true;
+is_infix_op({erlang, '=/=', 2}) -> true;
+is_infix_op({erlang, '=/', 2}) -> true;
+is_infix_op({erlang, '<', 2}) -> true;
+is_infix_op({erlang, '=<', 2}) -> true;
+is_infix_op({erlang, '>', 2}) -> true;
+is_infix_op({erlang, '>=', 2}) -> true;
+is_infix_op({M, F, A}) when is_atom(M), is_atom(F),
+ is_integer(A), 0 =< A, A =< 255 -> false.
+
+bif_args(M, F, A) ->
+ case erl_bif_types:arg_types(M, F, A) of
+ unknown -> lists:duplicate(A, t_any());
+ List -> List
+ end.
+
+bind_guard_case_clauses(Arg, Clauses, Map0, Env, Eval, State) ->
+ Clauses1 = filter_fail_clauses(Clauses),
+ Map = join_maps_begin(Map0),
+ {GenMap, GenArgType} = bind_guard(Arg, Map, Env, dont_know, State),
+ bind_guard_case_clauses(GenArgType, GenMap, Arg, Clauses1, Map, Env, Eval,
+ t_none(), [], State).
+
+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) 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;
+ false ->
+ [Clause|filter_fail_clauses(Left)]
+ end;
+filter_fail_clauses([]) ->
+ [].
+
+bind_guard_case_clauses(GenArgType, GenMap, ArgExpr, [Clause|Left],
+ Map, Env, Eval, AccType, AccMaps, State) ->
+ Pats = cerl:clause_pats(Clause),
+ {NewMap0, ArgType} =
+ case Pats of
+ [Pat] ->
+ case cerl:is_literal(Pat) of
+ true ->
+ try
+ case cerl:concrete(Pat) of
+ true -> bind_guard(ArgExpr, Map, Env, pos, State);
+ false -> bind_guard(ArgExpr, Map, Env, neg, State);
+ _ -> {GenMap, GenArgType}
+ end
+ catch
+ throw:{fail, _} -> {none, GenArgType}
+ end;
+ false ->
+ {GenMap, GenArgType}
+ end;
+ _ -> {GenMap, GenArgType}
+ end,
+ NewMap1 =
+ case Pats =:= [] of
+ true -> NewMap0;
+ false ->
+ case t_is_none(ArgType) of
+ true -> none;
+ false ->
+ ArgTypes = case t_is_any(ArgType) of
+ true -> Any = t_any(), [Any || _ <- Pats];
+ false -> t_to_tlist(ArgType)
+ end,
+ case bind_pat_vars(Pats, ArgTypes, [], NewMap0, State) of
+ {error, _, _, _, _} -> none;
+ {PatMap, _PatTypes} -> PatMap
+ end
+ end
+ end,
+ Guard = cerl:clause_guard(Clause),
+ GenPatType = dialyzer_typesig:get_safe_underapprox(Pats, Guard),
+ NewGenArgType = t_subtract(GenArgType, GenPatType),
+ case (NewMap1 =:= none) orelse t_is_none(GenArgType) of
+ true ->
+ bind_guard_case_clauses(NewGenArgType, GenMap, ArgExpr, Left, Map, Env,
+ Eval, AccType, AccMaps, State);
+ false ->
+ {NewAccType, NewAccMaps} =
+ try
+ {NewMap2, GuardType} = bind_guard(Guard, NewMap1, Env, pos, State),
+ case t_is_none(t_inf(t_atom(true), GuardType)) of
+ true -> throw({fail, none});
+ false -> ok
+ end,
+ {NewMap3, CType} = bind_guard(cerl:clause_body(Clause), NewMap2,
+ Env, Eval, State),
+ Opaques = State#state.opaques,
+ case Eval of
+ pos ->
+ case t_is_any_atom(true, CType, Opaques) of
+ true -> ok;
+ false -> throw({fail, none})
+ end;
+ neg ->
+ case t_is_any_atom(false, CType, Opaques) of
+ true -> ok;
+ false -> throw({fail, none})
+ end;
+ dont_know ->
+ ok
+ end,
+ {t_sup(AccType, CType), [NewMap3|AccMaps]}
+ catch
+ throw:{fail, _What} -> {AccType, AccMaps}
+ end,
+ bind_guard_case_clauses(NewGenArgType, GenMap, ArgExpr, Left, Map, Env,
+ Eval, NewAccType, NewAccMaps, State)
+ end;
+bind_guard_case_clauses(_GenArgType, _GenMap, _ArgExpr, [], Map, _Env, _Eval,
+ AccType, AccMaps, _State) ->
+ case t_is_none(AccType) of
+ true -> throw({fail, none});
+ false -> {join_maps_end(AccMaps, Map), AccType}
+ end.
+
+%%% ===========================================================================
+%%%
+%%% Maps and types.
+%%%
+%%% ===========================================================================
+
+map__new() ->
+ #map{}.
+
+%% join_maps_begin pushes 'modified' to the stack; join_maps pops
+%% 'modified' from the stack.
+
+join_maps_begin(#map{modified = M, modified_stack = S, ref = Ref} = Map) ->
+ Map#map{ref = make_ref(), modified = [], modified_stack = [{M,Ref} | S]}.
+
+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{map = Map, subst = Subst} = MapOut,
+ Keys = [Key ||
+ Key <- Keys0,
+ maps:is_key(Key, Map) orelse maps:is_key(Key, Subst)],
+ Out = case Maps of
+ [] -> join_maps(Maps, MapOut);
+ _ -> join_maps(Keys, Maps, MapOut)
+ end,
+ debug_join_check(Maps, MapOut, Out),
+ Out#map{ref = R1,
+ modified = Out#map.modified ++ M1, % duplicates possible
+ modified_stack = S}.
+
+join_maps(Maps, MapOut) ->
+ #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) ->
+ KTs = join_maps_collect(Keys, Maps, MapOut),
+ lists:foldl(fun({K, T}, M) -> enter_type(K, T, M) end, MapOut, KTs).
+
+join_maps_collect([Key|Left], Maps, MapOut) ->
+ Type = join_maps_one_key(Maps, Key, t_none()),
+ case t_is_equal(lookup_type(Key, MapOut), Type) of
+ true -> join_maps_collect(Left, Maps, MapOut);
+ false -> [{Key, Type} | join_maps_collect(Left, Maps, MapOut)]
+ end;
+join_maps_collect([], _Maps, _MapOut) ->
+ [].
+
+join_maps_one_key([Map|Left], Key, AccType) ->
+ case t_is_any(AccType) of
+ true ->
+ %% We can stop here
+ AccType;
+ false ->
+ join_maps_one_key(Left, Key, t_sup(lookup_type(Key, Map), AccType))
+ end;
+join_maps_one_key([], _Key, AccType) ->
+ AccType.
+
+-ifdef(DEBUG).
+debug_join_check(Maps, MapOut, Out) ->
+ #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(Map) =/= F(Map2) orelse F(Subst) =/= F(Subst2)].
+-else.
+debug_join_check(_Maps, _MapOut, _Out) -> ok.
+-endif.
+
+enter_type_lists([Key|KeyTail], [Val|ValTail], Map) ->
+ Map1 = enter_type(Key, Val, Map),
+ enter_type_lists(KeyTail, ValTail, Map1);
+enter_type_lists([], [], Map) ->
+ Map.
+
+enter_type_list([{Key, Val}|Left], Map) ->
+ Map1 = enter_type(Key, Val, Map),
+ enter_type_list(Left, Map1);
+enter_type_list([], Map) ->
+ Map.
+
+enter_type(Key, Val, MS) ->
+ case cerl:is_literal(Key) of
+ true -> MS;
+ false ->
+ case cerl:is_c_values(Key) of
+ true ->
+ Keys = cerl:values_es(Key),
+ case t_is_any(Val) orelse t_is_none(Val) of
+ true ->
+ enter_type_lists(Keys, [Val || _ <- Keys], MS);
+ false ->
+ enter_type_lists(Keys, t_to_tlist(Val), MS)
+ end;
+ false ->
+ #map{map = Map, subst = Subst} = MS,
+ KeyLabel = get_label(Key),
+ 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 maps:find(KeyLabel, Map) of
+ {ok, Value} ->
+ case erl_types:t_is_equal(Val, Value) of
+ true -> MS;
+ false -> store_map(KeyLabel, Val, MS)
+ end;
+ error -> store_map(KeyLabel, Val, MS)
+ end
+ end
+ end
+ end.
+
+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, 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);
+ false ->
+ case cerl:is_c_var(Val) of
+ false -> MS;
+ true ->
+ ValLabel = get_label(Val),
+ case maps:find(ValLabel, Subst) of
+ {ok, NewVal} ->
+ enter_subst(Key, NewVal, MS);
+ error ->
+ if KeyLabel =:= ValLabel -> MS;
+ true ->
+ ?debug("Subst: storing ~p = ~p\n", [KeyLabel, ValLabel]),
+ store_subst(KeyLabel, ValLabel, MS)
+ end
+ end
+ end
+ end.
+
+store_subst(Key, Val, #map{subst = S, ref = undefined} = Map) ->
+ Map#map{subst = maps:put(Key, Val, S)};
+store_subst(Key, Val, #map{subst = S, modified = Mod} = Map) ->
+ Map#map{subst = maps:put(Key, Val, S), modified = [Key | Mod]}.
+
+lookup_type(Key, #map{map = Map, subst = Subst}) ->
+ lookup(Key, Map, Subst, t_none()).
+
+lookup(Key, Map, Subst, AnyNone) ->
+ case cerl:is_literal(Key) of
+ true -> literal_type(Key);
+ false ->
+ Label = get_label(Key),
+ case maps:find(Label, Subst) of
+ {ok, NewKey} -> lookup(NewKey, Map, Subst, AnyNone);
+ error ->
+ case maps:find(Label, Map) of
+ {ok, Val} -> Val;
+ error -> AnyNone
+ end
+ end
+ end.
+
+lookup_fun_sig(Fun, Callgraph, Plt) ->
+ MFAorLabel =
+ case dialyzer_callgraph:lookup_name(Fun, Callgraph) of
+ error -> Fun;
+ {ok, MFA} -> MFA
+ end,
+ dialyzer_plt:lookup(Plt, MFAorLabel).
+
+literal_type(Lit) ->
+ t_from_term(cerl:concrete(Lit)).
+
+mark_as_fresh([Tree|Left], Map) ->
+ SubTrees1 = lists:append(cerl:subtrees(Tree)),
+ {SubTrees2, Map1} =
+ case cerl:type(Tree) of
+ 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)};
+ _ ->
+ {SubTrees1, Map}
+ end,
+ mark_as_fresh(SubTrees2 ++ Left, Map1);
+mark_as_fresh([], Map) ->
+ Map.
+
+-ifdef(DEBUG).
+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, MapRec))])
+ end, Keys),
+ ok.
+-else.
+debug_pp_map(_Map) -> ok.
+-endif.
+
+%%% ===========================================================================
+%%%
+%%% Utilities
+%%%
+%%% ===========================================================================
+
+get_label(L) when is_integer(L) ->
+ L;
+get_label(T) ->
+ cerl_trees:get_label(T).
+
+t_is_simple(ArgType, State) ->
+ Opaques = State#state.opaques,
+ t_is_atom(ArgType, Opaques) orelse t_is_number(ArgType, Opaques)
+ orelse t_is_port(ArgType, Opaques)
+ orelse t_is_pid(ArgType, Opaques) orelse t_is_reference(ArgType, Opaques)
+ orelse t_is_nil(ArgType, Opaques).
+
+remove_local_opaque_types(Type, Opaques) ->
+ t_unopaque(Type, Opaques).
+
+%% t_is_structured(ArgType) ->
+%% case t_is_nil(ArgType) of
+%% true -> false;
+%% false ->
+%% SType = t_inf(t_sup([t_list(), t_tuple(), t_binary()]), ArgType),
+%% t_is_equal(ArgType, SType)
+%% end.
+
+is_call_to_send(Tree) ->
+ case cerl:is_c_call(Tree) of
+ false -> false;
+ true ->
+ Mod = cerl:call_module(Tree),
+ Name = cerl:call_name(Tree),
+ Arity = cerl:call_arity(Tree),
+ cerl:is_c_atom(Mod)
+ andalso cerl:is_c_atom(Name)
+ andalso is_send(cerl:atom_val(Name))
+ andalso (cerl:atom_val(Mod) =:= erlang)
+ andalso (Arity =:= 2)
+ end.
+
+is_send('!') -> true;
+is_send(send) -> true;
+is_send(_) -> false.
+
+is_lc_simple_list(Tree, TreeType, State) ->
+ Opaques = State#state.opaques,
+ Ann = cerl:get_ann(Tree),
+ lists:member(list_comprehension, Ann)
+ andalso t_is_list(TreeType)
+ andalso t_is_simple(t_list_elements(TreeType, Opaques), State).
+
+filter_match_fail([Clause] = Cls) ->
+ Body = cerl:clause_body(Clause),
+ case cerl:type(Body) of
+ primop ->
+ case cerl:atom_val(cerl:primop_name(Body)) of
+ match_fail -> [];
+ raise -> [];
+ _ -> Cls
+ end;
+ _ -> Cls
+ end;
+filter_match_fail([H|T]) ->
+ [H|filter_match_fail(T)];
+filter_match_fail([]) ->
+ %% This can actually happen, for example in
+ %% receive after 1 -> ok end
+ [].
+
+%%% ===========================================================================
+%%%
+%%% The State.
+%%%
+%%% ===========================================================================
+
+state__new(Callgraph, Codeserver, Tree, Plt, Module, Records) ->
+ Opaques = erl_types:t_opaque_from_records(Records),
+ {TreeMap, FunHomes} = build_tree_map(Tree, Callgraph),
+ Funs = dict:fetch_keys(TreeMap),
+ FunTab = init_fun_tab(Funs, dict:new(), TreeMap, Callgraph, Plt),
+ ExportedFuns =
+ [Fun || Fun <- Funs--[top], dialyzer_callgraph:is_escaping(Fun, Callgraph)],
+ Work = init_work(ExportedFuns),
+ Env = lists:foldl(fun(Fun, Env) -> dict:store(Fun, map__new(), Env) end,
+ dict:new(), Funs),
+ #state{callgraph = Callgraph, codeserver = Codeserver,
+ envs = Env, fun_tab = FunTab, fun_homes = FunHomes, opaques = Opaques,
+ plt = Plt, races = dialyzer_races:new(), records = Records,
+ warning_mode = false, warnings = [], work = Work, tree_map = TreeMap,
+ module = Module}.
+
+state__warning_mode(#state{warning_mode = WM}) ->
+ WM.
+
+state__set_warning_mode(#state{tree_map = TreeMap, fun_tab = FunTab,
+ races = Races} = State) ->
+ ?debug("==========\nStarting warning pass\n==========\n", []),
+ Funs = dict:fetch_keys(TreeMap),
+ State#state{work = init_work([top|Funs--[top]]),
+ fun_tab = FunTab, warning_mode = true,
+ races = dialyzer_races:put_race_analysis(true, Races)}.
+
+state__race_analysis(Analysis, #state{races = Races} = State) ->
+ State#state{races = dialyzer_races:put_race_analysis(Analysis, Races)}.
+
+state__renew_curr_fun(CurrFun, CurrFunLabel,
+ #state{races = Races} = State) ->
+ State#state{races = dialyzer_races:put_curr_fun(CurrFun, CurrFunLabel,
+ Races)}.
+
+state__renew_fun_args(Args, #state{races = Races} = State) ->
+ case state__warning_mode(State) of
+ true -> State;
+ false ->
+ State#state{races = dialyzer_races:put_fun_args(Args, Races)}
+ end.
+
+state__renew_race_list(RaceList, RaceListSize,
+ #state{races = Races} = State) ->
+ State#state{races = dialyzer_races:put_race_list(RaceList, RaceListSize,
+ Races)}.
+
+state__renew_warnings(Warnings, State) ->
+ State#state{warnings = Warnings}.
+
+-spec state__add_warning(raw_warning(), state()) -> state().
+
+state__add_warning(Warn, #state{warnings = Warnings} = State) ->
+ State#state{warnings = [Warn|Warnings]}.
+
+state__add_warning(State, Tag, Tree, Msg) ->
+ state__add_warning(State, Tag, Tree, Msg, false).
+
+state__add_warning(#state{warning_mode = false} = State, _, _, _, _) ->
+ State;
+state__add_warning(#state{warnings = Warnings, warning_mode = true} = State,
+ Tag, Tree, Msg, Force) ->
+ Ann = cerl:get_ann(Tree),
+ case Force of
+ true ->
+ WarningInfo = {get_file(Ann),
+ abs(get_line(Ann)),
+ State#state.curr_fun},
+ Warn = {Tag, WarningInfo, Msg},
+ ?debug("MSG ~s\n", [dialyzer:format_warning(Warn)]),
+ State#state{warnings = [Warn|Warnings]};
+ false ->
+ case is_compiler_generated(Ann) of
+ true -> State;
+ false ->
+ WarningInfo = {get_file(Ann), get_line(Ann), State#state.curr_fun},
+ Warn = {Tag, WarningInfo, Msg},
+ case Tag of
+ ?WARN_CONTRACT_RANGE -> ok;
+ _ -> ?debug("MSG ~s\n", [dialyzer:format_warning(Warn)])
+ end,
+ State#state{warnings = [Warn|Warnings]}
+ end
+ end.
+
+state__remove_added_warnings(OldState, NewState) ->
+ #state{warnings = OldWarnings} = OldState,
+ #state{warnings = NewWarnings} = NewState,
+ {NewWarnings -- OldWarnings, NewState#state{warnings = OldWarnings}}.
+
+state__add_warnings(Warns, #state{warnings = Warnings} = State) ->
+ State#state{warnings = Warns ++ Warnings}.
+
+-spec state__set_curr_fun(curr_fun(), state()) -> state().
+
+state__set_curr_fun(undefined, State) ->
+ State#state{curr_fun = undefined};
+state__set_curr_fun(FunLbl, State) ->
+ State#state{curr_fun = find_function(FunLbl, State)}.
+
+-spec state__find_function(mfa_or_funlbl(), state()) -> mfa_or_funlbl().
+
+state__find_function(FunLbl, State) ->
+ find_function(FunLbl, State).
+
+state__get_race_warnings(#state{races = Races} = State) ->
+ {Races1, State1} = dialyzer_races:get_race_warnings(Races, State),
+ State1#state{races = Races1}.
+
+state__get_warnings(#state{tree_map = TreeMap, fun_tab = FunTab,
+ callgraph = Callgraph, plt = Plt} = State) ->
+ FoldFun =
+ fun({top, _}, AccState) -> AccState;
+ ({FunLbl, Fun}, AccState) ->
+ AccState1 = state__set_curr_fun(FunLbl, AccState),
+ {NotCalled, Ret} =
+ case dict:fetch(get_label(Fun), FunTab) of
+ {not_handled, {_Args0, Ret0}} -> {true, Ret0};
+ {_Args0, Ret0} -> {false, Ret0}
+ end,
+ case NotCalled of
+ true ->
+ case dialyzer_callgraph:lookup_name(FunLbl, Callgraph) of
+ error -> AccState1;
+ {ok, {_M, F, A}} ->
+ Msg = {unused_fun, [F, A]},
+ state__add_warning(AccState1, ?WARN_NOT_CALLED, Fun, Msg)
+ end;
+ false ->
+ {Name, Contract} =
+ case dialyzer_callgraph:lookup_name(FunLbl, Callgraph) of
+ error -> {[], none};
+ {ok, {_M, F, A} = MFA} ->
+ {[F, A], dialyzer_plt:lookup_contract(Plt, MFA)}
+ end,
+ case t_is_none(Ret) of
+ true ->
+ %% Check if the function has a contract that allows this.
+ Warn =
+ case Contract of
+ none -> not parent_allows_this(FunLbl, AccState1);
+ {value, C} ->
+ GenRet = dialyzer_contracts:get_contract_return(C),
+ not t_is_unit(GenRet)
+ end,
+ case Warn of
+ true ->
+ case classify_returns(Fun) of
+ no_match ->
+ Msg = {no_return, [no_match|Name]},
+ state__add_warning(AccState1, ?WARN_RETURN_NO_RETURN,
+ Fun, Msg);
+ only_explicit ->
+ Msg = {no_return, [only_explicit|Name]},
+ state__add_warning(AccState1, ?WARN_RETURN_ONLY_EXIT,
+ Fun, Msg);
+ only_normal ->
+ Msg = {no_return, [only_normal|Name]},
+ state__add_warning(AccState1, ?WARN_RETURN_NO_RETURN,
+ Fun, Msg);
+ both ->
+ Msg = {no_return, [both|Name]},
+ state__add_warning(AccState1, ?WARN_RETURN_NO_RETURN,
+ Fun, Msg)
+ end;
+ false ->
+ AccState
+ end;
+ false ->
+ AccState
+ end
+ end
+ end,
+ #state{warnings = Warn} = lists:foldl(FoldFun, State, dict:to_list(TreeMap)),
+ Warn.
+
+state__is_escaping(Fun, #state{callgraph = Callgraph}) ->
+ dialyzer_callgraph:is_escaping(Fun, Callgraph).
+
+state__lookup_type_for_letrec(Var, #state{callgraph = Callgraph} = State) ->
+ Label = get_label(Var),
+ case dialyzer_callgraph:lookup_letrec(Label, Callgraph) of
+ error -> error;
+ {ok, FunLabel} ->
+ {ok, state__fun_type(FunLabel, State)}
+ end.
+
+state__lookup_name({_, _, _} = MFA, #state{}) ->
+ MFA;
+state__lookup_name(top, #state{}) ->
+ top;
+state__lookup_name(Fun, #state{callgraph = Callgraph}) ->
+ case dialyzer_callgraph:lookup_name(Fun, Callgraph) of
+ {ok, MFA} -> MFA;
+ error -> Fun
+ end.
+
+state__lookup_record(Tag, Arity, #state{records = Records}) ->
+ case erl_types:lookup_record(Tag, Arity, Records) of
+ {ok, Fields} ->
+ RecType =
+ t_tuple([t_atom(Tag)|
+ [FieldType || {_FieldName, _Abstr, FieldType} <- Fields]]),
+ {ok, RecType};
+ error ->
+ error
+ end.
+
+state__get_args_and_status(Tree, #state{fun_tab = FunTab}) ->
+ Fun = get_label(Tree),
+ case dict:find(Fun, FunTab) of
+ {ok, {not_handled, {ArgTypes, _}}} -> {ArgTypes, false};
+ {ok, {ArgTypes, _}} -> {ArgTypes, true}
+ end.
+
+build_tree_map(Tree, Callgraph) ->
+ Fun =
+ fun(T, {Dict, Homes, FunLbls} = Acc) ->
+ case cerl:is_c_fun(T) of
+ true ->
+ FunLbl = get_label(T),
+ Dict1 = dict:store(FunLbl, T, Dict),
+ case catch dialyzer_callgraph:lookup_name(FunLbl, Callgraph) of
+ {ok, MFA} ->
+ F2 =
+ fun(Lbl, Dict0) ->
+ dict:store(Lbl, MFA, Dict0)
+ end,
+ Homes1 = lists:foldl(F2, Homes, [FunLbl|FunLbls]),
+ {Dict1, Homes1, []};
+ _ ->
+ {Dict1, Homes, [FunLbl|FunLbls]}
+ end;
+ false ->
+ Acc
+ end
+ end,
+ Dict0 = dict:new(),
+ {Dict, Homes, _} = cerl_trees:fold(Fun, {Dict0, Dict0, []}, Tree),
+ {Dict, Homes}.
+
+init_fun_tab([top|Left], Dict, TreeMap, Callgraph, Plt) ->
+ NewDict = dict:store(top, {[], t_none()}, Dict),
+ init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt);
+init_fun_tab([Fun|Left], Dict, TreeMap, Callgraph, Plt) ->
+ Arity = cerl:fun_arity(dict:fetch(Fun, TreeMap)),
+ FunEntry =
+ case dialyzer_callgraph:is_escaping(Fun, Callgraph) of
+ true ->
+ Args = lists:duplicate(Arity, t_any()),
+ case lookup_fun_sig(Fun, Callgraph, Plt) of
+ none -> {Args, t_unit()};
+ {value, {RetType, _}} ->
+ case t_is_none(RetType) of
+ true -> {Args, t_none()};
+ false -> {Args, t_unit()}
+ end
+ end;
+ false -> {not_handled, {lists:duplicate(Arity, t_none()), t_unit()}}
+ end,
+ NewDict = dict:store(Fun, FunEntry, Dict),
+ init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt);
+init_fun_tab([], Dict, _TreeMap, _Callgraph, _Plt) ->
+ ?debug("DICT:~p\n",[dict:to_list(Dict)]),
+ Dict.
+
+state__update_fun_env(Tree, Map, #state{envs = Envs} = State) ->
+ NewEnvs = dict:store(get_label(Tree), Map, Envs),
+ State#state{envs = NewEnvs}.
+
+state__fun_env(Tree, #state{envs = Envs}) ->
+ Fun = get_label(Tree),
+ case dict:find(Fun, Envs) of
+ error -> none;
+ {ok, Map} -> Map
+ end.
+
+state__clean_not_called(#state{fun_tab = FunTab} = State) ->
+ NewFunTab =
+ dict:map(fun(top, Entry) -> Entry;
+ (_Fun, {not_handled, {Args, _}}) -> {Args, t_none()};
+ (_Fun, Entry) -> Entry
+ end, FunTab),
+ State#state{fun_tab = NewFunTab}.
+
+state__all_fun_types(State) ->
+ #state{fun_tab = FunTab} = state__clean_not_called(State),
+ Tab1 = dict:erase(top, FunTab),
+ dict:map(fun(_Fun, {Args, Ret}) -> t_fun(Args, Ret)end, Tab1).
+
+state__fun_type(Fun, #state{fun_tab = FunTab}) ->
+ Label =
+ if is_integer(Fun) -> Fun;
+ true -> get_label(Fun)
+ end,
+ Entry = dict:find(Label, FunTab),
+ ?debug("FunType ~p:~p\n",[Label, Entry]),
+ case Entry of
+ {ok, {not_handled, {A, R}}} ->
+ t_fun(A, R);
+ {ok, {A, R}} ->
+ t_fun(A, R)
+ end.
+
+state__update_fun_entry(Tree, ArgTypes, Out0,
+ #state{fun_tab=FunTab, callgraph=CG, plt=Plt} = State)->
+ Fun = get_label(Tree),
+ Out1 =
+ if Fun =:= top -> Out0;
+ true ->
+ case lookup_fun_sig(Fun, CG, Plt) of
+ {value, {SigRet, _}} -> t_inf(SigRet, Out0);
+ none -> Out0
+ end
+ end,
+ Out = t_limit(Out1, ?TYPE_LIMIT),
+ {ok, {OldArgTypes, OldOut}} = dict:find(Fun, FunTab),
+ SameArgs = lists:all(fun({A, B}) -> erl_types:t_is_equal(A, B)
+ end, lists:zip(OldArgTypes, ArgTypes)),
+ SameOut = t_is_equal(OldOut, Out),
+ if
+ SameArgs, SameOut ->
+ ?debug("Fixpoint for ~w: ~s\n",
+ [state__lookup_name(Fun, State),
+ t_to_string(t_fun(ArgTypes, Out))]),
+ State;
+ true ->
+ %% Can only happen in self-recursive functions.
+ NewEntry = {OldArgTypes, Out},
+ ?debug("New Entry for ~w: ~s\n",
+ [state__lookup_name(Fun, State),
+ t_to_string(t_fun(OldArgTypes, Out))]),
+ NewFunTab = dict:store(Fun, NewEntry, FunTab),
+ State1 = State#state{fun_tab = NewFunTab},
+ state__add_work_from_fun(Tree, State1)
+ end.
+
+state__add_work_from_fun(_Tree, #state{warning_mode = true} = State) ->
+ State;
+state__add_work_from_fun(Tree, #state{callgraph = Callgraph,
+ tree_map = TreeMap} = State) ->
+ case get_label(Tree) of
+ top -> State;
+ Label when is_integer(Label) ->
+ case dialyzer_callgraph:in_neighbours(Label, Callgraph) of
+ none -> State;
+ MFAList ->
+ LabelList = [dialyzer_callgraph:lookup_label(MFA, Callgraph)
+ || MFA <- MFAList],
+ %% Must filter the result for results in this module.
+ FilteredList = [L || {ok, L} <- LabelList, dict:is_key(L, TreeMap)],
+ ?debug("~w: Will try to add:~w\n",
+ [state__lookup_name(Label, State), MFAList]),
+ lists:foldl(fun(L, AccState) ->
+ state__add_work(L, AccState)
+ end, State, FilteredList)
+ end
+ end.
+
+state__add_work(external, State) ->
+ State;
+state__add_work(top, State) ->
+ State;
+state__add_work(Fun, #state{work = Work} = State) ->
+ NewWork = add_work(Fun, Work),
+ State#state{work = NewWork}.
+
+state__get_work(#state{work = Work, tree_map = TreeMap} = State) ->
+ case get_work(Work) of
+ none -> none;
+ {Fun, NewWork} ->
+ {dict:fetch(Fun, TreeMap), State#state{work = NewWork}}
+ end.
+
+state__lookup_call_site(Tree, #state{callgraph = Callgraph}) ->
+ Label = get_label(Tree),
+ dialyzer_callgraph:lookup_call_site(Label, Callgraph).
+
+state__fun_info(external, #state{}) ->
+ external;
+state__fun_info({_, _, _} = MFA, #state{plt = PLT}) ->
+ {MFA,
+ dialyzer_plt:lookup(PLT, MFA),
+ dialyzer_plt:lookup_contract(PLT, MFA),
+ t_any()};
+state__fun_info(Fun, #state{callgraph = CG, fun_tab = FunTab, plt = PLT}) ->
+ {Sig, Contract} =
+ case dialyzer_callgraph:lookup_name(Fun, CG) of
+ error ->
+ {dialyzer_plt:lookup(PLT, Fun), none};
+ {ok, MFA} ->
+ {dialyzer_plt:lookup(PLT, MFA), dialyzer_plt:lookup_contract(PLT, MFA)}
+ end,
+ LocalRet =
+ case dict:fetch(Fun, FunTab) of
+ {not_handled, {_Args, Ret}} -> Ret;
+ {_Args, Ret} -> Ret
+ end,
+ ?debug("LocalRet: ~s\n", [t_to_string(LocalRet)]),
+ {Fun, Sig, Contract, LocalRet}.
+
+forward_args(Fun, ArgTypes, #state{work = Work, fun_tab = FunTab} = State) ->
+ {OldArgTypes, OldOut, Fixpoint} =
+ case dict:find(Fun, FunTab) of
+ {ok, {not_handled, {OldArgTypes0, OldOut0}}} ->
+ {OldArgTypes0, OldOut0, false};
+ {ok, {OldArgTypes0, OldOut0}} ->
+ {OldArgTypes0, OldOut0,
+ t_is_subtype(t_product(ArgTypes), t_product(OldArgTypes0))}
+ end,
+ case Fixpoint of
+ true -> State;
+ false ->
+ NewArgTypes = [t_sup(X, Y) ||
+ {X, Y} <- lists:zip(ArgTypes, OldArgTypes)],
+ NewWork = add_work(Fun, Work),
+ ?debug("~w: forwarding args ~s\n",
+ [state__lookup_name(Fun, State),
+ t_to_string(t_product(NewArgTypes))]),
+ NewFunTab = dict:store(Fun, {NewArgTypes, OldOut}, FunTab),
+ State#state{work = NewWork, fun_tab = NewFunTab}
+ end.
+
+-spec state__cleanup(state()) -> state().
+
+state__cleanup(#state{callgraph = Callgraph,
+ races = Races,
+ records = Records}) ->
+ #state{callgraph = dialyzer_callgraph:cleanup(Callgraph),
+ races = dialyzer_races:cleanup(Races),
+ records = Records}.
+
+-spec state__duplicate(state()) -> state().
+
+state__duplicate(#state{callgraph = Callgraph} = State) ->
+ State#state{callgraph = dialyzer_callgraph:duplicate(Callgraph)}.
+
+-spec dispose_state(state()) -> ok.
+
+dispose_state(#state{callgraph = Callgraph}) ->
+ dialyzer_callgraph:dispose_race_server(Callgraph).
+
+-spec state__get_callgraph(state()) -> dialyzer_callgraph:callgraph().
+
+state__get_callgraph(#state{callgraph = Callgraph}) ->
+ Callgraph.
+
+-spec state__get_races(state()) -> dialyzer_races:races().
+
+state__get_races(#state{races = Races}) ->
+ Races.
+
+-spec state__get_records(state()) -> types().
+
+state__get_records(#state{records = Records}) ->
+ Records.
+
+-spec state__put_callgraph(dialyzer_callgraph:callgraph(), state()) ->
+ state().
+
+state__put_callgraph(Callgraph, State) ->
+ State#state{callgraph = Callgraph}.
+
+-spec state__put_races(dialyzer_races:races(), state()) -> state().
+
+state__put_races(Races, State) ->
+ State#state{races = Races}.
+
+-spec state__records_only(state()) -> state().
+
+state__records_only(#state{records = Records}) ->
+ #state{records = Records}.
+
+%%% ===========================================================================
+%%%
+%%% Races
+%%%
+%%% ===========================================================================
+
+is_race_analysis_enabled(#state{races = Races, callgraph = Callgraph}) ->
+ RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph),
+ RaceAnalysis = dialyzer_races:get_race_analysis(Races),
+ RaceDetection andalso RaceAnalysis.
+
+get_race_list_and_size(#state{races = Races}) ->
+ dialyzer_races:get_race_list_and_size(Races).
+
+renew_race_code(#state{races = Races, callgraph = Callgraph,
+ warning_mode = WarningMode} = State) ->
+ case WarningMode of
+ true -> State;
+ false ->
+ NewCallgraph = dialyzer_callgraph:renew_race_code(Races, Callgraph),
+ State#state{callgraph = NewCallgraph}
+ end.
+
+renew_race_public_tables([Var], #state{races = Races, callgraph = Callgraph,
+ warning_mode = WarningMode} = State) ->
+ case WarningMode of
+ true -> State;
+ false ->
+ Table = dialyzer_races:get_new_table(Races),
+ case Table of
+ no_t -> State;
+ _Other ->
+ VarLabel = get_label(Var),
+ NewCallgraph =
+ dialyzer_callgraph:renew_race_public_tables(VarLabel, Callgraph),
+ State#state{callgraph = NewCallgraph}
+ end
+ end.
+
+%%% ===========================================================================
+%%%
+%%% Worklist
+%%%
+%%% ===========================================================================
+
+init_work(List) ->
+ {List, [], sets:from_list(List)}.
+
+get_work({[], [], _Set}) ->
+ none;
+get_work({[H|T], Rev, Set}) ->
+ {H, {T, Rev, sets:del_element(H, Set)}};
+get_work({[], Rev, Set}) ->
+ get_work({lists:reverse(Rev), [], Set}).
+
+add_work(New, {List, Rev, Set} = Work) ->
+ case sets:is_element(New, Set) of
+ true -> Work;
+ false -> {List, [New|Rev], sets:add_element(New, Set)}
+ end.
+
+%%% ===========================================================================
+%%%
+%%% Utilities.
+%%%
+%%% ===========================================================================
+
+get_line([Line|_]) when is_integer(Line) -> Line;
+get_line([_|Tail]) -> get_line(Tail);
+get_line([]) -> -1.
+
+get_file([]) -> [];
+get_file([{file, File}|_]) -> File;
+get_file([_|Tail]) -> get_file(Tail).
+
+is_compiler_generated(Ann) ->
+ lists:member(compiler_generated, Ann) orelse (get_line(Ann) < 1).
+
+is_literal_record(Tree) ->
+ Ann = cerl:get_ann(Tree),
+ lists:member(record, Ann).
+
+-spec format_args([cerl:cerl()], [type()], state()) ->
+ nonempty_string().
+
+format_args([], [], _State) ->
+ "()";
+format_args(ArgList0, TypeList, State) ->
+ ArgList = fold_literals(ArgList0),
+ "(" ++ format_args_1(ArgList, TypeList, State) ++ ")".
+
+format_args_1([Arg], [Type], State) ->
+ format_arg(Arg) ++ format_type(Type, State);
+format_args_1([Arg|Args], [Type|Types], State) ->
+ String =
+ case cerl:is_literal(Arg) of
+ true -> format_cerl(Arg);
+ false -> format_arg(Arg) ++ format_type(Type, State)
+ end,
+ String ++ "," ++ format_args_1(Args, Types, State).
+
+format_arg(Arg) ->
+ Default = "",
+ case cerl:is_c_var(Arg) of
+ true ->
+ case cerl:var_name(Arg) of
+ Atom when is_atom(Atom) ->
+ case atom_to_list(Atom) of
+ "cor"++_ -> Default;
+ "rec"++_ -> Default;
+ Name -> Name ++ "::"
+ end;
+ _What -> Default
+ end;
+ false ->
+ Default
+ end.
+
+-spec format_type(type(), state()) -> string().
+
+format_type(Type, #state{records = R}) ->
+ t_to_string(Type, R).
+
+-spec format_field_diffs(type(), state()) -> string().
+
+format_field_diffs(RecConstruction, #state{records = R}) ->
+ erl_types:record_field_diffs_to_string(RecConstruction, R).
+
+-spec format_sig_args(type(), state()) -> string().
+
+format_sig_args(Type, #state{opaques = Opaques} = State) ->
+ SigArgs = t_fun_args(Type, Opaques),
+ case SigArgs of
+ [] -> "()";
+ [SArg|SArgs] ->
+ lists:flatten("(" ++ format_type(SArg, State)
+ ++ ["," ++ format_type(T, State) || T <- SArgs] ++ ")")
+ end.
+
+format_cerl(Tree) ->
+ cerl_prettypr:format(cerl:set_ann(Tree, []),
+ [{hook, dialyzer_utils:pp_hook()},
+ {noann, true},
+ {paper, 100000}, %% These guys strip
+ {ribbon, 100000} %% newlines.
+ ]).
+
+format_patterns(Pats0) ->
+ Pats = fold_literals(Pats0),
+ NewPats = map_pats(cerl:c_values(Pats)),
+ String = format_cerl(NewPats),
+ case Pats of
+ [PosVar] ->
+ case cerl:is_c_var(PosVar) andalso (cerl:var_name(PosVar) =/= '') of
+ true -> "variable "++String;
+ false -> "pattern "++String
+ end;
+ _ ->
+ "pattern "++String
+ end.
+
+map_pats(Pats) ->
+ Fun = fun(Tree) ->
+ case cerl:is_c_var(Tree) of
+ true ->
+ case cerl:var_name(Tree) of
+ Atom when is_atom(Atom) ->
+ case atom_to_list(Atom) of
+ "cor"++_ -> cerl:c_var('');
+ "rec"++_ -> cerl:c_var('');
+ _ -> cerl:set_ann(Tree, [])
+ end;
+ _What -> cerl:c_var('')
+ end;
+ false ->
+ cerl:set_ann(Tree, [])
+ end
+ end,
+ cerl_trees:map(Fun, Pats).
+
+fold_literals(TreeList) ->
+ [cerl:fold_literal(Tree) || Tree <- TreeList].
+
+type(Tree) ->
+ Folded = cerl:fold_literal(Tree),
+ case cerl:type(Folded) of
+ literal -> {literal, Folded};
+ Type -> Type
+ end.
+
+is_literal(Tree) ->
+ Folded = cerl:fold_literal(Tree),
+ case cerl:is_literal(Folded) of
+ true -> {yes, Folded};
+ false -> no
+ end.
+
+parent_allows_this(FunLbl, #state{callgraph = Callgraph, plt = Plt} =State) ->
+ case state__is_escaping(FunLbl, State) of
+ false -> false; % if it isn't escaping it can't be a return value
+ true ->
+ case state__lookup_name(FunLbl, State) of
+ {_M, _F, _A} -> false; % if it has a name it is not a fun
+ _ ->
+ case dialyzer_callgraph:in_neighbours(FunLbl, Callgraph) of
+ [Parent] ->
+ case state__lookup_name(Parent, State) of
+ {_M, _F, _A} = PMFA ->
+ case dialyzer_plt:lookup_contract(Plt, PMFA) of
+ none -> false;
+ {value, C} ->
+ GenRet = dialyzer_contracts:get_contract_return(C),
+ case erl_types:t_is_fun(GenRet) of
+ false -> false; % element of structure? far-fetched...
+ true -> t_is_unit(t_fun_range(GenRet))
+ end
+ end;
+ _ -> false % parent should have a name to have a contract
+ end;
+ _ -> false % called in other funs? far-fetched...
+ end
+ end
+ end.
+
+find_function({_, _, _} = MFA, _State) ->
+ MFA;
+find_function(top, _State) ->
+ top;
+find_function(FunLbl, #state{fun_homes = Homes}) ->
+ dict:fetch(FunLbl, Homes).
+
+classify_returns(Tree) ->
+ case find_terminals(cerl:fun_body(Tree)) of
+ {false, false} -> no_match;
+ {true, false} -> only_explicit;
+ {false, true} -> only_normal;
+ {true, true} -> both
+ end.
+
+find_terminals(Tree) ->
+ case cerl:type(Tree) of
+ apply -> {false, true};
+ binary -> {false, true};
+ bitstr -> {false, true};
+ call ->
+ M0 = cerl:call_module(Tree),
+ F0 = cerl:call_name(Tree),
+ A = length(cerl:call_args(Tree)),
+ case {is_literal(M0), is_literal(F0)} of
+ {{yes, LitM}, {yes, LitF}} ->
+ M = cerl:concrete(LitM),
+ F = cerl:concrete(LitF),
+ case (erl_bif_types:is_known(M, F, A)
+ andalso t_is_none(erl_bif_types:type(M, F, A))) of
+ true -> {true, false};
+ false -> {false, true}
+ end;
+ _ ->
+ %% We cannot make assumptions. Say that both are true.
+ {true, true}
+ end;
+ 'case' -> find_terminals_list(cerl:case_clauses(Tree));
+ 'catch' -> find_terminals(cerl:catch_body(Tree));
+ clause -> find_terminals(cerl:clause_body(Tree));
+ cons -> {false, true};
+ 'fun' -> {false, true};
+ 'let' -> find_terminals(cerl:let_body(Tree));
+ letrec -> find_terminals(cerl:letrec_body(Tree));
+ literal -> {false, true};
+ map -> {false, true};
+ primop -> {false, false}; %% match_fail, etc. are not explicit exits.
+ 'receive' ->
+ Timeout = cerl:receive_timeout(Tree),
+ Clauses = cerl:receive_clauses(Tree),
+ case (cerl:is_literal(Timeout) andalso
+ (cerl:concrete(Timeout) =:= infinity)) of
+ true ->
+ if Clauses =:= [] -> {false, true}; %% A never ending receive.
+ true -> find_terminals_list(Clauses)
+ end;
+ false -> find_terminals_list([cerl:receive_action(Tree)|Clauses])
+ end;
+ seq -> find_terminals(cerl:seq_body(Tree));
+ 'try' ->
+ find_terminals_list([cerl:try_handler(Tree), cerl:try_body(Tree)]);
+ tuple -> {false, true};
+ values -> {false, true};
+ var -> {false, true}
+ end.
+
+find_terminals_list(List) ->
+ find_terminals_list(List, false, false).
+
+find_terminals_list([Tree|Left], Explicit1, Normal1) ->
+ {Explicit2, Normal2} = find_terminals(Tree),
+ case {Explicit1 or Explicit2, Normal1 or Normal2} of
+ {true, true} = Ans -> Ans;
+ {NewExplicit, NewNormal} ->
+ find_terminals_list(Left, NewExplicit, NewNormal)
+ end;
+find_terminals_list([], Explicit, Normal) ->
+ {Explicit, Normal}.
+
+%%----------------------------------------------------------------------------
+
+-ifdef(DEBUG_PP).
+debug_pp(Tree, true) ->
+ io:put_chars(cerl_prettypr:format(Tree, [{hook, cerl_typean:pp_hook()}])),
+ io:nl(),
+ ok;
+debug_pp(Tree, false) ->
+ io:put_chars(cerl_prettypr:format(strip_annotations(Tree))),
+ io:nl(),
+ ok.
+
+strip_annotations(Tree) ->
+ Fun = fun(T) ->
+ case cerl:type(T) of
+ var ->
+ cerl:set_ann(T, [{label, cerl_trees:get_label(T)}]);
+ 'fun' ->
+ cerl:set_ann(T, [{label, cerl_trees:get_label(T)}]);
+ _ ->
+ cerl:set_ann(T, [])
+ end
+ end,
+ cerl_trees:map(Fun, Tree).
+
+-else.
+
+debug_pp(_Tree, _UseHook) ->
+ ok.
+-endif.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer_races.erl b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer_races.erl
new file mode 100644
index 0000000000..637927c932
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/dialyzer_races.erl
@@ -0,0 +1,2494 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-----------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-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%
+%%
+
+%%%----------------------------------------------------------------------
+%%% File : dialyzer_races.erl
+%%% Author : Maria Christakis <[email protected]>
+%%% Description : Utility functions for race condition detection
+%%%
+%%% Created : 21 Nov 2008 by Maria Christakis <[email protected]>
+%%%----------------------------------------------------------------------
+-module(dialyzer_races).
+
+%% Race Analysis
+
+-export([store_race_call/5, race/1, get_race_warnings/2, format_args/4]).
+
+%% Record Interfaces
+
+-export([beg_clause_new/3, cleanup/1, end_case_new/1, end_clause_new/3,
+ get_curr_fun/1, get_curr_fun_args/1, get_new_table/1,
+ get_race_analysis/1, get_race_list/1, get_race_list_size/1,
+ get_race_list_and_size/1,
+ let_tag_new/2, new/0, put_curr_fun/3, put_fun_args/2,
+ put_race_analysis/2, put_race_list/3]).
+
+-export_type([races/0, core_vars/0]).
+
+-include("dialyzer.hrl").
+
+%%% ===========================================================================
+%%%
+%%% Definitions
+%%%
+%%% ===========================================================================
+
+-define(local, 5).
+-define(no_arg, no_arg).
+-define(no_label, no_label).
+-define(bypassed, bypassed).
+
+-define(WARN_WHEREIS_REGISTER, warn_whereis_register).
+-define(WARN_WHEREIS_UNREGISTER, warn_whereis_unregister).
+-define(WARN_ETS_LOOKUP_INSERT, warn_ets_lookup_insert).
+-define(WARN_MNESIA_DIRTY_READ_WRITE, warn_mnesia_dirty_read_write).
+-define(WARN_NO_WARN, warn_no_warn).
+
+%%% ===========================================================================
+%%%
+%%% Local Types
+%%%
+%%% ===========================================================================
+
+-type label_type() :: label() | [label()] | {label()} | ?no_label.
+-type args() :: [label_type() | [string()]].
+-type core_vars() :: cerl:cerl() | ?no_arg | ?bypassed.
+-type var_to_map1() :: core_vars() | [cerl:cerl()].
+-type var_to_map2() :: cerl:cerl() | [cerl:cerl()] | ?bypassed.
+-type core_args() :: [core_vars()] | 'empty'.
+-type op() :: 'bind' | 'unbind'.
+
+-type dep_calls() :: 'whereis' | 'ets_lookup' | 'mnesia_dirty_read'.
+-type warn_calls() :: 'register' | 'unregister' | 'ets_insert'
+ | 'mnesia_dirty_write'.
+-type call() :: 'whereis' | 'register' | 'unregister' | 'ets_new'
+ | 'ets_lookup' | 'ets_insert' | 'mnesia_dirty_read1'
+ | 'mnesia_dirty_read2' | 'mnesia_dirty_write1'
+ | 'mnesia_dirty_write2' | 'function_call'.
+-type race_tag() :: 'whereis_register' | 'whereis_unregister'
+ | 'ets_lookup_insert' | 'mnesia_dirty_read_write'.
+
+%% The following type is similar to the raw_warning() type but has a
+%% tag which is local to this module and is not propagated to outside
+-type dial_race_warning() :: {race_warn_tag(), warning_info(), {atom(), [term()]}}.
+-type race_warn_tag() :: ?WARN_WHEREIS_REGISTER | ?WARN_WHEREIS_UNREGISTER
+ | ?WARN_ETS_LOOKUP_INSERT | ?WARN_MNESIA_DIRTY_READ_WRITE.
+
+-record(beg_clause, {arg :: var_to_map1() | 'undefined',
+ pats :: var_to_map1() | 'undefined',
+ guard :: cerl:cerl() | 'undefined'}).
+-record(end_clause, {arg :: var_to_map1() | 'undefined',
+ pats :: var_to_map1() | 'undefined',
+ guard :: cerl:cerl() | 'undefined'}).
+-record(end_case, {clauses :: [#end_clause{}]}).
+-record(curr_fun, {status :: 'in' | 'out' | 'undefined',
+ mfa :: dialyzer_callgraph:mfa_or_funlbl()
+ | 'undefined',
+ label :: label() | 'undefined',
+ def_vars :: [core_vars()] | 'undefined',
+ arg_types :: [erl_types:erl_type()] | 'undefined',
+ call_vars :: [core_vars()] | 'undefined',
+ var_map :: dict:dict() | 'undefined'}).
+-record(dep_call, {call_name :: dep_calls(),
+ args :: args() | 'undefined',
+ arg_types :: [erl_types:erl_type()],
+ vars :: [core_vars()],
+ state :: dialyzer_dataflow:state(),
+ file_line :: file_line(),
+ var_map :: dict:dict() | 'undefined'}).
+-record(fun_call, {caller :: dialyzer_callgraph:mfa_or_funlbl(),
+ callee :: dialyzer_callgraph:mfa_or_funlbl(),
+ arg_types :: [erl_types:erl_type()],
+ vars :: [core_vars()]}).
+-record(let_tag, {var :: var_to_map1(),
+ arg :: var_to_map1()}).
+-record(warn_call, {call_name :: warn_calls(),
+ args :: args(),
+ var_map :: dict:dict() | 'undefined'}).
+
+-type case_tags() :: 'beg_case' | #beg_clause{} | #end_clause{} | #end_case{}.
+-type code() :: [#dep_call{} | #fun_call{} | #warn_call{} |
+ #curr_fun{} | #let_tag{} | case_tags() | race_tag()].
+
+-type table_var() :: label() | ?no_label.
+-type table() :: {'named', table_var(), [string()]} | 'other' | 'no_t'.
+
+-record(race_fun, {mfa :: mfa(),
+ args :: args(),
+ arg_types :: [erl_types:erl_type()],
+ vars :: [core_vars()],
+ file_line :: file_line(),
+ index :: non_neg_integer(),
+ fun_mfa :: dialyzer_callgraph:mfa_or_funlbl(),
+ fun_label :: label()}).
+
+-record(races, {curr_fun :: dialyzer_callgraph:mfa_or_funlbl()
+ | 'undefined',
+ curr_fun_label :: label() | 'undefined',
+ curr_fun_args = 'empty' :: core_args(),
+ new_table = 'no_t' :: table(),
+ race_list = [] :: code(),
+ race_list_size = 0 :: non_neg_integer(),
+ race_tags = [] :: [#race_fun{}],
+ %% true for fun types and warning mode
+ race_analysis = false :: boolean(),
+ race_warnings = [] :: [dial_race_warning()]}).
+
+%%% ===========================================================================
+%%%
+%%% Exported Types
+%%%
+%%% ===========================================================================
+
+-opaque races() :: #races{}.
+
+%%% ===========================================================================
+%%%
+%%% Race Analysis
+%%%
+%%% ===========================================================================
+
+-spec store_race_call(dialyzer_callgraph:mfa_or_funlbl(),
+ [erl_types:erl_type()], [core_vars()],
+ file_line(), dialyzer_dataflow:state()) ->
+ dialyzer_dataflow:state().
+
+store_race_call(Fun, ArgTypes, Args, FileLine, State) ->
+ Races = dialyzer_dataflow:state__get_races(State),
+ CurrFun = Races#races.curr_fun,
+ CurrFunLabel = Races#races.curr_fun_label,
+ RaceTags = Races#races.race_tags,
+ CleanState = dialyzer_dataflow:state__records_only(State),
+ {NewRaceList, NewRaceListSize, NewRaceTags, NewTable} =
+ case CurrFun of
+ {_Module, module_info, A} when A =:= 0 orelse A =:= 1 ->
+ {[], 0, RaceTags, no_t};
+ _Thing ->
+ RaceList = Races#races.race_list,
+ RaceListSize = Races#races.race_list_size,
+ case Fun of
+ {erlang, get_module_info, A} when A =:= 1 orelse A =:= 2 ->
+ {[], 0, RaceTags, no_t};
+ {erlang, register, 2} ->
+ VarArgs = format_args(Args, ArgTypes, CleanState, register),
+ RaceFun = #race_fun{mfa = Fun, args = VarArgs,
+ arg_types = ArgTypes, vars = Args,
+ file_line = FileLine, index = RaceListSize,
+ fun_mfa = CurrFun, fun_label = CurrFunLabel},
+ {[#warn_call{call_name = register, args = VarArgs}|
+ RaceList], RaceListSize + 1, [RaceFun|RaceTags], no_t};
+ {erlang, unregister, 1} ->
+ VarArgs = format_args(Args, ArgTypes, CleanState, unregister),
+ RaceFun = #race_fun{mfa = Fun, args = VarArgs,
+ arg_types = ArgTypes, vars = Args,
+ file_line = FileLine, index = RaceListSize,
+ fun_mfa = CurrFun, fun_label = CurrFunLabel},
+ {[#warn_call{call_name = unregister, args = VarArgs}|
+ RaceList], RaceListSize + 1, [RaceFun|RaceTags], no_t};
+ {erlang, whereis, 1} ->
+ VarArgs = format_args(Args, ArgTypes, CleanState, whereis),
+ {[#dep_call{call_name = whereis, args = VarArgs,
+ arg_types = ArgTypes, vars = Args,
+ state = CleanState, file_line = FileLine}|
+ RaceList], RaceListSize + 1, RaceTags, no_t};
+ {ets, insert, 2} ->
+ VarArgs = format_args(Args, ArgTypes, CleanState, ets_insert),
+ RaceFun = #race_fun{mfa = Fun, args = VarArgs,
+ arg_types = ArgTypes, vars = Args,
+ file_line = FileLine, index = RaceListSize,
+ fun_mfa = CurrFun, fun_label = CurrFunLabel},
+ {[#warn_call{call_name = ets_insert, args = VarArgs}|
+ RaceList], RaceListSize + 1, [RaceFun|RaceTags], no_t};
+ {ets, lookup, 2} ->
+ VarArgs = format_args(Args, ArgTypes, CleanState, ets_lookup),
+ {[#dep_call{call_name = ets_lookup, args = VarArgs,
+ arg_types = ArgTypes, vars = Args,
+ state = CleanState, file_line = FileLine}|
+ RaceList], RaceListSize + 1, RaceTags, no_t};
+ {ets, new, 2} ->
+ VarArgs = format_args(Args, ArgTypes, CleanState, ets_new),
+ [VarArgs1, VarArgs2, _, Options] = VarArgs,
+ NewTable1 =
+ case lists:member("'public'", Options) of
+ true ->
+ case lists:member("'named_table'", Options) of
+ true ->
+ {named, VarArgs1, VarArgs2};
+ false -> other
+ end;
+ false -> no_t
+ end,
+ {RaceList, RaceListSize, RaceTags, NewTable1};
+ {mnesia, dirty_read, A} when A =:= 1 orelse A =:= 2 ->
+ VarArgs =
+ case A of
+ 1 ->
+ format_args(Args, ArgTypes, CleanState, mnesia_dirty_read1);
+ 2 ->
+ format_args(Args, ArgTypes, CleanState, mnesia_dirty_read2)
+ end,
+ {[#dep_call{call_name = mnesia_dirty_read, args = VarArgs,
+ arg_types = ArgTypes, vars = Args,
+ state = CleanState, file_line = FileLine}|RaceList],
+ RaceListSize + 1, RaceTags, no_t};
+ {mnesia, dirty_write, A} when A =:= 1 orelse A =:= 2 ->
+ VarArgs =
+ case A of
+ 1 ->
+ format_args(Args, ArgTypes, CleanState, mnesia_dirty_write1);
+ 2 ->
+ format_args(Args, ArgTypes, CleanState, mnesia_dirty_write2)
+ end,
+ RaceFun = #race_fun{mfa = Fun, args = VarArgs,
+ arg_types = ArgTypes, vars = Args,
+ file_line = FileLine, index = RaceListSize,
+ fun_mfa = CurrFun, fun_label = CurrFunLabel},
+ {[#warn_call{call_name = mnesia_dirty_write,
+ args = VarArgs}|RaceList],
+ RaceListSize + 1, [RaceFun|RaceTags], no_t};
+ Int when is_integer(Int) ->
+ {[#fun_call{caller = CurrFun, callee = Int, arg_types = ArgTypes,
+ vars = Args}|RaceList],
+ RaceListSize + 1, RaceTags, no_t};
+ _Other ->
+ Callgraph = dialyzer_dataflow:state__get_callgraph(State),
+ case digraph:vertex(dialyzer_callgraph:get_digraph(Callgraph),
+ Fun) of
+ {Fun, confirmed} ->
+ {[#fun_call{caller = CurrFun, callee = Fun,
+ arg_types = ArgTypes, vars = Args}|RaceList],
+ RaceListSize + 1, RaceTags, no_t};
+ false ->
+ {RaceList, RaceListSize, RaceTags, no_t}
+ end
+ end
+ end,
+ state__renew_info(NewRaceList, NewRaceListSize, NewRaceTags, NewTable, State).
+
+-spec race(dialyzer_dataflow:state()) -> dialyzer_dataflow:state().
+
+race(State) ->
+ Races = dialyzer_dataflow:state__get_races(State),
+ RaceTags = Races#races.race_tags,
+ RetState =
+ case RaceTags of
+ [] -> State;
+ [#race_fun{mfa = Fun,
+ args = VarArgs, arg_types = ArgTypes,
+ vars = Args, file_line = FileLine,
+ index = Index, fun_mfa = CurrFun,
+ fun_label = CurrFunLabel}|T] ->
+ Callgraph = dialyzer_dataflow:state__get_callgraph(State),
+ {ok, [_Args, Code]} =
+ dict:find(CurrFun, dialyzer_callgraph:get_race_code(Callgraph)),
+ RaceList = lists:reverse(Code),
+ RaceWarnTag =
+ case Fun of
+ {erlang, register, 2} -> ?WARN_WHEREIS_REGISTER;
+ {erlang, unregister, 1} -> ?WARN_WHEREIS_UNREGISTER;
+ {ets, insert, 2} -> ?WARN_ETS_LOOKUP_INSERT;
+ {mnesia, dirty_write, _A} -> ?WARN_MNESIA_DIRTY_READ_WRITE
+ end,
+ State1 =
+ state__renew_curr_fun(CurrFun,
+ state__renew_curr_fun_label(CurrFunLabel,
+ state__renew_race_list(lists:nthtail(length(RaceList) - Index,
+ RaceList), State))),
+ DepList = fixup_race_list(RaceWarnTag, VarArgs, State1),
+ {State2, RaceWarn} =
+ get_race_warn(Fun, Args, ArgTypes, DepList, State),
+ {File, Line} = FileLine,
+ CurrMFA = dialyzer_dataflow:state__find_function(CurrFun, State),
+ WarningInfo = {File, Line, CurrMFA},
+ race(
+ state__add_race_warning(
+ state__renew_race_tags(T, State2), RaceWarn, RaceWarnTag,
+ WarningInfo))
+ end,
+ state__renew_race_tags([], RetState).
+
+fixup_race_list(RaceWarnTag, WarnVarArgs, State) ->
+ Races = dialyzer_dataflow:state__get_races(State),
+ CurrFun = Races#races.curr_fun,
+ CurrFunLabel = Races#races.curr_fun_label,
+ RaceList = Races#races.race_list,
+ Callgraph = dialyzer_dataflow:state__get_callgraph(State),
+ Digraph = dialyzer_callgraph:get_digraph(Callgraph),
+ Calls = digraph:edges(Digraph),
+ RaceTag =
+ case RaceWarnTag of
+ ?WARN_WHEREIS_REGISTER -> whereis_register;
+ ?WARN_WHEREIS_UNREGISTER -> whereis_unregister;
+ ?WARN_ETS_LOOKUP_INSERT -> ets_lookup_insert;
+ ?WARN_MNESIA_DIRTY_READ_WRITE -> mnesia_dirty_read_write
+ end,
+ NewRaceList = [RaceTag|RaceList],
+ CleanState = dialyzer_dataflow:state__cleanup(State),
+ NewState = state__renew_race_list(NewRaceList, CleanState),
+ DepList1 =
+ fixup_race_forward_pullout(CurrFun, CurrFunLabel, Calls,
+ lists:reverse(NewRaceList), [], CurrFun,
+ WarnVarArgs, RaceWarnTag, dict:new(),
+ [], [], [], 2 * ?local, NewState),
+ Parents = fixup_race_backward(CurrFun, Calls, Calls, [], ?local),
+ UParents = lists:usort(Parents),
+ Filtered = filter_parents(UParents, UParents, Digraph),
+ NewParents =
+ case lists:member(CurrFun, Filtered) of
+ true -> Filtered;
+ false -> [CurrFun|Filtered]
+ end,
+ DepList2 =
+ fixup_race_list_helper(NewParents, Calls, CurrFun, WarnVarArgs,
+ RaceWarnTag, NewState),
+ dialyzer_dataflow:dispose_state(CleanState),
+ lists:usort(cleanup_dep_calls(DepList1 ++ DepList2)).
+
+fixup_race_list_helper(Parents, Calls, CurrFun, WarnVarArgs, RaceWarnTag,
+ State) ->
+ case Parents of
+ [] -> [];
+ [Head|Tail] ->
+ Callgraph = dialyzer_dataflow:state__get_callgraph(State),
+ Code =
+ case dict:find(Head, dialyzer_callgraph:get_race_code(Callgraph)) of
+ error -> [];
+ {ok, [_A, C]} -> C
+ end,
+ {ok, FunLabel} = dialyzer_callgraph:lookup_label(Head, Callgraph),
+ DepList1 =
+ fixup_race_forward_pullout(Head, FunLabel, Calls, Code, [], CurrFun,
+ WarnVarArgs, RaceWarnTag, dict:new(),
+ [], [], [], 2 * ?local, State),
+ DepList2 =
+ fixup_race_list_helper(Tail, Calls, CurrFun, WarnVarArgs,
+ RaceWarnTag, State),
+ DepList1 ++ DepList2
+ end.
+
+%%% ===========================================================================
+%%%
+%%% Forward Analysis
+%%%
+%%% ===========================================================================
+
+fixup_race_forward_pullout(CurrFun, CurrFunLabel, Calls, Code, RaceList,
+ InitFun, WarnVarArgs, RaceWarnTag, RaceVarMap,
+ FunDefVars, FunCallVars, FunArgTypes, NestingLevel,
+ State) ->
+ TState = dialyzer_dataflow:state__duplicate(State),
+ {DepList, NewCurrFun, NewCurrFunLabel, NewCalls,
+ NewCode, NewRaceList, NewRaceVarMap, NewFunDefVars,
+ NewFunCallVars, NewFunArgTypes, NewNestingLevel} =
+ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
+ InitFun, WarnVarArgs, RaceWarnTag, RaceVarMap,
+ FunDefVars, FunCallVars, FunArgTypes, NestingLevel,
+ cleanup_race_code(TState)),
+ dialyzer_dataflow:dispose_state(TState),
+ case NewCode of
+ [] -> DepList;
+ [#fun_call{caller = NewCurrFun, callee = Call, arg_types = FunTypes,
+ vars = FunArgs}|Tail] ->
+ Callgraph = dialyzer_dataflow:state__get_callgraph(State),
+ OkCall = {ok, Call},
+ {Name, Label} =
+ case is_integer(Call) of
+ true ->
+ case dialyzer_callgraph:lookup_name(Call, Callgraph) of
+ error -> {OkCall, OkCall};
+ N -> {N, OkCall}
+ end;
+ false ->
+ {OkCall, dialyzer_callgraph:lookup_label(Call, Callgraph)}
+ end,
+ {NewCurrFun1, NewCurrFunLabel1, NewCalls1, NewCode1, NewRaceList1,
+ NewRaceVarMap1, NewFunDefVars1, NewFunCallVars1, NewFunArgTypes1,
+ NewNestingLevel1} =
+ case Label =:= error of
+ true ->
+ {NewCurrFun, NewCurrFunLabel, NewCalls, Tail, NewRaceList,
+ NewRaceVarMap, NewFunDefVars, NewFunCallVars, NewFunArgTypes,
+ NewNestingLevel};
+ false ->
+ {ok, Fun} = Name,
+ {ok, Int} = Label,
+ case dict:find(Fun, dialyzer_callgraph:get_race_code(Callgraph)) of
+ error ->
+ {NewCurrFun, NewCurrFunLabel, NewCalls, Tail, NewRaceList,
+ NewRaceVarMap, NewFunDefVars, NewFunCallVars, NewFunArgTypes,
+ NewNestingLevel};
+ {ok, [Args, CodeB]} ->
+ Races = dialyzer_dataflow:state__get_races(State),
+ {RetCurrFun, RetCurrFunLabel, RetCalls, RetCode,
+ RetRaceList, RetRaceVarMap, RetFunDefVars, RetFunCallVars,
+ RetFunArgTypes, RetNestingLevel} =
+ fixup_race_forward_helper(NewCurrFun,
+ NewCurrFunLabel, Fun, Int, NewCalls, NewCalls,
+ [#curr_fun{status = out, mfa = NewCurrFun,
+ label = NewCurrFunLabel,
+ var_map = NewRaceVarMap,
+ def_vars = NewFunDefVars,
+ call_vars = NewFunCallVars,
+ arg_types = NewFunArgTypes}|
+ Tail],
+ NewRaceList, InitFun, FunArgs, FunTypes, RaceWarnTag,
+ NewRaceVarMap, NewFunDefVars, NewFunCallVars,
+ NewFunArgTypes, NewNestingLevel, Args, CodeB,
+ Races#races.race_list),
+ case RetCode of
+ [#curr_fun{}|_CodeTail] ->
+ {NewCurrFun, NewCurrFunLabel, RetCalls, RetCode,
+ RetRaceList, NewRaceVarMap, NewFunDefVars,
+ NewFunCallVars, NewFunArgTypes, RetNestingLevel};
+ _Else ->
+ {RetCurrFun, RetCurrFunLabel, RetCalls, RetCode,
+ RetRaceList, RetRaceVarMap, RetFunDefVars,
+ RetFunCallVars, RetFunArgTypes, RetNestingLevel}
+ end
+ end
+ end,
+ DepList ++
+ fixup_race_forward_pullout(NewCurrFun1, NewCurrFunLabel1, NewCalls1,
+ NewCode1, NewRaceList1, InitFun, WarnVarArgs,
+ RaceWarnTag, NewRaceVarMap1, NewFunDefVars1,
+ NewFunCallVars1, NewFunArgTypes1,
+ NewNestingLevel1, State)
+ end.
+
+fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
+ InitFun, WarnVarArgs, RaceWarnTag, RaceVarMap,
+ FunDefVars, FunCallVars, FunArgTypes, NestingLevel,
+ State) ->
+ case Code of
+ [] ->
+ {[], CurrFun, CurrFunLabel, Calls, Code, RaceList, RaceVarMap,
+ FunDefVars, FunCallVars, FunArgTypes, NestingLevel};
+ [Head|Tail] ->
+ Callgraph = dialyzer_dataflow:state__get_callgraph(State),
+ {NewRL, DepList, NewNL, Return} =
+ case Head of
+ #dep_call{call_name = whereis} ->
+ case RaceWarnTag of
+ WarnWhereis when WarnWhereis =:= ?WARN_WHEREIS_REGISTER orelse
+ WarnWhereis =:= ?WARN_WHEREIS_UNREGISTER ->
+ {[Head#dep_call{var_map = RaceVarMap}|RaceList],
+ [], NestingLevel, false};
+ _Other ->
+ {RaceList, [], NestingLevel, false}
+ end;
+ #dep_call{call_name = ets_lookup} ->
+ case RaceWarnTag of
+ ?WARN_ETS_LOOKUP_INSERT ->
+ {[Head#dep_call{var_map = RaceVarMap}|RaceList],
+ [], NestingLevel, false};
+ _Other ->
+ {RaceList, [], NestingLevel, false}
+ end;
+ #dep_call{call_name = mnesia_dirty_read} ->
+ case RaceWarnTag of
+ ?WARN_MNESIA_DIRTY_READ_WRITE ->
+ {[Head#dep_call{var_map = RaceVarMap}|RaceList],
+ [], NestingLevel, false};
+ _Other ->
+ {RaceList, [], NestingLevel, false}
+ end;
+ #warn_call{call_name = RegCall} when RegCall =:= register orelse
+ RegCall =:= unregister ->
+ case RaceWarnTag of
+ WarnWhereis when WarnWhereis =:= ?WARN_WHEREIS_REGISTER orelse
+ WarnWhereis =:= ?WARN_WHEREIS_UNREGISTER ->
+ {[Head#warn_call{var_map = RaceVarMap}|RaceList],
+ [], NestingLevel, false};
+ _Other ->
+ {RaceList, [], NestingLevel, false}
+ end;
+ #warn_call{call_name = ets_insert} ->
+ case RaceWarnTag of
+ ?WARN_ETS_LOOKUP_INSERT ->
+ {[Head#warn_call{var_map = RaceVarMap}|RaceList],
+ [], NestingLevel, false};
+ _Other ->
+ {RaceList, [], NestingLevel, false}
+ end;
+ #warn_call{call_name = mnesia_dirty_write} ->
+ case RaceWarnTag of
+ ?WARN_MNESIA_DIRTY_READ_WRITE ->
+ {[Head#warn_call{var_map = RaceVarMap}|RaceList],
+ [], NestingLevel, false};
+ _Other ->
+ {RaceList, [], NestingLevel, false}
+ end;
+ #fun_call{caller = CurrFun, callee = InitFun} ->
+ {RaceList, [], NestingLevel, false};
+ #fun_call{caller = CurrFun} ->
+ {RaceList, [], NestingLevel - 1, false};
+ beg_case ->
+ {[Head|RaceList], [], NestingLevel, false};
+ #beg_clause{} ->
+ {[#beg_clause{}|RaceList], [], NestingLevel, false};
+ #end_clause{} ->
+ {[#end_clause{}|RaceList], [], NestingLevel, false};
+ #end_case{} ->
+ {[Head|RaceList], [], NestingLevel, false};
+ #let_tag{} ->
+ {RaceList, [], NestingLevel, false};
+ #curr_fun{status = in, mfa = InitFun,
+ label = _InitFunLabel, var_map = _NewRVM,
+ def_vars = NewFDV, call_vars = NewFCV,
+ arg_types = _NewFAT} ->
+ {[#curr_fun{status = out, var_map = RaceVarMap,
+ def_vars = NewFDV, call_vars = NewFCV}|
+ RaceList], [], NestingLevel - 1, false};
+ #curr_fun{status = in, def_vars = NewFDV,
+ call_vars = NewFCV} ->
+ {[#curr_fun{status = out, var_map = RaceVarMap,
+ def_vars = NewFDV, call_vars = NewFCV}|
+ RaceList],
+ [], NestingLevel - 1, false};
+ #curr_fun{status = out} ->
+ {[#curr_fun{status = in, var_map = RaceVarMap}|RaceList], [],
+ NestingLevel + 1, false};
+ RaceTag ->
+ PublicTables = dialyzer_callgraph:get_public_tables(Callgraph),
+ NamedTables = dialyzer_callgraph:get_named_tables(Callgraph),
+ WarnVarArgs1 =
+ var_type_analysis(FunDefVars, FunArgTypes, WarnVarArgs,
+ RaceWarnTag, RaceVarMap,
+ dialyzer_dataflow:state__records_only(State)),
+ {NewDepList, IsPublic, _Return} =
+ get_deplist_paths(RaceList, WarnVarArgs1, RaceWarnTag,
+ RaceVarMap, 0, PublicTables, NamedTables),
+ {NewHead, NewDepList1} =
+ case RaceTag of
+ whereis_register ->
+ {[#warn_call{call_name = register, args = WarnVarArgs,
+ var_map = RaceVarMap}],
+ NewDepList};
+ whereis_unregister ->
+ {[#warn_call{call_name = unregister, args = WarnVarArgs,
+ var_map = RaceVarMap}],
+ NewDepList};
+ ets_lookup_insert ->
+ NewWarnCall =
+ [#warn_call{call_name = ets_insert, args = WarnVarArgs,
+ var_map = RaceVarMap}],
+ [Tab, Names, _, _] = WarnVarArgs,
+ case IsPublic orelse
+ compare_var_list(Tab, PublicTables, RaceVarMap)
+ orelse
+ length(Names -- NamedTables) < length(Names) of
+ true ->
+ {NewWarnCall, NewDepList};
+ false -> {NewWarnCall, []}
+ end;
+ mnesia_dirty_read_write ->
+ {[#warn_call{call_name = mnesia_dirty_write,
+ args = WarnVarArgs, var_map = RaceVarMap}],
+ NewDepList}
+ end,
+ {NewHead ++ RaceList, NewDepList1, NestingLevel,
+ is_last_race(RaceTag, InitFun, Tail, Callgraph)}
+ end,
+ {NewCurrFun, NewCurrFunLabel, NewCode, NewRaceList, NewRaceVarMap,
+ NewFunDefVars, NewFunCallVars, NewFunArgTypes, NewNestingLevel,
+ PullOut} =
+ case Head of
+ #fun_call{caller = CurrFun} ->
+ case NewNL =:= 0 of
+ true ->
+ {CurrFun, CurrFunLabel, Tail, NewRL, RaceVarMap,
+ FunDefVars, FunCallVars, FunArgTypes, NewNL, false};
+ false ->
+ {CurrFun, CurrFunLabel, Code, NewRL, RaceVarMap,
+ FunDefVars, FunCallVars, FunArgTypes, NewNL, true}
+ end;
+ #beg_clause{arg = Arg, pats = Pats, guard = Guard} ->
+ {RaceVarMap1, RemoveClause} =
+ race_var_map_guard(Arg, Pats, Guard, RaceVarMap, bind),
+ case RemoveClause of
+ true ->
+ {RaceList2,
+ #curr_fun{mfa = CurrFun2, label = CurrFunLabel2,
+ var_map = RaceVarMap2, def_vars = FunDefVars2,
+ call_vars = FunCallVars2, arg_types = FunArgTypes2},
+ Code2, NestingLevel2} =
+ remove_clause(NewRL,
+ #curr_fun{mfa = CurrFun, label = CurrFunLabel,
+ var_map = RaceVarMap1,
+ def_vars = FunDefVars,
+ call_vars = FunCallVars,
+ arg_types = FunArgTypes},
+ Tail, NewNL),
+ {CurrFun2, CurrFunLabel2, Code2, RaceList2,
+ RaceVarMap2, FunDefVars2, FunCallVars2, FunArgTypes2,
+ NestingLevel2, false};
+ false ->
+ {CurrFun, CurrFunLabel, Tail, NewRL, RaceVarMap1,
+ FunDefVars, FunCallVars, FunArgTypes, NewNL, false}
+ end;
+ #end_clause{arg = Arg, pats = Pats, guard = Guard} ->
+ {RaceVarMap1, _RemoveClause} =
+ race_var_map_guard(Arg, Pats, Guard, RaceVarMap, unbind),
+ {CurrFun, CurrFunLabel, Tail, NewRL, RaceVarMap1,
+ FunDefVars, FunCallVars, FunArgTypes, NewNL,
+ false};
+ #end_case{clauses = Clauses} ->
+ RaceVarMap1 =
+ race_var_map_clauses(Clauses, RaceVarMap),
+ {CurrFun, CurrFunLabel, Tail, NewRL, RaceVarMap1,
+ FunDefVars, FunCallVars, FunArgTypes, NewNL,
+ false};
+ #let_tag{var = Var, arg = Arg} ->
+ {CurrFun, CurrFunLabel, Tail, NewRL,
+ race_var_map(Var, Arg, RaceVarMap, bind), FunDefVars,
+ FunCallVars, FunArgTypes, NewNL, false};
+ #curr_fun{mfa = CurrFun1, label = CurrFunLabel1,
+ var_map = RaceVarMap1, def_vars = FunDefVars1,
+ call_vars = FunCallVars1, arg_types = FunArgTypes1} ->
+ case NewNL =:= 0 of
+ true ->
+ {CurrFun, CurrFunLabel,
+ remove_nonlocal_functions(Tail, 1), NewRL, RaceVarMap,
+ FunDefVars, FunCallVars, FunArgTypes, NewNL, false};
+ false ->
+ {CurrFun1, CurrFunLabel1, Tail, NewRL, RaceVarMap1,
+ FunDefVars1, FunCallVars1, FunArgTypes1, NewNL, false}
+ end;
+ _Thing ->
+ {CurrFun, CurrFunLabel, Tail, NewRL, RaceVarMap,
+ FunDefVars, FunCallVars, FunArgTypes, NewNL, false}
+ end,
+ case Return of
+ true ->
+ {DepList, NewCurrFun, NewCurrFunLabel, Calls,
+ [], NewRaceList, NewRaceVarMap, NewFunDefVars,
+ NewFunCallVars, NewFunArgTypes, NewNestingLevel};
+ false ->
+ NewNestingLevel1 =
+ case NewNestingLevel =:= 0 of
+ true -> NewNestingLevel + 1;
+ false -> NewNestingLevel
+ end,
+ case PullOut of
+ true ->
+ {DepList, NewCurrFun, NewCurrFunLabel, Calls,
+ NewCode, NewRaceList, NewRaceVarMap, NewFunDefVars,
+ NewFunCallVars, NewFunArgTypes, NewNestingLevel1};
+ false ->
+ {RetDepList, NewCurrFun1, NewCurrFunLabel1, NewCalls1,
+ NewCode1, NewRaceList1, NewRaceVarMap1, NewFunDefVars1,
+ NewFunCallVars1, NewFunArgTypes1, NewNestingLevel2} =
+ fixup_race_forward(NewCurrFun, NewCurrFunLabel, Calls,
+ NewCode, NewRaceList, InitFun, WarnVarArgs,
+ RaceWarnTag, NewRaceVarMap, NewFunDefVars,
+ NewFunCallVars, NewFunArgTypes,
+ NewNestingLevel1, State),
+ {DepList ++ RetDepList, NewCurrFun1, NewCurrFunLabel1,
+ NewCalls1, NewCode1, NewRaceList1, NewRaceVarMap1,
+ NewFunDefVars1, NewFunCallVars1, NewFunArgTypes1,
+ NewNestingLevel2}
+ end
+ end
+ end.
+
+get_deplist_paths(RaceList, WarnVarArgs, RaceWarnTag, RaceVarMap, CurrLevel,
+ PublicTables, NamedTables) ->
+ case RaceList of
+ [] -> {[], false, true};
+ [Head|Tail] ->
+ case Head of
+ #end_case{} ->
+ {RaceList1, DepList1, IsPublic1, Continue1} =
+ handle_case(Tail, WarnVarArgs, RaceWarnTag, RaceVarMap, CurrLevel,
+ PublicTables, NamedTables),
+ case Continue1 of
+ true ->
+ {DepList2, IsPublic2, Continue2} =
+ get_deplist_paths(RaceList1, WarnVarArgs, RaceWarnTag,
+ RaceVarMap, CurrLevel, PublicTables,
+ NamedTables),
+ {DepList1 ++ DepList2, IsPublic1 orelse IsPublic2, Continue2};
+ false -> {DepList1, IsPublic1, false}
+ end;
+ #beg_clause{} ->
+ get_deplist_paths(fixup_before_case_path(Tail), WarnVarArgs,
+ RaceWarnTag, RaceVarMap, CurrLevel, PublicTables,
+ NamedTables);
+ #curr_fun{status = in, var_map = RaceVarMap1} ->
+ {DepList, IsPublic, Continue} =
+ get_deplist_paths(Tail, WarnVarArgs, RaceWarnTag, RaceVarMap,
+ CurrLevel + 1, PublicTables, NamedTables),
+ IsPublic1 =
+ case RaceWarnTag of
+ ?WARN_ETS_LOOKUP_INSERT ->
+ [Tabs, Names, _, _] = WarnVarArgs,
+ IsPublic orelse
+ lists:any(
+ fun (T) ->
+ compare_var_list(T, PublicTables, RaceVarMap1)
+ end, Tabs)
+ orelse
+ length(Names -- NamedTables) < length(Names);
+ _ -> true
+ end,
+ {DepList, IsPublic1, Continue};
+ #curr_fun{status = out, var_map = RaceVarMap1, def_vars = FunDefVars,
+ call_vars = FunCallVars} ->
+ WarnVarArgs1 =
+ var_analysis([format_arg(DefVar) || DefVar <- FunDefVars],
+ [format_arg(CallVar) || CallVar <- FunCallVars],
+ WarnVarArgs, RaceWarnTag),
+ {WarnVarArgs2, Stop} =
+ case RaceWarnTag of
+ ?WARN_WHEREIS_REGISTER ->
+ [WVA1, WVA2, WVA3, WVA4] = WarnVarArgs1,
+ Vars =
+ lists:flatten(
+ [find_all_bound_vars(V, RaceVarMap1) || V <- WVA1]),
+ case {Vars, CurrLevel} of
+ {[], 0} ->
+ {WarnVarArgs, true};
+ {[], _} ->
+ {WarnVarArgs, false};
+ _ ->
+ {[Vars, WVA2, WVA3, WVA4], false}
+ end;
+ ?WARN_WHEREIS_UNREGISTER ->
+ [WVA1, WVA2] = WarnVarArgs1,
+ Vars =
+ lists:flatten(
+ [find_all_bound_vars(V, RaceVarMap1) || V <- WVA1]),
+ case {Vars, CurrLevel} of
+ {[], 0} ->
+ {WarnVarArgs, true};
+ {[], _} ->
+ {WarnVarArgs, false};
+ _ ->
+ {[Vars, WVA2], false}
+ end;
+ ?WARN_ETS_LOOKUP_INSERT ->
+ [WVA1, WVA2, WVA3, WVA4] = WarnVarArgs1,
+ Vars1 =
+ lists:flatten(
+ [find_all_bound_vars(V1, RaceVarMap1) || V1 <- WVA1]),
+ Vars2 =
+ lists:flatten(
+ [find_all_bound_vars(V2, RaceVarMap1) || V2 <- WVA3]),
+ case {Vars1, Vars2, CurrLevel} of
+ {[], _, 0} ->
+ {WarnVarArgs, true};
+ {[], _, _} ->
+ {WarnVarArgs, false};
+ {_, [], 0} ->
+ {WarnVarArgs, true};
+ {_, [], _} ->
+ {WarnVarArgs, false};
+ _ ->
+ {[Vars1, WVA2, Vars2, WVA4], false}
+ end;
+ ?WARN_MNESIA_DIRTY_READ_WRITE ->
+ [WVA1, WVA2|T] = WarnVarArgs1,
+ Vars =
+ lists:flatten(
+ [find_all_bound_vars(V, RaceVarMap1) || V <- WVA1]),
+ case {Vars, CurrLevel} of
+ {[], 0} ->
+ {WarnVarArgs, true};
+ {[], _} ->
+ {WarnVarArgs, false};
+ _ ->
+ {[Vars, WVA2|T], false}
+ end
+ end,
+ case Stop of
+ true -> {[], false, false};
+ false ->
+ CurrLevel1 =
+ case CurrLevel of
+ 0 -> CurrLevel;
+ _ -> CurrLevel - 1
+ end,
+ get_deplist_paths(Tail, WarnVarArgs2, RaceWarnTag, RaceVarMap1,
+ CurrLevel1, PublicTables, NamedTables)
+ end;
+ #warn_call{call_name = RegCall, args = WarnVarArgs1,
+ var_map = RaceVarMap1} when RegCall =:= register orelse
+ RegCall =:= unregister ->
+ case compare_first_arg(WarnVarArgs, WarnVarArgs1, RaceVarMap1) of
+ true -> {[], false, false};
+ NewWarnVarArgs ->
+ get_deplist_paths(Tail, NewWarnVarArgs, RaceWarnTag, RaceVarMap,
+ CurrLevel, PublicTables, NamedTables)
+ end;
+ #warn_call{call_name = ets_insert, args = WarnVarArgs1,
+ var_map = RaceVarMap1} ->
+ case compare_ets_insert(WarnVarArgs, WarnVarArgs1, RaceVarMap1) of
+ true -> {[], false, false};
+ NewWarnVarArgs ->
+ get_deplist_paths(Tail, NewWarnVarArgs, RaceWarnTag, RaceVarMap,
+ CurrLevel, PublicTables, NamedTables)
+ end;
+ #warn_call{call_name = mnesia_dirty_write, args = WarnVarArgs1,
+ var_map = RaceVarMap1} ->
+ case compare_first_arg(WarnVarArgs, WarnVarArgs1, RaceVarMap1) of
+ true -> {[], false, false};
+ NewWarnVarArgs ->
+ get_deplist_paths(Tail, NewWarnVarArgs, RaceWarnTag, RaceVarMap,
+ CurrLevel, PublicTables, NamedTables)
+ end;
+ #dep_call{var_map = RaceVarMap1} ->
+ {DepList, IsPublic, Continue} =
+ get_deplist_paths(Tail, WarnVarArgs, RaceWarnTag, RaceVarMap,
+ CurrLevel, PublicTables, NamedTables),
+ {refine_race(Head, WarnVarArgs, RaceWarnTag, DepList, RaceVarMap1),
+ IsPublic, Continue}
+ end
+ end.
+
+handle_case(RaceList, WarnVarArgs, RaceWarnTag, RaceVarMap, CurrLevel,
+ PublicTables, NamedTables) ->
+ case RaceList of
+ [] -> {[], [], false, true};
+ [Head|Tail] ->
+ case Head of
+ #end_clause{} ->
+ {RestRaceList, DepList1, IsPublic1, Continue1} =
+ do_clause(Tail, WarnVarArgs, RaceWarnTag, RaceVarMap, CurrLevel,
+ PublicTables, NamedTables),
+ {RetRaceList, DepList2, IsPublic2, Continue2} =
+ handle_case(RestRaceList, WarnVarArgs, RaceWarnTag, RaceVarMap,
+ CurrLevel, PublicTables, NamedTables),
+ {RetRaceList, DepList1 ++ DepList2, IsPublic1 orelse IsPublic2,
+ Continue1 orelse Continue2};
+ beg_case -> {Tail, [], false, false}
+ end
+ end.
+
+do_clause(RaceList, WarnVarArgs, RaceWarnTag, RaceVarMap, CurrLevel,
+ PublicTables, NamedTables) ->
+ {DepList, IsPublic, Continue} =
+ get_deplist_paths(fixup_case_path(RaceList, 0), WarnVarArgs,
+ RaceWarnTag, RaceVarMap, CurrLevel,
+ PublicTables, NamedTables),
+ {fixup_case_rest_paths(RaceList, 0), DepList, IsPublic, Continue}.
+
+fixup_case_path(RaceList, NestingLevel) ->
+ case RaceList of
+ [] -> [];
+ [Head|Tail] ->
+ {NewNestingLevel, Return} =
+ case Head of
+ beg_case -> {NestingLevel - 1, false};
+ #end_case{} -> {NestingLevel + 1, false};
+ #beg_clause{} ->
+ case NestingLevel =:= 0 of
+ true -> {NestingLevel, true};
+ false -> {NestingLevel, false}
+ end;
+ _Other -> {NestingLevel, false}
+ end,
+ case Return of
+ true -> [];
+ false -> [Head|fixup_case_path(Tail, NewNestingLevel)]
+ end
+ end.
+
+%% Gets the race list before a case clause.
+fixup_before_case_path(RaceList) ->
+ case RaceList of
+ [] -> [];
+ [Head|Tail] ->
+ case Head of
+ #end_clause{} ->
+ fixup_before_case_path(fixup_case_rest_paths(Tail, 0));
+ beg_case -> Tail
+ end
+ end.
+
+fixup_case_rest_paths(RaceList, NestingLevel) ->
+ case RaceList of
+ [] -> [];
+ [Head|Tail] ->
+ {NewNestingLevel, Return} =
+ case Head of
+ beg_case -> {NestingLevel - 1, false};
+ #end_case{} -> {NestingLevel + 1, false};
+ #beg_clause{} ->
+ case NestingLevel =:= 0 of
+ true -> {NestingLevel, true};
+ false -> {NestingLevel, false}
+ end;
+ _Other -> {NestingLevel, false}
+ end,
+ case Return of
+ true -> Tail;
+ false -> fixup_case_rest_paths(Tail, NewNestingLevel)
+ end
+ end.
+
+fixup_race_forward_helper(CurrFun, CurrFunLabel, Fun, FunLabel,
+ Calls, CallsToAnalyze, Code, RaceList,
+ InitFun, NewFunArgs, NewFunTypes,
+ RaceWarnTag, RaceVarMap, FunDefVars,
+ FunCallVars, FunArgTypes, NestingLevel,
+ Args, CodeB, StateRaceList) ->
+ case Calls of
+ [] ->
+ {NewRaceList,
+ #curr_fun{mfa = NewCurrFun, label = NewCurrFunLabel,
+ var_map = NewRaceVarMap, def_vars = NewFunDefVars,
+ call_vars = NewFunCallVars, arg_types = NewFunArgTypes},
+ NewCode, NewNestingLevel} =
+ remove_clause(RaceList,
+ #curr_fun{mfa = CurrFun, label = CurrFunLabel, var_map = RaceVarMap,
+ def_vars = FunDefVars, call_vars = FunCallVars,
+ arg_types = FunArgTypes},
+ Code, NestingLevel),
+ {NewCurrFun, NewCurrFunLabel, CallsToAnalyze, NewCode, NewRaceList,
+ NewRaceVarMap, NewFunDefVars, NewFunCallVars, NewFunArgTypes,
+ NewNestingLevel};
+ [Head|Tail] ->
+ case Head of
+ {InitFun, InitFun} when CurrFun =:= InitFun, Fun =:= InitFun ->
+ NewCallsToAnalyze = lists:delete(Head, CallsToAnalyze),
+ NewRaceVarMap =
+ race_var_map(Args, NewFunArgs, RaceVarMap, bind),
+ RetC =
+ fixup_all_calls(InitFun, InitFun, FunLabel, Args,
+ CodeB ++
+ [#curr_fun{status = out, mfa = InitFun,
+ label = CurrFunLabel, var_map = RaceVarMap,
+ def_vars = FunDefVars, call_vars = FunCallVars,
+ arg_types = FunArgTypes}],
+ Code, RaceVarMap),
+ NewCode =
+ fixup_all_calls(InitFun, InitFun, FunLabel, Args,
+ CodeB ++
+ [#curr_fun{status = out, mfa = InitFun,
+ label = CurrFunLabel, var_map = NewRaceVarMap,
+ def_vars = Args, call_vars = NewFunArgs,
+ arg_types = NewFunTypes}],
+ [#curr_fun{status = in, mfa = Fun,
+ label = FunLabel, var_map = NewRaceVarMap,
+ def_vars = Args, call_vars = NewFunArgs,
+ arg_types = NewFunTypes}|
+ lists:reverse(StateRaceList)] ++
+ RetC, NewRaceVarMap),
+ {InitFun, FunLabel, NewCallsToAnalyze, NewCode, RaceList,
+ NewRaceVarMap, Args, NewFunArgs, NewFunTypes, NestingLevel};
+ {CurrFun, Fun} ->
+ NewCallsToAnalyze = lists:delete(Head, CallsToAnalyze),
+ NewRaceVarMap = race_var_map(Args, NewFunArgs, RaceVarMap, bind),
+ RetC =
+ case Fun of
+ InitFun ->
+ fixup_all_calls(CurrFun, Fun, FunLabel, Args,
+ lists:reverse(StateRaceList) ++
+ [#curr_fun{status = out, mfa = CurrFun,
+ label = CurrFunLabel, var_map = RaceVarMap,
+ def_vars = FunDefVars, call_vars = FunCallVars,
+ arg_types = FunArgTypes}],
+ Code, RaceVarMap);
+ _Other1 ->
+ fixup_all_calls(CurrFun, Fun, FunLabel, Args,
+ CodeB ++
+ [#curr_fun{status = out, mfa = CurrFun,
+ label = CurrFunLabel, var_map = RaceVarMap,
+ def_vars = FunDefVars, call_vars = FunCallVars,
+ arg_types = FunArgTypes}],
+ Code, RaceVarMap)
+ end,
+ NewCode =
+ case Fun of
+ InitFun ->
+ [#curr_fun{status = in, mfa = Fun,
+ label = FunLabel, var_map = NewRaceVarMap,
+ def_vars = Args, call_vars = NewFunArgs,
+ arg_types = NewFunTypes}|
+ lists:reverse(StateRaceList)] ++ RetC;
+ _ ->
+ [#curr_fun{status = in, mfa = Fun,
+ label = FunLabel, var_map = NewRaceVarMap,
+ def_vars = Args, call_vars = NewFunArgs,
+ arg_types = NewFunTypes}|CodeB] ++
+ RetC
+ end,
+ {Fun, FunLabel, NewCallsToAnalyze, NewCode, RaceList, NewRaceVarMap,
+ Args, NewFunArgs, NewFunTypes, NestingLevel};
+ {_TupleA, _TupleB} ->
+ fixup_race_forward_helper(CurrFun, CurrFunLabel, Fun, FunLabel,
+ Tail, CallsToAnalyze, Code, RaceList, InitFun, NewFunArgs,
+ NewFunTypes, RaceWarnTag, RaceVarMap, FunDefVars, FunCallVars,
+ FunArgTypes, NestingLevel, Args, CodeB, StateRaceList)
+ end
+ end.
+
+%%% ===========================================================================
+%%%
+%%% Backward Analysis
+%%%
+%%% ===========================================================================
+
+fixup_race_backward(CurrFun, Calls, CallsToAnalyze, Parents, Height) ->
+ case Height =:= 0 of
+ true -> Parents;
+ false ->
+ case Calls of
+ [] ->
+ case is_integer(CurrFun) orelse lists:member(CurrFun, Parents) of
+ true -> Parents;
+ false -> [CurrFun|Parents]
+ end;
+ [Head|Tail] ->
+ {Parent, TupleB} = Head,
+ case TupleB =:= CurrFun of
+ true -> % more paths are needed
+ NewCallsToAnalyze = lists:delete(Head, CallsToAnalyze),
+ NewParents =
+ fixup_race_backward(Parent, NewCallsToAnalyze,
+ NewCallsToAnalyze, Parents, Height - 1),
+ fixup_race_backward(CurrFun, Tail, NewCallsToAnalyze, NewParents,
+ Height);
+ false ->
+ fixup_race_backward(CurrFun, Tail, CallsToAnalyze, Parents,
+ Height)
+ end
+ end
+ end.
+
+%%% ===========================================================================
+%%%
+%%% Utilities
+%%%
+%%% ===========================================================================
+
+are_bound_labels(Label1, Label2, RaceVarMap) ->
+ case dict:find(Label1, RaceVarMap) of
+ error -> false;
+ {ok, Labels} ->
+ lists:member(Label2, Labels) orelse
+ are_bound_labels_helper(Labels, Label1, Label2, RaceVarMap)
+ end.
+
+are_bound_labels_helper(Labels, OldLabel, CompLabel, RaceVarMap) ->
+ case dict:size(RaceVarMap) of
+ 0 -> false;
+ _ ->
+ case Labels of
+ [] -> false;
+ [Head|Tail] ->
+ NewRaceVarMap = dict:erase(OldLabel, RaceVarMap),
+ are_bound_labels(Head, CompLabel, NewRaceVarMap) orelse
+ are_bound_labels_helper(Tail, Head, CompLabel, NewRaceVarMap)
+ end
+ end.
+
+are_bound_vars(Vars1, Vars2, RaceVarMap) ->
+ case is_list(Vars1) andalso is_list(Vars2) of
+ true ->
+ case Vars1 of
+ [] -> false;
+ [AHead|ATail] ->
+ case Vars2 of
+ [] -> false;
+ [PHead|PTail] ->
+ are_bound_vars(AHead, PHead, RaceVarMap) andalso
+ are_bound_vars(ATail, PTail, RaceVarMap)
+ end
+ end;
+ false ->
+ {NewVars1, NewVars2, IsList} =
+ case is_list(Vars1) of
+ true ->
+ case Vars1 of
+ [Var1] -> {Var1, Vars2, true};
+ _Thing -> {Vars1, Vars2, false}
+ end;
+ false ->
+ case is_list(Vars2) of
+ true ->
+ case Vars2 of
+ [Var2] -> {Vars1, Var2, true};
+ _Thing -> {Vars1, Vars2, false}
+ end;
+ false -> {Vars1, Vars2, true}
+ end
+ end,
+ case IsList of
+ true ->
+ case cerl:type(NewVars1) of
+ var ->
+ case cerl:type(NewVars2) of
+ var ->
+ ALabel = cerl_trees:get_label(NewVars1),
+ PLabel = cerl_trees:get_label(NewVars2),
+ are_bound_labels(ALabel, PLabel, RaceVarMap) orelse
+ are_bound_labels(PLabel, ALabel, RaceVarMap);
+ alias ->
+ are_bound_vars(NewVars1, cerl:alias_var(NewVars2),
+ RaceVarMap);
+ values ->
+ are_bound_vars(NewVars1, cerl:values_es(NewVars2),
+ RaceVarMap);
+ _Other -> false
+ end;
+ tuple ->
+ case cerl:type(NewVars2) of
+ tuple ->
+ are_bound_vars(cerl:tuple_es(NewVars1),
+ cerl:tuple_es(NewVars2), RaceVarMap);
+ alias ->
+ are_bound_vars(NewVars1, cerl:alias_var(NewVars2),
+ RaceVarMap);
+ values ->
+ are_bound_vars(NewVars1, cerl:values_es(NewVars2),
+ RaceVarMap);
+ _Other -> false
+ end;
+ cons ->
+ case cerl:type(NewVars2) of
+ cons ->
+ are_bound_vars(cerl:cons_hd(NewVars1),
+ cerl:cons_hd(NewVars2), RaceVarMap)
+ andalso
+ are_bound_vars(cerl:cons_tl(NewVars1),
+ cerl:cons_tl(NewVars2), RaceVarMap);
+ alias ->
+ are_bound_vars(NewVars1, cerl:alias_var(NewVars2),
+ RaceVarMap);
+ values ->
+ are_bound_vars(NewVars1, cerl:values_es(NewVars2),
+ RaceVarMap);
+ _Other -> false
+ end;
+ alias ->
+ case cerl:type(NewVars2) of
+ alias ->
+ are_bound_vars(cerl:alias_var(NewVars1),
+ cerl:alias_var(NewVars2), RaceVarMap);
+ _Other ->
+ are_bound_vars(cerl:alias_var(NewVars1),
+ NewVars2, RaceVarMap)
+ end;
+ values ->
+ case cerl:type(NewVars2) of
+ values ->
+ are_bound_vars(cerl:values_es(NewVars1),
+ cerl:values_es(NewVars2), RaceVarMap);
+ _Other ->
+ are_bound_vars(cerl:values_es(NewVars1),
+ NewVars2, RaceVarMap)
+ end;
+ _Other -> false
+ end;
+ false -> false
+ end
+ end.
+
+callgraph__renew_tables(Table, Callgraph) ->
+ case Table of
+ {named, NameLabel, Names} ->
+ PTablesToAdd =
+ case NameLabel of
+ ?no_label -> [];
+ _Other -> [NameLabel]
+ end,
+ NamesToAdd = filter_named_tables(Names),
+ PTables = dialyzer_callgraph:get_public_tables(Callgraph),
+ NTables = dialyzer_callgraph:get_named_tables(Callgraph),
+ dialyzer_callgraph:put_public_tables(
+ lists:usort(PTablesToAdd ++ PTables),
+ dialyzer_callgraph:put_named_tables(
+ NamesToAdd ++ NTables, Callgraph));
+ _Other ->
+ Callgraph
+ end.
+
+cleanup_clause_code(#curr_fun{mfa = CurrFun} = CurrTuple, Code,
+ NestingLevel, LocalNestingLevel) ->
+ case Code of
+ [] -> {CurrTuple, []};
+ [Head|Tail] ->
+ {NewLocalNestingLevel, NewNestingLevel, NewCurrTuple, Return} =
+ case Head of
+ beg_case ->
+ {LocalNestingLevel, NestingLevel + 1, CurrTuple, false};
+ #end_case{} ->
+ {LocalNestingLevel, NestingLevel - 1, CurrTuple, false};
+ #end_clause{} ->
+ case NestingLevel =:= 0 of
+ true ->
+ {LocalNestingLevel, NestingLevel, CurrTuple, true};
+ false ->
+ {LocalNestingLevel, NestingLevel, CurrTuple, false}
+ end;
+ #fun_call{caller = CurrFun} ->
+ {LocalNestingLevel - 1, NestingLevel, CurrTuple, false};
+ #curr_fun{status = in} ->
+ {LocalNestingLevel - 1, NestingLevel, Head, false};
+ #curr_fun{status = out} ->
+ {LocalNestingLevel + 1, NestingLevel, Head, false};
+ Other when Other =/= #fun_call{} ->
+ {LocalNestingLevel, NestingLevel, CurrTuple, false}
+ end,
+ case Return of
+ true -> {NewCurrTuple, Tail};
+ false ->
+ cleanup_clause_code(NewCurrTuple, Tail, NewNestingLevel,
+ NewLocalNestingLevel)
+ end
+ end.
+
+cleanup_dep_calls(DepList) ->
+ case DepList of
+ [] -> [];
+ [#dep_call{call_name = CallName, arg_types = ArgTypes,
+ vars = Vars, state = State, file_line = FileLine}|T] ->
+ [#dep_call{call_name = CallName, arg_types = ArgTypes,
+ vars = Vars, state = State, file_line = FileLine}|
+ cleanup_dep_calls(T)]
+ end.
+
+cleanup_race_code(State) ->
+ Callgraph = dialyzer_dataflow:state__get_callgraph(State),
+ dialyzer_dataflow:state__put_callgraph(
+ dialyzer_callgraph:race_code_new(Callgraph), State).
+
+filter_named_tables(NamesList) ->
+ case NamesList of
+ [] -> [];
+ [Head|Tail] ->
+ NewHead =
+ case string:rstr(Head, "()") of
+ 0 -> [Head];
+ _Other -> []
+ end,
+ NewHead ++ filter_named_tables(Tail)
+ end.
+
+filter_parents(Parents, NewParents, Digraph) ->
+ case Parents of
+ [] -> NewParents;
+ [Head|Tail] ->
+ NewParents1 = filter_parents_helper1(Head, Tail, NewParents, Digraph),
+ filter_parents(Tail, NewParents1, Digraph)
+ end.
+
+filter_parents_helper1(First, Rest, NewParents, Digraph) ->
+ case Rest of
+ [] -> NewParents;
+ [Head|Tail] ->
+ NewParents1 = filter_parents_helper2(First, Head, NewParents, Digraph),
+ filter_parents_helper1(First, Tail, NewParents1, Digraph)
+ end.
+
+filter_parents_helper2(Parent1, Parent2, NewParents, Digraph) ->
+ case digraph:get_path(Digraph, Parent1, Parent2) of
+ false ->
+ case digraph:get_path(Digraph, Parent2, Parent1) of
+ false -> NewParents;
+ _Vertices -> NewParents -- [Parent1]
+ end;
+ _Vertices -> NewParents -- [Parent2]
+ end.
+
+find_all_bound_vars(Label, RaceVarMap) ->
+ case dict:find(Label, RaceVarMap) of
+ error -> [Label];
+ {ok, Labels} ->
+ lists:usort(Labels ++
+ find_all_bound_vars_helper(Labels, Label, RaceVarMap))
+ end.
+
+find_all_bound_vars_helper(Labels, Label, RaceVarMap) ->
+ case dict:size(RaceVarMap) of
+ 0 -> [];
+ _ ->
+ case Labels of
+ [] -> [];
+ [Head|Tail] ->
+ NewRaceVarMap = dict:erase(Label, RaceVarMap),
+ find_all_bound_vars(Head, NewRaceVarMap) ++
+ find_all_bound_vars_helper(Tail, Head, NewRaceVarMap)
+ end
+ end.
+
+fixup_all_calls(CurrFun, NextFun, NextFunLabel, Args, CodeToReplace,
+ Code, RaceVarMap) ->
+ case Code of
+ [] -> [];
+ [Head|Tail] ->
+ NewCode =
+ case Head of
+ #fun_call{caller = CurrFun, callee = Callee,
+ arg_types = FunArgTypes, vars = FunArgs}
+ when Callee =:= NextFun orelse Callee =:= NextFunLabel ->
+ RaceVarMap1 = race_var_map(Args, FunArgs, RaceVarMap, bind),
+ [#curr_fun{status = in, mfa = NextFun, label = NextFunLabel,
+ var_map = RaceVarMap1, def_vars = Args,
+ call_vars = FunArgs, arg_types = FunArgTypes}|
+ CodeToReplace];
+ _Other -> [Head]
+ end,
+ RetCode =
+ fixup_all_calls(CurrFun, NextFun, NextFunLabel, Args, CodeToReplace,
+ Tail, RaceVarMap),
+ NewCode ++ RetCode
+ end.
+
+is_last_race(RaceTag, InitFun, Code, Callgraph) ->
+ case Code of
+ [] -> true;
+ [Head|Tail] ->
+ case Head of
+ RaceTag -> false;
+ #fun_call{callee = Fun} ->
+ FunName =
+ case is_integer(Fun) of
+ true ->
+ case dialyzer_callgraph:lookup_name(Fun, Callgraph) of
+ error -> Fun;
+ {ok, Name} -> Name
+ end;
+ false -> Fun
+ end,
+ Digraph = dialyzer_callgraph:get_digraph(Callgraph),
+ case FunName =:= InitFun orelse
+ digraph:get_path(Digraph, FunName, InitFun) of
+ false -> is_last_race(RaceTag, InitFun, Tail, Callgraph);
+ _Vertices -> false
+ end;
+ _Other -> is_last_race(RaceTag, InitFun, Tail, Callgraph)
+ end
+ end.
+
+lists_key_member(Member, List, N) when is_integer(Member) ->
+ case List of
+ [] -> 0;
+ [Head|Tail] ->
+ NewN = N + 1,
+ case Head of
+ Member -> NewN;
+ _Other -> lists_key_member(Member, Tail, NewN)
+ end
+ end;
+lists_key_member(_M, _L, _N) ->
+ 0.
+
+lists_key_member_lists(MemberList, List) ->
+ case MemberList of
+ [] -> 0;
+ [Head|Tail] ->
+ case lists_key_member(Head, List, 0) of
+ 0 -> lists_key_member_lists(Tail, List);
+ Other -> Other
+ end
+ end.
+
+lists_key_members_lists(MemberList, List) ->
+ case MemberList of
+ [] -> [];
+ [Head|Tail] ->
+ lists:usort(
+ lists_key_members_lists_helper(Head, List, 1) ++
+ lists_key_members_lists(Tail, List))
+ end.
+
+lists_key_members_lists_helper(Elem, List, N) when is_integer(Elem) ->
+ case List of
+ [] -> [];
+ [Head|Tail] ->
+ NewHead =
+ case Head =:= Elem of
+ true -> [N];
+ false -> []
+ end,
+ NewHead ++ lists_key_members_lists_helper(Elem, Tail, N + 1)
+ end;
+lists_key_members_lists_helper(_Elem, _List, _N) ->
+ [0].
+
+lists_key_replace(N, List, NewMember) ->
+ {Before, [_|After]} = lists:split(N - 1, List),
+ Before ++ [NewMember|After].
+
+lists_get(0, _List) -> ?no_label;
+lists_get(N, List) -> lists:nth(N, List).
+
+refine_race(RaceCall, WarnVarArgs, RaceWarnTag, DependencyList, RaceVarMap) ->
+ case RaceWarnTag of
+ WarnWhereis when WarnWhereis =:= ?WARN_WHEREIS_REGISTER orelse
+ WarnWhereis =:= ?WARN_WHEREIS_UNREGISTER ->
+ case RaceCall of
+ #dep_call{call_name = ets_lookup} ->
+ DependencyList;
+ #dep_call{call_name = mnesia_dirty_read} ->
+ DependencyList;
+ #dep_call{call_name = whereis, args = VarArgs} ->
+ refine_race_helper(RaceCall, VarArgs, WarnVarArgs, RaceWarnTag,
+ DependencyList, RaceVarMap)
+ end;
+ ?WARN_ETS_LOOKUP_INSERT ->
+ case RaceCall of
+ #dep_call{call_name = whereis} ->
+ DependencyList;
+ #dep_call{call_name = mnesia_dirty_read} ->
+ DependencyList;
+ #dep_call{call_name = ets_lookup, args = VarArgs} ->
+ refine_race_helper(RaceCall, VarArgs, WarnVarArgs, RaceWarnTag,
+ DependencyList, RaceVarMap)
+ end;
+ ?WARN_MNESIA_DIRTY_READ_WRITE ->
+ case RaceCall of
+ #dep_call{call_name = whereis} ->
+ DependencyList;
+ #dep_call{call_name = ets_lookup} ->
+ DependencyList;
+ #dep_call{call_name = mnesia_dirty_read, args = VarArgs} ->
+ refine_race_helper(RaceCall, VarArgs, WarnVarArgs, RaceWarnTag,
+ DependencyList, RaceVarMap)
+ end
+ end.
+
+refine_race_helper(RaceCall, VarArgs, WarnVarArgs, RaceWarnTag, DependencyList,
+ RaceVarMap) ->
+ case compare_types(VarArgs, WarnVarArgs, RaceWarnTag, RaceVarMap) of
+ true -> [RaceCall|DependencyList];
+ false -> DependencyList
+ end.
+
+remove_clause(RaceList, CurrTuple, Code, NestingLevel) ->
+ NewRaceList = fixup_case_rest_paths(RaceList, 0),
+ {NewCurrTuple, NewCode} =
+ cleanup_clause_code(CurrTuple, Code, 0, NestingLevel),
+ ReturnTuple = {NewRaceList, NewCurrTuple, NewCode, NestingLevel},
+ case NewRaceList of
+ [beg_case|RTail] ->
+ case NewCode of
+ [#end_case{}|CTail] ->
+ remove_clause(RTail, NewCurrTuple, CTail, NestingLevel);
+ _Other -> ReturnTuple
+ end;
+ _Else -> ReturnTuple
+ end.
+
+remove_nonlocal_functions(Code, NestingLevel) ->
+ case Code of
+ [] -> [];
+ [H|T] ->
+ NewNL =
+ case H of
+ #curr_fun{status = in} ->
+ NestingLevel + 1;
+ #curr_fun{status = out} ->
+ NestingLevel - 1;
+ _Other ->
+ NestingLevel
+ end,
+ case NewNL =:= 0 of
+ true -> T;
+ false -> remove_nonlocal_functions(T, NewNL)
+ end
+ end.
+
+renew_curr_fun(CurrFun, Races) ->
+ Races#races{curr_fun = CurrFun}.
+
+renew_curr_fun_label(CurrFunLabel, Races) ->
+ Races#races{curr_fun_label = CurrFunLabel}.
+
+renew_race_list(RaceList, Races) ->
+ Races#races{race_list = RaceList}.
+
+renew_race_list_size(RaceListSize, Races) ->
+ Races#races{race_list_size = RaceListSize}.
+
+renew_race_tags(RaceTags, Races) ->
+ Races#races{race_tags = RaceTags}.
+
+renew_table(Table, Races) ->
+ Races#races{new_table = Table}.
+
+state__renew_curr_fun(CurrFun, State) ->
+ Races = dialyzer_dataflow:state__get_races(State),
+ dialyzer_dataflow:state__put_races(renew_curr_fun(CurrFun, Races), State).
+
+state__renew_curr_fun_label(CurrFunLabel, State) ->
+ Races = dialyzer_dataflow:state__get_races(State),
+ dialyzer_dataflow:state__put_races(
+ renew_curr_fun_label(CurrFunLabel, Races), State).
+
+state__renew_race_list(RaceList, State) ->
+ Races = dialyzer_dataflow:state__get_races(State),
+ dialyzer_dataflow:state__put_races(renew_race_list(RaceList, Races), State).
+
+state__renew_race_tags(RaceTags, State) ->
+ Races = dialyzer_dataflow:state__get_races(State),
+ dialyzer_dataflow:state__put_races(renew_race_tags(RaceTags, Races), State).
+
+state__renew_info(RaceList, RaceListSize, RaceTags, Table, State) ->
+ Callgraph = dialyzer_dataflow:state__get_callgraph(State),
+ Races = dialyzer_dataflow:state__get_races(State),
+ dialyzer_dataflow:state__put_callgraph(
+ callgraph__renew_tables(Table, Callgraph),
+ dialyzer_dataflow:state__put_races(
+ renew_table(Table,
+ renew_race_list(RaceList,
+ renew_race_list_size(RaceListSize,
+ renew_race_tags(RaceTags, Races)))), State)).
+
+%%% ===========================================================================
+%%%
+%%% Variable and Type Utilities
+%%%
+%%% ===========================================================================
+
+any_args(StrList) ->
+ case StrList of
+ [] -> false;
+ [Head|Tail] ->
+ case string:rstr(Head, "()") of
+ 0 -> any_args(Tail);
+ _Other -> true
+ end
+ end.
+
+-spec bind_dict_vars(label(), label(), dict:dict()) -> dict:dict().
+
+bind_dict_vars(Key, Label, RaceVarMap) ->
+ case Key =:= Label of
+ true -> RaceVarMap;
+ false ->
+ case dict:find(Key, RaceVarMap) of
+ error -> dict:store(Key, [Label], RaceVarMap);
+ {ok, Labels} ->
+ case lists:member(Label, Labels) of
+ true -> RaceVarMap;
+ false -> dict:store(Key, [Label|Labels], RaceVarMap)
+ end
+ end
+ end.
+
+bind_dict_vars_list(Key, Labels, RaceVarMap) ->
+ case Labels of
+ [] -> RaceVarMap;
+ [Head|Tail] ->
+ bind_dict_vars_list(Key, Tail, bind_dict_vars(Key, Head, RaceVarMap))
+ end.
+
+compare_ets_insert(OldWarnVarArgs, NewWarnVarArgs, RaceVarMap) ->
+ [Old1, Old2, Old3, Old4] = OldWarnVarArgs,
+ [New1, New2, New3, New4] = NewWarnVarArgs,
+ Bool =
+ case any_args(Old2) of
+ true -> compare_var_list(New1, Old1, RaceVarMap);
+ false ->
+ case any_args(New2) of
+ true -> compare_var_list(New1, Old1, RaceVarMap);
+ false -> compare_var_list(New1, Old1, RaceVarMap)
+ orelse (Old2 =:= New2)
+ end
+ end,
+ case Bool of
+ true ->
+ case any_args(Old4) of
+ true ->
+ case compare_list_vars(Old3, ets_list_args(New3), [], RaceVarMap) of
+ true -> true;
+ Args3 -> lists_key_replace(3, OldWarnVarArgs, Args3)
+ end;
+ false ->
+ case any_args(New4) of
+ true ->
+ case compare_list_vars(Old3, ets_list_args(New3), [],
+ RaceVarMap) of
+ true -> true;
+ Args3 -> lists_key_replace(3, OldWarnVarArgs, Args3)
+ end;
+ false ->
+ case compare_list_vars(Old3, ets_list_args(New3), [],
+ RaceVarMap) of
+ true -> true;
+ Args3 ->
+ lists_key_replace(4,
+ lists_key_replace(3, OldWarnVarArgs, Args3), Old4 -- New4)
+ end
+ end
+ end;
+ false -> OldWarnVarArgs
+ end.
+
+compare_first_arg(OldWarnVarArgs, NewWarnVarArgs, RaceVarMap) ->
+ [Old1, Old2|_OldT] = OldWarnVarArgs,
+ [New1, New2|_NewT] = NewWarnVarArgs,
+ case any_args(Old2) of
+ true ->
+ case compare_var_list(New1, Old1, RaceVarMap) of
+ true -> true;
+ false -> OldWarnVarArgs
+ end;
+ false ->
+ case any_args(New2) of
+ true ->
+ case compare_var_list(New1, Old1, RaceVarMap) of
+ true -> true;
+ false -> OldWarnVarArgs
+ end;
+ false ->
+ case compare_var_list(New1, Old1, RaceVarMap) of
+ true -> true;
+ false -> lists_key_replace(2, OldWarnVarArgs, Old2 -- New2)
+ end
+ end
+ end.
+
+compare_argtypes(ArgTypes, WarnArgTypes) ->
+ lists:any(fun (X) -> lists:member(X, WarnArgTypes) end, ArgTypes).
+
+%% Compares the argument types of the two suspicious calls.
+compare_types(VarArgs, WarnVarArgs, RaceWarnTag, RaceVarMap) ->
+ case RaceWarnTag of
+ ?WARN_WHEREIS_REGISTER ->
+ [VA1, VA2] = VarArgs,
+ [WVA1, WVA2, _, _] = WarnVarArgs,
+ case any_args(VA2) of
+ true -> compare_var_list(VA1, WVA1, RaceVarMap);
+ false ->
+ case any_args(WVA2) of
+ true -> compare_var_list(VA1, WVA1, RaceVarMap);
+ false ->
+ compare_var_list(VA1, WVA1, RaceVarMap) orelse
+ compare_argtypes(VA2, WVA2)
+ end
+ end;
+ ?WARN_WHEREIS_UNREGISTER ->
+ [VA1, VA2] = VarArgs,
+ [WVA1, WVA2] = WarnVarArgs,
+ case any_args(VA2) of
+ true -> compare_var_list(VA1, WVA1, RaceVarMap);
+ false ->
+ case any_args(WVA2) of
+ true -> compare_var_list(VA1, WVA1, RaceVarMap);
+ false ->
+ compare_var_list(VA1, WVA1, RaceVarMap) orelse
+ compare_argtypes(VA2, WVA2)
+ end
+ end;
+ ?WARN_ETS_LOOKUP_INSERT ->
+ [VA1, VA2, VA3, VA4] = VarArgs,
+ [WVA1, WVA2, WVA3, WVA4] = WarnVarArgs,
+ Bool =
+ case any_args(VA2) of
+ true -> compare_var_list(VA1, WVA1, RaceVarMap);
+ false ->
+ case any_args(WVA2) of
+ true -> compare_var_list(VA1, WVA1, RaceVarMap);
+ false ->
+ compare_var_list(VA1, WVA1, RaceVarMap) orelse
+ compare_argtypes(VA2, WVA2)
+ end
+ end,
+ Bool andalso
+ (case any_args(VA4) of
+ true ->
+ compare_var_list(VA3, WVA3, RaceVarMap);
+ false ->
+ case any_args(WVA4) of
+ true ->
+ compare_var_list(VA3, WVA3, RaceVarMap);
+ false ->
+ compare_var_list(VA3, WVA3, RaceVarMap) orelse
+ compare_argtypes(VA4, WVA4)
+ end
+ end);
+ ?WARN_MNESIA_DIRTY_READ_WRITE ->
+ [VA1, VA2|_] = VarArgs, %% Two or four elements
+ [WVA1, WVA2|_] = WarnVarArgs,
+ case any_args(VA2) of
+ true -> compare_var_list(VA1, WVA1, RaceVarMap);
+ false ->
+ case any_args(WVA2) of
+ true -> compare_var_list(VA1, WVA1, RaceVarMap);
+ false ->
+ compare_var_list(VA1, WVA1, RaceVarMap) orelse
+ compare_argtypes(VA2, WVA2)
+ end
+ end
+ end.
+
+compare_list_vars(VarList1, VarList2, NewVarList1, RaceVarMap) ->
+ case VarList1 of
+ [] ->
+ case NewVarList1 of
+ [] -> true;
+ _Other -> NewVarList1
+ end;
+ [Head|Tail] ->
+ NewHead =
+ case compare_var_list(Head, VarList2, RaceVarMap) of
+ true -> [];
+ false -> [Head]
+ end,
+ compare_list_vars(Tail, VarList2, NewHead ++ NewVarList1, RaceVarMap)
+ end.
+
+compare_vars(Var1, Var2, RaceVarMap) when is_integer(Var1), is_integer(Var2) ->
+ Var1 =:= Var2 orelse
+ are_bound_labels(Var1, Var2, RaceVarMap) orelse
+ are_bound_labels(Var2, Var1, RaceVarMap);
+compare_vars(_Var1, _Var2, _RaceVarMap) ->
+ false.
+
+-spec compare_var_list(label_type(), [label_type()], dict:dict()) -> boolean().
+
+compare_var_list(Var, VarList, RaceVarMap) ->
+ lists:any(fun (V) -> compare_vars(Var, V, RaceVarMap) end, VarList).
+
+ets_list_args(MaybeList) ->
+ case is_list(MaybeList) of
+ true ->
+ try [ets_tuple_args(T) || T <- MaybeList]
+ catch _:_ -> [?no_label]
+ end;
+ false -> [ets_tuple_args(MaybeList)]
+ end.
+
+ets_list_argtypes(ListStr) ->
+ ListStr1 = string:strip(ListStr, left, $[),
+ ListStr2 = string:strip(ListStr1, right, $]),
+ ListStr3 = string:strip(ListStr2, right, $.),
+ string:strip(ListStr3, right, $,).
+
+ets_tuple_args(MaybeTuple) ->
+ case is_tuple(MaybeTuple) of
+ true -> element(1, MaybeTuple);
+ false -> ?no_label
+ end.
+
+ets_tuple_argtypes2(TupleList, ElemList) ->
+ case TupleList of
+ [] -> ElemList;
+ [H|T] ->
+ ets_tuple_argtypes2(T,
+ ets_tuple_argtypes2_helper(H, [], 0) ++ ElemList)
+ end.
+
+ets_tuple_argtypes2_helper(TupleStr, ElemStr, NestingLevel) ->
+ case TupleStr of
+ [] -> [];
+ [H|T] ->
+ {NewElemStr, NewNestingLevel, Return} =
+ case H of
+ ${ when NestingLevel =:= 0 ->
+ {ElemStr, NestingLevel + 1, false};
+ ${ ->
+ {[H|ElemStr], NestingLevel + 1, false};
+ $[ ->
+ {[H|ElemStr], NestingLevel + 1, false};
+ $( ->
+ {[H|ElemStr], NestingLevel + 1, false};
+ $} ->
+ {[H|ElemStr], NestingLevel - 1, false};
+ $] ->
+ {[H|ElemStr], NestingLevel - 1, false};
+ $) ->
+ {[H|ElemStr], NestingLevel - 1, false};
+ $, when NestingLevel =:= 1 ->
+ {lists:reverse(ElemStr), NestingLevel, true};
+ _Other ->
+ {[H|ElemStr], NestingLevel, false}
+ end,
+ case Return of
+ true -> string:tokens(NewElemStr, " |");
+ false ->
+ ets_tuple_argtypes2_helper(T, NewElemStr, NewNestingLevel)
+ end
+ end.
+
+ets_tuple_argtypes1(Str, Tuple, TupleList, NestingLevel) ->
+ case Str of
+ [] -> TupleList;
+ [H|T] ->
+ {NewTuple, NewNestingLevel, Add} =
+ case H of
+ ${ ->
+ {[H|Tuple], NestingLevel + 1, false};
+ $} ->
+ case NestingLevel of
+ 1 ->
+ {[H|Tuple], NestingLevel - 1, true};
+ _Else ->
+ {[H|Tuple], NestingLevel - 1, false}
+ end;
+ _Other1 when NestingLevel =:= 0 ->
+ {Tuple, NestingLevel, false};
+ _Other2 ->
+ {[H|Tuple], NestingLevel, false}
+ end,
+ case Add of
+ true ->
+ ets_tuple_argtypes1(T, [],
+ [lists:reverse(NewTuple)|TupleList],
+ NewNestingLevel);
+ false ->
+ ets_tuple_argtypes1(T, NewTuple, TupleList, NewNestingLevel)
+ end
+ end.
+
+format_arg(?bypassed) -> ?no_label;
+format_arg(Arg0) ->
+ Arg = cerl:fold_literal(Arg0),
+ case cerl:type(Arg) of
+ var -> cerl_trees:get_label(Arg);
+ tuple -> list_to_tuple([format_arg(A) || A <- cerl:tuple_es(Arg)]);
+ cons -> [format_arg(cerl:cons_hd(Arg))|format_arg(cerl:cons_tl(Arg))];
+ alias -> format_arg(cerl:alias_var(Arg));
+ literal ->
+ case cerl:is_c_nil(Arg) of
+ true -> [];
+ false -> ?no_label
+ end;
+ _Other -> ?no_label
+ end.
+
+-spec format_args([core_vars()], [erl_types:erl_type()],
+ dialyzer_dataflow:state(), call()) ->
+ args().
+
+format_args([], [], _State, _Call) ->
+ [];
+format_args(ArgList, TypeList, CleanState, Call) ->
+ format_args_2(format_args_1(ArgList, TypeList, CleanState), Call).
+
+format_args_1([Arg], [Type], CleanState) ->
+ [format_arg(Arg), format_type(Type, CleanState)];
+format_args_1([Arg|Args], [Type|Types], CleanState) ->
+ List =
+ case Arg =:= ?bypassed of
+ true -> [?no_label, format_type(Type, CleanState)];
+ false ->
+ case cerl:is_literal(cerl:fold_literal(Arg)) of
+ true -> [?no_label, format_cerl(Arg)];
+ false -> [format_arg(Arg), format_type(Type, CleanState)]
+ end
+ end,
+ List ++ format_args_1(Args, Types, CleanState).
+
+format_args_2(StrArgList, Call) ->
+ case Call of
+ whereis ->
+ lists_key_replace(2, StrArgList,
+ string:tokens(lists:nth(2, StrArgList), " |"));
+ register ->
+ lists_key_replace(2, StrArgList,
+ string:tokens(lists:nth(2, StrArgList), " |"));
+ unregister ->
+ lists_key_replace(2, StrArgList,
+ string:tokens(lists:nth(2, StrArgList), " |"));
+ ets_new ->
+ StrArgList1 = lists_key_replace(2, StrArgList,
+ string:tokens(lists:nth(2, StrArgList), " |")),
+ lists_key_replace(4, StrArgList1,
+ string:tokens(ets_list_argtypes(lists:nth(4, StrArgList1)), " |"));
+ ets_lookup ->
+ StrArgList1 = lists_key_replace(2, StrArgList,
+ string:tokens(lists:nth(2, StrArgList), " |")),
+ lists_key_replace(4, StrArgList1,
+ string:tokens(lists:nth(4, StrArgList1), " |"));
+ ets_insert ->
+ StrArgList1 = lists_key_replace(2, StrArgList,
+ string:tokens(lists:nth(2, StrArgList), " |")),
+ lists_key_replace(4, StrArgList1,
+ ets_tuple_argtypes2(
+ ets_tuple_argtypes1(lists:nth(4, StrArgList1), [], [], 0),
+ []));
+ mnesia_dirty_read1 ->
+ lists_key_replace(2, StrArgList,
+ [mnesia_tuple_argtypes(T) || T <- string:tokens(
+ lists:nth(2, StrArgList), " |")]);
+ mnesia_dirty_read2 ->
+ lists_key_replace(2, StrArgList,
+ string:tokens(lists:nth(2, StrArgList), " |"));
+ mnesia_dirty_write1 ->
+ lists_key_replace(2, StrArgList,
+ [mnesia_record_tab(R) || R <- string:tokens(
+ lists:nth(2, StrArgList), " |")]);
+ mnesia_dirty_write2 ->
+ lists_key_replace(2, StrArgList,
+ string:tokens(lists:nth(2, StrArgList), " |"));
+ function_call -> StrArgList
+ end.
+
+format_cerl(Tree) ->
+ cerl_prettypr:format(cerl:set_ann(Tree, []),
+ [{hook, dialyzer_utils:pp_hook()},
+ {noann, true},
+ {paper, 100000},
+ {ribbon, 100000}
+ ]).
+
+format_type(Type, State) ->
+ R = dialyzer_dataflow:state__get_records(State),
+ erl_types:t_to_string(Type, R).
+
+mnesia_record_tab(RecordStr) ->
+ case string:str(RecordStr, "#") =:= 1 of
+ true ->
+ "'" ++
+ string:sub_string(RecordStr, 2, string:str(RecordStr, "{") - 1) ++
+ "'";
+ false -> RecordStr
+ end.
+
+mnesia_tuple_argtypes(TupleStr) ->
+ TupleStr1 = string:strip(TupleStr, left, ${),
+ [TupleStr2|_T] = string:tokens(TupleStr1, " ,"),
+ lists:flatten(string:tokens(TupleStr2, " |")).
+
+-spec race_var_map(var_to_map1(), var_to_map2(), dict:dict(), op()) ->
+ dict:dict().
+
+race_var_map(Vars1, Vars2, RaceVarMap, Op) ->
+ case Vars1 =:= ?no_arg orelse Vars1 =:= ?bypassed
+ orelse Vars2 =:= ?bypassed of
+ true -> RaceVarMap;
+ false ->
+ case is_list(Vars1) andalso is_list(Vars2) of
+ true ->
+ case Vars1 of
+ [] -> RaceVarMap;
+ [AHead|ATail] ->
+ case Vars2 of
+ [] -> RaceVarMap;
+ [PHead|PTail] ->
+ NewRaceVarMap = race_var_map(AHead, PHead, RaceVarMap, Op),
+ race_var_map(ATail, PTail, NewRaceVarMap, Op)
+ end
+ end;
+ false ->
+ {NewVars1, NewVars2, Bool} =
+ case is_list(Vars1) of
+ true ->
+ case Vars1 of
+ [Var1] -> {Var1, Vars2, true};
+ _Thing -> {Vars1, Vars2, false}
+ end;
+ false ->
+ case is_list(Vars2) of
+ true ->
+ case Vars2 of
+ [Var2] -> {Vars1, Var2, true};
+ _Thing -> {Vars1, Vars2, false}
+ end;
+ false -> {Vars1, Vars2, true}
+ end
+ end,
+ case Bool of
+ true ->
+ case cerl:type(NewVars1) of
+ var ->
+ case cerl:type(NewVars2) of
+ var ->
+ ALabel = cerl_trees:get_label(NewVars1),
+ PLabel = cerl_trees:get_label(NewVars2),
+ case Op of
+ bind ->
+ TempRaceVarMap =
+ bind_dict_vars(ALabel, PLabel, RaceVarMap),
+ bind_dict_vars(PLabel, ALabel, TempRaceVarMap);
+ unbind ->
+ TempRaceVarMap =
+ unbind_dict_vars(ALabel, PLabel, RaceVarMap),
+ unbind_dict_vars(PLabel, ALabel, TempRaceVarMap)
+ end;
+ alias ->
+ race_var_map(NewVars1, cerl:alias_var(NewVars2),
+ RaceVarMap, Op);
+ values ->
+ race_var_map(NewVars1, cerl:values_es(NewVars2),
+ RaceVarMap, Op);
+ _Other -> RaceVarMap
+ end;
+ tuple ->
+ case cerl:type(NewVars2) of
+ tuple ->
+ race_var_map(cerl:tuple_es(NewVars1),
+ cerl:tuple_es(NewVars2), RaceVarMap, Op);
+ alias ->
+ race_var_map(NewVars1, cerl:alias_var(NewVars2),
+ RaceVarMap, Op);
+ values ->
+ race_var_map(NewVars1, cerl:values_es(NewVars2),
+ RaceVarMap, Op);
+ _Other -> RaceVarMap
+ end;
+ cons ->
+ case cerl:type(NewVars2) of
+ cons ->
+ NewRaceVarMap = race_var_map(cerl:cons_hd(NewVars1),
+ cerl:cons_hd(NewVars2), RaceVarMap, Op),
+ race_var_map(cerl:cons_tl(NewVars1),
+ cerl:cons_tl(NewVars2), NewRaceVarMap, Op);
+ alias ->
+ race_var_map(NewVars1, cerl:alias_var(NewVars2),
+ RaceVarMap, Op);
+ values ->
+ race_var_map(NewVars1, cerl:values_es(NewVars2),
+ RaceVarMap, Op);
+ _Other -> RaceVarMap
+ end;
+ alias ->
+ case cerl:type(NewVars2) of
+ alias ->
+ race_var_map(cerl:alias_var(NewVars1),
+ cerl:alias_var(NewVars2), RaceVarMap, Op);
+ _Other ->
+ race_var_map(cerl:alias_var(NewVars1),
+ NewVars2, RaceVarMap, Op)
+ end;
+ values ->
+ case cerl:type(NewVars2) of
+ values ->
+ race_var_map(cerl:values_es(NewVars1),
+ cerl:values_es(NewVars2), RaceVarMap, Op);
+ _Other ->
+ race_var_map(cerl:values_es(NewVars1),
+ NewVars2, RaceVarMap, Op)
+ end;
+ _Other -> RaceVarMap
+ end;
+ false -> RaceVarMap
+ end
+ end
+ end.
+
+race_var_map_clauses(Clauses, RaceVarMap) ->
+ case Clauses of
+ [] -> RaceVarMap;
+ [#end_clause{arg = Arg, pats = Pats, guard = Guard}|T] ->
+ {RaceVarMap1, _RemoveClause} =
+ race_var_map_guard(Arg, Pats, Guard, RaceVarMap, bind),
+ race_var_map_clauses(T, RaceVarMap1)
+ end.
+
+race_var_map_guard(Arg, Pats, Guard, RaceVarMap, Op) ->
+ {NewRaceVarMap, RemoveClause} =
+ case cerl:type(Guard) of
+ call ->
+ CallName = cerl:call_name(Guard),
+ case cerl:is_literal(CallName) of
+ true ->
+ case cerl:concrete(CallName) of
+ '=:=' ->
+ [Arg1, Arg2] = cerl:call_args(Guard),
+ {race_var_map(Arg1, Arg2, RaceVarMap, Op), false};
+ '==' ->
+ [Arg1, Arg2] = cerl:call_args(Guard),
+ {race_var_map(Arg1, Arg2, RaceVarMap, Op), false};
+ '=/=' ->
+ case Op of
+ bind ->
+ [Arg1, Arg2] = cerl:call_args(Guard),
+ {RaceVarMap, are_bound_vars(Arg1, Arg2, RaceVarMap)};
+ unbind -> {RaceVarMap, false}
+ end;
+ _Other -> {RaceVarMap, false}
+ end;
+ false -> {RaceVarMap, false}
+ end;
+ _Other -> {RaceVarMap, false}
+ end,
+ {RaceVarMap1, RemoveClause1} =
+ race_var_map_guard_helper1(Arg, Pats,
+ race_var_map(Arg, Pats, NewRaceVarMap, Op), Op),
+ {RaceVarMap1, RemoveClause orelse RemoveClause1}.
+
+race_var_map_guard_helper1(Arg, Pats, RaceVarMap, Op) ->
+ case Arg =:= ?no_arg orelse Arg =:= ?bypassed of
+ true -> {RaceVarMap, false};
+ false ->
+ case cerl:type(Arg) of
+ call ->
+ case Pats of
+ [NewPat] ->
+ ModName = cerl:call_module(Arg),
+ CallName = cerl:call_name(Arg),
+ case cerl:is_literal(ModName) andalso
+ cerl:is_literal(CallName) of
+ true ->
+ case {cerl:concrete(ModName),
+ cerl:concrete(CallName)} of
+ {erlang, '=:='} ->
+ race_var_map_guard_helper2(Arg, NewPat, true,
+ RaceVarMap, Op);
+ {erlang, '=='} ->
+ race_var_map_guard_helper2(Arg, NewPat, true,
+ RaceVarMap, Op);
+ {erlang, '=/='} ->
+ race_var_map_guard_helper2(Arg, NewPat, false,
+ RaceVarMap, Op);
+ _Else -> {RaceVarMap, false}
+ end;
+ false -> {RaceVarMap, false}
+ end;
+ _Other -> {RaceVarMap, false}
+ end;
+ _Other -> {RaceVarMap, false}
+ end
+ end.
+
+race_var_map_guard_helper2(Arg, Pat0, Bool, RaceVarMap, Op) ->
+ Pat = cerl:fold_literal(Pat0),
+ case cerl:type(Pat) of
+ literal ->
+ [Arg1, Arg2] = cerl:call_args(Arg),
+ case cerl:concrete(Pat) of
+ Bool ->
+ {race_var_map(Arg1, Arg2, RaceVarMap, Op), false};
+ _Else ->
+ case Op of
+ bind ->
+ {RaceVarMap, are_bound_vars(Arg1, Arg2, RaceVarMap)};
+ unbind -> {RaceVarMap, false}
+ end
+ end;
+ _Else -> {RaceVarMap, false}
+ end.
+
+unbind_dict_vars(Var, Var, RaceVarMap) ->
+ RaceVarMap;
+unbind_dict_vars(Var1, Var2, RaceVarMap) ->
+ case dict:find(Var1, RaceVarMap) of
+ error -> RaceVarMap;
+ {ok, Labels} ->
+ case Labels of
+ [] -> dict:erase(Var1, RaceVarMap);
+ _Else ->
+ case lists:member(Var2, Labels) of
+ true ->
+ unbind_dict_vars(Var1, Var2,
+ bind_dict_vars_list(Var1, Labels -- [Var2],
+ dict:erase(Var1, RaceVarMap)));
+ false ->
+ unbind_dict_vars_helper(Labels, Var1, Var2, RaceVarMap)
+ end
+ end
+ end.
+
+unbind_dict_vars_helper(Labels, Key, CompLabel, RaceVarMap) ->
+ case dict:size(RaceVarMap) of
+ 0 -> RaceVarMap;
+ _ ->
+ case Labels of
+ [] -> RaceVarMap;
+ [Head|Tail] ->
+ NewRaceVarMap =
+ case are_bound_labels(Head, CompLabel, RaceVarMap) orelse
+ are_bound_labels(CompLabel, Head, RaceVarMap) of
+ true ->
+ bind_dict_vars_list(Key, Labels -- [Head],
+ dict:erase(Key, RaceVarMap));
+ false -> RaceVarMap
+ end,
+ unbind_dict_vars_helper(Tail, Key, CompLabel, NewRaceVarMap)
+ end
+ end.
+
+var_analysis(FunDefArgs, FunCallArgs, WarnVarArgs, RaceWarnTag) ->
+ case RaceWarnTag of
+ ?WARN_WHEREIS_REGISTER ->
+ [WVA1, WVA2, WVA3, WVA4] = WarnVarArgs,
+ ArgNos = lists_key_members_lists(WVA1, FunDefArgs),
+ [[lists_get(N, FunCallArgs) || N <- ArgNos], WVA2, WVA3, WVA4];
+ ?WARN_WHEREIS_UNREGISTER ->
+ [WVA1, WVA2] = WarnVarArgs,
+ ArgNos = lists_key_members_lists(WVA1, FunDefArgs),
+ [[lists_get(N, FunCallArgs) || N <- ArgNos], WVA2];
+ ?WARN_ETS_LOOKUP_INSERT ->
+ [WVA1, WVA2, WVA3, WVA4] = WarnVarArgs,
+ ArgNos1 = lists_key_members_lists(WVA1, FunDefArgs),
+ ArgNos2 = lists_key_members_lists(WVA3, FunDefArgs),
+ [[lists_get(N1, FunCallArgs) || N1 <- ArgNos1], WVA2,
+ [lists_get(N2, FunCallArgs) || N2 <- ArgNos2], WVA4];
+ ?WARN_MNESIA_DIRTY_READ_WRITE ->
+ [WVA1, WVA2|T] = WarnVarArgs,
+ ArgNos = lists_key_members_lists(WVA1, FunDefArgs),
+ [[lists_get(N, FunCallArgs) || N <- ArgNos], WVA2|T]
+ end.
+
+var_type_analysis(FunDefArgs, FunCallTypes, WarnVarArgs, RaceWarnTag,
+ RaceVarMap, CleanState) ->
+ FunVarArgs = format_args(FunDefArgs, FunCallTypes, CleanState, function_call),
+ case RaceWarnTag of
+ ?WARN_WHEREIS_REGISTER ->
+ [WVA1, WVA2, WVA3, WVA4] = WarnVarArgs,
+ Vars = find_all_bound_vars(WVA1, RaceVarMap),
+ case lists_key_member_lists(Vars, FunVarArgs) of
+ 0 -> [Vars, WVA2, WVA3, WVA4];
+ N when is_integer(N) ->
+ NewWVA2 = string:tokens(lists:nth(N + 1, FunVarArgs), " |"),
+ [Vars, NewWVA2, WVA3, WVA4]
+ end;
+ ?WARN_WHEREIS_UNREGISTER ->
+ [WVA1, WVA2] = WarnVarArgs,
+ Vars = find_all_bound_vars(WVA1, RaceVarMap),
+ case lists_key_member_lists(Vars, FunVarArgs) of
+ 0 -> [Vars, WVA2];
+ N when is_integer(N) ->
+ NewWVA2 = string:tokens(lists:nth(N + 1, FunVarArgs), " |"),
+ [Vars, NewWVA2]
+ end;
+ ?WARN_ETS_LOOKUP_INSERT ->
+ [WVA1, WVA2, WVA3, WVA4] = WarnVarArgs,
+ Vars1 = find_all_bound_vars(WVA1, RaceVarMap),
+ FirstVarArg =
+ case lists_key_member_lists(Vars1, FunVarArgs) of
+ 0 -> [Vars1, WVA2];
+ N1 when is_integer(N1) ->
+ NewWVA2 = string:tokens(lists:nth(N1 + 1, FunVarArgs), " |"),
+ [Vars1, NewWVA2]
+ end,
+ Vars2 =
+ lists:flatten(
+ [find_all_bound_vars(A, RaceVarMap) || A <- ets_list_args(WVA3)]),
+ case lists_key_member_lists(Vars2, FunVarArgs) of
+ 0 -> FirstVarArg ++ [Vars2, WVA4];
+ N2 when is_integer(N2) ->
+ NewWVA4 =
+ ets_tuple_argtypes2(
+ ets_tuple_argtypes1(lists:nth(N2 + 1, FunVarArgs), [], [], 0),
+ []),
+ FirstVarArg ++ [Vars2, NewWVA4]
+
+ end;
+ ?WARN_MNESIA_DIRTY_READ_WRITE ->
+ [WVA1, WVA2|T] = WarnVarArgs,
+ Arity =
+ case T of
+ [] -> 1;
+ _Else -> 2
+ end,
+ Vars = find_all_bound_vars(WVA1, RaceVarMap),
+ case lists_key_member_lists(Vars, FunVarArgs) of
+ 0 -> [Vars, WVA2|T];
+ N when is_integer(N) ->
+ NewWVA2 =
+ case Arity of
+ 1 ->
+ [mnesia_record_tab(R) || R <- string:tokens(
+ lists:nth(2, FunVarArgs), " |")];
+ 2 ->
+ string:tokens(lists:nth(N + 1, FunVarArgs), " |")
+ end,
+ [Vars, NewWVA2|T]
+ end
+ end.
+
+%%% ===========================================================================
+%%%
+%%% Warning Format Utilities
+%%%
+%%% ===========================================================================
+
+add_race_warning(Warn, #races{race_warnings = Warns} = Races) ->
+ Races#races{race_warnings = [Warn|Warns]}.
+
+get_race_warn(Fun, Args, ArgTypes, DepList, State) ->
+ {M, F, _A} = Fun,
+ case DepList of
+ [] -> {State, no_race};
+ _Other ->
+ {State, {race_condition, [M, F, Args, ArgTypes, State, DepList]}}
+ end.
+
+-spec get_race_warnings(races(), dialyzer_dataflow:state()) ->
+ {races(), dialyzer_dataflow:state()}.
+
+get_race_warnings(#races{race_warnings = RaceWarnings}, State) ->
+ get_race_warnings_helper(RaceWarnings, State).
+
+get_race_warnings_helper(Warnings, State) ->
+ case Warnings of
+ [] ->
+ {dialyzer_dataflow:state__get_races(State), State};
+ [H|T] ->
+ {RaceWarnTag, WarningInfo, {race_condition, [M, F, A, AT, S, DepList]}} = H,
+ Reason =
+ case RaceWarnTag of
+ ?WARN_WHEREIS_REGISTER ->
+ get_reason(lists:keysort(7, DepList),
+ "might fail due to a possible race condition "
+ "caused by its combination with ");
+ ?WARN_WHEREIS_UNREGISTER ->
+ get_reason(lists:keysort(7, DepList),
+ "might fail due to a possible race condition "
+ "caused by its combination with ");
+ ?WARN_ETS_LOOKUP_INSERT ->
+ get_reason(lists:keysort(7, DepList),
+ "might have an unintended effect due to " ++
+ "a possible race condition " ++
+ "caused by its combination with ");
+ ?WARN_MNESIA_DIRTY_READ_WRITE ->
+ get_reason(lists:keysort(7, DepList),
+ "might have an unintended effect due to " ++
+ "a possible race condition " ++
+ "caused by its combination with ")
+ end,
+ W =
+ {?WARN_RACE_CONDITION, WarningInfo,
+ {race_condition,
+ [M, F, dialyzer_dataflow:format_args(A, AT, S), Reason]}},
+ get_race_warnings_helper(T,
+ dialyzer_dataflow:state__add_warning(W, State))
+ end.
+
+get_reason(DependencyList, Reason) ->
+ case DependencyList of
+ [] -> "";
+ [#dep_call{call_name = Call, arg_types = ArgTypes, vars = Args,
+ state = State, file_line = {File, Line}}|T] ->
+ R =
+ Reason ++
+ case Call of
+ whereis -> "the erlang:whereis";
+ ets_lookup -> "the ets:lookup";
+ mnesia_dirty_read -> "the mnesia:dirty_read"
+ end ++
+ dialyzer_dataflow:format_args(Args, ArgTypes, State) ++
+ " call in " ++
+ filename:basename(File) ++
+ " on line " ++
+ lists:flatten(io_lib:write(Line)),
+ case T of
+ [] -> R;
+ _ -> get_reason(T, R ++ ", ")
+ end
+ end.
+
+state__add_race_warning(State, RaceWarn, RaceWarnTag, WarningInfo) ->
+ case RaceWarn of
+ no_race -> State;
+ _Else ->
+ Races = dialyzer_dataflow:state__get_races(State),
+ Warn = {RaceWarnTag, WarningInfo, RaceWarn},
+ dialyzer_dataflow:state__put_races(add_race_warning(Warn, Races), State)
+ end.
+
+%%% ===========================================================================
+%%%
+%%% Record Interfaces
+%%%
+%%% ===========================================================================
+
+-spec beg_clause_new(var_to_map1(), var_to_map1(), cerl:cerl()) ->
+ #beg_clause{}.
+
+beg_clause_new(Arg, Pats, Guard) ->
+ #beg_clause{arg = Arg, pats = Pats, guard = Guard}.
+
+-spec cleanup(races()) -> races().
+
+cleanup(#races{race_list = RaceList}) ->
+ #races{race_list = RaceList}.
+
+-spec end_case_new([#end_clause{}]) -> #end_case{}.
+
+end_case_new(Clauses) ->
+ #end_case{clauses = Clauses}.
+
+-spec end_clause_new(var_to_map1(), var_to_map1(), cerl:cerl()) ->
+ #end_clause{}.
+
+end_clause_new(Arg, Pats, Guard) ->
+ #end_clause{arg = Arg, pats = Pats, guard = Guard}.
+
+-spec get_curr_fun(races()) -> dialyzer_callgraph:mfa_or_funlbl().
+
+get_curr_fun(#races{curr_fun = CurrFun}) ->
+ CurrFun.
+
+-spec get_curr_fun_args(races()) -> core_args().
+
+get_curr_fun_args(#races{curr_fun_args = CurrFunArgs}) ->
+ CurrFunArgs.
+
+-spec get_new_table(races()) -> table().
+
+get_new_table(#races{new_table = Table}) ->
+ Table.
+
+-spec get_race_analysis(races()) -> boolean().
+
+get_race_analysis(#races{race_analysis = RaceAnalysis}) ->
+ RaceAnalysis.
+
+-spec get_race_list(races()) -> code().
+
+get_race_list(#races{race_list = RaceList}) ->
+ RaceList.
+
+-spec get_race_list_size(races()) -> non_neg_integer().
+
+get_race_list_size(#races{race_list_size = RaceListSize}) ->
+ RaceListSize.
+
+-spec get_race_list_and_size(races()) -> {code(), non_neg_integer()}.
+
+get_race_list_and_size(#races{race_list = RaceList,
+ race_list_size = RaceListSize}) ->
+ {RaceList, RaceListSize}.
+
+-spec let_tag_new(var_to_map1(), var_to_map1()) -> #let_tag{}.
+
+let_tag_new(Var, Arg) ->
+ #let_tag{var = Var, arg = Arg}.
+
+-spec new() -> races().
+
+new() -> #races{}.
+
+-spec put_curr_fun(dialyzer_callgraph:mfa_or_funlbl(), label(), races()) ->
+ races().
+
+put_curr_fun(CurrFun, CurrFunLabel, Races) ->
+ Races#races{curr_fun = CurrFun,
+ curr_fun_label = CurrFunLabel,
+ curr_fun_args = empty}.
+
+-spec put_fun_args(core_args(), races()) -> races().
+
+put_fun_args(Args, #races{curr_fun_args = CurrFunArgs} = Races) ->
+ case CurrFunArgs of
+ empty -> Races#races{curr_fun_args = Args};
+ _Other -> Races
+ end.
+
+-spec put_race_analysis(boolean(), races()) ->
+ races().
+
+put_race_analysis(Analysis, Races) ->
+ Races#races{race_analysis = Analysis}.
+
+-spec put_race_list(code(), non_neg_integer(), races()) ->
+ races().
+
+put_race_list(RaceList, RaceListSize, Races) ->
+ Races#races{race_list = RaceList, race_list_size = RaceListSize}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/recrec/erl_types.erl b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/erl_types.erl
new file mode 100644
index 0000000000..449bf4cbb6
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/recrec/erl_types.erl
@@ -0,0 +1,5741 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% %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%
+%%
+%% ======================================================================
+%% Copyright (C) 2000-2003 Richard Carlsson
+%%
+%% ======================================================================
+%% Provides a representation of Erlang types.
+%%
+%% The initial author of this file is Richard Carlsson (2000-2004).
+%% In July 2006, the type representation was totally re-designed by
+%% Tobias Lindahl. This is the representation which is used currently.
+%% In late 2008, Manouk Manoukian and Kostis Sagonas added support for
+%% opaque types to the structure-based representation of types.
+%% During February and March 2009, Kostis Sagonas significantly
+%% cleaned up the type representation and added spec declarations.
+%%
+%% ======================================================================
+
+-module(erl_types).
+
+-export([any_none/1,
+ any_none_or_unit/1,
+ lookup_record/3,
+ max/2,
+ min/2,
+ number_max/1, number_max/2,
+ number_min/1, number_min/2,
+ t_abstract_records/2,
+ t_any/0,
+ t_arity/0,
+ t_atom/0,
+ t_atom/1,
+ t_atoms/1,
+ t_atom_vals/1, t_atom_vals/2,
+ t_binary/0,
+ t_bitstr/0,
+ t_bitstr/2,
+ t_bitstr_base/1,
+ t_bitstr_concat/1,
+ t_bitstr_concat/2,
+ t_bitstr_match/2,
+ t_bitstr_unit/1,
+ t_bitstrlist/0,
+ t_boolean/0,
+ t_byte/0,
+ t_char/0,
+ t_collect_vars/1,
+ t_cons/0,
+ t_cons/2,
+ t_cons_hd/1, t_cons_hd/2,
+ t_cons_tl/1, t_cons_tl/2,
+ t_contains_opaque/1, t_contains_opaque/2,
+ t_decorate_with_opaque/3,
+ t_elements/1,
+ t_find_opaque_mismatch/3,
+ t_find_unknown_opaque/3,
+ t_fixnum/0,
+ t_map/2,
+ t_non_neg_fixnum/0,
+ t_pos_fixnum/0,
+ t_float/0,
+ t_var_names/1,
+ t_form_to_string/1,
+ t_from_form/6,
+ t_from_form_without_remote/3,
+ t_check_record_fields/6,
+ t_from_range/2,
+ t_from_range_unsafe/2,
+ t_from_term/1,
+ t_fun/0,
+ t_fun/1,
+ t_fun/2,
+ t_fun_args/1, t_fun_args/2,
+ t_fun_arity/1, t_fun_arity/2,
+ t_fun_range/1, t_fun_range/2,
+ t_has_opaque_subtype/2,
+ t_has_var/1,
+ t_identifier/0,
+ %% t_improper_list/2,
+ t_inf/1,
+ t_inf/2,
+ t_inf/3,
+ t_inf_lists/2,
+ t_inf_lists/3,
+ t_integer/0,
+ t_integer/1,
+ t_non_neg_integer/0,
+ t_pos_integer/0,
+ t_integers/1,
+ t_iodata/0,
+ t_iolist/0,
+ t_is_any/1,
+ t_is_atom/1, t_is_atom/2,
+ t_is_any_atom/2, t_is_any_atom/3,
+ t_is_binary/1, t_is_binary/2,
+ t_is_bitstr/1, t_is_bitstr/2,
+ t_is_bitwidth/1,
+ t_is_boolean/1, t_is_boolean/2,
+ %% t_is_byte/1,
+ %% t_is_char/1,
+ t_is_cons/1, t_is_cons/2,
+ t_is_equal/2,
+ t_is_fixnum/1,
+ t_is_float/1, t_is_float/2,
+ t_is_fun/1, t_is_fun/2,
+ t_is_instance/2,
+ t_is_integer/1, t_is_integer/2,
+ t_is_list/1,
+ t_is_map/1,
+ t_is_map/2,
+ t_is_matchstate/1,
+ t_is_nil/1, t_is_nil/2,
+ t_is_non_neg_integer/1,
+ t_is_none/1,
+ t_is_none_or_unit/1,
+ t_is_number/1, t_is_number/2,
+ t_is_opaque/1, t_is_opaque/2,
+ t_is_pid/1, t_is_pid/2,
+ 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,
+ t_is_unit/1,
+ t_is_var/1,
+ t_limit/2,
+ t_list/0,
+ t_list/1,
+ t_list_elements/1, t_list_elements/2,
+ 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,
+ t_matchstate_slot/2,
+ t_matchstate_slots/1,
+ t_matchstate_update_present/2,
+ t_matchstate_update_slot/3,
+ t_mfa/0,
+ t_module/0,
+ t_nil/0,
+ t_node/0,
+ t_none/0,
+ t_nonempty_list/0,
+ t_nonempty_list/1,
+ t_nonempty_string/0,
+ t_number/0,
+ t_number/1,
+ t_number_vals/1, t_number_vals/2,
+ t_opaque_from_records/1,
+ t_opaque_structure/1,
+ t_pid/0,
+ t_port/0,
+ t_maybe_improper_list/0,
+ %% 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,
+ t_subtract/2,
+ t_subtract_list/2,
+ t_sup/1,
+ t_sup/2,
+ t_timeout/0,
+ t_to_string/1,
+ t_to_string/2,
+ t_to_tlist/1,
+ t_tuple/0,
+ t_tuple/1,
+ t_tuple_args/1, t_tuple_args/2,
+ t_tuple_size/1, t_tuple_size/2,
+ t_tuple_sizes/1,
+ t_tuple_subtypes/1,
+ t_tuple_subtypes/2,
+ t_unify/2,
+ t_unit/0,
+ t_unopaque/1, t_unopaque/2,
+ t_var/1,
+ t_var_name/1,
+ %% t_assign_variables_to_subtype/2,
+ type_is_defined/4,
+ record_field_diffs_to_string/2,
+ subst_all_vars_to_any/1,
+ lift_list_to_pos_empty/1, lift_list_to_pos_empty/2,
+ is_opaque_type/2,
+ is_erl_type/1,
+ atom_to_string/1,
+ var_table__new/0,
+ cache__new/0,
+ map_pairwise_merge/3
+ ]).
+
+%%-define(DO_ERL_TYPES_TEST, true).
+-compile({no_auto_import,[min/2,max/2]}).
+
+-ifdef(DO_ERL_TYPES_TEST).
+-export([test/0]).
+-else.
+-define(NO_UNUSED, true).
+-endif.
+
+-ifndef(NO_UNUSED).
+-export([t_is_identifier/1]).
+-endif.
+
+-export_type([erl_type/0, opaques/0, type_table/0, var_table/0, cache/0]).
+
+%%-define(DEBUG, true).
+
+-ifdef(DEBUG).
+-define(debug(__A), __A).
+-else.
+-define(debug(__A), ok).
+-endif.
+
+%%=============================================================================
+%%
+%% Definition of the type structure
+%%
+%%=============================================================================
+
+%%-----------------------------------------------------------------------------
+%% Limits
+%%
+
+-define(REC_TYPE_LIMIT, 2).
+-define(EXPAND_DEPTH, 16).
+-define(EXPAND_LIMIT, 10000).
+
+-define(TUPLE_TAG_LIMIT, 5).
+-define(TUPLE_ARITY_LIMIT, 8).
+-define(SET_LIMIT, 13).
+-define(MAX_BYTE, 255).
+-define(MAX_CHAR, 16#10ffff).
+
+-define(UNIT_MULTIPLIER, 8).
+
+-define(TAG_IMMED1_SIZE, 4).
+-define(BITS, (erlang:system_info(wordsize) * 8) - ?TAG_IMMED1_SIZE).
+
+-define(MAX_TUPLE_SIZE, (1 bsl 10)).
+
+%%-----------------------------------------------------------------------------
+%% Type tags and qualifiers
+%%
+
+-define(atom_tag, atom).
+-define(binary_tag, binary).
+-define(function_tag, function).
+-define(identifier_tag, identifier).
+-define(list_tag, list).
+-define(map_tag, map).
+-define(matchstate_tag, matchstate).
+-define(nil_tag, nil).
+-define(number_tag, number).
+-define(opaque_tag, opaque).
+-define(product_tag, product).
+-define(tuple_set_tag, tuple_set).
+-define(tuple_tag, tuple).
+-define(union_tag, union).
+-define(var_tag, var).
+
+-type tag() :: ?atom_tag | ?binary_tag | ?function_tag | ?identifier_tag
+ | ?list_tag | ?map_tag | ?matchstate_tag | ?nil_tag | ?number_tag
+ | ?opaque_tag | ?product_tag
+ | ?tuple_tag | ?tuple_set_tag | ?union_tag | ?var_tag.
+
+-define(float_qual, float).
+-define(integer_qual, integer).
+-define(nonempty_qual, nonempty).
+-define(pid_qual, pid).
+-define(port_qual, port).
+-define(reference_qual, reference).
+-define(unknown_qual, unknown).
+
+-type qual() :: ?float_qual | ?integer_qual | ?nonempty_qual | ?pid_qual
+ | ?port_qual | ?reference_qual | ?unknown_qual | {_, _}.
+
+%%-----------------------------------------------------------------------------
+%% The type representation
+%%
+
+-define(any, any).
+-define(none, none).
+-define(unit, unit).
+%% Generic constructor - elements can be many things depending on the tag.
+-record(c, {tag :: tag(),
+ elements = [] :: term(),
+ qualifier = ?unknown_qual :: qual()}).
+
+-opaque erl_type() :: ?any | ?none | ?unit | #c{}.
+
+%%-----------------------------------------------------------------------------
+%% Auxiliary types and convenient macros
+%%
+
+-type parse_form() :: erl_parse:abstract_type().
+-type rng_elem() :: 'pos_inf' | 'neg_inf' | integer().
+
+-record(int_set, {set :: [integer()]}).
+-record(int_rng, {from :: rng_elem(), to :: rng_elem()}).
+%% Note: the definition of #opaque{} was changed to 'mod' and 'name';
+%% it used to be an ordsets of {Mod, Name} pairs. The Dialyzer version
+%% was updated to 2.7 due to this change.
+-record(opaque, {mod :: module(), name :: atom(),
+ args = [] :: [erl_type()], struct :: erl_type()}).
+
+-define(atom(Set), #c{tag=?atom_tag, elements=Set}).
+-define(bitstr(Unit, Base), #c{tag=?binary_tag, elements=[Unit,Base]}).
+-define(float, ?number(?any, ?float_qual)).
+-define(function(Domain, Range), #c{tag=?function_tag,
+ elements=[Domain, Range]}).
+-define(identifier(Types), #c{tag=?identifier_tag, elements=Types}).
+-define(integer(Types), ?number(Types, ?integer_qual)).
+-define(int_range(From, To), ?integer(#int_rng{from=From, to=To})).
+-define(int_set(Set), ?integer(#int_set{set=Set})).
+-define(list(Types, Term, Size), #c{tag=?list_tag, elements=[Types,Term],
+ qualifier=Size}).
+-define(nil, #c{tag=?nil_tag}).
+-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,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,
+ qualifier={Arity, Qual}}).
+-define(tuple_set(Tuples), #c{tag=?tuple_set_tag, elements=Tuples}).
+-define(var(Id), #c{tag=?var_tag, elements=Id}).
+
+-define(matchstate(P, Slots), #c{tag=?matchstate_tag, elements=[P,Slots]}).
+-define(any_matchstate, ?matchstate(t_bitstr(), ?any)).
+
+-define(byte, ?int_range(0, ?MAX_BYTE)).
+-define(char, ?int_range(0, ?MAX_CHAR)).
+-define(integer_pos, ?int_range(1, pos_inf)).
+-define(integer_non_neg, ?int_range(0, pos_inf)).
+-define(integer_neg, ?int_range(neg_inf, -1)).
+
+-type opaques() :: [erl_type()] | 'universe'.
+
+-type record_key() :: {'record', atom()}.
+-type type_key() :: {'type' | 'opaque', mfa()}.
+-type record_value() :: [{atom(), erl_parse:abstract_expr(), erl_type()}].
+-type type_value() :: {{module(), {file:name(), erl_anno:line()},
+ erl_parse:abstract_type(), ArgNames :: [atom()]},
+ erl_type()}.
+-type type_table() :: dict:dict(record_key() | type_key(),
+ record_value() | type_value()).
+
+-opaque var_table() :: #{atom() => erl_type()}.
+
+%%-----------------------------------------------------------------------------
+%% Unions
+%%
+
+-define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_]=List}).
+
+-define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none])).
+-define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none])).
+-define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none])).
+-define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none])).
+-define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none])).
+-define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none])).
+-define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none])).
+-define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none])).
+-define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none])).
+-define(map_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T])).
+-define(integer_union(T), ?number_union(T)).
+-define(float_union(T), ?number_union(T)).
+-define(nil_union(T), ?list_union(T)).
+
+
+%%=============================================================================
+%%
+%% Primitive operations such as type construction and type tests
+%%
+%%=============================================================================
+
+%%-----------------------------------------------------------------------------
+%% Top and bottom
+%%
+
+-spec t_any() -> erl_type().
+
+t_any() ->
+ ?any.
+
+-spec t_is_any(erl_type()) -> boolean().
+
+t_is_any(Type) ->
+ do_opaque(Type, 'universe', fun is_any/1).
+
+is_any(?any) -> true;
+is_any(_) -> false.
+
+-spec t_none() -> erl_type().
+
+t_none() ->
+ ?none.
+
+-spec t_is_none(erl_type()) -> boolean().
+
+t_is_none(?none) -> true;
+t_is_none(_) -> false.
+
+%%-----------------------------------------------------------------------------
+%% Opaque types
+%%
+
+-spec t_opaque(module(), atom(), [_], erl_type()) -> erl_type().
+
+t_opaque(Mod, Name, Args, Struct) ->
+ O = #opaque{mod = Mod, name = Name, args = Args, struct = Struct},
+ ?opaque(set_singleton(O)).
+
+-spec t_is_opaque(erl_type(), [erl_type()]) -> boolean().
+
+t_is_opaque(?opaque(_) = Type, Opaques) ->
+ not is_opaque_type(Type, Opaques);
+t_is_opaque(_Type, _Opaques) -> false.
+
+-spec t_is_opaque(erl_type()) -> boolean().
+
+t_is_opaque(?opaque(_)) -> true;
+t_is_opaque(_) -> false.
+
+-spec t_has_opaque_subtype(erl_type(), opaques()) -> boolean().
+
+t_has_opaque_subtype(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun has_opaque_subtype/1).
+
+has_opaque_subtype(?union(Ts)) ->
+ lists:any(fun t_is_opaque/1, Ts);
+has_opaque_subtype(T) ->
+ t_is_opaque(T).
+
+-spec t_opaque_structure(erl_type()) -> erl_type().
+
+t_opaque_structure(?opaque(Elements)) ->
+ t_sup([Struct || #opaque{struct = Struct} <- ordsets:to_list(Elements)]).
+
+-spec t_contains_opaque(erl_type()) -> boolean().
+
+t_contains_opaque(Type) ->
+ t_contains_opaque(Type, []).
+
+%% Returns 'true' iff there is an opaque type that is *not* one of
+%% the types of the second argument.
+
+-spec t_contains_opaque(erl_type(), [erl_type()]) -> boolean().
+
+t_contains_opaque(?any, _Opaques) -> false;
+t_contains_opaque(?none, _Opaques) -> false;
+t_contains_opaque(?unit, _Opaques) -> false;
+t_contains_opaque(?atom(_Set), _Opaques) -> false;
+t_contains_opaque(?bitstr(_Unit, _Base), _Opaques) -> false;
+t_contains_opaque(?float, _Opaques) -> false;
+t_contains_opaque(?function(Domain, Range), Opaques) ->
+ t_contains_opaque(Domain, Opaques)
+ orelse t_contains_opaque(Range, Opaques);
+t_contains_opaque(?identifier(_Types), _Opaques) -> false;
+t_contains_opaque(?integer(_Types), _Opaques) -> false;
+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_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;
+t_contains_opaque(?opaque(_)=T, Opaques) ->
+ not is_opaque_type(T, Opaques)
+ orelse t_contains_opaque(t_opaque_structure(T));
+t_contains_opaque(?product(Types), Opaques) ->
+ list_contains_opaque(Types, Opaques);
+t_contains_opaque(?tuple(?any, _, _), _Opaques) -> false;
+t_contains_opaque(?tuple(Types, _, _), Opaques) ->
+ list_contains_opaque(Types, Opaques);
+t_contains_opaque(?tuple_set(_Set) = T, Opaques) ->
+ list_contains_opaque(t_tuple_subtypes(T), Opaques);
+t_contains_opaque(?union(List), Opaques) ->
+ list_contains_opaque(List, Opaques);
+t_contains_opaque(?var(_Id), _Opaques) -> false.
+
+-spec list_contains_opaque([erl_type()], [erl_type()]) -> boolean().
+
+list_contains_opaque(List, Opaques) ->
+ lists:any(fun(E) -> t_contains_opaque(E, Opaques) end, List).
+
+%% t_find_opaque_mismatch/2 of two types should only be used if their
+%% t_inf is t_none() due to some opaque type violation.
+%%
+%% The first argument of the function is the pattern and its second
+%% argument the type we are matching against the pattern.
+
+-spec t_find_opaque_mismatch(erl_type(), erl_type(), [erl_type()]) ->
+ 'error' | {'ok', erl_type(), erl_type()}.
+
+t_find_opaque_mismatch(T1, T2, Opaques) ->
+ t_find_opaque_mismatch(T1, T2, T2, Opaques).
+
+t_find_opaque_mismatch(?any, _Type, _TopType, _Opaques) -> error;
+t_find_opaque_mismatch(?none, _Type, _TopType, _Opaques) -> error;
+t_find_opaque_mismatch(?list(T1, Tl1, _), ?list(T2, Tl2, _), TopType, Opaques) ->
+ t_find_opaque_mismatch_ordlists([T1, Tl1], [T2, Tl2], TopType, Opaques);
+t_find_opaque_mismatch(T1, ?opaque(_) = T2, TopType, Opaques) ->
+ case is_opaque_type(T2, Opaques) of
+ false -> {ok, TopType, T2};
+ true ->
+ t_find_opaque_mismatch(T1, t_opaque_structure(T2), TopType, Opaques)
+ end;
+t_find_opaque_mismatch(?opaque(_) = T1, T2, TopType, Opaques) ->
+ %% The generated message is somewhat misleading:
+ case is_opaque_type(T1, Opaques) of
+ false -> {ok, TopType, T1};
+ true ->
+ t_find_opaque_mismatch(t_opaque_structure(T1), T2, TopType, Opaques)
+ end;
+t_find_opaque_mismatch(?product(T1), ?product(T2), TopType, Opaques) ->
+ t_find_opaque_mismatch_ordlists(T1, T2, TopType, Opaques);
+t_find_opaque_mismatch(?tuple(T1, Arity, _), ?tuple(T2, Arity, _),
+ TopType, Opaques) ->
+ t_find_opaque_mismatch_ordlists(T1, T2, TopType, Opaques);
+t_find_opaque_mismatch(?tuple(_, _, _) = T1, ?tuple_set(_) = T2,
+ TopType, Opaques) ->
+ Tuples1 = t_tuple_subtypes(T1),
+ Tuples2 = t_tuple_subtypes(T2),
+ t_find_opaque_mismatch_lists(Tuples1, Tuples2, TopType, Opaques);
+t_find_opaque_mismatch(T1, ?union(U2), TopType, Opaques) ->
+ t_find_opaque_mismatch_lists([T1], U2, TopType, Opaques);
+t_find_opaque_mismatch(_T1, _T2, _TopType, _Opaques) -> error.
+
+t_find_opaque_mismatch_ordlists(L1, L2, TopType, Opaques) ->
+ List = lists:zipwith(fun(T1, T2) ->
+ t_find_opaque_mismatch(T1, T2, TopType, Opaques)
+ end, L1, L2),
+ t_find_opaque_mismatch_list(List).
+
+t_find_opaque_mismatch_lists(L1, L2, _TopType, Opaques) ->
+ List = [t_find_opaque_mismatch(T1, T2, T2, Opaques) || T1 <- L1, T2 <- L2],
+ t_find_opaque_mismatch_list(List).
+
+t_find_opaque_mismatch_list([]) -> error;
+t_find_opaque_mismatch_list([H|T]) ->
+ case H of
+ {ok, _T1, _T2} -> H;
+ error -> t_find_opaque_mismatch_list(T)
+ end.
+
+-spec t_find_unknown_opaque(erl_type(), erl_type(), opaques()) ->
+ [pos_integer()].
+
+%% The nice thing about using two types and t_inf() as compared to
+%% calling t_contains_opaque/2 is that the traversal stops when
+%% there is a mismatch which means that unknown opaque types "below"
+%% the mismatch are not found.
+t_find_unknown_opaque(_T1, _T2, 'universe') -> [];
+t_find_unknown_opaque(T1, T2, Opaques) ->
+ try t_inf(T1, T2, {match, Opaques}) of
+ _ -> []
+ catch throw:{pos, Ns} -> Ns
+ end.
+
+-spec t_decorate_with_opaque(erl_type(), erl_type(), [erl_type()]) -> erl_type().
+
+%% The first argument can contain opaque types. The second argument
+%% is assumed to be taken from the contract.
+
+t_decorate_with_opaque(T1, T2, Opaques) ->
+ case t_is_equal(T1, T2) orelse not t_contains_opaque(T2) of
+ true -> T1;
+ false ->
+ T = t_inf(T1, T2),
+ case t_contains_opaque(T) of
+ false -> T1;
+ true ->
+ R = decorate(T1, T, Opaques),
+ ?debug(case catch t_is_equal(t_unopaque(R), t_unopaque(T1)) of
+ true -> ok;
+ false ->
+ io:format("T1 = ~p,\n", [T1]),
+ io:format("T2 = ~p,\n", [T2]),
+ io:format("O = ~p,\n", [Opaques]),
+ io:format("erl_types:t_decorate_with_opaque(T1,T2,O).\n"),
+ throw({error, "Failed to handle opaque types"})
+ end),
+ R
+ end
+ end.
+
+decorate(Type, ?none, _Opaques) -> Type;
+decorate(?function(Domain, Range), ?function(D, R), Opaques) ->
+ ?function(decorate(Domain, D, Opaques), decorate(Range, R, Opaques));
+decorate(?list(Types, Tail, Size), ?list(Ts, Tl, _Sz), Opaques) ->
+ ?list(decorate(Types, Ts, Opaques), decorate(Tail, Tl, Opaques), Size);
+decorate(?product(Types), ?product(Ts), Opaques) ->
+ ?product(list_decorate(Types, Ts, Opaques));
+decorate(?tuple(_, _, _)=T, ?tuple(?any, _, _), _Opaques) -> T;
+decorate(?tuple(?any, _, _)=T, ?tuple(_, _, _), _Opaques) -> T;
+decorate(?tuple(Types, Arity, Tag), ?tuple(Ts, Arity, _), Opaques) ->
+ ?tuple(list_decorate(Types, Ts, Opaques), Arity, Tag);
+decorate(?tuple_set(List), ?tuple(_, Arity, _) = T, Opaques) ->
+ decorate_tuple_sets(List, [{Arity, [T]}], Opaques);
+decorate(?tuple_set(List), ?tuple_set(L), Opaques) ->
+ decorate_tuple_sets(List, L, Opaques);
+decorate(?union(List), T, Opaques) when T =/= ?any ->
+ ?union(L) = force_union(T),
+ union_decorate(List, L, Opaques);
+decorate(?opaque(_)=T, _, _Opaques) -> T;
+decorate(T, ?union(L), Opaques) when T =/= ?any ->
+ ?union(List) = force_union(T),
+ union_decorate(List, L, Opaques);
+decorate(Type, ?opaque(_)=T, Opaques) ->
+ decorate_with_opaque(Type, T, Opaques);
+decorate(Type, _T, _Opaques) -> Type.
+
+%% Note: it is important that #opaque.struct is a subtype of the
+%% opaque type.
+decorate_with_opaque(Type, ?opaque(Set2), Opaques) ->
+ case decoration(set_to_list(Set2), Type, Opaques, [], false) of
+ {[], false} -> Type;
+ {List, All} when List =/= [] ->
+ NewType = ?opaque(ordsets:from_list(List)),
+ case All of
+ true -> NewType;
+ false -> t_sup(NewType, Type)
+ end
+ end.
+
+decoration([#opaque{struct = S} = Opaque|OpaqueTypes], Type, Opaques,
+ NewOpaqueTypes0, All) ->
+ IsOpaque = is_opaque_type2(Opaque, Opaques),
+ I = t_inf(Type, S),
+ case not IsOpaque orelse t_is_none(I) of
+ true -> decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes0, All);
+ false ->
+ NewOpaque = Opaque#opaque{struct = decorate(I, S, Opaques)},
+ NewAll = All orelse t_is_equal(I, Type),
+ NewOpaqueTypes = [NewOpaque|NewOpaqueTypes0],
+ decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes, NewAll)
+ end;
+decoration([], _Type, _Opaques, NewOpaqueTypes, All) ->
+ {NewOpaqueTypes, All}.
+
+-spec list_decorate([erl_type()], [erl_type()], opaques()) -> [erl_type()].
+
+list_decorate(List, L, Opaques) ->
+ [decorate(Elem, E, Opaques) || {Elem, E} <- lists:zip(List, L)].
+
+union_decorate(U1, U2, Opaques) ->
+ Union = union_decorate(U1, U2, Opaques, 0, []),
+ [A,B,F,I,L,N,T,M,_,Map] = U1,
+ [_,_,_,_,_,_,_,_,Opaque,_] = U2,
+ List = [A,B,F,I,L,N,T,M,Map],
+ DecList = [Dec ||
+ E <- List,
+ not t_is_none(E),
+ not t_is_none(Dec = decorate(E, Opaque, Opaques))],
+ t_sup([Union|DecList]).
+
+union_decorate([?none|Left1], [_|Left2], Opaques, N, Acc) ->
+ union_decorate(Left1, Left2, Opaques, N, [?none|Acc]);
+union_decorate([T1|Left1], [?none|Left2], Opaques, N, Acc) ->
+ union_decorate(Left1, Left2, Opaques, N+1, [T1|Acc]);
+union_decorate([T1|Left1], [T2|Left2], Opaques, N, Acc) ->
+ union_decorate(Left1, Left2, Opaques, N+1, [decorate(T1, T2, Opaques)|Acc]);
+union_decorate([], [], _Opaques, N, Acc) ->
+ if N =:= 0 -> ?none;
+ N =:= 1 ->
+ [Type] = [T || T <- Acc, T =/= ?none],
+ Type;
+ N >= 2 -> ?union(lists:reverse(Acc))
+ end.
+
+decorate_tuple_sets(List, L, Opaques) ->
+ decorate_tuple_sets(List, L, Opaques, []).
+
+decorate_tuple_sets([{Arity, Tuples}|List], [{Arity, Ts}|L], Opaques, Acc) ->
+ DecTs = decorate_tuples_in_sets(Tuples, Ts, Opaques),
+ decorate_tuple_sets(List, L, Opaques, [{Arity, DecTs}|Acc]);
+decorate_tuple_sets([ArTup|List], L, Opaques, Acc) ->
+ decorate_tuple_sets(List, L, Opaques, [ArTup|Acc]);
+decorate_tuple_sets([], _L, _Opaques, Acc) ->
+ ?tuple_set(lists:reverse(Acc)).
+
+decorate_tuples_in_sets([?tuple(Elements, _, ?any)], Ts, Opaques) ->
+ NewList = [list_decorate(Elements, Es, Opaques) || ?tuple(Es, _, _) <- Ts],
+ case t_sup([t_tuple(Es) || Es <- NewList]) of
+ ?tuple_set([{_Arity, Tuples}]) -> Tuples;
+ ?tuple(_, _, _)=Tuple -> [Tuple]
+ end;
+decorate_tuples_in_sets(Tuples, Ts, Opaques) ->
+ decorate_tuples_in_sets(Tuples, Ts, Opaques, []).
+
+decorate_tuples_in_sets([?tuple(Elements, Arity, Tag1) = T1|Tuples] = L1,
+ [?tuple(Es, Arity, Tag2)|Ts] = L2, Opaques, Acc) ->
+ if
+ Tag1 < Tag2 -> decorate_tuples_in_sets(Tuples, L2, Opaques, [T1|Acc]);
+ Tag1 > Tag2 -> decorate_tuples_in_sets(L1, Ts, Opaques, Acc);
+ Tag1 =:= Tag2 ->
+ NewElements = list_decorate(Elements, Es, Opaques),
+ NewAcc = [?tuple(NewElements, Arity, Tag1)|Acc],
+ decorate_tuples_in_sets(Tuples, Ts, Opaques, NewAcc)
+ end;
+decorate_tuples_in_sets([T1|Tuples], L2, Opaques, Acc) ->
+ decorate_tuples_in_sets(Tuples, L2, Opaques, [T1|Acc]);
+decorate_tuples_in_sets([], _L, _Opaques, Acc) ->
+ lists:reverse(Acc).
+
+-spec t_opaque_from_records(type_table()) -> [erl_type()].
+
+t_opaque_from_records(RecDict) ->
+ OpaqueRecDict =
+ dict:filter(fun(Key, _Value) ->
+ case Key of
+ {opaque, _Name, _Arity} -> true;
+ _ -> false
+ end
+ end, RecDict),
+ OpaqueTypeDict =
+ dict:map(fun({opaque, Name, _Arity},
+ {{Module, _FileLine, _Form, ArgNames}, _Type}) ->
+ %% Args = args_to_types(ArgNames),
+ %% List = lists:zip(ArgNames, Args),
+ %% TmpVarTab = maps:to_list(List),
+ %% Rep = t_from_form(Type, RecDict, TmpVarTab),
+ Rep = t_any(), % not used for anything right now
+ Args = [t_any() || _ <- ArgNames],
+ t_opaque(Module, Name, Args, Rep)
+ end, OpaqueRecDict),
+ [OpaqueType || {_Key, OpaqueType} <- dict:to_list(OpaqueTypeDict)].
+
+%% Decompose opaque instances of type arg2 to structured types, in arg1
+%% XXX: Same as t_unopaque
+-spec t_struct_from_opaque(erl_type(), [erl_type()]) -> erl_type().
+
+t_struct_from_opaque(?function(Domain, Range), Opaques) ->
+ ?function(t_struct_from_opaque(Domain, Opaques),
+ t_struct_from_opaque(Range, Opaques));
+t_struct_from_opaque(?list(Types, Term, Size), Opaques) ->
+ ?list(t_struct_from_opaque(Types, Opaques),
+ t_struct_from_opaque(Term, Opaques), Size);
+t_struct_from_opaque(?opaque(_) = T, Opaques) ->
+ case is_opaque_type(T, Opaques) of
+ true -> t_opaque_structure(T);
+ false -> T
+ end;
+t_struct_from_opaque(?product(Types), Opaques) ->
+ ?product(list_struct_from_opaque(Types, Opaques));
+t_struct_from_opaque(?tuple(?any, _, _) = T, _Opaques) -> T;
+t_struct_from_opaque(?tuple(Types, Arity, Tag), Opaques) ->
+ ?tuple(list_struct_from_opaque(Types, Opaques), Arity, Tag);
+t_struct_from_opaque(?tuple_set(Set), Opaques) ->
+ NewSet = [{Sz, [t_struct_from_opaque(T, Opaques) || T <- Tuples]}
+ || {Sz, Tuples} <- Set],
+ ?tuple_set(NewSet);
+t_struct_from_opaque(?union(List), Opaques) ->
+ t_sup(list_struct_from_opaque(List, Opaques));
+t_struct_from_opaque(Type, _Opaques) -> Type.
+
+list_struct_from_opaque(Types, Opaques) ->
+ [t_struct_from_opaque(Type, Opaques) || Type <- Types].
+
+%%-----------------------------------------------------------------------------
+
+-type mod_records() :: dict:dict(module(), type_table()).
+
+%%-----------------------------------------------------------------------------
+%% Unit type. Signals non termination.
+%%
+
+-spec t_unit() -> erl_type().
+
+t_unit() ->
+ ?unit.
+
+-spec t_is_unit(erl_type()) -> boolean().
+
+t_is_unit(?unit) -> true;
+t_is_unit(_) -> false.
+
+-spec t_is_none_or_unit(erl_type()) -> boolean().
+
+t_is_none_or_unit(?none) -> true;
+t_is_none_or_unit(?unit) -> true;
+t_is_none_or_unit(_) -> false.
+
+%%-----------------------------------------------------------------------------
+%% Atoms and the derived type boolean
+%%
+
+-spec t_atom() -> erl_type().
+
+t_atom() ->
+ ?atom(?any).
+
+-spec t_atom(atom()) -> erl_type().
+
+t_atom(A) when is_atom(A) ->
+ ?atom(set_singleton(A)).
+
+-spec t_atoms([atom()]) -> erl_type().
+
+t_atoms(List) when is_list(List) ->
+ t_sup([t_atom(A) || A <- List]).
+
+-spec t_atom_vals(erl_type()) -> 'unknown' | [atom(),...].
+
+t_atom_vals(Type) ->
+ t_atom_vals(Type, 'universe').
+
+-spec t_atom_vals(erl_type(), opaques()) -> 'unknown' | [atom(),...].
+
+t_atom_vals(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun atom_vals/1).
+
+atom_vals(?atom(?any)) -> unknown;
+atom_vals(?atom(Set)) -> set_to_list(Set);
+atom_vals(?opaque(_)) -> unknown;
+atom_vals(Other) ->
+ ?atom(_) = Atm = t_inf(t_atom(), Other),
+ atom_vals(Atm).
+
+-spec t_is_atom(erl_type()) -> boolean().
+
+t_is_atom(Type) ->
+ t_is_atom(Type, 'universe').
+
+-spec t_is_atom(erl_type(), opaques()) -> boolean().
+
+t_is_atom(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_atom1/1).
+
+is_atom1(?atom(_)) -> true;
+is_atom1(_) -> false.
+
+-spec t_is_any_atom(atom(), erl_type()) -> boolean().
+
+t_is_any_atom(Atom, SomeAtomsType) ->
+ t_is_any_atom(Atom, SomeAtomsType, 'universe').
+
+-spec t_is_any_atom(atom(), erl_type(), opaques()) -> boolean().
+
+t_is_any_atom(Atom, SomeAtomsType, Opaques) ->
+ do_opaque(SomeAtomsType, Opaques,
+ fun(AtomsType) -> is_any_atom(Atom, AtomsType) end).
+
+is_any_atom(Atom, ?atom(?any)) when is_atom(Atom) -> false;
+is_any_atom(Atom, ?atom(Set)) when is_atom(Atom) ->
+ set_is_singleton(Atom, Set);
+is_any_atom(Atom, _) when is_atom(Atom) -> false.
+
+%%------------------------------------
+
+-spec t_is_boolean(erl_type()) -> boolean().
+
+t_is_boolean(Type) ->
+ t_is_boolean(Type, 'universe').
+
+-spec t_is_boolean(erl_type(), opaques()) -> boolean().
+
+t_is_boolean(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_boolean/1).
+
+-spec t_boolean() -> erl_type().
+
+t_boolean() ->
+ ?atom(set_from_list([false, true])).
+
+is_boolean(?atom(?any)) -> false;
+is_boolean(?atom(Set)) ->
+ case set_size(Set) of
+ 1 -> set_is_element(true, Set) orelse set_is_element(false, Set);
+ 2 -> set_is_element(true, Set) andalso set_is_element(false, Set);
+ N when is_integer(N), N > 2 -> false
+ end;
+is_boolean(_) -> false.
+
+%%-----------------------------------------------------------------------------
+%% Binaries
+%%
+
+-spec t_binary() -> erl_type().
+
+t_binary() ->
+ ?bitstr(8, 0).
+
+-spec t_is_binary(erl_type()) -> boolean().
+
+t_is_binary(Type) ->
+ t_is_binary(Type, 'universe').
+
+-spec t_is_binary(erl_type(), opaques()) -> boolean().
+
+t_is_binary(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_binary/1).
+
+is_binary(?bitstr(U, B)) ->
+ ((U rem 8) =:= 0) andalso ((B rem 8) =:= 0);
+is_binary(_) -> false.
+
+%%-----------------------------------------------------------------------------
+%% Bitstrings
+%%
+
+-spec t_bitstr() -> erl_type().
+
+t_bitstr() ->
+ ?bitstr(1, 0).
+
+-spec t_bitstr(non_neg_integer(), non_neg_integer()) -> erl_type().
+
+t_bitstr(U, B) ->
+ NewB =
+ if
+ U =:= 0 -> B;
+ B >= (U * (?UNIT_MULTIPLIER + 1)) ->
+ (B rem U) + U * ?UNIT_MULTIPLIER;
+ true ->
+ B
+ end,
+ ?bitstr(U, NewB).
+
+-spec t_bitstr_unit(erl_type()) -> non_neg_integer().
+
+t_bitstr_unit(?bitstr(U, _)) -> U.
+
+-spec t_bitstr_base(erl_type()) -> non_neg_integer().
+
+t_bitstr_base(?bitstr(_, B)) -> B.
+
+-spec t_bitstr_concat([erl_type()]) -> erl_type().
+
+t_bitstr_concat(List) ->
+ t_bitstr_concat_1(List, t_bitstr(0, 0)).
+
+t_bitstr_concat_1([T|Left], Acc) ->
+ t_bitstr_concat_1(Left, t_bitstr_concat(Acc, T));
+t_bitstr_concat_1([], Acc) ->
+ Acc.
+
+-spec t_bitstr_concat(erl_type(), erl_type()) -> erl_type().
+
+t_bitstr_concat(T1, T2) ->
+ T1p = t_inf(t_bitstr(), T1),
+ T2p = t_inf(t_bitstr(), T2),
+ bitstr_concat(t_unopaque(T1p), t_unopaque(T2p)).
+
+-spec t_bitstr_match(erl_type(), erl_type()) -> erl_type().
+
+t_bitstr_match(T1, T2) ->
+ T1p = t_inf(t_bitstr(), T1),
+ T2p = t_inf(t_bitstr(), T2),
+ bitstr_match(t_unopaque(T1p), t_unopaque(T2p)).
+
+-spec t_is_bitstr(erl_type()) -> boolean().
+
+t_is_bitstr(Type) ->
+ t_is_bitstr(Type, 'universe').
+
+-spec t_is_bitstr(erl_type(), opaques()) -> boolean().
+
+t_is_bitstr(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_bitstr/1).
+
+is_bitstr(?bitstr(_, _)) -> true;
+is_bitstr(_) -> false.
+
+%%-----------------------------------------------------------------------------
+%% Matchstates
+%%
+
+-spec t_matchstate() -> erl_type().
+
+t_matchstate() ->
+ ?any_matchstate.
+
+-spec t_matchstate(erl_type(), non_neg_integer()) -> erl_type().
+
+t_matchstate(Init, 0) ->
+ ?matchstate(Init, Init);
+t_matchstate(Init, Max) when is_integer(Max) ->
+ Slots = [Init|[?none || _ <- lists:seq(1, Max)]],
+ ?matchstate(Init, t_product(Slots)).
+
+-spec t_is_matchstate(erl_type()) -> boolean().
+
+t_is_matchstate(?matchstate(_, _)) -> true;
+t_is_matchstate(_) -> false.
+
+-spec t_matchstate_present(erl_type()) -> erl_type().
+
+t_matchstate_present(Type) ->
+ case t_inf(t_matchstate(), Type) of
+ ?matchstate(P, _) -> P;
+ _ -> ?none
+ end.
+
+-spec t_matchstate_slot(erl_type(), non_neg_integer()) -> erl_type().
+
+t_matchstate_slot(Type, Slot) ->
+ RealSlot = Slot + 1,
+ case t_inf(t_matchstate(), Type) of
+ ?matchstate(_, ?any) -> ?any;
+ ?matchstate(_, ?product(Vals)) when length(Vals) >= RealSlot ->
+ lists:nth(RealSlot, Vals);
+ ?matchstate(_, ?product(_)) ->
+ ?none;
+ ?matchstate(_, SlotType) when RealSlot =:= 1 ->
+ SlotType;
+ _ ->
+ ?none
+ end.
+
+-spec t_matchstate_slots(erl_type()) -> erl_type().
+
+t_matchstate_slots(?matchstate(_, Slots)) ->
+ Slots.
+
+-spec t_matchstate_update_present(erl_type(), erl_type()) -> erl_type().
+
+t_matchstate_update_present(New, Type) ->
+ case t_inf(t_matchstate(), Type) of
+ ?matchstate(_, Slots) ->
+ ?matchstate(New, Slots);
+ _ -> ?none
+ end.
+
+-spec t_matchstate_update_slot(erl_type(), erl_type(), non_neg_integer()) -> erl_type().
+
+t_matchstate_update_slot(New, Type, Slot) ->
+ RealSlot = Slot + 1,
+ case t_inf(t_matchstate(), Type) of
+ ?matchstate(Pres, Slots) ->
+ NewSlots =
+ case Slots of
+ ?any ->
+ ?any;
+ ?product(Vals) when length(Vals) >= RealSlot ->
+ NewTuple = setelement(RealSlot, list_to_tuple(Vals), New),
+ NewVals = tuple_to_list(NewTuple),
+ ?product(NewVals);
+ ?product(_) ->
+ ?none;
+ _ when RealSlot =:= 1 ->
+ New;
+ _ ->
+ ?none
+ end,
+ ?matchstate(Pres, NewSlots);
+ _ ->
+ ?none
+ end.
+
+%%-----------------------------------------------------------------------------
+%% Functions
+%%
+
+-spec t_fun() -> erl_type().
+
+t_fun() ->
+ ?function(?any, ?any).
+
+-spec t_fun(erl_type()) -> erl_type().
+
+t_fun(Range) ->
+ ?function(?any, Range).
+
+-spec t_fun([erl_type()] | arity(), erl_type()) -> erl_type().
+
+t_fun(Domain, Range) when is_list(Domain) ->
+ ?function(?product(Domain), Range);
+t_fun(Arity, Range) when is_integer(Arity), 0 =< Arity, Arity =< 255 ->
+ ?function(?product(lists:duplicate(Arity, ?any)), Range).
+
+-spec t_fun_args(erl_type()) -> 'unknown' | [erl_type()].
+
+t_fun_args(Type) ->
+ t_fun_args(Type, 'universe').
+
+-spec t_fun_args(erl_type(), opaques()) -> 'unknown' | [erl_type()].
+
+t_fun_args(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun fun_args/1).
+
+fun_args(?function(?any, _)) ->
+ unknown;
+fun_args(?function(?product(Domain), _)) when is_list(Domain) ->
+ Domain.
+
+-spec t_fun_arity(erl_type()) -> 'unknown' | non_neg_integer().
+
+t_fun_arity(Type) ->
+ t_fun_arity(Type, 'universe').
+
+-spec t_fun_arity(erl_type(), opaques()) -> 'unknown' | non_neg_integer().
+
+t_fun_arity(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun fun_arity/1).
+
+fun_arity(?function(?any, _)) ->
+ unknown;
+fun_arity(?function(?product(Domain), _)) ->
+ length(Domain).
+
+-spec t_fun_range(erl_type()) -> erl_type().
+
+t_fun_range(Type) ->
+ t_fun_range(Type, 'universe').
+
+-spec t_fun_range(erl_type(), opaques()) -> erl_type().
+
+t_fun_range(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun fun_range/1).
+
+fun_range(?function(_, Range)) ->
+ Range.
+
+-spec t_is_fun(erl_type()) -> boolean().
+
+t_is_fun(Type) ->
+ t_is_fun(Type, 'universe').
+
+-spec t_is_fun(erl_type(), opaques()) -> boolean().
+
+t_is_fun(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_fun/1).
+
+is_fun(?function(_, _)) -> true;
+is_fun(_) -> false.
+
+%%-----------------------------------------------------------------------------
+%% Identifiers. Includes ports, pids and refs.
+%%
+
+-spec t_identifier() -> erl_type().
+
+t_identifier() ->
+ ?identifier(?any).
+
+-ifdef(DO_ERL_TYPES_TEST).
+-spec t_is_identifier(erl_type()) -> erl_type().
+
+t_is_identifier(?identifier(_)) -> true;
+t_is_identifier(_) -> false.
+-endif.
+
+%%------------------------------------
+
+-spec t_port() -> erl_type().
+
+t_port() ->
+ ?identifier(set_singleton(?port_qual)).
+
+-spec t_is_port(erl_type()) -> boolean().
+
+t_is_port(Type) ->
+ t_is_port(Type, 'universe').
+
+-spec t_is_port(erl_type(), opaques()) -> boolean().
+
+t_is_port(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_port1/1).
+
+is_port1(?identifier(?any)) -> false;
+is_port1(?identifier(Set)) -> set_is_singleton(?port_qual, Set);
+is_port1(_) -> false.
+
+%%------------------------------------
+
+-spec t_pid() -> erl_type().
+
+t_pid() ->
+ ?identifier(set_singleton(?pid_qual)).
+
+-spec t_is_pid(erl_type()) -> boolean().
+
+t_is_pid(Type) ->
+ t_is_pid(Type, 'universe').
+
+-spec t_is_pid(erl_type(), opaques()) -> boolean().
+
+t_is_pid(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_pid1/1).
+
+is_pid1(?identifier(?any)) -> false;
+is_pid1(?identifier(Set)) -> set_is_singleton(?pid_qual, Set);
+is_pid1(_) -> false.
+
+%%------------------------------------
+
+-spec t_reference() -> erl_type().
+
+t_reference() ->
+ ?identifier(set_singleton(?reference_qual)).
+
+-spec t_is_reference(erl_type()) -> boolean().
+
+t_is_reference(Type) ->
+ t_is_reference(Type, 'universe').
+
+-spec t_is_reference(erl_type(), opaques()) -> boolean().
+
+t_is_reference(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_reference1/1).
+
+is_reference1(?identifier(?any)) -> false;
+is_reference1(?identifier(Set)) -> set_is_singleton(?reference_qual, Set);
+is_reference1(_) -> false.
+
+%%-----------------------------------------------------------------------------
+%% Numbers are divided into floats, integers, chars and bytes.
+%%
+
+-spec t_number() -> erl_type().
+
+t_number() ->
+ ?number(?any, ?unknown_qual).
+
+-spec t_number(integer()) -> erl_type().
+
+t_number(X) when is_integer(X) ->
+ t_integer(X).
+
+-spec t_is_number(erl_type()) -> boolean().
+
+t_is_number(Type) ->
+ t_is_number(Type, 'universe').
+
+-spec t_is_number(erl_type(), opaques()) -> boolean().
+
+t_is_number(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_number/1).
+
+is_number(?number(_, _)) -> true;
+is_number(_) -> false.
+
+%% Currently, the type system collapses all floats to ?float and does
+%% not keep any information about their values. As a result, the list
+%% that this function returns contains only integers.
+
+-spec t_number_vals(erl_type()) -> 'unknown' | [integer(),...].
+
+t_number_vals(Type) ->
+ t_number_vals(Type, 'universe').
+
+-spec t_number_vals(erl_type(), opaques()) -> 'unknown' | [integer(),...].
+
+t_number_vals(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun number_vals/1).
+
+number_vals(?int_set(Set)) -> set_to_list(Set);
+number_vals(?number(_, _)) -> unknown;
+number_vals(?opaque(_)) -> unknown;
+number_vals(Other) ->
+ Inf = t_inf(Other, t_number()),
+ false = t_is_none(Inf), % sanity check
+ number_vals(Inf).
+
+%%------------------------------------
+
+-spec t_float() -> erl_type().
+
+t_float() ->
+ ?float.
+
+-spec t_is_float(erl_type()) -> boolean().
+
+t_is_float(Type) ->
+ t_is_float(Type, 'universe').
+
+-spec t_is_float(erl_type(), opaques()) -> boolean().
+
+t_is_float(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_float1/1).
+
+is_float1(?float) -> true;
+is_float1(_) -> false.
+
+%%------------------------------------
+
+-spec t_integer() -> erl_type().
+
+t_integer() ->
+ ?integer(?any).
+
+-spec t_integer(integer()) -> erl_type().
+
+t_integer(I) when is_integer(I) ->
+ ?int_set(set_singleton(I)).
+
+-spec t_integers([integer()]) -> erl_type().
+
+t_integers(List) when is_list(List) ->
+ t_sup([t_integer(I) || I <- List]).
+
+-spec t_is_integer(erl_type()) -> boolean().
+
+t_is_integer(Type) ->
+ t_is_integer(Type, 'universe').
+
+-spec t_is_integer(erl_type(), opaques()) -> boolean().
+
+t_is_integer(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_integer1/1).
+
+is_integer1(?integer(_)) -> true;
+is_integer1(_) -> false.
+
+%%------------------------------------
+
+-spec t_byte() -> erl_type().
+
+t_byte() ->
+ ?byte.
+
+-ifdef(DO_ERL_TYPES_TEST).
+-spec t_is_byte(erl_type()) -> boolean().
+
+t_is_byte(?int_range(neg_inf, _)) -> false;
+t_is_byte(?int_range(_, pos_inf)) -> false;
+t_is_byte(?int_range(From, To))
+ when is_integer(From), From >= 0, is_integer(To), To =< ?MAX_BYTE -> true;
+t_is_byte(?int_set(Set)) ->
+ (set_min(Set) >= 0) andalso (set_max(Set) =< ?MAX_BYTE);
+t_is_byte(_) -> false.
+-endif.
+
+%%------------------------------------
+
+-spec t_char() -> erl_type().
+
+t_char() ->
+ ?char.
+
+-spec t_is_char(erl_type()) -> boolean().
+
+t_is_char(?int_range(neg_inf, _)) -> false;
+t_is_char(?int_range(_, pos_inf)) -> false;
+t_is_char(?int_range(From, To))
+ when is_integer(From), From >= 0, is_integer(To), To =< ?MAX_CHAR -> true;
+t_is_char(?int_set(Set)) ->
+ (set_min(Set) >= 0) andalso (set_max(Set) =< ?MAX_CHAR);
+t_is_char(_) -> false.
+
+%%-----------------------------------------------------------------------------
+%% Lists
+%%
+
+-spec t_cons() -> erl_type().
+
+t_cons() ->
+ ?nonempty_list(?any, ?any).
+
+%% Note that if the tail argument can be a list, we must collapse the
+%% content of the list to include both the content of the tail list
+%% and the head of the cons. If for example the tail argument is any()
+%% then there can be any list in the tail and the content of the
+%% returned list must be any().
+
+-spec t_cons(erl_type(), erl_type()) -> erl_type().
+
+t_cons(?none, _) -> ?none;
+t_cons(_, ?none) -> ?none;
+t_cons(?unit, _) -> ?none;
+t_cons(_, ?unit) -> ?none;
+t_cons(Hd, ?nil) ->
+ ?nonempty_list(Hd, ?nil);
+t_cons(Hd, ?list(Contents, Termination, _)) ->
+ ?nonempty_list(t_sup(Contents, Hd), Termination);
+t_cons(Hd, Tail) ->
+ case cons_tail(t_inf(Tail, t_maybe_improper_list())) of
+ ?list(Contents, Termination, _Size) ->
+ %% Collapse the list part of the termination but keep the
+ %% non-list part intact.
+ NewTermination = t_sup(t_subtract(Tail, t_maybe_improper_list()),
+ Termination),
+ ?nonempty_list(t_sup(Hd, Contents), NewTermination);
+ ?nil -> ?nonempty_list(Hd, Tail);
+ ?none -> ?nonempty_list(Hd, Tail);
+ ?unit -> ?none
+ end.
+
+cons_tail(Type) ->
+ do_opaque(Type, 'universe', fun(T) -> T end).
+
+-spec t_is_cons(erl_type()) -> boolean().
+
+t_is_cons(Type) ->
+ t_is_cons(Type, 'universe').
+
+-spec t_is_cons(erl_type(), opaques()) -> boolean().
+
+t_is_cons(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_cons/1).
+
+is_cons(?nonempty_list(_, _)) -> true;
+is_cons(_) -> false.
+
+-spec t_cons_hd(erl_type()) -> erl_type().
+
+t_cons_hd(Type) ->
+ t_cons_hd(Type, 'universe').
+
+-spec t_cons_hd(erl_type(), opaques()) -> erl_type().
+
+t_cons_hd(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun cons_hd/1).
+
+cons_hd(?nonempty_list(Contents, _Termination)) -> Contents.
+
+-spec t_cons_tl(erl_type()) -> erl_type().
+
+t_cons_tl(Type) ->
+ t_cons_tl(Type, 'universe').
+
+-spec t_cons_tl(erl_type(), opaques()) -> erl_type().
+
+t_cons_tl(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun cons_tl/1).
+
+cons_tl(?nonempty_list(_Contents, Termination) = T) ->
+ t_sup(Termination, T).
+
+-spec t_nil() -> erl_type().
+
+t_nil() ->
+ ?nil.
+
+-spec t_is_nil(erl_type()) -> boolean().
+
+t_is_nil(Type) ->
+ t_is_nil(Type, 'universe').
+
+-spec t_is_nil(erl_type(), opaques()) -> boolean().
+
+t_is_nil(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_nil/1).
+
+is_nil(?nil) -> true;
+is_nil(_) -> false.
+
+-spec t_list() -> erl_type().
+
+t_list() ->
+ ?list(?any, ?nil, ?unknown_qual).
+
+-spec t_list(erl_type()) -> erl_type().
+
+t_list(?none) -> ?none;
+t_list(?unit) -> ?none;
+t_list(Contents) ->
+ ?list(Contents, ?nil, ?unknown_qual).
+
+-spec t_list_elements(erl_type()) -> erl_type().
+
+t_list_elements(Type) ->
+ t_list_elements(Type, 'universe').
+
+-spec t_list_elements(erl_type(), opaques()) -> erl_type().
+
+t_list_elements(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun list_elements/1).
+
+list_elements(?list(Contents, _, _)) -> Contents;
+list_elements(?nil) -> ?none.
+
+-spec t_list_termination(erl_type(), opaques()) -> erl_type().
+
+t_list_termination(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun t_list_termination/1).
+
+-spec t_list_termination(erl_type()) -> erl_type().
+
+t_list_termination(?nil) -> ?nil;
+t_list_termination(?list(_, Term, _)) -> Term.
+
+-spec t_is_list(erl_type()) -> boolean().
+
+t_is_list(?list(_Contents, ?nil, _)) -> true;
+t_is_list(?nil) -> true;
+t_is_list(_) -> false.
+
+-spec t_nonempty_list() -> erl_type().
+
+t_nonempty_list() ->
+ t_cons(?any, ?nil).
+
+-spec t_nonempty_list(erl_type()) -> erl_type().
+
+t_nonempty_list(Type) ->
+ t_cons(Type, ?nil).
+
+-spec t_nonempty_string() -> erl_type().
+
+t_nonempty_string() ->
+ t_nonempty_list(t_char()).
+
+-spec t_string() -> erl_type().
+
+t_string() ->
+ t_list(t_char()).
+
+-spec t_is_string(erl_type()) -> boolean().
+
+t_is_string(X) ->
+ t_is_list(X) andalso t_is_char(t_list_elements(X)).
+
+-spec t_maybe_improper_list() -> erl_type().
+
+t_maybe_improper_list() ->
+ ?list(?any, ?any, ?unknown_qual).
+
+%% Should only be used if you know what you are doing. See t_cons/2
+-spec t_maybe_improper_list(erl_type(), erl_type()) -> erl_type().
+
+t_maybe_improper_list(_Content, ?unit) -> ?none;
+t_maybe_improper_list(?unit, _Termination) -> ?none;
+t_maybe_improper_list(Content, Termination) ->
+ %% Safety check: would be nice to have but does not work with remote types
+ %% true = t_is_subtype(t_nil(), Termination),
+ ?list(Content, Termination, ?unknown_qual).
+
+-spec t_is_maybe_improper_list(erl_type()) -> boolean().
+
+t_is_maybe_improper_list(Type) ->
+ t_is_maybe_improper_list(Type, 'universe').
+
+-spec t_is_maybe_improper_list(erl_type(), opaques()) -> boolean().
+
+t_is_maybe_improper_list(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_maybe_improper_list/1).
+
+is_maybe_improper_list(?list(_, _, _)) -> true;
+is_maybe_improper_list(?nil) -> true;
+is_maybe_improper_list(_) -> false.
+
+%% %% Should only be used if you know what you are doing. See t_cons/2
+%% -spec t_improper_list(erl_type(), erl_type()) -> erl_type().
+%%
+%% t_improper_list(?unit, _Termination) -> ?none;
+%% t_improper_list(_Content, ?unit) -> ?none;
+%% t_improper_list(Content, Termination) ->
+%% %% Safety check: would be nice to have but does not work with remote types
+%% %% false = t_is_subtype(t_nil(), Termination),
+%% ?list(Content, Termination, ?any).
+
+-spec lift_list_to_pos_empty(erl_type(), opaques()) -> erl_type().
+
+lift_list_to_pos_empty(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun lift_list_to_pos_empty/1).
+
+-spec lift_list_to_pos_empty(erl_type()) -> erl_type().
+
+lift_list_to_pos_empty(?nil) -> ?nil;
+lift_list_to_pos_empty(?list(Content, Termination, _)) ->
+ ?list(Content, Termination, ?unknown_qual).
+
+%%-----------------------------------------------------------------------------
+%% 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() ->
+ t_map([], t_any(), t_any()).
+
+-spec t_map([{erl_type(), erl_type()}]) -> erl_type().
+
+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().
+
+t_is_map(Type) ->
+ t_is_map(Type, 'universe').
+
+-spec t_is_map(erl_type(), opaques()) -> boolean().
+
+t_is_map(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_map1/1).
+
+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) ->
+ {K1, AMNess1, AV1, As1, BMNess1, BV1, Bs1} =
+ case {As0, Bs0} of
+ {[{K,AMNess,AV}|As], [{K, BMNess,BV}|Bs]} ->
+ {K, AMNess, AV, As, BMNess, BV, Bs};
+ {[{K,AMNess,AV}|As], [{BK,_, _ }|_]=Bs} when K < BK ->
+ {K, AMNess, AV, As, ?opt, mapmerge_otherv(K, BDefK, BDefV), Bs};
+ {As, [{K, BMNess,BV}|Bs]} ->
+ {K, ?opt, mapmerge_otherv(K, ADefK, ADefV), As, BMNess, BV, Bs};
+ {[{K,AMNess,AV}|As], []=Bs} ->
+ {K, AMNess, AV, As, ?opt, mapmerge_otherv(K, BDefK, BDefV), Bs}
+ end,
+ MK = K1, %% Rename to make clear that we are matching below
+ case F(K1, AMNess1, AV1, BMNess1, BV1) of
+ false -> map_pairwise_merge(F,As1,ADefK,ADefV,Bs1,BDefK,BDefV);
+ {MK,_,_}=M -> [M|map_pairwise_merge(F,As1,ADefK,ADefV,Bs1,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) ->
+ {K1, AMNess1, AV1, As1, BMNess1, BV1, Bs1} =
+ case {As0, Bs0} of
+ {[{K,AMNess,AV}|As], [{K,BMNess,BV}|Bs]} ->
+ {K, AMNess, AV, As, BMNess, BV, Bs};
+ {[{K,AMNess,AV}|As], [{BK,_, _ }|_]=Bs} when K < BK ->
+ {K, AMNess, AV, As, ?opt, mapmerge_otherv(K, BDefK, BDefV), Bs};
+ {As, [{K,BMNess,BV}|Bs]} ->
+ {K, ?opt, mapmerge_otherv(K, ADefK, ADefV), As, BMNess, BV, Bs};
+ {[{K,AMNess,AV}|As], []=Bs} ->
+ {K, AMNess, AV, As, ?opt, mapmerge_otherv(K, BDefK, BDefV), Bs}
+ end,
+ F(K1, AMNess1, AV1, BMNess1, BV1,
+ map_pairwise_merge_foldr(F,AccIn,As1,ADefK,ADefV,Bs1,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
+%%
+
+-spec t_tuple() -> erl_type().
+
+t_tuple() ->
+ ?tuple(?any, ?any, ?any).
+
+-spec t_tuple(non_neg_integer() | [erl_type()]) -> erl_type().
+
+t_tuple(N) when is_integer(N), N > ?MAX_TUPLE_SIZE ->
+ t_tuple();
+t_tuple(N) when is_integer(N) ->
+ ?tuple(lists:duplicate(N, ?any), N, ?any);
+t_tuple(List) ->
+ case any_none_or_unit(List) of
+ true -> t_none();
+ false ->
+ Arity = length(List),
+ case get_tuple_tags(List) of
+ [Tag] -> ?tuple(List, Arity, Tag); %% Tag can also be ?any here
+ TagList ->
+ SortedTagList = lists:sort(TagList),
+ Tuples = [?tuple([T|tl(List)], Arity, T) || T <- SortedTagList],
+ ?tuple_set([{Arity, Tuples}])
+ end
+ end.
+
+-spec get_tuple_tags([erl_type()]) -> [erl_type(),...].
+
+get_tuple_tags([Tag|_]) ->
+ do_opaque(Tag, 'universe', fun tuple_tags/1);
+get_tuple_tags(_) -> [?any].
+
+tuple_tags(?atom(?any)) -> [?any];
+tuple_tags(?atom(Set)) ->
+ case set_size(Set) > ?TUPLE_TAG_LIMIT of
+ true -> [?any];
+ false -> [t_atom(A) || A <- set_to_list(Set)]
+ end;
+tuple_tags(_) -> [?any].
+
+%% to be used for a tuple with known types for its arguments (not ?any)
+-spec t_tuple_args(erl_type()) -> [erl_type()].
+
+t_tuple_args(Type) ->
+ t_tuple_args(Type, 'universe').
+
+%% to be used for a tuple with known types for its arguments (not ?any)
+-spec t_tuple_args(erl_type(), opaques()) -> [erl_type()].
+
+t_tuple_args(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun tuple_args/1).
+
+tuple_args(?tuple(Args, _, _)) when is_list(Args) -> Args.
+
+%% to be used for a tuple with a known size (not ?any)
+-spec t_tuple_size(erl_type()) -> non_neg_integer().
+
+t_tuple_size(Type) ->
+ t_tuple_size(Type, 'universe').
+
+%% to be used for a tuple with a known size (not ?any)
+-spec t_tuple_size(erl_type(), opaques()) -> non_neg_integer().
+
+t_tuple_size(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun tuple_size1/1).
+
+tuple_size1(?tuple(_, Size, _)) when is_integer(Size) -> Size.
+
+-spec t_tuple_sizes(erl_type()) -> 'unknown' | [non_neg_integer(),...].
+
+t_tuple_sizes(Type) ->
+ do_opaque(Type, 'universe', fun tuple_sizes/1).
+
+tuple_sizes(?tuple(?any, ?any, ?any)) -> unknown;
+tuple_sizes(?tuple(_, Size, _)) when is_integer(Size) -> [Size];
+tuple_sizes(?tuple_set(List)) -> [Size || {Size, _} <- List].
+
+-spec t_tuple_subtypes(erl_type(), opaques()) ->
+ 'unknown' | [erl_type(),...].
+
+t_tuple_subtypes(Type, Opaques) ->
+ Fun = fun(?tuple_set(List)) ->
+ t_tuple_subtypes_tuple_list(List, Opaques);
+ (?opaque(_)) -> unknown;
+ (T) -> t_tuple_subtypes(T)
+ end,
+ do_opaque(Type, Opaques, Fun).
+
+t_tuple_subtypes_tuple_list(List, Opaques) ->
+ lists:append([t_tuple_subtypes_list(Tuples, Opaques) ||
+ {_Size, Tuples} <- List]).
+
+t_tuple_subtypes_list(List, Opaques) ->
+ ListOfLists = [t_tuple_subtypes(E, Opaques) || E <- List, E =/= ?none],
+ lists:append([L || L <- ListOfLists, L =/= 'unknown']).
+
+-spec t_tuple_subtypes(erl_type()) -> 'unknown' | [erl_type(),...].
+
+%% XXX. Not the same as t_tuple_subtypes(T, 'universe')...
+t_tuple_subtypes(?tuple(?any, ?any, ?any)) -> unknown;
+t_tuple_subtypes(?tuple(_, _, _) = T) -> [T];
+t_tuple_subtypes(?tuple_set(List)) ->
+ lists:append([Tuples || {_Size, Tuples} <- List]).
+
+-spec t_is_tuple(erl_type()) -> boolean().
+
+t_is_tuple(Type) ->
+ t_is_tuple(Type, 'universe').
+
+-spec t_is_tuple(erl_type(), opaques()) -> boolean().
+
+t_is_tuple(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun is_tuple1/1).
+
+is_tuple1(?tuple(_, _, _)) -> true;
+is_tuple1(?tuple_set(_)) -> true;
+is_tuple1(_) -> false.
+
+%%-----------------------------------------------------------------------------
+%% Non-primitive types, including some handy syntactic sugar types
+%%
+
+-spec t_bitstrlist() -> erl_type().
+
+t_bitstrlist() ->
+ t_iolist(1, t_bitstr()).
+
+-spec t_arity() -> erl_type().
+
+t_arity() ->
+ t_from_range(0, 255). % was t_byte().
+
+-spec t_pos_integer() -> erl_type().
+
+t_pos_integer() ->
+ t_from_range(1, pos_inf).
+
+-spec t_non_neg_integer() -> erl_type().
+
+t_non_neg_integer() ->
+ t_from_range(0, pos_inf).
+
+-spec t_is_non_neg_integer(erl_type()) -> boolean().
+
+t_is_non_neg_integer(?integer(_) = T) ->
+ t_is_subtype(T, t_non_neg_integer());
+t_is_non_neg_integer(_) -> false.
+
+-spec t_neg_integer() -> erl_type().
+
+t_neg_integer() ->
+ t_from_range(neg_inf, -1).
+
+-spec t_fixnum() -> erl_type().
+
+t_fixnum() ->
+ t_integer(). % Gross over-approximation
+
+-spec t_pos_fixnum() -> erl_type().
+
+t_pos_fixnum() ->
+ t_pos_integer(). % Gross over-approximation
+
+-spec t_non_neg_fixnum() -> erl_type().
+
+t_non_neg_fixnum() ->
+ t_non_neg_integer(). % Gross over-approximation
+
+-spec t_mfa() -> erl_type().
+
+t_mfa() ->
+ t_tuple([t_atom(), t_atom(), t_arity()]).
+
+-spec t_module() -> erl_type().
+
+t_module() ->
+ t_atom().
+
+-spec t_node() -> erl_type().
+
+t_node() ->
+ t_atom().
+
+-spec t_iodata() -> erl_type().
+
+t_iodata() ->
+ t_sup(t_iolist(), t_binary()).
+
+-spec t_iolist() -> erl_type().
+
+t_iolist() ->
+ t_iolist(1, t_binary()).
+
+%% Added a second argument which currently is t_binary() | t_bitstr()
+-spec t_iolist(non_neg_integer(), erl_type()) -> erl_type().
+
+t_iolist(N, T) when N > 0 ->
+ t_maybe_improper_list(t_sup([t_iolist(N-1, T), T, t_byte()]),
+ t_sup(T, t_nil()));
+t_iolist(0, T) ->
+ t_maybe_improper_list(t_any(), t_sup(T, t_nil())).
+
+-spec t_timeout() -> erl_type().
+
+t_timeout() ->
+ t_sup(t_non_neg_integer(), t_atom('infinity')).
+
+%%------------------------------------
+
+%% ?none is allowed in products. A product of size 1 is not a product.
+
+-spec t_product([erl_type()]) -> erl_type().
+
+t_product([T]) -> T;
+t_product(Types) when is_list(Types) ->
+ ?product(Types).
+
+%% This function is intended to be the inverse of the one above.
+%% It should NOT be used with ?any, ?none or ?unit as input argument.
+
+-spec t_to_tlist(erl_type()) -> [erl_type()].
+
+t_to_tlist(?product(Types)) -> Types;
+t_to_tlist(T) when T =/= ?any orelse T =/= ?none orelse T =/= ?unit -> [T].
+
+%%------------------------------------
+
+-spec t_var(atom() | integer()) -> erl_type().
+
+t_var(Atom) when is_atom(Atom) -> ?var(Atom);
+t_var(Int) when is_integer(Int) -> ?var(Int).
+
+-spec t_is_var(erl_type()) -> boolean().
+
+t_is_var(?var(_)) -> true;
+t_is_var(_) -> false.
+
+-spec t_var_name(erl_type()) -> atom() | integer().
+
+t_var_name(?var(Id)) -> Id.
+
+-spec t_has_var(erl_type()) -> boolean().
+
+t_has_var(?var(_)) -> true;
+t_has_var(?function(Domain, Range)) ->
+ t_has_var(Domain) orelse t_has_var(Range);
+t_has_var(?list(Contents, Termination, _)) ->
+ t_has_var(Contents) orelse t_has_var(Termination);
+t_has_var(?product(Types)) -> t_has_var_list(Types);
+t_has_var(?tuple(?any, ?any, ?any)) -> false;
+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(_, 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)]);
+t_has_var(?union(List)) ->
+ t_has_var_list(List);
+t_has_var(_) -> false.
+
+-spec t_has_var_list([erl_type()]) -> boolean().
+
+t_has_var_list([T|Ts]) ->
+ t_has_var(T) orelse t_has_var_list(Ts);
+t_has_var_list([]) -> false.
+
+-spec t_collect_vars(erl_type()) -> [erl_type()].
+
+t_collect_vars(T) ->
+ t_collect_vars(T, []).
+
+-spec t_collect_vars(erl_type(), [erl_type()]) -> [erl_type()].
+
+t_collect_vars(?var(_) = Var, Acc) ->
+ ordsets:add_element(Var, Acc);
+t_collect_vars(?function(Domain, Range), Acc) ->
+ ordsets:union(t_collect_vars(Domain, Acc), t_collect_vars(Range, []));
+t_collect_vars(?list(Contents, Termination, _), Acc) ->
+ ordsets:union(t_collect_vars(Contents, Acc), t_collect_vars(Termination, []));
+t_collect_vars(?product(Types), Acc) ->
+ t_collect_vars_list(Types, Acc);
+t_collect_vars(?tuple(?any, ?any, ?any), Acc) ->
+ Acc;
+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(_, 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);
+t_collect_vars(?union(List), Acc) ->
+ t_collect_vars_list(List, Acc);
+t_collect_vars(_, Acc) ->
+ Acc.
+
+t_collect_vars_list([T|Ts], Acc0) ->
+ Acc = t_collect_vars(T, Acc0),
+ t_collect_vars_list(Ts, Acc);
+t_collect_vars_list([], Acc) -> Acc.
+
+%%=============================================================================
+%%
+%% Type construction from Erlang terms.
+%%
+%%=============================================================================
+
+%%-----------------------------------------------------------------------------
+%% Make a type from a term. No type depth is enforced.
+%%
+
+-spec t_from_term(term()) -> erl_type().
+
+t_from_term([H|T]) -> t_cons(t_from_term(H), t_from_term(T));
+t_from_term([]) -> t_nil();
+t_from_term(T) when is_atom(T) -> t_atom(T);
+t_from_term(T) when is_bitstring(T) -> t_bitstr(0, erlang:bit_size(T));
+t_from_term(T) when is_float(T) -> t_float();
+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) ->
+ 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();
+t_from_term(T) when is_tuple(T) ->
+ t_tuple([t_from_term(E) || E <- tuple_to_list(T)]).
+
+%%-----------------------------------------------------------------------------
+%% Integer types from a range.
+%%-----------------------------------------------------------------------------
+
+%%-define(USE_UNSAFE_RANGES, true).
+
+-spec t_from_range(rng_elem(), rng_elem()) -> erl_type().
+
+-ifdef(USE_UNSAFE_RANGES).
+
+t_from_range(X, Y) ->
+ t_from_range_unsafe(X, Y).
+
+-else.
+
+t_from_range(neg_inf, pos_inf) -> t_integer();
+t_from_range(neg_inf, Y) when is_integer(Y), Y < 0 -> ?integer_neg;
+t_from_range(neg_inf, Y) when is_integer(Y), Y >= 0 -> t_integer();
+t_from_range(X, pos_inf) when is_integer(X), X >= 1 -> ?integer_pos;
+t_from_range(X, pos_inf) when is_integer(X), X >= 0 -> ?integer_non_neg;
+t_from_range(X, pos_inf) when is_integer(X), X < 0 -> t_integer();
+t_from_range(X, Y) when is_integer(X), is_integer(Y), X > Y -> t_none();
+t_from_range(X, Y) when is_integer(X), is_integer(Y) ->
+ case ((Y - X) < ?SET_LIMIT) of
+ true -> t_integers(lists:seq(X, Y));
+ false ->
+ case X >= 0 of
+ false ->
+ if Y < 0 -> ?integer_neg;
+ true -> t_integer()
+ end;
+ true ->
+ if Y =< ?MAX_BYTE, X >= 1 -> ?int_range(1, ?MAX_BYTE);
+ Y =< ?MAX_BYTE -> t_byte();
+ Y =< ?MAX_CHAR, X >= 1 -> ?int_range(1, ?MAX_CHAR);
+ Y =< ?MAX_CHAR -> t_char();
+ X >= 1 -> ?integer_pos;
+ X >= 0 -> ?integer_non_neg
+ end
+ end
+ end;
+t_from_range(pos_inf, neg_inf) -> t_none().
+
+-endif.
+
+-spec t_from_range_unsafe(rng_elem(), rng_elem()) -> erl_type().
+
+t_from_range_unsafe(neg_inf, pos_inf) -> t_integer();
+t_from_range_unsafe(neg_inf, Y) -> ?int_range(neg_inf, Y);
+t_from_range_unsafe(X, pos_inf) -> ?int_range(X, pos_inf);
+t_from_range_unsafe(X, Y) when is_integer(X), is_integer(Y), X =< Y ->
+ if (Y - X) < ?SET_LIMIT -> t_integers(lists:seq(X, Y));
+ true -> ?int_range(X, Y)
+ end;
+t_from_range_unsafe(X, Y) when is_integer(X), is_integer(Y) -> t_none();
+t_from_range_unsafe(pos_inf, neg_inf) -> t_none().
+
+-spec t_is_fixnum(erl_type()) -> boolean().
+
+t_is_fixnum(?int_range(neg_inf, _)) -> false;
+t_is_fixnum(?int_range(_, pos_inf)) -> false;
+t_is_fixnum(?int_range(From, To)) ->
+ is_fixnum(From) andalso is_fixnum(To);
+t_is_fixnum(?int_set(Set)) ->
+ is_fixnum(set_min(Set)) andalso is_fixnum(set_max(Set));
+t_is_fixnum(_) -> false.
+
+-spec is_fixnum(integer()) -> boolean().
+
+is_fixnum(N) when is_integer(N) ->
+ Bits = ?BITS,
+ (N =< ((1 bsl (Bits - 1)) - 1)) andalso (N >= -(1 bsl (Bits - 1))).
+
+infinity_geq(pos_inf, _) -> true;
+infinity_geq(_, pos_inf) -> false;
+infinity_geq(_, neg_inf) -> true;
+infinity_geq(neg_inf, _) -> false;
+infinity_geq(A, B) -> A >= B.
+
+-spec t_is_bitwidth(erl_type()) -> boolean().
+
+t_is_bitwidth(?int_range(neg_inf, _)) -> false;
+t_is_bitwidth(?int_range(_, pos_inf)) -> false;
+t_is_bitwidth(?int_range(From, To)) ->
+ infinity_geq(From, 0) andalso infinity_geq(?BITS, To);
+t_is_bitwidth(?int_set(Set)) ->
+ infinity_geq(set_min(Set), 0) andalso infinity_geq(?BITS, set_max(Set));
+t_is_bitwidth(_) -> false.
+
+-spec number_min(erl_type()) -> rng_elem().
+
+number_min(Type) ->
+ number_min(Type, 'universe').
+
+-spec number_min(erl_type(), opaques()) -> rng_elem().
+
+number_min(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun number_min2/1).
+
+number_min2(?int_range(From, _)) -> From;
+number_min2(?int_set(Set)) -> set_min(Set);
+number_min2(?number(?any, _Tag)) -> neg_inf.
+
+-spec number_max(erl_type()) -> rng_elem().
+
+number_max(Type) ->
+ number_max(Type, 'universe').
+
+-spec number_max(erl_type(), opaques()) -> rng_elem().
+
+number_max(Type, Opaques) ->
+ do_opaque(Type, Opaques, fun number_max2/1).
+
+number_max2(?int_range(_, To)) -> To;
+number_max2(?int_set(Set)) -> set_max(Set);
+number_max2(?number(?any, _Tag)) -> pos_inf.
+
+%% -spec int_range(rgn_elem(), rng_elem()) -> erl_type().
+%%
+%% int_range(neg_inf, pos_inf) -> t_integer();
+%% int_range(neg_inf, To) -> ?int_range(neg_inf, To);
+%% int_range(From, pos_inf) -> ?int_range(From, pos_inf);
+%% int_range(From, To) when From =< To -> t_from_range(From, To);
+%% int_range(From, To) when To < From -> ?none.
+
+in_range(_, ?int_range(neg_inf, pos_inf)) -> true;
+in_range(X, ?int_range(From, pos_inf)) -> X >= From;
+in_range(X, ?int_range(neg_inf, To)) -> X =< To;
+in_range(X, ?int_range(From, To)) -> (X >= From) andalso (X =< To).
+
+-spec min(rng_elem(), rng_elem()) -> rng_elem().
+
+min(neg_inf, _) -> neg_inf;
+min(_, neg_inf) -> neg_inf;
+min(pos_inf, Y) -> Y;
+min(X, pos_inf) -> X;
+min(X, Y) when X =< Y -> X;
+min(_, Y) -> Y.
+
+-spec max(rng_elem(), rng_elem()) -> rng_elem().
+
+max(neg_inf, Y) -> Y;
+max(X, neg_inf) -> X;
+max(pos_inf, _) -> pos_inf;
+max(_, pos_inf) -> pos_inf;
+max(X, Y) when X =< Y -> Y;
+max(X, _) -> X.
+
+expand_range_from_set(Range = ?int_range(From, To), Set) ->
+ Min = min(set_min(Set), From),
+ Max = max(set_max(Set), To),
+ if From =:= Min, To =:= Max -> Range;
+ true -> t_from_range(Min, Max)
+ end.
+
+%%=============================================================================
+%%
+%% Lattice operations
+%%
+%%=============================================================================
+
+%%-----------------------------------------------------------------------------
+%% Supremum
+%%
+
+-spec t_sup([erl_type()]) -> erl_type().
+
+t_sup([]) -> ?none;
+t_sup(Ts) ->
+ case lists:any(fun is_any/1, Ts) of
+ true -> ?any;
+ false ->
+ t_sup1(Ts, [])
+ end.
+
+t_sup1([H1, H2|T], L) ->
+ t_sup1(T, [t_sup(H1, H2)|L]);
+t_sup1([T], []) -> subst_all_vars_to_any(T);
+t_sup1(Ts, L) ->
+ t_sup1(Ts++L, []).
+
+-spec t_sup(erl_type(), erl_type()) -> erl_type().
+
+t_sup(?any, _) -> ?any;
+t_sup(_, ?any) -> ?any;
+t_sup(?none, T) -> T;
+t_sup(T, ?none) -> T;
+t_sup(?unit, T) -> T;
+t_sup(T, ?unit) -> T;
+t_sup(T, T) -> subst_all_vars_to_any(T);
+t_sup(?var(_), _) -> ?any;
+t_sup(_, ?var(_)) -> ?any;
+t_sup(?atom(Set1), ?atom(Set2)) ->
+ ?atom(set_union(Set1, Set2));
+t_sup(?bitstr(U1, B1), ?bitstr(U2, B2)) ->
+ t_bitstr(gcd(gcd(U1, U2), abs(B1-B2)), lists:min([B1, B2]));
+t_sup(?function(Domain1, Range1), ?function(Domain2, Range2)) ->
+ %% The domain is either a product or any.
+ ?function(t_sup(Domain1, Domain2), t_sup(Range1, Range2));
+t_sup(?identifier(Set1), ?identifier(Set2)) ->
+ ?identifier(set_union(Set1, Set2));
+t_sup(?opaque(Set1), ?opaque(Set2)) ->
+ sup_opaque(set_to_list(ordsets:union(Set1, Set2)));
+%%Disallow unions with opaque types
+%%t_sup(T1=?opaque(_,_,_), T2) ->
+%% io:format("Debug: t_sup executed with args ~w and ~w~n",[T1, T2]), ?none;
+%%t_sup(T1, T2=?opaque(_,_,_)) ->
+%% io:format("Debug: t_sup executed with args ~w and ~w~n",[T1, T2]), ?none;
+t_sup(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2)) ->
+ ?matchstate(t_sup(Pres1, Pres2), t_sup(Slots1, Slots2));
+t_sup(?nil, ?nil) -> ?nil;
+t_sup(?nil, ?list(Contents, Termination, _)) ->
+ ?list(Contents, t_sup(?nil, Termination), ?unknown_qual);
+t_sup(?list(Contents, Termination, _), ?nil) ->
+ ?list(Contents, t_sup(?nil, Termination), ?unknown_qual);
+t_sup(?list(Contents1, Termination1, Size1),
+ ?list(Contents2, Termination2, Size2)) ->
+ NewSize =
+ case {Size1, Size2} of
+ {?unknown_qual, ?unknown_qual} -> ?unknown_qual;
+ {?unknown_qual, ?nonempty_qual} -> ?unknown_qual;
+ {?nonempty_qual, ?unknown_qual} -> ?unknown_qual;
+ {?nonempty_qual, ?nonempty_qual} -> ?nonempty_qual
+ end,
+ NewContents = t_sup(Contents1, Contents2),
+ NewTermination = t_sup(Termination1, Termination2),
+ TmpList = t_cons(NewContents, NewTermination),
+ case NewSize of
+ ?nonempty_qual -> TmpList;
+ ?unknown_qual ->
+ ?list(FinalContents, FinalTermination, _) = TmpList,
+ ?list(FinalContents, FinalTermination, ?unknown_qual)
+ end;
+t_sup(?number(_, _), ?number(?any, ?unknown_qual) = T) -> T;
+t_sup(?number(?any, ?unknown_qual) = T, ?number(_, _)) -> T;
+t_sup(?float, ?float) -> ?float;
+t_sup(?float, ?integer(_)) -> t_number();
+t_sup(?integer(_), ?float) -> t_number();
+t_sup(?integer(?any) = T, ?integer(_)) -> T;
+t_sup(?integer(_), ?integer(?any) = T) -> T;
+t_sup(?int_set(Set1), ?int_set(Set2)) ->
+ case set_union(Set1, Set2) of
+ ?any ->
+ t_from_range(min(set_min(Set1), set_min(Set2)),
+ max(set_max(Set1), set_max(Set2)));
+ Set -> ?int_set(Set)
+ end;
+t_sup(?int_range(From1, To1), ?int_range(From2, To2)) ->
+ t_from_range(min(From1, From2), max(To1, To2));
+t_sup(Range = ?int_range(_, _), ?int_set(Set)) ->
+ expand_range_from_set(Range, Set);
+t_sup(?int_set(Set), Range = ?int_range(_, _)) ->
+ expand_range_from_set(Range, Set);
+t_sup(?product(Types1), ?product(Types2)) ->
+ L1 = length(Types1),
+ L2 = length(Types2),
+ if L1 =:= L2 -> ?product(t_sup_lists(Types1, Types2));
+ true -> ?any
+ end;
+t_sup(?product(_), _) ->
+ ?any;
+t_sup(_, ?product(_)) ->
+ ?any;
+t_sup(?tuple(?any, ?any, ?any) = T, ?tuple(_, _, _)) -> T;
+t_sup(?tuple(_, _, _), ?tuple(?any, ?any, ?any) = T) -> T;
+t_sup(?tuple(?any, ?any, ?any) = T, ?tuple_set(_)) -> T;
+t_sup(?tuple_set(_), ?tuple(?any, ?any, ?any) = T) -> T;
+t_sup(?tuple(Elements1, Arity, Tag1) = T1,
+ ?tuple(Elements2, Arity, Tag2) = T2) ->
+ if Tag1 =:= Tag2 -> t_tuple(t_sup_lists(Elements1, Elements2));
+ Tag1 =:= ?any -> t_tuple(t_sup_lists(Elements1, Elements2));
+ Tag2 =:= ?any -> t_tuple(t_sup_lists(Elements1, Elements2));
+ Tag1 < Tag2 -> ?tuple_set([{Arity, [T1, T2]}]);
+ Tag1 > Tag2 -> ?tuple_set([{Arity, [T2, T1]}])
+ end;
+t_sup(?tuple(_, Arity1, _) = T1, ?tuple(_, Arity2, _) = T2) ->
+ sup_tuple_sets([{Arity1, [T1]}], [{Arity2, [T2]}]);
+t_sup(?tuple_set(List1), ?tuple_set(List2)) ->
+ sup_tuple_sets(List1, List2);
+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),
+ sup_union(U1, U2).
+
+sup_opaque([]) -> ?none;
+sup_opaque(List) ->
+ L = sup_opaq(List),
+ ?opaque(ordsets:from_list(L)).
+
+sup_opaq(L0) ->
+ L1 = [{{Mod,Name,Args}, T} ||
+ #opaque{mod = Mod, name = Name, args = Args}=T <- L0],
+ F = family(L1),
+ [supl(Ts) || {_, Ts} <- F].
+
+supl([O]) -> O;
+supl(Ts) -> supl(Ts, t_none()).
+
+supl([#opaque{struct = S}=O|L], S0) ->
+ S1 = t_sup(S, S0),
+ case L =:= [] of
+ true -> O#opaque{struct = S1};
+ false -> supl(L, S1)
+ end.
+
+-spec t_sup_lists([erl_type()], [erl_type()]) -> [erl_type()].
+
+t_sup_lists([T1|Left1], [T2|Left2]) ->
+ [t_sup(T1, T2)|t_sup_lists(Left1, Left2)];
+t_sup_lists([], []) ->
+ [].
+
+sup_tuple_sets(L1, L2) ->
+ TotalArities = ordsets:union([Arity || {Arity, _} <- L1],
+ [Arity || {Arity, _} <- L2]),
+ if length(TotalArities) > ?TUPLE_ARITY_LIMIT -> t_tuple();
+ true ->
+ case sup_tuple_sets(L1, L2, []) of
+ [{_Arity, [OneTuple = ?tuple(_, _, _)]}] -> OneTuple;
+ List -> ?tuple_set(List)
+ end
+ end.
+
+sup_tuple_sets([{Arity, Tuples1}|Left1], [{Arity, Tuples2}|Left2], Acc) ->
+ NewAcc = [{Arity, sup_tuples_in_set(Tuples1, Tuples2)}|Acc],
+ sup_tuple_sets(Left1, Left2, NewAcc);
+sup_tuple_sets([{Arity1, _} = T1|Left1] = L1,
+ [{Arity2, _} = T2|Left2] = L2, Acc) ->
+ if Arity1 < Arity2 -> sup_tuple_sets(Left1, L2, [T1|Acc]);
+ Arity1 > Arity2 -> sup_tuple_sets(L1, Left2, [T2|Acc])
+ end;
+sup_tuple_sets([], L2, Acc) -> lists:reverse(Acc, L2);
+sup_tuple_sets(L1, [], Acc) -> lists:reverse(Acc, L1).
+
+sup_tuples_in_set([?tuple(_, _, ?any) = T], L) ->
+ [t_tuple(sup_tuple_elements([T|L]))];
+sup_tuples_in_set(L, [?tuple(_, _, ?any) = T]) ->
+ [t_tuple(sup_tuple_elements([T|L]))];
+sup_tuples_in_set(L1, L2) ->
+ FoldFun = fun(?tuple(_, _, Tag), AccTag) -> t_sup(Tag, AccTag) end,
+ TotalTag0 = lists:foldl(FoldFun, ?none, L1),
+ TotalTag = lists:foldl(FoldFun, TotalTag0, L2),
+ case TotalTag of
+ ?atom(?any) ->
+ %% We will reach the set limit. Widen now.
+ [t_tuple(sup_tuple_elements(L1 ++ L2))];
+ ?atom(Set) ->
+ case set_size(Set) > ?TUPLE_TAG_LIMIT of
+ true ->
+ %% We will reach the set limit. Widen now.
+ [t_tuple(sup_tuple_elements(L1 ++ L2))];
+ false ->
+ %% We can go on and build the tuple set.
+ sup_tuples_in_set(L1, L2, [])
+ end
+ end.
+
+sup_tuple_elements([?tuple(Elements, _, _)|L]) ->
+ lists:foldl(fun (?tuple(Es, _, _), Acc) -> t_sup_lists(Es, Acc) end,
+ Elements, L).
+
+sup_tuples_in_set([?tuple(Elements1, Arity, Tag1) = T1|Left1] = L1,
+ [?tuple(Elements2, Arity, Tag2) = T2|Left2] = L2, Acc) ->
+ if
+ Tag1 < Tag2 -> sup_tuples_in_set(Left1, L2, [T1|Acc]);
+ Tag1 > Tag2 -> sup_tuples_in_set(L1, Left2, [T2|Acc]);
+ Tag2 =:= Tag2 -> NewElements = t_sup_lists(Elements1, Elements2),
+ NewAcc = [?tuple(NewElements, Arity, Tag1)|Acc],
+ sup_tuples_in_set(Left1, Left2, NewAcc)
+ end;
+sup_tuples_in_set([], L2, Acc) -> lists:reverse(Acc, L2);
+sup_tuples_in_set(L1, [], Acc) -> lists:reverse(Acc, L1).
+
+sup_union(U1, U2) ->
+ sup_union(U1, U2, 0, []).
+
+sup_union([?none|Left1], [?none|Left2], N, Acc) ->
+ sup_union(Left1, Left2, N, [?none|Acc]);
+sup_union([T1|Left1], [T2|Left2], N, Acc) ->
+ sup_union(Left1, Left2, N+1, [t_sup(T1, T2)|Acc]);
+sup_union([], [], N, Acc) ->
+ if N =:= 0 -> ?none;
+ N =:= 1 ->
+ [Type] = [T || T <- Acc, T =/= ?none],
+ Type;
+ N =:= length(Acc) -> ?any;
+ true -> ?union(lists:reverse(Acc))
+ end.
+
+force_union(T = ?atom(_)) -> ?atom_union(T);
+force_union(T = ?bitstr(_, _)) -> ?bitstr_union(T);
+force_union(T = ?function(_, _)) -> ?function_union(T);
+force_union(T = ?identifier(_)) -> ?identifier_union(T);
+force_union(T = ?list(_, _, _)) -> ?list_union(T);
+force_union(T = ?nil) -> ?list_union(T);
+force_union(T = ?number(_, _)) -> ?number_union(T);
+force_union(T = ?opaque(_)) -> ?opaque_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);
+force_union(T = ?union(_)) -> T.
+
+%%-----------------------------------------------------------------------------
+%% An attempt to write the inverse operation of t_sup/1 -- XXX: INCOMPLETE !!
+%%
+
+-spec t_elements(erl_type()) -> [erl_type()].
+
+t_elements(?none) -> [];
+t_elements(?unit) -> [];
+t_elements(?any = T) -> [T];
+t_elements(?nil = T) -> [T];
+t_elements(?atom(?any) = T) -> [T];
+t_elements(?atom(Atoms)) ->
+ [t_atom(A) || A <- Atoms];
+t_elements(?bitstr(_, _) = T) -> [T];
+t_elements(?function(_, _) = T) -> [T];
+t_elements(?identifier(?any) = T) -> [T];
+t_elements(?identifier(IDs)) ->
+ [?identifier([T]) || T <- IDs];
+t_elements(?list(_, _, _) = T) -> [T];
+t_elements(?number(_, _) = T) ->
+ case T of
+ ?number(?any, ?unknown_qual) ->
+ [?float, ?integer(?any)];
+ ?float -> [T];
+ ?integer(?any) -> [T];
+ ?int_range(_, _) -> [T];
+ ?int_set(Set) ->
+ [t_integer(I) || I <- Set]
+ end;
+t_elements(?opaque(_) = T) ->
+ do_elements(T);
+t_elements(?map(_,_,_) = T) -> [T];
+t_elements(?tuple(_, _, _) = T) -> [T];
+t_elements(?tuple_set(_) = TS) ->
+ case t_tuple_subtypes(TS) of
+ unknown -> [];
+ Elems -> Elems
+ end;
+t_elements(?union(_) = T) ->
+ do_elements(T);
+t_elements(?var(_)) -> [?any]. %% yes, vars exist -- what else to do here?
+%% t_elements(T) ->
+%% io:format("T_ELEMENTS => ~p\n", [T]).
+
+do_elements(Type0) ->
+ case do_opaque(Type0, 'universe', fun(T) -> T end) of
+ ?union(List) -> lists:append([t_elements(T) || T <- List]);
+ Type -> t_elements(Type)
+ end.
+
+%%-----------------------------------------------------------------------------
+%% Infimum
+%%
+
+-spec t_inf([erl_type()]) -> erl_type().
+
+t_inf([H1, H2|T]) ->
+ case t_inf(H1, H2) of
+ ?none -> ?none;
+ NewH -> t_inf([NewH|T])
+ end;
+t_inf([H]) -> H;
+t_inf([]) -> ?none.
+
+-spec t_inf(erl_type(), erl_type()) -> erl_type().
+
+t_inf(T1, T2) ->
+ t_inf(T1, T2, 'universe').
+
+%% 'match' should be used from t_find_unknown_opaque() only
+-type t_inf_opaques() :: opaques() | {'match', [erl_type() | 'universe']}.
+
+-spec t_inf(erl_type(), erl_type(), t_inf_opaques()) -> erl_type().
+
+t_inf(?var(_), ?var(_), _Opaques) -> ?any;
+t_inf(?var(_), T, _Opaques) -> subst_all_vars_to_any(T);
+t_inf(T, ?var(_), _Opaques) -> subst_all_vars_to_any(T);
+t_inf(?any, T, _Opaques) -> subst_all_vars_to_any(T);
+t_inf(T, ?any, _Opaques) -> subst_all_vars_to_any(T);
+t_inf(?none, _, _Opaques) -> ?none;
+t_inf(_, ?none, _Opaques) -> ?none;
+t_inf(?unit, _, _Opaques) -> ?unit; % ?unit cases should appear below ?none
+t_inf(_, ?unit, _Opaques) -> ?unit;
+t_inf(T, T, _Opaques) -> subst_all_vars_to_any(T);
+t_inf(?atom(Set1), ?atom(Set2), _) ->
+ case set_intersection(Set1, Set2) of
+ ?none -> ?none;
+ NewSet -> ?atom(NewSet)
+ end;
+t_inf(?bitstr(U1, B1), ?bitstr(0, B2), _Opaques) ->
+ if B2 >= B1 andalso (B2-B1) rem U1 =:= 0 -> t_bitstr(0, B2);
+ true -> ?none
+ end;
+t_inf(?bitstr(0, B1), ?bitstr(U2, B2), _Opaques) ->
+ if B1 >= B2 andalso (B1-B2) rem U2 =:= 0 -> t_bitstr(0, B1);
+ true -> ?none
+ end;
+t_inf(?bitstr(U1, B1), ?bitstr(U1, B1), _Opaques) ->
+ t_bitstr(U1, B1);
+t_inf(?bitstr(U1, B1), ?bitstr(U2, B2), _Opaques) when U2 > U1 ->
+ inf_bitstr(U2, B2, U1, B1);
+t_inf(?bitstr(U1, B1), ?bitstr(U2, B2), _Opaques) ->
+ inf_bitstr(U1, B1, U2, B2);
+t_inf(?function(Domain1, Range1), ?function(Domain2, Range2), Opaques) ->
+ case t_inf(Domain1, Domain2, Opaques) of
+ ?none -> ?none;
+ Domain -> ?function(Domain, t_inf(Range1, Range2, Opaques))
+ end;
+t_inf(?identifier(Set1), ?identifier(Set2), _Opaques) ->
+ case set_intersection(Set1, Set2) of
+ ?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;
+t_inf(?nil, ?nonempty_list(_, _), _Opaques) ->
+ ?none;
+t_inf(?nonempty_list(_, _), ?nil, _Opaques) ->
+ ?none;
+t_inf(?nil, ?list(_Contents, Termination, _), Opaques) ->
+ t_inf(?nil, t_unopaque(Termination), Opaques);
+t_inf(?list(_Contents, Termination, _), ?nil, Opaques) ->
+ t_inf(?nil, t_unopaque(Termination), Opaques);
+t_inf(?list(Contents1, Termination1, Size1),
+ ?list(Contents2, Termination2, Size2), Opaques) ->
+ case t_inf(Termination1, Termination2, Opaques) of
+ ?none -> ?none;
+ Termination ->
+ case t_inf(Contents1, Contents2, Opaques) of
+ ?none ->
+ %% If none of the lists are nonempty, then the infimum is nil.
+ case (Size1 =:= ?unknown_qual) andalso (Size2 =:= ?unknown_qual) of
+ true -> t_nil();
+ false -> ?none
+ end;
+ Contents ->
+ Size =
+ case {Size1, Size2} of
+ {?unknown_qual, ?unknown_qual} -> ?unknown_qual;
+ {?unknown_qual, ?nonempty_qual} -> ?nonempty_qual;
+ {?nonempty_qual, ?unknown_qual} -> ?nonempty_qual;
+ {?nonempty_qual, ?nonempty_qual} -> ?nonempty_qual
+ end,
+ ?list(Contents, Termination, Size)
+ end
+ end;
+t_inf(?number(_, _) = T1, ?number(_, _) = T2, _Opaques) ->
+ case {T1, T2} of
+ {T, T} -> T;
+ {_, ?number(?any, ?unknown_qual)} -> T1;
+ {?number(?any, ?unknown_qual), _} -> T2;
+ {?float, ?integer(_)} -> ?none;
+ {?integer(_), ?float} -> ?none;
+ {?integer(?any), ?integer(_)} -> T2;
+ {?integer(_), ?integer(?any)} -> T1;
+ {?int_set(Set1), ?int_set(Set2)} ->
+ case set_intersection(Set1, Set2) of
+ ?none -> ?none;
+ Set -> ?int_set(Set)
+ end;
+ {?int_range(From1, To1), ?int_range(From2, To2)} ->
+ t_from_range(max(From1, From2), min(To1, To2));
+ {Range = ?int_range(_, _), ?int_set(Set)} ->
+ %% io:format("t_inf range, set args ~p ~p ~n", [T1, T2]),
+ Ans2 =
+ case set_filter(fun(X) -> in_range(X, Range) end, Set) of
+ ?none -> ?none;
+ NewSet -> ?int_set(NewSet)
+ end,
+ %% io:format("Ans2 ~p ~n", [Ans2]),
+ Ans2;
+ {?int_set(Set), ?int_range(_, _) = Range} ->
+ case set_filter(fun(X) -> in_range(X, Range) end, Set) of
+ ?none -> ?none;
+ NewSet -> ?int_set(NewSet)
+ end
+ end;
+t_inf(?product(Types1), ?product(Types2), Opaques) ->
+ L1 = length(Types1),
+ L2 = length(Types2),
+ if L1 =:= L2 -> ?product(t_inf_lists(Types1, Types2, Opaques));
+ true -> ?none
+ end;
+t_inf(?product(_), _, _Opaques) ->
+ ?none;
+t_inf(_, ?product(_), _Opaques) ->
+ ?none;
+t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Opaques) ->
+ subst_all_vars_to_any(T);
+t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Opaques) ->
+ subst_all_vars_to_any(T);
+t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Opaques) ->
+ subst_all_vars_to_any(T);
+t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Opaques) ->
+ subst_all_vars_to_any(T);
+t_inf(?tuple(Elements1, Arity, _Tag1), ?tuple(Elements2, Arity, _Tag2), Opaques) ->
+ case t_inf_lists_strict(Elements1, Elements2, Opaques) of
+ bottom -> ?none;
+ NewElements -> t_tuple(NewElements)
+ end;
+t_inf(?tuple_set(List1), ?tuple_set(List2), Opaques) ->
+ inf_tuple_sets(List1, List2, Opaques);
+t_inf(?tuple_set(List), ?tuple(_, Arity, _) = T, Opaques) ->
+ inf_tuple_sets(List, [{Arity, [T]}], Opaques);
+t_inf(?tuple(_, Arity, _) = T, ?tuple_set(List), Opaques) ->
+ inf_tuple_sets(List, [{Arity, [T]}], Opaques);
+%% be careful: here and in the next clause T can be ?opaque
+t_inf(?union(U1), T, Opaques) ->
+ ?union(U2) = force_union(T),
+ inf_union(U1, U2, Opaques);
+t_inf(T, ?union(U2), Opaques) ->
+ ?union(U1) = force_union(T),
+ inf_union(U1, U2, Opaques);
+t_inf(?opaque(Set1), ?opaque(Set2), Opaques) ->
+ inf_opaque(Set1, Set2, Opaques);
+t_inf(?opaque(_) = T1, T2, Opaques) ->
+ inf_opaque1(T2, T1, 1, Opaques);
+t_inf(T1, ?opaque(_) = T2, Opaques) ->
+ inf_opaque1(T1, T2, 2, Opaques);
+%% and as a result, the cases for ?opaque should appear *after* ?union
+t_inf(#c{}, #c{}, _) ->
+ ?none.
+
+inf_opaque1(T1, ?opaque(Set2)=T2, Pos, Opaques) ->
+ case Opaques =:= 'universe' orelse inf_is_opaque_type(T2, Pos, Opaques) of
+ false -> ?none;
+ true ->
+ List2 = set_to_list(Set2),
+ case inf_collect(T1, List2, Opaques, []) of
+ [] -> ?none;
+ OpL -> ?opaque(ordsets:from_list(OpL))
+ end
+ end.
+
+inf_is_opaque_type(T, Pos, {match, Opaques}) ->
+ is_opaque_type(T, Opaques) orelse throw({pos, [Pos]});
+inf_is_opaque_type(T, _Pos, Opaques) ->
+ is_opaque_type(T, Opaques).
+
+inf_collect(T1, [T2|List2], Opaques, OpL) ->
+ #opaque{struct = S2} = T2,
+ case t_inf(T1, S2, Opaques) of
+ ?none -> inf_collect(T1, List2, Opaques, OpL);
+ Inf ->
+ Op = T2#opaque{struct = Inf},
+ inf_collect(T1, List2, Opaques, [Op|OpL])
+ end;
+inf_collect(_T1, [], _Opaques, OpL) ->
+ OpL.
+
+combine(S, T1, T2) ->
+ #opaque{mod = Mod1, name = Name1, args = Args1} = T1,
+ #opaque{mod = Mod2, name = Name2, args = Args2} = T2,
+ Comb1 = comb(Mod1, Name1, Args1, S, T1),
+ case is_compat_opaque_names({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) of
+ true -> Comb1;
+ false -> Comb1 ++ comb(Mod2, Name2, Args2, S, T2)
+ end.
+
+comb(Mod, Name, Args, S, T) ->
+ case can_combine_opaque_names(Mod, Name, Args, S) of
+ true ->
+ ?opaque(Set) = S,
+ Set;
+ false ->
+ [T#opaque{struct = S}]
+ end.
+
+can_combine_opaque_names(Mod1, Name1, Args1,
+ ?opaque([#opaque{mod = Mod2, name = Name2, args = Args2}])) ->
+ is_compat_opaque_names({Mod1, Name1, Args1}, {Mod2, Name2, Args2});
+can_combine_opaque_names(_, _, _, _) -> false.
+
+%% Combining two lists this way can be very time consuming...
+%% Note: two parameterized opaque types are not the same if their
+%% actual parameters differ
+inf_opaque(Set1, Set2, Opaques) ->
+ List1 = inf_look_up(Set1, Opaques),
+ List2 = inf_look_up(Set2, Opaques),
+ List0 = [combine(Inf, T1, T2) ||
+ {Is1, ModNameArgs1, T1} <- List1,
+ {Is2, ModNameArgs2, T2} <- List2,
+ not t_is_none(Inf = inf_opaque_types(Is1, ModNameArgs1, T1,
+ Is2, ModNameArgs2, T2,
+ Opaques))],
+ List = lists:sort(lists:append(List0)),
+ sup_opaque(List).
+
+%% Optimization: do just one lookup.
+inf_look_up(Set, Opaques) ->
+ [{Opaques =:= 'universe' orelse inf_is_opaque_type2(T, Opaques),
+ {M, N, Args}, T} ||
+ #opaque{mod = M, name = N, args = Args} = T <- set_to_list(Set)].
+
+inf_is_opaque_type2(T, {match, Opaques}) ->
+ is_opaque_type2(T, Opaques);
+inf_is_opaque_type2(T, Opaques) ->
+ is_opaque_type2(T, Opaques).
+
+inf_opaque_types(IsOpaque1, ModNameArgs1, T1,
+ IsOpaque2, ModNameArgs2, T2, Opaques) ->
+ #opaque{struct = S1}=T1,
+ #opaque{struct = S2}=T2,
+ case
+ Opaques =:= 'universe' orelse
+ is_compat_opaque_names(ModNameArgs1, ModNameArgs2)
+ of
+ true -> t_inf(S1, S2, Opaques);
+ false ->
+ case {IsOpaque1, IsOpaque2} of
+ {true, true} -> t_inf(S1, S2, Opaques);
+ {true, false} -> t_inf(S1, ?opaque(set_singleton(T2)), Opaques);
+ {false, true} -> t_inf(?opaque(set_singleton(T1)), S2, Opaques);
+ {false, false} when element(1, Opaques) =:= match ->
+ throw({pos, [1, 2]});
+ {false, false} -> t_none()
+ end
+ end.
+
+is_compat_opaque_names(ModNameArgs, ModNameArgs) -> true;
+is_compat_opaque_names({Mod,Name,Args1}, {Mod,Name,Args2}) ->
+ is_compat_args(Args1, Args2);
+is_compat_opaque_names(_, _) -> false.
+
+is_compat_args([A1|Args1], [A2|Args2]) ->
+ is_compat_arg(A1, A2) andalso is_compat_args(Args1, Args2);
+is_compat_args([], []) -> true;
+is_compat_args(_, _) -> false.
+
+is_compat_arg(A1, A2) ->
+ is_specialization(A1, A2) orelse is_specialization(A2, A1).
+
+-spec is_specialization(erl_type(), erl_type()) -> boolean().
+
+%% Returns true if the first argument is a specialization of the
+%% second argument in the sense that every type is a specialization of
+%% any(). For example, {_,_} is a specialization of any(), but not of
+%% tuple(). Does not handle variables, but any() and unions (sort of).
+
+is_specialization(T, T) -> true;
+is_specialization(_, ?any) -> true;
+is_specialization(?any, _) -> false;
+is_specialization(?function(Domain1, Range1), ?function(Domain2, Range2)) ->
+ (is_specialization(Domain1, Domain2) andalso
+ is_specialization(Range1, Range2));
+is_specialization(?list(Contents1, Termination1, Size1),
+ ?list(Contents2, Termination2, Size2)) ->
+ (Size1 =:= Size2 andalso
+ is_specialization(Contents1, Contents2) andalso
+ is_specialization(Termination1, Termination2));
+is_specialization(?product(Types1), ?product(Types2)) ->
+ specialization_list(Types1, Types2);
+is_specialization(?tuple(?any, ?any, ?any), ?tuple(_, _, _)) -> false;
+is_specialization(?tuple(_, _, _), ?tuple(?any, ?any, ?any)) -> false;
+is_specialization(?tuple(Elements1, Arity, _),
+ ?tuple(Elements2, Arity, _)) when Arity =/= ?any ->
+ specialization_list(Elements1, Elements2);
+is_specialization(?tuple_set([{Arity, List}]),
+ ?tuple(Elements2, Arity, _)) when Arity =/= ?any ->
+ specialization_list(sup_tuple_elements(List), Elements2);
+is_specialization(?tuple(Elements1, Arity, _),
+ ?tuple_set([{Arity, List}])) when Arity =/= ?any ->
+ specialization_list(Elements1, sup_tuple_elements(List));
+is_specialization(?tuple_set(List1), ?tuple_set(List2)) ->
+ try
+ specialization_list_list([sup_tuple_elements(T) || {_Arity, T} <- List1],
+ [sup_tuple_elements(T) || {_Arity, T} <- List2])
+ catch _:_ -> false
+ end;
+is_specialization(?union(List1)=T1, ?union(List2)=T2) ->
+ case specialization_union2(T1, T2) of
+ {yes, Type1, Type2} -> is_specialization(Type1, Type2);
+ no -> specialization_list(List1, List2)
+ end;
+is_specialization(?union(List), T2) ->
+ case unify_union(List) of
+ {yes, Type} -> is_specialization(Type, T2);
+ no -> false
+ end;
+is_specialization(T1, ?union(List)) ->
+ case unify_union(List) of
+ {yes, Type} -> is_specialization(T1, Type);
+ no -> false
+ end;
+is_specialization(?opaque(_) = T1, T2) ->
+ is_specialization(t_opaque_structure(T1), T2);
+is_specialization(T1, ?opaque(_) = T2) ->
+ is_specialization(T1, t_opaque_structure(T2));
+is_specialization(?var(_), _) -> exit(error);
+is_specialization(_, ?var(_)) -> exit(error);
+is_specialization(?none, _) -> false;
+is_specialization(_, ?none) -> false;
+is_specialization(?unit, _) -> false;
+is_specialization(_, ?unit) -> false;
+is_specialization(#c{}, #c{}) -> false.
+
+specialization_list_list(LL1, LL2) ->
+ length(LL1) =:= length(LL2) andalso specialization_list_list1(LL1, LL2).
+
+specialization_list_list1([], []) -> true;
+specialization_list_list1([L1|LL1], [L2|LL2]) ->
+ specialization_list(L1, L2) andalso specialization_list_list1(LL1, LL2).
+
+specialization_list(L1, L2) ->
+ length(L1) =:= length(L2) andalso specialization_list1(L1, L2).
+
+specialization_list1([], []) -> true;
+specialization_list1([T1|L1], [T2|L2]) ->
+ is_specialization(T1, T2) andalso specialization_list1(L1, L2).
+
+specialization_union2(?union(List1)=T1, ?union(List2)=T2) ->
+ case {unify_union(List1), unify_union(List2)} of
+ {{yes, Type1}, {yes, Type2}} -> {yes, Type1, Type2};
+ {{yes, Type1}, no} -> {yes, Type1, T2};
+ {no, {yes, Type2}} -> {yes, T1, Type2};
+ {no, no} -> no
+ end.
+
+-spec t_inf_lists([erl_type()], [erl_type()]) -> [erl_type()].
+
+t_inf_lists(L1, L2) ->
+ t_inf_lists(L1, L2, 'universe').
+
+-spec t_inf_lists([erl_type()], [erl_type()], t_inf_opaques()) -> [erl_type()].
+
+t_inf_lists(L1, L2, Opaques) ->
+ t_inf_lists(L1, L2, [], Opaques).
+
+-spec t_inf_lists([erl_type()], [erl_type()], [erl_type()], [erl_type()]) -> [erl_type()].
+
+t_inf_lists([T1|Left1], [T2|Left2], Acc, Opaques) ->
+ t_inf_lists(Left1, Left2, [t_inf(T1, T2, Opaques)|Acc], Opaques);
+t_inf_lists([], [], Acc, _Opaques) ->
+ lists:reverse(Acc).
+
+%% Infimum of lists with strictness.
+%% If any element is the ?none type, the value 'bottom' is returned.
+
+-spec t_inf_lists_strict([erl_type()], [erl_type()], [erl_type()]) -> 'bottom' | [erl_type()].
+
+t_inf_lists_strict(L1, L2, Opaques) ->
+ t_inf_lists_strict(L1, L2, [], Opaques).
+
+-spec t_inf_lists_strict([erl_type()], [erl_type()], [erl_type()], [erl_type()]) -> 'bottom' | [erl_type()].
+
+t_inf_lists_strict([T1|Left1], [T2|Left2], Acc, Opaques) ->
+ case t_inf(T1, T2, Opaques) of
+ ?none -> bottom;
+ T -> t_inf_lists_strict(Left1, Left2, [T|Acc], Opaques)
+ end;
+t_inf_lists_strict([], [], Acc, _Opaques) ->
+ lists:reverse(Acc).
+
+inf_tuple_sets(L1, L2, Opaques) ->
+ case inf_tuple_sets(L1, L2, [], Opaques) of
+ [] -> ?none;
+ [{_Arity, [?tuple(_, _, _) = OneTuple]}] -> OneTuple;
+ List -> ?tuple_set(List)
+ end.
+
+inf_tuple_sets([{Arity, Tuples1}|Ts1], [{Arity, Tuples2}|Ts2], Acc, Opaques) ->
+ case inf_tuples_in_sets(Tuples1, Tuples2, Opaques) of
+ [] -> inf_tuple_sets(Ts1, Ts2, Acc, Opaques);
+ [?tuple_set([{Arity, NewTuples}])] ->
+ inf_tuple_sets(Ts1, Ts2, [{Arity, NewTuples}|Acc], Opaques);
+ NewTuples -> inf_tuple_sets(Ts1, Ts2, [{Arity, NewTuples}|Acc], Opaques)
+ end;
+inf_tuple_sets([{Arity1, _}|Ts1] = L1, [{Arity2, _}|Ts2] = L2, Acc, Opaques) ->
+ if Arity1 < Arity2 -> inf_tuple_sets(Ts1, L2, Acc, Opaques);
+ Arity1 > Arity2 -> inf_tuple_sets(L1, Ts2, Acc, Opaques)
+ end;
+inf_tuple_sets([], _, Acc, _Opaques) -> lists:reverse(Acc);
+inf_tuple_sets(_, [], Acc, _Opaques) -> lists:reverse(Acc).
+
+inf_tuples_in_sets([?tuple(Elements1, _, ?any)], L2, Opaques) ->
+ NewList = [t_inf_lists_strict(Elements1, Elements2, Opaques)
+ || ?tuple(Elements2, _, _) <- L2],
+ [t_tuple(Es) || Es <- NewList, Es =/= bottom];
+inf_tuples_in_sets(L1, [?tuple(Elements2, _, ?any)], Opaques) ->
+ NewList = [t_inf_lists_strict(Elements1, Elements2, Opaques)
+ || ?tuple(Elements1, _, _) <- L1],
+ [t_tuple(Es) || Es <- NewList, Es =/= bottom];
+inf_tuples_in_sets(L1, L2, Opaques) ->
+ inf_tuples_in_sets2(L1, L2, [], Opaques).
+
+inf_tuples_in_sets2([?tuple(Elements1, Arity, Tag)|Ts1],
+ [?tuple(Elements2, Arity, Tag)|Ts2], Acc, Opaques) ->
+ case t_inf_lists_strict(Elements1, Elements2, Opaques) of
+ bottom -> inf_tuples_in_sets2(Ts1, Ts2, Acc, Opaques);
+ NewElements ->
+ inf_tuples_in_sets2(Ts1, Ts2, [?tuple(NewElements, Arity, Tag)|Acc],
+ Opaques)
+ end;
+inf_tuples_in_sets2([?tuple(_, _, Tag1)|Ts1] = L1,
+ [?tuple(_, _, Tag2)|Ts2] = L2, Acc, Opaques) ->
+ if Tag1 < Tag2 -> inf_tuples_in_sets2(Ts1, L2, Acc, Opaques);
+ Tag1 > Tag2 -> inf_tuples_in_sets2(L1, Ts2, Acc, Opaques)
+ end;
+inf_tuples_in_sets2([], _, Acc, _Opaques) -> lists:reverse(Acc);
+inf_tuples_in_sets2(_, [], Acc, _Opaques) -> lists:reverse(Acc).
+
+inf_union(U1, U2, Opaques) ->
+ OpaqueFun =
+ fun(Union1, Union2, InfFun) ->
+ [_,_,_,_,_,_,_,_,Opaque,_] = Union1,
+ [A,B,F,I,L,N,T,M,_,Map] = Union2,
+ List = [A,B,F,I,L,N,T,M,Map],
+ inf_union_collect(List, Opaque, InfFun, [], [])
+ end,
+ {O1, ThrowList1} =
+ OpaqueFun(U1, U2, fun(E, Opaque) -> t_inf(Opaque, E, Opaques) end),
+ {O2, ThrowList2}
+ = OpaqueFun(U2, U1, fun(E, Opaque) -> t_inf(E, Opaque, Opaques) end),
+ {Union, ThrowList3} = inf_union(U1, U2, 0, [], [], Opaques),
+ ThrowList = lists:merge3(ThrowList1, ThrowList2, ThrowList3),
+ case t_sup([O1, O2, Union]) of
+ ?none when ThrowList =/= [] -> throw({pos, lists:usort(ThrowList)});
+ Sup -> Sup
+ end.
+
+inf_union_collect([], _Opaque, _InfFun, InfList, ThrowList) ->
+ {t_sup(InfList), lists:usort(ThrowList)};
+inf_union_collect([?none|L], Opaque, InfFun, InfList, ThrowList) ->
+ inf_union_collect(L, Opaque, InfFun, [?none|InfList], ThrowList);
+inf_union_collect([E|L], Opaque, InfFun, InfList, ThrowList) ->
+ try InfFun(E, Opaque)of
+ Inf ->
+ inf_union_collect(L, Opaque, InfFun, [Inf|InfList], ThrowList)
+ catch throw:{pos, Ns} ->
+ inf_union_collect(L, Opaque, InfFun, InfList, Ns ++ ThrowList)
+ end.
+
+inf_union([?none|Left1], [?none|Left2], N, Acc, ThrowList, Opaques) ->
+ inf_union(Left1, Left2, N, [?none|Acc], ThrowList, Opaques);
+inf_union([T1|Left1], [T2|Left2], N, Acc, ThrowList, Opaques) ->
+ try t_inf(T1, T2, Opaques) of
+ ?none -> inf_union(Left1, Left2, N, [?none|Acc], ThrowList, Opaques);
+ T -> inf_union(Left1, Left2, N+1, [T|Acc], ThrowList, Opaques)
+ catch throw:{pos, Ns} ->
+ inf_union(Left1, Left2, N, [?none|Acc], Ns ++ ThrowList, Opaques)
+ end;
+inf_union([], [], N, Acc, ThrowList, _Opaques) ->
+ if N =:= 0 -> {?none, ThrowList};
+ N =:= 1 ->
+ [Type] = [T || T <- Acc, T =/= ?none],
+ {Type, ThrowList};
+ N >= 2 -> {?union(lists:reverse(Acc)), ThrowList}
+ end.
+
+inf_bitstr(U1, B1, U2, B2) ->
+ GCD = gcd(U1, U2),
+ case (B2-B1) rem GCD of
+ 0 ->
+ U = (U1*U2) div GCD,
+ B = findfirst(0, 0, U1, B1, U2, B2),
+ t_bitstr(U, B);
+ _ ->
+ ?none
+ end.
+
+findfirst(N1, N2, U1, B1, U2, B2) ->
+ Val1 = U1*N1+B1,
+ Val2 = U2*N2+B2,
+ if Val1 =:= Val2 ->
+ Val1;
+ Val1 > Val2 ->
+ findfirst(N1, N2+1, U1, B1, U2, B2);
+ Val1 < Val2 ->
+ findfirst(N1+1, N2, U1, B1, U2, B2)
+ end.
+
+%%-----------------------------------------------------------------------------
+%% Substitution of variables
+%%
+
+-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_aux(T, Map);
+ false -> T
+ end.
+
+-spec subst_all_vars_to_any(erl_type()) -> erl_type().
+
+subst_all_vars_to_any(T) ->
+ t_subst(T, #{}).
+
+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), 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, Map) 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_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), 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.
+
+%%-----------------------------------------------------------------------------
+%% Unification
+%%
+
+-type t_unify_ret() :: {erl_type(), [{_, erl_type()}]}.
+
+-spec t_unify(erl_type(), erl_type()) -> t_unify_ret().
+
+t_unify(T1, T2) ->
+ {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 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;
+ {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 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 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),
+ {Range, VarMap2} = t_unify(Range1, Range2, VarMap1),
+ {?function(Domain, Range), VarMap2};
+t_unify(?list(Contents1, Termination1, Size),
+ ?list(Contents2, Termination2, Size), VarMap) ->
+ {Contents, VarMap1} = t_unify(Contents1, Contents2, VarMap),
+ {Termination, VarMap2} = t_unify(Termination1, Termination2, VarMap1),
+ {?list(Contents, Termination, Size), VarMap2};
+t_unify(?product(Types1), ?product(Types2), VarMap) ->
+ {Types, VarMap1} = unify_lists(Types1, Types2, VarMap),
+ {?product(Types), VarMap1};
+t_unify(?tuple(?any, ?any, ?any) = T, ?tuple(?any, ?any, ?any), VarMap) ->
+ {T, VarMap};
+t_unify(?tuple(Elements1, Arity, _),
+ ?tuple(Elements2, Arity, _), VarMap) when Arity =/= ?any ->
+ {NewElements, VarMap1} = unify_lists(Elements1, Elements2, VarMap),
+ {t_tuple(NewElements), VarMap1};
+t_unify(?tuple_set([{Arity, _}]) = T1,
+ ?tuple(_, Arity, _) = T2, VarMap) when Arity =/= ?any ->
+ unify_tuple_set_and_tuple1(T1, T2, VarMap);
+t_unify(?tuple(_, Arity, _) = T1,
+ ?tuple_set([{Arity, _}]) = T2, VarMap) when Arity =/= ?any ->
+ unify_tuple_set_and_tuple2(T1, T2, VarMap);
+t_unify(?tuple_set(List1) = T1, ?tuple_set(List2) = T2, VarMap) ->
+ try
+ unify_lists(lists:append([T || {_Arity, T} <- List1]),
+ lists:append([T || {_Arity, T} <- List2]), VarMap)
+ of
+ {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) ->
+ t_unify(T1, t_opaque_structure(T2), VarMap);
+t_unify(?opaque(_) = T1, T2, VarMap) ->
+ t_unify(t_opaque_structure(T1), T2, VarMap);
+t_unify(T, T, VarMap) ->
+ {T, VarMap};
+t_unify(?union(_)=T1, ?union(_)=T2, VarMap) ->
+ {Type1, Type2} = unify_union2(T1, T2),
+ t_unify(Type1, Type2, VarMap);
+t_unify(?union(_)=T1, T2, VarMap) ->
+ t_unify(unify_union1(T1, T1, T2), T2, VarMap);
+t_unify(T1, ?union(_)=T2, VarMap) ->
+ t_unify(T1, unify_union1(T2, T1, T2), VarMap);
+t_unify(T1, T2, _) ->
+ throw({mismatch, T1, T2}).
+
+unify_union2(?union(List1)=T1, ?union(List2)=T2) ->
+ case {unify_union(List1), unify_union(List2)} of
+ {{yes, Type1}, {yes, Type2}} -> {Type1, Type2};
+ {{yes, Type1}, no} -> {Type1, T2};
+ {no, {yes, Type2}} -> {T1, Type2};
+ {no, no} -> throw({mismatch, T1, T2})
+ end.
+
+unify_union1(?union(List), T1, T2) ->
+ case unify_union(List) of
+ {yes, Type} -> Type;
+ no -> throw({mismatch, T1, T2})
+ end.
+
+unify_union(List) ->
+ [A,B,F,I,L,N,T,M,O,Map] = List,
+ if O =:= ?none -> no;
+ true ->
+ S = t_opaque_structure(O),
+ {yes, t_sup([A,B,F,I,L,N,T,M,S,Map])}
+ end.
+
+-spec is_opaque_type(erl_type(), [erl_type()]) -> boolean().
+
+%% An opaque type is a union of types. Returns true iff any of the type
+%% names (Module and Name) of the first argument (the opaque type to
+%% check) occurs in any of the opaque types of the second argument.
+is_opaque_type(?opaque(Elements), Opaques) ->
+ lists:any(fun(Opaque) -> is_opaque_type2(Opaque, Opaques) end, Elements).
+
+is_opaque_type2(#opaque{mod = Mod1, name = Name1, args = Args1}, Opaques) ->
+ F1 = fun(?opaque(Es)) ->
+ F2 = fun(#opaque{mod = Mod, name = Name, args = Args}) ->
+ is_type_name(Mod1, Name1, Args1, Mod, Name, Args)
+ end,
+ lists:any(F2, Es)
+ end,
+ lists:any(F1, Opaques).
+
+is_type_name(Mod, Name, Args1, Mod, Name, Args2) ->
+ length(Args1) =:= length(Args2);
+is_type_name(_Mod1, _Name1, _Args1, _Mod2, _Name2, _Args2) ->
+ false.
+
+%% Two functions since t_unify is not symmetric.
+unify_tuple_set_and_tuple1(?tuple_set([{Arity, List}]),
+ ?tuple(Elements2, Arity, _), VarMap) ->
+ %% Can only work if the single tuple has variables at correct places.
+ %% Collapse the tuple set.
+ {NewElements, VarMap1} =
+ unify_lists(sup_tuple_elements(List), Elements2, VarMap),
+ {t_tuple(NewElements), VarMap1}.
+
+unify_tuple_set_and_tuple2(?tuple(Elements2, Arity, _),
+ ?tuple_set([{Arity, List}]), VarMap) ->
+ %% Can only work if the single tuple has variables at correct places.
+ %% Collapse the tuple set.
+ {NewElements, VarMap1} =
+ unify_lists(Elements2, sup_tuple_elements(List), VarMap),
+ {t_tuple(NewElements), VarMap1}.
+
+unify_lists(L1, L2, VarMap) ->
+ unify_lists(L1, L2, VarMap, []).
+
+unify_lists([T1|Left1], [T2|Left2], VarMap, Acc) ->
+ {NewT, NewVarMap} = t_unify(T1, T2, VarMap),
+ unify_lists(Left1, Left2, NewVarMap, [NewT|Acc]);
+unify_lists([], [], VarMap, Acc) ->
+ {lists:reverse(Acc), VarMap}.
+
+%%t_assign_variables_to_subtype(T1, T2) ->
+%% try
+%% Dict = assign_vars(T1, T2, dict:new()),
+%% {ok, dict:map(fun(_Param, List) -> t_sup(List) end, Dict)}
+%% catch
+%% throw:error -> error
+%% end.
+
+%%assign_vars(_, ?var(_), _Dict) ->
+%% erlang:error("Variable in right hand side of assignment");
+%%assign_vars(?any, _, Dict) ->
+%% Dict;
+%%assign_vars(?var(_) = Var, Type, Dict) ->
+%% store_var(Var, Type, Dict);
+%%assign_vars(?function(Domain1, Range1), ?function(Domain2, Range2), Dict) ->
+%% DomainList =
+%% case Domain2 of
+%% ?any -> [];
+%% ?product(List) -> List
+%% end,
+%% case any_none([Range2|DomainList]) of
+%% true -> throw(error);
+%% false ->
+%% Dict1 = assign_vars(Domain1, Domain2, Dict),
+%% assign_vars(Range1, Range2, Dict1)
+%% end;
+%%assign_vars(?list(_Contents, _Termination, ?any), ?nil, Dict) ->
+%% Dict;
+%%assign_vars(?list(Contents1, Termination1, Size1),
+%% ?list(Contents2, Termination2, Size2), Dict) ->
+%% Dict1 = assign_vars(Contents1, Contents2, Dict),
+%% Dict2 = assign_vars(Termination1, Termination2, Dict1),
+%% case {Size1, Size2} of
+%% {S, S} -> Dict2;
+%% {?any, ?nonempty_qual} -> Dict2;
+%% {_, _} -> throw(error)
+%% end;
+%%assign_vars(?product(Types1), ?product(Types2), Dict) ->
+%% case length(Types1) =:= length(Types2) of
+%% true -> assign_vars_lists(Types1, Types2, Dict);
+%% false -> throw(error)
+%% end;
+%%assign_vars(?tuple(?any, ?any, ?any), ?tuple(?any, ?any, ?any), Dict) ->
+%% Dict;
+%%assign_vars(?tuple(?any, ?any, ?any), ?tuple(_, _, _), Dict) ->
+%% Dict;
+%%assign_vars(?tuple(Elements1, Arity, _),
+%% ?tuple(Elements2, Arity, _), Dict) when Arity =/= ?any ->
+%% assign_vars_lists(Elements1, Elements2, Dict);
+%%assign_vars(?tuple_set(_) = T, ?tuple_set(List2), Dict) ->
+%% %% All Rhs tuples must already be subtypes of Lhs, so we can take
+%% %% each one separatly.
+%% assign_vars_lists([T || _ <- List2], List2, Dict);
+%%assign_vars(?tuple(?any, ?any, ?any), ?tuple_set(_), Dict) ->
+%% Dict;
+%%assign_vars(?tuple(_, Arity, _) = T1, ?tuple_set(List), Dict) ->
+%% case reduce_tuple_tags(List) of
+%% [Tuple = ?tuple(_, Arity, _)] -> assign_vars(T1, Tuple, Dict);
+%% _ -> throw(error)
+%% end;
+%%assign_vars(?tuple_set(List), ?tuple(_, Arity, Tag) = T2, Dict) ->
+%% case [T || ?tuple(_, Arity1, Tag1) = T <- List,
+%% Arity1 =:= Arity, Tag1 =:= Tag] of
+%% [] -> throw(error);
+%% [T1] -> assign_vars(T1, T2, Dict)
+%% end;
+%%assign_vars(?union(U1), T2, Dict) ->
+%% ?union(U2) = force_union(T2),
+%% assign_vars_lists(U1, U2, Dict);
+%%assign_vars(T, T, Dict) ->
+%% Dict;
+%%assign_vars(T1, T2, Dict) ->
+%% case t_is_subtype(T2, T1) of
+%% false -> throw(error);
+%% true -> Dict
+%% end.
+
+%%assign_vars_lists([T1|Left1], [T2|Left2], Dict) ->
+%% assign_vars_lists(Left1, Left2, assign_vars(T1, T2, Dict));
+%%assign_vars_lists([], [], Dict) ->
+%% Dict.
+
+%%store_var(?var(Id), Type, Dict) ->
+%% case dict:find(Id, Dict) of
+%% error -> dict:store(Id, [Type], Dict);
+%% {ok, _VarType0} -> dict:update(Id, fun(X) -> [Type|X] end, Dict)
+%% end.
+
+%%-----------------------------------------------------------------------------
+%% Subtraction.
+%%
+%% Note that the subtraction is an approximation since we do not have
+%% negative types. Also, tuples and products should be handled using
+%% the cartesian product of the elements, but this is not feasible to
+%% do.
+%%
+%% Example: {a|b,c|d}\{a,d} = {a,c}|{a,d}|{b,c}|{b,d} \ {a,d} =
+%% = {a,c}|{b,c}|{b,d} = {a|b,c|d}
+%%
+%% Instead, we can subtract if all elements but one becomes none after
+%% subtracting element-wise.
+%%
+%% Example: {a|b,c|d}\{a|b,d} = {a,c}|{a,d}|{b,c}|{b,d} \ {a,d}|{b,d} =
+%% = {a,c}|{b,c} = {a|b,c}
+
+-spec t_subtract_list(erl_type(), [erl_type()]) -> erl_type().
+
+t_subtract_list(T1, [T2|Left]) ->
+ t_subtract_list(t_subtract(T1, T2), Left);
+t_subtract_list(T, []) ->
+ T.
+
+-spec t_subtract(erl_type(), erl_type()) -> erl_type().
+
+t_subtract(_, ?any) -> ?none;
+t_subtract(T, ?var(_)) -> T;
+t_subtract(?any, _) -> ?any;
+t_subtract(?var(_) = T, _) -> T;
+t_subtract(T, ?unit) -> T;
+t_subtract(?unit, _) -> ?unit;
+t_subtract(?none, _) -> ?none;
+t_subtract(T, ?none) -> T;
+t_subtract(?atom(Set1), ?atom(Set2)) ->
+ case set_subtract(Set1, Set2) of
+ ?none -> ?none;
+ Set -> ?atom(Set)
+ end;
+t_subtract(?bitstr(U1, B1), ?bitstr(U2, B2)) ->
+ subtract_bin(t_bitstr(U1, B1), t_inf(t_bitstr(U1, B1), t_bitstr(U2, B2)));
+t_subtract(?function(_, _) = T1, ?function(_, _) = T2) ->
+ case t_is_subtype(T1, T2) of
+ true -> ?none;
+ false -> T1
+ end;
+t_subtract(?identifier(Set1), ?identifier(Set2)) ->
+ case set_subtract(Set1, Set2) of
+ ?none -> ?none;
+ Set -> ?identifier(Set)
+ end;
+t_subtract(?opaque(_)=T1, ?opaque(_)=T2) ->
+ opaque_subtract(T1, t_opaque_structure(T2));
+t_subtract(?opaque(_)=T1, T2) ->
+ opaque_subtract(T1, T2);
+t_subtract(T1, ?opaque(_)=T2) ->
+ t_subtract(T1, t_opaque_structure(T2));
+t_subtract(?matchstate(Pres1, Slots1), ?matchstate(Pres2, _Slots2)) ->
+ Pres = t_subtract(Pres1, Pres2),
+ case t_is_none(Pres) of
+ true -> ?none;
+ false -> ?matchstate(Pres, Slots1)
+ end;
+t_subtract(?matchstate(Present, Slots), _) ->
+ ?matchstate(Present, Slots);
+t_subtract(?nil, ?nil) ->
+ ?none;
+t_subtract(?nil, ?nonempty_list(_, _)) ->
+ ?nil;
+t_subtract(?nil, ?list(_, _, _)) ->
+ ?none;
+t_subtract(?list(Contents, Termination, _Size) = T, ?nil) ->
+ case Termination =:= ?nil of
+ true -> ?nonempty_list(Contents, Termination);
+ false -> T
+ end;
+t_subtract(?list(Contents1, Termination1, Size1) = T,
+ ?list(Contents2, Termination2, Size2)) ->
+ case t_is_subtype(Contents1, Contents2) of
+ true ->
+ case t_is_subtype(Termination1, Termination2) of
+ true ->
+ case {Size1, Size2} of
+ {?nonempty_qual, ?unknown_qual} -> ?none;
+ {?unknown_qual, ?nonempty_qual} -> ?nil;
+ {S, S} -> ?none
+ end;
+ false ->
+ %% If the termination is not covered by the subtracted type
+ %% we cannot really say anything about the result.
+ T
+ end;
+ false ->
+ %% All contents must be covered if there is going to be any
+ %% change to the list.
+ T
+ end;
+t_subtract(?float, ?float) -> ?none;
+t_subtract(?number(_, _) = T1, ?float) -> t_inf(T1, t_integer());
+t_subtract(?float, ?number(_Set, Tag)) ->
+ case Tag of
+ ?unknown_qual -> ?none;
+ _ -> ?float
+ end;
+t_subtract(?number(_, _), ?number(?any, ?unknown_qual)) -> ?none;
+t_subtract(?number(_, _) = T1, ?integer(?any)) -> t_inf(?float, T1);
+t_subtract(?int_set(Set1), ?int_set(Set2)) ->
+ case set_subtract(Set1, Set2) of
+ ?none -> ?none;
+ Set -> ?int_set(Set)
+ end;
+t_subtract(?int_range(From1, To1) = T1, ?int_range(_, _) = T2) ->
+ case t_inf(T1, T2) of
+ ?none -> T1;
+ ?int_range(From1, To1) -> ?none;
+ ?int_range(neg_inf, To) -> t_from_range(To + 1, To1);
+ ?int_range(From, pos_inf) -> t_from_range(From1, From - 1);
+ ?int_range(From, To) -> t_sup(t_from_range(From1, From - 1),
+ t_from_range(To + 1, To))
+ end;
+t_subtract(?int_range(From, To) = T1, ?int_set(Set)) ->
+ NewFrom = case set_is_element(From, Set) of
+ true -> From + 1;
+ false -> From
+ end,
+ NewTo = case set_is_element(To, Set) of
+ true -> To - 1;
+ false -> To
+ end,
+ if (NewFrom =:= From) and (NewTo =:= To) -> T1;
+ true -> t_from_range(NewFrom, NewTo)
+ end;
+t_subtract(?int_set(Set), ?int_range(From, To)) ->
+ case set_filter(fun(X) -> not ((X =< From) orelse (X >= To)) end, Set) of
+ ?none -> ?none;
+ NewSet -> ?int_set(NewSet)
+ end;
+t_subtract(?integer(?any) = T1, ?integer(_)) -> T1;
+t_subtract(?number(_, _) = T1, ?number(_, _)) -> T1;
+t_subtract(?tuple(_, _, _), ?tuple(?any, ?any, ?any)) -> ?none;
+t_subtract(?tuple_set(_), ?tuple(?any, ?any, ?any)) -> ?none;
+t_subtract(?tuple(?any, ?any, ?any) = T1, ?tuple_set(_)) -> T1;
+t_subtract(?tuple(Elements1, Arity1, _Tag1) = T1,
+ ?tuple(Elements2, Arity2, _Tag2)) ->
+ if Arity1 =/= Arity2 -> T1;
+ Arity1 =:= Arity2 ->
+ NewElements = t_subtract_lists(Elements1, Elements2),
+ case [E || E <- NewElements, E =/= ?none] of
+ [] -> ?none;
+ [_] -> t_tuple(replace_nontrivial_element(Elements1, NewElements));
+ _ -> T1
+ end
+ end;
+t_subtract(?tuple_set(List1) = T1, ?tuple(_, Arity, _) = T2) ->
+ case orddict:find(Arity, List1) of
+ error -> T1;
+ {ok, List2} ->
+ TuplesLeft0 = [Tuple || {_Arity, Tuple} <- orddict:erase(Arity, List1)],
+ TuplesLeft1 = lists:append(TuplesLeft0),
+ t_sup([t_subtract(L, T2) || L <- List2] ++ TuplesLeft1)
+ end;
+t_subtract(?tuple(_, Arity, _) = T1, ?tuple_set(List1)) ->
+ case orddict:find(Arity, List1) of
+ error -> T1;
+ {ok, List2} -> t_inf([t_subtract(T1, L) || L <- List2])
+ end;
+t_subtract(?tuple_set(_) = T1, ?tuple_set(_) = T2) ->
+ t_sup([t_subtract(T, T2) || T <- t_tuple_subtypes(T1)]);
+t_subtract(?product(Elements1) = T1, ?product(Elements2)) ->
+ Arity1 = length(Elements1),
+ Arity2 = length(Elements2),
+ if Arity1 =/= Arity2 -> T1;
+ Arity1 =:= Arity2 ->
+ NewElements = t_subtract_lists(Elements1, Elements2),
+ case [E || E <- NewElements, E =/= ?none] of
+ [] -> ?none;
+ [_] -> t_product(replace_nontrivial_element(Elements1, NewElements));
+ _ -> T1
+ end
+ end;
+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(_)) ->
+ T;
+t_subtract(?union(U1), ?union(U2)) ->
+ subtract_union(U1, U2);
+t_subtract(T1, T2) ->
+ ?union(U1) = force_union(T1),
+ ?union(U2) = force_union(T2),
+ subtract_union(U1, U2).
+
+-spec opaque_subtract(erl_type(), erl_type()) -> erl_type().
+
+opaque_subtract(?opaque(Set1), T2) ->
+ List = [T1#opaque{struct = Sub} ||
+ #opaque{struct = S1}=T1 <- set_to_list(Set1),
+ not t_is_none(Sub = t_subtract(S1, T2))],
+ case List of
+ [] -> ?none;
+ _ -> ?opaque(ordsets:from_list(List))
+ end.
+
+-spec t_subtract_lists([erl_type()], [erl_type()]) -> [erl_type()].
+
+t_subtract_lists(L1, L2) ->
+ t_subtract_lists(L1, L2, []).
+
+-spec t_subtract_lists([erl_type()], [erl_type()], [erl_type()]) -> [erl_type()].
+
+t_subtract_lists([T1|Left1], [T2|Left2], Acc) ->
+ t_subtract_lists(Left1, Left2, [t_subtract(T1, T2)|Acc]);
+t_subtract_lists([], [], Acc) ->
+ lists:reverse(Acc).
+
+-spec subtract_union([erl_type(),...], [erl_type(),...]) -> erl_type().
+
+subtract_union(U1, U2) ->
+ [A1,B1,F1,I1,L1,N1,T1,M1,O1,Map1] = U1,
+ [A2,B2,F2,I2,L2,N2,T2,M2,O2,Map2] = U2,
+ List1 = [A1,B1,F1,I1,L1,N1,T1,M1,?none,Map1],
+ List2 = [A2,B2,F2,I2,L2,N2,T2,M2,?none,Map2],
+ Sub1 = subtract_union(List1, List2, 0, []),
+ O = if O1 =:= ?none -> O1;
+ true -> t_subtract(O1, ?union(U2))
+ end,
+ Sub2 = if O2 =:= ?none -> Sub1;
+ true -> t_subtract(Sub1, t_opaque_structure(O2))
+ end,
+ t_sup(O, Sub2).
+
+-spec subtract_union([erl_type()], [erl_type()], non_neg_integer(), [erl_type()]) -> erl_type().
+
+subtract_union([T1|Left1], [T2|Left2], N, Acc) ->
+ case t_subtract(T1, T2) of
+ ?none -> subtract_union(Left1, Left2, N, [?none|Acc]);
+ T -> subtract_union(Left1, Left2, N+1, [T|Acc])
+ end;
+subtract_union([], [], 0, _Acc) ->
+ ?none;
+subtract_union([], [], 1, Acc) ->
+ [T] = [X || X <- Acc, X =/= ?none],
+ T;
+subtract_union([], [], N, Acc) when is_integer(N), N > 1 ->
+ ?union(lists:reverse(Acc)).
+
+replace_nontrivial_element(El1, El2) ->
+ replace_nontrivial_element(El1, El2, []).
+
+replace_nontrivial_element([T1|Left1], [?none|Left2], Acc) ->
+ replace_nontrivial_element(Left1, Left2, [T1|Acc]);
+replace_nontrivial_element([_|Left1], [T2|_], Acc) ->
+ lists:reverse(Acc) ++ [T2|Left1].
+
+subtract_bin(?bitstr(U1, B1), ?bitstr(U1, B1)) ->
+ ?none;
+subtract_bin(?bitstr(U1, B1), ?none) ->
+ t_bitstr(U1, B1);
+subtract_bin(?bitstr(U1, B1), ?bitstr(0, B1)) ->
+ t_bitstr(U1, B1+U1);
+subtract_bin(?bitstr(U1, B1), ?bitstr(U1, B2)) ->
+ if (B1+U1) =/= B2 -> t_bitstr(0, B1);
+ true -> t_bitstr(U1, B1)
+ end;
+subtract_bin(?bitstr(U1, B1), ?bitstr(U2, B2)) ->
+ if (2 * U1) =:= U2 ->
+ if B1 =:= B2 ->
+ t_bitstr(U2, B1+U1);
+ (B1 + U1) =:= B2 ->
+ t_bitstr(U2, B1);
+ true ->
+ t_bitstr(U1, B1)
+ end;
+ true ->
+ t_bitstr(U1, B1)
+ end.
+
+%%-----------------------------------------------------------------------------
+%% Relations
+%%
+
+-spec t_is_equal(erl_type(), erl_type()) -> boolean().
+
+t_is_equal(T, T) -> true;
+t_is_equal(_, _) -> false.
+
+-spec t_is_subtype(erl_type(), erl_type()) -> boolean().
+
+t_is_subtype(T1, T2) ->
+ Inf = t_inf(T1, T2),
+ subtype_is_equal(T1, Inf).
+
+%% The subtype relation has to behave correctly irrespective of opaque
+%% types.
+subtype_is_equal(T, T) -> true;
+subtype_is_equal(T1, T2) ->
+ t_is_equal(case t_contains_opaque(T1) of
+ true -> t_unopaque(T1);
+ false -> T1
+ end,
+ case t_contains_opaque(T2) of
+ true -> t_unopaque(T2);
+ false -> T2
+ end).
+
+-spec t_is_instance(erl_type(), erl_type()) -> boolean().
+
+%% XXX. To be removed.
+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) ->
+ t_unopaque(T, 'universe').
+
+-spec t_unopaque(erl_type(), opaques()) -> erl_type().
+
+t_unopaque(?opaque(_) = T, Opaques) ->
+ case Opaques =:= 'universe' orelse is_opaque_type(T, Opaques) of
+ true -> t_unopaque(t_opaque_structure(T), Opaques);
+ false -> T
+ end;
+t_unopaque(?list(ElemT, Termination, Sz), Opaques) ->
+ ?list(t_unopaque(ElemT, Opaques), t_unopaque(Termination, Opaques), Sz);
+t_unopaque(?tuple(?any, _, _) = T, _) -> T;
+t_unopaque(?tuple(ArgTs, Sz, Tag), Opaques) when is_list(ArgTs) ->
+ NewArgTs = [t_unopaque(A, Opaques) || A <- ArgTs],
+ ?tuple(NewArgTs, Sz, Tag);
+t_unopaque(?tuple_set(Set), Opaques) ->
+ NewSet = [{Sz, [t_unopaque(T, Opaques) || T <- Tuples]}
+ || {Sz, Tuples} <- Set],
+ ?tuple_set(NewSet);
+t_unopaque(?product(Types), Opaques) ->
+ ?product([t_unopaque(T, Opaques) || T <- Types]);
+t_unopaque(?function(Domain, Range), Opaques) ->
+ ?function(t_unopaque(Domain, Opaques), t_unopaque(Range, Opaques));
+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,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.
+
+%%-----------------------------------------------------------------------------
+%% K-depth abstraction.
+%%
+%% t_limit/2 is the exported function, which checks the type of the
+%% second argument and calls the module local t_limit_k/2 function.
+%%
+
+-spec t_limit(erl_type(), integer()) -> erl_type().
+
+t_limit(Term, K) when is_integer(K) ->
+ t_limit_k(Term, K).
+
+t_limit_k(_, K) when K =< 0 -> ?any;
+t_limit_k(?tuple(?any, ?any, ?any) = T, _K) -> T;
+t_limit_k(?tuple(Elements, Arity, _), K) ->
+ if K =:= 1 -> t_tuple(Arity);
+ true -> t_tuple([t_limit_k(E, K-1) || E <- Elements])
+ end;
+t_limit_k(?tuple_set(_) = T, K) ->
+ t_sup([t_limit_k(Tuple, K) || Tuple <- t_tuple_subtypes(T)]);
+t_limit_k(?list(Elements, Termination, Size), K) ->
+ NewTermination =
+ if K =:= 1 ->
+ %% We do not want to lose the termination information.
+ t_limit_k(Termination, K);
+ true -> t_limit_k(Termination, K - 1)
+ end,
+ NewElements = t_limit_k(Elements, K - 1),
+ TmpList = t_cons(NewElements, NewTermination),
+ case Size of
+ ?nonempty_qual -> TmpList;
+ ?unknown_qual ->
+ ?list(NewElements1, NewTermination1, _) = TmpList,
+ ?list(NewElements1, NewTermination1, ?unknown_qual)
+ end;
+t_limit_k(?function(Domain, Range), K) ->
+ %% The domain is either a product or any() so we do not decrease the K.
+ ?function(t_limit_k(Domain, K), t_limit_k(Range, K-1));
+t_limit_k(?product(Elements), K) ->
+ ?product([t_limit_k(X, K - 1) || X <- Elements]);
+t_limit_k(?union(Elements), K) ->
+ ?union([t_limit_k(X, K) || X <- Elements]);
+t_limit_k(?opaque(Es), K) ->
+ List = [begin
+ NewS = t_limit_k(S, 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.
+
+%%============================================================================
+%%
+%% Abstract records. Used for comparing contracts.
+%%
+%%============================================================================
+
+-spec t_abstract_records(erl_type(), type_table()) -> erl_type().
+
+t_abstract_records(?list(Contents, Termination, Size), RecDict) ->
+ case t_abstract_records(Contents, RecDict) of
+ ?none -> ?none;
+ NewContents ->
+ %% Be careful here to make the termination collapse if necessary.
+ case t_abstract_records(Termination, RecDict) 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_abstract_records(?function(Domain, Range), RecDict) ->
+ ?function(t_abstract_records(Domain, RecDict),
+ t_abstract_records(Range, RecDict));
+t_abstract_records(?product(Types), RecDict) ->
+ ?product([t_abstract_records(T, RecDict) || T <- Types]);
+t_abstract_records(?union(Types), RecDict) ->
+ t_sup([t_abstract_records(T, RecDict) || T <- Types]);
+t_abstract_records(?tuple(?any, ?any, ?any) = T, _RecDict) ->
+ T;
+t_abstract_records(?tuple(Elements, Arity, ?atom(_) = Tag), RecDict) ->
+ [TagAtom] = atom_vals(Tag),
+ case lookup_record(TagAtom, Arity - 1, RecDict) of
+ error -> t_tuple([t_abstract_records(E, RecDict) || E <- Elements]);
+ {ok, Fields} -> t_tuple([Tag|[T || {_Name, _Abstr, T} <- Fields]])
+ end;
+t_abstract_records(?tuple(Elements, _Arity, _Tag), RecDict) ->
+ t_tuple([t_abstract_records(E, RecDict) || E <- Elements]);
+t_abstract_records(?tuple_set(_) = Tuples, RecDict) ->
+ t_sup([t_abstract_records(T, RecDict) || T <- t_tuple_subtypes(Tuples)]);
+t_abstract_records(?opaque(_)=Type, RecDict) ->
+ t_abstract_records(t_opaque_structure(Type), RecDict);
+t_abstract_records(T, _RecDict) ->
+ T.
+
+%% Map over types. Depth first. Used by the contract checker. ?list is
+%% not fully implemented so take care when changing the type in Termination.
+
+-spec t_map(fun((erl_type()) -> erl_type()), erl_type()) -> erl_type().
+
+t_map(Fun, ?list(Contents, Termination, Size)) ->
+ Fun(?list(t_map(Fun, Contents), t_map(Fun, Termination), Size));
+t_map(Fun, ?function(Domain, Range)) ->
+ Fun(?function(t_map(Fun, Domain), t_map(Fun, Range)));
+t_map(Fun, ?product(Types)) ->
+ Fun(?product([t_map(Fun, T) || T <- Types]));
+t_map(Fun, ?union(Types)) ->
+ Fun(t_sup([t_map(Fun, T) || T <- Types]));
+t_map(Fun, ?tuple(?any, ?any, ?any) = T) ->
+ Fun(T);
+t_map(Fun, ?tuple(Elements, _Arity, _Tag)) ->
+ Fun(t_tuple([t_map(Fun, E) || E <- Elements]));
+t_map(Fun, ?tuple_set(_) = Tuples) ->
+ Fun(t_sup([t_map(Fun, T) || T <- t_tuple_subtypes(Tuples)]));
+t_map(Fun, ?opaque(Set)) ->
+ L = [Opaque#opaque{struct = NewS} ||
+ #opaque{struct = S} = Opaque <- set_to_list(Set),
+ not t_is_none(NewS = t_map(Fun, S))],
+ Fun(case L of
+ [] -> ?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).
+
+%%=============================================================================
+%%
+%% Prettyprinter
+%%
+%%=============================================================================
+
+-spec t_to_string(erl_type()) -> string().
+
+t_to_string(T) ->
+ t_to_string(T, dict:new()).
+
+-spec t_to_string(erl_type(), type_table()) -> string().
+
+t_to_string(?any, _RecDict) ->
+ "any()";
+t_to_string(?none, _RecDict) ->
+ "none()";
+t_to_string(?unit, _RecDict) ->
+ "no_return()";
+t_to_string(?atom(?any), _RecDict) ->
+ "atom()";
+t_to_string(?atom(Set), _RecDict) ->
+ case set_size(Set) of
+ 2 ->
+ case set_is_element(true, Set) andalso set_is_element(false, Set) of
+ true -> "boolean()";
+ false -> set_to_string(Set)
+ end;
+ _ ->
+ set_to_string(Set)
+ end;
+t_to_string(?bitstr(0, 0), _RecDict) ->
+ "<<>>";
+t_to_string(?bitstr(8, 0), _RecDict) ->
+ "binary()";
+t_to_string(?bitstr(1, 0), _RecDict) ->
+ "bitstring()";
+t_to_string(?bitstr(0, B), _RecDict) ->
+ flat_format("<<_:~w>>", [B]);
+t_to_string(?bitstr(U, 0), _RecDict) ->
+ flat_format("<<_:_*~w>>", [U]);
+t_to_string(?bitstr(U, B), _RecDict) ->
+ flat_format("<<_:~w,_:_*~w>>", [B, U]);
+t_to_string(?function(?any, ?any), _RecDict) ->
+ "fun()";
+t_to_string(?function(?any, Range), RecDict) ->
+ "fun((...) -> " ++ t_to_string(Range, RecDict) ++ ")";
+t_to_string(?function(?product(ArgList), Range), RecDict) ->
+ "fun((" ++ comma_sequence(ArgList, RecDict) ++ ") -> "
+ ++ t_to_string(Range, RecDict) ++ ")";
+t_to_string(?identifier(Set), _RecDict) ->
+ case Set of
+ ?any -> "identifier()";
+ _ ->
+ string:join([flat_format("~w()", [T]) || T <- set_to_list(Set)], " | ")
+ end;
+t_to_string(?opaque(Set), RecDict) ->
+ string:join([opaque_type(Mod, Name, Args, S, RecDict) ||
+ #opaque{mod = Mod, name = Name, struct = S, args = Args}
+ <- set_to_list(Set)],
+ " | ");
+t_to_string(?matchstate(Pres, Slots), RecDict) ->
+ flat_format("ms(~s,~s)", [t_to_string(Pres, RecDict),
+ t_to_string(Slots,RecDict)]);
+t_to_string(?nil, _RecDict) ->
+ "[]";
+t_to_string(?nonempty_list(Contents, Termination), RecDict) ->
+ ContentString = t_to_string(Contents, RecDict),
+ case Termination of
+ ?nil ->
+ case Contents of
+ ?char -> "nonempty_string()";
+ _ -> "["++ContentString++",...]"
+ end;
+ ?any ->
+ %% Just a safety check.
+ case Contents =:= ?any of
+ true -> ok;
+ false ->
+ %% XXX. See comment below.
+ %% erlang:error({illegal_list, ?nonempty_list(Contents, Termination)})
+ ok
+ end,
+ "nonempty_maybe_improper_list()";
+ _ ->
+ case t_is_subtype(t_nil(), Termination) of
+ true ->
+ "nonempty_maybe_improper_list("++ContentString++","
+ ++t_to_string(Termination, RecDict)++")";
+ false ->
+ "nonempty_improper_list("++ContentString++","
+ ++t_to_string(Termination, RecDict)++")"
+ end
+ end;
+t_to_string(?list(Contents, Termination, ?unknown_qual), RecDict) ->
+ ContentString = t_to_string(Contents, RecDict),
+ case Termination of
+ ?nil ->
+ case Contents of
+ ?char -> "string()";
+ _ -> "["++ContentString++"]"
+ end;
+ ?any ->
+ %% Just a safety check.
+ %% XXX. Types such as "maybe_improper_list(integer(), any())"
+ %% are OK, but cannot be printed!?
+ case Contents =:= ?any of
+ true -> ok;
+ false ->
+ ok
+ %% L = ?list(Contents, Termination, ?unknown_qual),
+ %% erlang:error({illegal_list, L})
+ end,
+ "maybe_improper_list()";
+ _ ->
+ case t_is_subtype(t_nil(), Termination) of
+ true ->
+ "maybe_improper_list("++ContentString++","
+ ++t_to_string(Termination, RecDict)++")";
+ false ->
+ "improper_list("++ContentString++","
+ ++t_to_string(Termination, RecDict)++")"
+ end
+ end;
+t_to_string(?int_set(Set), _RecDict) ->
+ set_to_string(Set);
+t_to_string(?byte, _RecDict) -> "byte()";
+t_to_string(?char, _RecDict) -> "char()";
+t_to_string(?integer_pos, _RecDict) -> "pos_integer()";
+t_to_string(?integer_non_neg, _RecDict) -> "non_neg_integer()";
+t_to_string(?integer_neg, _RecDict) -> "neg_integer()";
+t_to_string(?int_range(From, To), _RecDict) ->
+ flat_format("~w..~w", [From, To]);
+t_to_string(?integer(?any), _RecDict) -> "integer()";
+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([],?any,?any), _RecDict) -> "map()";
+t_to_string(?map(Pairs0,DefK,DefV), RecDict) ->
+ {Pairs, ExtraEl} =
+ case {DefK, DefV} of
+ {?none, ?none} -> {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) ++ "}";
+t_to_string(?tuple(Elements, Arity, Tag), RecDict) ->
+ [TagAtom] = atom_vals(Tag),
+ case lookup_record(TagAtom, Arity-1, RecDict) of
+ error -> "{" ++ comma_sequence(Elements, RecDict) ++ "}";
+ {ok, FieldNames} ->
+ record_to_string(TagAtom, Elements, FieldNames, RecDict)
+ end;
+t_to_string(?tuple_set(_) = T, RecDict) ->
+ union_sequence(t_tuple_subtypes(T), RecDict);
+t_to_string(?union(Types), RecDict) ->
+ union_sequence([T || T <- Types, T =/= ?none], RecDict);
+t_to_string(?var(Id), _RecDict) when is_atom(Id) ->
+ flat_format("~s", [atom_to_list(Id)]);
+t_to_string(?var(Id), _RecDict) when is_integer(Id) ->
+ flat_format("var(~w)", [Id]).
+
+
+record_to_string(Tag, [_|Fields], FieldNames, RecDict) ->
+ FieldStrings = record_fields_to_string(Fields, FieldNames, RecDict, []),
+ "#" ++ atom_to_string(Tag) ++ "{" ++ string:join(FieldStrings, ",") ++ "}".
+
+record_fields_to_string([F|Fs], [{FName, _Abstr, DefType}|FDefs],
+ RecDict, Acc) ->
+ NewAcc =
+ case
+ t_is_equal(F, t_any()) orelse
+ (t_is_any_atom('undefined', F) andalso
+ not t_is_none(t_inf(F, DefType)))
+ of
+ true -> Acc;
+ false ->
+ StrFV = atom_to_string(FName) ++ "::" ++ t_to_string(F, RecDict),
+ [StrFV|Acc]
+ end,
+ record_fields_to_string(Fs, FDefs, RecDict, NewAcc);
+record_fields_to_string([], [], _RecDict, Acc) ->
+ lists:reverse(Acc).
+
+-spec record_field_diffs_to_string(erl_type(), type_table()) -> string().
+
+record_field_diffs_to_string(?tuple([_|Fs], Arity, Tag), RecDict) ->
+ [TagAtom] = atom_vals(Tag),
+ {ok, FieldNames} = lookup_record(TagAtom, Arity-1, RecDict),
+ %% io:format("RecCElems = ~p\nRecTypes = ~p\n", [Fs, FieldNames]),
+ FieldDiffs = field_diffs(Fs, FieldNames, RecDict, []),
+ string:join(FieldDiffs, " and ").
+
+field_diffs([F|Fs], [{FName, _Abstr, DefType}|FDefs], RecDict, Acc) ->
+ %% Don't care about opacity for now.
+ NewAcc =
+ case not t_is_none(t_inf(F, DefType)) of
+ true -> Acc;
+ false ->
+ Str = atom_to_string(FName) ++ "::" ++ t_to_string(DefType, RecDict),
+ [Str|Acc]
+ end,
+ field_diffs(Fs, FDefs, RecDict, NewAcc);
+field_diffs([], [], _, Acc) ->
+ lists:reverse(Acc).
+
+comma_sequence(Types, RecDict) ->
+ List = [case T =:= ?any of
+ true -> "_";
+ false -> t_to_string(T, RecDict)
+ end || T <- Types],
+ string:join(List, ",").
+
+union_sequence(Types, RecDict) ->
+ List = [t_to_string(T, RecDict) || T <- Types],
+ string:join(List, " | ").
+
+-ifdef(DEBUG).
+opaque_type(Mod, Name, _Args, S, RecDict) ->
+ ArgsString = comma_sequence(_Args, RecDict),
+ String = t_to_string(S, RecDict),
+ opaque_name(Mod, Name, ArgsString) ++ "[" ++ String ++ "]".
+-else.
+opaque_type(Mod, Name, Args, _S, RecDict) ->
+ ArgsString = comma_sequence(Args, RecDict),
+ opaque_name(Mod, Name, ArgsString).
+-endif.
+
+opaque_name(Mod, Name, Extra) ->
+ S = mod_name(Mod, Name),
+ flat_format("~s(~s)", [S, Extra]).
+
+mod_name(Mod, Name) ->
+ flat_format("~w:~w", [Mod, Name]).
+
+%%=============================================================================
+%%
+%% Build a type from parse forms.
+%%
+%%=============================================================================
+
+-type type_names() :: [type_key() | record_key()].
+
+-type mta() :: {module(), atom(), arity()}.
+-type mra() :: {module(), atom(), arity()}.
+-type site() :: {'type', mta()} | {'spec', mfa()} | {'record', mra()}.
+-type cache_key() :: {module(), atom(), expand_depth(),
+ [erl_type()], type_names()}.
+-opaque cache() :: #{cache_key() => {erl_type(), expand_limit()}}.
+
+-spec t_from_form(parse_form(), sets:set(mfa()), site(), mod_records(),
+ var_table(), cache()) -> {erl_type(), cache()}.
+
+t_from_form(Form, ExpTypes, Site, RecDict, VarTab, Cache) ->
+ t_from_form1(Form, ExpTypes, Site, RecDict, VarTab, Cache).
+
+%% Replace external types with with none().
+-spec t_from_form_without_remote(parse_form(), site(), type_table()) ->
+ {erl_type(), cache()}.
+
+t_from_form_without_remote(Form, Site, TypeTable) ->
+ Module = site_module(Site),
+ RecDict = dict:from_list([{Module, TypeTable}]),
+ ExpTypes = replace_by_none,
+ VarTab = var_table__new(),
+ Cache = cache__new(),
+ t_from_form1(Form, ExpTypes, Site, RecDict, VarTab, Cache).
+
+%% REC_TYPE_LIMIT is used for limiting the depth of recursive types.
+%% EXPAND_LIMIT is used for limiting the size of types by
+%% limiting the number of elements of lists within one type form.
+%% EXPAND_DEPTH is used in conjunction with EXPAND_LIMIT to make the
+%% types balanced (unions will otherwise collapse to any()) by limiting
+%% the depth the same way as t_limit/2 does.
+
+-type expand_limit() :: integer().
+
+-type expand_depth() :: integer().
+
+-record(from_form, {site :: site(),
+ xtypes :: sets:set(mfa()) | 'replace_by_none',
+ mrecs :: mod_records(),
+ vtab :: var_table(),
+ tnames :: type_names()}).
+
+-spec t_from_form1(parse_form(), sets:set(mfa()) | 'replace_by_none',
+ site(), mod_records(), var_table(), cache()) ->
+ {erl_type(), cache()}.
+
+t_from_form1(Form, ET, Site, MR, V, C) ->
+ TypeNames = initial_typenames(Site),
+ State = #from_form{site = Site,
+ xtypes = ET,
+ mrecs = MR,
+ vtab = V,
+ tnames = TypeNames},
+ L = ?EXPAND_LIMIT,
+ {T1, L1, C1} = from_form(Form, State, ?EXPAND_DEPTH, L, C),
+ if
+ L1 =< 0 ->
+ from_form_loop(Form, State, 1, L, C1);
+ true ->
+ {T1, C1}
+ end.
+
+initial_typenames({type, _MTA}=Site) -> [Site];
+initial_typenames({spec, _MFA}) -> [];
+initial_typenames({record, _MRA}) -> [].
+
+from_form_loop(Form, State, D, Limit, C) ->
+ {T1, L1, C1} = from_form(Form, State, D, Limit, C),
+ Delta = Limit - L1,
+ if
+ %% Save some time by assuming next depth will exceed the limit.
+ Delta * 8 > Limit ->
+ {T1, C1};
+ true ->
+ D1 = D + 1,
+ from_form_loop(Form, State, D1, Limit, C1)
+ end.
+
+-spec from_form(parse_form(),
+ #from_form{},
+ expand_depth(),
+ expand_limit(),
+ cache()) -> {erl_type(), expand_limit(), cache()}.
+
+%% If there is something wrong with parse_form()
+%% throw({error, io_lib:chars()} is called;
+%% for unknown remote types
+%% self() ! {self(), ext_types, {RemMod, Name, ArgsLen}}
+%% is called, unless 'replace_by_none' is given.
+%%
+%% It is assumed that site_module(S) can be found in MR.
+
+from_form(_, _S, D, L, C) when D =< 0 ; L =< 0 ->
+ {t_any(), L, C};
+from_form({var, _L, '_'}, _S, _D, L, C) ->
+ {t_any(), L, C};
+from_form({var, _L, Name}, S, _D, L, C) ->
+ V = S#from_form.vtab,
+ case maps:find(Name, V) of
+ error -> {t_var(Name), L, C};
+ {ok, Val} -> {Val, L, C}
+ end;
+from_form({ann_type, _L, [_Var, Type]}, S, D, L, C) ->
+ from_form(Type, S, D, L, C);
+from_form({paren_type, _L, [Type]}, S, D, L, C) ->
+ from_form(Type, S, D, L, C);
+from_form({remote_type, _L, [{atom, _, Module}, {atom, _, Type}, Args]},
+ S, D, L, C) ->
+ remote_from_form(Module, Type, Args, S, D, L, C);
+from_form({atom, _L, Atom}, _S, _D, L, C) ->
+ {t_atom(Atom), L, C};
+from_form({integer, _L, Int}, _S, _D, L, C) ->
+ {t_integer(Int), L, C};
+from_form({op, _L, _Op, _Arg} = Op, _S, _D, L, C) ->
+ case erl_eval:partial_eval(Op) of
+ {integer, _, Val} ->
+ {t_integer(Val), L, C};
+ _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Op])})
+ end;
+from_form({op, _L, _Op, _Arg1, _Arg2} = Op, _S, _D, L, C) ->
+ case erl_eval:partial_eval(Op) of
+ {integer, _, Val} ->
+ {t_integer(Val), L, C};
+ _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Op])})
+ end;
+from_form({type, _L, any, []}, _S, _D, L, C) ->
+ {t_any(), L, C};
+from_form({type, _L, arity, []}, _S, _D, L, C) ->
+ {t_arity(), L, C};
+from_form({type, _L, atom, []}, _S, _D, L, C) ->
+ {t_atom(), L, C};
+from_form({type, _L, binary, []}, _S, _D, L, C) ->
+ {t_binary(), L, C};
+from_form({type, _L, binary, [Base, Unit]} = Type, _S, _D, L, C) ->
+ case {erl_eval:partial_eval(Base), erl_eval:partial_eval(Unit)} of
+ {{integer, _, B}, {integer, _, U}} when B >= 0, U >= 0 ->
+ {t_bitstr(U, B), L, C};
+ _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Type])})
+ end;
+from_form({type, _L, bitstring, []}, _S, _D, L, C) ->
+ {t_bitstr(), L, C};
+from_form({type, _L, bool, []}, _S, _D, L, C) ->
+ {t_boolean(), L, C}; % XXX: Temporarily
+from_form({type, _L, boolean, []}, _S, _D, L, C) ->
+ {t_boolean(), L, C};
+from_form({type, _L, byte, []}, _S, _D, L, C) ->
+ {t_byte(), L, C};
+from_form({type, _L, char, []}, _S, _D, L, C) ->
+ {t_char(), L, C};
+from_form({type, _L, float, []}, _S, _D, L, C) ->
+ {t_float(), L, C};
+from_form({type, _L, function, []}, _S, _D, L, C) ->
+ {t_fun(), L, C};
+from_form({type, _L, 'fun', []}, _S, _D, L, C) ->
+ {t_fun(), L, C};
+from_form({type, _L, 'fun', [{type, _, any}, Range]}, S, D, L, C) ->
+ {T, L1, C1} = from_form(Range, S, D - 1, L - 1, C),
+ {t_fun(T), L1, C1};
+from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]},
+ S, D, L, C) ->
+ {Dom1, L1, C1} = list_from_form(Domain, S, D, L, C),
+ {Ran1, L2, C2} = from_form(Range, S, D, L1, C1),
+ {t_fun(Dom1, Ran1), L2, C2};
+from_form({type, _L, identifier, []}, _S, _D, L, C) ->
+ {t_identifier(), L, C};
+from_form({type, _L, integer, []}, _S, _D, L, C) ->
+ {t_integer(), L, C};
+from_form({type, _L, iodata, []}, _S, _D, L, C) ->
+ {t_iodata(), L, C};
+from_form({type, _L, iolist, []}, _S, _D, L, C) ->
+ {t_iolist(), L, C};
+from_form({type, _L, list, []}, _S, _D, L, C) ->
+ {t_list(), L, C};
+from_form({type, _L, list, [Type]}, S, D, L, C) ->
+ {T, L1, C1} = from_form(Type, S, D - 1, L - 1, C),
+ {t_list(T), L1, C1};
+from_form({type, _L, map, any}, S, D, L, C) ->
+ builtin_type(map, t_map(), S, D, L, C);
+from_form({type, _L, map, List}, S, D0, L, C) ->
+ {Pairs1, L5, C5} =
+ fun PairsFromForm(_, L1, C1) when L1 =< 0 -> {[{?any,?opt,?any}], L1, C1};
+ PairsFromForm([], L1, C1) -> {[], L1, C1};
+ PairsFromForm([{type, _, Oper, [KF, VF]}|T], L1, C1) ->
+ D = D0 - 1,
+ {Key, L2, C2} = from_form(KF, S, D, L1, C1),
+ {Val, L3, C3} = from_form(VF, S, D, L2, C2),
+ {Pairs0, L4, C4} = PairsFromForm(T, L3 - 1, C3),
+ case Oper of
+ map_field_assoc -> {[{Key,?opt, Val}|Pairs0], L4, C4};
+ map_field_exact -> {[{Key,?mand,Val}|Pairs0], L4, C4}
+ end
+ end(List, L, C),
+ try
+ {Pairs, DefK, DefV} = map_from_form(Pairs1, [], [], [], ?none, ?none),
+ {t_map(Pairs, DefK, DefV), L5, C5}
+ catch none -> {t_none(), L5, C5}
+ end;
+from_form({type, _L, mfa, []}, _S, _D, L, C) ->
+ {t_mfa(), L, C};
+from_form({type, _L, module, []}, _S, _D, L, C) ->
+ {t_module(), L, C};
+from_form({type, _L, nil, []}, _S, _D, L, C) ->
+ {t_nil(), L, C};
+from_form({type, _L, neg_integer, []}, _S, _D, L, C) ->
+ {t_neg_integer(), L, C};
+from_form({type, _L, non_neg_integer, []}, _S, _D, L, C) ->
+ {t_non_neg_integer(), L, C};
+from_form({type, _L, no_return, []}, _S, _D, L, C) ->
+ {t_unit(), L, C};
+from_form({type, _L, node, []}, _S, _D, L, C) ->
+ {t_node(), L, C};
+from_form({type, _L, none, []}, _S, _D, L, C) ->
+ {t_none(), L, C};
+from_form({type, _L, nonempty_list, []}, _S, _D, L, C) ->
+ {t_nonempty_list(), L, C};
+from_form({type, _L, nonempty_list, [Type]}, S, D, L, C) ->
+ {T, L1, C1} = from_form(Type, S, D, L - 1, C),
+ {t_nonempty_list(T), L1, C1};
+from_form({type, _L, nonempty_improper_list, [Cont, Term]}, S, D, L, C) ->
+ {T1, L1, C1} = from_form(Cont, S, D, L - 1, C),
+ {T2, L2, C2} = from_form(Term, S, D, L1, C1),
+ {t_cons(T1, T2), L2, C2};
+from_form({type, _L, nonempty_maybe_improper_list, []}, _S, _D, L, C) ->
+ {t_cons(?any, ?any), L, C};
+from_form({type, _L, nonempty_maybe_improper_list, [Cont, Term]},
+ S, D, L, C) ->
+ {T1, L1, C1} = from_form(Cont, S, D, L - 1, C),
+ {T2, L2, C2} = from_form(Term, S, D, L1, C1),
+ {t_cons(T1, T2), L2, C2};
+from_form({type, _L, nonempty_string, []}, _S, _D, L, C) ->
+ {t_nonempty_string(), L, C};
+from_form({type, _L, number, []}, _S, _D, L, C) ->
+ {t_number(), L, C};
+from_form({type, _L, pid, []}, _S, _D, L, C) ->
+ {t_pid(), L, C};
+from_form({type, _L, port, []}, _S, _D, L, C) ->
+ {t_port(), L, C};
+from_form({type, _L, pos_integer, []}, _S, _D, L, C) ->
+ {t_pos_integer(), L, C};
+from_form({type, _L, maybe_improper_list, []}, _S, _D, L, C) ->
+ {t_maybe_improper_list(), L, C};
+from_form({type, _L, maybe_improper_list, [Content, Termination]},
+ S, D, L, C) ->
+ {T1, L1, C1} = from_form(Content, S, D, L - 1, C),
+ {T2, L2, C2} = from_form(Termination, S, D, L1, C1),
+ {t_maybe_improper_list(T1, T2), L2, C2};
+from_form({type, _L, product, Elements}, S, D, L, C) ->
+ {Lst, L1, C1} = list_from_form(Elements, S, D - 1, L, C),
+ {t_product(Lst), L1, C1};
+from_form({type, _L, range, [From, To]} = Type, _S, _D, L, C) ->
+ case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
+ {{integer, _, FromVal}, {integer, _, ToVal}} ->
+ {t_from_range(FromVal, ToVal), L, C};
+ _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Type])})
+ end;
+from_form({type, _L, record, [Name|Fields]}, S, D, L, C) ->
+ record_from_form(Name, Fields, S, D, L, C);
+from_form({type, _L, reference, []}, _S, _D, L, C) ->
+ {t_reference(), L, C};
+from_form({type, _L, string, []}, _S, _D, L, C) ->
+ {t_string(), L, C};
+from_form({type, _L, term, []}, _S, _D, L, C) ->
+ {t_any(), L, C};
+from_form({type, _L, timeout, []}, _S, _D, L, C) ->
+ {t_timeout(), L, C};
+from_form({type, _L, tuple, any}, _S, _D, L, C) ->
+ {t_tuple(), L, C};
+from_form({type, _L, tuple, Args}, S, D, L, C) ->
+ {Lst, L1, C1} = list_from_form(Args, S, D - 1, L, C),
+ {t_tuple(Lst), L1, C1};
+from_form({type, _L, union, Args}, S, D, L, C) ->
+ {Lst, L1, C1} = list_from_form(Args, S, D, L, C),
+ {t_sup(Lst), L1, C1};
+from_form({user_type, _L, Name, Args}, S, D, L, C) ->
+ type_from_form(Name, Args, S, D, L, C);
+from_form({type, _L, Name, Args}, S, D, L, C) ->
+ %% Compatibility: modules compiled before Erlang/OTP 18.0.
+ type_from_form(Name, Args, S, D, L, C);
+from_form({opaque, _L, Name, {Mod, Args, Rep}}, _S, _D, L, C) ->
+ %% XXX. To be removed.
+ {t_opaque(Mod, Name, Args, Rep), L, C}.
+
+builtin_type(Name, Type, S, D, L, C) ->
+ #from_form{site = Site, mrecs = MR} = S,
+ M = site_module(Site),
+ case dict:find(M, MR) of
+ {ok, R} ->
+ case lookup_type(Name, 0, R) of
+ {_, {{_M, _FL, _F, _A}, _T}} ->
+ type_from_form(Name, [], S, D, L, C);
+ error ->
+ {Type, L, C}
+ end;
+ error ->
+ {Type, L, C}
+ end.
+
+type_from_form(Name, Args, S, D, L, C) ->
+ #from_form{site = Site, mrecs = MR, tnames = TypeNames} = S,
+ ArgsLen = length(Args),
+ Module = site_module(Site),
+ TypeName = {type, {Module, Name, ArgsLen}},
+ case can_unfold_more(TypeName, TypeNames) of
+ true ->
+ {ok, R} = dict:find(Module, MR),
+ type_from_form1(Name, Args, ArgsLen, R, TypeName, TypeNames,
+ S, D, L, C);
+ false ->
+ {t_any(), L, C}
+ end.
+
+type_from_form1(Name, Args, ArgsLen, R, TypeName, TypeNames, S, D, L, C) ->
+ case lookup_type(Name, ArgsLen, R) of
+ {Tag, {{Module, _FileName, Form, ArgNames}, Type}} ->
+ NewTypeNames = [TypeName|TypeNames],
+ S1 = S#from_form{tnames = NewTypeNames},
+ {ArgTypes, L1, C1} = list_from_form(Args, S1, D, L, C),
+ CKey = cache_key(Module, Name, ArgTypes, TypeNames, D),
+ case cache_find(CKey, C) of
+ {CachedType, DeltaL} ->
+ {CachedType, L1 - DeltaL, C};
+ error ->
+ List = lists:zip(ArgNames, ArgTypes),
+ TmpV = maps:from_list(List),
+ S2 = S1#from_form{site = TypeName, vtab = TmpV},
+ Fun = fun(DD, LL) -> from_form(Form, S2, DD, LL, C1) end,
+ {NewType, L3, C3} =
+ case Tag of
+ type ->
+ recur_limit(Fun, D, L1, TypeName, TypeNames);
+ opaque ->
+ {Rep, L2, C2} = recur_limit(Fun, D, L1, TypeName, TypeNames),
+ Rep1 = choose_opaque_type(Rep, Type),
+ Rep2 = case cannot_have_opaque(Rep1, TypeName, TypeNames) of
+ true -> Rep1;
+ false ->
+ ArgTypes2 = subst_all_vars_to_any_list(ArgTypes),
+ t_opaque(Module, Name, ArgTypes2, Rep1)
+ end,
+ {Rep2, L2, C2}
+ end,
+ C4 = cache_put(CKey, NewType, L1 - L3, C3),
+ {NewType, L3, C4}
+ end;
+ error ->
+ Msg = io_lib:format("Unable to find type ~w/~w\n",
+ [Name, ArgsLen]),
+ throw({error, Msg})
+ end.
+
+remote_from_form(RemMod, Name, Args, S, D, L, C) ->
+ #from_form{xtypes = ET, mrecs = MR, tnames = TypeNames} = S,
+ if
+ ET =:= replace_by_none ->
+ {t_none(), L, C};
+ true ->
+ ArgsLen = length(Args),
+ MFA = {RemMod, Name, ArgsLen},
+ case dict:find(RemMod, MR) of
+ error ->
+ self() ! {self(), ext_types, MFA},
+ {t_any(), L, C};
+ {ok, RemDict} ->
+ case sets:is_element(MFA, ET) of
+ true ->
+ RemType = {type, MFA},
+ case can_unfold_more(RemType, TypeNames) of
+ true ->
+ remote_from_form1(RemMod, Name, Args, ArgsLen, RemDict,
+ RemType, TypeNames, S, D, L, C);
+ false ->
+ {t_any(), L, C}
+ end;
+ false ->
+ self() ! {self(), ext_types, {RemMod, Name, ArgsLen}},
+ {t_any(), L, C}
+ end
+ end
+ end.
+
+remote_from_form1(RemMod, Name, Args, ArgsLen, RemDict, RemType, TypeNames,
+ S, D, L, C) ->
+ case lookup_type(Name, ArgsLen, RemDict) of
+ {Tag, {{Mod, _FileLine, Form, ArgNames}, Type}} ->
+ NewTypeNames = [RemType|TypeNames],
+ S1 = S#from_form{tnames = NewTypeNames},
+ {ArgTypes, L1, C1} = list_from_form(Args, S1, D, L, C),
+ CKey = cache_key(RemMod, Name, ArgTypes, TypeNames, D),
+ %% case error of
+ case cache_find(CKey, C) of
+ {CachedType, DeltaL} ->
+ {CachedType, L - DeltaL, C};
+ error ->
+ List = lists:zip(ArgNames, ArgTypes),
+ TmpVarTab = maps:from_list(List),
+ S2 = S1#from_form{site = RemType, vtab = TmpVarTab},
+ Fun = fun(DD, LL) -> from_form(Form, S2, DD, LL, C1) end,
+ {NewType, L3, C3} =
+ case Tag of
+ type ->
+ recur_limit(Fun, D, L1, RemType, TypeNames);
+ opaque ->
+ {NewRep, L2, C2} = recur_limit(Fun, D, L1, RemType, TypeNames),
+ NewRep1 = choose_opaque_type(NewRep, Type),
+ NewRep2 =
+ case cannot_have_opaque(NewRep1, RemType, TypeNames) of
+ true -> NewRep1;
+ false ->
+ ArgTypes2 = subst_all_vars_to_any_list(ArgTypes),
+ t_opaque(Mod, Name, ArgTypes2, NewRep1)
+ end,
+ {NewRep2, L2, C2}
+ end,
+ C4 = cache_put(CKey, NewType, L1 - L3, C3),
+ {NewType, L3, C4}
+ end;
+ error ->
+ Msg = io_lib:format("Unable to find remote type ~w:~w()\n",
+ [RemMod, Name]),
+ throw({error, Msg})
+ end.
+
+subst_all_vars_to_any_list(Types) ->
+ [subst_all_vars_to_any(Type) || Type <- Types].
+
+%% Opaque types (both local and remote) are problematic when it comes
+%% to the limits (TypeNames, D, and L). The reason is that if any() is
+%% substituted for a more specialized subtype of an opaque type, the
+%% property stated along with decorate_with_opaque() (the type has to
+%% be a subtype of the declared type) no longer holds.
+%%
+%% The less than perfect remedy: if the opaque type created from a
+%% form is not a subset of the declared type, the declared type is
+%% used instead, effectively bypassing the limits, and potentially
+%% resulting in huge types.
+choose_opaque_type(Type, DeclType) ->
+ case
+ t_is_subtype(subst_all_vars_to_any(Type),
+ subst_all_vars_to_any(DeclType))
+ of
+ true -> Type;
+ false -> DeclType
+ end.
+
+record_from_form({atom, _, Name}, ModFields, S, D0, L0, C) ->
+ #from_form{site = Site, mrecs = MR, tnames = TypeNames} = S,
+ RecordType = {record, Name},
+ case can_unfold_more(RecordType, TypeNames) of
+ true ->
+ M = site_module(Site),
+ {ok, R} = dict:find(M, MR),
+ case lookup_record(Name, R) of
+ {ok, DeclFields} ->
+ NewTypeNames = [RecordType|TypeNames],
+ Site1 = {record, {M, Name, length(DeclFields)}},
+ S1 = S#from_form{site = Site1, tnames = NewTypeNames},
+ Fun = fun(D, L) ->
+ {GetModRec, L1, C1} =
+ get_mod_record(ModFields, DeclFields, S1, D, L, C),
+ case GetModRec of
+ {error, FieldName} ->
+ throw({error,
+ io_lib:format("Illegal declaration of #~w{~w}\n",
+ [Name, FieldName])});
+ {ok, NewFields} ->
+ S2 = S1#from_form{vtab = var_table__new()},
+ {NewFields1, L2, C2} =
+ fields_from_form(NewFields, S2, D, L1, C1),
+ Rec = t_tuple(
+ [t_atom(Name)|[Type
+ || {_FieldName, Type} <- NewFields1]]),
+ {Rec, L2, C2}
+ end
+ end,
+ recur_limit(Fun, D0, L0, RecordType, TypeNames);
+ error ->
+ throw({error, io_lib:format("Unknown record #~w{}\n", [Name])})
+ end;
+ false ->
+ {t_any(), L0, C}
+ end.
+
+get_mod_record([], DeclFields, _S, _D, L, C) ->
+ {{ok, DeclFields}, L, C};
+get_mod_record(ModFields, DeclFields, S, D, L, C) ->
+ DeclFieldsDict = lists:keysort(1, DeclFields),
+ {ModFieldsDict, L1, C1} = build_field_dict(ModFields, S, D, L, C),
+ case get_mod_record_types(DeclFieldsDict, ModFieldsDict, []) of
+ {error, _FieldName} = Error -> {Error, L1, C1};
+ {ok, FinalKeyDict} ->
+ Fields = [lists:keyfind(FieldName, 1, FinalKeyDict)
+ || {FieldName, _, _} <- DeclFields],
+ {{ok, Fields}, L1, C1}
+ end.
+
+build_field_dict(FieldTypes, S, D, L, C) ->
+ build_field_dict(FieldTypes, S, D, L, C, []).
+
+build_field_dict([{type, _, field_type, [{atom, _, Name}, Type]}|Left],
+ S, D, L, C, Acc) ->
+ {T, L1, C1} = from_form(Type, S, D, L - 1, C),
+ NewAcc = [{Name, Type, T}|Acc],
+ build_field_dict(Left, S, D, L1, C1, NewAcc);
+build_field_dict([], _S, _D, L, C, Acc) ->
+ {lists:keysort(1, Acc), L, C}.
+
+get_mod_record_types([{FieldName, _Abstr, _DeclType}|Left1],
+ [{FieldName, TypeForm, ModType}|Left2],
+ Acc) ->
+ get_mod_record_types(Left1, Left2, [{FieldName, TypeForm, ModType}|Acc]);
+get_mod_record_types([{FieldName1, _Abstr, _DeclType} = DT|Left1],
+ [{FieldName2, _FormType, _ModType}|_] = List2,
+ Acc) when FieldName1 < FieldName2 ->
+ get_mod_record_types(Left1, List2, [DT|Acc]);
+get_mod_record_types(Left1, [], Acc) ->
+ {ok, lists:keysort(1, Left1++Acc)};
+get_mod_record_types(_, [{FieldName2, _FormType, _ModType}|_], _Acc) ->
+ {error, FieldName2}.
+
+%% It is important to create a limited version of the record type
+%% since nested record types can otherwise easily result in huge
+%% terms.
+fields_from_form([], _S, _D, L, C) ->
+ {[], L, C};
+fields_from_form([{Name, Abstr, _Type}|Tail], S, D, L, C) ->
+ {T, L1, C1} = from_form(Abstr, S, D, L, C),
+ {F, L2, C2} = fields_from_form(Tail, S, D, L1, C1),
+ {[{Name, T}|F], L2, C2}.
+
+list_from_form([], _S, _D, L, C) ->
+ {[], L, C};
+list_from_form([H|Tail], S, D, L, C) ->
+ {H1, L1, C1} = from_form(H, S, D, L - 1, C),
+ {T1, L2, C2} = list_from_form(Tail, S, D, L1, C1),
+ {[H1|T1], L2, C2}.
+
+%% 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)].
+
+-define(RECUR_EXPAND_LIMIT, 10).
+-define(RECUR_EXPAND_DEPTH, 2).
+
+%% If more of the limited resources is spent on the non-recursive
+%% forms, more warnings are found. And the analysis is also a bit
+%% faster.
+%%
+%% Setting REC_TYPE_LIMIT to 1 would work also work well.
+
+recur_limit(Fun, D, L, _, _) when L =< ?RECUR_EXPAND_DEPTH,
+ D =< ?RECUR_EXPAND_LIMIT ->
+ Fun(D, L);
+recur_limit(Fun, D, L, TypeName, TypeNames) ->
+ case is_recursive(TypeName, TypeNames) of
+ true ->
+ {T, L1, C1} = Fun(?RECUR_EXPAND_DEPTH, ?RECUR_EXPAND_LIMIT),
+ {T, L - L1, C1};
+ false ->
+ Fun(D, L)
+ end.
+
+-spec t_check_record_fields(parse_form(), sets:set(mfa()), site(),
+ mod_records(), var_table(), cache()) -> cache().
+
+t_check_record_fields(Form, ExpTypes, Site, RecDict, VarTable, Cache) ->
+ State = #from_form{site = Site,
+ xtypes = ExpTypes,
+ mrecs = RecDict,
+ vtab = VarTable,
+ tnames = []},
+ check_record_fields(Form, State, Cache).
+
+-spec check_record_fields(parse_form(), #from_form{}, cache()) -> cache().
+
+%% If there is something wrong with parse_form()
+%% throw({error, io_lib:chars()} is called.
+
+check_record_fields({var, _L, _}, _S, C) -> C;
+check_record_fields({ann_type, _L, [_Var, Type]}, S, C) ->
+ check_record_fields(Type, S, C);
+check_record_fields({paren_type, _L, [Type]}, S, C) ->
+ check_record_fields(Type, S, C);
+check_record_fields({remote_type, _L, [{atom, _, _}, {atom, _, _}, Args]},
+ S, C) ->
+ list_check_record_fields(Args, S, C);
+check_record_fields({atom, _L, _}, _S, C) -> C;
+check_record_fields({integer, _L, _}, _S, C) -> C;
+check_record_fields({op, _L, _Op, _Arg}, _S, C) -> C;
+check_record_fields({op, _L, _Op, _Arg1, _Arg2}, _S, C) -> C;
+check_record_fields({type, _L, tuple, any}, _S, C) -> C;
+check_record_fields({type, _L, map, any}, _S, C) -> C;
+check_record_fields({type, _L, binary, [_Base, _Unit]}, _S, C) -> C;
+check_record_fields({type, _L, 'fun', [{type, _, any}, Range]}, S, C) ->
+ check_record_fields(Range, S, C);
+check_record_fields({type, _L, range, [_From, _To]}, _S, C) -> C;
+check_record_fields({type, _L, record, [Name|Fields]}, S, C) ->
+ check_record(Name, Fields, S, C);
+check_record_fields({type, _L, _, Args}, S, C) ->
+ list_check_record_fields(Args, S, C);
+check_record_fields({user_type, _L, _Name, Args}, S, C) ->
+ list_check_record_fields(Args, S, C).
+
+check_record({atom, _, Name}, ModFields, S, C) ->
+ #from_form{site = Site, mrecs = MR} = S,
+ M = site_module(Site),
+ {ok, R} = dict:find(M, MR),
+ {ok, DeclFields} = lookup_record(Name, R),
+ case check_fields(Name, ModFields, DeclFields, S, C) of
+ {error, FieldName} ->
+ throw({error, io_lib:format("Illegal declaration of #~w{~w}\n",
+ [Name, FieldName])});
+ C1 -> C1
+ end.
+
+check_fields(RecName, [{type, _, field_type, [{atom, _, Name}, Abstr]}|Left],
+ DeclFields, S, C) ->
+ #from_form{site = Site0, xtypes = ET, mrecs = MR, vtab = V} = S,
+ M = site_module(Site0),
+ Site = {record, {M, RecName, length(DeclFields)}},
+ {Type, C1} = t_from_form(Abstr, ET, Site, MR, V, C),
+ {Name, _, DeclType} = lists:keyfind(Name, 1, DeclFields),
+ TypeNoVars = subst_all_vars_to_any(Type),
+ case t_is_subtype(TypeNoVars, DeclType) of
+ false -> {error, Name};
+ true -> check_fields(RecName, Left, DeclFields, S, C1)
+ end;
+check_fields(_RecName, [], _Decl, _S, C) ->
+ C.
+
+list_check_record_fields([], _S, C) ->
+ C;
+list_check_record_fields([H|Tail], S, C) ->
+ C1 = check_record_fields(H, S, C),
+ list_check_record_fields(Tail, S, C1).
+
+site_module({_, {Module, _, _}}) ->
+ Module.
+
+-spec cache__new() -> cache().
+
+cache__new() ->
+ maps:new().
+
+-spec cache_key(module(), atom(), [erl_type()],
+ type_names(), expand_depth()) -> cache_key().
+
+%% If TypeNames is left out from the key, the cache is smaller, and
+%% the form-to-type translation is faster. But it would be a shame if,
+%% for example, any() is used, where a more complex type should be
+%% used. There is also a slight risk of creating unnecessarily big
+%% types.
+
+cache_key(Module, Name, ArgTypes, TypeNames, D) ->
+ {Module, Name, D, ArgTypes, TypeNames}.
+
+-spec cache_find(cache_key(), cache()) ->
+ {erl_type(), expand_limit()} | 'error'.
+
+cache_find(Key, Cache) ->
+ case maps:find(Key, Cache) of
+ {ok, Value} ->
+ Value;
+ error ->
+ error
+ end.
+
+-spec cache_put(cache_key(), erl_type(), expand_limit(), cache()) -> cache().
+
+cache_put(_Key, _Type, DeltaL, Cache) when DeltaL < 0 ->
+ %% The type is truncated; do not reuse it.
+ Cache;
+cache_put(Key, Type, DeltaL, Cache) ->
+ maps:put(Key, {Type, DeltaL}, Cache).
+
+-spec t_var_names([erl_type()]) -> [atom()].
+
+t_var_names([{var, _, Name}|L]) when L =/= '_' ->
+ [Name|t_var_names(L)];
+t_var_names([]) ->
+ [].
+
+-spec t_form_to_string(parse_form()) -> string().
+
+t_form_to_string({var, _L, '_'}) -> "_";
+t_form_to_string({var, _L, Name}) -> atom_to_list(Name);
+t_form_to_string({atom, _L, Atom}) ->
+ io_lib:write_string(atom_to_list(Atom), $'); % To quote or not to quote... '
+t_form_to_string({integer, _L, Int}) -> integer_to_list(Int);
+t_form_to_string({op, _L, _Op, _Arg} = Op) ->
+ case erl_eval:partial_eval(Op) of
+ {integer, _, _} = Int -> t_form_to_string(Int);
+ _ -> io_lib:format("Badly formed type ~w", [Op])
+ end;
+t_form_to_string({op, _L, _Op, _Arg1, _Arg2} = Op) ->
+ case erl_eval:partial_eval(Op) of
+ {integer, _, _} = Int -> t_form_to_string(Int);
+ _ -> io_lib:format("Badly formed type ~w", [Op])
+ end;
+t_form_to_string({ann_type, _L, [Var, Type]}) ->
+ t_form_to_string(Var) ++ "::" ++ t_form_to_string(Type);
+t_form_to_string({paren_type, _L, [Type]}) ->
+ flat_format("(~s)", [t_form_to_string(Type)]);
+t_form_to_string({remote_type, _L, [{atom, _, Mod}, {atom, _, Name}, Args]}) ->
+ ArgString = "(" ++ string:join(t_form_to_string_list(Args), ",") ++ ")",
+ flat_format("~w:~w", [Mod, Name]) ++ ArgString;
+t_form_to_string({type, _L, arity, []}) -> "arity()";
+t_form_to_string({type, _L, binary, []}) -> "binary()";
+t_form_to_string({type, _L, binary, [Base, Unit]} = Type) ->
+ case {erl_eval:partial_eval(Base), erl_eval:partial_eval(Unit)} of
+ {{integer, _, B}, {integer, _, U}} ->
+ %% the following mirrors the clauses of t_to_string/2
+ case {U, B} of
+ {0, 0} -> "<<>>";
+ {8, 0} -> "binary()";
+ {1, 0} -> "bitstring()";
+ {0, B} -> flat_format("<<_:~w>>", [B]);
+ {U, 0} -> flat_format("<<_:_*~w>>", [U]);
+ {U, B} -> flat_format("<<_:~w,_:_*~w>>", [B, U])
+ end;
+ _ -> io_lib:format("Badly formed bitstr type ~w", [Type])
+ end;
+t_form_to_string({type, _L, bitstring, []}) -> "bitstring()";
+t_form_to_string({type, _L, 'fun', []}) -> "fun()";
+t_form_to_string({type, _L, 'fun', [{type, _, any}, Range]}) ->
+ "fun(...) -> " ++ t_form_to_string(Range);
+t_form_to_string({type, _L, 'fun', [{type, _, product, Domain}, Range]}) ->
+ "fun((" ++ string:join(t_form_to_string_list(Domain), ",") ++ ") -> "
+ ++ t_form_to_string(Range) ++ ")";
+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, 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()";
+t_form_to_string({type, _L, nonempty_list, [Type]}) ->
+ "[" ++ t_form_to_string(Type) ++ ",...]";
+t_form_to_string({type, _L, nonempty_string, []}) -> "nonempty_string()";
+t_form_to_string({type, _L, product, Elements}) ->
+ "<" ++ string:join(t_form_to_string_list(Elements), ",") ++ ">";
+t_form_to_string({type, _L, range, [From, To]} = Type) ->
+ case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
+ {{integer, _, FromVal}, {integer, _, ToVal}} ->
+ flat_format("~w..~w", [FromVal, ToVal]);
+ _ -> flat_format("Badly formed type ~w",[Type])
+ end;
+t_form_to_string({type, _L, record, [{atom, _, Name}]}) ->
+ flat_format("#~w{}", [Name]);
+t_form_to_string({type, _L, record, [{atom, _, Name}|Fields]}) ->
+ FieldString = string:join(t_form_to_string_list(Fields), ","),
+ flat_format("#~w{~s}", [Name, FieldString]);
+t_form_to_string({type, _L, field_type, [{atom, _, Name}, Type]}) ->
+ flat_format("~w::~s", [Name, t_form_to_string(Type)]);
+t_form_to_string({type, _L, term, []}) -> "term()";
+t_form_to_string({type, _L, timeout, []}) -> "timeout()";
+t_form_to_string({type, _L, tuple, any}) -> "tuple()";
+t_form_to_string({type, _L, tuple, Args}) ->
+ "{" ++ string:join(t_form_to_string_list(Args), ",") ++ "}";
+t_form_to_string({type, _L, union, Args}) ->
+ string:join(t_form_to_string_list(Args), " | ");
+t_form_to_string({type, _L, Name, []} = T) ->
+ try
+ M = mod,
+ D0 = dict:new(),
+ MR = dict:from_list([{M, D0}]),
+ Site = {type, {M,Name,0}},
+ V = var_table__new(),
+ C = cache__new(),
+ State = #from_form{site = Site,
+ xtypes = sets:new(),
+ mrecs = MR,
+ vtab = V,
+ tnames = []},
+ {T1, _, _} = from_form(T, State, _Deep=1000, _ALot=1000000, C),
+ t_to_string(T1)
+ catch throw:{error, _} -> atom_to_string(Name) ++ "()"
+ end;
+t_form_to_string({user_type, _L, Name, List}) ->
+ flat_format("~w(~s)",
+ [Name, string:join(t_form_to_string_list(List), ",")]);
+t_form_to_string({type, L, Name, List}) ->
+ %% Compatibility: modules compiled before Erlang/OTP 18.0.
+ t_form_to_string({user_type, L, Name, List}).
+
+t_form_to_string_list(List) ->
+ t_form_to_string_list(List, []).
+
+t_form_to_string_list([H|T], Acc) ->
+ t_form_to_string_list(T, [t_form_to_string(H)|Acc]);
+t_form_to_string_list([], Acc) ->
+ lists:reverse(Acc).
+
+-spec atom_to_string(atom()) -> string().
+
+atom_to_string(Atom) ->
+ flat_format("~w", [Atom]).
+
+%%=============================================================================
+%%
+%% Utilities
+%%
+%%=============================================================================
+
+-spec any_none([erl_type()]) -> boolean().
+
+any_none([?none|_Left]) -> true;
+any_none([_|Left]) -> any_none(Left);
+any_none([]) -> false.
+
+-spec any_none_or_unit([erl_type()]) -> boolean().
+
+any_none_or_unit([?none|_]) -> true;
+any_none_or_unit([?unit|_]) -> true;
+any_none_or_unit([_|Left]) -> any_none_or_unit(Left);
+any_none_or_unit([]) -> false.
+
+-spec is_erl_type(any()) -> boolean().
+
+is_erl_type(?any) -> true;
+is_erl_type(?none) -> true;
+is_erl_type(?unit) -> true;
+is_erl_type(#c{}) -> true;
+is_erl_type(_) -> false.
+
+-spec lookup_record(atom(), type_table()) ->
+ 'error' | {'ok', [{atom(), parse_form(), erl_type()}]}.
+
+lookup_record(Tag, RecDict) when is_atom(Tag) ->
+ case dict:find({record, Tag}, RecDict) of
+ {ok, {_FileLine, [{_Arity, Fields}]}} ->
+ {ok, Fields};
+ {ok, {_FileLine, List}} when is_list(List) ->
+ %% This will have to do, since we do not know which record we
+ %% are looking for.
+ error;
+ error ->
+ error
+ end.
+
+-spec lookup_record(atom(), arity(), type_table()) ->
+ 'error' | {'ok', [{atom(), parse_form(), erl_type()}]}.
+
+lookup_record(Tag, Arity, RecDict) when is_atom(Tag) ->
+ case dict:find({record, Tag}, RecDict) of
+ {ok, {_FileLine, [{Arity, Fields}]}} -> {ok, Fields};
+ {ok, {_FileLine, OrdDict}} -> orddict:find(Arity, OrdDict);
+ error -> error
+ end.
+
+-spec lookup_type(_, _, _) -> {'type' | 'opaque', type_value()} | 'error'.
+lookup_type(Name, Arity, RecDict) ->
+ case dict:find({type, Name, Arity}, RecDict) of
+ error ->
+ case dict:find({opaque, Name, Arity}, RecDict) of
+ error -> error;
+ {ok, Found} -> {opaque, Found}
+ end;
+ {ok, Found} -> {type, Found}
+ end.
+
+-spec type_is_defined('type' | 'opaque', atom(), arity(), type_table()) ->
+ boolean().
+
+type_is_defined(TypeOrOpaque, Name, Arity, RecDict) ->
+ dict:is_key({TypeOrOpaque, Name, Arity}, RecDict).
+
+cannot_have_opaque(Type, TypeName, TypeNames) ->
+ t_is_none(Type) orelse is_recursive(TypeName, TypeNames).
+
+is_recursive(TypeName, TypeNames) ->
+ lists:member(TypeName, TypeNames).
+
+can_unfold_more(TypeName, TypeNames) ->
+ Fun = fun(E, Acc) -> case E of TypeName -> Acc + 1; _ -> Acc end end,
+ lists:foldl(Fun, 0, TypeNames) < ?REC_TYPE_LIMIT.
+
+-spec do_opaque(erl_type(), opaques(), fun((_) -> T)) -> T.
+
+%% Probably a little faster than calling t_unopaque/2.
+%% Unions that are due to opaque types are unopaqued.
+do_opaque(?opaque(_) = Type, Opaques, Pred) ->
+ case Opaques =:= 'universe' orelse is_opaque_type(Type, Opaques) of
+ true -> do_opaque(t_opaque_structure(Type), Opaques, Pred);
+ false -> Pred(Type)
+ end;
+do_opaque(?union(List) = Type, Opaques, Pred) ->
+ [A,B,F,I,L,N,T,M,O,Map] = List,
+ if O =:= ?none -> Pred(Type);
+ true ->
+ case Opaques =:= 'universe' orelse is_opaque_type(O, Opaques) of
+ true ->
+ S = t_opaque_structure(O),
+ do_opaque(t_sup([A,B,F,I,L,N,T,M,S,Map]), Opaques, Pred);
+ false -> Pred(Type)
+ end
+ end;
+do_opaque(Type, _Opaques, Pred) ->
+ Pred(Type).
+
+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).
+
+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
+%%
+
+set_singleton(Element) ->
+ ordsets:from_list([Element]).
+
+set_is_singleton(Element, Set) ->
+ set_singleton(Element) =:= Set.
+
+set_is_element(Element, Set) ->
+ ordsets:is_element(Element, Set).
+
+set_union(?any, _) -> ?any;
+set_union(_, ?any) -> ?any;
+set_union(S1, S2) ->
+ case ordsets:union(S1, S2) of
+ S when length(S) =< ?SET_LIMIT -> S;
+ _ -> ?any
+ end.
+
+%% The intersection and subtraction can return ?none.
+%% This should always be handled right away since ?none is not a valid set.
+%% However, ?any is considered a valid set.
+
+set_intersection(?any, S) -> S;
+set_intersection(S, ?any) -> S;
+set_intersection(S1, S2) ->
+ case ordsets:intersection(S1, S2) of
+ [] -> ?none;
+ S -> S
+ end.
+
+set_subtract(_, ?any) -> ?none;
+set_subtract(?any, _) -> ?any;
+set_subtract(S1, S2) ->
+ case ordsets:subtract(S1, S2) of
+ [] -> ?none;
+ S -> S
+ end.
+
+set_from_list(List) ->
+ case length(List) of
+ L when L =< ?SET_LIMIT -> ordsets:from_list(List);
+ L when L > ?SET_LIMIT -> ?any
+ end.
+
+set_to_list(Set) ->
+ ordsets:to_list(Set).
+
+set_filter(Fun, Set) ->
+ case ordsets:filter(Fun, Set) of
+ [] -> ?none;
+ NewSet -> NewSet
+ end.
+
+set_size(Set) ->
+ ordsets:size(Set).
+
+set_to_string(Set) ->
+ L = [case is_atom(X) of
+ true -> io_lib:write_string(atom_to_list(X), $'); % stupid emacs '
+ false -> flat_format("~w", [X])
+ end || X <- set_to_list(Set)],
+ string:join(L, " | ").
+
+set_min([H|_]) -> H.
+
+set_max(Set) ->
+ hd(lists:reverse(Set)).
+
+flat_format(F, S) ->
+ lists:flatten(io_lib:format(F, S)).
+
+%%=============================================================================
+%%
+%% Utilities for the binary type
+%%
+%%=============================================================================
+
+-spec gcd(integer(), integer()) -> integer().
+
+gcd(A, B) when B > A ->
+ gcd1(B, A);
+gcd(A, B) ->
+ gcd1(A, B).
+
+-spec gcd1(integer(), integer()) -> integer().
+
+gcd1(A, 0) -> A;
+gcd1(A, B) ->
+ case A rem B of
+ 0 -> B;
+ X -> gcd1(B, X)
+ end.
+
+-spec bitstr_concat(erl_type(), erl_type()) -> erl_type().
+
+bitstr_concat(?none, _) -> ?none;
+bitstr_concat(_, ?none) -> ?none;
+bitstr_concat(?bitstr(U1, B1), ?bitstr(U2, B2)) ->
+ t_bitstr(gcd(U1, U2), B1+B2).
+
+-spec bitstr_match(erl_type(), erl_type()) -> erl_type().
+
+bitstr_match(?none, _) -> ?none;
+bitstr_match(_, ?none) -> ?none;
+bitstr_match(?bitstr(0, B1), ?bitstr(0, B2)) when B1 =< B2 ->
+ t_bitstr(0, B2-B1);
+bitstr_match(?bitstr(0, _B1), ?bitstr(0, _B2)) ->
+ ?none;
+bitstr_match(?bitstr(0, B1), ?bitstr(U2, B2)) when B1 =< B2 ->
+ t_bitstr(U2, B2-B1);
+bitstr_match(?bitstr(0, B1), ?bitstr(U2, B2)) ->
+ t_bitstr(U2, handle_base(U2, B2-B1));
+bitstr_match(?bitstr(_, B1), ?bitstr(0, B2)) when B1 > B2 ->
+ ?none;
+bitstr_match(?bitstr(U1, B1), ?bitstr(U2, B2)) ->
+ GCD = gcd(U1, U2),
+ t_bitstr(GCD, handle_base(GCD, B2-B1)).
+
+-spec handle_base(integer(), integer()) -> integer().
+
+handle_base(Unit, Pos) when Pos >= 0 ->
+ Pos rem Unit;
+handle_base(Unit, Neg) ->
+ (Unit+(Neg rem Unit)) rem Unit.
+
+family(L) ->
+ R = sofs:relation(L),
+ F = sofs:relation_to_family(R),
+ sofs:to_external(F).
+
+%%=============================================================================
+%%
+%% Interface functions for abstract data types defined in this module
+%%
+%%=============================================================================
+
+-spec var_table__new() -> var_table().
+
+var_table__new() ->
+ maps:new().
+
+%%=============================================================================
+%% Consistency-testing function(s) below
+%%=============================================================================
+
+-ifdef(DO_ERL_TYPES_TEST).
+
+test() ->
+ Atom1 = t_atom(),
+ Atom2 = t_atom(foo),
+ Atom3 = t_atom(bar),
+ true = t_is_atom(Atom2),
+
+ True = t_atom(true),
+ False = t_atom(false),
+ Bool = t_boolean(),
+ true = t_is_boolean(True),
+ true = t_is_boolean(Bool),
+ false = t_is_boolean(Atom1),
+
+ Binary = t_binary(),
+ true = t_is_binary(Binary),
+
+ Bitstr = t_bitstr(),
+ true = t_is_bitstr(Bitstr),
+
+ Bitstr1 = t_bitstr(7, 3),
+ true = t_is_bitstr(Bitstr1),
+ false = t_is_binary(Bitstr1),
+
+ Bitstr2 = t_bitstr(16, 8),
+ true = t_is_bitstr(Bitstr2),
+ true = t_is_binary(Bitstr2),
+
+ ?bitstr(8, 16) = t_subtract(t_bitstr(4, 12), t_bitstr(8, 12)),
+ ?bitstr(8, 16) = t_subtract(t_bitstr(4, 12), t_bitstr(8, 12)),
+
+ Int1 = t_integer(),
+ Int2 = t_integer(1),
+ Int3 = t_integer(16#ffffffff),
+ true = t_is_integer(Int2),
+ true = t_is_byte(Int2),
+ false = t_is_byte(Int3),
+ false = t_is_byte(t_from_range(-1, 1)),
+ true = t_is_byte(t_from_range(1, ?MAX_BYTE)),
+
+ Tuple1 = t_tuple(),
+ Tuple2 = t_tuple(3),
+ Tuple3 = t_tuple([Atom1, Int1]),
+ Tuple4 = t_tuple([Tuple1, Tuple2]),
+ Tuple5 = t_tuple([Tuple3, Tuple4]),
+ Tuple6 = t_limit(Tuple5, 2),
+ Tuple7 = t_limit(Tuple5, 3),
+ true = t_is_tuple(Tuple1),
+
+ Port = t_port(),
+ Pid = t_pid(),
+ Ref = t_reference(),
+ Identifier = t_identifier(),
+ false = t_is_reference(Port),
+ true = t_is_identifier(Port),
+
+ Function1 = t_fun(),
+ Function2 = t_fun(Pid),
+ Function3 = t_fun([], Pid),
+ Function4 = t_fun([Port, Pid], Pid),
+ Function5 = t_fun([Pid, Atom1], Int2),
+ true = t_is_fun(Function3),
+
+ List1 = t_list(),
+ List2 = t_list(t_boolean()),
+ List3 = t_cons(t_boolean(), List2),
+ List4 = t_cons(t_boolean(), t_atom()),
+ List5 = t_cons(t_boolean(), t_nil()),
+ List6 = t_cons_tl(List5),
+ List7 = t_sup(List4, List5),
+ List8 = t_inf(List7, t_list()),
+ List9 = t_cons(),
+ List10 = t_cons_tl(List9),
+ true = t_is_boolean(t_cons_hd(List5)),
+ true = t_is_list(List5),
+ false = t_is_list(List4),
+
+ Product1 = t_product([Atom1, Atom2]),
+ Product2 = t_product([Atom3, Atom1]),
+ Product3 = t_product([Atom3, Atom2]),
+
+ Union1 = t_sup(Atom2, Atom3),
+ Union2 = t_sup(Tuple2, Tuple3),
+ Union3 = t_sup(Int2, Atom3),
+ Union4 = t_sup(Port, Pid),
+ Union5 = t_sup(Union4, Int1),
+ Union6 = t_sup(Function1, Function2),
+ Union7 = t_sup(Function4, Function5),
+ Union8 = t_sup(True, False),
+ true = t_is_boolean(Union8),
+ Union9 = t_sup(Int2, t_integer(2)),
+ true = t_is_byte(Union9),
+ Union10 = t_sup(t_tuple([t_atom(true), ?any]),
+ t_tuple([t_atom(false), ?any])),
+
+ ?any = t_sup(Product3, Function5),
+
+ Atom3 = t_inf(Union3, Atom1),
+ Union2 = t_inf(Union2, Tuple1),
+ Int2 = t_inf(Int1, Union3),
+ Union4 = t_inf(Union4, Identifier),
+ Port = t_inf(Union5, Port),
+ Function4 = t_inf(Union7, Function4),
+ ?none = t_inf(Product2, Atom1),
+ Product3 = t_inf(Product1, Product2),
+ Function5 = t_inf(Union7, Function5),
+ true = t_is_byte(t_inf(Union9, t_number())),
+ true = t_is_char(t_inf(Union9, t_number())),
+
+ io:format("3? ~p ~n", [?int_set([3])]),
+
+ RecDict = dict:store({foo, 2}, [bar, baz], dict:new()),
+ Record1 = t_from_term({foo, [1,2], {1,2,3}}),
+
+ Types = [
+ Atom1,
+ Atom2,
+ Atom3,
+ Binary,
+ Int1,
+ Int2,
+ Tuple1,
+ Tuple2,
+ Tuple3,
+ Tuple4,
+ Tuple5,
+ Tuple6,
+ Tuple7,
+ Ref,
+ Port,
+ Pid,
+ Identifier,
+ List1,
+ List2,
+ List3,
+ List4,
+ List5,
+ List6,
+ List7,
+ List8,
+ List9,
+ List10,
+ Function1,
+ Function2,
+ Function3,
+ Function4,
+ Function5,
+ Product1,
+ Product2,
+ Record1,
+ Union1,
+ Union2,
+ Union3,
+ Union4,
+ Union5,
+ Union6,
+ Union7,
+ Union8,
+ Union10,
+ t_inf(Union10, t_tuple([t_atom(true), t_integer()]))
+ ],
+ io:format("~p\n", [[t_to_string(X, RecDict) || X <- Types]]).
+
+-endif.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl b/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl
index c19330eb30..597460ce77 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/simple/exact_api.erl
@@ -52,7 +52,7 @@ exact_api_set_type(#exact_api{}=E) -> E.
-record(exact_adt, {}).
exact_adt_test(X) ->
- #exact_adt{} = exact_adt:exact_adt_set_type(X). % breaks the opaqueness
+ #exact_adt{} = exact_adt:exact_adt_set_type(X). % breaks the opacity
exact_adt_new(A) ->
A = #exact_adt{},
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/simple/is_rec.erl b/lib/dialyzer/test/opaque_SUITE_data/src/simple/is_rec.erl
index 2b157483bc..b906431b44 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/simple/is_rec.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/simple/is_rec.erl
@@ -16,11 +16,11 @@ ri11() ->
ri13() ->
A = simple1_adt:d1(),
- if is_record(A, r) -> true end. % breaks the opaqueness
+ if is_record(A, r) -> true end. % breaks the opacity
ri14() ->
A = simple1_adt:d1(),
- if is_record({A, 1}, r) -> true end. % breaks the opaqueness
+ if is_record({A, 1}, r) -> true end. % breaks the opacity
-type '1-3-t'() :: 1..3.
@@ -54,7 +54,7 @@ ri5() ->
ri6() ->
A = simple1_adt:d1(),
- if is_record(A, r) -> true end. % breaks opaqueness
+ if is_record(A, r) -> true end. % breaks opacity
ri7() ->
A = simple1_adt:d1(),
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/simple/rec_api.erl b/lib/dialyzer/test/opaque_SUITE_data/src/simple/rec_api.erl
index fb6d59d263..59b9e0fec4 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/simple/rec_api.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/simple/rec_api.erl
@@ -30,7 +30,7 @@ t3() ->
adt_t1() ->
R = rec_adt:r1(),
- {r1, a} = R. % breaks the opaqueness
+ {r1, a} = R. % breaks the opacity
-spec adt_t1(rec_adt:r1()) -> rec_adt:r1(). % invalid type spec
@@ -82,7 +82,7 @@ f() ->
r_adt() ->
{{r, rec_adt:f(), 2},
- #r{f = rec_adt:f(), o = 2}}. % breaks the opaqueness
+ #r{f = rec_adt:f(), o = 2}}. % breaks the opacity
-record(r2, % like #r1{}, but with initial value
{f1 = a :: a()}).
@@ -110,7 +110,7 @@ u3() ->
v1() ->
A = #r3{f1 = queue:new()},
- {r3, a} = A. % breaks the opaqueness
+ {r3, a} = A. % breaks the opacity
v2() ->
A = {r3, 10},
@@ -120,4 +120,4 @@ v2() ->
v3() ->
A = {r3, 10},
- #r3{f1 = 10} = A. % breaks the opaqueness
+ #r3{f1 = 10} = A. % breaks the opacity
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/simple/simple1_api.erl b/lib/dialyzer/test/opaque_SUITE_data/src/simple/simple1_api.erl
index 7db1100597..d67aa913d8 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/simple/simple1_api.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/simple/simple1_api.erl
@@ -194,7 +194,7 @@ tt1() ->
tt2() ->
A = simple1_adt:d1(),
- is_integer(A). % breaks the opaqueness
+ is_integer(A). % breaks the opacity
%% Comparison with integers
@@ -262,11 +262,11 @@ f2() ->
adt_f1() ->
T = simple1_adt:n1(),
- if is_function(T) -> ok end. % breaks the opaqueness
+ if is_function(T) -> ok end. % breaks the opacity
adt_f2() ->
T = simple1_adt:n1(),
- is_function(T). % breaks the opaqueness
+ is_function(T). % breaks the opacity
f3() ->
A = i1(),
@@ -281,12 +281,12 @@ f4() ->
adt_f3() ->
A = simple1_adt:i1(),
T = simple1_adt:n1(),
- if is_function(T, A) -> ok end. % breaks the opaqueness
+ if is_function(T, A) -> ok end. % breaks the opacity
adt_f4() ->
A = simple1_adt:i1(),
T = simple1_adt:n1(),
- is_function(T, A). % breaks the opaqueness
+ is_function(T, A). % breaks the opacity
adt_f4_a() ->
A = simple1_adt:i1(),
@@ -297,7 +297,7 @@ adt_f4_a() ->
adt_f4_b() ->
A = i1(),
T = simple1_adt:n1(),
- is_function(T, A). % breaks the opaqueness
+ is_function(T, A). % breaks the opacity
%% A few Boolean examples
@@ -404,7 +404,7 @@ bit_t1() ->
bit_adt_t1() ->
A = simple1_adt:i1(),
- <<100:(A)>>. % breaks the opaqueness
+ <<100:(A)>>. % breaks the opacity
bit_t3(A) ->
B = i1(),
@@ -415,14 +415,14 @@ bit_t3(A) ->
bit_adt_t2() ->
A = simple1_adt:i1(),
case <<"hej">> of
- <<_:A>> -> ok % breaks the opaqueness (but the message is strange)
+ <<_:A>> -> ok % breaks the opacity (but the message is strange)
end.
bit_adt_t3(A) ->
B = simple1_adt:i1(),
case none:none() of
- <<A: % breaks the opaqueness (the message is less than perfect)
+ <<A: % breaks the opacity (the message is less than perfect)
B>> -> 1
end.
@@ -445,7 +445,7 @@ bit_t4(A) ->
bit_adt_t4(A) ->
Sz = simple1_adt:i1(),
case A of
- <<_:Sz>> -> 1 % breaks the opaqueness
+ <<_:Sz>> -> 1 % breaks the opacity
end.
bit_t5() ->
@@ -457,7 +457,7 @@ bit_t5() ->
bit_adt_t5() ->
A = simple1_adt:bit1(),
case A of
- <<_/binary>> -> 1 % breaks the opaqueness
+ <<_/binary>> -> 1 % breaks the opacity
end.
-opaque bit1() :: binary().
@@ -475,7 +475,7 @@ call_f(A) ->
call_f_adt(A) ->
A = simple1_adt:a(),
- foo:A(A). % breaks the opaqueness
+ foo:A(A). % breaks the opacity
call_m(A) ->
A = a(),
@@ -483,7 +483,7 @@ call_m(A) ->
call_m_adt(A) ->
A = simple1_adt:a(),
- A:foo(A). % breaks the opaqueness
+ A:foo(A). % breaks the opacity
-opaque a() :: atom().
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/timer/timer_use.erl b/lib/dialyzer/test/opaque_SUITE_data/src/timer/timer_use.erl
index 9c8ea0af1c..ed6810634f 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/timer/timer_use.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/timer/timer_use.erl
@@ -1,8 +1,8 @@
%%---------------------------------------------------------------------------
%% A test case with:
%% - a genuine matching error -- 1st branch
-%% - a violation of the opaqueness of timer:tref() -- 2nd branch
-%% - a subtle violation of the opaqueness of timer:tref() -- 3rd branch
+%% - a violation of the opacity of timer:tref() -- 2nd branch
+%% - a subtle violation of the opacity of timer:tref() -- 3rd branch
%% The test is supposed to check that these cases are treated properly.
%%---------------------------------------------------------------------------
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl
new file mode 100644
index 0000000000..094138e72b
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl
@@ -0,0 +1,18 @@
+-module(weird_warning1).
+-export([public_func/0]).
+
+-record(a, {
+ d = dict:new() :: dict:dict()
+ }).
+
+-record(b, {
+ q = queue:new() :: queue:queue()
+ }).
+
+public_func() ->
+ add_element(#b{}, my_key, my_value).
+
+add_element(#a{d = Dict}, Key, Value) ->
+ dict:store(Key, Value, Dict);
+add_element(#b{q = Queue}, Key, Value) ->
+ queue:in({Key, Value}, Queue).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl
new file mode 100644
index 0000000000..4e4512157b
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl
@@ -0,0 +1,14 @@
+-module(weird_warning2).
+-export([public_func/0]).
+
+-record(a, {d = dict:new() :: dict:dict()}).
+
+-record(b, {q = queue:new() :: queue:queue()}).
+
+public_func() ->
+ add_element(#a{}, my_key, my_value).
+
+add_element(#a{d = Dict}, Key, Value) ->
+ dict:store(Key, Value, Dict);
+add_element(#b{q = Queue}, Key, Value) ->
+ queue:in({Key, Value}, Queue).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl
new file mode 100644
index 0000000000..b70ca645cb
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl
@@ -0,0 +1,19 @@
+-module(weird_warning3).
+-export([public_func/0]).
+
+-record(a, {
+ d = dict:new() :: dict:dict()
+ }).
+
+-record(b, {
+ q = queue:new() :: queue:queue()
+ }).
+
+public_func() ->
+ %% Notice that t_to_string() will create "#a{d::queue:queue(_)}".
+ add_element({a, queue:new()}, my_key, my_value).
+
+add_element(#a{d = Dict}, Key, Value) ->
+ dict:store(Key, Value, Dict);
+add_element(#b{q = Queue}, Key, Value) ->
+ queue:in({Key, Value}, Queue).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl
index ca6bc0ab4a..6b825d85fe 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/wings/wings_util.erl
@@ -14,12 +14,12 @@
rel2fam(Rel) ->
sofs:to_external(sofs:relation_to_family(sofs:relation(Rel))).
-%% a definition that does not violate the opaqueness of gb_trees:tree()
+%% a definition that does not violate the opacity of gb_trees:tree()
gb_trees_smallest_key(Tree) ->
{Key, _V} = gb_trees:smallest(Tree),
Key.
-%% a definition that violates the opaqueness of gb_trees:tree()
+%% a definition that violates the opacity of gb_trees:tree()
gb_trees_largest_key({_, Tree}) ->
largest_key1(Tree).
diff --git a/lib/dialyzer/test/options2_SUITE_data/dialyzer_options b/lib/dialyzer/test/options2_SUITE_data/dialyzer_options
index 5db2e50d23..be57e2de72 100644
--- a/lib/dialyzer/test/options2_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/options2_SUITE_data/dialyzer_options
@@ -1 +1 @@
-{dialyzer_options, [{defines, [{'vsn', 4}]}, {warnings, [no_return]}]}.
+{dialyzer_options, [{defines, [{'vsn', 4}]}, {warnings, [unknown, no_return]}]}.
diff --git a/lib/dialyzer/test/options2_SUITE_data/results/unknown_type b/lib/dialyzer/test/options2_SUITE_data/results/unknown_type
new file mode 100644
index 0000000000..d308c5a810
--- /dev/null
+++ b/lib/dialyzer/test/options2_SUITE_data/results/unknown_type
@@ -0,0 +1,2 @@
+
+:0: Unknown type unknown:type/0 \ No newline at end of file
diff --git a/lib/dialyzer/test/options2_SUITE_data/src/unknown_type.erl b/lib/dialyzer/test/options2_SUITE_data/src/unknown_type.erl
new file mode 100644
index 0000000000..7a891c0831
--- /dev/null
+++ b/lib/dialyzer/test/options2_SUITE_data/src/unknown_type.erl
@@ -0,0 +1,7 @@
+-module(unknown_type).
+
+-export([t/0]).
+
+-spec t() -> unknown:type().
+t() ->
+ a.
diff --git a/lib/dialyzer/test/plt_SUITE.erl b/lib/dialyzer/test/plt_SUITE.erl
index 6ebe23b54b..fbfa979e1b 100644
--- a/lib/dialyzer/test/plt_SUITE.erl
+++ b/lib/dialyzer/test/plt_SUITE.erl
@@ -8,13 +8,15 @@
-export([suite/0, all/0, build_plt/1, beam_tests/1, update_plt/1,
local_fun_same_as_callback/1,
- remove_plt/1, run_plt_check/1, run_succ_typings/1]).
+ remove_plt/1, run_plt_check/1, run_succ_typings/1,
+ bad_dialyzer_attr/1]).
suite() ->
[{timetrap, ?plt_timeout}].
all() -> [build_plt, beam_tests, update_plt, run_plt_check,
- remove_plt, run_succ_typings, local_fun_same_as_callback].
+ remove_plt, run_succ_typings, local_fun_same_as_callback,
+ bad_dialyzer_attr].
build_plt(Config) ->
OutDir = ?config(priv_dir, Config),
@@ -24,6 +26,8 @@ build_plt(Config) ->
end.
beam_tests(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ Plt = filename:join(PrivDir, "beam_tests.plt"),
Prog = <<"
-module(no_auto_import).
@@ -40,10 +44,12 @@ beam_tests(Config) when is_list(Config) ->
">>,
Opts = [no_auto_import],
{ok, BeamFile} = compile(Config, Prog, no_auto_import, Opts),
- [] = run_dialyzer(plt_build, [BeamFile], []),
+ [] = run_dialyzer(plt_build, [BeamFile], [{output_plt, Plt}]),
ok.
run_plt_check(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ Plt = filename:join(PrivDir, "run_plt_check.plt"),
Mod1 = <<"
-module(run_plt_check1).
">>,
@@ -54,7 +60,7 @@ run_plt_check(Config) when is_list(Config) ->
{ok, BeamFile1} = compile(Config, Mod1, run_plt_check1, []),
{ok, BeamFile2} = compile(Config, Mod2A, run_plt_check2, []),
- [] = run_dialyzer(plt_build, [BeamFile1, BeamFile2], []),
+ [] = run_dialyzer(plt_build, [BeamFile1, BeamFile2], [{output_plt, Plt}]),
Mod2B = <<"
-module(run_plt_check2).
@@ -68,11 +74,13 @@ run_plt_check(Config) when is_list(Config) ->
% callgraph warning as run_plt_check2:call/1 makes a call to unexported
% function run_plt_check1:call/1.
- [_] = run_dialyzer(plt_check, [], []),
+ [_] = run_dialyzer(plt_check, [], [{init_plt, Plt}]),
ok.
run_succ_typings(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ Plt = filename:join(PrivDir, "run_succ_typings.plt"),
Mod1A = <<"
-module(run_succ_typings1).
@@ -82,7 +90,7 @@ run_succ_typings(Config) when is_list(Config) ->
">>,
{ok, BeamFile1} = compile(Config, Mod1A, run_succ_typings1, []),
- [] = run_dialyzer(plt_build, [BeamFile1], []),
+ [] = run_dialyzer(plt_build, [BeamFile1], [{output_plt, Plt}]),
Mod1B = <<"
-module(run_succ_typings1).
@@ -105,9 +113,11 @@ run_succ_typings(Config) when is_list(Config) ->
{ok, BeamFile2} = compile(Config, Mod2, run_succ_typings2, []),
% contract types warning as run_succ_typings2:call/0 makes a call to
% run_succ_typings1:call/0, which returns a (not b) in the PLT.
- [_] = run_dialyzer(succ_typings, [BeamFile2], [{check_plt, false}]),
+ [_] = run_dialyzer(succ_typings, [BeamFile2],
+ [{check_plt, false}, {init_plt, Plt}]),
% warning not returned as run_succ_typings1 is updated in the PLT.
- [] = run_dialyzer(succ_typings, [BeamFile2], [{check_plt, true}]),
+ [] = run_dialyzer(succ_typings, [BeamFile2],
+ [{check_plt, true}, {init_plt, Plt}]),
ok.
@@ -249,6 +259,27 @@ remove_plt(Config) ->
{init_plt, Plt}] ++ Opts),
ok.
+bad_dialyzer_attr(Config) ->
+ Prog1 = <<"-module(dial).
+ -dialyzer({no_return, [undef/0]}).">>,
+ {ok, Beam1} = compile(Config, Prog1, dial, []),
+ {dialyzer_error,
+ "Analysis failed with error:\n"
+ "Could not scan the following file(s):\n"
+ " Unknown function undef/0 in line " ++ _} =
+ (catch run_dialyzer(plt_build, [Beam1], [])),
+
+ Prog2 = <<"-module(dial).
+ -dialyzer({no_return, [{undef,1,2}]}).">>,
+ {ok, Beam2} = compile(Config, Prog2, dial, []),
+ {dialyzer_error,
+ "Analysis failed with error:\n"
+ "Could not scan the following file(s):\n"
+ " Bad function {undef,1,2} in line " ++ _} =
+ (catch run_dialyzer(plt_build, [Beam2], [])),
+
+ ok.
+
compile(Config, Prog, Module, CompileOpts) ->
Source = lists:concat([Module, ".erl"]),
PrivDir = ?config(priv_dir,Config),
diff --git a/lib/dialyzer/test/small_SUITE_data/results/chars b/lib/dialyzer/test/small_SUITE_data/results/chars
new file mode 100644
index 0000000000..2c1f8f8d17
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/chars
@@ -0,0 +1,4 @@
+
+chars.erl:29: Invalid type specification for function chars:f/1. The success typing is (#{'b':=50}) -> 'ok'
+chars.erl:32: Function t1/0 has no local return
+chars.erl:32: The call chars:f(#{'b':=50}) breaks the contract (#{'a':=49,'b'=>50,'c'=>51}) -> 'ok'
diff --git a/lib/dialyzer/test/small_SUITE_data/results/guards b/lib/dialyzer/test/small_SUITE_data/results/guards
index 824a7cfa24..cd0d3cace0 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/guards
+++ b/lib/dialyzer/test/small_SUITE_data/results/guards
@@ -10,8 +10,8 @@ guards.erl:136: The call guards:t16('a') will never return since it differs in t
guards.erl:136: The call guards:t16('c') will never return since it differs in the 1st argument from the success typing arguments: ('b')
guards.erl:55: Function t5/1 has no local return
guards.erl:55: Guard test is_integer(A::atom()) can never succeed
-guards.erl:59: Clause guard cannot succeed. The variable A was matched against the type any()
guards.erl:59: Function t6/1 has no local return
+guards.erl:59: Guard test is_integer(A::atom()) can never succeed
guards.erl:67: The call guards:t7({42}) will never return since it differs in the 1st argument from the success typing arguments: (atom() | integer())
guards.erl:75: The call guards:t8({42}) will never return since it differs in the 1st argument from the success typing arguments: (atom() | integer())
guards.erl:92: The variable _ can never match since previous clauses completely covered the type {'true','true'}
diff --git a/lib/dialyzer/test/small_SUITE_data/src/anno.erl b/lib/dialyzer/test/small_SUITE_data/src/anno.erl
new file mode 100644
index 0000000000..70f1d42141
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/anno.erl
@@ -0,0 +1,18 @@
+-module(anno).
+
+%% OTP-14131
+
+-export([t1/0, t2/0, t3/0]).
+
+t1() ->
+ A = erl_parse:anno_from_term({attribute, 1, module, my_test}),
+ compile:forms([A], []).
+
+t2() ->
+ A = erl_parse:new_anno({attribute, 1, module, my_test}),
+ compile:forms([A], []).
+
+t3() ->
+ A = erl_parse:new_anno({attribute, 1, module, my_test}),
+ T = erl_parse:anno_to_term(A),
+ {attribute, 1, module, my_test} = T.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/chars.erl b/lib/dialyzer/test/small_SUITE_data/src/chars.erl
new file mode 100644
index 0000000000..1e9c8ab6b9
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/chars.erl
@@ -0,0 +1,32 @@
+-module(chars).
+
+%% ERL-313
+
+-export([t/0]).
+-export([t1/0]).
+
+-record(r, {f :: $A .. $Z}).
+
+-type cs() :: $A..$Z | $a .. $z | $/.
+
+-spec t() -> $0-$0..$9-$0| $?.
+
+t() ->
+ c(#r{f = $z - 3}),
+ c($z - 3),
+ c($B).
+
+-spec c(cs()) -> $3-$0..$9-$0.
+
+c($A + 1) -> 2;
+c(C) ->
+ case C of
+ $z - 3 -> 3;
+ #r{f = $z - 3} -> 7
+ end.
+
+%% Display contract with character in warning:
+-spec f(#{a := $1, b => $2, c => $3}) -> ok. % invalid type spec
+f(_) -> ok.
+
+t1() -> f(#{b => $2}). % breaks the contract
diff --git a/lib/dialyzer/test/small_SUITE_data/src/ms.erl b/lib/dialyzer/test/small_SUITE_data/src/ms.erl
new file mode 100644
index 0000000000..47a5e886cf
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/ms.erl
@@ -0,0 +1,8 @@
+-module(ms).
+-export([t/0]).
+
+-include_lib("stdlib/include/ms_transform.hrl").
+
+t() ->
+ MS = dbg:fun2ms(fun(All) -> message(All) end),
+ erlang:trace_pattern({m, f, '_'}, MS).
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index 077fe01e85..7d239fe9cd 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 3.0
+DIALYZER_VSN = 3.1.1
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index d68a78ed6d..72181a42b0 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -94,12 +94,12 @@ in this module.</p>
<taglist>
-<tag><c>Address()</c></tag>
-<tag><c>DiameterIdentity()</c></tag>
-<tag><c>Grouped()</c></tag>
-<tag><c>OctetString()</c></tag>
-<tag><c>Time()</c></tag>
-<tag><c>Unsigned32()</c></tag>
+<tag><c>Address()</c></tag><item/>
+<tag><c>DiameterIdentity()</c></tag><item/>
+<tag><c>Grouped()</c></tag><item/>
+<tag><c>OctetString()</c></tag><item/>
+<tag><c>Time()</c></tag><item/>
+<tag><c>Unsigned32()</c></tag><item/>
<tag><c>UTF8String()</c></tag>
<item>
<p>
@@ -159,8 +159,7 @@ Has one the following types.</p>
<p>
Unique identifier for the application in the scope of the
service.
-Defaults to the value of the <c>dictionary</c> option if
-unspecified.</p>
+Defaults to the value of the <c>dictionary</c> option.</p>
</item>
<tag><c>{dictionary, atom()}</c></tag>
@@ -187,7 +186,7 @@ Initial callback state.
The prevailing state is passed to some
&man_app;
callbacks, which can then return a new state.
-Defaults to the value of the <c>alias</c> option if unspecified.</p>
+Defaults to the value of the <c>alias</c> option.</p>
</item>
<tag><c>{call_mutates_state, true|false}</c></tag>
@@ -195,7 +194,7 @@ Defaults to the value of the <c>alias</c> option if unspecified.</p>
<p>
Whether or not the &app_pick_peer;
application callback can modify the application state.
-Defaults to <c>false</c> if unspecified.</p>
+Defaults to <c>false</c>.</p>
<warning>
<p>
@@ -228,7 +227,7 @@ question is as if a callback had taken place and returned
<c>{error, failure}</c>.</p>
<p>
-Defaults to <c>discard</c> if unspecified.</p>
+Defaults to <c>discard</c>.</p>
</item>
<tag><c>{request_errors, answer_3xxx|answer|callback}</c></tag>
@@ -249,7 +248,7 @@ place and its return value determines the answer sent to the peer, if
any.</p>
<p>
-Defaults to <c>answer_3xxx</c> if unspecified.</p>
+Defaults to <c>answer_3xxx</c>.</p>
<note>
<p>
@@ -339,8 +338,8 @@ Has one of the following types.</p>
<taglist>
-<tag><c>{'Origin-Host', &dict_DiameterIdentity;}</c></tag>
-<tag><c>{'Origin-Realm', &dict_DiameterIdentity;}</c></tag>
+<tag><c>{'Origin-Host', &dict_DiameterIdentity;}</c></tag><item/>
+<tag><c>{'Origin-Realm', &dict_DiameterIdentity;}</c></tag><item/>
<tag><c>{'Host-IP-Address', [&dict_Address;]}</c></tag>
<item>
<p>
@@ -352,8 +351,8 @@ question communicates an address list as described in
&man_transport;</p>
</item>
-<tag><c>{'Vendor-Id', &dict_Unsigned32;}</c></tag>
-<tag><c>{'Product-Name', &dict_UTF8String;}</c></tag>
+<tag><c>{'Vendor-Id', &dict_Unsigned32;}</c></tag><item/>
+<tag><c>{'Product-Name', &dict_UTF8String;}</c></tag><item/>
<tag><c>{'Origin-State-Id', &dict_Unsigned32;}</c></tag>
<item>
<p>
@@ -366,8 +365,8 @@ can be used as to retrieve a value that is computed when the diameter
application is started.</p>
</item>
-<tag><c>{'Supported-Vendor-Id', [&dict_Unsigned32;]}</c></tag>
-<tag><c>{'Auth-Application-Id', [&dict_Unsigned32;]}</c></tag>
+<tag><c>{'Supported-Vendor-Id', [&dict_Unsigned32;]}</c></tag><item/>
+<tag><c>{'Auth-Application-Id', [&dict_Unsigned32;]}</c></tag><item/>
<tag><c>{'Inband-Security-Id', [&dict_Unsigned32;]}</c></tag>
<item>
<p>
@@ -377,9 +376,9 @@ If 1 (TLS) is specified then TLS is selected if the CER/CEA received
from the peer offers it.</p>
</item>
-<tag><c>{'Acct-Application-Id', [&dict_Unsigned32;]}</c></tag>
-<tag><c>{'Vendor-Specific-Application-Id', [&dict_Grouped;]}</c></tag>
-<tag><c>{'Firmware-Revision', &dict_Unsigned32;}</c></tag>
+<tag><c>{'Acct-Application-Id', [&dict_Unsigned32;]}</c></tag><item/>
+<tag><c>{'Vendor-Specific-Application-Id', [&dict_Grouped;]}</c></tag><item/>
+<tag><c>{'Firmware-Revision', &dict_Unsigned32;}</c></tag><item/>
</taglist>
@@ -567,9 +566,8 @@ Can have one of the following types.</p>
<taglist>
-<tag><c>start</c></tag>
+<tag><c>start</c></tag><item/>
<tag><c>stop</c></tag>
-
<item>
<p>
The service is being started or stopped.
@@ -578,8 +576,8 @@ No event follows a <c>stop</c> event, and this event
implies the termination of all transport processes.</p>
</item>
-<tag><c>{up, Ref, Peer, Config, Pkt}</c></tag>
-<tag><c>{up, Ref, Peer, Config}</c></tag>
+<tag><c>{up, Ref, Peer, Config, Pkt}</c></tag><item/>
+<tag><c>{up, Ref, Peer, Config}</c></tag><item/>
<tag><c>{down, Ref, Peer, Config}</c></tag>
<item>
<pre>
@@ -788,8 +786,8 @@ be matched by corresponding &capability; configuration, of
</item>
-<marker id="incoming_maxlen"/>
-<tag><c>{incoming_maxlen, 0..16777215}</c></tag>
+<tag>
+<marker id="incoming_maxlen"/><c>{incoming_maxlen, 0..16777215}</c></tag>
<item>
<p>
Bound on the expected size of incoming Diameter messages.
@@ -917,8 +915,8 @@ Options <c>monitor</c> and <c>link</c> are ignored.</p>
Defaults to the empty list.</p>
</item>
-<marker id="strict_mbit"/>
-<tag><c>{strict_mbit, boolean()}</c></tag>
+<tag>
+<marker id="strict_mbit"/><c>{strict_mbit, boolean()}</c></tag>
<item>
<p>
Whether or not to regard an AVP setting the M-bit as erroneous when
@@ -935,7 +933,7 @@ Defaults to <c>true</c>.</p>
<p>
RFC 6733 is unclear about the semantics of the M-bit.
One the one hand, the CCF specification in section 3.2 documents AVP
-in a command grammar as meaning <b>any</b> arbitrary AVP; on the
+in a command grammar as meaning <em>any</em> arbitrary AVP; on the
other hand, 1.3.4 states that AVPs setting the M-bit cannot be added
to an existing command: the modified command must instead be
placed in a new Diameter application.</p>
@@ -945,7 +943,7 @@ allowing arbitrary AVPs setting the M-bit in a command makes its
interpretation implementation-dependent, since there's no
guarantee that all implementations will understand the same set of
arbitrary AVPs in the context of a given command.
-However, interpreting <c>AVP</c> in a command grammar as <b>any</b>
+However, interpreting <c>AVP</c> in a command grammar as any
AVP, regardless of M-bit, renders 1.3.4 meaningless, since the receiver
can simply ignore any AVP it thinks isn't relevant, regardless of the
sender's intent.</p>
@@ -960,8 +958,8 @@ occur in the message in question.</p>
</item>
-<marker id="string_decode"/>
-<tag><c>{string_decode, boolean()}</c></tag>
+<tag>
+<marker id="string_decode"/><c>{string_decode, boolean()}</c></tag>
<item>
<p>
Whether or not to decode AVPs of type &dict_OctetString; and its
@@ -1031,8 +1029,9 @@ Option passed to &add_transport;.
Has one of the following types.</p>
<taglist>
-<marker id="applications"/>
-<tag><c>{applications, [&application_alias;]}</c></tag>
+
+<tag>
+<marker id="applications"/><c>{applications, [&application_alias;]}</c></tag>
<item>
<p>
Diameter applications to which the transport should be restricted.
@@ -1050,8 +1049,8 @@ implies having to set matching *-Application-Id AVPs in a
</item>
-<marker id="capabilities"/>
-<tag><c>{capabilities, [&capability;]}</c></tag>
+<tag>
+<marker id="capabilities"/><c>{capabilities, [&capability;]}</c></tag>
<item>
<p>
AVPs used to construct outgoing CER/CEA messages.
@@ -1064,8 +1063,8 @@ may be particularly appropriate for Inband-Security-Id, in case
TLS is desired over TCP as implemented by &man_tcp;.</p>
</item>
-<marker id="capabilities_cb"/>
-<tag><c>{capabilities_cb, &evaluable;}</c></tag>
+<tag>
+<marker id="capabilities_cb"/><c>{capabilities_cb, &evaluable;}</c></tag>
<item>
<p>
Callback invoked upon reception of CER/CEA during capabilities
@@ -1112,8 +1111,8 @@ case the corresponding callbacks are applied until either all return
<c>ok</c> or one does not.</p>
</item>
-<marker id="capx_timeout"/>
-<tag><c>{capx_timeout, &dict_Unsigned32;}</c></tag>
+<tag>
+<marker id="capx_timeout"/><c>{capx_timeout, &dict_Unsigned32;}</c></tag>
<item>
<p>
Number of milliseconds after which a transport process having an
@@ -1127,8 +1126,8 @@ For a listening transport, the peer determines the timing.</p>
Defaults to 10000.</p>
</item>
-<marker id="connect_timer"/>
-<tag><c>{connect_timer, Tc}</c></tag>
+<tag>
+<marker id="connect_timer"/><c>{connect_timer, Tc}</c></tag>
<item>
<pre>
Tc = &dict_Unsigned32;
@@ -1158,9 +1157,8 @@ Defaults to 30000 for a connecting transport and 60000 for a listening
transport.</p>
</item>
-<marker id="disconnect_cb"/>
-<tag><c>{disconnect_cb, &evaluable;}</c></tag>
-
+<tag>
+<marker id="disconnect_cb"/><c>{disconnect_cb, &evaluable;}</c></tag>
<item>
<p>
Callback invoked prior to terminating the transport process of a
@@ -1236,8 +1234,8 @@ configured them.</p>
Defaults to a single callback returning <c>dpr</c>.</p>
</item>
-<marker id="dpa_timeout"/>
-<tag><c>{dpa_timeout, &dict_Unsigned32;}</c></tag>
+<tag>
+<marker id="dpa_timeout"/><c>{dpa_timeout, &dict_Unsigned32;}</c></tag>
<item>
<p>
Number of milliseconds after which a transport connection is
@@ -1247,8 +1245,8 @@ terminated following an outgoing DPR if DPA is not received.</p>
Defaults to 1000.</p>
</item>
-<marker id="dpr_timeout"/>
-<tag><c>{dpr_timeout, &dict_Unsigned32;}</c></tag>
+<tag>
+<marker id="dpr_timeout"/><c>{dpr_timeout, &dict_Unsigned32;}</c></tag>
<item>
<p>
Number of milliseconds after which a transport connection is
@@ -1259,8 +1257,8 @@ connection.</p>
Defaults to 5000.</p>
</item>
-<marker id="length_errors"/>
-<tag><c>{length_errors, exit|handle|discard}</c></tag>
+<tag>
+<marker id="length_errors"/><c>{length_errors, exit|handle|discard}</c></tag>
<item>
<p>
How to deal with errors in the Message Length field of the
@@ -1308,8 +1306,8 @@ the same peer.</p>
</item>
-<marker id="spawn_opt"/>
-<tag><c>{spawn_opt, [term()]}</c></tag>
+<tag>
+<marker id="spawn_opt"/><c>{spawn_opt, [term()]}</c></tag>
<item>
<p>
Options passed to &spawn_opt; when spawning a process for an
@@ -1320,15 +1318,15 @@ Options <c>monitor</c> and <c>link</c> are ignored.</p>
Defaults to the list configured on the service if not specified.</p>
</item>
-<marker id="transport_config"/>
-<tag><c>{transport_config, term()}</c></tag>
+<tag>
+<marker id="transport_config"/><c>{transport_config, term()}</c></tag><item/>
<tag><c>{transport_config, term(), &dict_Unsigned32; | infinity}</c></tag>
<item>
<p>
Term passed as the third argument to the &transport_start; function of
the relevant &transport_module; in order to
start a transport process.
-Defaults to the empty list if unspecified.</p>
+Defaults to the empty list.</p>
<p>
The 3-tuple form additionally specifies an interval, in milliseconds,
@@ -1349,12 +1347,12 @@ request a connection with one peer over SCTP or another
To listen on both SCTP and TCP, define one transport for each.</p>
</item>
-<marker id="transport_module"/>
-<tag><c>{transport_module, atom()}</c></tag>
+<tag>
+<marker id="transport_module"/><c>{transport_module, atom()}</c></tag>
<item>
<p>
Module implementing a transport process as defined in &man_transport;.
-Defaults to <c>diameter_tcp</c> if unspecified.</p>
+Defaults to <c>diameter_tcp</c>.</p>
<p>
Multiple <c>transport_module</c> and &transport_config;
@@ -1369,8 +1367,9 @@ modules in order until one establishes a connection within the
corresponding timeout (see below) or all fail.</p>
</item>
-<marker id="watchdog_config"/>
-<tag><c>{watchdog_config, [{okay|suspect, non_neg_integer()}]}</c></tag>
+<tag>
+<marker id="watchdog_config"/><c>{watchdog_config,
+ [{okay|suspect, non_neg_integer()}]}</c></tag>
<item>
<p>
Configuration that alters the behaviour of the watchdog
@@ -1393,8 +1392,8 @@ misbehaving nodes during test.</p>
</warning>
</item>
-<marker id="watchdog_timer"/>
-<tag><c>{watchdog_timer, TwInit}</c></tag>
+<tag>
+<marker id="watchdog_timer"/><c>{watchdog_timer, TwInit}</c></tag>
<item>
<pre>
TwInit = &dict_Unsigned32;
@@ -1412,7 +1411,7 @@ the callback.</p>
<p>
An integer value must be at least 6000 as required by RFC 3539.
-Defaults to 30000 if unspecified.</p>
+Defaults to 30000.</p>
</item>
</taglist>
@@ -1422,10 +1421,10 @@ Unrecognized options are silently ignored but are returned unmodified
by &service_info; and can be referred to
in predicate functions passed to &remove_transport;.</p>
-<marker id="transport_ref"/>
</item>
-<tag><c>transport_ref() = reference()</c></tag>
+<tag>
+<marker id="transport_ref"/><c>transport_ref() = reference()</c></tag>
<item>
<p>
Reference returned by &add_transport; that
@@ -1682,17 +1681,17 @@ returned.</p>
<taglist>
-<tag><c>'Origin-Host'</c></tag>
-<tag><c>'Origin-Realm'</c></tag>
-<tag><c>'Vendor-Id'</c></tag>
-<tag><c>'Product-Name'</c></tag>
-<tag><c>'Origin-State-Id'</c></tag>
-<tag><c>'Host-IP-Address'</c></tag>
-<tag><c>'Supported-Vendor'</c></tag>
-<tag><c>'Auth-Application-Id'</c></tag>
-<tag><c>'Inband-Security-Id'</c></tag>
-<tag><c>'Acct-Application-Id'</c></tag>
-<tag><c>'Vendor-Specific-Application-Id'</c></tag>
+<tag><c>'Origin-Host'</c></tag><item/>
+<tag><c>'Origin-Realm'</c></tag><item/>
+<tag><c>'Vendor-Id'</c></tag><item/>
+<tag><c>'Product-Name'</c></tag><item/>
+<tag><c>'Origin-State-Id'</c></tag><item/>
+<tag><c>'Host-IP-Address'</c></tag><item/>
+<tag><c>'Supported-Vendor'</c></tag><item/>
+<tag><c>'Auth-Application-Id'</c></tag><item/>
+<tag><c>'Inband-Security-Id'</c></tag><item/>
+<tag><c>'Acct-Application-Id'</c></tag><item/>
+<tag><c>'Vendor-Specific-Application-Id'</c></tag><item/>
<tag><c>'Firmware-Revision'</c></tag>
<item>
<p>
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index 973b6eb732..dfcd00975b 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -90,17 +90,14 @@ is called in response to an incoming Diameter request message.</p>
</list>
-</description>
-
-<note>
<p>
-The arities given for the the callback functions here assume no extra
-arguments.
+The arities for the the callback functions here assume no extra arguments.
All functions will also be passed any extra arguments configured with
the callback module itself when calling &mod_start_service;
and, for the call-specific callbacks, any extra arguments passed to
&mod_call;.</p>
-</note>
+
+</description>
<!-- ===================================================================== -->
<!-- ===================================================================== -->
@@ -110,9 +107,8 @@ and, for the call-specific callbacks, any extra arguments passed to
<taglist>
-<marker id="capabilities"/>
-
-<tag><c>capabilities() = #diameter_caps{}</c></tag>
+<tag>
+<marker id="capabilities"/><c>capabilities() = #diameter_caps{}</c></tag>
<item>
<p>
A record containing the identities of
@@ -126,9 +122,8 @@ Optional or possibly multiple values are encoded as lists of values,
mandatory values as the bare value.</p>
</item>
-<marker id="message"/>
-
-<tag><c>message() = &codec_message;</c></tag>
+<tag>
+<marker id="message"/><c>message() = &codec_message;</c></tag>
<item>
<p>
The representation of a Diameter message as passed to
@@ -136,9 +131,8 @@ The representation of a Diameter message as passed to
</item>
-<marker id="packet"/>
-
-<tag><c>packet() = &codec_packet;</c></tag>
+<tag>
+<marker id="packet"/><c>packet() = &codec_packet;</c></tag>
<item>
<p>
A container for incoming and outgoing Diameter messages that's passed
@@ -146,25 +140,22 @@ through encode/decode and transport.
Fields should not be set in return values except as documented.</p>
</item>
-<marker id="peer_ref"/>
-
-<tag><c>peer_ref() = term()</c></tag>
+<tag>
+<marker id="peer_ref"/><c>peer_ref() = term()</c></tag>
<item>
<p>
A term identifying a transport connection with a Diameter peer.</p>
</item>
-<marker id="peer"/>
-
-<tag><c>peer() = {&peer_ref;, &capabilities;}</c></tag>
+<tag>
+<marker id="peer"/><c>peer() = {&peer_ref;, &capabilities;}</c></tag>
<item>
<p>
A tuple representing a Diameter peer connection.</p>
</item>
-<marker id="state"/>
-
-<tag><c>state() = term()</c></tag>
+<tag>
+<marker id="state"/><c>state() = term()</c></tag>
<item>
<p>
The state maintained by the application callback functions
diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml
index a0313e2877..91e96058dd 100644
--- a/lib/diameter/doc/src/diameter_codec.xml
+++ b/lib/diameter/doc/src/diameter_codec.xml
@@ -88,10 +88,9 @@ files resulting from dictionary file compilation.</p>
<taglist>
-<marker id="integers"/>
-
-<tag><c>uint8()&nbsp; = 0..255</c></tag>
-<tag><c>uint24() = 0..16777215</c></tag>
+<tag>
+<marker id="integers"/><c>uint8()&nbsp; = 0..255</c></tag><item/>
+<tag><c>uint24() = 0..16777215</c></tag><item/>
<tag><c>uint32() = 0..4294967295</c></tag>
<item>
<p>
@@ -99,9 +98,8 @@ files resulting from dictionary file compilation.</p>
headers.</p>
</item>
-<marker id="avp"/>
-
-<tag><c>avp() = #diameter_avp{}</c></tag>
+<tag>
+<marker id="avp"/><c>avp() = #diameter_avp{}</c></tag>
<item>
<p>
The application-neutral representation of an AVP.
@@ -116,9 +114,9 @@ Fields have the following types.</p>
<taglist>
-<tag><c>code = uint32()</c></tag>
-<tag><c>is_mandatory = boolean()</c></tag>
-<tag><c>need_encryption = boolean()</c></tag>
+<tag><c>code = uint32()</c></tag><item/>
+<tag><c>is_mandatory = boolean()</c></tag><item/>
+<tag><c>need_encryption = boolean()</c></tag><item/>
<tag><c>vendor_id = uint32() | undefined</c></tag>
<item>
<p>
@@ -167,9 +165,8 @@ Possible types are <c>undefined</c> and the Diameter types:
</item>
-<marker id="dictionary"/>
-
-<tag><c>dictionary() = module()</c></tag>
+<tag>
+<marker id="dictionary"/><c>dictionary() = module()</c></tag>
<item>
<p>
@@ -179,9 +176,8 @@ The interface provided by a dictionary module is an
implementation detail that may change.</p>
</item>
-<marker id="header"/>
-
-<tag><c>header() = #diameter_header{}</c></tag>
+<tag>
+<marker id="header"/><c>header() = #diameter_header{}</c></tag>
<item>
<p>
The record representation of the Diameter header.
@@ -204,11 +200,11 @@ Fields have the following types.</p>
<taglist>
-<tag><c>version = uint8()</c></tag>
-<tag><c>length = uint24()</c></tag>
-<tag><c>cmd_code = uint24()</c></tag>
-<tag><c>application_id = uint32()</c></tag>
-<tag><c>hop_by_hop_id = uint32()</c></tag>
+<tag><c>version = uint8()</c></tag><item/>
+<tag><c>length = uint24()</c></tag><item/>
+<tag><c>cmd_code = uint24()</c></tag><item/>
+<tag><c>application_id = uint32()</c></tag><item/>
+<tag><c>hop_by_hop_id = uint32()</c></tag><item/>
<tag><c>end_to_end_id = uint32()</c></tag>
<item>
<p>
@@ -217,9 +213,9 @@ Hop-by-Hop Identifier and End-to-End Identifier fields of the Diameter
header.</p>
</item>
-<tag><c>is_request = boolean()</c></tag>
-<tag><c>is_proxiable = boolean()</c></tag>
-<tag><c>is_error = boolean()</c></tag>
+<tag><c>is_request = boolean()</c></tag><item/>
+<tag><c>is_proxiable = boolean()</c></tag><item/>
+<tag><c>is_error = boolean()</c></tag><item/>
<tag><c>is_retransmitted = boolean()</c></tag>
<item>
<p>
@@ -232,9 +228,8 @@ header.</p>
</item>
-<marker id="message"/>
-
-<tag><c>message() = record() | list()</c></tag>
+<tag>
+<marker id="message"/><c>message() = record() | list()</c></tag>
<item>
<p>
The representation of a Diameter message as passed to
@@ -257,9 +252,8 @@ question: messages are sent exactly as specified.</p>
</item>
-<marker id="packet"/>
-
-<tag><c>packet() = #diameter_packet{}</c></tag>
+<tag>
+<marker id="packet"/><c>packet() = #diameter_packet{}</c></tag>
<item>
<p>
A container for incoming and outgoing Diameter messages.
@@ -296,7 +290,7 @@ corresponding values.</p>
<warning>
<p>
-A record-valued <c>msg</c> field does <b>not</b> imply an absence of
+A record-valued <c>msg</c> field does <em>not</em> imply an absence of
decode errors.
The <c>errors</c> field should also be examined.</p>
</warning>
diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml
index ae40f99aee..9584d682c2 100644
--- a/lib/diameter/doc/src/diameter_dict.xml
+++ b/lib/diameter/doc/src/diameter_dict.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE erlref SYSTEM "fileref.dtd" [
+<!DOCTYPE fileref SYSTEM "fileref.dtd" [
<!ENTITY format
'<seealso marker="#FILE_FORMAT">FILE FORMAT</seealso>'>
<!ENTITY records
@@ -121,9 +121,8 @@ The order in which sections are specified is unimportant.</p>
<taglist>
-<marker id="id"/>
-
-<tag><c>@id Number</c></tag>
+<tag>
+<marker id="id"/><c>@id Number</c></tag>
<item>
<p>
Defines the integer Number as the Diameter Application Id of the
@@ -146,14 +145,13 @@ Example:</p>
</item>
-<marker id="name"/>
-
-<tag><c>@name Mod</c></tag>
+<tag>
+<marker id="name"/><c>@name Mod</c></tag>
<item>
<p>
Defines the name of the generated dictionary module.
Can occur at most once and defaults to the name of the dictionary file
-minus any extension if unspecified.
+minus any extension.
The section has empty content.</p>
<p>
@@ -169,9 +167,8 @@ Example:</p>
</item>
-<marker id="prefix"/>
-
-<tag><c>@prefix Name</c></tag>
+<tag>
+<marker id="prefix"/><c>@prefix Name</c></tag>
<item>
<p>
Defines Name as the prefix to be added to record and constant names
@@ -194,9 +191,8 @@ Example:</p>
</item>
-<marker id="vendor"/>
-
-<tag><c>@vendor Number Name</c></tag>
+<tag>
+<marker id="vendor"/><c>@vendor Number Name</c></tag>
<item>
<p>
Defines the integer Number as the the default Vendor-Id of AVPs for
@@ -216,9 +212,8 @@ Example:</p>
</item>
-<marker id="avp_vendor_id"/>
-
-<tag><c>@avp_vendor_id Number</c></tag>
+<tag>
+<marker id="avp_vendor_id"/><c>@avp_vendor_id Number</c></tag>
<item>
<p>
Defines the integer Number as the Vendor-Id of the AVPs listed in the
@@ -238,9 +233,8 @@ Region-Set
</item>
-<marker id="inherits"/>
-
-<tag><c>@inherits Mod</c></tag>
+<tag>
+<marker id="inherits"/><c>@inherits Mod</c></tag>
<item>
<p>
Defines the name of a dictionary module containing AVP
@@ -274,9 +268,8 @@ Example:</p>
</pre>
</item>
-<marker id="avp_types"/>
-
-<tag><c>@avp_types</c></tag>
+<tag>
+<marker id="avp_types"/><c>@avp_types</c></tag>
<item>
<p>
Defines the name, code, type and flags of individual AVPs.
@@ -308,9 +301,8 @@ The P flag has been deprecated by &the_rfc;.</p>
</item>
-<marker id="custom_types"/>
-
-<tag><c>@custom_types Mod</c></tag>
+<tag>
+<marker id="custom_types"/><c>@custom_types Mod</c></tag>
<item>
<p>
Specifies AVPs for which module Mod provides encode/decode functions.
@@ -331,9 +323,8 @@ Framed-IP-Address
</pre>
</item>
-<marker id="codecs"/>
-
-<tag><c>@codecs Mod</c></tag>
+<tag>
+<marker id="codecs"/><c>@codecs Mod</c></tag>
<item>
<p>
Like <c>@custom_types</c> but requires the specified module to export
@@ -350,9 +341,8 @@ Framed-IP-Address
</pre>
</item>
-<marker id="messages"/>
-
-<tag><c>@messages</c></tag>
+<tag>
+<marker id="messages"/><c>@messages</c></tag>
<item>
<p>
Defines the messages of the application.
@@ -397,9 +387,8 @@ RTA ::= &lt; Diameter Header: 287, PXY >
</item>
-<marker id="grouped"/>
-
-<tag><c>@grouped</c></tag>
+<tag>
+<marker id="grouped"/><c>@grouped</c></tag>
<item>
<p>
Defines the contents of the AVPs of the application having type
@@ -424,9 +413,8 @@ Specifying a Vendor-Id in the definition of a grouped AVP is
equivalent to specifying it with <c>@avp_vendor_id</c>.</p>
</item>
-<marker id="enum"/>
-
-<tag><c>@enum Name</c></tag>
+<tag>
+<marker id="enum"/><c>@enum Name</c></tag>
<item>
<p>
Defines values of AVP Name having type Enumerated.
@@ -452,9 +440,8 @@ REMOVE_SIP_SERVER 3
</pre>
</item>
-<marker id="end"/>
-
-<tag><c>@end</c></tag>
+<tag>
+<marker id="end"/><c>@end</c></tag>
<item>
<p>
Causes parsing of the dictionary to terminate:
diff --git a/lib/diameter/doc/src/diameter_examples.xml b/lib/diameter/doc/src/diameter_examples.xml
index 853ef96bb3..2e1f2b3c03 100644
--- a/lib/diameter/doc/src/diameter_examples.xml
+++ b/lib/diameter/doc/src/diameter_examples.xml
@@ -42,4 +42,3 @@ Example code can be found in the diameter application's
<c>examples</c> subdirectory.</p>
</chapter>
-
diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml
index 00ccc39c15..6ca280c52b 100644
--- a/lib/diameter/doc/src/diameter_tcp.xml
+++ b/lib/diameter/doc/src/diameter_tcp.xml
@@ -150,7 +150,7 @@ Options <c>binary</c>,
<c>packet</c> and <c>active</c> cannot be specified.
Also, option <c>port</c> can be specified for a listening transport
to specify the local listening port, the default being the standardized
-3868 if unspecified.
+3868.
Note that the option <c>ip</c> specifies the local address.</p>
<p>
diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml
index 736d4cbfbd..294e8a8864 100644
--- a/lib/diameter/doc/src/diameter_transport.xml
+++ b/lib/diameter/doc/src/diameter_transport.xml
@@ -65,9 +65,8 @@ parent).</p>
<taglist>
-<marker id="message"/>
-
-<tag><c>message() = binary() | &codec_packet;</c></tag>
+<tag>
+<marker id="message"/><c>message() = binary() | &codec_packet;</c></tag>
<item>
<p>
A Diameter message as passed over the transport interface.</p>
@@ -160,9 +159,9 @@ It should exit if its transport connection with its peer is lost.</p>
</funcs>
<!-- ===================================================================== -->
-<marker id="MESSAGES"/>
<section>
+<marker id="MESSAGES"/>
<title>MESSAGES</title>
<p>
@@ -234,7 +233,7 @@ established a connection with the peer.
Not sent if the transport process has <c>Type=connect</c>.</p>
</item>
-<tag><c>{diameter, {self(), connected, Remote}}</c></tag>
+<tag><c>{diameter, {self(), connected, Remote}}</c></tag><item/>
<tag><c>{diameter, {self(), connected, Remote, [LocalAddr]}}</c></tag>
<item>
<p>
diff --git a/lib/diameter/doc/src/diameterc.xml b/lib/diameter/doc/src/diameterc.xml
index 5bffe9a771..8f1c660989 100644
--- a/lib/diameter/doc/src/diameterc.xml
+++ b/lib/diameter/doc/src/diameterc.xml
@@ -11,7 +11,7 @@
<comref>
<header>
<copyright>
-<year>2011</year><year>2013</year>
+<year>2011</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -79,14 +79,14 @@ Write generated source to the specified directory.
Defaults to the current working directory.</p>
</item>
-<tag><![CDATA[-E]]></tag>
+<tag><![CDATA[-E]]></tag><item/>
<tag><![CDATA[-H]]></tag>
<item>
<p>
Suppress erl and hrl generation, respectively.</p>
</item>
-<tag><![CDATA[--name <name>]]></tag>
+<tag><![CDATA[--name <name>]]></tag><item/>
<tag><![CDATA[--prefix <prefix>]]></tag>
<item>
<p>
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index b1be7bdcf7..70e1880be5 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -43,6 +43,55 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 1.12.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ An improvement in the handling of peer failover in
+ diameter 1.12.1 adversely affected performance when
+ sending requests. Further, the inefficient use of a
+ public table to route incoming answers has been removed.</p>
+ <p>
+ Own Id: OTP-14206</p>
+ </item>
+ <item>
+ <p>
+ Fixed xml issues in old release notes</p>
+ <p>
+ Own Id: OTP-14269</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>diameter 1.12.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Close diameter_tcp/sctp listening sockets at
+ diameter:stop_service/1.</p>
+ <p>
+ Broken by OTP-13611.</p>
+ <p>
+ Own Id: OTP-13787 Aux Id: OTP-13611 </p>
+ </item>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 1.12</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -230,8 +279,8 @@ first.</p>
Fix decode of Grouped AVPs containing errors.</p>
<p>
RFC 6733 says this of Failed-AVP in 7.5:</p>
- <p>
- <taglist><item><p><c> In the case where the offending AVP
+
+ <taglist><tag></tag><item><p><c> In the case where the offending AVP
is embedded within a Grouped AVP, the Failed-AVP MAY
contain the grouped AVP, which in turn contains the
single offending AVP. The same method MAY be employed if
@@ -240,11 +289,11 @@ first.</p>
the grouped AVP hierarchy up to the single offending AVP.
This enables the recipient to detect the location of the
offending AVP when embedded in a
- group.</c></p></item></taglist></p>
+ group.</c></p></item></taglist>
<p>
It says this of DIAMETER_INVALID_AVP_LENGTH in 7.1.5:</p>
- <p>
- <taglist><item><p><c> The request contained an AVP with
+
+ <taglist><tag></tag><item><p><c> The request contained an AVP with
an invalid length. A Diameter message indicating this
error MUST include the offending AVPs within a Failed-AVP
AVP. In cases where the erroneous AVP length value
@@ -259,7 +308,8 @@ first.</p>
the minimum AVP header length, it is sufficient to
include an offending AVP header that is formulated by
padding the incomplete AVP header with zero up to the
- minimum AVP header length.</c></p></item></taglist></p>
+ minimum AVP header length.</c></p></item></taglist>
+
<p>
The AVPs placed in the errors field of a diameter_packet
record are intended to be appropriate for inclusion in a
@@ -924,8 +974,8 @@ first.</p>
Be lenient with the M-bit in Grouped AVPs.</p>
<p>
RFC 6733 says this, in 4.4:</p>
- <p>
- <taglist><item><p><c>Receivers of a Grouped AVP that does
+
+ <taglist><tag></tag><item><p><c>Receivers of a Grouped AVP that does
not have the 'M' (mandatory) bit set and one or more of
the encapsulated AVPs within the group has the 'M'
(mandatory) bit set MAY simply be ignored if the Grouped
@@ -933,14 +983,14 @@ first.</p>
encapsulated AVP with its 'M' (mandatory) bit set is
further encapsulated within other sub-groups, i.e., other
Grouped AVPs embedded within the Grouped
- AVP.</c></p></item></taglist></p>
+ AVP.</c></p></item></taglist>
<p>
The first sentence is mangled but take it to mean this:</p>
- <p>
- <taglist><item><p><c>An unrecognized AVP of type Grouped
+
+ <taglist><tag></tag><item><p><c>An unrecognized AVP of type Grouped
that does not set the 'M' bit MAY be ignored even if one
of its encapsulated AVPs sets the 'M'
- bit.</c></p></item></taglist></p>
+ bit.</c></p></item></taglist>
<p>
This is a bit of a non-statement since if the AVP is
unrecognized then its type is unknown. We therefore don't
diff --git a/lib/diameter/examples/code/relay.erl b/lib/diameter/examples/code/relay.erl
index 3846b1d161..806f79915b 100644
--- a/lib/diameter/examples/code/relay.erl
+++ b/lib/diameter/examples/code/relay.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.
@@ -53,7 +53,7 @@
{'Auth-Application-Id', [16#FFFFFFFF]},
{string_decode, false},
{application, [{alias, relay},
- {dictionary, diameter_relay},
+ {dictionary, diameter_gen_relay},
{module, relay_cb}]}]).
%% start/1
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 7f61620fc1..6bf748a727 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -123,7 +123,7 @@ ERL_COMPILE_FLAGS += \
# erl/hrl from dictionary file.
gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
$(dia_verbose) \
- ../bin/diameterc -o gen -i $(EBIN) $<
+ escript ../bin/diameterc -o gen -i $(EBIN) $<
opt: $(TARGET_FILES)
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index e8f2f63f86..253f64133c 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -377,6 +377,7 @@ call(SvcName, App, Message) ->
| {capabilities, [capability()]}
| {capabilities_cb, evaluable()}
| {capx_timeout, 'Unsigned32'()}
+ | {capx_strictness, boolean()}
| {disconnect_cb, evaluable()}
| {dpr_timeout, 'Unsigned32'()}
| {dpa_timeout, 'Unsigned32'()}
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index fdbbd412a1..245a3ea7ac 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -580,6 +580,9 @@ opt({K, Tmo})
K == dpa_timeout ->
?IS_UINT32(Tmo);
+opt({capx_strictness, B}) ->
+ is_boolean(B);
+
opt({length_errors, T}) ->
lists:member(T, [exit, handle, discard]);
diff --git a/lib/diameter/src/base/diameter_lib.erl b/lib/diameter/src/base/diameter_lib.erl
index b835e87967..3928769b5e 100644
--- a/lib/diameter/src/base/diameter_lib.erl
+++ b/lib/diameter/src/base/diameter_lib.erl
@@ -299,8 +299,28 @@ spawn_opts(server, Opts) ->
spawn_opts(worker, Opts) ->
opts(5000, Opts).
-opts(HeapSize, Opts) ->
- [{min_heap_size, HeapSize} | lists:keydelete(min_heap_size, 1, Opts)].
+%% These setting are historical rather than useful. In particular, the
+%% server setting can bloat many processes unnecessarily. Let them be
+%% disabled with -diameter min_heap_size false.
+
+opts(Def, Opts) ->
+ Key = min_heap_size,
+ case getenv(Key, Def) of
+ N when is_integer(N), 0 =< N ->
+ [{Key, N} | lists:keydelete(Key, 1, Opts)];
+ _ ->
+ Opts
+ end.
+
+%% getenv/1
+
+getenv(Key, Def) ->
+ case application:get_env(Key) of
+ {ok, T} ->
+ T;
+ undefined ->
+ Def
+ end.
%% ---------------------------------------------------------------------------
%% # wait/1
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index 996e75a8d3..7ee1e5fe59 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-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -128,6 +128,7 @@
%% outgoing DPR; boolean says whether or not
%% the request was sent explicitly with
%% diameter:call/4.
+ strict :: boolean(),
length_errors :: exit | handle | discard,
incoming_maxlen :: integer() | infinity}).
@@ -233,6 +234,7 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) ->
proplists:get_value(dpa_timeout, Opts, ?DPA_TIMEOUT)}),
Tmo = proplists:get_value(capx_timeout, Opts, ?CAPX_TIMEOUT),
+ Strictness = proplists:get_value(capx_strictness, Opts, true),
OnLengthErr = proplists:get_value(length_errors, Opts, exit),
{TPid, Addrs} = start_transport(T, Rest, Svc),
@@ -246,6 +248,7 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) ->
mode = M,
service = svc(Svc, Addrs),
length_errors = OnLengthErr,
+ strict = Strictness,
incoming_maxlen = Maxlen}.
%% The transport returns its local ip addresses so that different
%% transports on the same service can use different local addresses.
@@ -454,6 +457,9 @@ transition({timeout, _}, _) ->
%% Outgoing message.
transition({send, Msg}, S) ->
outgoing(Msg, S);
+transition({send, Msg, Route}, S) ->
+ put_route(Route),
+ outgoing(Msg, S);
%% Request for graceful shutdown at remove_transport, stop_service of
%% application shutdown.
@@ -483,8 +489,10 @@ transition({'DOWN', _, process, TPid, _},
= S) ->
start_next(S);
-%% Transport has died after connection timeout.
-transition({'DOWN', _, process, _, _}, _) ->
+%% Transport has died after connection timeout, or handler process has
+%% died.
+transition({'DOWN', _, process, Pid, _}, _) ->
+ erase_route(Pid),
ok;
%% State query.
@@ -494,6 +502,40 @@ transition({state, Pid}, #state{state = S, transport = TPid}) ->
%% Crash on anything unexpected.
+%% put_route/1
+%%
+%% Map identifiers in an outgoing request to be able to lookup the
+%% handler process when the answer is received.
+
+put_route({Pid, Ref, Seqs}) ->
+ MRef = monitor(process, Pid),
+ put(Pid, Seqs),
+ put(Seqs, {Pid, Ref, MRef}).
+
+%% get_route/1
+
+get_route(#diameter_packet{header = #diameter_header{is_request = false}}
+ = Pkt) ->
+ Seqs = diameter_codec:sequence_numbers(Pkt),
+ case erase(Seqs) of
+ {Pid, Ref, MRef} ->
+ demonitor(MRef),
+ erase(Pid),
+ {Pid, Ref, self()};
+ undefined ->
+ false
+ end;
+
+get_route(_) ->
+ false.
+
+%% erase_route/1
+
+erase_route(Pid) ->
+ erase(erase(Pid)).
+
+%% capx/1
+
capx(recv_CER) ->
'CER';
capx({'Wait-CEA', _, _}) ->
@@ -576,8 +618,7 @@ incoming({Msg, NPid}, S) ->
T
catch
{?MODULE, Name, Pkt} ->
- S#state.parent ! {recv, self(), Name, {Pkt, NPid}},
- rcv(Name, Pkt, S)
+ incoming(Name, Pkt, NPid, S)
end;
incoming(Msg, S) ->
@@ -585,10 +626,15 @@ incoming(Msg, S) ->
recv(Msg, S)
catch
{?MODULE, Name, Pkt} ->
- S#state.parent ! {recv, self(), Name, Pkt},
- rcv(Name, Pkt, S)
+ incoming(Name, Pkt, false, S)
end.
+%% incoming/4
+
+incoming(Name, Pkt, NPid, #state{parent = Pid} = S) ->
+ Pid ! {recv, self(), get_route(Pkt), Name, Pkt, NPid},
+ rcv(Name, Pkt, S).
+
%% recv/2
recv(#diameter_packet{header = #diameter_header{} = Hdr}
@@ -614,6 +660,17 @@ recv1(_,
when M < size(Bin) ->
invalid(false, incoming_maxlen_exceeded, {size(Bin), H});
+%% Ignore anything but an expected CER/CEA if so configured. This is
+%% non-standard behaviour.
+recv1(Name, _, #state{state = {'Wait-CEA', _, _},
+ strict = false})
+ when Name /= 'CEA' ->
+ ok;
+recv1(Name, _, #state{state = recv_CER,
+ strict = false})
+ when Name /= 'CER' ->
+ ok;
+
%% Incoming request after outgoing DPR: discard. Don't discard DPR, so
%% both ends don't do so when sending simultaneously.
recv1(Name,
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index ccf68f4d93..e4f77e3a24 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -1858,13 +1858,6 @@ eq(Any, Id, PeerId) ->
%% OctetString() can be specified as an iolist() so test for string
%% rather then term equality.
-%% transports/1
-
-transports(#state{watchdogT = WatchdogT}) ->
- ets:select(WatchdogT, [{#watchdog{peer = '$1', _ = '_'},
- [{'is_pid', '$1'}],
- ['$1']}]).
-
%% ---------------------------------------------------------------------------
%% # service_info/2
%% ---------------------------------------------------------------------------
@@ -1887,7 +1880,6 @@ transports(#state{watchdogT = WatchdogT}) ->
-define(ALL_INFO, [capabilities,
applications,
transport,
- pending,
options]).
%% The rest.
@@ -1981,7 +1973,6 @@ complete_info(Item, #state{service = Svc} = S) ->
applications -> info_apps(S);
transport -> info_transport(S);
options -> info_options(S);
- pending -> info_pending(S);
keys -> ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO;
all -> service_info(?ALL_INFO, S);
statistics -> info_stats(S);
@@ -2189,13 +2180,6 @@ info_apps(#state{service = #diameter_service{applications = Apps}}) ->
mk_app(#diameter_app{} = A) ->
lists:zip(record_info(fields, diameter_app), tl(tuple_to_list(A))).
-%% info_pending/1
-%%
-%% One entry for each outgoing request whose answer is outstanding.
-
-info_pending(#state{} = S) ->
- diameter_traffic:pending(transports(S)).
-
%% info_info/1
%%
%% Extract process_info from connections info.
diff --git a/lib/diameter/src/base/diameter_sup.erl b/lib/diameter/src/base/diameter_sup.erl
index 482289cb9a..01c51f0856 100644
--- a/lib/diameter/src/base/diameter_sup.erl
+++ b/lib/diameter/src/base/diameter_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -42,7 +42,7 @@
-define(TABLES, [{diameter_sequence, [set]},
{diameter_service, [set, {keypos, 3}]},
- {diameter_request, [bag]},
+ {diameter_request, [set]},
{diameter_config, [bag, {keypos, 2}]}]).
%% start_link/0
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 2112941d5e..bc1ccf4feb 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. 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.
@@ -30,7 +30,7 @@
-export([send_request/4]).
%% towards diameter_watchdog
--export([receive_message/4]).
+-export([receive_message/6]).
%% towards diameter_peer_fsm and diameter_watchdog
-export([incr/4,
@@ -40,11 +40,11 @@
%% towards diameter_service
-export([make_recvdata/1,
peer_up/1,
- peer_down/1,
- pending/1]).
+ peer_down/1]).
-%% towards ?MODULE
--export([send/1]). %% send from remote node
+%% internal
+-export([send/1, %% send from remote node
+ init/1]). %% monitor process start
-include_lib("diameter/include/diameter.hrl").
-include("diameter_internal.hrl").
@@ -57,14 +57,12 @@
-define(DEFAULT_TIMEOUT, 5000). %% for outgoing requests
-define(DEFAULT_SPAWN_OPTS, []).
-%% Table containing outgoing requests for which a reply has yet to be
-%% received.
+%% Table containing outgoing entries that live and die with
+%% peer_up/down. The name is historic, since the table used to contain
+%% information about outgoing requests for which an answer has yet to
+%% be received.
-define(REQUEST_TABLE, diameter_request).
-%% Workaround for dialyzer's lack of understanding of match specs.
--type match(T)
- :: T | '_' | '$1' | '$2' | '$3' | '$4'.
-
%% Record diameter:call/4 options are parsed into.
-record(options,
{filter = none :: diameter:peer_filter(),
@@ -72,7 +70,7 @@
timeout = ?DEFAULT_TIMEOUT :: 0..16#FFFFFFFF,
detach = false :: boolean()}).
-%% Term passed back to receive_message/4 with every incoming message.
+%% Term passed back to receive_message/6 with every incoming message.
-record(recvdata,
{peerT :: ets:tid(),
service_name :: diameter:service_name(),
@@ -87,12 +85,12 @@
%% Record stored in diameter_request for each outgoing request.
-record(request,
- {ref :: match(reference()), %% used to receive answer
- caller :: match(pid()), %% calling process
- handler :: match(pid()), %% request process
- transport :: match(pid()), %% peer process
- caps :: match(#diameter_caps{}), %% of connection
- packet :: match(#diameter_packet{})}). %% of request
+ {ref :: reference(), %% used to receive answer
+ caller :: pid() | undefined, %% calling process
+ handler :: pid(), %% request process
+ transport :: pid() | undefined, %% peer process
+ caps :: #diameter_caps{} | undefined, %% of connection
+ packet :: #diameter_packet{} | undefined}). %% of request
%% ---------------------------------------------------------------------------
%% # make_recvdata/1
@@ -113,18 +111,27 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) ->
%% peer_up/1
%% ---------------------------------------------------------------------------
-%% Insert an element that is used to detect whether or not there has
-%% been a failover when inserting an outgoing request.
+%% Start a process that dies with peer_down/1, on which request
+%% processes can monitor. There is no other process that dies with
+%% peer_down since failover doesn't imply the loss of transport in the
+%% case of a watchdog transition into state SUSPECT.
peer_up(TPid) ->
- ets:insert(?REQUEST_TABLE, {TPid}).
+ proc_lib:start(?MODULE, init, [TPid]).
+
+init(TPid) ->
+ ets:insert(?REQUEST_TABLE, {TPid, self()}),
+ proc_lib:init_ack(self()),
+ proc_lib:hibernate(erlang, exit, [{shutdown, TPid}]).
%% ---------------------------------------------------------------------------
%% peer_down/1
%% ---------------------------------------------------------------------------
peer_down(TPid) ->
+ [{_, Pid}] = ets:lookup(?REQUEST_TABLE, TPid),
ets:delete(?REQUEST_TABLE, TPid),
- failover(TPid).
+ Pid ! ok, %% make it die
+ Pid.
%% ---------------------------------------------------------------------------
%% incr/4
@@ -199,54 +206,25 @@ incr_rc(Dir, Pkt, TPid, Dict0) ->
incr_rc(Dir, Pkt, TPid, {Dict0, Dict0, Dict0}).
%% ---------------------------------------------------------------------------
-%% pending/1
-%% ---------------------------------------------------------------------------
-
-pending(TPids) ->
- MatchSpec = [{{'$1',
- #request{caller = '$2',
- handler = '$3',
- transport = '$4',
- _ = '_'},
- '_'},
- [?ORCOND([{'==', T, '$4'} || T <- TPids])],
- [{{'$1', [{{caller, '$2'}},
- {{handler, '$3'}},
- {{transport, '$4'}}]}}]}],
-
- try
- ets:select(?REQUEST_TABLE, MatchSpec)
- catch
- error: badarg -> [] %% service has gone down
- end.
-
-%% ---------------------------------------------------------------------------
-%% # receive_message/4
+%% # receive_message/6
%%
%% Handle an incoming Diameter message.
%% ---------------------------------------------------------------------------
-%% Handle an incoming Diameter message in the watchdog process. This
-%% used to come through the service process but this avoids that
-%% becoming a bottleneck.
+%% Handle an incoming Diameter message in the watchdog process.
-receive_message(TPid, {Pkt, NPid}, Dict0, RecvData) ->
- NPid ! {diameter, incoming(TPid, Pkt, Dict0, RecvData)};
+receive_message(TPid, Route, Pkt, false, Dict0, RecvData) ->
+ incoming(TPid, Route, Pkt, Dict0, RecvData);
-receive_message(TPid, Pkt, Dict0, RecvData) ->
- incoming(TPid, Pkt, Dict0, RecvData).
+receive_message(TPid, Route, Pkt, NPid, Dict0, RecvData) ->
+ NPid ! {diameter, incoming(TPid, Route, Pkt, Dict0, RecvData)}.
%% incoming/4
-incoming(TPid, Pkt, Dict0, RecvData)
+incoming(TPid, Route, Pkt, Dict0, RecvData)
when is_pid(TPid) ->
#diameter_packet{header = #diameter_header{is_request = R}} = Pkt,
- recv(R,
- (not R) andalso lookup_request(Pkt, TPid),
- TPid,
- Pkt,
- Dict0,
- RecvData).
+ recv(R, Route, TPid, Pkt, Dict0, RecvData).
%% recv/6
@@ -261,8 +239,8 @@ recv(true, false, TPid, Pkt, Dict0, T) ->
end;
%% ... answer to known request ...
-recv(false, #request{ref = Ref, handler = Pid} = Req, _, Pkt, Dict0, _) ->
- Pid ! {answer, Ref, Req, Dict0, Pkt},
+recv(false, {Pid, Ref, TPid}, _, Pkt, Dict0, _) ->
+ Pid ! {answer, Ref, TPid, Dict0, Pkt},
{answer, Pid};
%% Note that failover could have happened prior to this message being
@@ -911,7 +889,7 @@ failed(Rec, FailedAvp, Dict) ->
{'Failed-AVP', [FailedAvp]}
catch
error: _ ->
- Avps = Dict:'get-'('AVP', Rec),
+ Avps = Dict:'#get-'('AVP', Rec),
A = #diameter_avp{name = 'Failed-AVP',
value = FailedAvp},
{'AVP', [A|Avps]}
@@ -1452,7 +1430,7 @@ make_request_packet(#diameter_packet{header = Hdr} = Pkt,
make_request_packet(Msg, Pkt) ->
Pkt#diameter_packet{msg = Msg}.
-%% make_retransmit_packet/2
+%% make_retransmit_packet/1
make_retransmit_packet(#diameter_packet{msg = [#diameter_header{} = Hdr
| Avps]}
@@ -1495,32 +1473,39 @@ send_R(Pkt0,
packet = Pkt0},
incr(send, Pkt, TPid, AppDict),
- TRef = send_request(TPid, Pkt, Req, SvcName, Timeout),
+ {TRef, MRef} = zend_requezt(TPid, Pkt, Req, SvcName, Timeout),
Pid ! Ref, %% tell caller a send has been attempted
handle_answer(SvcName,
App,
- recv_A(Timeout, SvcName, App, Opts, {TRef, Req})).
+ recv_A(Timeout, SvcName, App, Opts, {TRef, MRef, Req})).
%% recv_A/5
-recv_A(Timeout, SvcName, App, Opts, {TRef, #request{ref = Ref} = Req}) ->
+recv_A(Timeout, SvcName, App, Opts, {TRef, MRef, #request{ref = Ref} = Req}) ->
%% Matching on TRef below ensures we ignore messages that pertain
%% to a previous transport prior to failover. The answer message
- %% includes the #request{} since it's not necessarily Req; that
- %% is, from the last peer to which we've transmitted.
+ %% includes the pid of the transport on which it was received,
+ %% which may not be the last peer to which we've transmitted.
receive
- {answer = A, Ref, Rq, Dict0, Pkt} -> %% Answer from peer
- {A, Rq, Dict0, Pkt};
+ {answer = A, Ref, TPid, Dict0, Pkt} -> %% Answer from peer
+ {A, #request{} = erase(TPid), Dict0, Pkt};
{timeout = Reason, TRef, _} -> %% No timely reply
{error, Req, Reason};
- {failover, TRef} -> %% Service says peer has gone down
- retransmit(pick_peer(SvcName, App, Req, Opts),
- Req,
- Opts,
- SvcName,
- Timeout)
+ {'DOWN', MRef, process, _, _} when false /= MRef -> %% local peer_down
+ failover(SvcName, App, Req, Opts, Timeout);
+ {failover, TRef} -> %% local or remote peer_down
+ failover(SvcName, App, Req, Opts, Timeout)
end.
+%% failover/5
+
+failover(SvcName, App, Req, Opts, Timeout) ->
+ retransmit(pick_peer(SvcName, App, Req, Opts),
+ Req,
+ Opts,
+ SvcName,
+ Timeout).
+
%% handle_answer/3
handle_answer(SvcName, App, {error, Req, Reason}) ->
@@ -1697,47 +1682,63 @@ encode(DictT, TPid, #diameter_packet{bin = undefined} = Pkt) ->
encode(_, _, #diameter_packet{} = Pkt) ->
Pkt.
+%% zend_requezt/5
+%%
+%% Strip potentially large record fields that aren't used by the
+%% processes the records can be send to, possibly on a remote node.
+
+zend_requezt(TPid, Pkt, Req, SvcName, Timeout) ->
+ put(TPid, Req),
+ send_request(TPid, z(Pkt), Req, SvcName, Timeout).
+
%% send_request/5
send_request(TPid, #diameter_packet{bin = Bin} = Pkt, Req, _SvcName, Timeout)
when node() == node(TPid) ->
Seqs = diameter_codec:sequence_numbers(Bin),
TRef = erlang:start_timer(Timeout, self(), TPid),
- Entry = {Seqs, Req, TRef},
-
- %% Ensure that request table is cleaned even if we receive an exit
- %% signal. An alternative would be to simply trap exits, but
- %% callbacks are applied in this process, and these could possibly
- %% be expecting the prevailing behaviour.
- Self = self(),
- spawn(fun() -> diameter_lib:wait([Self]), erase_request(Entry) end),
-
- store_request(Entry, TPid),
- send(TPid, Pkt),
- TRef;
+ send(TPid, Pkt, _Route = {self(), Req#request.ref, Seqs}),
+ {TRef, _MRef = peer_monitor(TPid, TRef)};
%% Send using a remote transport: spawn a process on the remote node
%% to relay the answer.
send_request(TPid, #diameter_packet{} = Pkt, Req, SvcName, Timeout) ->
TRef = erlang:start_timer(Timeout, self(), TPid),
- T = {TPid, Pkt, Req, SvcName, Timeout, TRef},
+ T = {TPid, Pkt, z(Req), SvcName, Timeout, TRef},
spawn(node(TPid), ?MODULE, send, [T]),
- TRef.
+ {TRef, false}.
+
+%% z/1
+%%
+%% Avoid sending potentially large terms unnecessarily. The records
+%% themselves are retained since they're sent between nodes in send/1
+%% and changing what's sent causes upgrade issues.
+
+z(#request{ref = Ref, handler = Pid}) ->
+ #request{ref = Ref,
+ handler = Pid};
+
+z(#diameter_packet{header = H, bin = Bin, transport_data = T}) ->
+ #diameter_packet{header = H,
+ bin = Bin,
+ transport_data = T}.
%% send/1
send({TPid, Pkt, #request{handler = Pid} = Req0, SvcName, Timeout, TRef}) ->
Req = Req0#request{handler = self()},
- recv(TPid, Pid, TRef, send_request(TPid, Pkt, Req, SvcName, Timeout)).
+ recv(TPid, Pid, TRef, zend_requezt(TPid, Pkt, Req, SvcName, Timeout)).
%% recv/4
%%
%% Relay an answer from a remote node.
-recv(TPid, Pid, TRef, LocalTRef) ->
+recv(TPid, Pid, TRef, {LocalTRef, MRef}) ->
receive
{answer, _, _, _, _} = A ->
Pid ! A;
+ {'DOWN', MRef, process, _, _} ->
+ Pid ! {failover, TRef};
{failover = T, LocalTRef} ->
Pid ! {T, TRef};
T ->
@@ -1746,14 +1747,13 @@ recv(TPid, Pid, TRef, LocalTRef) ->
%% send/2
-send(Pid, Pkt) -> %% Strip potentially large message terms.
- #diameter_packet{header = H,
- bin = Bin,
- transport_data = T}
- = Pkt,
- Pid ! {send, #diameter_packet{header = H,
- bin = Bin,
- transport_data = T}}.
+send(Pid, Pkt) ->
+ Pid ! {send, Pkt}.
+
+%% send/3
+
+send(Pid, Pkt, Route) ->
+ Pid ! {send, Pkt, Route}.
%% retransmit/4
@@ -1763,8 +1763,8 @@ retransmit({TPid, Caps, App}
= Req,
SvcName,
Timeout) ->
- have_request(Pkt0, TPid) %% Don't failover to a peer we've
- andalso ?THROW(timeout), %% already sent to.
+ undefined == get(TPid) %% Don't failover to a peer we've
+ orelse ?THROW(timeout), %% already sent to.
Pkt = make_retransmit_packet(Pkt0),
@@ -1774,6 +1774,8 @@ retransmit({TPid, Caps, App}
SvcName,
Timeout,
[]).
+%% When sending a binary, it's up to prepare_retransmit to modify it
+%% accordingly.
retransmit({send, Msg},
Transport,
@@ -1815,77 +1817,20 @@ resend_request(Pkt0,
?LOG(retransmission, Pkt#diameter_packet.header),
incr(TPid, {msg_id(Pkt, AppDict), send, retransmission}),
- TRef = send_request(TPid, Pkt, Req, SvcName, Tmo),
- {TRef, Req}.
+ {TRef, MRef} = zend_requezt(TPid, Pkt, Req, SvcName, Tmo),
+ {TRef, MRef, Req}.
-%% store_request/2
+%% peer_monitor/2
-store_request(T, TPid) ->
- ets:insert(?REQUEST_TABLE, T),
- ets:member(?REQUEST_TABLE, TPid)
- orelse begin
- {_Seqs, _Req, TRef} = T,
- self() ! {failover, TRef} %% failover/1 may have missed
- end.
-
-%% lookup_request/2
-%%
-%% Note the match on both the key and transport pid. The latter is
-%% necessary since the same Hop-by-Hop and End-to-End identifiers are
-%% reused in the case of retransmission.
-
-lookup_request(Msg, TPid) ->
- Seqs = diameter_codec:sequence_numbers(Msg),
- Spec = [{{Seqs, #request{transport = TPid, _ = '_'}, '_'},
- [],
- ['$_']}],
- case ets:select(?REQUEST_TABLE, Spec) of
- [{_, Req, _}] ->
- Req;
- [] ->
+peer_monitor(TPid, TRef) ->
+ case ets:lookup(?REQUEST_TABLE, TPid) of %% at peer_up/1
+ [{_, MPid}] ->
+ monitor(process, MPid);
+ [] -> %% transport has gone down
+ self() ! {failover, TRef},
false
end.
-%% erase_request/1
-
-erase_request(T) ->
- ets:delete_object(?REQUEST_TABLE, T).
-
-%% match_requests/1
-
-match_requests(TPid) ->
- Pat = {'_', #request{transport = TPid, _ = '_'}, '_'},
- ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}]).
-
-%% have_request/2
-
-have_request(Pkt, TPid) ->
- Seqs = diameter_codec:sequence_numbers(Pkt),
- Pat = {Seqs, #request{transport = TPid, _ = '_'}, '_'},
- '$end_of_table' /= ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}], 1).
-
-%% ---------------------------------------------------------------------------
-%% # failover/1-2
-%% ---------------------------------------------------------------------------
-
-failover(TPid)
- when is_pid(TPid) ->
- lists:foreach(fun failover/1, match_requests(TPid));
-%% Note that a request process can store its request after failover
-%% notifications are sent here: store_request/2 sends the notification
-%% in that case.
-
-%% Failover as a consequence of peer_down/1: inform the
-%% request process.
-failover({_, Req, TRef}) ->
- #request{handler = Pid,
- packet = #diameter_packet{msg = M}}
- = Req,
- M /= undefined andalso (Pid ! {failover, TRef}).
-%% Failover is not performed when msg = binary() since sending
-%% pre-encoded binaries is only partially supported. (Mostly for
-%% test.)
-
%% get_destination/2
get_destination(Dict, Msg) ->
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index 2ba60a65fb..f28b8f2910 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-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -283,7 +283,7 @@ event(Msg,
?LOG(transition, {From, To}).
data(Msg, TPid, reopen, okay) ->
- {recv, TPid, 'DWA', _Pkt} = Msg, %% assert
+ {recv, TPid, false, 'DWA', _Pkt, _NPid} = Msg, %% assert
{TPid, T} = eraser(open),
[T];
@@ -447,12 +447,14 @@ transition({'DOWN', _, process, TPid, _Reason} = D,
end;
%% Incoming message.
-transition({recv, TPid, Name, PktT}, #watchdog{transport = TPid} = S) ->
+transition({recv, TPid, Route, Name, Pkt, NPid},
+ #watchdog{transport = TPid}
+ = S) ->
try
- incoming(Name, PktT, S)
+ incoming(Name, Pkt, NPid, S)
catch
#watchdog{dictionary = Dict0, receive_data = T} = NS ->
- diameter_traffic:receive_message(TPid, PktT, Dict0, T),
+ diameter_traffic:receive_message(TPid, Route, Pkt, NPid, Dict0, T),
NS
end;
@@ -582,15 +584,17 @@ send_watchdog(#watchdog{pending = false,
%% Don't count encode errors since we don't expect any on DWR/DWA.
-%% incoming/3
+%% incoming/4
-incoming(Name, {Pkt, NPid}, S) ->
- NS = recv(Name, Pkt, S),
- NPid ! {diameter, discard},
- NS;
+incoming(Name, Pkt, false, S) ->
+ recv(Name, Pkt, S);
-incoming(Name, Pkt, S) ->
- recv(Name, Pkt, S).
+incoming(Name, Pkt, NPid, S) ->
+ try
+ recv(Name, Pkt, S)
+ after
+ NPid ! {diameter, discard}
+ end.
%% recv/3
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl
index cdaa9aa7f9..864d5f0691 100644
--- a/lib/diameter/src/compiler/diameter_codegen.erl
+++ b/lib/diameter/src/compiler/diameter_codegen.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.
@@ -790,20 +790,7 @@ header() ->
("%% -------------------------------------------------------------------\n"
"%% This is a generated file.\n"
"%% -------------------------------------------------------------------\n"
- "\n"
- "%%\n"
- "%% Copyright (c) Ericsson AB. All rights reserved.\n"
- "%%\n"
- "%% The information in this document is the property of Ericsson.\n"
- "%%\n"
- "%% Except as specifically authorized in writing by Ericsson, the\n"
- "%% receiver of this document shall keep the information contained\n"
- "%% herein confidential and shall protect the same in whole or in\n"
- "%% part from disclosure and dissemination to third parties.\n"
- "%%\n"
- "%% Disclosure and disseminations to the receivers employees shall\n"
- "%% only be made on a strict need to know basis.\n"
- "%%\n\n").
+ "\n").
hrl_header(Name) ->
header() ++ "-hrl_name('" ++ ?S(Name) ++ ".hrl').\n".
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 618d5a7f10..eb5a5a44f3 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -49,7 +49,9 @@
{"1.10", [{restart_application, diameter}]}, %% 18.0
{"1.11", [{restart_application, diameter}]}, %% 18.1
{"1.11.1", [{restart_application, diameter}]}, %% 18.2
- {"1.11.2", [{restart_application, diameter}]} %% 18.3
+ {"1.11.2", [{restart_application, diameter}]}, %% 18.3
+ {"1.12", [{restart_application, diameter}]}, %% 19.0
+ {"1.12.1", [{restart_application, diameter}]} %% 19.1
],
[
{"0.9", [{restart_application, diameter}]},
@@ -80,6 +82,8 @@
{"1.10", [{restart_application, diameter}]},
{"1.11", [{restart_application, diameter}]},
{"1.11.1", [{restart_application, diameter}]},
- {"1.11.2", [{restart_application, diameter}]}
+ {"1.11.2", [{restart_application, diameter}]},
+ {"1.12", [{restart_application, diameter}]},
+ {"1.12.1", [{restart_application, diameter}]}
]
}.
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index 4a005b853d..f48e4347ee 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -98,7 +98,7 @@
-record(listener,
{ref :: reference(),
socket :: gen_sctp:sctp_socket(),
- count = 0 :: uint(), %% attached transport processes
+ service = false :: false | pid(), %% service process
pending = {0, queue:new()},
accept :: [match()]}).
%% Field pending implements two queues: the first of transport-to-be
@@ -129,11 +129,14 @@
-> {ok, pid(), [inet:ip_address()]}
when Ref :: diameter:transport_ref().
-start(T, #diameter_service{capabilities = Caps}, Opts)
+start(T, Svc, Opts)
when is_list(Opts) ->
+ #diameter_service{capabilities = Caps,
+ pid = SPid}
+ = Svc,
diameter_sctp_sup:start(), %% start supervisors on demand
Addrs = Caps#diameter_caps.host_ip_address,
- s(T, Addrs, lists:map(fun ip/1, Opts)).
+ s(T, Addrs, SPid, lists:map(fun ip/1, Opts)).
ip({ifaddr, A}) ->
{ip, A};
@@ -144,18 +147,22 @@ ip(T) ->
%% when there is not yet an association to assign it, or at comm_up on
%% a new association in which case the call retrieves a transport from
%% the pending queue.
-s({accept, Ref} = A, Addrs, Opts) ->
- {LPid, LAs} = listener(Ref, {Opts, Addrs}),
- try gen_server:call(LPid, {A, self()}, infinity) of
- {ok, TPid} -> {ok, TPid, LAs}
+s({accept, Ref} = A, Addrs, SPid, Opts) ->
+ {ok, LPid, LAs} = listener(Ref, {Opts, Addrs}),
+ try gen_server:call(LPid, {A, self(), SPid}, infinity) of
+ {ok, TPid} ->
+ {ok, TPid, LAs};
+ No ->
+ {error, No}
catch
- exit: Reason -> {error, Reason}
+ exit: Reason ->
+ {error, Reason}
end;
%% This implementation is due to there being no accept call in
%% gen_sctp in order to be able to accept a new association only
%% *after* an accepting transport has been spawned.
-s({connect = C, Ref}, Addrs, Opts) ->
+s({connect = C, Ref}, Addrs, _SPid, Opts) ->
diameter_sctp_sup:start_child({C, self(), Opts, Addrs, Ref}).
%% start_link/1
@@ -281,24 +288,23 @@ i({K, Ref}, #transport{mode = {accept, _}} = S) ->
%% Accepting processes can be started concurrently: ensure only one
%% listener is started.
-listener(LRef, T) ->
- diameter_sync:call({?MODULE, listener, LRef},
- {?MODULE, listener, [{LRef, T}]},
+listener(Ref, T) ->
+ diameter_sync:call({?MODULE, listener, Ref},
+ {?MODULE, listener, [{Ref, T}]},
infinity,
infinity).
-listener({LRef, T}) ->
- l(diameter_reg:match({?MODULE, listener, {LRef, '_'}}), LRef, T).
+listener({Ref, T}) ->
+ l(diameter_reg:match({?MODULE, listener, {Ref, '_'}}), Ref, T).
%% Existing listening process ...
l([{{?MODULE, listener, {_, AS}}, LPid}], _, _) ->
- {LAs, _Sock} = AS,
- {LPid, LAs};
+ {LAs, _Sock} = AS,
+ {ok, LPid, LAs};
%% ... or not.
-l([], LRef, T) ->
- {ok, LPid, LAs} = diameter_sctp_sup:start_child({listen, LRef, T}),
- {LPid, LAs}.
+l([], Ref, T) ->
+ diameter_sctp_sup:start_child({listen, Ref, T}).
%% open/3
@@ -364,11 +370,17 @@ type(T) ->
%% # handle_call/3
%% ---------------------------------------------------------------------------
-handle_call({{accept, Ref}, Pid}, _, #listener{ref = Ref,
- count = K}
- = S) ->
+handle_call({{accept, Ref}, Pid}, _, #listener{ref = Ref} = S) ->
{TPid, NewS} = accept(Ref, Pid, S),
- {reply, {ok, TPid}, NewS#listener{count = K+1}};
+ {reply, {ok, TPid}, NewS};
+
+handle_call({{accept, _} = T, Pid, SPid}, From, #listener{service = P} = S) ->
+ handle_call({T, Pid}, From, if not is_pid(P), is_pid(SPid) ->
+ monitor(process, SPid),
+ S#listener{service = SPid};
+ true ->
+ S
+ end);
handle_call(_, _, State) ->
{reply, nok, State}.
@@ -441,6 +453,13 @@ l({sctp, Sock, _RA, _RP, Data} = T, #listener{socket = Sock,
setopts(Sock),
NewS;
+%% Service process has died.
+l({'DOWN', _, process, Pid, _} = T, #listener{service = Pid,
+ socket = Sock}) ->
+ gen_sctp:close(Sock),
+ x(T);
+
+%% Accepting process has died.
l({'DOWN', _MRef, process, TPid, _}, #listener{pending = {_,Q}} = S) ->
down(queue:member(TPid, Q), TPid, S);
@@ -454,20 +473,17 @@ l({transport, remove, _} = T, #listener{socket = Sock}) ->
%% Accepting transport has died.
%% One that's waiting for transport start in the pending queue ...
-down(true, TPid, #listener{pending = {N,Q},
- count = K}
- = S) ->
+down(true, TPid, #listener{pending = {N,Q}} = S) ->
NQ = queue:filter(fun(P) -> P /= TPid end, Q),
if N < 0 -> %% awaiting an association ...
- S#listener{count = K-1,
- pending = {N+1, NQ}};
+ S#listener{pending = {N+1, NQ}};
true -> %% ... or one has been assigned
S#listener{pending = {N-1, NQ}}
end;
%% ... or one that's already attached.
-down(false, _TPid, #listener{count = K} = S) ->
- S#listener{count = K-1}.
+down(false, _TPid, S) ->
+ S.
%% t/2
%%
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index 546c2cfa5e..44abc5c3b4 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -71,11 +71,8 @@
%% a process owning the listening port.
%% Listener process state.
--record(listener, {socket :: inet:socket(),
- count = 1 :: non_neg_integer()}). %% accepting processes
-%% The count of accepting processes was previously used to terminate
-%% the listening process, but diameter_reg:subscribe/2 is now used for
-%% this. Leave the the count for trace purposes.
+-record(listener, {socket :: inet:socket(),
+ service = false :: false | pid()}). %% service process
%% Monitor process state.
-record(monitor,
@@ -138,11 +135,15 @@
| {ok, pid()}
when Ref :: diameter:transport_ref().
-start({T, Ref}, #diameter_service{capabilities = Caps}, Opts) ->
+start({T, Ref}, Svc, Opts) ->
+ #diameter_service{capabilities = Caps,
+ pid = SPid}
+ = Svc,
+
diameter_tcp_sup:start(), %% start tcp supervisors on demand
{Mod, Rest} = split(Opts),
Addrs = Caps#diameter_caps.host_ip_address,
- Arg = {T, Ref, Mod, self(), Rest, Addrs},
+ Arg = {T, Ref, Mod, self(), Rest, Addrs, SPid},
diameter_tcp_sup:start_child(Arg).
split([{module, M} | Opts]) ->
@@ -196,7 +197,7 @@ init(T) ->
%% i/1
%% A transport process.
-i({T, Ref, Mod, Pid, Opts, Addrs})
+i({T, Ref, Mod, Pid, Opts, Addrs, SPid})
when T == accept;
T == connect ->
monitor(process, Pid),
@@ -214,7 +215,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
?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),
+ Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs, SPid),
MPid ! {stop, self()}, %% tell the monitor to die
M = if SslOpts -> ssl; true -> Mod end,
putr(?REF_KEY, Ref),
@@ -228,6 +229,11 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
%% Put the reference in the process dictionary since we now use it
%% advertise the ssl socket after TLS upgrade.
+i({T, _Ref, _Mod, _Pid, _Opts, _Addrs} = Arg) %% from old code
+ when T == accept;
+ T == connect ->
+ i(erlang:append_element(Arg, _SPid = false));
+
%% A monitor process to kill the transport if the parent dies.
i(#monitor{parent = Pid, transport = TPid} = S) ->
proc_lib:init_ack({ok, self()}),
@@ -240,16 +246,18 @@ i(#monitor{parent = Pid, transport = TPid} = S) ->
%% death. However, a link can be unlinked and this is exactly what
%% gen_tcp seems to so. Links should be left to supervisors.
-i({listen, LRef, APid, {Mod, Opts, Addrs}}) ->
- [_] = diameter_config:subscribe(LRef, transport), %% assert existence
+i({listen = L, Ref, _APid, T}) -> %% from old code
+ i({L, Ref, T});
+
+i({listen, Ref, {Mod, Opts, Addrs}}) ->
+ [_] = diameter_config:subscribe(Ref, transport), %% assert existence
{[LA, LP], Rest} = proplists:split(Opts, [ip, port]),
LAddrOpt = get_addr(LA, Addrs),
LPort = get_port(LP),
{ok, LSock} = Mod:listen(LPort, gen_opts(LAddrOpt, Rest)),
LAddr = laddr(LAddrOpt, Mod, LSock),
- true = diameter_reg:add_new({?MODULE, listener, {LRef, {LAddr, LSock}}}),
+ true = diameter_reg:add_new({?MODULE, listener, {Ref, {LAddr, LSock}}}),
proc_lib:init_ack({ok, self(), {LAddr, LSock}}),
- monitor(process, APid),
#listener{socket = LSock}.
laddr([], Mod, Sock) ->
@@ -268,21 +276,22 @@ ssl_opts([{ssl_options, Opts}])
ssl_opts(T) ->
?ERROR({ssl_options, T}).
-%% init/7
+%% init/8
%% Establish a TLS connection before capabilities exchange ...
-init(Type, Ref, Mod, Pid, true, Opts, Addrs) ->
- init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs);
+init(Type, Ref, Mod, Pid, true, Opts, Addrs, SPid) ->
+ init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs, SPid);
%% ... or not.
-init(Type, Ref, Mod, Pid, _, Opts, Addrs) ->
- init(Type, Ref, Mod, Pid, Opts, Addrs).
+init(Type, Ref, Mod, Pid, _, Opts, Addrs, SPid) ->
+ init(Type, Ref, Mod, Pid, Opts, Addrs, SPid).
-%% init/6
+%% init/7
-init(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
+init(accept = T, Ref, Mod, Pid, Opts, Addrs, SPid) ->
{[Matches], Rest} = proplists:split(Opts, [accept]),
- {LAddr, LSock} = listener(Ref, {Mod, Rest, Addrs}),
+ {ok, LPid, {LAddr, LSock}} = listener(Ref, {Mod, Rest, Addrs}),
+ ok = gen_server:call(LPid, {accept, SPid}, infinity),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(accept(Mod, LSock)),
ok = accept_peer(Mod, Sock, accept(Matches)),
@@ -290,7 +299,7 @@ init(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
diameter_peer:up(Pid),
Sock;
-init(connect = T, Ref, Mod, Pid, Opts, Addrs) ->
+init(connect = T, Ref, Mod, Pid, Opts, Addrs, _SPid) ->
{[LA, RA, RP], Rest} = proplists:split(Opts, [ip, raddr, rport]),
LAddrOpt = get_addr(LA, Addrs),
RAddr = get_addr(RA),
@@ -344,24 +353,26 @@ accept(Opts) ->
%% Accepting processes can be started concurrently: ensure only one
%% listener is started.
-listener(LRef, T) ->
- diameter_sync:call({?MODULE, listener, LRef},
- {?MODULE, listener, [{LRef, T, self()}]},
+listener(Ref, T) ->
+ diameter_sync:call({?MODULE, listener, Ref},
+ {?MODULE, listener, [{Ref, T, self()}]},
infinity,
infinity).
-listener({LRef, T, TPid}) ->
- l(diameter_reg:match({?MODULE, listener, {LRef, '_'}}), LRef, T, TPid).
+%% listener/1
+
+listener({Ref, T, _TPid}) ->
+ l(diameter_reg:match({?MODULE, listener, {Ref, '_'}}), Ref, T).
+
+%% l/3
%% Existing listening process ...
-l([{{?MODULE, listener, {_, AS}}, LPid}], _, _, TPid) ->
- LPid ! {accept, TPid},
- AS;
+l([{{?MODULE, listener, {_, AS}}, LPid}], _, _) ->
+ {ok, LPid, AS};
%% ... or not.
-l([], LRef, T, TPid) ->
- {ok, _, AS} = diameter_tcp_sup:start_child({listen, LRef, TPid, T}),
- AS.
+l([], Ref, T) ->
+ diameter_tcp_sup:start_child({listen, Ref, T}).
%% get_addr/1
@@ -440,6 +451,14 @@ portnr(Sock) ->
%% # handle_call/3
%% ---------------------------------------------------------------------------
+handle_call({accept, SPid}, _From, #listener{service = P} = S) ->
+ {reply, ok, if not is_pid(P), is_pid(SPid) ->
+ monitor(process, SPid),
+ S#listener{service = SPid};
+ true ->
+ S
+ end};
+
handle_call(_, _, State) ->
{reply, nok, State}.
@@ -507,19 +526,20 @@ m({'DOWN', _, process, Pid, _}, #monitor{parent = Pid,
%%
%% Transition listener state.
-%% An accepting transport is attaching.
-l({accept, TPid}, #listener{count = N} = S) ->
- monitor(process, TPid),
- S#listener{count = N+1};
-
-%% Accepting process has died.
-l({'DOWN', _, process, _, _}, #listener{count = N} = S) ->
- S#listener{count = N-1};
+%% Service process has died.
+l({'DOWN', _, process, Pid, _} = T, #listener{service = Pid,
+ socket = Sock}) ->
+ gen_tcp:close(Sock),
+ x(T);
%% Transport has been removed.
l({transport, remove, _} = T, #listener{socket = Sock}) ->
gen_tcp:close(Sock),
- x(T).
+ x(T);
+
+%% Possibly death of an accepting process monitored in old code.
+l(_, S) ->
+ S.
%% t/2
%%
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index 37c41a1761..869797f11f 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.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.
diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl
index b5e520e642..5353688bf4 100644
--- a/lib/diameter/test/diameter_relay_SUITE.erl
+++ b/lib/diameter/test/diameter_relay_SUITE.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.
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 6f3a4801ee..4c82d4dee2 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -248,17 +248,14 @@ all() ->
groups() ->
Ts = tc(),
Sctp = ?util:have_sctp(),
- [{?util:name([R,D,A,C]), [parallel], Ts} || R <- ?ENCODINGS,
- D <- ?RFCS,
- A <- ?ENCODINGS,
- C <- ?CONTAINERS]
+ [{B, [P], Ts} || {B,P} <- [{true, shuffle}, {false, parallel}]]
++
[{?util:name([T,R,D,A,C,SD,CD]),
[],
[start_services,
add_transports,
result_codes,
- {group, ?util:name([R,D,A,C])},
+ {group, SD orelse CD},
remove_transports,
stop_services]}
|| T <- ?TRANSPORTS,
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl
index 37fcbbc267..cca28dd23c 100644
--- a/lib/diameter/test/diameter_util.erl
+++ b/lib/diameter/test/diameter_util.erl
@@ -195,13 +195,21 @@ unique_string() ->
%% have_sctp/0
have_sctp() ->
- case gen_sctp:open() of
- {ok, Sock} ->
- gen_sctp:close(Sock),
- true;
- {error, E} when E == eprotonosupport;
- E == esocktnosupport -> %% fail on any other reason
- false
+ case erlang:system_info(system_architecture) of
+ %% We do not support the sctp version present in solaris
+ %% version "sparc-sun-solaris2.10", that behaves differently
+ %% from later versions and linux
+ "sparc-sun-solaris2.10" ->
+ false;
+ _->
+ case gen_sctp:open() of
+ {ok, Sock} ->
+ gen_sctp:close(Sock),
+ true;
+ {error, E} when E == eprotonosupport;
+ E == esocktnosupport -> %% fail on any other reason
+ false
+ end
end.
%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index cb750c69a3..94d9d72a48 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -1,6 +1,6 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2016. All Rights Reserved.
+# Copyright Ericsson AB 2010-2017. 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.12
+DIAMETER_VSN = 1.12.2
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/edoc/doc/overview.edoc b/lib/edoc/doc/overview.edoc
index d2bba9d744..3a2118928a 100644
--- a/lib/edoc/doc/overview.edoc
+++ b/lib/edoc/doc/overview.edoc
@@ -377,6 +377,12 @@ The following tags can be used before a function definition:
Useful for debug/test functions, etc. The content can be
used as a comment; it is ignored by EDoc.</dd>
+ <dt><a name="ftag-param">`@param'</a></dt>
+ <dd>Provide more information on a single parameter of the
+ enclosing function. The content consists of a parameter name,
+ followed by one or more whitespace characters, and XHTML text.
+ </dd>
+
<dt><a name="ftag-private">`@private'</a></dt>
<dd>Marks the function as private (i.e., not part of the public
interface), so that it will not appear in the normal
@@ -386,6 +392,10 @@ The following tags can be used before a function definition:
always "private".) The content can be used as a comment; it is
ignored by EDoc.</dd>
+ <dt><a name="ftag-returns">`@returns'</a></dt>
+ <dd>Specify additional information about the value returned by
+ the function. Content consists of XHTML text.</dd>
+
<dt><a name="ftag-see">`@see'</a></dt>
<dd>Make a reference to a module, function, datatype, or
application. (See {@section References}.)
diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
index ae8147c564..4982488335 100644
--- a/lib/edoc/doc/src/notes.xml
+++ b/lib/edoc/doc/src/notes.xml
@@ -32,6 +32,36 @@
<p>This document describes the changes made to the EDoc
application.</p>
+<section><title>Edoc 0.8.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Document the function tags <c>@param</c> and
+ <c>@returns</c>. </p>
+ <p>
+ Own Id: OTP-13930 Aux Id: PR-1175 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Edoc 0.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Improve types and specs in OTP documentation generated
+ from Erlang source files. </p>
+ <p>
+ Own Id: OTP-13720 Aux Id: ERL-120 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Edoc 0.7.19</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/edoc/include/edoc_doclet.hrl b/lib/edoc/include/edoc_doclet.hrl
index ac6763fb33..9893ff3ee0 100644
--- a/lib/edoc/include/edoc_doclet.hrl
+++ b/lib/edoc/include/edoc_doclet.hrl
@@ -15,7 +15,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% Author contact: [email protected]
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
index 94013bb5ac..7d61b9e9c5 100644
--- a/lib/edoc/src/edoc.erl
+++ b/lib/edoc/src/edoc.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 2001-2007 Richard Carlsson
diff --git a/lib/edoc/src/edoc.hrl b/lib/edoc/src/edoc.hrl
index 5b0fb68cf9..4b28c0b12e 100644
--- a/lib/edoc/src/edoc.hrl
+++ b/lib/edoc/src/edoc.hrl
@@ -15,7 +15,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% Author contact: [email protected]
diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl
index b797d74a71..86273b8352 100644
--- a/lib/edoc/src/edoc_data.erl
+++ b/lib/edoc/src/edoc_data.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @private
diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl
index 5961ca8cc0..9128c19a4d 100644
--- a/lib/edoc/src/edoc_doclet.erl
+++ b/lib/edoc/src/edoc_doclet.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 2003-2006 Richard Carlsson
diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl
index e7a4c36ca4..5c8c648319 100644
--- a/lib/edoc/src/edoc_extract.erl
+++ b/lib/edoc/src/edoc_extract.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 2001-2003 Richard Carlsson
@@ -32,9 +32,9 @@
%% %% @headerfile "edoc.hrl" (disabled until it can be made private)
-include("edoc.hrl").
-%% @type filename() = file:filename().
-%% @type proplist() = proplists:property().
-%% @type syntaxTree() = erl_syntax:syntaxTree().
+%% @type filename() = //kernel/file:filename().
+%% @type proplist() = //stdlib/proplists:property().
+%% @type syntaxTree() = //syntax_tools/erl_syntax:syntaxTree().
%% @spec source(File::filename(), Env::edoc_env(), Options::proplist())
%% -> {ModuleName, edoc:edoc_module()}
@@ -639,11 +639,11 @@ file_macros(_Context, Env) ->
%%
%% The idea is to mimic how the @type tag works.
%% Using @type:
-%% @type t() = t1(). Some docs of t/0;
-%% Further docs of t/0.
+%%```@type t() = t1(). Some docs of t/0;
+%% Further docs of t/0.'''
%% The same thing using -type:
-%% -type t() :: t1(). % Some docs of t/0;
-%% Further docs of t/0.
+%%```-type t() :: t1(). % Some docs of t/0;
+%% Further docs of t/0.'''
find_type_docs(Forms0, Comments, Env, File) ->
Tree = erl_recomment:recomment_forms(Forms0, Comments),
Forms = preprocess_forms(Tree),
diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl
index ef57b7b084..41a0b193ec 100644
--- a/lib/edoc/src/edoc_layout.erl
+++ b/lib/edoc/src/edoc_layout.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index dcc239f6b4..8fbc83fa29 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 2001-2003 Richard Carlsson
@@ -935,7 +935,7 @@ get_doc_env(Opts) ->
%% Modules = [atom()]
%% proplist() = [term()]
%%
-%% @type proplist() = proplists:property().
+%% @type proplist() = //stdlib/proplists:property().
%% @type edoc_env(). Environment information needed by EDoc for
%% generating references. The data representation is not documented.
%%
diff --git a/lib/edoc/src/edoc_macros.erl b/lib/edoc/src/edoc_macros.erl
index e1a54d5090..7c91cb0f57 100644
--- a/lib/edoc/src/edoc_macros.erl
+++ b/lib/edoc/src/edoc_macros.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @private
diff --git a/lib/edoc/src/edoc_refs.erl b/lib/edoc/src/edoc_refs.erl
index b9a9391053..9a45d5b0fa 100644
--- a/lib/edoc/src/edoc_refs.erl
+++ b/lib/edoc/src/edoc_refs.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @private
diff --git a/lib/edoc/src/edoc_report.erl b/lib/edoc/src/edoc_report.erl
index dc6320df6d..ce54e71538 100644
--- a/lib/edoc/src/edoc_report.erl
+++ b/lib/edoc/src/edoc_report.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @private
diff --git a/lib/edoc/src/edoc_run.erl b/lib/edoc/src/edoc_run.erl
index 261a649c70..d69bddc999 100644
--- a/lib/edoc/src/edoc_run.erl
+++ b/lib/edoc/src/edoc_run.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 2003 Richard Carlsson
diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl
index 9e2e41e902..919159e125 100644
--- a/lib/edoc/src/edoc_tags.erl
+++ b/lib/edoc/src/edoc_tags.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @private
@@ -213,8 +213,10 @@ filter_tags([#tag{name = N, line = L} = T | Ts], Tags, Where, Ts1) ->
true ->
filter_tags(Ts, Tags, Where, [T | Ts1]);
false ->
- [warning(L, Where, "tag @~s not recognized.", [N]) ||
- Where =/= no],
+ case Where of
+ no -> ok;
+ _ -> warning(L, Where, "tag @~s not recognized.", [N])
+ end,
filter_tags(Ts, Tags, Where, Ts1)
end;
filter_tags([], _, _, Ts) ->
@@ -451,7 +453,7 @@ check_type(#tag{line = L, data = Data}, P0, Ls, Ts) ->
check_type(#t_def{type = Type}, P, Ls, Ts) ->
check_type(Type, P, Ls, Ts);
check_type(#t_type{name = Name, args = Args}, P, Ls, Ts) ->
- _ = check_used_type(Name, Args, P, Ls),
+ check_used_type(Name, Args, P, Ls),
check_types3(Args++Ts, P, Ls);
check_type(#t_var{}, P, Ls, Ts) ->
check_types3(Ts, P, Ls);
@@ -503,7 +505,8 @@ check_used_type(#t_name{name = N, module = Mod}=Name, Args, P, LocalTypes) ->
false ->
#parms{warn = W, line = L, file = File} = P,
%% true = ets:insert(DT, TypeName),
- [type_warning(L, File, "missing type", N, NArgs) || W]
+ _ = [type_warning(L, File, "missing type", N, NArgs) || W],
+ ok
end.
type_warning(Line, File, S, N, NArgs) ->
diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl
index 5bb68e79fb..bd4a583b8e 100644
--- a/lib/edoc/src/edoc_types.erl
+++ b/lib/edoc/src/edoc_types.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @private
diff --git a/lib/edoc/src/edoc_types.hrl b/lib/edoc/src/edoc_types.hrl
index 3e5e91484f..3f502068af 100644
--- a/lib/edoc/src/edoc_types.hrl
+++ b/lib/edoc/src/edoc_types.hrl
@@ -15,7 +15,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% Author contact: [email protected]
diff --git a/lib/edoc/src/edoc_wiki.erl b/lib/edoc/src/edoc_wiki.erl
index 5d0d78bf3c..5d15bbcfcc 100644
--- a/lib/edoc/src/edoc_wiki.erl
+++ b/lib/edoc/src/edoc_wiki.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @private
diff --git a/lib/edoc/src/otpsgml_layout.erl b/lib/edoc/src/otpsgml_layout.erl
index 052c75b9d4..8458a52f2d 100644
--- a/lib/edoc/src/otpsgml_layout.erl
+++ b/lib/edoc/src/otpsgml_layout.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index f38800b3e0..d66802fdac 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.19
+EDOC_VSN = 0.8.1
diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
index 43873e44e2..f2c7889e58 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -197,7 +197,7 @@
</type>
<desc>
<p> Add an entry. The entry must not exist.</p>
- <pre>
+ <code>
add(Handle,
"cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com",
[{"objectclass", ["person"]},
@@ -205,7 +205,7 @@
{"sn", ["Valentine"]},
{"telephoneNumber", ["545 555 00"]}]
)
- </pre>
+ </code>
</desc>
</func>
<func>
@@ -216,9 +216,9 @@
</type>
<desc>
<p> Delete an entry.</p>
- <pre>
+ <code>
delete(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com")
- </pre>
+ </code>
</desc>
</func>
@@ -259,11 +259,11 @@
</type>
<desc>
<p> Modify an entry.</p>
- <pre>
+ <code>
modify(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com",
[eldap:mod_replace("telephoneNumber", ["555 555 00"]),
eldap:mod_add("description", ["LDAP Hacker"]) ])
- </pre>
+ </code>
</desc>
</func>
<func>
@@ -320,10 +320,10 @@
whether the current RDN should be removed from the attribute list after the after operation.
<c>NewSupDN</c> is the new parent that the RDN shall be moved to. If the old parent should
remain as parent, <c>NewSupDN</c> shall be "".</p>
- <pre>
+ <code>
modify_dn(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com ",
"cn=Bill Jr Valentine", true, "")
- </pre>
+ </code>
</desc>
</func>
<func>
@@ -342,10 +342,10 @@
Default values: scope is <c>wholeSubtree()</c>, deref is <c>derefAlways()</c>,
types_only is <c>false</c> and timeout is <c>0</c> (meaning infinity).
</p>
- <pre>
+ <code>
Filter = eldap:substrings("cn", [{any,"V"}]),
search(Handle, [{base, "dc=example, dc=com"}, {filter, Filter}, {attributes, ["cn"]}]),
- </pre>
+ </code>
<p>The <c>timeout</c> option in the <c>SearchOptions</c> is for the ldap server, while
the timeout in <seealso marker="#open/2">eldap:open/2</seealso> is used for each
individual request in the search operation.
@@ -454,7 +454,7 @@
</type>
<desc> <p>Creates an extensible match filter. For example, </p>
<code>
- eldap:extensibleMatch("Bar", [{type,"sn"}, {matchingRule,"caseExactMatch"}]))
+ eldap:extensibleMatch("Bar", [{type,"sn"}, {matchingRule,"caseExactMatch"}]))
</code>
<p>creates a filter which performs a <c>caseExactMatch</c> on the attribute <c>sn</c> and matches with the value <c>"Bar"</c>. The default value of <c>dnAttributes</c> is <c>false</c>.</p> </desc>
</func>
diff --git a/lib/eldap/test/Makefile b/lib/eldap/test/Makefile
index 21a0da926f..81fa8f187a 100644
--- a/lib/eldap/test/Makefile
+++ b/lib/eldap/test/Makefile
@@ -42,7 +42,7 @@ TARGET_FILES= \
SPEC_FILES = eldap.spec
-# COVER_FILE = eldap.cover
+COVER_FILE = eldap.cover
# ----------------------------------------------------
diff --git a/lib/eldap/test/eldap.cover b/lib/eldap/test/eldap.cover
new file mode 100644
index 0000000000..8c15956e72
--- /dev/null
+++ b/lib/eldap/test/eldap.cover
@@ -0,0 +1,3 @@
+{incl_app,eldap,details}.
+
+{excl_mods, eldap, ['ELDAPv3']}.
diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index 21dc617d55..4824a755d9 100644
--- a/lib/erl_docgen/doc/src/notes.xml
+++ b/lib/erl_docgen/doc/src/notes.xml
@@ -31,7 +31,39 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.5</title>
+ <section><title>Erl_Docgen 0.6.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Docgen would previously emit "utf8" as the default
+ encoding in xml. This has now been remedied by emitting
+ the correct string "UTF-8" instead.</p>
+ <p>
+ Own Id: OTP-13971</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Improve types and specs in OTP documentation generated
+ from Erlang source files. </p>
+ <p>
+ Own Id: OTP-13720 Aux Id: ERL-120 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/erl_docgen/priv/bin/xref_mod_app.escript b/lib/erl_docgen/priv/bin/xref_mod_app.escript
index ac4278bf22..4a418fe144 100755
--- a/lib/erl_docgen/priv/bin/xref_mod_app.escript
+++ b/lib/erl_docgen/priv/bin/xref_mod_app.escript
@@ -84,7 +84,7 @@ preloaded(TopDir) ->
%% It's OK if too much data is generated as long as all applications
%% and all modules are mentioned.
appmods(D) ->
- ErlFiles = filelib:wildcard(filename:join([D,"src","*.erl"])),
+ ErlFiles = filelib:wildcard(filename:join([D,"src","**","*.erl"])),
AppV = filename:basename(D),
App = case string:rstr(AppV, "-") of
0 -> AppV;
diff --git a/lib/erl_docgen/priv/dtd/erlref.dtd b/lib/erl_docgen/priv/dtd/erlref.dtd
index 835407520a..615b88b61a 100644
--- a/lib/erl_docgen/priv/dtd/erlref.dtd
+++ b/lib/erl_docgen/priv/dtd/erlref.dtd
@@ -29,7 +29,7 @@
<!-- `name' is used in common.refs.dtd and must therefore
be defined in each *ref. dtd -->
-<!ELEMENT name (#PCDATA) >
+<!ELEMENT name (#PCDATA|seealso)* >
<!ATTLIST name name CDATA #IMPLIED
arity CDATA #IMPLIED
clause_i CDATA #IMPLIED
diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index fe6c7d3c28..edab8e1c7e 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -2007,10 +2007,10 @@
</xsl:variable>
<xsl:choose>
<xsl:when test="ancestor::datatype">
- <a name="type-{$fname}"><span class="bold_code"><xsl:value-of select="."/></span></a><br/>
+ <a name="type-{$fname}"></a><span class="bold_code"><xsl:apply-templates/></span><br/>
</xsl:when>
<xsl:otherwise>
- <a name="{$fname}-{$arity}"><span class="bold_code"><xsl:value-of select="."/></span></a><br/>
+ <a name="{$fname}-{$arity}"></a><span class="bold_code"><xsl:apply-templates/></span><br/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
index 0ac7985a48..13ff0a139d 100644
--- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
+%% 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.
@@ -111,7 +111,16 @@ root_attributes(Element, Opts) ->
Enc ->
Enc
end,
- [#xmlAttribute{name=encoding, value=Encoding}].
+ [#xmlAttribute{name=encoding, value=reformat_encoding(Encoding)}].
+
+%% epp:default_encoding/0 returns 'utf8'
+reformat_encoding(utf8) -> "UTF-8";
+reformat_encoding(List) when is_list(List) ->
+ case string:to_lower(List) of
+ "utf8" -> "UTF-8";
+ _ -> List
+ end;
+reformat_encoding(Other) -> Other.
layout_chapter(#xmlElement{name=overview, content=Es}) ->
Title = get_text(title, Es),
@@ -563,7 +572,14 @@ otp_xmlify_a_fileref(FileRef1, AppS) ->
true ->
case split(Marker0, "-") of
[Func,Arity] ->
- Func++"/"++Arity;
+ try list_to_integer(Arity) of
+ _ ->
+ Func++"/"++Arity
+ catch
+ _:_ ->
+ %% This is "type-"<a-type>.
+ Marker0
+ end;
_ ->
Marker0
end
@@ -831,19 +847,49 @@ local_types([]) -> [];
local_types(Es) ->
local_defs2(get_elem(localdef, Es)).
+-define(LOCAL_TYPES, edoc_local_defs).
+
local_defs2([]) -> [];
local_defs2(Es) ->
+ case collect_local_types(Es) of
+ [] -> local_defs3(Es);
+ LocalTypes ->
+ ?LOCAL_TYPES = ets:new(?LOCAL_TYPES, [named_table]),
+ true = ets:insert(?LOCAL_TYPES, LocalTypes),
+ try
+ local_defs3(Es)
+ after
+ ets:delete(?LOCAL_TYPES)
+ end
+ end.
+
+local_defs3(Es) ->
{type,[?NL | [{v, localdef2(E)} || E <- Es]]}.
+%% Does not work well for parametrized types.
+collect_local_types(Es) ->
+ lists:append([collect_local_type(E) || E <- Es]).
+
+collect_local_type(#xmlElement{content = Es}) ->
+ case get_elem(typevar, Es) of
+ [] ->
+ [{t_abstype(get_content(abstype, Es))}];
+ [_] ->
+ []
+ end.
+
%% Like localdef/1, but does not use label_anchor/2 -- we don't want any
%% markers or em tags in <v> tag, plain text only!
+%% When used stand-alone, EDoc generates links to local types. An ETS
+%% table holds local types, to avoid generating links to them.
localdef2(#xmlElement{content = Es}) ->
- case get_elem(typevar, Es) of
- [] ->
- t_utype(get_elem(type, Es));
- [V] ->
- t_var(V) ++ [" = "] ++ t_utype(get_elem(type, Es))
- end.
+ Var = case get_elem(typevar, Es) of
+ [] ->
+ [t_abstype(get_content(abstype, Es))];
+ [V] ->
+ t_var(V)
+ end,
+ Var ++ [" = "] ++ t_utype(get_elem(type, Es)).
type_name(#xmlElement{content = Es}) ->
t_name(get_elem(erlangName, get_content(typedef, Es))).
@@ -855,10 +901,9 @@ types(Ts) ->
typedecl(Name, #xmlElement{content = Es}) ->
TypedefEs = get_content(typedef, Es),
Id = "type-"++Name,
- [{tag, typedef(TypedefEs)},
+ [{tag, [{marker,[{id,Id}],[]}] ++ typedef(TypedefEs)},
?NL,
- {item, [{marker,[{id,Id}],[]} |
- local_defs(get_elem(localdef, TypedefEs)) ++ fulldesc(Es)]},
+ {item, local_defs(get_elem(localdef, TypedefEs)) ++ fulldesc(Es)},
?NL].
typedef(Es) ->
@@ -866,14 +911,14 @@ typedef(Es) ->
++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])),
case get_elem(type, Es) of
[] ->
- [{tt, Name}];
+ Name;
Type ->
- [{tt, Name ++ [" = "] ++ t_utype(Type)}]
+ Name ++ [" = "] ++ t_utype(Type)
end.
-local_defs([]) -> [];
+local_defs([]) -> [{p,[]}];
local_defs(Es) ->
- [?NL, {ul, [{li, [{tt, localdef(E)}]} || E <- Es]}].
+ [?NL, {ul, [{li, [{p, localdef(E)}]} || E <- Es]}].
localdef(E = #xmlElement{content = Es}) ->
Var = case get_elem(typevar, Es) of
@@ -917,6 +962,7 @@ seealso_module(Es) ->
Es1 ->
{section,[{title,["See also"]},{p,seq(fun see/1, Es1, [])}]}
end.
+
seealso_function(Es) ->
case get_elem(see, Es) of
[] -> [];
@@ -988,7 +1034,14 @@ t_name([E]) ->
end.
t_utype([E]) ->
- t_utype_elem(E).
+ flatten_type(t_utype_elem(E)).
+
+%% Make sure see also are top elements of lists.
+flatten_type(T) ->
+ [case is_integer(E) of
+ true -> [E];
+ false -> E
+ end || E <- lists:flatten(T)].
t_utype_elem(E=#xmlElement{content = Es}) ->
case get_attrval(name, E) of
@@ -1021,16 +1074,14 @@ t_type([#xmlElement{name = tuple, content = Es}]) ->
t_tuple(Es);
t_type([#xmlElement{name = 'fun', content = Es}]) ->
t_fun(Es);
-t_type([#xmlElement{name = abstype, content = Es}]) ->
- t_abstype(Es);
+t_type([E = #xmlElement{name = abstype, content = Es}]) ->
+ t_abstype(E, Es);
t_type([#xmlElement{name = union, content = Es}]) ->
t_union(Es);
t_type([#xmlElement{name = record, content = Es}]) ->
t_record(Es);
t_type([#xmlElement{name = map, content = Es}]) ->
- t_map(Es);
-t_type([#xmlElement{name = map_field, content = Es}]) ->
- t_map_field(Es).
+ t_map(Es).
t_var(E) ->
[get_attrval(name, E)].
@@ -1065,35 +1116,53 @@ t_fun(Es) ->
t_record([E|Es]) ->
["#", get_attrval(value, E), "{"++ seq(fun t_field/1, Es) ++"}"].
+
t_field(#xmlElement{name=field, content=[Atom,Type]}) ->
[get_attrval(value, Atom), "="] ++ t_utype_elem(Type).
t_map(Es) ->
- ["#{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+ ["#{"] ++ seq(fun t_map_field/1, Es, ["}"]).
+
+t_map_field(E = #xmlElement{name = map_field, content = [K,V]}) ->
+ KElem = t_utype_elem(K),
+ VElem = t_utype_elem(V),
+ AS = case get_attrval(assoc_type, E) of
+ "assoc" -> " => ";
+ "exact" -> " := "
+ end,
+ [KElem ++ AS ++ VElem].
-t_map_field([K,V]) ->
- [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)].
+t_abstype(E, Es) ->
+ see_type(E, t_abstype(Es)).
t_abstype(Es) ->
- case split_at_colon(t_name(get_elem(erlangName, Es)),[]) of
- {Mod,Type} ->
- [Type, "("] ++
- seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) ++
- [" (see module ", Mod, ")"];
- Type ->
- [Type, "("] ++
- seq(fun t_utype_elem/1, get_elem(type, Es), [")"])
+ Name = t_name(get_elem(erlangName, Es)),
+ [Name, "("] ++ seq(fun t_utype_elem/1, get_elem(type, Es), [")"]).
+
+see_type(E, Es0) ->
+ case get_attrval(href, E) of
+ [] -> Es0;
+ Href0 ->
+ try
+ false = is_local_type(Es0),
+ %% Fails for parametrized types:
+ Text = #xmlText{value = lists:append(Es0)},
+ {Href, Es} = otp_xmlify_a_href(Href0, [Text]),
+ [{seealso, [{marker, Href}], Es}]
+ catch
+ _:_ ->
+ Es0
+ end
end.
-%% Split at one colon, but not at two (or more)
-split_at_colon([$:,$:|_]=Rest,Acc) ->
- lists:reverse(Acc)++Rest;
-split_at_colon([$:|Type],Acc) ->
- {lists:reverse(Acc),Type};
-split_at_colon([Char|Rest],Acc) ->
- split_at_colon(Rest,[Char|Acc]);
-split_at_colon([],Acc) ->
- lists:reverse(Acc).
+is_local_type(Es) ->
+ try
+ [_] = ets:lookup(?LOCAL_TYPES, Es),
+ true
+ catch
+ _:_->
+ false
+ end.
t_union(Es) ->
seq(fun t_utype_elem/1, Es, " | ", []).
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index 98d1c3f7be..d6106a2823 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1 +1 @@
-ERL_DOCGEN_VSN = 0.5
+ERL_DOCGEN_VSN = 0.6.1
diff --git a/lib/erl_interface/doc/src/book.xml b/lib/erl_interface/doc/src/book.xml
index c9194d96ff..94bfef7455 100644
--- a/lib/erl_interface/doc/src/book.xml
+++ b/lib/erl_interface/doc/src/book.xml
@@ -11,7 +11,7 @@
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
@@ -19,19 +19,19 @@
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>Erlang Interface</title>
+ <title>Erl_Interface</title>
<prepared>Gordon Beaton</prepared>
<docno></docno>
<date>1998-11-30</date>
<rev>1.2</rev>
- <file>book.sgml</file>
+ <file>book.xml</file>
</header>
<insidecover>
</insidecover>
- <pagetext>Erlang Interface</pagetext>
+ <pagetext>Erl_Interface</pagetext>
<preamble>
<contents level="2"></contents>
</preamble>
@@ -47,4 +47,3 @@
<listofterms></listofterms>
<index></index>
</book>
-
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml
index 1177954eb9..ddfb4d88a8 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -11,7 +11,7 @@
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
@@ -30,361 +30,508 @@
<checked></checked>
<date>2000-11-27</date>
<rev>PA1</rev>
- <file>ei.sgml</file>
+ <file>ei.xml</file>
</header>
<lib>ei</lib>
- <libsummary>routines for handling the erlang binary term format</libsummary>
+ <libsummary>Routines for handling the Erlang binary term format.</libsummary>
<description>
- <p>The library <c><![CDATA[ei]]></c> contains macros and functions to encode
- and decode the erlang binary term format.</p>
- <p>With <c><![CDATA[ei]]></c>, you can convert atoms, lists, numbers and
+ <p>The library <c>ei</c> contains macros and functions to encode
+ and decode the Erlang binary term format.</p>
+
+ <p><c>ei</c> allows you to convert atoms, lists, numbers, and
binaries to and from the binary format. This is useful when
- writing port programs and drivers. <c><![CDATA[ei]]></c> uses a given
- buffer, and no dynamic memory (with the exception of
- <c><![CDATA[ei_decode_fun()]]></c>), and is often quite fast.</p>
- <p>It also handles C-nodes, C-programs that talks erlang
- distribution with erlang nodes (or other C-nodes) using the
- erlang distribution format. The difference between <c><![CDATA[ei]]></c> and
- <c><![CDATA[erl_interface]]></c> is that <c><![CDATA[ei]]></c> uses the binary format
- directly when sending and receiving terms. It is also thread
- safe, and using threads, one process can handle multiple
- C-nodes. The <c><![CDATA[erl_interface]]></c> library is built on top of
- <c><![CDATA[ei]]></c>, but of legacy reasons, it doesn't allow for multiple
- C-nodes. In general, <c><![CDATA[ei]]></c> is the preferred way of doing
- C-nodes.</p>
- <p>The decode and encode functions use a buffer an index into the
+ writing port programs and drivers. <c>ei</c> uses a given
+ buffer, no dynamic memory (except
+ <c>ei_decode_fun()</c>) and is often quite fast.</p>
+
+ <p><c>ei</c> also handles C-nodes, C-programs that talks Erlang
+ distribution with Erlang nodes (or other C-nodes) using the
+ Erlang distribution format. The difference between <c>ei</c>
+ and <c>erl_interface</c> is that <c>ei</c> uses
+ the binary format directly when sending and receiving terms. It is also
+ thread safe, and using threads, one process can handle multiple
+ C-nodes. The <c>erl_interface</c> library is built on top of
+ <c>ei</c>, but of legacy reasons, it does not allow for
+ multiple C-nodes. In general, <c>ei</c> is the preferred way
+ of doing C-nodes.</p>
+
+ <p>The decode and encode functions use a buffer and an index into the
buffer, which points at the point where to encode and
decode. The index is updated to point right after the term
encoded/decoded. No checking is done whether the term fits in
the buffer or not. If encoding goes outside the buffer, the
- program may crash.</p>
- <p>All functions takes two parameter, <c><![CDATA[buf]]></c> is a pointer to
- the buffer where the binary data is / will be, <c><![CDATA[index]]></c> is a
- pointer to an index into the buffer. This parameter will be
- incremented with the size of the term decoded / encoded. The
- data is thus at <c><![CDATA[buf[*index]]]></c> when an <c><![CDATA[ei]]></c> function is
- called.</p>
- <p>The encode functions all assumes that the <c><![CDATA[buf]]></c> and
- <c><![CDATA[index]]></c> parameters points to a buffer big enough for the
- data. To get the size of an encoded term, without encoding it,
- pass <c><![CDATA[NULL]]></c> instead of a buffer pointer. The <c><![CDATA[index]]></c>
- parameter will be incremented, but nothing will be encoded. This
- is the way in <c><![CDATA[ei]]></c> to "preflight" term encoding.</p>
- <p>There are also encode-functions that uses a dynamic buffer. It
+ program can crash.</p>
+
+ <p>All functions take two parameters:</p>
+
+ <list type="bulleted">
+ <item><p><c>buf</c> is a pointer to
+ the buffer where the binary data is or will be.</p>
+ </item>
+ <item><p><c>index</c> is a pointer to an index into the
+ buffer. This parameter is incremented with the size of the term
+ decoded/encoded.</p>
+ </item>
+ </list>
+
+ <p>The data is thus at <c>buf[*index]</c> when an
+ <c>ei</c> function is called.</p>
+
+ <p>All encode functions assume that the <c>buf</c> and
+ <c>index</c> parameters point to a buffer large enough for
+ the data. To get the size of an encoded term, without encoding it,
+ pass <c>NULL</c> instead of a buffer pointer. Parameter
+ <c>index</c> is incremented, but nothing will be encoded. This
+ is the way in <c>ei</c> to "preflight" term encoding.</p>
+
+ <p>There are also encode functions that use a dynamic buffer. It
is often more convenient to use these to encode data. All encode
- functions comes in two versions: those starting with <c><![CDATA[ei_x]]></c>,
- uses a dynamic buffer.</p>
- <p>All functions return <c><![CDATA[0]]></c> if successful, and <c><![CDATA[-1]]></c> if
- not. (For instance, if a term is not of the expected type, or
- the data to decode is not a valid erlang term.)</p>
- <p>Some of the decode-functions needs a preallocated buffer. This
- buffer must be allocated big enough, and for non compound types
- the <c><![CDATA[ei_get_type()]]></c>
- function returns the size required (note that for strings an
- extra byte is needed for the 0 string terminator).</p>
+ functions comes in two versions; those starting with
+ <c>ei_x</c> use a dynamic buffer.</p>
+
+ <p>All functions return <c>0</c> if successful, otherwise
+ <c>-1</c> (for example, if a term is not of the expected
+ type, or the data to decode is an invalid Erlang term).</p>
+
+ <p>Some of the decode functions need a pre-allocated buffer. This
+ buffer must be allocated large enough, and for non-compound types
+ the <c>ei_get_type()</c>
+ function returns the size required (notice that for strings an
+ extra byte is needed for the <c>NULL</c>-terminator).</p>
</description>
- <section>
- <title>DATA TYPES</title>
+ <section>
+ <title>Data Types</title>
<taglist>
<tag><marker id="erlang_char_encoding"/>erlang_char_encoding</tag>
<item>
- <p/>
<code type="none">
typedef enum {
ERLANG_ASCII = 1,
ERLANG_LATIN1 = 2,
ERLANG_UTF8 = 4
-}erlang_char_encoding;
-</code>
- <p>The character encodings used for atoms. <c>ERLANG_ASCII</c> represents 7-bit ASCII.
- Latin1 and UTF8 are different extensions of 7-bit ASCII. All 7-bit ASCII characters
- are valid Latin1 and UTF8 characters. ASCII and Latin1 both represent each character
- by one byte. A UTF8 character can consist of one to four bytes. Note that these
- constants are bit-flags and can be combined with bitwise-or.</p>
+} erlang_char_encoding;</code>
+ <p>The character encodings used for atoms. <c>ERLANG_ASCII</c>
+ represents 7-bit ASCII. Latin-1 and UTF-8 are different extensions
+ of 7-bit ASCII. All 7-bit ASCII characters are valid Latin-1 and
+ UTF-8 characters. ASCII and Latin-1 both represent each character
+ by one byte. An UTF-8 character can consist of 1-4 bytes.
+ Notice that these constants are bit-flags and can be combined with
+ bitwise OR.</p>
</item>
</taglist>
</section>
+
<funcs>
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_atom(const char *buf, int *index, char *p)</nametext></name>
+ <fsummary>Decode an atom.</fsummary>
+ <desc>
+ <p>Decodes an atom from the binary format. The <c>NULL</c>-terminated
+ name of the atom is placed at <c>p</c>. At most
+ <c>MAXATOMLEN</c> bytes can be placed in the buffer.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_atom_as(const char *buf, int *index, char *p, int plen, erlang_char_encoding want, erlang_char_encoding* was, erlang_char_encoding* result)</nametext></name>
+ <fsummary>Decode an atom.</fsummary>
+ <desc>
+ <p>Decodes an atom from the binary format. The <c>NULL</c>-terminated
+ name of the atom is placed in buffer at <c>p</c> of length <c>plen</c>
+ bytes.</p>
+ <p>The wanted string encoding is specified by
+ <seealso marker="#erlang_char_encoding"><c>want</c></seealso>.
+ The original encoding used in the binary format (Latin-1 or UTF-8) can
+ be obtained from <c>*was</c>. The encoding of the resulting string
+ (7-bit ASCII, Latin-1, or UTF-8) can be obtained from <c>*result</c>.
+ Both <c>was</c> and <c>result</c> can be <c>NULL</c>. <c>*result</c>
+ can differ from <c>want</c> if <c>want</c> is a bitwise OR'd
+ combination like <c>ERLANG_LATIN1|ERLANG_UTF8</c> or if
+ <c>*result</c> turns out to be pure 7-bit ASCII
+ (compatible with both Latin-1 and UTF-8).</p>
+ <p>This function fails if the atom is too long for the buffer
+ or if it cannot be represented with encoding <c>want</c>.</p>
+ <p>This function was introduced in Erlang/OTP R16 as part of a first
+ step to support UTF-8 atoms.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_bignum(const char *buf, int *index, mpz_t obj)</nametext></name>
+ <fsummary>Decode a GMP arbitrary precision integer.</fsummary>
+ <desc>
+ <p>Decodes an integer in the binary format to a GMP
+ <c>mpz_t</c> integer. To use this function, the <c>ei</c>
+ library must be configured and compiled to use the GMP library.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_binary(const char *buf, int *index, void *p, long *len)</nametext></name>
+ <fsummary>Decode a binary.</fsummary>
+ <desc>
+ <p>Decodes a binary from the binary format. Parameter
+ <c>len</c> is set to the actual size of the
+ binary. Notice that <c>ei_decode_binary()</c> assumes that
+ there is enough room for the binary. The size required can be
+ fetched by <c>ei_get_type()</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_boolean(const char *buf, int *index, int *p)</nametext></name>
+ <fsummary>Decode a boolean.</fsummary>
+ <desc>
+ <p>Decodes a boolean value from the binary format.
+ A boolean is actually an atom, <c>true</c> decodes 1
+ and <c>false</c> decodes 0.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_char(const char *buf, int *index, char *p)</nametext></name>
+ <fsummary>Decode an 8-bit integer between 0-255.</fsummary>
+ <desc>
+ <p>Decodes a char (8-bit) integer between 0-255 from the binary format.
+ For historical reasons the returned integer is of
+ type <c>char</c>. Your C code is to consider the
+ returned value to be of type <c>unsigned char</c> even if
+ the C compilers and system can define <c>char</c> to be
+ signed.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_double(const char *buf, int *index, double *p)</nametext></name>
+ <fsummary>Decode a double.</fsummary>
+ <desc>
+ <p>Decodes a double-precision (64-bit) floating
+ point number from the binary format.</p>
+ </desc>
+ </func>
+
<func>
- <name><ret>void</ret><nametext>ei_set_compat_rel(release_number)</nametext></name>
- <fsummary>Set the ei library in compatibility mode</fsummary>
- <type>
- <v>unsigned release_number;</v>
- </type>
+ <name><ret>int</ret><nametext>ei_decode_ei_term(const char* buf, int* index, ei_term* term)</nametext></name>
+ <fsummary>Decode a term, without previous knowledge of type.</fsummary>
+ <desc>
+ <p>Decodes any term, or at least tries to. If the term
+ pointed at by <c>*index</c> in <c>buf</c> fits
+ in the <c>term</c> union, it is decoded, and the
+ appropriate field in <c>term->value</c> is set, and
+ <c>*index</c> is incremented by the term size.</p>
+ <p>The function returns <c>1</c> on successful decoding, <c>-1</c> on
+ error, and <c>0</c> if the term seems alright, but does not fit in the
+ <c>term</c> structure. If <c>1</c> is returned, the
+ <c>index</c> is incremented, and <c>term</c>
+ contains the decoded term.</p>
+ <p>The <c>term</c> structure contains the arity for a tuple
+ or list, size for a binary, string, or atom. It contains
+ a term if it is any of the following: integer, float, atom,
+ pid, port, or ref.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_fun(const char *buf, int *index, erlang_fun *p)</nametext></name>
+ <name><ret>void</ret><nametext>free_fun(erlang_fun* f)</nametext></name>
+ <fsummary>Decode a fun.</fsummary>
<desc>
- <marker id="ei_set_compat_rel"></marker>
- <p>By default, the <c><![CDATA[ei]]></c> library is only guaranteed
- to be compatible with other Erlang/OTP components from the same
- release as the <c><![CDATA[ei]]></c> library itself. For example, <c><![CDATA[ei]]></c> from
- the OTP R10 release is not compatible with an Erlang emulator
- from the OTP R9 release by default.</p>
- <p>A call to <c><![CDATA[ei_set_compat_rel(release_number)]]></c> sets the
- <c><![CDATA[ei]]></c> library in compatibility mode of release
- <c><![CDATA[release_number]]></c>. Valid range of <c><![CDATA[release_number]]></c>
- is [7, current release]. This makes it possible to
- communicate with Erlang/OTP components from earlier releases.</p>
- <note>
- <p>If this function is called, it may only be called once
- and must be called before any other functions in the <c><![CDATA[ei]]></c>
- library is called.</p>
- </note>
- <warning>
- <p>You may run into trouble if this feature is used
- carelessly. Always make sure that all communicating
- components are either from the same Erlang/OTP release, or
- from release X and release Y where all components
- from release Y are in compatibility mode of release X.</p>
- </warning>
+ <p>Decodes a fun from the binary format. Parameter
+ <c>p</c> is to be <c>NULL</c> or point to an
+ <c>erlang_fun</c> structure. This is the only decode
+ function that allocates memory. When the <c>erlang_fun</c>
+ is no longer needed, it is to be freed with
+ <c>free_fun</c>. (This has to do with the arbitrary size
+ of the environment for a fun.)</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_version(char *buf, int *index)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_version(ei_x_buff* x)</nametext></name>
- <fsummary>Encode version</fsummary>
+ <name><ret>int</ret><nametext>ei_decode_list_header(const char *buf, int *index, int *arity)</nametext></name>
+ <fsummary>Decode a list.</fsummary>
<desc>
- <p>Encodes a version magic number for the binary format. Must
- be the first token in a binary term.</p>
+ <p>Decodes a list header from the binary
+ format. The number of elements is returned in
+ <c>arity</c>. The <c>arity+1</c> elements
+ follow (the last one is the tail of the list, normally an empty list).
+ If <c>arity</c> is <c>0</c>, it is an empty
+ list.</p>
+ <p>Notice that lists are encoded as strings if they consist
+ entirely of integers in the range 0..255. This function do
+ not decode such strings, use <c>ei_decode_string()</c>
+ instead.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_long(char *buf, int *index, long p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_long(ei_x_buff* x, long p)</nametext></name>
- <fsummary>Encode integer</fsummary>
+ <name><ret>int</ret><nametext>ei_decode_long(const char *buf, int *index, long *p)</nametext></name>
+ <fsummary>Decode integer.</fsummary>
<desc>
- <p>Encodes a long integer in the binary format.
- Note that if the code is 64 bits the function ei_encode_long() is
- exactly the same as ei_encode_longlong().</p>
+ <p>Decodes a long integer from the binary format.
+ If the code is 64 bits, the function <c>ei_decode_long()</c> is
+ the same as <c>ei_decode_longlong()</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_ulong(char *buf, int *index, unsigned long p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_ulong(ei_x_buff* x, unsigned long p)</nametext></name>
- <fsummary>Encode unsigned integer</fsummary>
+ <name><ret>int</ret><nametext>ei_decode_longlong(const char *buf, int *index, long long *p)</nametext></name>
+ <fsummary>Decode integer.</fsummary>
<desc>
- <p>Encodes an unsigned long integer in the binary format.
- Note that if the code is 64 bits the function ei_encode_ulong() is
- exactly the same as ei_encode_ulonglong().</p>
+ <p>Decodes a GCC <c>long long</c> or Visual C++
+ <c>__int64</c>
+ (64-bit) integer from the binary format. This
+ function is missing in the VxWorks port.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_longlong(char *buf, int *index, long long p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_longlong(ei_x_buff* x, long long p)</nametext></name>
- <fsummary>Encode integer</fsummary>
+ <name><ret>int</ret><nametext>ei_decode_map_header(const char *buf, int *index, int *arity)</nametext></name>
+ <fsummary>Decode a map.</fsummary>
<desc>
- <p>Encodes a GCC <c><![CDATA[long long]]></c> or Visual C++ <c><![CDATA[__int64]]></c> (64 bit)
- integer in the binary format. Note that this function is missing
- in the VxWorks port.</p>
+ <p>Decodes a map header from the binary
+ format. The number of key-value pairs is returned in
+ <c>*arity</c>. Keys and values follow in this order:
+ <c>K1, V1, K2, V2, ..., Kn, Vn</c>. This makes a total of
+ <c>arity*2</c> terms. If <c>arity</c> is zero, it is an empty map.
+ A correctly encoded map does not have duplicate keys.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_ulonglong(char *buf, int *index, unsigned long long p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_ulonglong(ei_x_buff* x, unsigned long long p)</nametext></name>
- <fsummary>Encode unsigned integer</fsummary>
+ <name><ret>int</ret><nametext>ei_decode_pid(const char *buf, int *index, erlang_pid *p)</nametext></name>
+ <fsummary>Decode a <c>pid</c>.</fsummary>
<desc>
- <p>Encodes a GCC <c><![CDATA[unsigned long long]]></c> or Visual C++ <c><![CDATA[unsigned __int64]]></c> (64 bit) integer in the binary format. Note that
- this function is missing in the VxWorks port.</p>
+ <p>Decodes a process identifier (pid) from the binary format.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_bignum(char *buf, int *index, mpz_t obj)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_bignum(ei_x_buff *x, mpz_t obj)</nametext></name>
- <fsummary>Encode an arbitrary precision integer</fsummary>
+ <name><ret>int</ret><nametext>ei_decode_port(const char *buf, int *index, erlang_port *p)</nametext></name>
+ <fsummary>Decode a port.</fsummary>
<desc>
- <p>Encodes a GMP <c><![CDATA[mpz_t]]></c> integer to binary format.
- To use this function the ei library needs to be configured and compiled
- to use the GMP library. </p>
+ <p>Decodes a port identifier from the binary format.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_double(char *buf, int *index, double p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_double(ei_x_buff* x, double p)</nametext></name>
- <fsummary>Encode a double float</fsummary>
+ <name><ret>int</ret><nametext>ei_decode_ref(const char *buf, int *index, erlang_ref *p)</nametext></name>
+ <fsummary>Decode a reference.</fsummary>
<desc>
- <p>Encodes a double-precision (64 bit) floating point number in
- the binary format.</p>
- <p>
- The function returns <c><![CDATA[-1]]></c> if the floating point number is not finite.
- </p>
+ <p>Decodes a reference from the binary format.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_boolean(char *buf, int *index, int p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_boolean(ei_x_buff* x, int p)</nametext></name>
- <fsummary>Encode a boolean</fsummary>
+ <name><ret>int</ret><nametext>ei_decode_string(const char *buf, int *index, char *p)</nametext></name>
+ <fsummary>Decode a string.</fsummary>
<desc>
- <p>Encodes a boolean value, as the atom <c><![CDATA[true]]></c> if p is not
- zero or <c><![CDATA[false]]></c> if p is zero.</p>
+ <p>Decodes a string from the binary format. A
+ string in Erlang is a list of integers between 0 and
+ 255. Notice that as the string is just a list, sometimes
+ lists are encoded as strings by <c>term_to_binary/1</c>,
+ even if it was not intended.</p>
+ <p>The string is copied to <c>p</c>, and enough space must
+ be allocated. The returned string is <c>NULL</c>-terminated, so you
+ must add an extra byte to the memory requirement.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_char(char *buf, int *index, char p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_char(ei_x_buff* x, char p)</nametext></name>
- <fsummary>Encode an 8-bit integer between 0-255</fsummary>
+ <name><ret>int</ret><nametext>ei_decode_term(const char *buf, int *index, void *t)</nametext></name>
+ <fsummary>Decode a <c>ETERM</c>.</fsummary>
+ <desc>
+ <p>Decodes a term from the binary format. The term
+ is return in <c>t</c> as a <c>ETERM*</c>, so
+ <c>t</c> is actually an <c>ETERM**</c> (see
+ <seealso marker="erl_eterm"><c>erl_eterm</c></seealso>).
+ The term is later to be deallocated.</p>
+ <p>Notice that this function is located in the <c>Erl_Interface</c>
+ library.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_trace(const char *buf, int *index, erlang_trace *p)</nametext></name>
+ <fsummary>Decode a trace token.</fsummary>
<desc>
- <p>Encodes a char (8-bit) as an integer between 0-255 in the binary format.
- Note that for historical reasons the integer argument is of
- type <c><![CDATA[char]]></c>. Your C code should consider the
- given argument to be of type <c><![CDATA[unsigned char]]></c> even if
- the C compilers and system may define <c><![CDATA[char]]></c> to be
- signed.</p>
+ <p>Decodes an Erlang trace token from the binary format.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_string(char *buf, int *index, const char *p)</nametext></name>
- <name><ret>int</ret><nametext>ei_encode_string_len(char *buf, int *index, const char *p, int len)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_string(ei_x_buff* x, const char *p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_string_len(ei_x_buff* x, const char* s, int len)</nametext></name>
- <fsummary>Encode a string</fsummary>
+ <name><ret>int</ret><nametext>ei_decode_tuple_header(const char *buf, int *index, int *arity)</nametext></name>
+ <fsummary>Decode a tuple.</fsummary>
<desc>
- <p>Encodes a string in the binary format. (A string in erlang
- is a list, but is encoded as a character array in the binary
- format.) The string should be zero-terminated, except for
- the <c><![CDATA[ei_x_encode_string_len()]]></c> function.</p>
+ <p>Decodes a tuple header, the number of elements
+ is returned in <c>arity</c>. The tuple elements follow
+ in order in the buffer.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_ulong(const char *buf, int *index, unsigned long *p)</nametext></name>
+ <fsummary>Decode unsigned integer.</fsummary>
+ <desc>
+ <p>Decodes an unsigned long integer from the binary format.
+ If the code is 64 bits, the function <c>ei_decode_ulong()</c> is
+ the same as <c>ei_decode_ulonglong()</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_ulonglong(const char *buf, int *index, unsigned long long *p)</nametext></name>
+ <fsummary>Decode unsigned integer.</fsummary>
+ <desc>
+ <p>Decodes a GCC <c>unsigned long long</c> or Visual C++
+ <c>unsigned __int64</c> (64-bit) integer from the binary
+ format. This function is missing in the VxWorks port.</p>
</desc>
</func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_decode_version(const char *buf, int *index, int *version)</nametext></name>
+ <fsummary>Decode an empty list (<c>nil</c>).</fsummary>
+ <desc>
+ <p>Decodes the version magic number for the
+ Erlang binary term format. It must be the first token in a
+ binary term.</p>
+ </desc>
+ </func>
+
<func>
<name><ret>int</ret><nametext>ei_encode_atom(char *buf, int *index, const char *p)</nametext></name>
<name><ret>int</ret><nametext>ei_encode_atom_len(char *buf, int *index, const char *p, int len)</nametext></name>
<name><ret>int</ret><nametext>ei_x_encode_atom(ei_x_buff* x, const char *p)</nametext></name>
<name><ret>int</ret><nametext>ei_x_encode_atom_len(ei_x_buff* x, const char *p, int len)</nametext></name>
- <fsummary>Encode an atom</fsummary>
+ <fsummary>Encode an atom.</fsummary>
<desc>
- <p>Encodes an atom in the binary format. The <c><![CDATA[p]]></c> parameter
- is the name of the atom in latin1 encoding. Only upto <c>MAXATOMLEN-1</c> bytes
- are encoded. The name should be zero-terminated, except for
- the <c><![CDATA[ei_x_encode_atom_len()]]></c> function.</p>
+ <p>Encodes an atom in the binary format. Parameter <c>p</c>
+ is the name of the atom in Latin-1 encoding. Only up to
+ <c>MAXATOMLEN-1</c> bytes
+ are encoded. The name is to be <c>NULL</c>-terminated, except for
+ the <c>ei_x_encode_atom_len()</c> function.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_encode_atom_as(char *buf, int *index, const char *p, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name>
<name><ret>int</ret><nametext>ei_encode_atom_len_as(char *buf, int *index, const char *p, int len, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name>
<name><ret>int</ret><nametext>ei_x_encode_atom_as(ei_x_buff* x, const char *p, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name>
<name><ret>int</ret><nametext>ei_x_encode_atom_len_as(ei_x_buff* x, const char *p, int len, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name>
- <fsummary>Encode an atom</fsummary>
+ <fsummary>Encode an atom.</fsummary>
<desc>
<p>Encodes an atom in the binary format with character encoding
- <seealso marker="#erlang_char_encoding"><c>to_enc</c></seealso> (latin1 or utf8).
- The <c>p</c> parameter is the name of the atom with character encoding
- <seealso marker="#erlang_char_encoding"><c>from_enc</c></seealso> (ascii, latin1 or utf8).
- The name must either be zero-terminated or a function variant with a <c>len</c>
- parameter must be used. If <c>to_enc</c> is set to the bitwise-or'd combination
- <c>(ERLANG_LATIN1|ERLANG_UTF8)</c>, utf8 encoding is only used if the atom string
- can not be represented in latin1 encoding.</p>
- <p>The encoding will fail if <c>p</c> is not a valid string in encoding <c>from_enc</c>,
- if the string is too long or if it can not be represented with character encoding <c>to_enc</c>.</p>
- <p>These functions were introduced in R16 release of Erlang/OTP as part of a first step
- to support UTF8 atoms. Atoms encoded with <c>ERLANG_UTF8</c>
- can not be decoded by earlier releases than R16.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_encode_binary(char *buf, int *index, const void *p, long len)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_binary(ei_x_buff* x, const void *p, long len)</nametext></name>
- <fsummary>Encode a binary</fsummary>
- <desc>
- <p>Encodes a binary in the binary format. The data is at
- <c><![CDATA[p]]></c>, of <c><![CDATA[len]]></c> bytes length.</p>
+ <seealso marker="#erlang_char_encoding"><c>to_enc</c></seealso>
+ (Latin-1 or UTF-8). Parameter <c>p</c> is the name of the atom with
+ character encoding
+ <seealso marker="#erlang_char_encoding"><c>from_enc</c></seealso>
+ (ASCII, Latin-1, or UTF-8). The name must either be <c>NULL</c>-terminated or
+ a function variant with a <c>len</c> parameter must be used.
+ If <c>to_enc</c> is set to the bitwise OR'd combination
+ <c>(ERLANG_LATIN1|ERLANG_UTF8)</c>, UTF-8 encoding is only used if the
+ atom string cannot be represented in Latin-1 encoding.</p>
+ <p>The encoding fails if <c>p</c> is an invalid string in encoding
+ <c>from_enc</c>, if the string is too long, or if it cannot be
+ represented with character encoding <c>to_enc</c>.</p>
+ <p>These functions were introduced in Erlang/OTP R16 as part of a first
+ step to support UTF-8 atoms. Atoms encoded with <c>ERLANG_UTF8</c>
+ cannot be decoded by earlier releases than R16.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_pid(char *buf, int *index, const erlang_pid *p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_pid(ei_x_buff* x, const erlang_pid *p)</nametext></name>
- <fsummary>Encode a pid</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_bignum(char *buf, int *index, mpz_t obj)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_bignum(ei_x_buff *x, mpz_t obj)</nametext></name>
+ <fsummary>Encode an arbitrary precision integer.</fsummary>
<desc>
- <p>Encodes an erlang process identifier, pid, in the binary
- format. The <c><![CDATA[p]]></c> parameter points to an
- <c><![CDATA[erlang_pid]]></c> structure (which should have been obtained
- earlier with <c><![CDATA[ei_decode_pid()]]></c>).</p>
+ <p>Encodes a GMP <c>mpz_t</c> integer to binary format.
+ To use this function, the <c>ei</c> library must be configured and
+ compiled to use the GMP library.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_fun(char *buf, int *index, const erlang_fun *p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_fun(ei_x_buff* x, const erlang_fun* fun)</nametext></name>
- <fsummary>Encode a fun</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_binary(char *buf, int *index, const void *p, long len)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_binary(ei_x_buff* x, const void *p, long len)</nametext></name>
+ <fsummary>Encode a binary.</fsummary>
<desc>
- <p>Encodes a fun in the binary format. The <c><![CDATA[p]]></c> parameter
- points to an <c><![CDATA[erlang_fun]]></c> structure. The
- <c><![CDATA[erlang_fun]]></c> is not freed automatically, the
- <c><![CDATA[free_fun]]></c> should be called if the fun is not needed
- after encoding.</p>
+ <p>Encodes a binary in the binary format. The data is at
+ <c>p</c>, of <c>len</c> bytes length.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_port(char *buf, int *index, const erlang_port *p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_port(ei_x_buff* x, const erlang_port *p)</nametext></name>
- <fsummary>Encodes a port</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_boolean(char *buf, int *index, int p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_boolean(ei_x_buff* x, int p)</nametext></name>
+ <fsummary>Encode a boolean.</fsummary>
<desc>
- <p>Encodes an erlang port in the binary format. The <c><![CDATA[p]]></c>
- parameter points to a <c><![CDATA[erlang_port]]></c> structure (which
- should have been obtained earlier with
- <c><![CDATA[ei_decode_port()]]></c>.</p>
+ <p>Encodes a boolean value as the atom <c>true</c> if
+ <c>p</c> is not zero, or <c>false</c> if <c>p</c> is
+ zero.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_ref(char *buf, int *index, const erlang_ref *p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_ref(ei_x_buff* x, const erlang_ref *p)</nametext></name>
- <fsummary>Encodes a ref</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_char(char *buf, int *index, char p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_char(ei_x_buff* x, char p)</nametext></name>
+ <fsummary>Encode an 8-bit integer between 0-255.</fsummary>
<desc>
- <p>Encodes an erlang reference in the binary format. The
- <c><![CDATA[p]]></c> parameter points to a <c><![CDATA[erlang_ref]]></c> structure
- (which should have been obtained earlier with
- <c><![CDATA[ei_decode_ref()]]></c>.</p>
+ <p>Encodes a char (8-bit) as an integer between 0-255 in the binary
+ format. For historical reasons the integer argument is of
+ type <c>char</c>. Your C code is to consider the specified
+ argument to be of type <c>unsigned char</c> even if
+ the C compilers and system may define <c>char</c> to be
+ signed.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_term(char *buf, int *index, void *t)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_term(ei_x_buff* x, void *t)</nametext></name>
- <fsummary>Encode an <c><![CDATA[erl_interface]]></c>term</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_double(char *buf, int *index, double p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_double(ei_x_buff* x, double p)</nametext></name>
+ <fsummary>Encode a double float.</fsummary>
<desc>
- <p>This function encodes an <c><![CDATA[ETERM]]></c>, as obtained from
- <c><![CDATA[erl_interface]]></c>. The <c><![CDATA[t]]></c> parameter is actually an
- <c><![CDATA[ETERM]]></c> pointer. This function doesn't free the
- <c><![CDATA[ETERM]]></c>.</p>
+ <p>Encodes a double-precision (64-bit) floating point number in
+ the binary format.</p>
+ <p>Returns <c>-1</c> if the floating point
+ number is not finite.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_trace(char *buf, int *index, const erlang_trace *p)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_trace(ei_x_buff* x, const erlang_trace *p)</nametext></name>
- <fsummary>Encode a trace token</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_empty_list(char* buf, int* index)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_empty_list(ei_x_buff* x)</nametext></name>
+ <fsummary>Encode an empty list (<c>nil</c>).</fsummary>
<desc>
- <p>This function encodes an erlang trace token in the binary
- format. The <c><![CDATA[p]]></c> parameter points to a
- <c><![CDATA[erlang_trace]]></c> structure (which should have been
- obtained earlier with <c><![CDATA[ei_decode_trace()]]></c>.</p>
+ <p>Encodes an empty list. It is often used at the tail of a list.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_tuple_header(char *buf, int *index, int arity)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_tuple_header(ei_x_buff* x, int arity)</nametext></name>
- <fsummary>Encode a tuple</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_fun(char *buf, int *index, const erlang_fun *p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_fun(ei_x_buff* x, const erlang_fun* fun)</nametext></name>
+ <fsummary>Encode a fun.</fsummary>
<desc>
- <p>This function encodes a tuple header, with a specified
- arity. The next <c><![CDATA[arity]]></c> terms encoded will be the
- elements of the tuple. Tuples and lists are encoded
- recursively, so that a tuple may contain another tuple or
- list.</p>
- <p>E.g. to encode the tuple <c><![CDATA[{a, {b, {}}}]]></c>:</p>
- <pre>
-ei_encode_tuple_header(buf, &amp;i, 2);
-ei_encode_atom(buf, &amp;i, "a");
-ei_encode_tuple_header(buf, &amp;i, 2);
-ei_encode_atom(buf, &amp;i, "b");
-ei_encode_tuple_header(buf, &amp;i, 0);
- </pre>
+ <p>Encodes a fun in the binary format. Parameter <c>p</c>
+ points to an <c>erlang_fun</c> structure. The
+ <c>erlang_fun</c> is not freed automatically, the
+ <c>free_fun</c> is to be called if the fun is not needed
+ after encoding.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_encode_list_header(char *buf, int *index, int arity)</nametext></name>
<name><ret>int</ret><nametext>ei_x_encode_list_header(ei_x_buff* x, int arity)</nametext></name>
- <fsummary>Encode a list</fsummary>
+ <fsummary>Encode a list.</fsummary>
<desc>
- <p>This function encodes a list header, with a specified
- arity. The next <c><![CDATA[arity+1]]></c> terms are the elements
- (actually its <c><![CDATA[arity]]></c> cons cells) and the tail of the
+ <p>Encodes a list header, with a specified
+ arity. The next <c>arity+1</c> terms are the elements
+ (actually its <c>arity</c> cons cells) and the tail of the
list. Lists and tuples are encoded recursively, so that a
- list may contain another list or tuple.</p>
- <p>E.g. to encode the list <c><![CDATA[[c, d, [e | f]]]]></c>:</p>
+ list can contain another list or tuple.</p>
+ <p>For example, to encode the list
+ <c>[c, d, [e | f]]</c>:</p>
<pre>
ei_encode_list_header(buf, &amp;i, 3);
ei_encode_atom(buf, &amp;i, "c");
@@ -392,14 +539,13 @@ ei_encode_atom(buf, &amp;i, "d");
ei_encode_list_header(buf, &amp;i, 1);
ei_encode_atom(buf, &amp;i, "e");
ei_encode_atom(buf, &amp;i, "f");
-ei_encode_empty_list(buf, &amp;i);
- </pre>
+ei_encode_empty_list(buf, &amp;i);</pre>
<note>
<p>It may seem that there is no way to create a list without
knowing the number of elements in advance. But indeed
- there is a way. Note that the list <c><![CDATA[[a, b, c]]]></c> can be
- written as <c><![CDATA[[a | [b | [c]]]]]></c>. Using this, a list can
- be written as conses.</p>
+ there is a way. Notice that the list <c>[a, b, c]</c>
+ can be written as <c>[a | [b | [c]]]</c>.
+ Using this, a list can be written as conses.</p>
</note>
<p>To encode a list, without knowing the arity in advance:</p>
<pre>
@@ -407,425 +553,350 @@ while (something()) {
ei_x_encode_list_header(&amp;x, 1);
ei_x_encode_ulong(&amp;x, i); /* just an example */
}
-ei_x_encode_empty_list(&amp;x);
- </pre>
+ei_x_encode_empty_list(&amp;x);</pre>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_encode_empty_list(char* buf, int* index)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_encode_empty_list(ei_x_buff* x)</nametext></name>
- <fsummary>Encode an empty list (<c><![CDATA[nil]]></c>)</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_long(char *buf, int *index, long p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_long(ei_x_buff* x, long p)</nametext></name>
+ <fsummary>Encode integer.</fsummary>
<desc>
- <p>This function encodes an empty list. It's often used at the
- tail of a list.</p>
+ <p>Encodes a long integer in the binary format.
+ If the code is 64 bits, the function <c>ei_encode_long()</c> is
+ the same as <c>ei_encode_longlong()</c>.</p>
</desc>
</func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_encode_longlong(char *buf, int *index, long long p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_longlong(ei_x_buff* x, long long p)</nametext></name>
+ <fsummary>Encode integer.</fsummary>
+ <desc>
+ <p>Encodes a GCC <c>long long</c> or Visual C++
+ <c>__int64</c> (64-bit) integer in the binary format.
+ This function is missing in the VxWorks port.</p>
+ </desc>
+ </func>
+
<func>
<name><ret>int</ret><nametext>ei_encode_map_header(char *buf, int *index, int arity)</nametext></name>
<name><ret>int</ret><nametext>ei_x_encode_map_header(ei_x_buff* x, int arity)</nametext></name>
- <fsummary>Encode a map</fsummary>
+ <fsummary>Encode a map.</fsummary>
<desc>
- <p>This function encodes a map header, with a specified arity. The next
+ <p>Encodes a map header, with a specified arity. The next
<c>arity*2</c> terms encoded will be the keys and values of the map
encoded in the following order: <c>K1, V1, K2, V2, ..., Kn, Vn</c>.
</p>
- <p>E.g. to encode the map <c>#{a => "Apple", b => "Banana"}</c>:</p>
+ <p>For example, to encode the map <c>#{a => "Apple", b =>
+ "Banana"}</c>:</p>
<pre>
ei_x_encode_map_header(&amp;x, 2);
ei_x_encode_atom(&amp;x, "a");
ei_x_encode_string(&amp;x, "Apple");
ei_x_encode_atom(&amp;x, "b");
-ei_x_encode_string(&amp;x, "Banana");
- </pre>
- <p>A correctly encoded map can not have duplicate keys.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_get_type(const char *buf, const int *index, int *type, int *size)</nametext></name>
- <fsummary>Fetch the type and size of an encoded term</fsummary>
- <desc>
- <p>This function returns the type in <c><![CDATA[type]]></c> and size in
- <c><![CDATA[size]]></c> of the encoded term.
- For strings and atoms, size
- is the number of characters <em>not</em> including the
- terminating 0. For binaries, <c><![CDATA[size]]></c> is the number of
- bytes. For lists and tuples, <c><![CDATA[size]]></c> is the arity of the
- object. For other types, <c><![CDATA[size]]></c> is 0. In all cases,
- <c><![CDATA[index]]></c> is left unchanged.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_decode_version(const char *buf, int *index, int *version)</nametext></name>
- <fsummary>Encode an empty list (<c><![CDATA[nil]]></c>)</fsummary>
- <desc>
- <p>This function decodes the version magic number for the
- erlang binary term format. It must be the first token in a
- binary term.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_decode_long(const char *buf, int *index, long *p)</nametext></name>
- <fsummary>Decode integer</fsummary>
- <desc>
- <p>This function decodes a long integer from the binary format.
- Note that if the code is 64 bits the function ei_decode_long() is
- exactly the same as ei_decode_longlong().</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_decode_ulong(const char *buf, int *index, unsigned long *p)</nametext></name>
- <fsummary>Decode unsigned integer</fsummary>
- <desc>
- <p>This function decodes an unsigned long integer from
- the binary format.
- Note that if the code is 64 bits the function ei_decode_ulong() is
- exactly the same as ei_decode_ulonglong().</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_decode_longlong(const char *buf, int *index, long long *p)</nametext></name>
- <fsummary>Decode integer</fsummary>
- <desc>
- <p>This function decodes a GCC <c><![CDATA[long long]]></c> or Visual C++ <c><![CDATA[__int64]]></c>
- (64 bit) integer from the binary format. Note that this
- function is missing in the VxWorks port.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_decode_ulonglong(const char *buf, int *index, unsigned long long *p)</nametext></name>
- <fsummary>Decode unsigned integer</fsummary>
- <desc>
- <p>This function decodes a GCC <c><![CDATA[unsigned long long]]></c> or Visual C++
- <c><![CDATA[unsigned __int64]]></c> (64 bit) integer from the binary format.
- Note that this function is missing in the VxWorks port.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_decode_bignum(const char *buf, int *index, mpz_t obj)</nametext></name>
- <fsummary>Decode a GMP arbitrary precision integer</fsummary>
- <desc>
- <p>This function decodes an integer in the binary format to a GMP <c><![CDATA[mpz_t]]></c> integer.
- To use this function the ei library needs to be configured and compiled
- to use the GMP library. </p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_decode_double(const char *buf, int *index, double *p)</nametext></name>
- <fsummary>Decode a double</fsummary>
- <desc>
- <p>This function decodes an double-precision (64 bit) floating
- point number from the binary format.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_decode_boolean(const char *buf, int *index, int *p)</nametext></name>
- <fsummary>Decode a boolean</fsummary>
- <desc>
- <p>This function decodes a boolean value from the binary
- format. A boolean is actually an atom, <c><![CDATA[true]]></c> decodes 1
- and <c><![CDATA[false]]></c> decodes 0.</p>
+ei_x_encode_string(&amp;x, "Banana");</pre>
+ <p>A correctly encoded map cannot have duplicate keys.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_char(const char *buf, int *index, char *p)</nametext></name>
- <fsummary>Decode an 8-bit integer between 0-255</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_pid(char *buf, int *index, const erlang_pid *p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_pid(ei_x_buff* x, const erlang_pid *p)</nametext></name>
+ <fsummary>Encode a pid.</fsummary>
<desc>
- <p>This function decodes a char (8-bit) integer between 0-255
- from the binary format.
- Note that for historical reasons the returned integer is of
- type <c><![CDATA[char]]></c>. Your C code should consider the
- returned value to be of type <c><![CDATA[unsigned char]]></c> even if
- the C compilers and system may define <c><![CDATA[char]]></c> to be
- signed.</p>
+ <p>Encodes an Erlang process identifier (pid) in the binary
+ format. Parameter <c>p</c> points to an
+ <c>erlang_pid</c> structure (which should have been
+ obtained earlier with <c>ei_decode_pid()</c>).</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_string(const char *buf, int *index, char *p)</nametext></name>
- <fsummary>Decode a string</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_port(char *buf, int *index, const erlang_port *p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_port(ei_x_buff* x, const erlang_port *p)</nametext></name>
+ <fsummary>Encode a port.</fsummary>
<desc>
- <p>This function decodes a string from the binary format. A
- string in erlang is a list of integers between 0 and
- 255. Note that since the string is just a list, sometimes
- lists are encoded as strings by <c><![CDATA[term_to_binary/1]]></c>,
- even if it was not intended.</p>
- <p>The string is copied to <c><![CDATA[p]]></c>, and enough space must be
- allocated. The returned string is null terminated so you
- need to add an extra byte to the memory requirement.</p>
+ <p>Encodes an Erlang port in the binary format. Parameter
+ <c>p</c> points to a <c>erlang_port</c>
+ structure (which should have been obtained earlier with
+ <c>ei_decode_port()</c>).</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_atom(const char *buf, int *index, char *p)</nametext></name>
- <fsummary>Decode an atom</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_ref(char *buf, int *index, const erlang_ref *p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_ref(ei_x_buff* x, const erlang_ref *p)</nametext></name>
+ <fsummary>Encode a ref.</fsummary>
<desc>
- <p>This function decodes an atom from the binary format. The
- null terminated name of the atom is placed at <c><![CDATA[p]]></c>. There can be at most
- <c><![CDATA[MAXATOMLEN]]></c> bytes placed in the buffer.</p>
+ <p>Encodes an Erlang reference in the binary format. Parameter
+ <c>p</c> points to a <c>erlang_ref</c>
+ structure (which should have been obtained earlier with
+ <c>ei_decode_ref()</c>).</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_atom_as(const char *buf, int *index, char *p, int plen, erlang_char_encoding want, erlang_char_encoding* was, erlang_char_encoding* result)</nametext></name>
- <fsummary>Decode an atom</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_string(char *buf, int *index, const char *p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_encode_string_len(char *buf, int *index, const char *p, int len)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_string(ei_x_buff* x, const char *p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_string_len(ei_x_buff* x, const char* s, int len)</nametext></name>
+ <fsummary>Encode a string.</fsummary>
<desc>
- <p>This function decodes an atom from the binary format. The
- null terminated name of the atom is placed in buffer at <c>p</c> of length
- <c>plen</c> bytes.</p>
- <p>The wanted string encoding is specified by <seealso marker="#erlang_char_encoding">
- <c>want</c></seealso>. The original encoding used in the
- binary format (latin1 or utf8) can be obtained from <c>*was</c>. The actual encoding of the resulting string
- (7-bit ascii, latin1 or utf8) can be obtained from <c>*result</c>. Both <c>was</c> and <c>result</c> can be <c>NULL</c>.
-
- <c>*result</c> may differ from <c>want</c> if <c>want</c> is a bitwise-or'd combination like
- <c>ERLANG_LATIN1|ERLANG_UTF8</c> or if <c>*result</c> turn out to be pure 7-bit ascii
- (compatible with both latin1 and utf8).</p>
- <p>This function fails if the atom is too long for the buffer
- or if it can not be represented with encoding <c>want</c>.</p>
- <p>This function was introduced in R16 release of Erlang/OTP as part of a first step
- to support UTF8 atoms.</p>
+ <p>Encodes a string in the binary format. (A string in Erlang
+ is a list, but is encoded as a character array in the binary
+ format.) The string is to be <c>NULL</c>-terminated, except for
+ the <c>ei_x_encode_string_len()</c> function.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_binary(const char *buf, int *index, void *p, long *len)</nametext></name>
- <fsummary>Decode a binary</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_term(char *buf, int *index, void *t)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_term(ei_x_buff* x, void *t)</nametext></name>
+ <fsummary>Encode an <c>erl_interface</c> term.</fsummary>
<desc>
- <p>This function decodes a binary from the binary format. The
- <c><![CDATA[len]]></c> parameter is set to the actual size of the
- binary. Note that <c><![CDATA[ei_decode_binary()]]></c> assumes that there
- are enough room for the binary. The size required can be
- fetched by <c><![CDATA[ei_get_type()]]></c>.</p>
+ <p>Encodes an <c>ETERM</c>, as obtained from
+ <c>erl_interface</c>. Parameter <c>t</c> is
+ actually an <c>ETERM</c> pointer. This function
+ does not free the <c>ETERM</c>.</p>
</desc>
</func>
<func>
- <name><ret>int</ret><nametext>ei_decode_fun(const char *buf, int *index, erlang_fun *p)</nametext></name>
- <name><ret>void</ret><nametext>free_fun(erlang_fun* f)</nametext></name>
- <fsummary>Decode a fun</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_trace(char *buf, int *index, const erlang_trace *p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_trace(ei_x_buff* x, const erlang_trace *p)</nametext></name>
+ <fsummary>Encode a trace token.</fsummary>
<desc>
- <p>This function decodes a fun from the binary format. The
- <c><![CDATA[p]]></c> parameter should be NULL or point to an
- <c><![CDATA[erlang_fun]]></c> structure. This is the only decode
- function that allocates memory; when the <c><![CDATA[erlang_fun]]></c>
- is no longer needed, it should be freed with
- <c><![CDATA[free_fun]]></c>. (This has to do with the arbitrary size of
- the environment for a fun.)</p>
+ <p>Encodes an Erlang trace token in the binary format.
+ Parameter <c>p</c> points to a
+ <c>erlang_trace</c> structure (which should have been
+ obtained earlier with <c>ei_decode_trace()</c>).</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_pid(const char *buf, int *index, erlang_pid *p)</nametext></name>
- <fsummary>Decode a <c><![CDATA[pid]]></c></fsummary>
+ <name><ret>int</ret><nametext>ei_encode_tuple_header(char *buf, int *index, int arity)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_tuple_header(ei_x_buff* x, int arity)</nametext></name>
+ <fsummary>Encode a tuple.</fsummary>
<desc>
- <p>Decodes a pid, process identifier, from the binary format.</p>
+ <p>Encodes a tuple header, with a specified
+ arity. The next <c>arity</c> terms encoded will be the
+ elements of the tuple. Tuples and lists are encoded
+ recursively, so that a tuple can contain another tuple or list.</p>
+ <p>For example, to encode the tuple <c>{a, {b, {}}}</c>:</p>
+ <pre>
+ei_encode_tuple_header(buf, &amp;i, 2);
+ei_encode_atom(buf, &amp;i, "a");
+ei_encode_tuple_header(buf, &amp;i, 2);
+ei_encode_atom(buf, &amp;i, "b");
+ei_encode_tuple_header(buf, &amp;i, 0);</pre>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_port(const char *buf, int *index, erlang_port *p)</nametext></name>
- <fsummary>Decode a port</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_ulong(char *buf, int *index, unsigned long p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_ulong(ei_x_buff* x, unsigned long p)</nametext></name>
+ <fsummary>Encode unsigned integer.</fsummary>
<desc>
- <p>This function decodes a port identifier from the binary
- format.</p>
+ <p>Encodes an unsigned long integer in the binary format.
+ If the code is 64 bits, the function <c>ei_encode_ulong()</c> is
+ the same as <c>ei_encode_ulonglong()</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_ref(const char *buf, int *index, erlang_ref *p)</nametext></name>
- <fsummary>Decode a reference</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_ulonglong(char *buf, int *index, unsigned long long p)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_ulonglong(ei_x_buff* x, unsigned long long p)</nametext></name>
+ <fsummary>Encode unsigned integer.</fsummary>
<desc>
- <p>This function decodes a reference from the binary format.</p>
+ <p>Encodes a GCC <c>unsigned long long</c> or Visual C++
+ <c>unsigned __int64</c> (64-bit) integer in the binary
+ format. This function is missing in the VxWorks port.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_trace(const char *buf, int *index, erlang_trace *p)</nametext></name>
- <fsummary>Decode a trace token</fsummary>
+ <name><ret>int</ret><nametext>ei_encode_version(char *buf, int *index)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_version(ei_x_buff* x)</nametext></name>
+ <fsummary>Encode version.</fsummary>
<desc>
- <p>Decodes an erlang trace token from the binary format.</p>
+ <p>Encodes a version magic number for the binary format. Must
+ be the first token in a binary term.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_tuple_header(const char *buf, int *index, int *arity)</nametext></name>
- <fsummary>Decode a tuple</fsummary>
+ <name><ret>int</ret><nametext>ei_get_type(const char *buf, const int *index, int *type, int *size)</nametext></name>
+ <fsummary>Fetch the type and size of an encoded term.</fsummary>
<desc>
- <p>This function decodes a tuple header, the number of elements
- is returned in <c><![CDATA[arity]]></c>. The tuple elements follows in order in
- the buffer.</p>
+ <p>Returns the type in <c>type</c> and size in
+ <c>size</c> of the encoded term. For strings and atoms,
+ size is the number of characters <em>not</em> including the
+ terminating <c>NULL</c>. For binaries, <c>size</c> is the number of
+ bytes. For lists and tuples, <c>size</c> is the arity of
+ the object. For other types, <c>size</c> is 0. In all
+ cases, <c>index</c> is left unchanged.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_list_header(const char *buf, int *index, int *arity)</nametext></name>
- <fsummary>Decode a list</fsummary>
- <desc>
- <p>This function decodes a list header from the binary
- format. The number of elements is returned in
- <c><![CDATA[arity]]></c>. The <c><![CDATA[arity+1]]></c> elements follows (the last
- one is the tail of the list, normally an empty list.) If
- <c><![CDATA[arity]]></c> is <c><![CDATA[0]]></c>, it's an empty list.</p>
- <p>Note that lists are encoded as strings, if they consist
- entirely of integers in the range 0..255. This function will
- not decode such strings, use <c><![CDATA[ei_decode_string()]]></c>
- instead.</p>
+ <name><ret>int</ret><nametext>ei_print_term(FILE* fp, const char* buf, int* index)</nametext></name>
+ <name><ret>int</ret><nametext>ei_s_print_term(char** s, const char* buf, int* index)</nametext></name>
+ <fsummary>Print a term in clear text.</fsummary>
+ <desc>
+ <p>Prints a term, in clear text, to the file
+ specified by <c>fp</c>, or the buffer pointed to by
+ <c>s</c>. It
+ tries to resemble the term printing in the Erlang shell.</p>
+ <p>In <c>ei_s_print_term()</c>, parameter
+ <c>s</c> is to
+ point to a dynamically (malloc) allocated string of
+ <c>BUFSIZ</c> bytes or a <c>NULL</c> pointer. The string
+ can be reallocated (and <c>*s</c> can be updated) by this
+ function if the result is more than <c>BUFSIZ</c>
+ characters. The string returned is <c>NULL</c>-terminated.</p>
+ <p>The return value is the number of characters written to the file
+ or string, or <c>-1</c> if <c>buf[index]</c> does not
+ contain a valid term.
+ Unfortunately, I/O errors on <c>fp</c> is not checked.</p>
+ <p>Argument <c>index</c> is updated, that is, this function
+ can be viewed as a decode function that decodes a term into a
+ human-readable format.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_map_header(const char *buf, int *index, int *arity)</nametext></name>
- <fsummary>Decode a map</fsummary>
+ <name><ret>void</ret><nametext>ei_set_compat_rel(release_number)</nametext></name>
+ <fsummary>Set the ei library in compatibility mode.</fsummary>
+ <type>
+ <v>unsigned release_number;</v>
+ </type>
<desc>
- <p>This function decodes a map header from the binary
- format. The number of key-value pairs is returned in
- <c>*arity</c>. Keys and values follow in the following order:
- <c>K1, V1, K2, V2, ..., Kn, Vn</c>. This makes a total of
- <c>arity*2</c> terms. If <c>arity</c> is zero, it's an empty map.
- A correctly encoded map does not have duplicate keys.</p>
+ <marker id="ei_set_compat_rel"></marker>
+ <p>By default, the <c>ei</c> library is only guaranteed
+ to be compatible with other Erlang/OTP components from the same
+ release as the <c>ei</c> library itself. For example,
+ <c>ei</c> from
+ Erlang/OTP R10 is not compatible with an Erlang emulator
+ from Erlang/OTP R9 by default.</p>
+ <p>A call to <c>ei_set_compat_rel(release_number)</c> sets
+ the <c>ei</c> library in compatibility mode of release
+ <c>release_number</c>. Valid range of
+ <c>release_number</c>
+ is <c>[7, current release]</c>. This makes it possible to
+ communicate with Erlang/OTP components from earlier releases.</p>
+ <note>
+ <p>If this function is called, it can only be called once
+ and must be called before any other functions in the
+ <c>ei</c> library are called.</p>
+ </note>
+ <warning>
+ <p>You can run into trouble if this feature is used
+ carelessly. Always ensure that all communicating
+ components are either from the same Erlang/OTP release, or
+ from release X and release Y where all components
+ from release Y are in compatibility mode of release X.</p>
+ </warning>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_decode_ei_term(const char* buf, int* index, ei_term* term)</nametext></name>
- <fsummary>Decode a term, without prior knowledge of type</fsummary>
+ <name><ret>int</ret><nametext>ei_skip_term(const char* buf, int* index)</nametext></name>
+ <fsummary>Skip a term.</fsummary>
<desc>
- <p>This function decodes any term, or at least tries to. If the
- term pointed at by <c><![CDATA[*index]]></c> in <c><![CDATA[buf]]></c> fits in the
- <c><![CDATA[term]]></c> union, it is decoded, and the appropriate field
- in <c><![CDATA[term->value]]></c> is set, and <c><![CDATA[*index]]></c> is
- incremented by the term size.</p>
- <p>The function returns 1 on successful decoding, -1 on error,
- and 0 if the term seems alright, but does not fit in the
- <c><![CDATA[term]]></c> structure. If it returns 1, the <c><![CDATA[index]]></c>
- will be incremented, and the <c><![CDATA[term]]></c> contains the
- decoded term.</p>
- <p>The <c><![CDATA[term]]></c> structure will contain the arity for a tuple
- or list, size for a binary, string or atom. It will contains
- a term if it's any of the following: integer, float, atom,
- pid, port or ref.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_decode_term(const char *buf, int *index, void *t)</nametext></name>
- <fsummary>Decode a <c><![CDATA[ETERM]]></c></fsummary>
- <desc>
- <p>This function decodes a term from the binary format. The
- term is return in <c><![CDATA[t]]></c> as a <c><![CDATA[ETERM*]]></c>, so <c><![CDATA[t]]></c>
- is actually an <c><![CDATA[ETERM**]]></c> (see
- <c><![CDATA[erl_interface(3)]]></c>. The term should later be
- deallocated.</p>
- <p>Note that this function is located in the erl_interface
- library.</p>
+ <p>Skips a term in the specified buffer;
+ recursively skips elements of lists and tuples, so that a
+ full term is skipped. This is a way to get the size of an
+ Erlang term.</p>
+ <p><c>buf</c> is the buffer.</p>
+ <p><c>index</c> is updated to point right after the term
+ in the buffer.</p>
+ <note>
+ <p>This can be useful when you want to hold arbitrary
+ terms: skip them and copy the binary term data to some
+ buffer.</p>
+ </note>
+ <p>Returns <c>0</c> on success, otherwise
+ <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_print_term(FILE* fp, const char* buf, int* index)</nametext></name>
- <name><ret>int</ret><nametext>ei_s_print_term(char** s, const char* buf, int* index)</nametext></name>
- <fsummary>Print a term in clear text</fsummary>
+ <name><ret>int</ret><nametext>ei_x_append(ei_x_buff* x, const ei_x_buff* x2)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_append_buf(ei_x_buff* x, const char* buf, int len)</nametext></name>
+ <fsummary>Append a buffer at the end.</fsummary>
<desc>
- <p>This function prints a term, in clear text, to the file
- given by <c><![CDATA[fp]]></c>, or the buffer pointed to by <c><![CDATA[s]]></c>. It
- tries to resemble the term printing in the erlang shell.</p>
- <p>In <c><![CDATA[ei_s_print_term()]]></c>, the parameter <c><![CDATA[s]]></c> should
- point to a dynamically (malloc) allocated string of
- <c><![CDATA[BUFSIZ]]></c> bytes or a NULL pointer. The string may be
- reallocated (and <c><![CDATA[*s]]></c> may be updated) by this function
- if the result is more than <c><![CDATA[BUFSIZ]]></c> characters. The
- string returned is zero-terminated.</p>
- <p>The return value is the number of characters written to the
- file or string, or -1 if <c><![CDATA[buf[index]]]></c> doesn't contain a
- valid term. Unfortunately, I/O errors on <c><![CDATA[fp]]></c> is not
- checked.</p>
- <p>The argument <c><![CDATA[index]]></c> is updated, i.e. this function can
- be viewed as en decode function that decodes a term into a
- human readable format.</p>
+ <p>Appends data at the end of buffer <c>x</c>.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_x_format(ei_x_buff* x, const char* fmt, ...)</nametext></name>
<name><ret>int</ret><nametext>ei_x_format_wo_ver(ei_x_buff* x, const char *fmt, ... )</nametext></name>
<fsummary>Format a term from a format string and parameters.</fsummary>
<desc>
- <p>Format a term, given as a string, to a buffer. This
- functions works like a sprintf for erlang terms. The
- <c><![CDATA[fmt]]></c> contains a format string, with arguments like
- <c><![CDATA[~d]]></c>, to insert terms from variables. The following
+ <p>Formats a term, given as a string, to a buffer.
+ Works like a sprintf for Erlang terms.
+ <c>fmt</c> contains a format string, with arguments like
+ <c>~d</c>, to insert terms from variables. The following
formats are supported (with the C types given):</p>
- <p></p>
<pre>
-~a - an atom, char*
-~c - a character, char
-~s - a string, char*
-~i - an integer, int
-~l - a long integer, long int
-~u - a unsigned long integer, unsigned long int
-~f - a float, float
-~d - a double float, double float
-~p - an Erlang PID, erlang_pid*
- </pre>
- <p>For instance, to encode a tuple with some stuff:</p>
+~a An atom, char*
+~c A character, char
+~s A string, char*
+~i An integer, int
+~l A long integer, long int
+~u A unsigned long integer, unsigned long int
+~f A float, float
+~d A double float, double float
+~p An Erlang pid, erlang_pid*</pre>
+ <p>For example, to encode a tuple with some stuff:</p>
<pre>
ei_x_format("{~a,~i,~d}", "numbers", 12, 3.14159)
-encodes the tuple {numbers,12,3.14159}
- </pre>
- <p>The <c><![CDATA[ei_x_format_wo_ver()]]></c> formats into a buffer, without
- the initial version byte.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_x_new(ei_x_buff* x)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_new_with_version(ei_x_buff* x)</nametext></name>
- <fsummary>Allocate a new buffer</fsummary>
- <desc>
- <p>This function allocates a new <c><![CDATA[ei_x_buff]]></c> buffer. The
- fields of the structure pointed to by <c><![CDATA[x]]></c> parameter is
- filled in, and a default buffer is allocated. The
- <c><![CDATA[ei_x_new_with_version()]]></c> also puts an initial version
- byte, that is used in the binary format. (So that
- <c><![CDATA[ei_x_encode_version()]]></c> won't be needed.)</p>
+encodes the tuple {numbers,12,3.14159}</pre>
+ <p><c>ei_x_format_wo_ver()</c> formats into a buffer,
+ without the initial version byte.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_x_free(ei_x_buff* x)</nametext></name>
- <fsummary>Frees a buffer</fsummary>
- <desc>
- <p>This function frees an <c><![CDATA[ei_x_buff]]></c> buffer. The memory
- used by the buffer is returned to the OS.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_x_append(ei_x_buff* x, const ei_x_buff* x2)</nametext></name>
- <name><ret>int</ret><nametext>ei_x_append_buf(ei_x_buff* x, const char* buf, int len)</nametext></name>
- <fsummary>Appends a buffer at the end</fsummary>
+ <fsummary>Free a buffer.</fsummary>
<desc>
- <p>These functions appends data at the end of the buffer <c><![CDATA[x]]></c>.</p>
+ <p>Frees an <c>ei_x_buff</c> buffer.
+ The memory used by the buffer is returned to the OS.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_skip_term(const char* buf, int* index)</nametext></name>
- <fsummary>skip a term</fsummary>
+ <name><ret>int</ret><nametext>ei_x_new(ei_x_buff* x)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_new_with_version(ei_x_buff* x)</nametext></name>
+ <fsummary>Allocate a new buffer.</fsummary>
<desc>
- <p>This function skips a term in the given buffer, it
- recursively skips elements of lists and tuples, so that a
- full term is skipped. This is a way to get the size of an
- erlang term.</p>
- <p><c><![CDATA[buf]]></c> is the buffer.</p>
- <p><c><![CDATA[index]]></c> is updated to point right after the term in the
- buffer.</p>
- <note>
- <p>This can be useful when you want to hold arbitrary
- terms: just skip them and copy the binary term data to some
- buffer.</p>
- </note>
- <p>The function returns <c><![CDATA[0]]></c> on success and <c><![CDATA[-1]]></c> on
- failure.</p>
+ <p>Allocates a new <c>ei_x_buff</c> buffer. The
+ fields of the structure pointed to by parameter <c>x</c>
+ is filled in, and a default buffer is allocated.
+ <c>ei_x_new_with_version()</c> also puts an initial
+ version byte, which is used in the binary format (so that
+ <c>ei_x_encode_version()</c> will not be needed.)</p>
</desc>
</func>
</funcs>
<section>
<title>Debug Information</title>
- <p>Some tips on what to check when the emulator doesn't seem to
- receive the terms that you send.</p>
+ <p>Some tips on what to check when the emulator does not seem to
+ receive the terms that you send:</p>
+
<list type="bulleted">
- <item>be careful with the version header, use
- <c><![CDATA[ei_x_new_with_version()]]></c> when appropriate</item>
- <item>turn on distribution tracing on the erlang node</item>
- <item>check the result codes from ei_decode_-calls</item>
+ <item>Be careful with the version header, use
+ <c>ei_x_new_with_version()</c> when appropriate.</item>
+ <item>Turn on distribution tracing on the Erlang node.</item>
+ <item>Check the result codes from <c>ei_decode_-calls</c>.</item>
</list>
</section>
<section>
<title>See Also</title>
- <p>erl_interface(3)</p>
+ <p><seealso marker="erl_eterm"><c>erl_eterm</c></seealso></p>
</section>
</cref>
-
diff --git a/lib/erl_interface/doc/src/ei_connect.xml b/lib/erl_interface/doc/src/ei_connect.xml
index 516357b6a3..607a7cbff4 100644
--- a/lib/erl_interface/doc/src/ei_connect.xml
+++ b/lib/erl_interface/doc/src/ei_connect.xml
@@ -11,7 +11,7 @@
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
@@ -19,100 +19,233 @@
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>ei_connect</title>
<prepared>Jakob Cederlund</prepared>
<docno></docno>
- <approved>?</approved>
- <checked>?</checked>
+ <approved></approved>
+ <checked></checked>
<date>2001-09-01</date>
<rev>A</rev>
- <file>ei_connect.sgml</file>
+ <file>ei_connect.xml</file>
</header>
<lib>ei_connect</lib>
- <libsummary>Communicate with distributed erlang</libsummary>
+ <libsummary>Communicate with distributed Erlang.</libsummary>
<description>
- <p>This module enables C programs to communicate with erlang nodes,
- using the erlang distribution over TCP/IP.</p>
- <p>A C node appears to Erlang as a
- <em>hidden node</em>.
+ <p>This module enables C-programs to communicate with Erlang nodes,
+ using the Erlang distribution over TCP/IP.</p>
+
+ <p>A C-node appears to Erlang as a <em>hidden node</em>.
That is, Erlang processes that know the name of the
- C node are able to communicate with it in a normal manner, but
- the node name will not appear in the listing provided by the
- Erlang function <c><![CDATA[nodes/0]]></c>.</p>
- <p>The environment variable <c><![CDATA[ERL_EPMD_PORT]]></c> can be used
- to indicate which logical cluster a C node belongs to.</p>
+ C-node can communicate with it in a normal manner, but
+ the node name is not shown in the listing provided by
+ <seealso marker="erts:erlang#nodes/0"><c>erlang:nodes/0</c></seealso>
+ in <c>ERTS</c>.</p>
+
+ <p>The environment variable <c>ERL_EPMD_PORT</c> can be used
+ to indicate which logical cluster a C-node belongs to.</p>
</description>
<section>
- <title>Timeout functions</title>
+ <title>Time-Out Functions</title>
<p>Most functions appear in a version with the suffix
- <c><![CDATA[_tmo]]></c> appended to the function name. Those function take
- an additional argument, a timeout in <em>milliseconds</em>. The
- semantics is this; for each communication primitive involved in
+ <c>_tmo</c> appended to the function name. Those functions
+ take an extra argument, a time-out in <em>milliseconds</em>. The
+ semantics is this: for each communication primitive involved in
the operation, if the primitive does not complete within the time
- specified, the function will return an error and
- <c><![CDATA[erl_errno]]></c> will be set to <c><![CDATA[ETIMEDOUT]]></c>. With
- communication primitive is meant an operation on the socket, like
- <c><![CDATA[connect]]></c>, <c><![CDATA[accept]]></c>, <c><![CDATA[recv]]></c> or <c><![CDATA[send]]></c>.</p>
- <p>Obviously the timeouts are for implementing fault tolerance,
- not to keep hard realtime promises. The <c><![CDATA[_tmo]]></c> functions
+ specified, the function returns an error and
+ <c>erl_errno</c> is set to <c>ETIMEDOUT</c>.
+ With communication primitive is meant an operation on the socket, like
+ <c>connect</c>, <c>accept</c>,
+ <c>recv</c>, or <c>send</c>.</p>
+
+ <p>Clearly the time-outs are for implementing fault tolerance,
+ not to keep hard real-time promises. The <c>_tmo</c> functions
are for detecting non-responsive peers and to avoid blocking on
- socket operations. </p>
- <p>A timeout value of <c><![CDATA[0]]></c> (zero), means that timeouts are
- disabled. Calling a <c><![CDATA[_tmo]]></c>-function with the last argument as
- <c><![CDATA[0]]></c> is therefore exactly the same thing as calling the
- function without the <c><![CDATA[_tmo]]></c> suffix.</p>
- <p>As with all other ei functions, you are <em>not</em> expected
- to put the socket in non blocking mode yourself in the program. Every
- use of non blocking mode is embedded inside the timeout
+ socket operations.</p>
+
+ <p>A time-out value of <c>0</c> (zero) means that time-outs are
+ disabled. Calling a <c>_tmo</c> function with the last
+ argument as <c>0</c> is therefore the same thing as calling
+ the function without the <c>_tmo</c> suffix.</p>
+
+ <p>As with all other functions starting with <c>ei_</c>,
+ you are <em>not</em> expected
+ to put the socket in non-blocking mode yourself in the program. Every
+ use of non-blocking mode is embedded inside the time-out
functions. The socket will always be back in blocking mode after
the operations are completed (regardless of the result). To
- avoid problems, leave the socket options alone. Ei will handle
+ avoid problems, leave the socket options alone. <c>ei</c> handles
any socket options that need modification.</p>
- <p>In all other senses, the <c><![CDATA[_tmo]]></c> functions inherit all
- the return values and the semantics from the functions without
- the <c><![CDATA[_tmo]]></c> suffix.</p>
+
+ <p>In all other senses, the <c>_tmo</c> functions inherit all
+ the return values and the semantics from the functions without
+ the <c>_tmo</c> suffix.</p>
</section>
+
<funcs>
<func>
+ <name><ret>struct hostent</ret><nametext>*ei_gethostbyaddr(const char *addr, int len, int type)</nametext></name>
+ <name><ret>struct hostent</ret><nametext>*ei_gethostbyaddr_r(const char *addr, int length, int type, struct hostent *hostp, char *buffer, int buflen, int *h_errnop)</nametext></name>
+ <name><ret>struct hostent</ret><nametext>*ei_gethostbyname(const char *name)</nametext></name>
+ <name><ret>struct hostent</ret><nametext>*ei_gethostbyname_r(const char *name, struct hostent *hostp, char *buffer, int buflen, int *h_errnop)</nametext></name>
+ <fsummary>Name lookup functions.</fsummary>
+ <desc>
+ <p>Convenience functions for some common name lookup functions.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_accept(ei_cnode *ec, int listensock, ErlConnect *conp)</nametext></name>
+ <fsummary>Accept a connection from another node.</fsummary>
+ <desc>
+ <p>Used by a server process to accept a
+ connection from a client process.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>ec</c> is the C-node structure.</p>
+ </item>
+ <item>
+ <p><c>listensock</c> is an open socket descriptor on
+ which <c>listen()</c> has previously been called.</p>
+ </item>
+ <item>
+ <p><c>conp</c> is a pointer to an
+ <c>ErlConnect</c> struct, described as follows:</p>
+ <code type="none"><![CDATA[
+typedef struct {
+ char ipadr[4];
+ char nodename[MAXNODELEN];
+} ErlConnect;
+ ]]></code>
+ </item>
+ </list>
+ <p>On success, <c>conp</c> is filled in with the address and
+ node name of the connecting client and a file descriptor is
+ returned. On failure, <c>ERL_ERROR</c> is returned and
+ <c>erl_errno</c> is set to <c>EIO</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_accept_tmo(ei_cnode *ec, int listensock, ErlConnect *conp, unsigned timeout_ms)</nametext></name>
+ <fsummary>Accept a connection from another node with optional
+ time-out.</fsummary>
+ <desc>
+ <p>Equivalent to
+ <c>ei_accept</c> with an optional time-out argument,
+ see the description at the beginning of this manual page.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_connect(ei_cnode* ec, char *nodename)</nametext></name>
+ <name><ret>int</ret><nametext>ei_xconnect(ei_cnode* ec, Erl_IpAddr adr, char *alivename)</nametext></name>
+ <fsummary>Establish a connection to an Erlang node.</fsummary>
+ <desc>
+ <p>Sets up a connection to an Erlang node.</p>
+ <p><c>ei_xconnect()</c> requires the IP address of the
+ remote host and the alive name of the remote node to be
+ specified. <c>ei_connect()</c> provides an alternative
+ interface and determines the information from the node name
+ provided.</p>
+ <list type="bulleted">
+ <item><c>addr</c> is the 32-bit IP address of the remote
+ host.</item>
+ <item><c>alive</c> is the alivename of the remote node.
+ </item>
+ <item><c>node</c> is the name of the remote node.</item>
+ </list>
+ <p>These functions return an open file descriptor on success, or
+ a negative value indicating that an error occurred. In the latter
+ case they set <c>erl_errno</c> to one of the
+ following:</p>
+ <taglist>
+ <tag><c>EHOSTUNREACH</c></tag>
+ <item>The remote host <c>node</c> is unreachable.</item>
+ <tag><c>ENOMEM</c></tag>
+ <item>No more memory is available.</item>
+ <tag><c>EIO</c></tag>
+ <item>I/O error.</item>
+ </taglist>
+ <p>Also, <c>errno</c> values from
+ <c>socket</c><em>(2)</em> and
+ <c>connect</c><em>(2)</em>
+ system calls may be propagated into <c>erl_errno</c>.</p>
+ <p><em>Example:</em></p>
+ <code type="none"><![CDATA[
+#define NODE "[email protected]"
+#define ALIVE "madonna"
+#define IP_ADDR "150.236.14.75"
+
+/*** Variant 1 ***/
+int fd = ei_connect(&ec, NODE);
+
+/*** Variant 2 ***/
+struct in_addr addr;
+addr.s_addr = inet_addr(IP_ADDR);
+fd = ei_xconnect(&ec, &addr, ALIVE);
+ ]]></code>
+ </desc>
+ </func>
+
+ <func>
<name><ret>int</ret><nametext>ei_connect_init(ei_cnode* ec, const char* this_node_name, const char *cookie, short creation)</nametext></name>
<name><ret>int</ret><nametext>ei_connect_xinit(ei_cnode* ec, const char *thishostname, const char *thisalivename, const char *thisnodename, Erl_IpAddr thisipaddr, const char *cookie, short creation)</nametext></name>
<fsummary>Initialize for a connection.</fsummary>
<desc>
- <p>These function initializes the <c><![CDATA[ec]]></c> structure, to
+ <p>Initializes the <c>ec</c> structure, to
identify the node name and cookie of the server. One of them
- has to be called before other functions that works on the
- type <c><![CDATA[ei_cnode]]></c> or a file descriptor associated with a
- connection to another node are used.</p>
- <p><c><![CDATA[ec]]></c> is a structure containing information about the
- C-node. It is used in other <c><![CDATA[ei]]></c> functions for
- connecting and receiving data.</p>
- <p><c><![CDATA[this_node_name]]></c> is the registered name of the process
- (the name before '@').</p>
- <p><c><![CDATA[cookie]]></c> is the cookie for the node.</p>
- <p><c><![CDATA[creation]]></c> identifies a specific instance of a C
- node. It can help prevent the node from receiving messages
- sent to an earlier process with the same registered name.</p>
- <p><c><![CDATA[thishostname]]></c> is the name of the machine we're running
- on. If long names are to be used, it should be fully
- qualified (i.e. <c><![CDATA[durin.erix.ericsson.se]]></c> instead of
- <c><![CDATA[durin]]></c>).</p>
- <p><c><![CDATA[thisalivename]]></c> is the registered name of the process.</p>
- <p><c><![CDATA[thisnodename]]></c> is the full name of the node,
- i.e. <c><![CDATA[einode@durin]]></c>.</p>
- <p><c><![CDATA[thispaddr]]></c> if the IP address of the host.</p>
- <p>A C node acting as a server will be assigned a creation
- number when it calls <c><![CDATA[ei_publish()]]></c>.</p>
- <p>A connection is closed by simply closing the socket. Refer
- to system documentation to close the socket gracefully (when
- there are outgoing packets before close).</p>
- <p>This function return a negative value indicating that an error
+ must be called before other functions that works on the
+ <c>ei_cnode</c> type or a file descriptor associated with
+ a connection to another node is used.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>ec</c> is a structure containing information about
+ the C-node. It is used in other <c>ei</c> functions
+ for connecting and receiving data.</p>
+ </item>
+ <item>
+ <p><c>this_node_name</c> is the registered name of the
+ process (the name before '@').</p>
+ </item>
+ <item>
+ <p><c>cookie</c> is the cookie for the node.</p>
+ </item>
+ <item>
+ <p><c>creation</c> identifies a specific instance of a
+ C-node. It can help prevent the node from receiving messages
+ sent to an earlier process with the same registered name.</p>
+ </item>
+ <item>
+ <p><c>thishostname</c> is the name of the machine we are
+ running on. If long names are to be used, they are to be fully
+ qualified (that is, <c>durin.erix.ericsson.se</c>
+ instead of <c>durin</c>).</p>
+ </item>
+ <item>
+ <p><c>thisalivename</c> is the registered name of the
+ process.</p>
+ </item>
+ <item>
+ <p><c>thisnodename</c> is the full name of the node,
+ that is, <c>einode@durin</c>.</p>
+ </item>
+ <item>
+ <p><c>thispaddr</c> if the IP address of the host.</p>
+ </item>
+ </list>
+ <p>A C-node acting as a server is assigned a creation
+ number when it calls <c>ei_publish()</c>.</p>
+ <p>A connection is closed by simply closing the socket.
+ For information about how to close the socket gracefully (when
+ there are outgoing packets before close), see the relevant system
+ documentation.</p>
+ <p>These functions return a negative value indicating that an error
occurred.</p>
- <p>Example 1:
- </p>
+ <p><em>Example 1:</em></p>
<code type="none"><![CDATA[
int n = 0;
struct in_addr addr;
@@ -129,8 +262,7 @@ if (ei_connect_xinit(&ec,
exit(-1);
}
]]></code>
- <p>Example 2:
- </p>
+ <p><em>Example 2:</em></p>
<code type="none"><![CDATA[
if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) {
fprintf(stderr,"ERROR when initializing: %d",erl_errno);
@@ -139,114 +271,184 @@ if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) {
]]></code>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_connect(ei_cnode* ec, char *nodename)</nametext></name>
- <name><ret>int</ret><nametext>ei_xconnect(ei_cnode* ec, Erl_IpAddr adr, char *alivename)</nametext></name>
- <fsummary>Establishe a connection to an Erlang node</fsummary>
+ <name><ret>int</ret><nametext>ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned timeout_ms)</nametext></name>
+ <name><ret>int</ret><nametext>ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr adr, char *alivename, unsigned timeout_ms)</nametext></name>
+ <fsummary>Establish a connection to an Erlang node with optional
+ time-out.</fsummary>
<desc>
- <p>These functions set up a connection to an Erlang node.</p>
- <p><c><![CDATA[ei_xconnect()]]></c> requires the IP address of the remote
- host and the alive name of the remote node
- to be specified. <c><![CDATA[ei_connect()]]></c> provides an alternative
- interface, and determines the information from the node name
- provided.</p>
- <p><c><![CDATA[addr]]></c> is the 32-bit IP address of the remote host.</p>
- <p><c><![CDATA[alive]]></c> is the alivename of the remote node.</p>
- <p><c><![CDATA[node]]></c> is the name of the remote node.</p>
- <p>These functions return an open file descriptor on success, or
- a negative value indicating that an error occurred --- in
- which case they will set <c><![CDATA[erl_errno]]></c> to one of:</p>
- <taglist>
- <tag><c><![CDATA[EHOSTUNREACH]]></c></tag>
- <item>The remote host <c><![CDATA[node]]></c> is unreachable</item>
- <tag><c><![CDATA[ENOMEM]]></c></tag>
- <item>No more memory available.</item>
- <tag><c><![CDATA[EIO]]></c></tag>
- <item>I/O error.</item>
- </taglist>
- <p>Additionally, <c><![CDATA[errno]]></c> values from
- <c><![CDATA[socket]]></c><em>(2)</em> and <c><![CDATA[connect]]></c><em>(2)</em>
- system calls may be propagated into <c><![CDATA[erl_errno]]></c>.</p>
- <p>Example:</p>
- <code type="none"><![CDATA[
-#define NODE "[email protected]"
-#define ALIVE "madonna"
-#define IP_ADDR "150.236.14.75"
+ <p>Equivalent to
+ <c>ei_connect</c> and <c>ei_xconnect</c> with an optional time-out
+ argument, see the description at the beginning of this manual
+ page.</p>
+ </desc>
+ </func>
-/*** Variant 1 ***/
-int fd = ei_connect(&ec, NODE);
+ <func>
+ <name><ret>int</ret><nametext>ei_get_tracelevel(void)</nametext></name>
+ <name><ret>void</ret><nametext>ei_set_tracelevel(int level)</nametext></name>
+ <fsummary>Get and set functions for tracing.</fsummary>
+ <desc>
+ <p>Used to set tracing on the distribution. The levels are different
+ verbosity levels. A higher level means more information. See also
+ section <seealso marker="#debug_information">
+ Debug Information</seealso>.</p>
+ <p>These functions are not thread safe.</p>
+ </desc>
+ </func>
-/*** Variant 2 ***/
-struct in_addr addr;
-addr.s_addr = inet_addr(IP_ADDR);
-fd = ei_xconnect(&ec, &addr, ALIVE);
- ]]></code>
+ <func>
+ <name><ret>int</ret><nametext>ei_publish(ei_cnode *ec, int port)</nametext></name>
+ <fsummary>Publish a node name.</fsummary>
+ <desc>
+ <p>Used by a server process to register
+ with the local name server EPMD, thereby allowing
+ other processes to send messages by using the registered name.
+ Before calling either of these functions, the process should
+ have called <c>bind()</c> and <c>listen()</c>
+ on an open socket.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>ec</c> is the C-node structure.</p>
+ </item>
+ <item>
+ <p><c>port</c> is the local name to register, and is to
+ be the same as the port number that was previously bound to the
+ socket.</p>
+ </item>
+ <item>
+ <p><c>addr</c> is the 32-bit IP address of the local
+ host.</p>
+ </item>
+ </list>
+ <p>To unregister with EPMD, simply close the returned descriptor. Do
+ not use <c>ei_unpublish()</c>, which is deprecated
+ anyway.</p>
+ <p>On success, the function returns a descriptor connecting the
+ calling process to EPMD. On failure, <c>-1</c> is returned and
+ <c>erl_errno</c> is set to <c>EIO</c>.</p>
+ <p>Also, <c>errno</c> values from
+ <c>socket</c><em>(2)</em> and
+ <c>connect</c><em>(2)</em> system calls may be propagated
+ into <c>erl_errno</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned timeout_ms)</nametext></name>
- <name><ret>int</ret><nametext>ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr adr, char *alivename, unsigned timeout_ms)</nametext></name>
- <fsummary>Establish a connection to an Erlang node with optional timeout</fsummary>
+ <name><ret>int</ret><nametext>ei_publish_tmo(ei_cnode *ec, int port, unsigned timeout_ms)</nametext></name>
+ <fsummary>Publish a node name with optional time-out.</fsummary>
<desc>
- <p>ei_connect and ei_xconnect with an optional timeout argument,
- see the description at the beginning of this document.</p>
+ <p>Equivalent to
+ <c>ei_publish</c> with an optional time-out argument,
+ see the description at the beginning of this manual page.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_receive(int fd, unsigned char* bufp, int bufsize)</nametext></name>
- <fsummary>Receive a message</fsummary>
+ <fsummary>Receive a message.</fsummary>
<desc>
- <p>This function receives a message consisting of a sequence
+ <p>Receives a message consisting of a sequence
of bytes in the Erlang external format.</p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection. It
- is obtained from a previous <c><![CDATA[ei_connect]]></c> or
- <c><![CDATA[ei_accept]]></c>.</p>
- <p><c><![CDATA[bufp]]></c> is a buffer large enough to hold the expected
- message. </p>
- <p><c><![CDATA[bufsize]]></c> indicates the size of <c><![CDATA[bufp]]></c>.</p>
- <p>If a <em>tick</em> occurs, i.e., the Erlang node on the
+ <list type="bulleted">
+ <item>
+ <p><c>fd</c> is an open descriptor to an Erlang
+ connection. It is obtained from a previous
+ <c>ei_connect</c> or <c>ei_accept</c>.</p>
+ </item>
+ <item>
+ <p><c>bufp</c> is a buffer large enough to hold the
+ expected message.</p>
+ </item>
+ <item>
+ <p><c>bufsize</c> indicates the size of
+ <c>bufp</c>.</p>
+ </item>
+ </list>
+ <p>If a <em>tick</em> occurs, that is, the Erlang node on the
other end of the connection has polled this node to see if it
- is still alive, the function will return <c><![CDATA[ERL_TICK]]></c> and
- no message will be placed in the buffer. Also,
- <c><![CDATA[erl_errno]]></c> will be set to <c><![CDATA[EAGAIN]]></c>.</p>
+ is still alive, the function returns <c>ERL_TICK</c> and
+ no message is placed in the buffer. Also,
+ <c>erl_errno</c> is set to <c>EAGAIN</c>.</p>
<p>On success, the message is placed in the specified buffer
and the function returns the number of bytes actually read. On
- failure, the function returns <c><![CDATA[ERL_ERROR]]></c> and will set
- <c><![CDATA[erl_errno]]></c> to one of:</p>
+ failure, the function returns <c>ERL_ERROR</c> and sets
+ <c>erl_errno</c> to one of the following:</p>
<taglist>
- <tag><c><![CDATA[EAGAIN]]></c></tag>
+ <tag><c>EAGAIN</c></tag>
<item>Temporary error: Try again.</item>
- <tag><c><![CDATA[EMSGSIZE]]></c></tag>
- <item>Buffer too small.</item>
- <tag><c><![CDATA[EIO]]></c></tag>
+ <tag><c>EMSGSIZE</c></tag>
+ <item>Buffer is too small.</item>
+ <tag><c>EIO</c></tag>
<item>I/O error.</item>
</taglist>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_receive_tmo(int fd, unsigned char* bufp, int bufsize, unsigned timeout_ms)</nametext></name>
- <fsummary>Receive a message with optional timeout</fsummary>
+ <name><ret>int</ret><nametext>ei_receive_encoded(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen)</nametext></name>
+ <fsummary>Obsolete function for receiving a message.</fsummary>
<desc>
- <p>ei_receive with an optional timeout argument,
- see the description at the beginning of this document.</p>
+ <p>This function is retained for compatibility with code
+ generated by the interface compiler and with code following
+ examples in the same application.</p>
+ <p>In essence, the function performs the same operation as
+ <c>ei_xreceive_msg</c>, but instead of using an
+ <c>ei_x_buff</c>, the function expects a pointer to a character
+ pointer (<c>mbufp</c>), where the character pointer
+ is to point to a memory area allocated by <c>malloc</c>.
+ Argument <c>bufsz</c> is to be a pointer to an integer
+ containing the exact size (in bytes) of the memory area. The function
+ may reallocate the memory area and will in such cases put the new
+ size in <c>*bufsz</c> and update
+ <c>*mbufp</c>.</p>
+ <p>Returns either <c>ERL_TICK</c> or the
+ <c>msgtype</c> field of the
+ <c>erlang_msg *msg</c>. The length
+ of the message is put in <c>*msglen</c>. On error
+ a value <c>&lt; 0</c> is returned.</p>
+ <p>It is recommended to use <c>ei_xreceive_msg</c> instead when
+ possible, for the sake of readability. However, the function will
+ be retained in the interface for compatibility and
+ will <em>not</em> be removed in future releases without prior
+ notice.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_receive_encoded_tmo(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen, unsigned timeout_ms)</nametext></name>
+ <fsummary>Obsolete function for receiving a message with time-out.
+ </fsummary>
+ <desc>
+ <p>Equivalent to
+ <c>ei_receive_encoded</c> with an optional time-out argument,
+ see the description at the beginning of this manual page.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_receive_msg(int fd, erlang_msg* msg, ei_x_buff* x)</nametext></name>
<name><ret>int</ret><nametext>ei_xreceive_msg(int fd, erlang_msg* msg, ei_x_buff* x)</nametext></name>
- <fsummary>Receive a message</fsummary>
+ <fsummary>Receive a message.</fsummary>
<desc>
- <p>These functions receives a message to the buffer in
- <c><![CDATA[x]]></c>. <c><![CDATA[ei_xreceive_msg]]></c> allows the buffer in
- <c><![CDATA[x]]></c> to grow, but <c><![CDATA[ei_receive_msg]]></c> fails if the
- message is bigger than the preallocated buffer in <c><![CDATA[x]]></c>.</p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.</p>
- <p><c><![CDATA[msg]]></c> is a pointer to an <c><![CDATA[erlang_msg]]></c> structure
- and contains information on the message received.</p>
- <p><c><![CDATA[x]]></c> is buffer obtained from <c><![CDATA[ei_x_new]]></c>.</p>
- <p>On success, the function returns <c><![CDATA[ERL_MSG]]></c> and the
- <c><![CDATA[msg]]></c> struct will be initialized. <c><![CDATA[erlang_msg]]></c>
- is defined as follows:</p>
+ <p>Receives a message to the buffer in <c>x</c>.
+ <c>ei_xreceive_msg</c> allows the buffer in
+ <c>x</c> to grow, but <c>ei_receive_msg</c>
+ fails if the message is larger than the pre-allocated buffer in
+ <c>x</c>.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>msg</c> is a pointer to an
+ <c>erlang_msg</c> structure
+ and contains information on the message received.</item>
+ <item><c>x</c> is buffer obtained from
+ <c>ei_x_new</c>.</item>
+ </list>
+ <p>On success, the functions return <c>ERL_MSG</c> and the
+ <c>msg</c> struct is initialized.
+ <c>erlang_msg</c> is defined as follows:</p>
<code type="none"><![CDATA[
typedef struct {
long msgtype;
@@ -257,125 +459,82 @@ typedef struct {
erlang_trace token;
} erlang_msg;
]]></code>
- <p><c><![CDATA[msgtype]]></c> identifies the type of message, and is one of
- <c><![CDATA[ERL_SEND]]></c>, <c><![CDATA[ERL_REG_SEND]]></c>, <c><![CDATA[ERL_LINK]]></c>,
- <c><![CDATA[ERL_UNLINK]]></c> and <c><![CDATA[ERL_EXIT]]></c>.</p>
- <p>If <c><![CDATA[msgtype]]></c> is <c><![CDATA[ERL_SEND]]></c> this indicates that an
- ordinary send operation has taken place, and <c><![CDATA[msg->to]]></c>
- contains the Pid of the recipient (the C-node). If
- <c><![CDATA[type]]></c> is <c><![CDATA[ERL_REG_SEND]]></c> then a registered send
- operation took place, and <c><![CDATA[msg->from]]></c> contains the Pid
- of the sender.</p>
- <p>If <c><![CDATA[msgtype]]></c> is <c><![CDATA[ERL_LINK]]></c> or <c><![CDATA[ERL_UNLINK]]></c>, then
- <c><![CDATA[msg->to]]></c> and <c><![CDATA[msg->from]]></c> contain the pids of the
- sender and recipient of the link or unlink.</p>
- <p>If <c><![CDATA[msgtype]]></c> is <c><![CDATA[ERL_EXIT]]></c>, then this indicates that
- a link has been broken. In this case, <c><![CDATA[msg->to]]></c> and
- <c><![CDATA[msg->from]]></c> contain the pids of the linked processes.</p>
- <p>The return value is the same as for <c><![CDATA[ei_receive]]></c>, see
- above.</p>
+ <p><c>msgtype</c> identifies the type of message, and is
+ one of the following:</p>
+ <taglist>
+ <tag><c>ERL_SEND</c></tag>
+ <item>
+ <p>Indicates that an ordinary send operation has occurred.
+ <c>msg-&gt;to</c> contains the pid of the recipient (the
+ C-node).</p>
+ </item>
+ <tag><c>ERL_REG_SEND</c></tag>
+ <item>
+ <p>A registered send operation occurred.
+ <c>msg-&gt;from</c> contains the pid of the sender.</p>
+ </item>
+ <tag><c>ERL_LINK</c> or
+ <c>ERL_UNLINK</c></tag>
+ <item>
+ <p><c>msg-&gt;to</c> and
+ <c>msg-&gt;from</c> contain the pids of the
+ sender and recipient of the link or unlink.</p>
+ </item>
+ <tag><c>ERL_EXIT</c></tag>
+ <item>
+ <p>Indicates a broken link. <c>msg-&gt;to</c> and
+ <c>msg-&gt;from</c> contain the pids of the linked
+ processes.</p>
+ </item>
+ </taglist>
+ <p>The return value is the same as for
+ <seealso marker="#ei_receive"><c>ei_receive</c></seealso>.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_receive_msg_tmo(int fd, erlang_msg* msg, ei_x_buff* x, unsigned imeout_ms)</nametext></name>
<name><ret>int</ret><nametext>ei_xreceive_msg_tmo(int fd, erlang_msg* msg, ei_x_buff* x, unsigned timeout_ms)</nametext></name>
- <fsummary>Receive a message with optional timeout</fsummary>
- <desc>
- <p>ei_receive_msg and ei_xreceive_msg with an optional timeout argument,
- see the description at the beginning of this document.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_receive_encoded(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen)</nametext></name>
- <fsummary>Obsolete function for receiving a message</fsummary>
- <desc>
- <p>This function is retained for compatibility with code
- generated by the interface compiler and with code following
- examples in the same application.</p>
- <p>In essence the function performs the same operation as
- <c><![CDATA[ei_xreceive_msg]]></c>, but instead of using an ei_x_buff, the
- function expects a pointer to a character pointer
- (<c><![CDATA[mbufp]]></c>), where the character pointer should point to a
- memory area allocated by <c><![CDATA[malloc]]></c>. The argument
- <c><![CDATA[bufsz]]></c> should be a pointer to an integer containing the
- exact size (in bytes) of the memory area. The function may
- reallocate the memory area and will in such cases put the new
- size in <c><![CDATA[*bufsz]]></c> and update <c><![CDATA[*mbufp]]></c>.</p>
- <p>Furthermore the function returns either ERL_TICK or the
- <c><![CDATA[msgtype]]></c> field of the <c><![CDATA[erlang_msg *msg]]></c>. The actual
- length of the message is put in <c><![CDATA[*msglen]]></c>. On error it
- will return a value <c><![CDATA[< 0]]></c>.</p>
- <p>It is recommended to use ei_xreceive_msg instead when
- possible, for the sake of readability. The function will
- however be retained in the interface for compatibility and
- will <em>not</em> be removed not be removed in future releases
- without notice.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_receive_encoded_tmo(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen, unsigned timeout_ms)</nametext></name>
- <fsummary>Obsolete function for receiving a message with timeout</fsummary>
- <desc>
- <p>ei_receive_encoded with an optional timeout argument,
- see the description at the beginning of this document.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_send(int fd, erlang_pid* to, char* buf, int len)</nametext></name>
- <fsummary>Send a message</fsummary>
- <desc>
- <p>This function sends an Erlang term to a process.</p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.</p>
- <p><c><![CDATA[to]]></c> is the Pid of the intended recipient of the
- message.</p>
- <p><c><![CDATA[buf]]></c> is the buffer containing the term in binary
- format.</p>
- <p><c><![CDATA[len]]></c> is the length of the message in bytes.</p>
- <p>The function returns 0 if successful, otherwise -1, in the
- latter case it will set <c><![CDATA[erl_errno]]></c> to <c><![CDATA[EIO]]></c>.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_send_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms)</nametext></name>
- <fsummary>Send a message with optional timeout</fsummary>
+ <fsummary>Receive a message with optional time-out.</fsummary>
<desc>
- <p>ei_send with an optional timeout argument,
- see the description at the beginning of this document.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_send_encoded(int fd, erlang_pid* to, char* buf, int len)</nametext></name>
- <fsummary>Obsolete function to send a message</fsummary>
- <desc>
- <p>Works exactly as ei_send, the alternative name retained for
- backward compatibility. The function will <em>not</em> be
- removed without notice.</p>
+ <p>Equivalent to <c>ei_receive_msg</c> and <c>ei_xreceive_msg</c>
+ with an optional time-out argument,
+ see the description at the beginning of this manual page.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_send_encoded_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms)</nametext></name>
- <fsummary>Obsolete function to send a message with optional timeout</fsummary>
+ <name><ret>int</ret><nametext>ei_receive_tmo(int fd, unsigned char* bufp, int bufsize, unsigned timeout_ms)</nametext></name>
+ <fsummary>Receive a message with optional time-out.</fsummary>
<desc>
- <p>ei_send_encoded with an optional timeout argument,
- see the description at the beginning of this document.</p>
+ <p>Equivalent to
+ <c>ei_receive</c> with an optional time-out argument,
+ see the description at the beginning of this manual page.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_reg_send(ei_cnode* ec, int fd, char* server_name, char* buf, int len)</nametext></name>
- <fsummary>Send a message to a registered name</fsummary>
+ <fsummary>Send a message to a registered name.</fsummary>
<desc>
- <p>This function sends an Erlang term to a registered process.
- </p>
- <p>This function sends an Erlang term to a process.</p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.</p>
- <p><c><![CDATA[server_name]]></c> is the registered name of the intended
- recipient.</p>
- <p><c><![CDATA[buf]]></c> is the buffer containing the term in binary
- format.</p>
- <p><c><![CDATA[len]]></c> is the length of the message in bytes.</p>
- <p>The function returns 0 if successful, otherwise -1, in the
- latter case it will set <c><![CDATA[erl_errno]]></c> to <c><![CDATA[EIO]]></c>.</p>
- <p>Example, send the atom "ok" to the process "worker":</p>
+ <p>Sends an Erlang term to a registered process.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>fd</c> is an open descriptor to an Erlang
+ connection.</p>
+ </item>
+ <item><c>server_name</c> is the registered name of the
+ intended recipient.</item>
+ <item><c>buf</c> is the buffer containing the term in
+ binary format.</item>
+ <item><c>len</c> is the length of the message in bytes.
+ </item>
+ </list>
+ <p>Returns <c>0</c> if successful, otherwise <c>-1</c>. In
+ the latter case it sets <c>erl_errno</c> to
+ <c>EIO</c>.</p>
+ <p><em>Example:</em></p>
+ <p>Send the atom "ok" to the process "worker":</p>
<code type="none"><![CDATA[
ei_x_buff x;
ei_x_new_with_version(&x);
@@ -385,96 +544,98 @@ if (ei_reg_send(&ec, fd, x.buff, x.index) < 0)
]]></code>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_reg_send_tmo(ei_cnode* ec, int fd, char* server_name, char* buf, int len, unsigned timeout_ms)</nametext></name>
- <fsummary>Send a message to a registered name with optional timeout</fsummary>
- <desc>
- <p>ei_reg_send with an optional timeout argument,
- see the description at the beginning of this document.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_send_reg_encoded(int fd, const erlang_pid *from, const char *to, const char *buf, int len)</nametext></name>
- <fsummary>Obsolete function to send a message to a registered name</fsummary>
- <desc>
- <p>This function is retained for compatibility with code
- generated by the interface compiler and with code following
- examples in the same application.</p>
- <p>The function works as <c><![CDATA[ei_reg_send]]></c> with one
- exception. Instead of taking the <c><![CDATA[ei_cnode]]></c> as a first
- argument, it takes a second argument, an <c><![CDATA[erlang_pid]]></c>
- which should be the process identifier of the sending process
- (in the erlang distribution protocol). </p>
- <p>A suitable <c><![CDATA[erlang_pid]]></c> can be constructed from the
- <c><![CDATA[ei_cnode]]></c> structure by the following example code:</p>
- <code type="none"><![CDATA[
- ei_cnode ec;
- erlang_pid *self;
- int fd; /* the connection fd */
- ...
- self = ei_self(&ec);
- self->num = fd;
- ]]></code>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_send_reg_encoded_tmo(int fd, const erlang_pid *from, const char *to, const char *buf, int len)</nametext></name>
- <fsummary>Obsolete function to send a message to a registered name with timeout</fsummary>
+ <fsummary>Send a message to a registered name with optional time-out
+ </fsummary>
<desc>
- <p>ei_send_reg_encoded with an optional timeout argument,
- see the description at the beginning of this document.</p>
+ <p>Equivalent to
+ <c>ei_reg_send</c> with an optional time-out argument,
+ see the description at the beginning of this manual page.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_rpc(ei_cnode *ec, int fd, char *mod, char *fun, const char *argbuf, int argbuflen, ei_x_buff *x)</nametext></name>
<name><ret>int</ret><nametext>ei_rpc_to(ei_cnode *ec, int fd, char *mod, char *fun, const char *argbuf, int argbuflen)</nametext></name>
<name><ret>int</ret><nametext>ei_rpc_from(ei_cnode *ec, int fd, int timeout, erlang_msg *msg, ei_x_buff *x)</nametext></name>
- <fsummary>Remote Procedure Call from C to Erlang</fsummary>
+ <fsummary>Remote Procedure Call from C to Erlang.</fsummary>
<desc>
- <p>These functions support calling Erlang functions on remote nodes.
- <c><![CDATA[ei_rpc_to()]]></c> sends an rpc request to a remote node and
- <c><![CDATA[ei_rpc_from()]]></c> receives the results of such a call.
- <c><![CDATA[ei_rpc()]]></c> combines the functionality of these two functions
- by sending an rpc request and waiting for the results. See also
- <c><![CDATA[rpc:call/4]]></c>. </p>
- <p><c><![CDATA[ec]]></c> is the C-node structure previously initiated by a
- call to <c><![CDATA[ei_connect_init()]]></c> or
- <c><![CDATA[ei_connect_xinit()]]></c></p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.</p>
- <p><c><![CDATA[timeout]]></c> is the maximum time (in ms) to wait for
- results. Specify <c><![CDATA[ERL_NO_TIMEOUT]]></c> to wait forever.
- <c><![CDATA[ei_rpc()]]></c> will wait infinitely for the answer,
- i.e. the call will never time out.</p>
- <p><c><![CDATA[mod]]></c> is the name of the module containing the function
- to be run on the remote node.</p>
- <p><c><![CDATA[fun]]></c> is the name of the function to run.</p>
- <p><c><![CDATA[argbuf]]></c> is a pointer to a buffer with an encoded
- Erlang list, without a version magic number, containing the
- arguments to be passed to the function.</p>
- <p><c><![CDATA[argbuflen]]></c> is the length of the buffer containing the
- encoded Erlang list.</p>
- <p><c><![CDATA[msg]]></c> structure of type <c><![CDATA[erlang_msg]]></c> and contains
- information on the message received. See <c><![CDATA[ei_receive_msg()]]></c>
- for a description of the <c><![CDATA[erlang_msg]]></c> format.</p>
- <p><c><![CDATA[x]]></c> points to the dynamic buffer that receives the
- result. For for <c><![CDATA[ei_rpc()]]></c> this will be the result
- without the version magic number. For <c><![CDATA[ei_rpc_from()]]></c>
- the result will return a version magic number and a 2-tuple
- <c><![CDATA[{rex,Reply}]]></c>.</p>
- <p><c><![CDATA[ei_rpc()]]></c> returns the number of bytes in the result
- on success and -1 on failure. <c><![CDATA[ei_rpc_from()]]></c> returns
- number of bytes or one of <c><![CDATA[ERL_TICK]]></c>, <c><![CDATA[ERL_TIMEOUT]]></c>
- and <c><![CDATA[ERL_ERROR]]></c> otherwise. When failing,
- all three functions set <c><![CDATA[erl_errno]]></c> to one of:</p>
+ <p>Supports calling Erlang functions on remote nodes.
+ <c>ei_rpc_to()</c> sends an RPC request to a remote node
+ and <c>ei_rpc_from()</c> receives the results of such a
+ call. <c>ei_rpc()</c> combines the functionality of these
+ two functions by sending an RPC request and waiting for the results.
+ See also <seealso marker="kernel:rpc#call/4">
+ <c>rpc:call/4</c></seealso> in Kernel.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>ec</c> is the C-node structure previously
+ initiated by a call to <c>ei_connect_init()</c> or
+ <c>ei_connect_xinit()</c>.</p>
+ </item>
+ <item>
+ <p><c>fd</c> is an open descriptor to an Erlang
+ connection.</p>
+ </item>
+ <item>
+ <p><c>timeout</c> is the maximum time (in milliseconds)
+ to wait for results. Specify <c>ERL_NO_TIMEOUT</c> to
+ wait forever.
+ <c>ei_rpc()</c> waits infinitely for the answer,
+ that is, the call will never time out.</p>
+ </item>
+ <item>
+ <p><c>mod</c> is the name of the module containing the
+ function to be run on the remote node.</p>
+ </item>
+ <item>
+ <p><c>fun</c> is the name of the function to run.</p>
+ </item>
+ <item>
+ <p><c>argbuf</c> is a pointer to a buffer with an
+ encoded Erlang list, without a version magic number, containing
+ the arguments to be passed to the function.</p>
+ </item>
+ <item>
+ <p><c>argbuflen</c> is the length of the buffer
+ containing the encoded Erlang list.</p>
+ </item>
+ <item>
+ <p><c>msg</c> is structure of type
+ <c>erlang_msg</c> and contains information on the
+ message
+ received. For a description of the <c>erlang_msg</c>
+ format, see <seealso marker="#ei_receive_msg">
+ <c>ei_receive_msg</c></seealso>.</p>
+ </item>
+ <item>
+ <p><c>x</c> points to the dynamic buffer that receives
+ the result. For <c>ei_rpc()</c> this is the result
+ without the version magic number. For
+ <c>ei_rpc_from()</c> the result returns a version
+ magic number and a 2-tuple <c>{rex,Reply}</c>.</p>
+ </item>
+ </list>
+ <p><c>ei_rpc()</c> returns the number of bytes in the
+ result on success and <c>-1</c> on failure.
+ <c>ei_rpc_from()</c> returns the
+ number of bytes, otherwise one of <c>ERL_TICK</c>,
+ <c>ERL_TIMEOUT</c>,
+ and <c>ERL_ERROR</c>. When failing, all three
+ functions set <c>erl_errno</c> to one of the
+ following:</p>
<taglist>
- <tag><c><![CDATA[EIO]]></c></tag>
+ <tag><c>EIO</c></tag>
<item>I/O error.</item>
- <tag><c><![CDATA[ETIMEDOUT]]></c></tag>
- <item>Timeout expired.</item>
- <tag><c><![CDATA[EAGAIN]]></c></tag>
+ <tag><c>ETIMEDOUT</c></tag>
+ <item>Time-out expired.</item>
+ <tag><c>EAGAIN</c></tag>
<item>Temporary error: Try again.</item>
</taglist>
- <p>Example, check to see if an erlang process is alive:</p>
+ <p><em>Example:</em></p>
+ <p>Check to see if an Erlang process is alive:</p>
<code type="none"><![CDATA[
int index = 0, is_alive;
ei_x_buff args, result;
@@ -495,170 +656,190 @@ if (ei_decode_version(result.buff, &index) < 0
]]></code>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_publish(ei_cnode *ec, int port)</nametext></name>
- <fsummary>Publish a node name</fsummary>
+ <name><ret>erlang_pid *</ret><nametext>ei_self(ei_cnode *ec)</nametext></name>
+ <fsummary>Retrieve the pid of the C-node.</fsummary>
<desc>
- <p>These functions are used by a server process to register
- with the local name server <em>epmd</em>, thereby allowing
- other processes to send messages by using the registered name.
- Before calling either of these functions, the process should
- have called <c><![CDATA[bind()]]></c> and <c><![CDATA[listen()]]></c> on an open socket.</p>
- <p><c><![CDATA[ec]]></c> is the C-node structure.</p>
- <p><c><![CDATA[port]]></c> is the local name to register, and should be the
- same as the port number that was previously bound to the socket.</p>
- <p><c><![CDATA[addr]]></c> is the 32-bit IP address of the local host.</p>
- <p>To unregister with epmd, simply close the returned
- descriptor. Do not use <c><![CDATA[ei_unpublish()]]></c>, which is deprecated anyway.</p>
- <p>On success, the functions return a descriptor connecting the
- calling process to epmd. On failure, they return -1 and set
- <c><![CDATA[erl_errno]]></c> to <c><![CDATA[EIO]]></c>.</p>
- <p>Additionally, <c><![CDATA[errno]]></c> values from <c><![CDATA[socket]]></c><em>(2)</em>
- and <c><![CDATA[connect]]></c><em>(2)</em> system calls may be propagated
- into <c><![CDATA[erl_errno]]></c>.</p>
+ <p>Retrieves the pid of the C-node. Every C-node
+ has a (pseudo) pid used in <c>ei_send_reg</c>,
+ <c>ei_rpc</c>,
+ and others. This is contained in a field in the <c>ec</c>
+ structure. It will be safe for a long time to fetch this
+ field directly from the <c>ei_cnode</c> structure.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_publish_tmo(ei_cnode *ec, int port, unsigned timeout_ms)</nametext></name>
- <fsummary>Publish a node name with optional timeout</fsummary>
+ <name><ret>int</ret><nametext>ei_send(int fd, erlang_pid* to, char* buf, int len)</nametext></name>
+ <fsummary>Send a message.</fsummary>
<desc>
- <p>ei_publish with an optional timeout argument,
- see the description at the beginning of this document.</p>
+ <p>Sends an Erlang term to a process.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>to</c> is the pid of the intended recipient of
+ the message.</item>
+ <item><c>buf</c> is the buffer containing the term in
+ binary format.</item>
+ <item><c>len</c> is the length of the message in bytes.
+ </item>
+ </list>
+ <p>Returns <c>0</c> if successful, otherwise <c>-1</c>. In
+ the latter case it sets <c>erl_errno</c> to
+ <c>EIO</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_accept(ei_cnode *ec, int listensock, ErlConnect *conp)</nametext></name>
- <fsummary>Accept a connection from another node</fsummary>
+ <name><ret>int</ret><nametext>ei_send_encoded(int fd, erlang_pid* to, char* buf, int len)</nametext></name>
+ <fsummary>Obsolete function to send a message.</fsummary>
<desc>
- <p>This function is used by a server process to accept a
- connection from a client process.</p>
- <p><c><![CDATA[ec]]></c> is the C-node structure.</p>
- <p><c><![CDATA[listensock]]></c> is an open socket descriptor on which
- <c><![CDATA[listen()]]></c> has previously been called.</p>
- <p><c><![CDATA[conp]]></c> is a pointer to an <c><![CDATA[ErlConnect]]></c> struct,
- described as follows:</p>
- <code type="none"><![CDATA[
-typedef struct {
- char ipadr[4];
- char nodename[MAXNODELEN];
-} ErlConnect;
- ]]></code>
- <p>On success, <c><![CDATA[conp]]></c> is filled in with the address and
- node name of the connecting client and a file descriptor is
- returned. On failure, <c><![CDATA[ERL_ERROR]]></c> is returned and
- <c><![CDATA[erl_errno]]></c> is set to <c><![CDATA[EIO]]></c>.</p>
+ <p>Works exactly as <c>ei_send</c>, the alternative name is retained for
+ backward compatibility. The function will <em>not</em> be
+ removed without prior notice.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_accept_tmo(ei_cnode *ec, int listensock, ErlConnect *conp, unsigned timeout_ms)</nametext></name>
- <fsummary>Accept a connection from another node with optional timeout</fsummary>
+ <name><ret>int</ret><nametext>ei_send_encoded_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms)</nametext></name>
+ <fsummary>Obsolete function to send a message with optional time-out.
+ </fsummary>
<desc>
- <p>ei_accept with an optional timeout argument,
- see the description at the beginning of this document.</p>
+ <p>Equivalent to
+ <c>ei_send_encoded</c> with an optional time-out argument,
+ see the description at the beginning of this manual page.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_unpublish(ei_cnode *ec)</nametext></name>
- <fsummary>Forcefully unpublish a node name</fsummary>
+ <name><ret>int</ret><nametext>ei_send_reg_encoded(int fd, const erlang_pid *from, const char *to, const char *buf, int len)</nametext></name>
+ <fsummary>Obsolete function to send a message to a registered name.
+ </fsummary>
<desc>
- <p>This function can be called by a process to unregister a
- specified node from epmd on the localhost. This is however usually not
- allowed, unless epmd was started with the -relaxed_command_check
- flag, which it normally isn't.</p>
-
- <p>To unregister a node you have published, you should
- close the descriptor that was returned by
- <c><![CDATA[ei_publish()]]></c>.</p>
+ <p>This function is retained for compatibility with code
+ generated by the interface compiler and with code following
+ examples in the same application.</p>
+ <p>The function works as <c>ei_reg_send</c> with one
+ exception. Instead of taking <c>ei_cnode</c> as first
+ argument, it takes a second argument, an
+ <c>erlang_pid</c>,
+ which is to be the process identifier of the sending process
+ (in the Erlang distribution protocol).</p>
+ <p>A suitable <c>erlang_pid</c> can be constructed from the
+ <c>ei_cnode</c> structure by the following example
+ code:</p>
+ <code type="none"><![CDATA[
+ei_cnode ec;
+erlang_pid *self;
+int fd; /* the connection fd */
+...
+self = ei_self(&ec);
+self->num = fd;
+ ]]></code>
+ </desc>
+ </func>
- <warning>
- <p>This function is deprecated and will be removed in a future
- release.</p>
- </warning>
- <p><c><![CDATA[ec]]></c> is the node structure of the node to unregister.</p>
- <p>If the node was successfully unregistered from epmd, the
- function returns 0. Otherwise, it returns -1 and sets
- <c><![CDATA[erl_errno]]></c> is to <c><![CDATA[EIO]]></c>.</p>
+ <func>
+ <name><ret>int</ret><nametext>ei_send_reg_encoded_tmo(int fd, const erlang_pid *from, const char *to, const char *buf, int len)</nametext></name>
+ <fsummary>Obsolete function to send a message to a registered name with
+ time-out.</fsummary>
+ <desc>
+ <p>Equivalent to
+ <c>ei_send_reg_encoded</c> with an optional time-out argument,
+ see the description at the beginning of this manual page.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_unpublish_tmo(ei_cnode *ec, unsigned timeout_ms)</nametext></name>
- <fsummary>Unpublish a node name with optional timeout</fsummary>
+ <name><ret>int</ret><nametext>ei_send_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms)</nametext></name>
+ <fsummary>Send a message with optional time-out.</fsummary>
<desc>
- <p>ei_unpublish with an optional timeout argument,
- see the description at the beginning of this document.</p>
+ <p>Equivalent to
+ <c>ei_send</c> with an optional time-out argument,
+ see the description at the beginning of this manual page.</p>
</desc>
</func>
+
<func>
<name><ret>const char *</ret><nametext>ei_thisnodename(ei_cnode *ec)</nametext></name>
<name><ret>const char *</ret><nametext>ei_thishostname(ei_cnode *ec)</nametext></name>
<name><ret>const char *</ret><nametext>ei_thisalivename(ei_cnode *ec)</nametext></name>
- <fsummary>Retrieve some values</fsummary>
+ <fsummary>Retrieve some values.</fsummary>
<desc>
- <p>These functions can be used to retrieve information about
- the C Node. These values are initially set with
- <c><![CDATA[ei_connect_init()]]></c> or <c><![CDATA[ei_connect_xinit()]]></c>.</p>
- <p>They simply fetches the appropriate field from the <c><![CDATA[ec]]></c>
+ <p>Can be used to retrieve information about
+ the C-node. These values are initially set with
+ <c>ei_connect_init()</c> or
+ <c>ei_connect_xinit()</c>.</p>
+ <p>These function simply fetch the appropriate field from the
+ <c>ec</c>
structure. Read the field directly will probably be safe for
a long time, so these functions are not really needed.</p>
</desc>
</func>
+
<func>
- <name><ret>erlang_pid *</ret><nametext>ei_self(ei_cnode *ec)</nametext></name>
- <fsummary>Retrieve the Pid of the C-node</fsummary>
- <desc>
- <p>This function retrieves the Pid of the C-node. Every C-node
- has a (pseudo) pid used in <c><![CDATA[ei_send_reg]]></c>, <c><![CDATA[ei_rpc]]></c>
- and others. This is contained in a field in the <c><![CDATA[ec]]></c>
- structure. It will be safe for a long time to fetch this
- field directly from the <c><![CDATA[ei_cnode]]></c> structure.</p>
- </desc>
- </func>
- <func>
- <name><ret>struct hostent</ret><nametext>*ei_gethostbyname(const char *name)</nametext></name>
- <name><ret>struct hostent</ret><nametext>*ei_gethostbyaddr(const char *addr, int len, int type)</nametext></name>
- <name><ret>struct hostent</ret><nametext>*ei_gethostbyname_r(const char *name, struct hostent *hostp, char *buffer, int buflen, int *h_errnop)</nametext></name>
- <name><ret>struct hostent</ret><nametext>*ei_gethostbyaddr_r(const char *addr, int length, int type, struct hostent *hostp, char *buffer, int buflen, int *h_errnop)</nametext></name>
- <fsummary>Name lookup functions</fsummary>
+ <name><ret>int</ret><nametext>ei_unpublish(ei_cnode *ec)</nametext></name>
+ <fsummary>Forcefully unpublish a node name.</fsummary>
<desc>
- <p>These are convenience functions for some common name lookup functions.</p>
+ <p>Can be called by a process to unregister a
+ specified node from EPMD on the local host. This is, however, usually
+ not allowed, unless EPMD was started with flag
+ <c>-relaxed_command_check</c>, which it normally is not.</p>
+ <p>To unregister a node you have published, you should
+ close the descriptor that was returned by
+ <c>ei_publish()</c>.</p>
+ <warning>
+ <p>This function is deprecated and will be removed in a future
+ release.</p>
+ </warning>
+ <p><c>ec</c> is the node structure of the node to
+ unregister.</p>
+ <p>If the node was successfully unregistered from EPMD, the
+ function returns <c>0</c>. Otherwise, <c>-1</c> is returned and
+ <c>erl_errno</c> is set to <c>EIO</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_get_tracelevel(void)</nametext></name>
- <name><ret>void</ret><nametext>ei_set_tracelevel(int level)</nametext></name>
- <fsummary>Get and set functions for tracing.</fsummary>
+ <name><ret>int</ret><nametext>ei_unpublish_tmo(ei_cnode *ec, unsigned timeout_ms)</nametext></name>
+ <fsummary>Unpublish a node name with optional time-out.</fsummary>
<desc>
- <p>These functions are used to set tracing on the distribution. The levels are different verbosity levels. A higher level means more information.
- See also Debug Information and <c><![CDATA[EI_TRACELEVEL]]></c> below. </p>
- <p> <c><![CDATA[ei_set_tracelevel]]></c> and <c><![CDATA[ei_get_tracelevel]]></c> are not thread safe. </p>
+ <p>Equivalent to
+ <c>ei_unpublish</c> with an optional time-out argument,
+ see the description at the beginning of this manual page.</p>
</desc>
</func>
</funcs>
<section>
+ <marker id="debug_information"></marker>
<title>Debug Information</title>
<p>If a connection attempt fails, the following can be checked:</p>
+
<list type="bulleted">
- <item><c><![CDATA[erl_errno]]></c></item>
- <item>that the right cookie was used</item>
- <item>that <em>epmd</em> is running</item>
- <item>the remote Erlang node on the other side is running the
- same version of Erlang as the <c><![CDATA[ei]]></c>
- library.</item>
- <item>the environment variable <c><![CDATA[ERL_EPMD_PORT]]></c>
- is set correctly.</item>
+ <item><c>erl_errno</c>.</item>
+ <item>That the correct cookie was used</item>
+ <item>That EPMD is running</item>
+ <item>That the remote Erlang node on the other side is running the
+ same version of Erlang as the <c>ei</c> library</item>
+ <item>That environment variable <c>ERL_EPMD_PORT</c>
+ is set correctly</item>
</list>
- <p>The connection attempt can be traced by setting a tracelevel by either using
- <c><![CDATA[ei_set_tracelevel]]></c> or by setting the environment variable <c><![CDATA[EI_TRACELEVEL]]></c>.
- The different tracelevels has the following messages:</p>
+
+ <p>The connection attempt can be traced by setting a trace level by either
+ using <c>ei_set_tracelevel</c> or by setting environment
+ variable <c>EI_TRACELEVEL</c>.
+ The trace levels have the following messages:</p>
+
<list>
- <item>1: Verbose error messages</item>
- <item>2: Above messages and verbose warning messages </item>
- <item>3: Above messages and progress reports for connection handling</item>
- <item>4: Above messages and progress reports for communication</item>
- <item>5: Above messages and progress reports for data conversion</item>
+ <item>1: Verbose error messages</item>
+ <item>2: Above messages and verbose warning messages</item>
+ <item>3: Above messages and progress reports for connection handling
+ </item>
+ <item>4: Above messages and progress reports for communication</item>
+ <item>5: Above messages and progress reports for data conversion</item>
</list>
</section>
</cref>
-
diff --git a/lib/erl_interface/doc/src/ei_users_guide.xml b/lib/erl_interface/doc/src/ei_users_guide.xml
index 4b9809aee4..0eed50b50b 100644
--- a/lib/erl_interface/doc/src/ei_users_guide.xml
+++ b/lib/erl_interface/doc/src/ei_users_guide.xml
@@ -11,7 +11,7 @@
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
@@ -19,10 +19,10 @@
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>The El Library User's Guide</title>
+ <title>Erl_Interface User's Guide</title>
<prepared>Kent Boortz</prepared>
<responsible>Kent Boortz</responsible>
<docno></docno>
@@ -32,100 +32,158 @@
<rev></rev>
<file>ei_users_guide.xml</file>
</header>
- <p>The Erl_Interface library contains functions. which help you
- integrate programs written in C and Erlang. The functions in
- Erl_Interface support the following:</p>
- <list type="bulleted">
- <item>manipulation of data represented as Erlang data types</item>
- <item>conversion of data between C and Erlang formats</item>
- <item>encoding and decoding of Erlang data types for transmission or storage</item>
- <item>communication between C nodes and Erlang processes</item>
- <item>backup and restore of C node state to and from Mnesia</item>
- </list>
- <p>In the following sections, these topics are described:</p>
- <list type="bulleted">
- <item>compiling your code for use with Erl_Interface</item>
- <item>initializing Erl_Interface</item>
- <item>encoding, decoding, and sending Erlang terms</item>
- <item>building terms and patterns</item>
- <item>pattern matching</item>
- <item>connecting to a distributed Erlang node</item>
- <item>using EPMD</item>
- <item>sending and receiving Erlang messages</item>
- <item>remote procedure calls</item>
- <item>global names</item>
- <item>the registry</item>
- </list>
+
+ <section>
+ <title>Introduction</title>
+ <p>The <c>Erl_Interface</c> library contains functions that help you
+ integrate programs written in C and Erlang. The functions in
+ <c>Erl_Interface</c> support the following:</p>
+ <list type="bulleted">
+ <item>Manipulation of data represented as Erlang data types</item>
+ <item>Conversion of data between C and Erlang formats</item>
+ <item>Encoding and decoding of Erlang data types for transmission or
+ storage</item>
+ <item>Communication between C nodes and Erlang processes</item>
+ <item>Backup and restore of C node state to and from
+ <seealso marker="mnesia:mnesia">Mnesia</seealso></item>
+ </list>
+ <note>
+ <p>By default, the <c>Erl_Interface</c> libraries are only guaranteed
+ to be compatible with other Erlang/OTP components from the same
+ release as the libraries themselves. For information about how to
+ communicate with Erlang/OTP components from earlier releases, see
+ function <seealso marker="ei#ei_set_compat_rel">
+ <c>ei:ei_set_compat_rel</c></seealso> and
+ <seealso marker="erl_eterm#erl_set_compat_rel">
+ <c>erl_eterm:erl_set_compat_rel</c></seealso>.</p>
+ </note>
+
+ <section>
+ <title>Scope</title>
+ <p>In the following sections, these topics are described:</p>
+ <list type="bulleted">
+ <item>Compiling your code for use with <c>Erl_Interface</c></item>
+ <item>Initializing <c>Erl_Interface</c></item>
+ <item>Encoding, decoding, and sending Erlang terms</item>
+ <item>Building terms and patterns</item>
+ <item>Pattern matching</item>
+ <item>Connecting to a distributed Erlang node</item>
+ <item>Using the Erlang Port Mapper Daemon (EPMD)</item>
+ <item>Sending and receiving Erlang messages</item>
+ <item>Remote procedure calls</item>
+ <item>Using global names</item>
+ <item>Using the registry</item>
+ </list>
+ </section>
+
+ <section>
+ <title>Prerequisites</title>
+ <p>It is assumed that the reader is familiar with the Erlang programming
+ language.</p>
+ </section>
+ </section>
<section>
<title>Compiling and Linking Your Code</title>
- <p>In order to use any of the Erl_Interface functions, include the
+ <p>To use any of the <c>Erl_Interface</c> functions, include the
following lines in your code:</p>
+
<code type="none"><![CDATA[
#include "erl_interface.h"
#include "ei.h" ]]></code>
- <p>Determine where the top directory of your OTP installation is. You
- can find this out by starting Erlang and entering the following
+
+ <p>Determine where the top directory of your OTP installation is.
+ To find this, start Erlang and enter the following
command at the Eshell prompt:</p>
+
<code type="none"><![CDATA[
Eshell V4.7.4 (abort with ^G)
1> code:root_dir().
/usr/local/otp ]]></code>
- <p>To compile your code, make sure that your C compiler knows where
- to find <c><![CDATA[erl_interface.h]]></c> by specifying an appropriate <c><![CDATA[-I]]></c>
- argument on the command line, or by adding it to the <c><![CDATA[CFLAGS]]></c>
- definition in your <c><![CDATA[Makefile]]></c>. The correct value for this path is
- <c><![CDATA[$OTPROOT/lib/erl_interface]]></c><em>Vsn</em><c><![CDATA[/include]]></c>, where <c><![CDATA[$OTPROOT]]></c> is the path
- reported by <c><![CDATA[code:root_dir/0]]></c> in the above example, and <em>Vsn</em> is
- the version of the Erl_interface application, for example
- <c><![CDATA[erl_interface-3.2.3]]></c></p>
+
+ <p>To compile your code, ensure that your C compiler knows where
+ to find <c>erl_interface.h</c> by specifying an appropriate
+ <c>-I</c> argument on the command line, or add it to
+ the <c>CFLAGS</c> definition in your
+ <c>Makefile</c>. The correct value for this path is
+ <c>$OTPROOT/lib/erl_interface-$EIVSN/include</c>,
+ where:</p>
+
+ <list type="bulleted">
+ <item>
+ <p><c>$OTPROOT</c> is the path reported by
+ <c>code:root_dir/0</c> in the example above.</p>
+ </item>
+ <item>
+ <p><c>$EIVSN</c> is the version of the <c>Erl_Interface</c> application,
+ for example, <c>erl_interface-3.2.3</c>.</p>
+ </item>
+ </list>
+
+ <p>Compiling the code:</p>
+
<code type="none"><![CDATA[
$ cc -c -I/usr/local/otp/lib/erl_interface-3.2.3/include myprog.c ]]></code>
- <p>When linking, you will need to specify the path to
- <c><![CDATA[liberl_interface.a]]></c> and <c><![CDATA[libei.a]]></c> with
- <c><![CDATA[-L$OTPROOT/lib/erl_interface-3.2.3/lib]]></c>, and you will need to specify the
- name of the libraries with <c><![CDATA[-lerl_interface -lei]]></c>. You can do
- this on the command line or by adding the flags to the <c><![CDATA[LDFLAGS]]></c>
- definition in your <c><![CDATA[Makefile]]></c>.</p>
+
+ <p>When linking:</p>
+
+ <list type="bulleted">
+ <item>Specify the path to <c>liberl_interface.a</c> and
+ <c>libei.a</c> with
+ <c>-L$OTPROOT/lib/erl_interface-3.2.3/lib</c>.</item>
+ <item>Specify the name of the libraries with
+ <c>-lerl_interface -lei</c>.</item>
+ </list>
+
+ <p>Do this on the command line or add the flags to the
+ <c>LDFLAGS</c> definition in your
+ <c>Makefile</c>.</p>
+
+ <p>Linking the code:</p>
+
<code type="none"><![CDATA[
$ ld -L/usr/local/otp/lib/erl_interface-3.2.3/
lib myprog.o -lerl_interface -lei -o myprog ]]></code>
- <p>Also, on some systems it may be necessary to link with some
- additional libraries (e.g. <c><![CDATA[libnsl.a]]></c> and <c><![CDATA[libsocket.a]]></c> on
- Solaris, or <c><![CDATA[wsock32.lib]]></c> on Windows) in order to use the
- communication facilities of Erl_Interface.</p>
- <p>If you are using Erl_Interface functions in a threaded
+
+ <p>On some systems it can be necessary to link with some more
+ libraries (for example, <c>libnsl.a</c> and
+ <c>libsocket.a</c> on Solaris, or
+ <c>wsock32.lib</c> on Windows) to use the
+ communication facilities of <c>Erl_Interface</c>.</p>
+
+ <p>If you use the <c>Erl_Interface</c> functions in a threaded
application based on POSIX threads or Solaris threads, then
- Erl_Interface needs access to some of the synchronization
- facilities in your threads package, and you will need to specify
- additional compiler flags in order to indicate which of the packages
- you are using. Define <c><![CDATA[_REENTRANT]]></c> and either <c><![CDATA[STHREADS]]></c> or
- <c><![CDATA[PTHREADS]]></c>. The default is to use POSIX threads if
- <c><![CDATA[_REENTRANT]]></c> is specified.</p>
+ <c>Erl_Interface</c> needs access to some of the synchronization
+ facilities in your threads package. You must specify extra
+ compiler flags to indicate which of the packages you use. Define
+ <c>_REENTRANT</c> and either <c>STHREADS</c> or
+ <c>PTHREADS</c>. The default is to use POSIX threads if
+ <c>_REENTRANT</c> is specified.</p>
</section>
<section>
- <title>Initializing the erl_interface Library</title>
- <p>Before calling any of the other Erl_Interface functions, you
- must call <c><![CDATA[erl_init()]]></c> exactly once to initialize the library.
- <c><![CDATA[erl_init()]]></c> takes two arguments, however the arguments are no
- longer used by Erl_Interface, and should therefore be specified
- as <c><![CDATA[erl_init(NULL,0)]]></c>.</p>
+ <title>Initializing the Erl_Interface Library</title>
+ <p>Before calling any of the other <c>Erl_Interface</c> functions, call
+ <c>erl_init()</c> exactly once to initialize the library.
+ <c>erl_init()</c> takes two arguments. However, the arguments
+ are no longer used by <c>Erl_Interface</c> and are therefore to be
+ specified as <c>erl_init(NULL,0)</c>.</p>
</section>
<section>
- <title>Encoding, Decoding and Sending Erlang Terms</title>
+ <title>Encoding, Decoding, and Sending Erlang Terms</title>
<p>Data sent between distributed Erlang nodes is encoded in the
- Erlang external format. Consequently, you have to encode and decode
+ Erlang external format. You must therefore encode and decode
Erlang terms into byte streams if you want to use the distribution
- protocol to communicate between a C program and Erlang. </p>
- <p>The Erl_Interface library supports this activity. It has a
- number of C functions which create and manipulate Erlang data
+ protocol to communicate between a C program and Erlang.</p>
+
+ <p>The <c>Erl_Interface</c> library supports this activity. It has
+ several C functions that create and manipulate Erlang data
structures. The library also contains an encode and a decode function.
- The example below shows how to create and encode an Erlang tuple
- <c><![CDATA[{tobbe,3928}]]></c>:</p>
- <code type="none"><![CDATA[
+ The following example shows how to create and encode an Erlang tuple
+ <c>{tobbe,3928}</c>:</p>
+ <code type="none"><![CDATA[
ETERM *arr[2], *tuple;
char buf[BUFSIZ];
int i;
@@ -134,58 +192,70 @@ arr[0] = erl_mk_atom("tobbe");
arr[1] = erl_mk_integer(3928);
tuple = erl_mk_tuple(arr, 2);
i = erl_encode(tuple, buf); ]]></code>
- <p>Alternatively, you can use <c><![CDATA[erl_send()]]></c> and
- <c><![CDATA[erl_receive_msg]]></c>, which handle the encoding and decoding of
- messages transparently.</p>
- <p>Refer to the Reference Manual for a complete description of the
- following modules:</p>
+
+ <p>Alternatively, you can use <c>erl_send()</c> and
+ <c>erl_receive_msg</c>, which handle the encoding and
+ decoding of messages transparently.</p>
+
+ <p>For a complete description, see the following modules:</p>
<list type="bulleted">
- <item>the <c><![CDATA[erl_eterm]]></c> module for creating Erlang terms</item>
- <item>the <c><![CDATA[erl_marshal]]></c> module for encoding and decoding routines.</item>
+ <item><seealso marker="erl_eterm"><c>erl_eterm</c></seealso>
+ for creating Erlang terms</item>
+ <item><seealso marker="erl_marshal"><c>erl_marshal</c></seealso>
+ for encoding and decoding routines</item>
</list>
</section>
<section>
+ <marker id="building_terms_and_patterns"/>
<title>Building Terms and Patterns</title>
- <p>The previous example can be simplified by using
- <c><![CDATA[erl_format()]]></c> to create an Erlang term.</p>
- <code type="none"><![CDATA[
+ <p>The previous example can be simplified by using the
+ <seealso marker="erl_format"><c>erl_format</c></seealso> module
+ to create an Erlang term:</p>
+ <code type="none"><![CDATA[
ETERM *ep;
ep = erl_format("{~a,~i}", "tobbe", 3928); ]]></code>
- <p>Refer to the Reference Manual, the <c><![CDATA[erl_format]]></c> module, for a
- full description of the different format directives. The following
- example is more complex:</p>
- <code type="none"><![CDATA[
+ <p>For a complete description of the different format directives, see
+ the <seealso marker="erl_format"><c>erl_format</c></seealso> module.</p>
+
+ <p>The following example is more complex:</p>
+
+ <code type="none"><![CDATA[
ETERM *ep;
ep = erl_format("[{name,~a},{age,~i},{data,~w}]",
"madonna",
21,
erl_format("[{adr,~s,~i}]", "E-street", 42));
erl_free_compound(ep); ]]></code>
- <p>As in previous examples, it is your responsibility to free the
- memory allocated for Erlang terms. In this example,
- <c><![CDATA[erl_free_compound()]]></c> ensures that the complete term pointed to
- by <c><![CDATA[ep]]></c> is released. This is necessary, because the pointer from
- the second call to <c><![CDATA[erl_format()]]></c> is lost. </p>
- <p>The following
- example shows a slightly different solution:</p>
- <code type="none"><![CDATA[
+ <p>As in the previous examples, it is your responsibility to free the
+ memory allocated for Erlang terms. In this example,
+ <c>erl_free_compound()</c> ensures that the complete term
+ pointed to by <c>ep</c> is released. This is necessary
+ because the pointer from the second call to <c>erl_format</c> is lost.</p>
+
+ <p>The following example shows a slightly different solution:</p>
+
+ <code type="none"><![CDATA[
ETERM *ep,*ep2;
ep2 = erl_format("[{adr,~s,~i}]","E-street",42);
ep = erl_format("[{name,~a},{age,~i},{data,~w}]",
"madonna", 21, ep2);
erl_free_term(ep);
erl_free_term(ep2); ]]></code>
+
<p>In this case, you free the two terms independently. The order in
- which you free the terms <c><![CDATA[ep]]></c> and <c><![CDATA[ep2]]></c> is not important,
- because the Erl_Interface library uses reference counting to
- determine when it is safe to actually remove objects. </p>
- <p>If you are not sure whether you have freed the terms properly, you
+ which you free the terms <c>ep</c> and <c>ep2</c>
+ is not important,
+ because the <c>Erl_Interface</c> library uses reference counting to
+ determine when it is safe to remove objects.</p>
+
+ <p>If you are unsure whether you have freed the terms properly, you
can use the following function to see the status of the fixed term
allocator:</p>
+
<code type="none"><![CDATA[
long allocated, freed;
@@ -196,36 +266,49 @@ printf("length of freelist: %ld\n",freed);
/* really free the freelist */
erl_eterm_release();
]]></code>
- <p>Refer to the Reference Manual, the <c><![CDATA[erl_malloc]]></c> module for more
- information.</p>
+
+ <p>For more information, see the
+ <seealso marker="erl_malloc"><c>erl_malloc</c></seealso> module.</p>
</section>
<section>
<title>Pattern Matching</title>
- <p>An Erlang pattern is a term that may contain unbound variables or
- <c><![CDATA["do not care"]]></c> symbols. Such a pattern can be matched against a
+ <p>An Erlang pattern is a term that can contain unbound variables or
+ <c>"do not care"</c> symbols. Such a pattern can be matched
+ against a
term and, if the match is successful, any unbound variables in the
pattern will be bound as a side effect. The content of a bound
- variable can then be retrieved.</p>
- <code type="none"><![CDATA[
+ variable can then be retrieved:</p>
+ <code type="none"><![CDATA[
ETERM *pattern;
pattern = erl_format("{madonna,Age,_}"); ]]></code>
- <p><c><![CDATA[erl_match()]]></c> is used to perform pattern matching. It takes a
+
+ <p>The <seealso marker="erl_format#erl_match">
+ <c>erl_format:erl_match</c></seealso> function
+ performs pattern matching. It takes a
pattern and a term and tries to match them. As a side effect any unbound
- variables in the pattern will be bound. In the following example, we
- create a pattern with a variable <em>Age</em> which appears at two
+ variables in the pattern will be bound. In the following example, a
+ pattern is created with a variable <c>Age</c>, which is included at two
positions in the tuple. The pattern match is performed as follows:</p>
- <list type="ordered">
- <item><c><![CDATA[erl_match()]]></c> will bind the contents of
- <em>Age</em> to <em>21</em> the first time it reaches the variable</item>
- <item>the second occurrence of <em>Age</em> will cause a test for
- equality between the terms since <em>Age</em> is already bound to
- <em>21</em>. Since <em>Age</em> is bound to 21, the equality test will
- succeed and the match continues until the end of the pattern.</item>
- <item>if the end of the pattern is reached, the match succeeds and you
- can retrieve the contents of the variable</item>
+
+ <list type="bulleted">
+ <item>
+ <p><c>erl_match</c> binds the contents of <c>Age</c> to <c>21</c>
+ the first time it reaches the variable.</p>
+ </item>
+ <item>
+ <p>The second occurrence of <c>Age</c> causes a test for
+ equality between the terms, as <c>Age</c> is already bound to
+ <c>21</c>. As <c>Age</c> is bound to <c>21</c>, the equality test
+ succeeds and the match continues until the end of the pattern.</p>
+ </item>
+ <item>
+ <p>If the end of the pattern is reached, the match succeeds and you
+ can retrieve the contents of the variable.</p>
+ </item>
</list>
+
<code type="none"><![CDATA[
ETERM *pattern,*term;
pattern = erl_format("{madonna,Age,Age}");
@@ -239,99 +322,136 @@ if (erl_match(pattern, term)) {
}
erl_free_term(pattern);
erl_free_term(term); ]]></code>
- <p>Refer to the Reference Manual, the <c><![CDATA[erl_match()]]></c> function for
- more information.</p>
+
+ <p>For more information, see the
+ <seealso marker="erl_format#erl_match">
+ <c>erl_format:erl_match</c></seealso> function.</p>
</section>
<section>
<title>Connecting to a Distributed Erlang Node</title>
- <p>In order to connect to a distributed Erlang node you need to first
- initialize the connection routine with <c><![CDATA[erl_connect_init()]]></c>,
- which stores information such as the host name, node name, and IP
+ <p>To connect to a distributed Erlang node, you must first
+ initialize the connection routine with
+ <seealso marker="erl_connect#erl_connect_init">
+ <c>erl_connect:erl_connect_init</c></seealso>,
+ which stores information, such as the hostname, node name, and IP
address for later use:</p>
+
<code type="none"><![CDATA[
int identification_number = 99;
int creation=1;
char *cookie="a secret cookie string"; /* An example */
erl_connect_init(identification_number, cookie, creation); ]]></code>
- <p>Refer to the Reference Manual, the <c><![CDATA[erl_connect]]></c> module for more information.</p>
+
+ <p>For more information, see the
+ <seealso marker="erl_connect"><c>erl_connect</c></seealso> module.</p>
+
<p>After initialization, you set up the connection to the Erlang node.
- Use <c><![CDATA[erl_connect()]]></c> to specify the Erlang node you want to
- connect to. The following example sets up the connection and should
- result in a valid socket file descriptor:</p>
+ To specify the Erlang node you want to connect to, use
+ <c>erl_connect()</c>. The following example sets up the
+ connection and is to result in a valid socket file descriptor:</p>
+
<code type="none"><![CDATA[
int sockfd;
char *nodename="[email protected]"; /* An example */
if ((sockfd = erl_connect(nodename)) < 0)
erl_err_quit("ERROR: erl_connect failed"); ]]></code>
- <p><c><![CDATA[erl_err_quit()]]></c> prints the specified string and terminates
- the program. Refer to the Reference Manual, the <c><![CDATA[erl_error()]]></c>
- function for more information.</p>
+
+ <p><c>erl_err_quit()</c> prints the specified string and
+ terminates the program. For more information, see the
+ <seealso marker="erl_error"><c>erl_error</c></seealso> module.</p>
</section>
<section>
<title>Using EPMD</title>
- <p><c><![CDATA[Epmd]]></c> is the Erlang Port Mapper Daemon. Distributed Erlang nodes
- register with <c><![CDATA[epmd]]></c> on the localhost to indicate to other nodes that
- they exist and can accept connections. <c><![CDATA[Epmd]]></c> maintains a register of
+ <p><seealso marker="erts:epmd"><c>erts:epmd</c></seealso>
+ is the Erlang Port Mapper Daemon. Distributed
+ Erlang nodes register with <c>epmd</c> on the local host to
+ indicate to other nodes that they exist and can accept connections.
+ <c>epmd</c> maintains a register of
node and port number information, and when a node wishes to connect to
- another node, it first contacts <c><![CDATA[epmd]]></c> in order to find out the correct
- port number to connect to.</p>
- <p>When you use <c><![CDATA[erl_connect()]]></c> to connect to an Erlang node, a
- connection is first made to <c><![CDATA[epmd]]></c> and, if the node is known, a
+ another node, it first contacts <c>epmd</c> to find the
+ correct port number to connect to.</p>
+
+ <p>When you use
+ <seealso marker="erl_connect"><c>erl_connect</c></seealso>
+ to connect to an Erlang node, a connection is first made to
+ <c>epmd</c> and, if the node is known, a
connection is then made to the Erlang node.</p>
- <p>C nodes can also register themselves with <c><![CDATA[epmd]]></c> if they want other
+
+ <p>C nodes can also register themselves with <c>epmd</c>
+ if they want other
nodes in the system to be able to find and connect to them.</p>
- <p>Before registering with <c><![CDATA[epmd]]></c>, you need to first create a listen socket
- and bind it to a port. Then:</p>
+
+ <p>Before registering with <c>epmd</c>, you must first
+ create a listen socket and bind it to a port. Then:</p>
+
<code type="none"><![CDATA[
int pub;
pub = erl_publish(port); ]]></code>
- <p><c><![CDATA[pub]]></c> is a file descriptor now connected to <c><![CDATA[epmd]]></c>. <c><![CDATA[Epmd]]></c>
- monitors the other end of the connection, and if it detects that the
- connection has been closed, the node will be unregistered. So, if you
- explicitly close the descriptor or if your node fails, it will be
- unregistered from <c><![CDATA[epmd]]></c>.</p>
- <p>Be aware that on some systems (such as VxWorks), a failed node will
- not be detected by this mechanism since the operating system does not
+
+ <p><c>pub</c> is a file descriptor now connected to
+ <c>epmd</c>. <c>epmd</c>
+ monitors the other end of the connection. If it detects that the
+ connection has been closed, the node becomes unregistered. So, if you
+ explicitly close the descriptor or if your node fails, it becomes
+ unregistered from <c>epmd</c>.</p>
+
+ <p>Notice that on some systems (such as VxWorks), a failed node is
+ not detected by this mechanism, as the operating system does not
automatically close descriptors that were left open when the node
- failed. If a node has failed in this way, <c><![CDATA[epmd]]></c> will prevent you from
- registering a new node with the old name, since it thinks that the old
+ failed. If a node has failed in this way, <c>epmd</c>
+ prevents you from
+ registering a new node with the old name, as it thinks that the old
name is still in use. In this case, you must unregister the name
explicitly:</p>
+
<code type="none"><![CDATA[
erl_unpublish(node); ]]></code>
- <p>This will cause <c><![CDATA[epmd]]></c> to close the connection from the far end. Note
+
+ <p>This causes <c>epmd</c> to close the connection from the
+ far end. Notice
that if the name was in fact still in use by a node, the results of
this operation are unpredictable. Also, doing this does not cause the
- local end of the connection to close, so resources may be consumed.</p>
+ local end of the connection to close, so resources can be consumed.</p>
</section>
<section>
<title>Sending and Receiving Erlang Messages</title>
<p>Use one of the following two functions to send messages:</p>
+
<list type="bulleted">
- <item><c><![CDATA[erl_send()]]></c></item>
- <item><c><![CDATA[erl_reg_send()]]></c></item>
+ <item><seealso marker="erl_connect#erl_send">
+ <c>erl_connect:erl_send</c></seealso></item>
+ <item><seealso marker="erl_connect#erl_reg_send">
+ <c>erl_connect:erl_reg_send</c></seealso></item>
</list>
- <p>As in Erlang, it is possible to send messages to a
- <em>Pid</em> or to a registered name. It is easier to send a
- message to a registered name because it avoids the problem of finding
- a suitable <em>Pid</em>.</p>
+
+ <p>As in Erlang, messages can be sent to a
+ pid or to a registered name. It is easier to send a
+ message to a registered name, as it avoids the problem of finding
+ a suitable pid.</p>
+
<p>Use one of the following two functions to receive messages:</p>
+
<list type="bulleted">
- <item><c><![CDATA[erl_receive()]]></c></item>
- <item><c><![CDATA[erl_receive_msg()]]></c></item>
+ <item><seealso marker="erl_connect#erl_receive">
+ <c>erl_connect:erl_receive</c></seealso></item>
+ <item><seealso marker="erl_connect#erl_receive_msg">
+ <c>erl_connect:erl_receive_msg</c></seealso></item>
</list>
- <p><c><![CDATA[erl_receive()]]></c> receives the message into a buffer, while
- <c><![CDATA[erl_receive_msg()]]></c> decodes the message into an Erlang term. </p>
+
+ <p><c>erl_receive()</c> receives the message into a buffer,
+ while <c>erl_receive_msg()</c> decodes the message into an
+ Erlang term.</p>
<section>
<title>Example of Sending Messages</title>
- <p>In the following example, <c><![CDATA[{Pid, hello_world}]]></c> is
- sent to a registered process <c><![CDATA[my_server]]></c>. The message is encoded
- by <c><![CDATA[erl_send()]]></c>:</p>
+ <p>In the following example, <c>{Pid, hello_world}</c> is
+ sent to a registered process <c>my_server</c>. The message
+ is encoded by <c>erl_send()</c>:</p>
+
<code type="none"><![CDATA[
extern const char *erl_thisnodename(void);
extern short erl_thiscreation(void);
@@ -345,16 +465,19 @@ emsg = erl_mk_tuple(arr, 2);
erl_reg_send(sockfd, "my_server", emsg);
erl_free_term(emsg); ]]></code>
+
<p>The first element of the tuple that is sent is your own
- <em>Pid</em>. This enables <c><![CDATA[my_server]]></c> to reply. Refer to the
- Reference Manual, the <c><![CDATA[erl_connect]]></c> module for more information
- about send primitives.</p>
+ pid. This enables <c>my_server</c> to reply.
+ For more information about the primitives, see the
+ <seealso marker="erl_connect"><c>erl_connect</c></seealso> module.</p>
</section>
<section>
<title>Example of Receiving Messages</title>
- <p>In this example <c><![CDATA[{Pid, Something}]]></c> is received. The
- received Pid is then used to return <c><![CDATA[{goodbye,Pid}]]></c></p>
+ <p>In this example, <c>{Pid, Something}</c> is received. The
+ received pid is then used to return
+ <c>{goodbye,Pid}</c>.</p>
+
<code type="none"><![CDATA[
ETERM *arr[2], *answer;
int sockfd,rc;
@@ -370,22 +493,25 @@ if ((rc = erl_receive_msg(sockfd , buf, BUFSIZE, &emsg)) == ERL_MSG) {
erl_free_term(emsg.msg);
erl_free_term(emsg.to);
} ]]></code>
- <p>In order to provide robustness, a distributed Erlang node
- occasionally polls all its connected neighbours in an attempt to
- detect failed nodes or communication links. A node which receives such
- a message is expected to respond immediately with an <c><![CDATA[ERL_TICK]]></c> message.
- This is done automatically by <c><![CDATA[erl_receive()]]></c>, however when this
- has occurred <c><![CDATA[erl_receive]]></c> returns <c><![CDATA[ERL_TICK]]></c> to the caller
- without storing a message into the <c><![CDATA[ErlMessage]]></c> structure.</p>
+
+ <p>To provide robustness, a distributed Erlang node
+ occasionally polls all its connected neighbors in an attempt to
+ detect failed nodes or communication links. A node that receives such
+ a message is expected to respond immediately with an
+ <c>ERL_TICK</c> message. This is done automatically by
+ <c>erl_receive()</c>. However, when this has occurred,
+ <c>erl_receive</c> returns <c>ERL_TICK</c> to
+ the caller without storing a message into the
+ <c>ErlMessage</c> structure.</p>
+
<p>When a message has been received, it is the caller's responsibility
- to free the received message <c><![CDATA[emsg.msg]]></c> as well as <c><![CDATA[emsg.to]]></c>
- or <c><![CDATA[emsg.from]]></c>, depending on the type of message received.</p>
- <p>Refer to the Reference Manual for additional information about the
- following modules:</p>
- <list type="bulleted">
- <item><c><![CDATA[erl_connect]]></c></item>
- <item><c><![CDATA[erl_eterm]]></c>.</item>
- </list>
+ to free the received message <c>emsg.msg</c> and
+ <c>emsg.to</c> or <c>emsg.from</c>,
+ depending on the type of message received.</p>
+
+ <p>For more information, see the
+ <seealso marker="erl_connect"><c>erl_connect</c></seealso> and
+ <seealso marker="erl_eterm"><c>erl_eterm</c></seealso> modules.</p>
</section>
</section>
@@ -394,10 +520,12 @@ if ((rc = erl_receive_msg(sockfd , buf, BUFSIZE, &emsg)) == ERL_MSG) {
<p>An Erlang node acting as a client to another Erlang node
typically sends a request and waits for a reply. Such a request is
included in a function call at a remote node and is called a remote
- procedure call. The following example shows how the
- Erl_Interface library supports remote procedure calls:</p>
- <code type="none"><![CDATA[
+ procedure call.</p>
+ <p>The following example shows how the
+ <c>Erl_Interface</c> library supports remote procedure calls:</p>
+
+ <code type="none"><![CDATA[
char modname[]=THE_MODNAME;
ETERM *reply,*ep;
ep = erl_format("[~a,[]]", modname);
@@ -409,26 +537,34 @@ if (!erl_match(ep, reply))
erl_err_msg("<ERROR> compiler errors !\n");
erl_free_term(ep);
erl_free_term(reply); ]]></code>
- <p><c><![CDATA[c:c/1]]></c> is called to compile the specified module on the
- remote node. <c><![CDATA[erl_match()]]></c> checks that the compilation was
- successful by testing for the expected <c><![CDATA[ok]]></c>.</p>
- <p>Refer to the Reference Manual, the <c><![CDATA[erl_connect]]></c> module for
- more information about <c><![CDATA[erl_rpc()]]></c>, and its companions
- <c><![CDATA[erl_rpc_to()]]></c> and <c><![CDATA[erl_rpc_from()]]></c>.</p>
+
+ <p><c>c:c/1</c> is called to compile the specified module on
+ the remote node. <c>erl_match()</c> checks that the
+ compilation was
+ successful by testing for the expected <c>ok</c>.</p>
+
+ <p>For more information about <c>erl_rpc()</c> and its
+ companions <c>erl_rpc_to()</c> and
+ <c>erl_rpc_from()</c>, see the
+ <seealso marker="erl_connect"><c>erl_connect</c></seealso> module.</p>
</section>
<section>
<title>Using Global Names</title>
- <p>A C node has access to names registered through the Erlang Global
- module. Names can be looked up, allowing the C node to send messages
+ <p>A C node has access to names registered through the
+ <seealso marker="kernel:global"><c>global</c></seealso>
+ module in Kernel. Names can be looked up, allowing the C node to send messages
to named Erlang services. C nodes can also register global names,
allowing them to provide named services to Erlang processes or other C
- nodes. </p>
- <p>Erl_Interface does not provide a native implementation of the global
- service. Instead it uses the global services provided by a "nearby"
- Erlang node. In order to use the services described in this section,
+ nodes.</p>
+
+ <p><c>Erl_Interface</c> does not provide a native implementation of the
+ global service. Instead it uses the global services provided by a "nearby"
+ Erlang node. To use the services described in this section,
it is necessary to first open a connection to an Erlang node.</p>
+
<p>To see what names there are:</p>
+
<code type="none"><![CDATA[
char **names;
int count;
@@ -441,71 +577,102 @@ if (names)
printf("%s\n",names[i]);
free(names); ]]></code>
- <p><c><![CDATA[erl_global_names()]]></c> allocates and returns a buffer containing
- all the names known to global. <c><![CDATA[count]]></c> will be initialized to
- indicate how many names are in the array. The array of strings in
- names is terminated by a NULL pointer, so it is not necessary to use
- <c><![CDATA[count]]></c> to determine when the last name is reached.</p>
+
+ <p><seealso marker="erl_global#erl_global_names">
+ <c>erl_global:erl_global_names</c></seealso>
+ allocates and returns a buffer containing
+ all the names known to the <c>global</c> module in <c>Kernel</c>.
+ <c>count</c> is initialized to
+ indicate the number of names in the array. The array of strings in names
+ is terminated by a <c>NULL</c> pointer, so it is not necessary to use
+ <c>count</c> to determine when the last name is reached.</p>
+
<p>It is the caller's responsibility to free the array.
- <c><![CDATA[erl_global_names()]]></c> allocates the array and all of the strings
- using a single call to <c><![CDATA[malloc()]]></c>, so <c><![CDATA[free(names)]]></c> is all
- that is necessary.</p>
+ <c>erl_global_names</c> allocates the array and all the strings
+ using a single call to <c>malloc()</c>, so
+ <c>free(names)</c> is all that is necessary.</p>
+
<p>To look up one of the names:</p>
+
<code type="none"><![CDATA[
ETERM *pid;
char node[256];
pid = erl_global_whereis(fd,"schedule",node); ]]></code>
- <p>If <c><![CDATA["schedule"]]></c> is known to global, an Erlang pid is returned
- that can be used to send messages to the schedule service.
- Additionally, <c><![CDATA[node]]></c> will be initialized to contain the name of
+
+ <p>If <c>"schedule"</c> is known to the
+ <c>global</c> module in <c>Kernel</c>, an Erlang pid is
+ returned that can be used to send messages to the schedule service.
+ Also, <c>node</c> is initialized to contain the name of
the node where the service is registered, so that you can make a
- connection to it by simply passing the variable to <c><![CDATA[erl_connect()]]></c>.</p>
+ connection to it by simply passing the variable to
+ <seealso marker="erl_connect"><c>erl_connect</c></seealso>.</p>
+
<p>Before registering a name, you should already have registered your
- port number with <c><![CDATA[epmd]]></c>. This is not strictly necessary, but if you
+ port number with <c>epmd</c>. This is not strictly necessary,
+ but if you
neglect to do so, then other nodes wishing to communicate with your
- service will be unable to find or connect to your process.</p>
+ service cannot find or connect to your process.</p>
+
<p>Create a pid that Erlang processes can use to communicate with your
service:</p>
+
<code type="none"><![CDATA[
ETERM *pid;
pid = erl_mk_pid(thisnode,14,0,0);
erl_global_register(fd,servicename,pid); ]]></code>
- <p>After registering the name, you should use <c><![CDATA[erl_accept()]]></c> to wait for
- incoming connections.</p>
- <p>Do not forget to free <c><![CDATA[pid]]></c> later with <c><![CDATA[erl_free_term()]]></c>!</p>
+
+ <p>After registering the name, use
+ <seealso marker="erl_connect#erl_accept">
+ <c>erl_connect:erl_accept</c></seealso>
+ to wait for incoming connections.</p>
+
+ <note>
+ <p>Remember to free <c>pid</c> later with
+ <seealso marker="erl_malloc#erl_free_term">
+ <c>erl_malloc:erl_free_term</c></seealso>.</p>
+ </note>
+
<p>To unregister a name:</p>
+
<code type="none"><![CDATA[
erl_global_unregister(fd,servicename); ]]></code>
</section>
<section>
- <title>The Registry</title>
+ <title>Using the Registry</title>
<p>This section describes the use of the registry, a simple mechanism
for storing key-value pairs in a C-node, as well as backing them up or
- restoring them from a Mnesia table on an Erlang node. More detailed
- information about the individual API functions can be found in the
- Reference Manual.</p>
- <p>Keys are strings, i.e. 0-terminated arrays of characters, and values
- are arbitrary objects. Although integers and floating point numbers
+ restoring them from an <c>Mnesia</c> table on an Erlang node. For more
+ detailed information about the individual API functions, see the
+ <seealso marker="registry"><c>registry</c></seealso> module.</p>
+
+ <p>Keys are strings, that is, <c>NULL</c>-terminated arrays of characters, and
+ values are arbitrary objects. Although integers and floating point numbers
are treated specially by the registry, you can store strings or binary
objects of any type as pointers.</p>
- <p>To start, you need to open a registry:</p>
+
+ <p>To start, open a registry:</p>
+
<code type="none"><![CDATA[
ei_reg *reg;
reg = ei_reg_open(45); ]]></code>
- <p>The number 45 in the example indicates the approximate number of
+
+ <p>The number <c>45</c> in the example indicates the approximate number of
objects that you expect to store in the registry. Internally the
registry uses hash tables with collision chaining, so there is no
absolute upper limit on the number of objects that the registry can
- contain, but if performance or memory usage are important, then you
- should choose a number accordingly. The registry can be resized later.</p>
+ contain, but if performance or memory usage is important, then you
+ are to choose a number accordingly. The registry can be resized later.</p>
+
<p>You can open as many registries as you like (if memory permits).</p>
- <p>Objects are stored and retrieved through set and get functions. In
- the following examples you see how to store integers, floats, strings
+
+ <p>Objects are stored and retrieved through set and get functions.
+ The following example shows how to store integers, floats, strings,
and arbitrary binary objects:</p>
+
<code type="none"><![CDATA[
struct bonk *b = malloc(sizeof(*b));
char *name = malloc(7);
@@ -519,13 +686,16 @@ ei_reg_setsval(reg,"name",name);
b->l = 42;
b->m = 12;
ei_reg_setpval(reg,"jox",b,sizeof(*b)); ]]></code>
- <p>If you attempt to store an object in the registry and there is an
- existing object with the same key, the new value will replace the old
+
+ <p>If you try to store an object in the registry and there is an
+ existing object with the same key, the new value replaces the old
one. This is done regardless of whether the new object and the old one
have the same type, so you can, for example, replace a string with an
- integer. If the existing value is a string or binary, it will be freed
+ integer. If the existing value is a string or binary, it is freed
before the new value is assigned.</p>
+
<p>Stored values are retrieved from the registry as follows:</p>
+
<code type="none"><![CDATA[
long i;
double f;
@@ -537,77 +707,100 @@ i = ei_reg_getival(reg,"age");
f = ei_reg_getfval(reg,"height");
s = ei_reg_getsval(reg,"name");
b = ei_reg_getpval(reg,"jox",&size); ]]></code>
- <p>In all of the above examples, the object must exist and it must be of
+
+ <p>In all the above examples, the object must exist and it must be of
the right type for the specified operation. If you do not know the
- type of a given object, you can ask:</p>
+ type of an object, you can ask:</p>
+
<code type="none"><![CDATA[
struct ei_reg_stat buf;
ei_reg_stat(reg,"name",&buf); ]]></code>
- <p>Buf will be initialized to contain object attributes.</p>
+
+ <p>Buf is initialized to contain object attributes.</p>
+
<p>Objects can be removed from the registry:</p>
+
<code type="none"><![CDATA[
ei_reg_delete(reg,"name"); ]]></code>
+
<p>When you are finished with a registry, close it to remove all the
objects and free the memory back to the system:</p>
+
<code type="none"><![CDATA[
ei_reg_close(reg); ]]></code>
<section>
<title>Backing Up the Registry to Mnesia</title>
- <p>The contents of a registry can be backed up to Mnesia on a "nearby"
- Erlang node. You need to provide an open connection to the Erlang node
- (see <c><![CDATA[erl_connect()]]></c>). Also, Mnesia 3.0 or later must be running
+ <p>The contents of a registry can be backed up to
+ <seealso marker="mnesia:mnesia"><c>Mnesia</c></seealso> on a "nearby" Erlang
+ node. You must provide an open connection to the Erlang node
+ (see <seealso marker="erl_connect"><c>erl_connect</c></seealso>).
+ Also, <c>Mnesia</c> 3.0 or later must be running
on the Erlang node before the backup is initiated:</p>
+
<code type="none"><![CDATA[
ei_reg_dump(fd, reg, "mtab", dumpflags); ]]></code>
- <p>The example above will backup the contents of the registry to the
- specified Mnesia table <c><![CDATA["mtab"]]></c>. Once a registry has been backed
- up to Mnesia in this manner, additional backups will only affect
- objects that have been modified since the most recent backup, i.e.
- objects that have been created, changed or deleted. The backup
- operation is done as a single atomic transaction, so that the entire
- backup will be performed or none of it will.</p>
- <p>In the same manner, a registry can be restored from a Mnesia table:</p>
+
+ <p>This example back up the contents of the registry to the
+ specified <c>Mnesia</c> table <c>"mtab"</c>.
+ Once a registry has been backed
+ up to <c>Mnesia</c> like this, more backups only affect
+ objects that have been modified since the most recent backup, that is,
+ objects that have been created, changed, or deleted. The backup
+ operation is done as a single atomic transaction, so that either the
+ entire backup is performed or none of it.</p>
+
+ <p>Likewise, a registry can be restored from a <c>Mnesia</c> table:</p>
+
<code type="none"><![CDATA[
ei_reg_restore(fd, reg, "mtab"); ]]></code>
- <p>This will read the entire contents of <c><![CDATA["mtab"]]></c> into the specified
- registry. After the restore, all of the objects in the registry will
- be marked as unmodified, so a subsequent backup will only affect
+
+ <p>This reads the entire contents of <c>"mtab"</c> into the
+ specified registry. After the restore, all the objects in the registry
+ are marked as unmodified, so a later backup only affects
objects that you have modified since the restore.</p>
- <p>Note that if you restore to a non-empty registry, objects in the
- table will overwrite objects in the registry with the same keys. Also,
+
+ <p>Notice that if you restore to a non-empty registry, objects in the
+ table overwrite objects in the registry with the same keys. Also,
the <em>entire</em> contents of the registry is marked as unmodified
after the restore, including any modified objects that were not
- overwritten by the restore operation. This may not be your intention.</p>
+ overwritten by the restore operation. This may not be your
+ intention.</p>
</section>
<section>
<title>Storing Strings and Binaries</title>
<p>When string or binary objects are stored in the registry it is
- important that a number of simple guidelines are followed. </p>
+ important that some simple guidelines are followed.</p>
+
<p>Most importantly, the object must have been created with a single call
- to <c><![CDATA[malloc()]]></c> (or similar), so that it can later be removed by a
- single call to <c><![CDATA[free()]]></c>. Objects will be freed by the registry
+ to <c>malloc()</c> (or similar), so that it can later be
+ removed by a single call to <c>free()</c>.
+ Objects are freed by the registry
when it is closed, or when you assign a new value to an object that
previously contained a string or binary.</p>
- <p>You should also be aware that if you store binary objects that are
- context-dependent (e.g. containing pointers or open file descriptors),
- they will lose their meaning if they are backed up to a Mnesia table
- and subsequently restored in a different context.</p>
+
+ <p>Notice that if you store binary objects that are context-dependent
+ (for example, containing pointers or open file descriptors),
+ they lose their meaning if they are backed up to a <c>Mnesia</c> table
+ and later restored in a different context.</p>
+
<p>When you retrieve a stored string or binary value from the registry,
the registry maintains a pointer to the object and you are passed a
copy of that pointer. You should never free an object retrieved in
this manner because when the registry later attempts to free it, a
- runtime error will occur that will likely cause the C-node to crash.</p>
+ runtime error occurs that likely causes the C-node to crash.</p>
+
<p>You are free to modify the contents of an object retrieved this way.
- However when you do so, the registry will not be aware of the changes
- you make, possibly causing it to be missed the next time you make a
- Mnesia backup of the registry contents. This can be avoided if you
- mark the object as dirty after any such changes with
- <c><![CDATA[ei_reg_markdirty()]]></c>, or pass appropriate flags to
- <c><![CDATA[ei_reg_dump()]]></c>.</p>
+ However, when you do so, the registry is not aware of your changes,
+ possibly causing it to be missed the next time you make an
+ <c>Mnesia</c> backup of the registry contents. This can be avoided if
+ you mark the object as dirty after any such changes with
+ <seealso marker="registry#ei_reg_markdirty">
+ <c>registry:ei_reg_markdirty</c></seealso>, or pass appropriate flags to
+ <seealso marker="registry#ei_reg_dump">
+ <c>registry:ei_reg_dump</c></seealso>.</p>
</section>
</section>
</chapter>
-
diff --git a/lib/erl_interface/doc/src/erl_call.xml b/lib/erl_interface/doc/src/erl_call.xml
index 46015621ac..73b9b13e4d 100644
--- a/lib/erl_interface/doc/src/erl_call.xml
+++ b/lib/erl_interface/doc/src/erl_call.xml
@@ -4,14 +4,14 @@
<comref>
<header>
<copyright>
- <year>1996</year><year>2016</year>
+ <year>1996</year><year>2017</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
@@ -28,135 +28,146 @@
<docno></docno>
<approved>Bjarne D&auml;cker</approved>
<checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>97-05-16</date>
+ <date>1997-05-16</date>
<rev>B</rev>
- <file>erl_call.sgml</file>
+ <file>erl_call.xml</file>
</header>
<com>erl_call</com>
- <comsummary>Call/Start a Distributed Erlang Node</comsummary>
+ <comsummary>Call/start a distributed Erlang node.</comsummary>
<description>
- <p><c><![CDATA[erl_call]]></c> makes it possible to start and/or communicate with
- a distributed Erlang node. It is built upon the <c><![CDATA[erl_interface]]></c>
- library as an example application. Its purpose is to use an Unix shell script to interact with a distributed Erlang node. It performs all
- communication with the Erlang <em>rex server</em>, using the standard Erlang RPC facility. It does not require any special
- software to be run at the Erlang target node.</p>
+ <p><c>erl_call</c> makes it possible to start and/or
+ communicate with a distributed Erlang node. It is built upon the
+ <c>Erl_Interface</c> library as an example application.
+ Its purpose is to use a Unix shell script to interact with a distributed
+ Erlang node. It performs all communication with the Erlang
+ <em>rex server</em>, using the standard Erlang RPC facility. It does not
+ require any special software to be run at the Erlang target node.</p>
+
<p>The main use is to either start a distributed Erlang node
or to make an ordinary function call. However, it is also
- possible to pipe an Erlang module to <c><![CDATA[erl_call]]></c> and have it
- compiled, or to pipe a sequence of Erlang expressions to be evaluated
+ possible to pipe an Erlang module to <c>erl_call</c> and have
+ it compiled, or to pipe a sequence of Erlang expressions to be evaluated
(similar to the Erlang shell).</p>
- <p>Options, which cause <c><![CDATA[stdin]]></c> to be read, can be used with
- advantage
- as scripts from within (Unix) shell scripts. Another
- nice use of <c><![CDATA[erl_call]]></c> could be from (http) CGI-bin scripts.</p>
+
+ <p>Options, which cause <c>stdin</c> to be read, can be used
+ with advantage,
+ as scripts from within (Unix) shell scripts. Another nice use
+ of <c>erl_call</c> could be from (HTTP) CGI-bin scripts.</p>
</description>
+
<funcs>
<func>
<name>erl_call &lt;options></name>
- <fsummary>Start/Call Erlang</fsummary>
+ <fsummary>Start/call Erlang.</fsummary>
<desc>
- <p>Each option flag is described below with its name, type and
- meaning. </p>
+ <p>Starts/calls Erlang.</p>
+ <p>Each option flag is described below with its name, type, and
+ meaning.</p>
<taglist>
- <tag>-a [Mod [Fun [Args]]]]</tag>
+ <tag><c>-a [Mod [Fun [Args]]]]</c></tag>
<item>
- <p>(<em>optional</em>): Applies the specified function
- and returns the result. <c><![CDATA[Mod]]></c> must be specified, however
- <c>start</c> and <c>[]</c> are assumed for unspecified <c><![CDATA[Fun]]></c> and <c><![CDATA[Args]]></c>, respectively. <c><![CDATA[Args]]></c> should
- be in the same format as for <c><![CDATA[erlang:apply/3]]></c>. Note
- that this flag takes exactly one argument, so quoting
- may be necessary in order to group <c><![CDATA[Mod]]></c>, <c><![CDATA[Fun]]></c>
- and <c><![CDATA[Args]]></c>, in a manner dependent on the behavior
- of your command shell.</p>
- <p></p>
+ <p>(<em>Optional.</em>) Applies the specified function
+ and returns the result. <c>Mod</c> must be specified.
+ However, <c>start</c> and <c>[]</c> are assumed for unspecified
+ <c>Fun</c> and <c>Args</c>, respectively.
+ <c>Args</c> is to be in the same format as for
+ <seealso marker="erts:erlang#apply/3">
+ <c>erlang:apply/3</c></seealso> in <c>ERTS</c>.</p>
+ <p>Notice that this flag takes exactly one argument, so quoting
+ can be necessary to group <c>Mod</c>,
+ <c>Fun</c>, and <c>Args</c> in a manner
+ dependent on the behavior of your command shell.</p>
</item>
- <tag>-c Cookie</tag>
+ <tag><c>-c Cookie</c></tag>
<item>
- <p>(<em>optional</em>): Use this option to specify a certain cookie. If no cookie is specified, the <c><![CDATA[~/.erlang.cookie]]></c> file is read and its content are used as cookie. The Erlang node we want to communicate with must have the same cookie.</p>
+ <p>(<em>Optional.</em>) Use this option to specify a certain cookie.
+ If no cookie is specified, the <c>~/.erlang.cookie</c>
+ file is read and its content is used as cookie. The Erlang node
+ we want to communicate with must have the same cookie.</p>
</item>
- <tag>-d</tag>
+ <tag><c>-d</c></tag>
<item>
- <p>(<em>optional</em>): Debug mode. This causes all IO to be output
- to the file <c><![CDATA[~/.erl_call.out.Nodename]]></c>, where <c><![CDATA[Nodename]]></c>
+ <p>(<em>Optional.</em>) Debug mode. This causes all I/O to be output
+ to the <c>~/.erl_call.out.Nodename</c> file, where
+ <c>Nodename</c>
is the node name of the Erlang node in question.</p>
- <p></p>
</item>
- <tag>-e</tag>
+ <tag><c>-e</c></tag>
<item>
- <p>(<em>optional</em>): Reads a sequence of Erlang expressions, separated
- by '<em>,</em>' and ended with a '<em>.</em>', from <c><![CDATA[stdin]]></c> until
- EOF (Control-D). Evaluates the expressions and returns the result from
- the last expression. Returns <c><![CDATA[{ok,Result}]]></c> if successful.</p>
- <p></p>
+ <p>(<em>Optional.</em>) Reads a sequence of Erlang expressions,
+ separated by comma (,) and ended with a full stop (.), from
+ <c>stdin</c> until EOF (Control-D). Evaluates the
+ expressions and returns the result from the last expression.
+ Returns <c>{ok,Result}</c> on success.</p>
</item>
- <tag>-h HiddenName</tag>
+ <tag><c>-h HiddenName</c></tag>
<item>
- <p>(<em>optional</em>): Specifies the name of the hidden node
- that <c><![CDATA[erl_call]]></c> represents.</p>
- <p></p>
+ <p>(<em>Optional.</em>) Specifies the name of the hidden node
+ that <c>erl_call</c> represents.</p>
</item>
- <tag>-m</tag>
+ <tag><c>-m</c></tag>
<item>
- <p>(<em>optional</em>): Reads an Erlang module from <c><![CDATA[stdin]]></c> and
- compiles it.</p>
- <p></p>
+ <p>(<em>Optional.</em>) Reads an Erlang module from
+ <c>stdin</c> and compiles it.</p>
</item>
- <tag>-n Node</tag>
+ <tag><c>-n Node</c></tag>
<item>
- <p>(one of <c><![CDATA[-n, -name, -sname]]></c> is required):
- Has the same meaning as <c><![CDATA[-name]]></c> and can still be used for
- backwards compatibility reasons.</p>
- <p></p>
+ <p>(One of <c>-n, -name, -sname</c> is required.)
+ Has the same meaning as <c>-name</c> and can still be
+ used for backward compatibility reasons.</p>
</item>
- <tag>-name Node</tag>
+ <tag><c>-name Node</c></tag>
<item>
- <p>(one of <c><![CDATA[-n, -name, -sname]]></c> is required): <c><![CDATA[Node]]></c> is the name of the node to be
- started or communicated with. It is assumed that
- <c><![CDATA[Node]]></c> is started with <c><![CDATA[erl -name]]></c>, which means that fully
- qualified long node names are used.
- If the <c><![CDATA[-s]]></c> option is given, an Erlang node will (if necessary)
- be started with <c><![CDATA[erl -name]]></c>.</p>
- <p></p>
+ <p>(One of <c>-n, -name, -sname</c> is required.)
+ <c>Node</c> is the name of the node to be
+ started or communicated with. It is assumed that
+ <c>Node</c> is started with
+ <c>erl -name</c>, which means that fully
+ qualified long node names are used. If option
+ <c>-s</c> is specified, an Erlang node will (if
+ necessary) be started with <c>erl -name</c>.</p>
</item>
- <tag>-q</tag>
+ <tag><c>-q</c></tag>
<item>
- <p>(<em>optional</em>): Halts the Erlang node specified
- with the -n switch. This switch overrides the -s switch.</p>
- <p></p>
+ <p>(<em>Optional.</em>) Halts the Erlang node specified
+ with switch <c>-n</c>. This switch overrides switch <c>-s</c>.</p>
</item>
- <tag>-r</tag>
+ <tag><c>-r</c></tag>
<item>
- <p>(<em>optional</em>): Generates a random name of the hidden node
- that <c><![CDATA[erl_call]]></c> represents.</p>
- <p></p>
+ <p>(<em>Optional.</em>) Generates a random name of the hidden node
+ that <c>erl_call</c> represents.</p>
</item>
- <tag>-s</tag>
+ <tag><c>-s</c></tag>
<item>
- <p>(<em>optional</em>): Starts a distributed Erlang node if necessary.
- This means that in a sequence of calls, where the '<c><![CDATA[-s]]></c>'
- and '<c><![CDATA[-n Node]]></c>' are constant, only the first call will start
- the Erlang node. This makes the rest of the communication
- very fast. This flag is currently only available on the Unix platform.</p>
- <p></p>
+ <p>(<em>Optional.</em>) Starts a distributed Erlang node if
+ necessary. This means that in a sequence of calls, where
+ '<c>-s</c>' and '<c>-n Node</c>' are
+ constant, only the first call starts the Erlang node. This makes
+ the rest of the communication very fast. This flag is currently
+ only available on Unix-like platforms (Linux, Mac OS X, Solaris,
+ and so on).</p>
</item>
- <tag>-sname Node</tag>
+ <tag><c>-sname Node</c></tag>
<item>
- <p>(one of <c><![CDATA[-n, -name, -sname]]></c> is required): <c><![CDATA[Node]]></c> is the name of the node to
- be started or communicated with. It is assumed that <c><![CDATA[Node]]></c> is started with <c><![CDATA[erl -sname]]></c> which means that short node names are used.
- If <c><![CDATA[-s]]></c> option is given, an Erlang node will be started (if necessary) with <c><![CDATA[erl -sname]]></c>.</p>
- <p></p>
+ <p>(One of <c>-n, -name, -sname</c> is required.)
+ <c>Node</c> is the name of the node to be started
+ or communicated with. It is assumed that <c>Node</c>
+ is started with <c>erl -sname</c>, which means that
+ short node names are used. If option <c>-s</c> is
+ specified, an Erlang node is started (if necessary) with
+ <c>erl -sname</c>.</p>
</item>
- <tag>-v</tag>
+ <tag><c>-v</c></tag>
<item>
- <p>(<em>optional</em>): Prints a lot of <c><![CDATA[verbose]]></c> information.
- This is only useful for the developer and maintainer of <c><![CDATA[erl_call]]></c>.</p>
- <p></p>
+ <p>(<em>Optional.</em>) Prints a lot of <c>verbose</c>
+ information. This is only useful for the developer and maintainer
+ of <c>erl_call</c>.</p>
</item>
- <tag>-x ErlScript</tag>
+ <tag><c>-x ErlScript</c></tag>
<item>
- <p>(<em>optional</em>): Specifies another name of the Erlang start-up script
- to be used. If not specified, the standard <c><![CDATA[erl]]></c> start-up script
- is used.</p>
+ <p>(<em>Optional.</em>) Specifies another name of the Erlang
+ startup script to be used. If not specified, the standard
+ <c>erl</c> startup script is used.</p>
</item>
</taglist>
</desc>
@@ -165,20 +176,29 @@
<section>
<title>Examples</title>
- <p>Starts an Erlang node and calls <c><![CDATA[erlang:time/0]]></c>.</p>
+ <p>To start an Erlang node and call <c>erlang:time/0</c>:</p>
+
<code type="none"><![CDATA[
erl_call -s -a 'erlang time' -n madonna
{18,27,34}
]]></code>
- <p>Terminates an Erlang node by calling <c><![CDATA[erlang:halt/0]]></c>.</p>
+
+ <p>To terminate an Erlang node by calling
+ <c>erlang:halt/0</c>:</p>
+
<code type="none"><![CDATA[
erl_call -s -a 'erlang halt' -n madonna
]]></code>
- <p>An apply with several arguments.</p>
+
+ <p>To apply with many arguments:</p>
+
<code type="none"><![CDATA[
-erl_call -s -a 'lists map [{math,sqrt},[1,4,9,16,25]]' -n madonna
+erl_call -s -a 'lists seq [1,10]' -n madonna
]]></code>
- <p>Evaluates a couple of expressions. <em>The input ends with EOF (Control-D)</em>.</p>
+
+ <p>To evaluate some expressions
+ (<em>the input ends with EOF (Control-D)</em>):</p>
+
<code type="none"><![CDATA[
erl_call -s -e -n madonna
statistics(runtime),
@@ -189,10 +209,14 @@ Y=2,
^D
{ok,{3,0}}
]]></code>
- <p>Compiles a module and runs it. <em>Again, the input ends with EOF (Control-D)</em>. (In the example shown, the output has been formatted afterwards).</p>
+
+ <p>To compile a module and run it (<em>again, the input ends with EOF
+ (Control-D)</em>):</p>
+ <p>(In the example, the output has been formatted afterwards.)</p>
+
<code type="none"><![CDATA[
-erl_call -s -m -a lolita -n madonna
--module(lolita).
+erl_call -s -m -a procnames -n madonna
+-module(procnames).
-compile(export_all).
start() ->
P = processes(),
@@ -238,4 +262,3 @@ start() ->
]]></code>
</section>
</comref>
-
diff --git a/lib/erl_interface/doc/src/erl_connect.xml b/lib/erl_interface/doc/src/erl_connect.xml
index 0fad98cd17..76ef6588c2 100644
--- a/lib/erl_interface/doc/src/erl_connect.xml
+++ b/lib/erl_interface/doc/src/erl_connect.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>erl_connect</title>
@@ -28,127 +28,110 @@
<docno></docno>
<approved>Bjarne D&auml;cker</approved>
<checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>980703</date>
+ <date>1998-07-03</date>
<rev>A</rev>
- <file>erl_connect.sgml</file>
+ <file>erl_connect.xml</file>
</header>
<lib>erl_connect</lib>
- <libsummary>Communicate with Distributed Erlang</libsummary>
+ <libsummary>Communicate with distributed Erlang.</libsummary>
<description>
<p>This module provides support for communication between distributed
- Erlang nodes and C nodes, in a manner that is transparent to Erlang
+ Erlang nodes and C-nodes, in a manner that is transparent to Erlang
processes.</p>
- <p>A C node appears to Erlang as a
- <em>hidden node</em>.
+
+ <p>A C-node appears to Erlang as a <em>hidden node</em>.
That is, Erlang processes that know the name of the
- C node are able to communicate with it in a normal manner, but
- the node name will not appear in the listing provided by the
- Erlang function <c><![CDATA[nodes/0]]></c>.</p>
+ C-node can communicate with it in a normal manner, but
+ the node name does not appear in the listing provided by
+ <seealso marker="erts:erlang#nodes/0"><c>erlang:nodes/0</c></seealso>
+ in <c>ERTS</c>.</p>
</description>
+
<funcs>
<func>
- <name><ret>int</ret><nametext>erl_connect_init(number, cookie, creation)</nametext></name>
- <name><ret>int</ret><nametext>erl_connect_xinit(host, alive, node, addr, cookie, creation)</nametext></name>
- <fsummary>Initialize communication</fsummary>
+ <name><ret>int</ret><nametext>erl_accept(listensock, conp)</nametext></name>
+ <fsummary>Accept a connection.</fsummary>
<type>
- <v>int number;</v>
- <v>char *cookie;</v>
- <v>short creation;</v>
- <v>char *host,*alive,*node;</v>
- <v>struct in_addr *addr;</v>
+ <v>int listensock;</v>
+ <v>ErlConnect *conp;</v>
</type>
<desc>
- <p>These functions initialize the <c><![CDATA[erl_connect]]></c>
- module. In particular, they are used to identify the name of the
- C-node from which they are called. One of these functions must
- be called before any of the other functions in the erl_connect
- module are used.</p>
- <p><c><![CDATA[erl_connect_xinit()]]></c> stores for later use information about
- the node's host name <c><![CDATA[host]]></c>, alive name <c><![CDATA[alive]]></c>, node
- name <c><![CDATA[node]]></c>, IP address <c><![CDATA[addr]]></c>, cookie <c><![CDATA[cookie]]></c>,
- and creation number <c><![CDATA[creation]]></c>. <c><![CDATA[erl_connect_init()]]></c>
- provides an alternative interface which does not require as much
- information from the caller. Instead, <c><![CDATA[erl_connect_init()]]></c>
- uses <c><![CDATA[gethostbyname()]]></c> to obtain default values.
- </p>
- <p>If you use <c><![CDATA[erl_connect_init()]]></c> your node will have a
- short name, i.e., it will not be fully qualified. If you need to
- use fully qualified (a.k.a. long) names, use
- <c><![CDATA[erl_connect_xinit()]]></c> instead.
- </p>
- <p><c><![CDATA[host]]></c> is the name of the host on which the node is running.</p>
- <p><c><![CDATA[alive]]></c> is the alivename of the node.</p>
- <p><c><![CDATA[node]]></c> is the name of the node. The nodename should
- be of the form <em>alivename@hostname</em>.</p>
- <p><c><![CDATA[addr]]></c> is the 32-bit IP address of <c><![CDATA[host]]></c>.</p>
- <p><c><![CDATA[cookie]]></c> is the authorization string required for access
- to the remote node. If NULL the user HOME directory is
- searched for a cookie file <c><![CDATA[.erlang.cookie]]></c>. The path to
- the home directory is retrieved from the environment variable
- <c><![CDATA[HOME]]></c> on Unix and from the <c><![CDATA[HOMEDRIVE]]></c> and
- <c><![CDATA[HOMEPATH]]></c> variables on Windows. Refer to the <c><![CDATA[auth]]></c>
- module for more details.</p>
- <p><c><![CDATA[creation]]></c> helps identify a particular instance of a C
- node. In particular, it can help prevent us from receiving
- messages sent to an earlier process with the same registered
- name.</p>
- <p>A C node acting as a server will be assigned a creation number
- when it calls <c><![CDATA[erl_publish()]]></c>.</p>
- <p><c><![CDATA[number]]></c> is used by <c><![CDATA[erl_connect_init()]]></c> to
- construct the actual node name. In the second example shown
- below, <em>"[email protected]"</em> will be the resulting node
- name.</p>
- <p>Example 1:</p>
- <code type="none"><![CDATA[
-struct in_addr addr;
-addr = inet_addr("150.236.14.75");
-if (!erl_connect_xinit("chivas",
- "madonna",
- &addr;
- "samplecookiestring..."),
- 0)
- erl_err_quit("<ERROR> when initializing !");
- ]]></code>
- <p>Example 2:</p>
+ <p>This function is used by a server process to accept a
+ connection from a client process.</p>
+ <list type="bulleted">
+ <item><c>listensock</c> is an open socket descriptor on
+ which <c>listen()</c> has previously been called.</item>
+ <item><c>conp</c> is a pointer to an
+ <c>ErlConnect</c> struct, described as follows:</item>
+ </list>
<code type="none"><![CDATA[
-if (!erl_connect_init(17, "samplecookiestring...", 0))
- erl_err_quit("<ERROR> when initializing !");
+typedef struct {
+ char ipadr[4];
+ char nodename[MAXNODELEN];
+} ErlConnect;
]]></code>
+ <p>On success, <c>conp</c> is filled in with the address and
+ node name of the connecting client and a file descriptor is
+ returned. On failure, <c>ERL_ERROR</c> is returned and
+ <c>erl_errno</c> is set to <c>EIO</c>.</p>
</desc>
</func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_close_connection(fd)</nametext></name>
+ <fsummary>Close a connection to an Erlang node.</fsummary>
+ <type>
+ <v>int fd;</v>
+ </type>
+ <desc>
+ <p>Closes an open connection to an Erlang node.</p>
+ <p><c>Fd</c> is a file descriptor obtained from
+ <c>erl_connect()</c> or
+ <c>erl_xconnect()</c>.</p>
+ <p>Returns <c>0</c> on success. If the call fails, a non-zero value
+ is returned, and the reason for the error can be obtained with the
+ appropriate platform-dependent call.</p>
+ </desc>
+ </func>
+
<func>
<name><ret>int</ret><nametext>erl_connect(node)</nametext></name>
<name><ret>int</ret><nametext>erl_xconnect(addr, alive)</nametext></name>
- <fsummary>Establishe a connection to an Erlang node</fsummary>
+ <fsummary>Establish a connection to an Erlang node.</fsummary>
<type>
<v>char *node, *alive;</v>
<v>struct in_addr *addr;</v>
</type>
<desc>
- <p>These functions set up a connection to an Erlang node.</p>
- <p><c><![CDATA[erl_xconnect()]]></c> requires the IP address of the remote
- host and the alive name of the remote node
- to be specified. <c><![CDATA[erl_connect()]]></c> provides an alternative
+ <p>Sets up a connection to an Erlang node.</p>
+ <p><c>erl_xconnect()</c> requires the IP address of the
+ remote host and the alivename of the remote node to be
+ specified. <c>erl_connect()</c> provides an alternative
interface, and determines the information from the node name
provided.</p>
- <p><c><![CDATA[addr]]></c> is the 32-bit IP address of the remote host.</p>
- <p><c><![CDATA[alive]]></c> is the alivename of the remote node.</p>
- <p><c><![CDATA[node]]></c> is the name of the remote node.</p>
- <p>These functions return an open file descriptor on success, or
- a negative value indicating that an error occurred --- in
- which case they will set <c><![CDATA[erl_errno]]></c> to one of:</p>
+ <list type="bulleted">
+ <item><c>addr</c> is the 32-bit IP address of the remote
+ host.</item>
+ <item><c>alive</c> is the alivename of the remote node.
+ </item>
+ <item><c>node</c> is the name of the remote node.</item>
+ </list>
+ <p>Returns an open file descriptor on success, otherwise a negative
+ value. In the latter case <c>erl_errno</c> is set to one
+ of:</p>
<taglist>
- <tag><c><![CDATA[EHOSTUNREACH]]></c></tag>
- <item>The remote host <c><![CDATA[node]]></c> is unreachable</item>
- <tag><c><![CDATA[ENOMEM]]></c></tag>
- <item>No more memory available.</item>
- <tag><c><![CDATA[EIO]]></c></tag>
+ <tag><c>EHOSTUNREACH</c></tag>
+ <item>The remote host <c>node</c> is unreachable.</item>
+ <tag><c>ENOMEM</c></tag>
+ <item>No more memory is available.</item>
+ <tag><c>EIO</c></tag>
<item>I/O error.</item>
</taglist>
- <p>Additionally, <c><![CDATA[errno]]></c> values from
- <c><![CDATA[socket]]></c><em>(2)</em> and <c><![CDATA[connect]]></c><em>(2)</em>
- system calls may be propagated into <c><![CDATA[erl_errno]]></c>.</p>
+ <p>Also, <c>errno</c> values from
+ <c>socket</c><em>(2)</em> and
+ <c>connect</c><em>(2)</em>
+ system calls can be propagated into <c>erl_errno</c>.</p>
+ <p><em>Example:</em></p>
<code type="none"><![CDATA[
#define NODE "[email protected]"
#define ALIVE "madonna"
@@ -164,59 +147,177 @@ erl_xconnect( &addr , ALIVE );
]]></code>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>erl_close_connection(fd)</nametext></name>
- <fsummary>Close a connection to an Erlang node</fsummary>
+ <name><ret>int</ret><nametext>erl_connect_init(number, cookie, creation)</nametext></name>
+ <name><ret>int</ret><nametext>erl_connect_xinit(host, alive, node, addr, cookie, creation)</nametext></name>
+ <fsummary>Initialize communication.</fsummary>
<type>
- <v>int fd;</v>
+ <v>int number;</v>
+ <v>char *cookie;</v>
+ <v>short creation;</v>
+ <v>char *host,*alive,*node;</v>
+ <v>struct in_addr *addr;</v>
+ </type>
+ <desc>
+ <p>Initializes the <c>erl_connect</c> module.
+ In particular, these functions are used to identify the name of the
+ C-node from which they are called. One of these functions must
+ be called before any of the other functions in the <c>erl_connect</c>
+ module are used.</p>
+ <p><c>erl_connect_xinit()</c> stores for later use
+ information about:</p>
+ <list type="bulleted">
+ <item>Hostname of the node, <c>host</c></item>
+ <item>Alivename, <c>alive</c></item>
+ <item>Node name, <c>node</c></item>
+ <item>IP address, <c>addr</c></item>
+ <item>Cookie, <c>cookie</c></item>
+ <item>Creation number, <c>creation</c></item>
+ </list>
+ <p><c>erl_connect_init()</c>
+ provides an alternative interface that does not require as much
+ information from the caller. Instead,
+ <c>erl_connect_init()</c>
+ uses <c>gethostbyname()</c> to obtain default values.</p>
+ <p>If you use <c>erl_connect_init()</c>, your node will
+ have a short name, that is, it will not be fully qualified. If you
+ need to use fully qualified (long) names, use
+ <c>erl_connect_xinit()</c> instead.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>host</c> is the name of the host on which the node
+ is running.</p>
+ </item>
+ <item>
+ <p><c>alive</c> is the alivename of the node.</p>
+ </item>
+ <item>
+ <p><c>node</c> is the node name. It is to
+ be of the form <em>alivename@hostname</em>.</p>
+ </item>
+ <item>
+ <p><c>addr</c> is the 32-bit IP address of
+ <c>host</c>.</p>
+ </item>
+ <item>
+ <p><c>cookie</c> is the authorization string required
+ for access to the remote node. If <c>NULL</c>, the user
+ <c>HOME</c> directory is searched for a cookie file
+ <c>.erlang.cookie</c>. The path to
+ the home directory is retrieved from environment variable
+ <c>HOME</c> on Unix and from the
+ <c>HOMEDRIVE</c> and
+ <c>HOMEPATH</c> variables on Windows. For more
+ details, see the <seealso marker="kernel:auth">
+ <c>auth</c></seealso> module in Kernel.</p>
+ </item>
+ <item>
+ <p><c>creation</c> helps identifying a particular
+ instance of a C-node. In particular, it can help prevent us from
+ receiving messages sent to an earlier process with the same
+ registered name.</p>
+ </item>
+ </list>
+ <p>A C-node acting as a server is assigned a creation number
+ when it calls <c>erl_publish()</c>.</p>
+ <p><c>number</c> is used by
+ <c>erl_connect_init()</c> to
+ construct the actual node name. In Example 2
+ below, <em>"[email protected]"</em> is the resulting node name.</p>
+ <p><em>Example 1:</em></p>
+ <code type="none"><![CDATA[
+struct in_addr addr;
+addr = inet_addr("150.236.14.75");
+if (!erl_connect_xinit("chivas",
+ "madonna",
+ &addr;
+ "samplecookiestring..."),
+ 0)
+ erl_err_quit("<ERROR> when initializing !");
+ ]]></code>
+ <p><em>Example 2:</em></p>
+ <code type="none"><![CDATA[
+if (!erl_connect_init(17, "samplecookiestring...", 0))
+ erl_err_quit("<ERROR> when initializing !");
+ ]]></code>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_publish(port)</nametext></name>
+ <fsummary>Publish a node name.</fsummary>
+ <type>
+ <v>int port;</v>
</type>
<desc>
- <p>This function closes an open connection to an Erlang node.</p>
- <p><c><![CDATA[Fd]]></c> is a file descriptor obtained from
- <c><![CDATA[erl_connect()]]></c> or <c><![CDATA[erl_xconnect()]]></c>.</p>
- <p>On success, 0 is returned. If the call fails, a non-zero value
- is returned, and the reason for
- the error can be obtained with the appropriate platform-dependent
- call.</p>
+ <p>This function is used by a server process to register
+ with the local name server EPMD, thereby allowing
+ other processes to send messages by using the registered name.
+ Before calling this function, the process should
+ have called <c>bind()</c> and <c>listen()</c>
+ on an open socket.</p>
+ <p><c>port</c> is the local name to register, and is to be
+ the same as the port number that was previously bound to the
+ socket.</p>
+ <p>To unregister with EPMD, simply close the returned descriptor.</p>
+ <p>On success, a descriptor connecting the calling process to EPMD is
+ returned. On failure, <c>-1</c> is returned and
+ <c>erl_errno</c> is set to:</p>
+ <taglist>
+ <tag><c>EIO</c></tag>
+ <item>I/O error.</item>
+ </taglist>
+ <p>Also, <c>errno</c> values from
+ <c>socket</c><em>(2)</em>
+ and <c>connect</c><em>(2)</em> system calls can be
+ propagated into <c>erl_errno</c>.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_receive(fd, bufp, bufsize)</nametext></name>
- <fsummary>Receive a message</fsummary>
+ <fsummary>Receive a message.</fsummary>
<type>
<v>int fd;</v>
<v>char *bufp;</v>
<v>int bufsize;</v>
</type>
<desc>
- <p>This function receives a message consisting of a sequence
+ <p>Receives a message consisting of a sequence
of bytes in the Erlang external format.</p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.</p>
- <p><c><![CDATA[bufp]]></c> is a buffer large enough to hold the expected
- message. </p>
- <p><c><![CDATA[bufsize]]></c> indicates the size of <c><![CDATA[bufp]]></c>.</p>
- <p>If a <em>tick</em> occurs, i.e., the Erlang node on the
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>bufp</c> is a buffer large enough to hold the
+ expected message.</item>
+ <item><c>bufsize</c> indicates the size of
+ <c>bufp</c>.</item>
+ </list>
+ <p>If a <em>tick</em> occurs, that is, the Erlang node on the
other end of the connection has polled this node to see if it
- is still alive, the function will return <c><![CDATA[ERL_TICK]]></c> and
- no message will be placed in the buffer. Also,
- <c><![CDATA[erl_errno]]></c> will be set to <c><![CDATA[EAGAIN]]></c>.</p>
+ is still alive, the function returns <c>ERL_TICK</c> and
+ no message is placed in the buffer. Also,
+ <c>erl_errno</c> is set to <c>EAGAIN</c>.</p>
<p>On success, the message is placed in the specified buffer
and the function returns the number of bytes actually read. On
- failure, the function returns a negative value and will set
- <c><![CDATA[erl_errno]]></c> to one of:</p>
+ failure, the function returns a negative value and sets
+ <c>erl_errno</c> to one of:</p>
<taglist>
- <tag><c><![CDATA[EAGAIN]]></c></tag>
+ <tag><c>EAGAIN</c></tag>
<item>Temporary error: Try again.</item>
- <tag><c><![CDATA[EMSGSIZE]]></c></tag>
- <item>Buffer too small.</item>
- <tag><c><![CDATA[EIO]]></c></tag>
+ <tag><c>EMSGSIZE</c></tag>
+ <item>Buffer is too small.</item>
+ <tag><c>EIO</c></tag>
<item>I/O error.</item>
</taglist>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_receive_msg(fd, bufp, bufsize, emsg)</nametext></name>
- <fsummary>Receive and decodes a message</fsummary>
+ <fsummary>Receive and decode a message.</fsummary>
<type>
<v>int fd;</v>
<v>unsigned char *bufp;</v>
@@ -224,14 +325,20 @@ erl_xconnect( &addr , ALIVE );
<v>ErlMessage *emsg;</v>
</type>
<desc>
- <p>This function receives the message into the specified buffer,
- and decodes into the <c><![CDATA[(ErlMessage *) emsg]]></c>.</p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.</p>
- <p><c><![CDATA[bufp]]></c> is a buffer large enough to hold the expected message.</p>
- <p><c><![CDATA[bufsize]]></c> indicates the size of <c><![CDATA[bufp]]></c>.</p>
- <p><c><![CDATA[emsg]]></c> is a pointer to an <c><![CDATA[ErlMessage]]></c> structure,
- into which the message will be decoded. <c><![CDATA[ErlMessage]]></c> is
- defined as follows:</p>
+ <p>Receives the message into the specified buffer
+ and decodes into <c>(ErlMessage *) emsg</c>.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>bufp</c> is a buffer large enough to hold the
+ expected message.</item>
+ <item><c>bufsize</c> indicates the size of
+ <c>bufp</c>.</item>
+ <item>><c>emsg</c> is a pointer to an
+ <c>ErlMessage</c> structure
+ into which the message will be decoded.
+ <c>ErlMessage</c> is defined as follows:</item>
+ </list>
<code type="none"><![CDATA[
typedef struct {
int type;
@@ -242,144 +349,100 @@ typedef struct {
} ErlMessage;
]]></code>
<note>
- <p>The definition of <c><![CDATA[ErlMessage]]></c> has changed since
- earlier versions of Erl_Interface.</p>
+ <p>The definition of <c>ErlMessage</c> has changed since
+ earlier versions of <c>Erl_Interface</c>.</p>
</note>
- <p><c><![CDATA[type]]></c> identifies the type of message, one of
- <c><![CDATA[ERL_SEND]]></c>, <c><![CDATA[ERL_REG_SEND]]></c>, <c><![CDATA[ERL_LINK]]></c>,
- <c><![CDATA[ERL_UNLINK]]></c> and <c><![CDATA[ERL_EXIT]]></c>.
- </p>
- <p>If <c><![CDATA[type]]></c> contains <c><![CDATA[ERL_SEND]]></c>
- this indicates that an ordinary send operation has taken
- place, and <c><![CDATA[emsg->to]]></c> contains the Pid of the
- recipient. If <c><![CDATA[type]]></c> contains <c><![CDATA[ERL_REG_SEND]]></c> then a
- registered send operation took place, and <c><![CDATA[emsg->from]]></c>
- contains the Pid of the sender. In both cases, the actual
- message will be in <c><![CDATA[emsg->msg]]></c>.
- </p>
- <p>If <c><![CDATA[type]]></c> contains one of <c><![CDATA[ERL_LINK]]></c> or
- <c><![CDATA[ERL_UNLINK]]></c>, then <c><![CDATA[emsg->to]]></c> and <c><![CDATA[emsg->from]]></c>
- contain the pids of the sender and recipient of the link or unlink.
- <c><![CDATA[emsg->msg]]></c> is not used in these cases.
- </p>
- <p>If <c><![CDATA[type]]></c> contains <c><![CDATA[ERL_EXIT]]></c>, then this
- indicates that a link has been broken. In this case,
- <c><![CDATA[emsg->to]]></c> and <c><![CDATA[emsg->from]]></c> contain the pids of the
- linked processes, and <c><![CDATA[emsg->msg]]></c> contains the reason for
- the exit.
- </p>
+ <p><c>type</c> identifies the type of message, one of the
+ following:</p>
+ <taglist>
+ <tag><c>ERL_SEND</c></tag>
+ <item>
+ <p>An ordinary send operation has occurred and
+ <c>emsg->to</c> contains the pid of the recipient.
+ The message is in <c>emsg->msg</c>.</p>
+ </item>
+ <tag><c>ERL_REG_SEND</c></tag>
+ <item>
+ <p>A registered send operation has occurred and
+ <c>emsg->from</c> contains the pid of the sender.
+ The message is in <c>emsg->msg</c>.</p>
+ </item>
+ <tag><c>ERL_LINK</c> or <c>ERL_UNLINK</c>
+ </tag>
+ <item>
+ <p><c>emsg->to</c> and <c>emsg->from</c>
+ contain the pids of the sender and recipient of the link or
+ unlink. <c>emsg->msg</c> is not used.</p>
+ </item>
+ <tag><c>ERL_EXIT</c></tag>
+ <item>
+ <p>A link is broken. <c>emsg->to</c> and
+ <c>emsg->from</c> contain the pids of the linked
+ processes, and <c>emsg->msg</c> contains the reason
+ for the exit.</p>
+ </item>
+ </taglist>
<note>
<p>It is the caller's responsibility to release the
- memory pointed to by <c><![CDATA[emsg->msg]]></c>, <c><![CDATA[emsg->to]]></c> and
- <c><![CDATA[emsg->from]]></c>.</p>
+ memory pointed to by <c>emsg->msg</c>,
+ <c>emsg->to</c>, and
+ <c>emsg->from</c>.</p>
</note>
- <p>If a <em>tick</em> occurs, i.e., the Erlang node on the
+ <p>If a <em>tick</em> occurs, that is, the Erlang node on the
other end of the connection has polled this node to see if it
- is still alive, the function will return <c><![CDATA[ERL_TICK]]></c>
+ is still alive, the function returns <c>ERL_TICK</c>
indicating that the tick has been received and responded to,
- but no message will be placed in the buffer. In this case you
- should call <c><![CDATA[erl_receive_msg()]]></c> again.</p>
- <p>On success, the function returns <c><![CDATA[ERL_MSG]]></c> and the
- <c><![CDATA[Emsg]]></c> struct will be initialized as described above, or
- <c><![CDATA[ERL_TICK]]></c>, in which case no message is returned. On
- failure, the function returns <c><![CDATA[ERL_ERROR]]></c> and will set
- <c><![CDATA[erl_errno]]></c> to one of:</p>
+ but no message is placed in the buffer. In this case you
+ are to call <c>erl_receive_msg()</c> again.</p>
+ <p>On success, the function returns <c>ERL_MSG</c> and the
+ <c>Emsg</c> struct is initialized as described above, or
+ <c>ERL_TICK</c>, in which case no message is returned. On
+ failure, the function returns <c>ERL_ERROR</c> and sets
+ <c>erl_errno</c> to one of:</p>
<taglist>
- <tag><c><![CDATA[EMSGSIZE]]></c></tag>
- <item>Buffer too small.</item>
- <tag><c><![CDATA[ENOMEM]]></c></tag>
- <item>No more memory available.</item>
- <tag><c><![CDATA[EIO]]></c></tag>
- <item>I/O error.</item>
- </taglist>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>erl_xreceive_msg(fd, bufpp, bufsizep, emsg)</nametext></name>
- <fsummary>Receive and decodes a message</fsummary>
- <type>
- <v>int fd;</v>
- <v>unsigned char **bufpp;</v>
- <v>int *bufsizep;</v>
- <v>ErlMessage *emsg;</v>
- </type>
- <desc>
- <p>This function is similar to <c><![CDATA[erl_receive_msg]]></c>. The
- difference is that <c><![CDATA[erl_xreceive_msg]]></c> expects the buffer to
- have been allocated by <c><![CDATA[malloc]]></c>, and reallocates it if the received
- message does not fit into the original buffer. For that reason,
- both buffer and buffer length are given as pointers - their values
- may change by the call.
- </p>
- <p>On success, the function returns <c><![CDATA[ERL_MSG]]></c> and the
- <c><![CDATA[Emsg]]></c> struct will be initialized as described above, or
- <c><![CDATA[ERL_TICK]]></c>, in which case no message is returned. On
- failure, the function returns <c><![CDATA[ERL_ERROR]]></c> and will set
- <c><![CDATA[erl_errno]]></c> to one of:</p>
- <taglist>
- <tag><c><![CDATA[EMSGSIZE]]></c></tag>
- <item>Buffer too small.</item>
- <tag><c><![CDATA[ENOMEM]]></c></tag>
- <item>No more memory available.</item>
- <tag><c><![CDATA[EIO]]></c></tag>
- <item>I/O error.</item>
- </taglist>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>erl_send(fd, to, msg)</nametext></name>
- <fsummary>Send a message</fsummary>
- <type>
- <v>int fd;</v>
- <v>ETERM *to, *msg;</v>
- </type>
- <desc>
- <p>This function sends an Erlang term to a process.</p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.</p>
- <p><c><![CDATA[to]]></c> is an Erlang term containing the Pid of the
- intended recipient of the message.</p>
- <p><c><![CDATA[msg]]></c> is the Erlang term to be sent.</p>
- <p>The function returns 1 if successful, otherwise 0 --- in
- which case it will set <c><![CDATA[erl_errno]]></c> to one of:</p>
- <taglist>
- <tag><c><![CDATA[EINVAL]]></c></tag>
- <item>Invalid argument: <c><![CDATA[to]]></c> is not a valid Erlang pid.</item>
- <tag><c><![CDATA[ENOMEM]]></c></tag>
- <item>No more memory available.</item>
- <tag><c><![CDATA[EIO]]></c></tag>
+ <tag><c>EMSGSIZE</c></tag>
+ <item>Buffer is too small.</item>
+ <tag><c>ENOMEM</c></tag>
+ <item>No more memory is available.</item>
+ <tag><c>EIO</c></tag>
<item>I/O error.</item>
</taglist>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_reg_send(fd, to, msg)</nametext></name>
- <fsummary>Send a message to a registered name</fsummary>
+ <fsummary>Send a message to a registered name.</fsummary>
<type>
<v>int fd;</v>
<v>char *to;</v>
<v>ETERM *msg;</v>
</type>
<desc>
- <p>This function sends an Erlang term to a registered process.</p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.</p>
- <p><c><![CDATA[to]]></c> is a string containing the registered name of
- the intended recipient of the message.</p>
- <p><c><![CDATA[msg]]></c> is the Erlang term to be sent.</p>
- <p>The function returns 1 if successful, otherwise 0 --- in
- which case it will set <c><![CDATA[erl_errno]]></c> to one of:</p>
+ <p>Sends an Erlang term to a registered process.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>to</c> is a string containing the registered name
+ of the intended recipient of the message.</item>
+ <item><c>msg</c> is the Erlang term to be sent.</item>
+ </list>
+ <p>Returns <c>1</c> on success, otherwise <c>0</c>. In
+ the latter case <c>erl_errno</c> is set to one of:</p>
<taglist>
- <tag><c><![CDATA[ENOMEM]]></c></tag>
- <item>No more memory available.</item>
- <tag><c><![CDATA[EIO]]></c></tag>
+ <tag><c>ENOMEM</c></tag>
+ <item>No more memory is available.</item>
+ <tag><c>EIO</c></tag>
<item>I/O error.</item>
</taglist>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_rpc(fd, mod, fun, args)</nametext></name>
- <name><ret>int</ret><nametext>erl_rpc_to(fd, mod, fun, args)</nametext></name>
<name><ret>int</ret><nametext>erl_rpc_from(fd, timeout, emsg)</nametext></name>
- <fsummary>Remote Procedure Call</fsummary>
+ <name><ret>int</ret><nametext>erl_rpc_to(fd, mod, fun, args)</nametext></name>
+ <fsummary>Remote Procedure Call.</fsummary>
<type>
<v>int fd, timeout;</v>
<v>char *mod, *fun;</v>
@@ -387,158 +450,178 @@ typedef struct {
<v>ErlMessage *emsg;</v>
</type>
<desc>
- <p>These functions support calling Erlang functions on remote nodes.
- <c><![CDATA[erl_rpc_to()]]></c> sends an rpc request to a remote node and
- <c><![CDATA[erl_rpc_from()]]></c> receives the results of such a call.
- <c><![CDATA[erl_rpc()]]></c> combines the functionality of these two functions
- by sending an rpc request and waiting for the results. See also
- <c><![CDATA[rpc:call/4]]></c>. </p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.</p>
- <p><c><![CDATA[timeout]]></c> is the maximum time (in ms) to wait for
- results. Specify <c><![CDATA[ERL_NO_TIMEOUT]]></c> to wait forever.
- When erl_rpc() calls erl_rpc_from(), the call will never
- timeout.</p>
- <p><c><![CDATA[mod]]></c> is the name of the module containing the function
- to be run on the remote node.</p>
- <p><c><![CDATA[fun]]></c> is the name of the function to run.</p>
- <p><c><![CDATA[args]]></c> is an Erlang list, containing the arguments to be
- passed to the function. </p>
- <p><c><![CDATA[emsg]]></c> is a message containing the result of the
- function call.</p>
- <p>The actual message returned by the rpc server
- is a 2-tuple <c><![CDATA[{rex,Reply}]]></c>. If you are using
- <c><![CDATA[erl_rpc_from()]]></c> in your code then this is the message you
- will need to parse. If you are using <c><![CDATA[erl_rpc()]]></c> then the
+ <p>Supports calling Erlang functions on remote nodes.
+ <c>erl_rpc_to()</c> sends an RPC request to a remote node
+ and <c>erl_rpc_from()</c> receives the results of such a
+ call. <c>erl_rpc()</c> combines the functionality of
+ these two functions by sending an RPC request and waiting for the
+ results. See also <seealso marker="kernel:rpc#call/4">
+ <c>rpc:call/4</c></seealso> in <c>Kernel</c>.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>timeout</c> is the maximum time (in milliseconds)
+ to wait for
+ results. To wait forever, specify <c>ERL_NO_TIMEOUT</c>.
+ When <c>erl_rpc()</c> calls <c>erl_rpc_from()</c>, the call will
+ never timeout.</item>
+ <item><c>mod</c> is the name of the module containing the
+ function to be run on the remote node.</item>
+ <item><c>fun</c> is the name of the function to run.
+ </item>
+ <item><c>args</c> is an Erlang list, containing the
+ arguments to be passed to the function.</item>
+ <item><c>emsg</c> is a message containing the result of
+ the function call.</item>
+ </list>
+ <p>The actual message returned by the RPC server
+ is a 2-tuple <c>{rex,Reply}</c>. If you use
+ <c>erl_rpc_from()</c> in your code, this is the message
+ you will need to parse. If you use <c>erl_rpc()</c>, the
tuple itself is parsed for you, and the message returned to your
- program is the erlang term containing <c><![CDATA[Reply]]></c> only. Replies
- to rpc requests are always ERL_SEND messages.
- </p>
+ program is the Erlang term containing <c>Reply</c> only.
+ Replies to RPC requests are always <c>ERL_SEND</c> messages.</p>
<note>
<p>It is the caller's responsibility to free the returned
- <c><![CDATA[ETERM]]></c> structure as well as the memory pointed to by
- <c><![CDATA[emsg->msg]]></c> and <c><![CDATA[emsg->to]]></c>. </p>
+ <c>ETERM</c> structure and the memory pointed to by
+ <c>emsg->msg</c> and <c>emsg->to</c>.</p>
</note>
- <p><c><![CDATA[erl_rpc()]]></c> returns the remote function's return value (or
- <c><![CDATA[NULL]]></c> if it failed). <c><![CDATA[erl_rpc_to()]]></c> returns 0 on
- success, and a negative number on failure. <c><![CDATA[erl_rcp_from()]]></c>
- returns <c><![CDATA[ERL_MSG]]></c> when successful (with <c><![CDATA[Emsg]]></c> now
- containing the reply tuple), and one of <c><![CDATA[ERL_TICK]]></c>,
- <c><![CDATA[ERL_TIMEOUT]]></c> and <c><![CDATA[ERL_ERROR]]></c> otherwise. When failing,
- all three functions set <c><![CDATA[erl_errno]]></c> to one of:</p>
+ <p><c>erl_rpc()</c> returns the remote function's return
+ value on success, otherwise <c>NULL</c>.</p>
+ <p><c>erl_rpc_to()</c> returns <c>0</c> on
+ success, otherwise a negative number.</p>
+ <p><c>erl_rcp_from()</c> returns <c>ERL_MSG</c>
+ on success (with <c>Emsg</c> now
+ containing the reply tuple), otherwise one of
+ <c>ERL_TICK</c>, <c>ERL_TIMEOUT</c>, or
+ <c>ERL_ERROR</c>.</p>
+ <p>When failing,
+ all three functions set <c>erl_errno</c> to one of:</p>
<taglist>
- <tag><c><![CDATA[ENOMEM]]></c></tag>
- <item>No more memory available.</item>
- <tag><c><![CDATA[EIO]]></c></tag>
+ <tag><c>ENOMEM</c></tag>
+ <item>No more memory is available.</item>
+ <tag><c>EIO</c></tag>
<item>I/O error.</item>
- <tag><c><![CDATA[ETIMEDOUT]]></c></tag>
- <item>Timeout expired.</item>
- <tag><c><![CDATA[EAGAIN]]></c></tag>
+ <tag><c>ETIMEDOUT</c></tag>
+ <item>Timeout has expired.</item>
+ <tag><c>EAGAIN</c></tag>
<item>Temporary error: Try again.</item>
</taglist>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>erl_publish(port)</nametext></name>
- <fsummary>Publish a node name</fsummary>
+ <name><ret>int</ret><nametext>erl_send(fd, to, msg)</nametext></name>
+ <fsummary>Send a message.</fsummary>
<type>
- <v>int port;</v>
+ <v>int fd;</v>
+ <v>ETERM *to, *msg;</v>
</type>
<desc>
- <p>These functions are used by a server process to register
- with the local name server <em>epmd</em>, thereby allowing
- other processes to send messages by using the registered name.
- Before calling either of these functions, the process should
- have called <c><![CDATA[bind()]]></c> and <c><![CDATA[listen()]]></c> on an open socket.</p>
- <p><c><![CDATA[port]]></c> is the local name to register, and should be the
- same as the port number that was previously bound to the socket.</p>
- <p>To unregister with epmd, simply close the returned
- descriptor.
- </p>
- <p>On success, the functions return a descriptor connecting the
- calling process to epmd. On failure, they return -1 and set
- <c><![CDATA[erl_errno]]></c> to:</p>
+ <p>Sends an Erlang term to a process.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>to</c> is an Erlang term containing the pid of
+ the intended recipient of the message.</item>
+ <item>><c>msg</c> is the Erlang term to be sent.</item>
+ </list>
+ <p>Returns <c>1</c> on success, otherwise <c>0</c>. In
+ the latter case <c>erl_errno</c> is set to one of:</p>
<taglist>
- <tag><c><![CDATA[EIO]]></c></tag>
- <item>I/O error</item>
+ <tag><c>EINVAL</c></tag>
+ <item>Invalid argument: <c>to</c> is not a valid Erlang
+ pid.</item>
+ <tag><c>ENOMEM</c></tag>
+ <item>No more memory is available.</item>
+ <tag><c>EIO</c></tag>
+ <item>I/O error.</item>
</taglist>
- <p>Additionally, <c><![CDATA[errno]]></c> values from <c><![CDATA[socket]]></c><em>(2)</em>
- and <c><![CDATA[connect]]></c><em>(2)</em> system calls may be propagated
- into <c><![CDATA[erl_errno]]></c>.
- </p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>erl_accept(listensock, conp)</nametext></name>
- <fsummary>Accept a connection</fsummary>
- <type>
- <v>int listensock;</v>
- <v>ErlConnect *conp;</v>
- </type>
- <desc>
- <p>This function is used by a server process to accept a
- connection from a client process.</p>
- <p><c><![CDATA[listensock]]></c> is an open socket descriptor on which
- <c><![CDATA[listen()]]></c> has previously been called.</p>
- <p><c><![CDATA[conp]]></c> is a pointer to an <c><![CDATA[ErlConnect]]></c> struct,
- described as follows:</p>
- <code type="none"><![CDATA[
-typedef struct {
- char ipadr[4];
- char nodename[MAXNODELEN];
-} ErlConnect;
- ]]></code>
- <p>On success, <c><![CDATA[conp]]></c> is filled in with the address and
- node name of the connecting client and a file descriptor is
- returned. On failure, <c><![CDATA[ERL_ERROR]]></c> is returned and
- <c><![CDATA[erl_errno]]></c> is set to <c><![CDATA[EIO]]></c>.</p>
</desc>
</func>
+
<func>
- <name><ret>const char *</ret><nametext>erl_thiscookie()</nametext></name>
- <name><ret>const char *</ret><nametext>erl_thisnodename()</nametext></name>
- <name><ret>const char *</ret><nametext>erl_thishostname()</nametext></name>
<name><ret>const char *</ret><nametext>erl_thisalivename()</nametext></name>
+ <name><ret>const char *</ret><nametext>erl_thiscookie()</nametext></name>
<name><ret>short</ret><nametext>erl_thiscreation()</nametext></name>
- <fsummary>Retrieve some values</fsummary>
+ <name><ret>const char *</ret><nametext>erl_thishostname()</nametext></name>
+ <name><ret>const char *</ret><nametext>erl_thisnodename()</nametext></name>
+ <fsummary>Retrieve some values.</fsummary>
<desc>
- <p>These functions can be used to retrieve information about
- the C Node. These values are initially set with
- <c><![CDATA[erl_connect_init()]]></c> or <c><![CDATA[erl_connect_xinit()]]></c>.</p>
+ <p>Retrieves information about
+ the C-node. These values are initially set with
+ <c>erl_connect_init()</c> or
+ <c>erl_connect_xinit()</c>.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_unpublish(alive)</nametext></name>
- <fsummary>Forcefully unpublish a node name</fsummary>
+ <fsummary>Forcefully unpublish a node name.</fsummary>
<type>
<v>char *alive;</v>
</type>
<desc>
<p>This function can be called by a process to unregister a
- specified node from epmd on the localhost. This is however usually not
- allowed, unless epmd was started with the -relaxed_command_check
- flag, which it normally isn't.</p>
-
- <p>To unregister a node you have published, you should instead
- close the descriptor that was returned by
- <c><![CDATA[ei_publish()]]></c>.</p>
-
+ specified node from EPMD on the local host. This is, however, usually
+ not allowed, unless EPMD was started with flag
+ <c>-relaxed_command_check</c>, which it normally is not.</p>
+ <p>To unregister a node you have published, you should instead
+ close the descriptor that was returned by
+ <c>ei_publish()</c>.</p>
<warning>
- <p>This function is deprecated and will be removed in a future
- release.</p>
+ <p>This function is deprecated and will be removed in a future
+ release.</p>
</warning>
- <p><c><![CDATA[alive]]></c> is the name of the node to unregister, i.e., the
- first component of the nodename, without the <c><![CDATA[@hostname]]></c>.</p>
- <p>If the node was successfully unregistered from epmd, the
- function returns 0. Otherwise, it returns -1 and sets
- <c><![CDATA[erl_errno]]></c> is to <c><![CDATA[EIO]]></c>.</p>
+ <p><c>alive</c> is the name of the node to unregister, that
+ is, the first component of the node name, without
+ <c>@hostname</c>.</p>
+ <p>If the node was successfully unregistered from EPMD, <c>0</c> is
+ returned, otherwise <c>-1</c> is returned and
+ <c>erl_errno</c> is set to <c>EIO</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_xreceive_msg(fd, bufpp, bufsizep, emsg)</nametext></name>
+ <fsummary>Receive and decode a message.</fsummary>
+ <type>
+ <v>int fd;</v>
+ <v>unsigned char **bufpp;</v>
+ <v>int *bufsizep;</v>
+ <v>ErlMessage *emsg;</v>
+ </type>
+ <desc>
+ <p>Similar to <c>erl_receive_msg</c>. The difference is
+ that <c>erl_xreceive_msg</c> expects the buffer to
+ have been allocated by <c>malloc</c>, and reallocates it
+ if the received
+ message does not fit into the original buffer. Therefore
+ both buffer and buffer length are given as pointers; their values
+ can change by the call.</p>
+ <p>On success, the function returns <c>ERL_MSG</c> and the
+ <c>Emsg</c> struct is initialized as described above, or
+ <c>ERL_TICK</c>, in which case no message is returned. On
+ failure, the function returns <c>ERL_ERROR</c> and sets
+ <c>erl_errno</c> to one of:</p>
+ <taglist>
+ <tag><c>EMSGSIZE</c></tag>
+ <item>Buffer is too small.</item>
+ <tag><c>ENOMEM</c></tag>
+ <item>No more memory is available.</item>
+ <tag><c>EIO</c></tag>
+ <item>I/O error.</item>
+ </taglist>
</desc>
</func>
+
<func>
- <name><ret>struct hostent</ret><nametext>*erl_gethostbyname(name)</nametext></name>
<name><ret>struct hostent</ret><nametext>*erl_gethostbyaddr(addr, length, type)</nametext></name>
- <name><ret>struct hostent</ret><nametext>*erl_gethostbyname_r(name, hostp, buffer, buflen, h_errnop)</nametext></name>
<name><ret>struct hostent</ret><nametext>*erl_gethostbyaddr_r(addr, length, type, hostp, buffer, buflen, h_errnop)</nametext></name>
- <fsummary>Name lookup functions</fsummary>
+ <name><ret>struct hostent</ret><nametext>*erl_gethostbyname(name)</nametext></name>
+ <name><ret>struct hostent</ret><nametext>*erl_gethostbyname_r(name, hostp, buffer, buflen, h_errnop)</nametext></name>
+
+ <fsummary>Name lookup functions.</fsummary>
<type>
<v>const char *name;</v>
<v>const char *addr;</v>
@@ -550,7 +633,7 @@ typedef struct {
<v>int *h_errnop;</v>
</type>
<desc>
- <p>These are convenience functions for some common name lookup functions.</p>
+ <p>Convenience functions for some common name lookup functions.</p>
</desc>
</func>
</funcs>
@@ -558,13 +641,13 @@ typedef struct {
<section>
<title>Debug Information</title>
<p>If a connection attempt fails, the following can be checked:</p>
+
<list type="bulleted">
- <item><c><![CDATA[erl_errno]]></c></item>
- <item>that the right cookie was used</item>
- <item>that <em>epmd</em> is running</item>
- <item>the remote Erlang node on the other side is running the same
- version of Erlang as the <c><![CDATA[erl_interface]]></c> library.</item>
+ <item><c>erl_errno</c></item>
+ <item>That the correct cookie was used</item>
+ <item>That EPMD is running</item>
+ <item>That the remote Erlang node on the other side is running the same
+ version of Erlang as the <c>erl_interface</c> library</item>
</list>
</section>
</cref>
-
diff --git a/lib/erl_interface/doc/src/erl_error.xml b/lib/erl_interface/doc/src/erl_error.xml
index abe84780e1..8139c9b343 100644
--- a/lib/erl_interface/doc/src/erl_error.xml
+++ b/lib/erl_interface/doc/src/erl_error.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>erl_error</title>
@@ -28,61 +28,66 @@
<docno></docno>
<approved>Bjarne D&auml;cker</approved>
<checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>961014</date>
+ <date>1996-10-14</date>
<rev>A</rev>
- <file>erl_error.sgml</file>
+ <file>erl_error.xml</file>
</header>
<lib>erl_error</lib>
- <libsummary>Error Print Routines</libsummary>
+ <libsummary>Error print routines.</libsummary>
<description>
<p>This module contains some error printing routines taken
- from <em>Advanced Programming in the UNIX Environment</em>
- by W. Richard Stevens. </p>
+ from "Advanced Programming in the UNIX Environment"
+ by W. Richard Stevens.</p>
+
<p>These functions are all called in the same manner as
- <c><![CDATA[printf()]]></c>, i.e. with a string containing format specifiers
- followed by a list of corresponding arguments. All output from
- these functions is to <c><![CDATA[stderr]]></c>.</p>
+ <c>printf()</c>, that is, with a string containing format
+ specifiers followed by a list of corresponding arguments. All output from
+ these functions is to <c>stderr</c>.</p>
</description>
+
<funcs>
<func>
<name><ret>void</ret><nametext>erl_err_msg(FormatStr, ... )</nametext></name>
- <fsummary>Non-fatal error, and not system call error</fsummary>
+ <fsummary>Non-fatal error, and not system call error.</fsummary>
<type>
<v>const char *FormatStr;</v>
</type>
<desc>
<p>The message provided by the caller is printed. This
- function is simply a wrapper for <c><![CDATA[fprintf()]]></c>.</p>
+ function is simply a wrapper for <c>fprintf()</c>.</p>
</desc>
</func>
+
<func>
<name><ret>void</ret><nametext>erl_err_quit(FormatStr, ... )</nametext></name>
- <fsummary>Fatal error, but not system call error</fsummary>
+ <fsummary>Fatal error, but not system call error.</fsummary>
<type>
<v>const char *FormatStr;</v>
</type>
<desc>
<p>Use this function when a fatal error has occurred that
- is not due to a system call. The message provided by the
- caller is printed and the process terminates with an exit
- value of 1. The function does not return.</p>
+ is not because of a system call. The message provided by the
+ caller is printed and the process terminates with exit
+ value <c>1</c>. This function does not return.</p>
</desc>
</func>
+
<func>
<name><ret>void</ret><nametext>erl_err_ret(FormatStr, ... )</nametext></name>
- <fsummary>Non-fatal system call error</fsummary>
+ <fsummary>Non-fatal system call error.</fsummary>
<type>
<v>const char *FormatStr;</v>
</type>
<desc>
<p>Use this function after a failed system call. The message
provided by the caller is printed followed by a string
- describing the reason for failure. </p>
+ describing the reason for failure.</p>
</desc>
</func>
+
<func>
<name><ret>void</ret><nametext>erl_err_sys(FormatStr, ... )</nametext></name>
- <fsummary>Fatal system call error</fsummary>
+ <fsummary>Fatal system call error.</fsummary>
<type>
<v>const char *FormatStr;</v>
</type>
@@ -90,7 +95,7 @@
<p>Use this function after a failed system call. The message
provided by the caller is printed followed by a string
describing the reason for failure, and the process
- terminates with an exit value of 1. The function does not
+ terminates with exit value <c>1</c>. This function does not
return.</p>
</desc>
</func>
@@ -98,40 +103,43 @@
<section>
<title>Error Reporting</title>
- <p>Most functions in erl_interface report failures to the caller by
- returning some otherwise meaningless value (typically <c><![CDATA[NULL]]></c>
+ <p>Most functions in <c>Erl_Interface</c> report failures to the caller by
+ returning some otherwise meaningless value (typically
+ <c>NULL</c>
or a negative number). As this only tells you that things did not
- go well, you will have to examine the error code in
- <c><![CDATA[erl_errno]]></c> if you want to find out more about the failure.</p>
+ go well, examine the error code in <c>erl_errno</c> if you
+ want to find out more about the failure.</p>
</section>
+
<funcs>
<func>
<name><ret>volatile int</ret><nametext>erl_errno</nametext></name>
- <fsummary>The variable <c><![CDATA[erl_errno]]></c>contains the erl_interface error number. You can change the value if you wish. </fsummary>
+ <fsummary>Variable <c>erl_errno</c> contains the
+ Erl_Interface error number. You can change the value if you wish.
+ </fsummary>
<desc>
- <p><c><![CDATA[erl_errno]]></c> is initially (at program startup) zero and
- is then set by many erl_interface functions on failure to a
- non-zero error code to indicate what kind of error it
- encountered. A successful function call might change
- <c><![CDATA[erl_errno]]></c> (by calling some other function that
- fails), but no function will ever set it to zero. This means
- that you cannot use <c><![CDATA[erl_errno]]></c> to see <em>if</em> a
+ <p><c>erl_errno</c> is initially (at program startup) zero
+ and is then set by many <c>Erl_Interface</c> functions on failure to
+ a non-zero error code to indicate what kind of error it
+ encountered. A successful function call can change
+ <c>erl_errno</c> (by calling some other function that
+ fails), but no function does never set it to zero. This means
+ that you cannot use <c>erl_errno</c> to see <em>if</em> a
function call failed. Instead, each function reports failure
in its own way (usually by returning a negative number or
- <c><![CDATA[NULL]]></c>), in which case you can examine <c><![CDATA[erl_errno]]></c>
- for details.</p>
- <p><c><![CDATA[erl_errno]]></c> uses the error codes defined in your
- system's <c><![CDATA[<errno.h>]]></c>.</p>
+ <c>NULL</c>), in which case you can examine
+ <c>erl_errno</c> for details.</p>
+ <p><c>erl_errno</c> uses the error codes defined in your
+ system's <c>&lt;errno.h&gt;</c>.</p>
<note>
- <p>Actually, <c><![CDATA[erl_errno]]></c> is a "modifiable lvalue" (just
- like ISO C defines <c><![CDATA[errno]]></c> to be) rather than a
- variable. This means it might be implemented as a macro
- (expanding to, e.g., <c><![CDATA[*_erl_errno()]]></c>). For reasons of
- thread- (or task-)safety, this is exactly what we do on most
- platforms.</p>
+ <p><c>erl_errno</c> is a "modifiable lvalue" (just
+ like ISO C defines <c>errno</c> to be) rather than a
+ variable. This means it can be implemented as a macro
+ (expanding to, for example, <c>*_erl_errno()</c>).
+ For reasons of thread safety (or task safety), this is exactly what
+ we do on most platforms.</p>
</note>
</desc>
</func>
</funcs>
</cref>
-
diff --git a/lib/erl_interface/doc/src/erl_eterm.xml b/lib/erl_interface/doc/src/erl_eterm.xml
index 800f8a3207..9a05196a70 100644
--- a/lib/erl_interface/doc/src/erl_eterm.xml
+++ b/lib/erl_interface/doc/src/erl_eterm.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>erl_eterm</title>
@@ -28,131 +28,145 @@
<docno></docno>
<approved>Bjarne D&auml;cker</approved>
<checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>980703</date>
+ <date>1998-07-03</date>
<rev>A</rev>
- <file>erl_eterm.sgml</file>
+ <file>erl_eterm.xml</file>
</header>
<lib>erl_eterm</lib>
- <libsummary>Functions for Erlang Term Construction</libsummary>
+ <libsummary>Functions for Erlang term construction.</libsummary>
<description>
- <p>This module contains functions for creating and manipulating
- Erlang terms. </p>
+ <p>This module provides functions for creating and manipulating
+ Erlang terms.</p>
+
<p>An Erlang term is represented by a C structure of type
- <c><![CDATA[ETERM]]></c>. Applications should not reference any fields in this
- structure directly, because it may be changed in future releases
+ <c>ETERM</c>. Applications should not reference any fields
+ in this structure directly, as it can be changed in future releases
to provide faster and more compact term storage. Instead,
- applications should us the macros and functions provided. </p>
- <p>The following macros each take a single ETERM pointer as an
- argument. They return a non-zero value if the test is true, and 0
- otherwise:</p>
+ applications should use the macros and functions provided.</p>
+
+ <p>Each of the following macros takes a single <c>ETERM</c> pointer as an
+ argument. The macros return a non-zero value if the test is true,
+ otherwise <c>0</c>.</p>
+
<taglist>
- <tag><c><![CDATA[ERL_IS_INTEGER(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is an integer.</item>
- <tag><c><![CDATA[ERL_IS_UNSIGNED_INTEGER(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is an integer.</item>
- <tag><c><![CDATA[ERL_IS_FLOAT(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is a floating point number.</item>
- <tag><c><![CDATA[ERL_IS_ATOM(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is an atom.</item>
- <tag><c><![CDATA[ERL_IS_PID(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is a Pid (process identifier).</item>
- <tag><c><![CDATA[ERL_IS_PORT(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is a port.</item>
- <tag><c><![CDATA[ERL_IS_REF(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is a reference.</item>
- <tag><c><![CDATA[ERL_IS_TUPLE(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is a tuple.</item>
- <tag><c><![CDATA[ERL_IS_BINARY(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is a binary.</item>
- <tag><c><![CDATA[ERL_IS_LIST(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is a list with zero or more elements.</item>
- <tag><c><![CDATA[ERL_IS_EMPTY_LIST(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is an empty list.</item>
- <tag><c><![CDATA[ERL_IS_CONS(t)]]></c></tag>
- <item>True if <c><![CDATA[t]]></c> is a list with at least one element.</item>
+ <tag><c>ERL_IS_INTEGER(t)</c></tag>
+ <item>True if <c>t</c> is an integer.</item>
+ <tag><c>ERL_IS_UNSIGNED_INTEGER(t)</c></tag>
+ <item>True if <c>t</c> is an integer.</item>
+ <tag><c>ERL_IS_FLOAT(t)</c></tag>
+ <item>True if <c>t</c> is a floating point number.</item>
+ <tag><c>ERL_IS_ATOM(t)</c></tag>
+ <item>True if <c>t</c> is an atom.</item>
+ <tag><c>ERL_IS_PID(t)</c></tag>
+ <item>True if <c>t</c> is a pid (process identifier).</item>
+ <tag><c>ERL_IS_PORT(t)</c></tag>
+ <item>True if <c>t</c> is a port.</item>
+ <tag><c>ERL_IS_REF(t)</c></tag>
+ <item>True if <c>t</c> is a reference.</item>
+ <tag><c>ERL_IS_TUPLE(t)</c></tag>
+ <item>True if <c>t</c> is a tuple.</item>
+ <tag><c>ERL_IS_BINARY(t)</c></tag>
+ <item>True if <c>t</c> is a binary.</item>
+ <tag><c>ERL_IS_LIST(t)</c></tag>
+ <item>True if <c>t</c> is a list with zero or more
+ elements.</item>
+ <tag><c>ERL_IS_EMPTY_LIST(t)</c></tag>
+ <item>True if <c>t</c> is an empty list.</item>
+ <tag><c>ERL_IS_CONS(t)</c></tag>
+ <item>True if <c>t</c> is a list with at least one
+ element.</item>
</taglist>
+
<p>The following macros can be used for retrieving parts of Erlang
- terms. None of these do any type checking; results are undefined
- if you pass an ETERM* containing the wrong type. For example,
- passing a tuple to ERL_ATOM_PTR() will likely result in garbage.
- </p>
+ terms. None of these do any type checking. Results are undefined
+ if you pass an <c>ETERM*</c> containing the wrong type. For example,
+ passing a tuple to <c>ERL_ATOM_PTR()</c> likely results in garbage.</p>
+
<taglist>
- <tag><c><![CDATA[char *ERL_ATOM_PTR(t)]]></c></tag>
- <item/>
- <tag><c><![CDATA[char *ERL_ATOM_PTR_UTF8(t)]]></c></tag>
- <item>A string representing atom <c><![CDATA[t]]></c>.
- </item>
- <tag><c><![CDATA[int ERL_ATOM_SIZE(t)]]></c></tag>
- <item/>
- <tag><c><![CDATA[int ERL_ATOM_SIZE_UTF8(t)]]></c></tag>
- <item>The length (in bytes) of atom t.</item>
- <tag><c><![CDATA[void *ERL_BIN_PTR(t)]]></c></tag>
- <item>A pointer to the contents of <c><![CDATA[t]]></c></item>
- <tag><c><![CDATA[int ERL_BIN_SIZE(t)]]></c></tag>
- <item>The length (in bytes) of binary object <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[int ERL_INT_VALUE(t)]]></c></tag>
- <item>The integer of <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[unsigned int ERL_INT_UVALUE(t)]]></c></tag>
- <item>The unsigned integer value of <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[double ERL_FLOAT_VALUE(t)]]></c></tag>
- <item>The floating point value of <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[ETERM *ERL_PID_NODE(t)]]></c></tag>
- <item/>
- <tag><c><![CDATA[ETERM *ERL_PID_NODE_UTF8(t)]]></c></tag>
- <item>The Node in pid <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[int ERL_PID_NUMBER(t)]]></c></tag>
- <item>The sequence number in pid <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[int ERL_PID_SERIAL(t)]]></c></tag>
- <item>The serial number in pid <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[int ERL_PID_CREATION(t)]]></c></tag>
- <item>The creation number in pid <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[int ERL_PORT_NUMBER(t)]]></c></tag>
- <item>The sequence number in port <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[int ERL_PORT_CREATION(t)]]></c></tag>
- <item>The creation number in port <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[ETERM *ERL_PORT_NODE(t)]]></c></tag>
- <item/>
- <tag><c><![CDATA[ETERM *ERL_PORT_NODE_UTF8(t)]]></c></tag>
- <item>The node in port <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[int ERL_REF_NUMBER(t)]]></c></tag>
- <item>The first part of the reference number in ref <c><![CDATA[t]]></c>. Use
- only for compatibility.</item>
- <tag><c><![CDATA[int ERL_REF_NUMBERS(t)]]></c></tag>
- <item>Pointer to the array of reference numbers in ref <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[int ERL_REF_LEN(t)]]></c></tag>
- <item>The number of used reference numbers in ref <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[int ERL_REF_CREATION(t)]]></c></tag>
- <item>The creation number in ref <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[int ERL_TUPLE_SIZE(t)]]></c></tag>
- <item>The number of elements in tuple <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[ETERM *ERL_CONS_HEAD(t)]]></c></tag>
- <item>The head element of list <c><![CDATA[t]]></c>.</item>
- <tag><c><![CDATA[ETERM *ERL_CONS_TAIL(t)]]></c></tag>
- <item>A List representing the tail elements of list <c><![CDATA[t]]></c>.</item>
+ <tag><c>char *ERL_ATOM_PTR(t)</c></tag>
+ <item></item>
+ <tag><c>char *ERL_ATOM_PTR_UTF8(t)</c></tag>
+ <item>A string representing atom <c>t</c>.</item>
+ <tag><c>int ERL_ATOM_SIZE(t)</c></tag>
+ <item></item>
+ <tag><c>int ERL_ATOM_SIZE_UTF8(t)</c></tag>
+ <item>The length (in bytes) of atom <c>t</c>.</item>
+ <tag><c>void *ERL_BIN_PTR(t)</c></tag>
+ <item>A pointer to the contents of <c>t</c>.</item>
+ <tag><c>int ERL_BIN_SIZE(t)</c></tag>
+ <item>The length (in bytes) of binary object <c>t</c>.</item>
+ <tag><c>int ERL_INT_VALUE(t)</c></tag>
+ <item>The integer of <c>t</c>.</item>
+ <tag><c>unsigned int ERL_INT_UVALUE(t)</c></tag>
+ <item>The unsigned integer value of <c>t</c>.</item>
+ <tag><c>double ERL_FLOAT_VALUE(t)</c></tag>
+ <item>The floating point value of <c>t</c>.</item>
+ <tag><c>ETERM *ERL_PID_NODE(t)</c></tag>
+ <item></item>
+ <tag><c>ETERM *ERL_PID_NODE_UTF8(t)</c></tag>
+ <item>The node in pid <c>t</c>.</item>
+ <tag><c>int ERL_PID_NUMBER(t)</c></tag>
+ <item>The sequence number in pid <c>t</c>.</item>
+ <tag><c>int ERL_PID_SERIAL(t)</c></tag>
+ <item>The serial number in pid <c>t</c>.</item>
+ <tag><c>int ERL_PID_CREATION(t)</c></tag>
+ <item>The creation number in pid <c>t</c>.</item>
+ <tag><c>int ERL_PORT_NUMBER(t)</c></tag>
+ <item>The sequence number in port <c>t</c>.</item>
+ <tag><c>int ERL_PORT_CREATION(t)</c></tag>
+ <item>The creation number in port <c>t</c>.</item>
+ <tag><c>ETERM *ERL_PORT_NODE(t)</c></tag>
+ <item></item>
+ <tag><c>ETERM *ERL_PORT_NODE_UTF8(t)</c></tag>
+ <item>The node in port <c>t</c>.</item>
+ <tag><c>int ERL_REF_NUMBER(t)</c></tag>
+ <item>The first part of the reference number in ref <c>t</c>.
+ Use only for compatibility.</item>
+ <tag><c>int ERL_REF_NUMBERS(t)</c></tag>
+ <item>Pointer to the array of reference numbers in ref
+ <c>t</c>.</item>
+ <tag><c>int ERL_REF_LEN(t)</c></tag>
+ <item>The number of used reference numbers in ref
+ <c>t</c>.</item>
+ <tag><c>int ERL_REF_CREATION(t)</c></tag>
+ <item>The creation number in ref <c>t</c>.</item>
+ <tag><c>int ERL_TUPLE_SIZE(t)</c></tag>
+ <item>The number of elements in tuple <c>t</c>.</item>
+ <tag><c>ETERM *ERL_CONS_HEAD(t)</c></tag>
+ <item>The head element of list <c>t</c>.</item>
+ <tag><c>ETERM *ERL_CONS_TAIL(t)</c></tag>
+ <item>A list representing the tail elements of list
+ <c>t</c>.</item>
</taglist>
</description>
+
<funcs>
<func>
<name><ret>ETERM *</ret><nametext>erl_cons(head, tail)</nametext></name>
- <fsummary>Prepends a term to the head of a list.</fsummary>
+ <fsummary>Prepend a term to the head of a list.</fsummary>
<type>
<v>ETERM *head;</v>
<v>ETERM *tail;</v>
</type>
<desc>
- <p>This function concatenates two Erlang terms, prepending
- <c><![CDATA[head]]></c> onto <c><![CDATA[tail]]></c> and thereby creating a <c><![CDATA[cons]]></c> cell.
- To make a proper list, <c><![CDATA[tail]]></c> should always be a
- list or an empty list. Note that NULL is not a valid list.</p>
- <p><c><![CDATA[head]]></c> is the new term to be added.</p>
- <p><c><![CDATA[tail]]></c> is the existing list to which <c><![CDATA[head]]></c> will
- be concatenated.</p>
+ <p>Concatenates two Erlang terms, prepending <c>head</c>
+ onto <c>tail</c> and thereby creating a
+ <c>cons</c> cell.
+ To make a proper list, <c>tail</c> is always to be a list
+ or an empty list. Notice that <c>NULL</c> is not a valid list.</p>
+ <list type="bulleted">
+ <item><c>head</c> is the new term to be added.</item>
+ <item><c>tail</c> is the existing list to which
+ <c>head</c> is concatenated.</item>
+ </list>
<p>The function returns a new list.</p>
- <p><c><![CDATA[ERL_CONS_HEAD(list)]]></c> and <c><![CDATA[ERL_CONS_TAIL(list)]]></c>
+ <p><c>ERL_CONS_HEAD(list)</c> and
+ <c>ERL_CONS_TAIL(list)</c>
can be used to retrieve the head and tail components
- from the list. <c><![CDATA[erl_hd(list)]]></c> and <c><![CDATA[erl_tl(list)]]></c> will do
+ from the list. <c>erl_hd(list)</c> and
+ <c>erl_tl(list)</c> do
the same thing, but check that the argument really is a list.</p>
- <p>For example:</p>
+ <p><em>Example:</em></p>
<code type="none"><![CDATA[
ETERM *list,*anAtom,*anInt;
anAtom = erl_mk_atom("madonna");
@@ -165,79 +179,102 @@ erl_free_compound(list);
]]></code>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_copy_term(term)</nametext></name>
- <fsummary>Creates a copy of an Erlang term</fsummary>
+ <fsummary>Create a copy of an Erlang term.</fsummary>
<type>
<v>ETERM *term;</v>
</type>
<desc>
- <p>This function creates and returns a copy of the Erlang term
- <c><![CDATA[term]]></c>.</p>
+ <p>Creates and returns a copy of the Erlang term
+ <c>term</c>.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_element(position, tuple)</nametext></name>
- <fsummary>Extracts an element from an Erlang tuple</fsummary>
+ <fsummary>Extract an element from an Erlang tuple.</fsummary>
<type>
<v>int position;</v>
<v>ETERM *tuple;</v>
</type>
<desc>
- <p>This function extracts a specified element from an Erlang
- tuple. </p>
- <p><c><![CDATA[position]]></c> specifies which element to retrieve from
- <c><![CDATA[tuple]]></c>. The elements are numbered starting from 1.</p>
- <p><c><![CDATA[tuple]]></c> is an Erlang term containing at least
- <c><![CDATA[position]]></c> elements.</p>
- <p>The function returns a new Erlang term corresponding to the
- requested element, or NULL if <c><![CDATA[position]]></c> was greater than
- the arity of <c><![CDATA[tuple]]></c>.</p>
+ <p>Extracts a specified element from an Erlang tuple.</p>
+ <list type="bulleted">
+ <item><c>position</c> specifies which element to retrieve
+ from <c>tuple</c>. The elements are numbered starting
+ from 1.</item>
+ <item><c>tuple</c> is an Erlang term containing at least
+ <c>position</c> elements.</item>
+ </list>
+ <p>Returns a new Erlang term corresponding to the requested element, or
+ <c>NULL</c> if <c>position</c> was greater
+ than the arity of <c>tuple</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ETERM *</ret><nametext>erl_hd(list)</nametext></name>
+ <fsummary>Extract the first element from a list.</fsummary>
+ <type>
+ <v>ETERM *list;</v>
+ </type>
+ <desc>
+ <p>Extracts the first element from a list.</p>
+ <p><c>list</c> is an Erlang term containing a list.</p>
+ <p>Returns an Erlang term corresponding to the head
+ head element in the list, or a <c>NULL</c> pointer if
+ <c>list</c> was not a list.</p>
</desc>
</func>
+
<func>
<name><ret>void</ret><nametext>erl_init(NULL, 0)</nametext></name>
- <fsummary>Initialization routine</fsummary>
+ <fsummary>Initialization routine.</fsummary>
<type>
<v>void *NULL;</v>
<v>int 0;</v>
</type>
<desc>
- <marker id="erl_init"></marker>
- <p>This function must be called before any of the others in
- the <c><![CDATA[erl_interface]]></c> library in order to initialize the
- library functions. The arguments must be specified as
- <c><![CDATA[erl_init(NULL,0)]]></c>.</p>
+ <p>This function must be called before any of the others in the
+ <c>Erl_Interface</c> library to initialize the
+ library functions. The arguments must be specified as
+ <c>erl_init(NULL,0)</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>ETERM *</ret><nametext>erl_hd(list)</nametext></name>
- <fsummary>Extracts the first element from a list</fsummary>
+ <name><ret>int</ret><nametext>erl_iolist_length(list)</nametext></name>
+ <fsummary>Return the length of an I/O list.</fsummary>
<type>
<v>ETERM *list;</v>
</type>
<desc>
- <p>Extracts the first element from a list.</p>
- <p><c><![CDATA[list]]></c> is an Erlang term containing a list.</p>
- <p>The function returns an Erlang term corresponding to the
- head element in the list, or a NULL pointer if <c><![CDATA[list]]></c> was
- not a list.</p>
+ <p>Returns the length of an I/O list.</p>
+ <p><c>list</c> is an Erlang term containing an I/O list.</p>
+ <p>Returns the length of <c>list</c>, or
+ <c>-1</c> if <c>list</c> is not an I/O list.</p>
+ <p>For the definition of an I/O list, see
+ <seealso marker="#erl_iolist_to_binary">
+ <c>erl_iolist_to_binary</c></seealso>.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_iolist_to_binary(term)</nametext></name>
- <fsummary>Converts an IO list to a binary</fsummary>
+ <fsummary>Convert an I/O list to a binary.</fsummary>
<type>
<v>ETERM *list;</v>
</type>
<desc>
- <p>This function converts an IO list to a binary term.</p>
- <p><c><![CDATA[list]]></c> is an Erlang term containing a list.</p>
- <p>This function an Erlang binary term, or NULL if <c><![CDATA[list]]></c>
- was not an IO list. </p>
- <p>Informally, an IO list is a deep list of characters and
- binaries which can be sent to an Erlang port. In BNF, an IO
- list is formally defined as follows: </p>
+ <p>Converts an I/O list to a binary term.</p>
+ <p><c>list</c> is an Erlang term containing a list.</p>
+ <p>Returns an Erlang binary term, or <c>NULL</c> if
+ <c>list</c> was not an I/O list.</p>
+ <p>Informally, an I/O list is a deep list of characters and
+ binaries that can be sent to an Erlang port. In BNF, an I/O
+ list is formally defined as follows:</p>
<code type="none"><![CDATA[
iolist ::= []
| Binary
@@ -250,158 +287,164 @@ iohead ::= Binary
]]></code>
</desc>
</func>
+
<func>
<name><ret>char *</ret><nametext>erl_iolist_to_string(list)</nametext></name>
- <fsummary>Converts an IO list to a zero terminated string</fsummary>
+ <fsummary>Convert an I/O list to a <c>NULL</c>-terminated string.</fsummary>
<type>
<v>ETERM *list;</v>
</type>
<desc>
- <p>This function converts an IO list to a '\0' terminated C
- string. </p>
- <p><c><![CDATA[list]]></c> is an Erlang term containing an IO list. The IO
- list must not contain the integer 0, since C strings may not
+ <p>Converts an I/O list to a <c>NULL</c>-terminated C string.</p>
+ <p><c>list</c> is an Erlang term containing an I/O list.
+ The I/O list must not contain the integer 0, as C strings may not
contain this value except as a terminating marker.</p>
- <p>This function returns a pointer to a dynamically allocated
- buffer containing a string. If <c><![CDATA[list]]></c> is not an IO list,
- or if <c><![CDATA[list]]></c> contains the integer 0, NULL is returned. It
- is the caller's responsibility free the allocated buffer
- with <c><![CDATA[erl_free()]]></c>. </p>
- <p>Refer to <c><![CDATA[erl_iolist_to_binary()]]></c> for the definition of an
- IO list. </p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>erl_iolist_length(list)</nametext></name>
- <fsummary>Return the length of an IO list</fsummary>
- <type>
- <v>ETERM *list;</v>
- </type>
- <desc>
- <p>Returns the length of an IO list.
- </p>
- <p><c><![CDATA[list]]></c> is an Erlang term containing an IO list. </p>
- <p>The function returns the length of <c><![CDATA[list]]></c>, or -1 if
- <c><![CDATA[list]]></c> is not an IO list.</p>
- <p>Refer to <c><![CDATA[erl_iolist_to_binary()]]></c> for the definition of
- an IO list. </p>
+ <p>Returns a pointer to a dynamically allocated
+ buffer containing a string. If <c>list</c> is not an I/O
+ list, or if <c>list</c> contains the integer 0,
+ <c>NULL</c> is returned. It
+ is the caller's responsibility to free the allocated buffer
+ with <c>erl_free()</c>.</p>
+ <p>For the definition of an I/O list, see
+ <seealso marker="#erl_iolist_to_binary">
+ <c>erl_iolist_to_binary</c></seealso>.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_length(list)</nametext></name>
- <fsummary>Determines the length of a list</fsummary>
+ <fsummary>Determine the length of a list.</fsummary>
<type>
<v>ETERM *list;</v>
</type>
<desc>
<p>Determines the length of a proper list.</p>
- <p><c><![CDATA[list]]></c> is an Erlang term containing proper list. In a
- proper list, all tails except the last point to another list
+ <p><c>list</c> is an Erlang term containing a proper list.
+ In a proper list, all tails except the last point to another list
cell, and the last tail points to an empty list.</p>
- <p>Returns -1 if <c><![CDATA[list]]></c> is not a proper list.</p>
+ <p>Returns <c>-1</c> if <c>list</c> is not a proper
+ list.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_atom(string)</nametext></name>
- <fsummary>Creates an atom</fsummary>
+ <fsummary>Create an atom.</fsummary>
<type>
<v>const char *string;</v>
</type>
<desc>
<p>Creates an atom.</p>
- <p><c><![CDATA[string]]></c> is the sequence of characters that will be
+ <p><c>string</c> is the sequence of characters that will be
used to create the atom.</p>
- <p>Returns an Erlang term containing an atom. Note that it is
- the callers responsibility to make sure that <c><![CDATA[string]]></c>
+ <p>Returns an Erlang term containing an atom. Notice that it is
+ the caller's responsibility to ensure that <c>string</c>
contains a valid name for an atom.</p>
- <p><c><![CDATA[ERL_ATOM_PTR(atom)]]></c> and <c><![CDATA[ERL_ATOM_PTR_UTF8(atom)]]></c>
- can be used to retrieve the atom name (as a null terminated string). <c><![CDATA[ERL_ATOM_SIZE(atom)]]></c>
- and <c><![CDATA[ERL_ATOM_SIZE_UTF8(atom)]]></c> returns the length of the atom name.</p>
- <note><p>Note that the UTF8 variants were introduced in Erlang/OTP releases R16
- and the string returned by <c>ERL_ATOM_PTR(atom)</c> was not null terminated on older releases.</p>
+ <p><c>ERL_ATOM_PTR(atom)</c> and
+ <c>ERL_ATOM_PTR_UTF8(atom)</c>
+ can be used to retrieve the atom name (as a <c>NULL</c>-terminated string).
+ <c>ERL_ATOM_SIZE(atom)</c>
+ and <c>ERL_ATOM_SIZE_UTF8(atom)</c> return the length
+ of the atom name.</p>
+ <note>
+ <p>The UTF-8 variants were introduced in Erlang/OTP R16 and the
+ string returned by <c>ERL_ATOM_PTR(atom)</c> was not
+ <c>NULL</c>-terminated on older releases.</p>
</note>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_binary(bptr, size)</nametext></name>
- <fsummary>Creates a binary object</fsummary>
+ <fsummary>Create a binary object.</fsummary>
<type>
<v>char *bptr;</v>
<v>int size;</v>
</type>
<desc>
- <p>This function produces an Erlang binary object from a
+ <p>Produces an Erlang binary object from a
buffer containing a sequence of bytes.</p>
- <p><c><![CDATA[bptr]]></c> is a pointer to a buffer containing data to be converted.</p>
- <p><c><![CDATA[size]]></c> indicates the length of <c><![CDATA[bptr]]></c>.</p>
- <p>The function returns an Erlang binary object.</p>
- <p><c><![CDATA[ERL_BIN_PTR(bin)]]></c> retrieves a pointer to
- the binary data. <c><![CDATA[ERL_BIN_SIZE(bin)]]></c> retrieves the
- size. </p>
+ <list type="bulleted">
+ <item><c>bptr</c> is a pointer to a buffer containing
+ data to be converted.</item>
+ <item><c>size</c> indicates the length of
+ <c>bptr</c>.</item>
+ </list>
+ <p>Returns an Erlang binary object.</p>
+ <p><c>ERL_BIN_PTR(bin)</c> retrieves a pointer to
+ the binary data. <c>ERL_BIN_SIZE(bin)</c> retrieves the
+ size.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_empty_list()</nametext></name>
- <fsummary>Creates an empty Erlang list</fsummary>
+ <fsummary>Create an empty Erlang list.</fsummary>
<desc>
- <p>This function creates and returns an empty Erlang list.
- Note that NULL is not used to represent an empty list;
+ <p>Creates and returns an empty Erlang list.
+ Notice that <c>NULL</c> is not used to represent an empty list;
Use this function instead.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_estring(string, len)</nametext></name>
- <fsummary>Creates an Erlang string</fsummary>
+ <fsummary>Create an Erlang string.</fsummary>
<type>
<v>char *string;</v>
<v>int len;</v>
</type>
<desc>
- <p>This function creates a list from a sequence of bytes.</p>
- <p><c><![CDATA[string]]></c> is a buffer containing a sequence of
- bytes. The buffer does not need to be zero-terminated.</p>
- <p><c><![CDATA[len]]></c> is the length of <c><![CDATA[string]]></c>.</p>
- <p>The function returns an Erlang list object corresponding to
- the character sequence in <c><![CDATA[string]]></c>.</p>
+ <p>Creates a list from a sequence of bytes.</p>
+ <list type="bulleted">
+ <item><c>string</c> is a buffer containing a sequence of
+ bytes. The buffer does not need to be <c>NULL</c>-terminated.</item>
+ <item><c>len</c> is the length of
+ <c>string</c>.</item>
+ </list>
+ <p>Returns an Erlang list object corresponding to
+ the character sequence in <c>string</c>.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_float(f)</nametext></name>
- <fsummary>Creates an Erlang float</fsummary>
+ <fsummary>Create an Erlang float.</fsummary>
<type>
<v>double f;</v>
</type>
<desc>
<p>Creates an Erlang float.</p>
- <p><c><![CDATA[f]]></c> is a value to be converted to an Erlang float.</p>
- <p></p>
- <p>The function returns an Erlang float object with the value
- specified in <c><![CDATA[f]]></c> or <c><![CDATA[NULL]]></c> if
- <c><![CDATA[f]]></c> is not finite.
- </p>
- <p><c><![CDATA[ERL_FLOAT_VALUE(t)]]></c> can be used to retrieve the
- value from an Erlang float.</p>
+ <p><c>f</c> is a value to be converted to an Erlang
+ float.</p>
+ <p>Returns an Erlang float object with the value
+ specified in <c>f</c> or <c>NULL</c> if
+ <c>f</c> is not finite.</p>
+ <p><c>ERL_FLOAT_VALUE(t)</c> can be used to retrieve the
+ value from an Erlang float.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_int(n)</nametext></name>
- <fsummary>Creates an Erlang integer</fsummary>
+ <fsummary>Create an Erlang integer.</fsummary>
<type>
<v>int n;</v>
</type>
<desc>
<p>Creates an Erlang integer.</p>
- <p><c><![CDATA[n]]></c> is a value to be converted to an Erlang integer.</p>
- <p></p>
- <p>The function returns an Erlang integer object with the
- value specified in <c><![CDATA[n]]></c>.</p>
- <p><c><![CDATA[ERL_INT_VALUE(t)]]></c> can be used to retrieve the value
+ <p><c>n</c> is a value to be converted to an Erlang
+ integer.</p>
+ <p>Returns an Erlang integer object with the
+ value specified in <c>n</c>.</p>
+ <p><c>ERL_INT_VALUE(t)</c> can be used to retrieve the
value from an Erlang integer.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_list(array, arrsize)</nametext></name>
- <fsummary>Creates a list from an array</fsummary>
+ <fsummary>Create a list from an array.</fsummary>
<type>
<v>ETERM **array;</v>
<v>int arrsize;</v>
@@ -409,280 +452,316 @@ iohead ::= Binary
<desc>
<p>Creates an Erlang list from an array of Erlang terms, such
that each element in the list corresponds to one element in
- the array. </p>
- <p><c><![CDATA[array]]></c> is an array of Erlang terms.</p>
- <p><c><![CDATA[arrsize]]></c> is the number of elements in <c><![CDATA[array]]></c>.</p>
+ the array.</p>
+ <list type="bulleted">
+ <item><c>array</c> is an array of Erlang terms.</item>
+ <item><c>arrsize</c> is the number of elements in
+ <c>array</c>.</item>
+ </list>
<p>The function creates an Erlang list object, whose length
- <c><![CDATA[arrsize]]></c> and whose elements are taken from the terms in
- <c><![CDATA[array]]></c>.</p>
+ <c>arrsize</c> and whose elements are taken from the
+ terms in <c>array</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>ETERM *</ret><nametext>erl_mk_pid(node, number, serial, creation)</nametext></name>
- <fsummary>Creates a process identifier</fsummary>
+ <name><ret>ETERM *</ret><nametext>erl_mk_long_ref(node, n1, n2, n3, creation)</nametext></name>
+ <fsummary>Create an Erlang reference.</fsummary>
<type>
<v>const char *node;</v>
- <v>unsigned int number;</v>
- <v>unsigned int serial;</v>
+ <v>unsigned int n1, n2, n3;</v>
<v>unsigned int creation;</v>
</type>
<desc>
- <p>This function creates an Erlang process identifier. The
- resulting pid can be used by Erlang processes wishing to
- communicate with the C node.</p>
- <p><c><![CDATA[node]]></c> is the name of the C node.</p>
- <p><c><![CDATA[number]]></c>, <c><![CDATA[serial]]></c> and <c><![CDATA[creation]]></c> are
- arbitrary numbers. Note though, that these are limited in
- precision, so only the low 15, 3 and 2 bits of these numbers
- are actually used.</p>
- <p>The function returns an Erlang pid object.</p>
- <p><c><![CDATA[ERL_PID_NODE(pid)]]></c>, <c><![CDATA[ERL_PID_NUMBER(pid)]]></c>,
- <c><![CDATA[ERL_PID_SERIAL(pid)]]></c> and <c><![CDATA[ERL_PID_CREATION(pid)]]></c>
- can be used to retrieve the four values used to create the pid.</p>
+ <p>Creates an Erlang reference, with 82 bits.</p>
+ <list type="bulleted">
+ <item><c>node</c> is the name of the C-node.</item>
+ <item><c>n1</c>, <c>n2</c>, and
+ <c>n3</c> can be seen as one big number
+ <c>n1*2^64+n2*2^32+n3</c>, which is to be chosen
+ uniquely for each reference created for a given C-node.</item>
+ <item><c>creation</c> is an arbitrary number.</item>
+ </list>
+ <p>Notice that <c>n3</c> and <c>creation</c>
+ are limited in precision, so only the low 18 and 2 bits of these
+ numbers are used.</p>
+ <p>Returns an Erlang reference object.</p>
+ <p><c>ERL_REF_NODE(ref)</c>,
+ <c>ERL_REF_NUMBERS(ref)</c>,
+ <c>ERL_REF_LEN(ref)</c>, and
+ <c>ERL_REF_CREATION(ref)</c> can be used to retrieve the
+ values used to create the reference.</p>
</desc>
</func>
+
<func>
- <name><ret>ETERM *</ret><nametext>erl_mk_port(node, number, creation)</nametext></name>
- <fsummary>Creates a port identifier</fsummary>
+ <name><ret>ETERM *</ret><nametext>erl_mk_pid(node, number, serial, creation)</nametext></name>
+ <fsummary>Create a process identifier.</fsummary>
<type>
<v>const char *node;</v>
<v>unsigned int number;</v>
+ <v>unsigned int serial;</v>
<v>unsigned int creation;</v>
</type>
<desc>
- <p>This function creates an Erlang port identifier. </p>
- <p><c><![CDATA[node]]></c> is the name of the C node.</p>
- <p><c><![CDATA[number]]></c> and <c><![CDATA[creation]]></c> are arbitrary numbers.
- Note though, that these are limited in
- precision, so only the low 18 and 2 bits of these numbers
- are actually used.</p>
- <p>The function returns an Erlang port object.</p>
- <p><c><![CDATA[ERL_PORT_NODE(port)]]></c>, <c><![CDATA[ERL_PORT_NUMBER(port)]]></c>
- and <c><![CDATA[ERL_PORT_CREATION]]></c> can be used to retrieve the three
- values used to create the port. </p>
+ <p>Creates an Erlang process identifier (pid). The
+ resulting pid can be used by Erlang processes wishing to
+ communicate with the C-node.</p>
+ <list type="bulleted">
+ <item><c>node</c> is the name of the C-node.</item>
+ <item><c>number</c>, <c>serial</c>, and
+ <c>creation</c> are
+ arbitrary numbers. Notice that these are limited in
+ precision, so only the low 15, 3, and 2 bits of these numbers
+ are used.</item>
+ </list>
+ <p>Returns an Erlang pid object.</p>
+ <p><c>ERL_PID_NODE(pid)</c>,
+ <c>ERL_PID_NUMBER(pid)</c>,
+ <c>ERL_PID_SERIAL(pid)</c>, and
+ <c>ERL_PID_CREATION(pid)</c>
+ can be used to retrieve the four values used to create the pid.</p>
</desc>
</func>
+
<func>
- <name><ret>ETERM *</ret><nametext>erl_mk_ref(node, number, creation)</nametext></name>
- <fsummary>Creates an old Erlang reference</fsummary>
+ <name><ret>ETERM *</ret><nametext>erl_mk_port(node, number, creation)</nametext></name>
+ <fsummary>Create a port identifier.</fsummary>
<type>
<v>const char *node;</v>
<v>unsigned int number;</v>
<v>unsigned int creation;</v>
</type>
<desc>
- <p>This function creates an old Erlang reference, with
- only 18 bits - use <c><![CDATA[erl_mk_long_ref]]></c> instead.</p>
- <p><c><![CDATA[node]]></c> is the name of the C node.</p>
- <p><c><![CDATA[number]]></c> should be chosen uniquely for each reference
- created for a given C node.</p>
- <p><c><![CDATA[creation]]></c> is an arbitrary number.</p>
- <p>Note that <c><![CDATA[number]]></c> and <c><![CDATA[creation]]></c> are limited in
- precision, so only the low 18 and 2 bits of these numbers
- are actually used.
- </p>
- <p>The function returns an Erlang reference object.</p>
- <p><c><![CDATA[ERL_REF_NODE(ref)]]></c>, <c><![CDATA[ERL_REF_NUMBER(ref)]]></c>, and
- <c><![CDATA[ERL_REF_CREATION(ref)]]></c> to retrieve the three values used
- to create the reference. </p>
+ <p>Creates an Erlang port identifier.</p>
+ <list type="bulleted">
+ <item><c>node</c> is the name of the C-node.</item>
+ <item><c>number</c> and <c>creation</c> are
+ arbitrary numbers. Notice that these are limited in
+ precision, so only the low 18 and 2 bits of these numbers
+ are used.</item>
+ </list>
+ <p>Returns an Erlang port object.</p>
+ <p><c>ERL_PORT_NODE(port)</c>,
+ <c>ERL_PORT_NUMBER(port)</c>,
+ and <c>ERL_PORT_CREATION</c> can be used to retrieve the
+ three values used to create the port.</p>
</desc>
</func>
+
<func>
- <name><ret>ETERM *</ret><nametext>erl_mk_long_ref(node, n1, n2, n3, creation)</nametext></name>
- <fsummary>Creates an Erlang reference</fsummary>
+ <name><ret>ETERM *</ret><nametext>erl_mk_ref(node, number, creation)</nametext></name>
+ <fsummary>Create an old Erlang reference.</fsummary>
<type>
<v>const char *node;</v>
- <v>unsigned int n1, n2, n3;</v>
+ <v>unsigned int number;</v>
<v>unsigned int creation;</v>
</type>
<desc>
- <p>This function creates an Erlang reference, with 82 bits.</p>
- <p><c><![CDATA[node]]></c> is the name of the C node.</p>
- <p><c><![CDATA[n1]]></c>, <c><![CDATA[n2]]></c> and <c><![CDATA[n3]]></c> can be seen as one big number
- <c><![CDATA[n1*2^64+n2*2^32+n3]]></c> which should be chosen uniquely for
- each reference
- created for a given C node.</p>
- <p><c><![CDATA[creation]]></c> is an arbitrary number.</p>
- <p>Note that <c><![CDATA[n3]]></c> and <c><![CDATA[creation]]></c> are limited in
- precision, so only the low 18 and 2 bits of these numbers
- are actually used.
- </p>
- <p>The function returns an Erlang reference object.</p>
- <p><c><![CDATA[ERL_REF_NODE(ref)]]></c>, <c><![CDATA[ERL_REF_NUMBERS(ref)]]></c>,
- <c><![CDATA[ERL_REF_LEN(ref)]]></c> and
- <c><![CDATA[ERL_REF_CREATION(ref)]]></c> to retrieve the values used
- to create the reference. </p>
+ <p>Creates an old Erlang reference, with
+ only 18 bits - use <c>erl_mk_long_ref</c> instead.</p>
+ <list type="bulleted">
+ <item><c>node</c> is the name of the C-node.</item>
+ <item><c>number</c> is to be chosen uniquely for each
+ reference created for a given C-node.</item>
+ <item><c>creation</c> is an arbitrary number.</item>
+ </list>
+ <p>Notice that <c>number</c> and <c>creation</c>
+ are limited in precision, so only the low 18 and 2 bits of these
+ numbers are used.</p>
+ <p>Returns an Erlang reference object.</p>
+ <p><c>ERL_REF_NODE(ref)</c>,
+ <c>ERL_REF_NUMBER(ref)</c>, and
+ <c>ERL_REF_CREATION(ref)</c> can be used to retrieve the
+ three values used to create the reference.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_string(string)</nametext></name>
- <fsummary>Creates a string</fsummary>
+ <fsummary>Create a string.</fsummary>
<type>
<v>char *string;</v>
</type>
<desc>
- <p>This function creates a list from a zero terminated string.</p>
- <p><c><![CDATA[string]]></c> is the zero-terminated sequence of characters
- (i.e. a C string) from which the list will be created.</p>
- <p>The function returns an Erlang list.</p>
+ <p>Creates a list from a <c>NULL</c>-terminated string.</p>
+ <p><c>string</c> is a <c>NULL</c>-terminated sequence of
+ characters
+ (that is, a C string) from which the list will be created.</p>
+ <p>Returns an Erlang list.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_tuple(array, arrsize)</nametext></name>
- <fsummary>Creates an Erlang tuple from an array</fsummary>
+ <fsummary>Create an Erlang tuple from an array.</fsummary>
<type>
<v>ETERM **array;</v>
<v>int arrsize;</v>
</type>
<desc>
<p>Creates an Erlang tuple from an array of Erlang terms.</p>
- <p><c><![CDATA[array]]></c> is an array of Erlang terms.</p>
- <p><c><![CDATA[arrsize]]></c> is the number of elements in <c><![CDATA[array]]></c>.</p>
+ <list type="bulleted">
+ <item><c>array</c> is an array of Erlang terms.</item>
+ <item><c>arrsize</c> is the number of elements in
+ <c>array</c>.</item>
+ </list>
<p>The function creates an Erlang tuple, whose arity is
- <c><![CDATA[size]]></c> and whose elements are taken from the terms in
- <c><![CDATA[array]]></c>.</p>
- <p>To retrieve the size of a tuple, either use the
- <c><![CDATA[erl_size]]></c> function (which checks the type of the checked
- term and works for a binary as well as for a tuple), or the
- <c><![CDATA[ERL_TUPLE_SIZE(tuple)]]></c> returns the arity of a tuple.
- <c><![CDATA[erl_size()]]></c> will do the same thing, but it checks that
- the argument really is a tuple.
- <c><![CDATA[erl_element(index,tuple)]]></c> returns the element
- corresponding to a given position in the tuple. </p>
+ <c>size</c> and whose elements are taken from the terms
+ in <c>array</c>.</p>
+ <p>To retrieve the size of a tuple, either use function
+ <c>erl_size</c> (which checks the type of the
+ checked term and works for a binary as well as for a tuple) or
+ <c>ERL_TUPLE_SIZE(tuple)</c> returns the arity of a tuple.
+ <c>erl_size()</c> does the same thing, but it checks
+ that the argument is a tuple.
+ <c>erl_element(index,tuple)</c> returns the element
+ corresponding to a given position in the tuple.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_uint(n)</nametext></name>
- <fsummary>Creates an unsigned integer</fsummary>
+ <fsummary>Create an unsigned integer.</fsummary>
<type>
<v>unsigned int n;</v>
</type>
<desc>
<p>Creates an Erlang unsigned integer.</p>
- <p><c><![CDATA[n]]></c> is a value to be converted to an Erlang
+ <p><c>n</c> is a value to be converted to an Erlang
unsigned integer.</p>
- <p></p>
- <p>The function returns an Erlang unsigned integer object with
- the value specified in <c><![CDATA[n]]></c>.</p>
- <p><c><![CDATA[ERL_INT_UVALUE(t)]]></c> can be used to retrieve the
+ <p>Returns an Erlang unsigned integer object with
+ the value specified in <c>n</c>.</p>
+ <p><c>ERL_INT_UVALUE(t)</c> can be used to retrieve the
value from an Erlang unsigned integer.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_mk_var(name)</nametext></name>
- <fsummary>Creates an Erlang variable</fsummary>
+ <fsummary>Create an Erlang variable.</fsummary>
<type>
<v>char *name;</v>
</type>
<desc>
- <p>This function creates an unbound Erlang variable. The
- variable can later be bound through pattern matching or assignment.</p>
- <p><c><![CDATA[name]]></c> specifies a name for the variable.</p>
- <p>The function returns an Erlang variable object with the
- name <c><![CDATA[name]]></c>. </p>
+ <p>Creates an unbound Erlang variable. The variable can later be bound
+ through pattern matching or assignment.</p>
+ <p><c>name</c> specifies a name for the variable.</p>
+ <p>Returns an Erlang variable object with the
+ name <c>name</c>.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_print_term(stream, term)</nametext></name>
- <fsummary>Prints an Erlang term</fsummary>
+ <fsummary>Print an Erlang term.</fsummary>
<type>
<v>FILE *stream;</v>
<v>ETERM *term;</v>
</type>
<desc>
- <p>This function prints the specified Erlang term to the given
- output stream.</p>
- <p><c><![CDATA[stream]]></c> indicates where the function should send its
- output.</p>
- <p><c><![CDATA[term]]></c> is the Erlang term to print.</p>
- <p>The function returns the number of characters written, or a
- negative value if there was an error.</p>
+ <p>Prints the specified Erlang term to the specified output stream.</p>
+ <list type="bulleted">
+ <item><c>stream</c> indicates where the function is to
+ send its output.</item>
+ <item><c>term</c> is the Erlang term to print.</item>
+ </list>
+ <p>Returns the number of characters written on success, otherwise a
+ negative value.</p>
</desc>
</func>
+
<func>
<name><ret>void</ret><nametext>erl_set_compat_rel(release_number)</nametext></name>
- <fsummary>Set the erl_interface library in compatibility mode</fsummary>
+ <fsummary>Set the Erl_Interface library in compatibility mode.</fsummary>
<type>
<v>unsigned release_number;</v>
</type>
<desc>
- <marker id="erl_set_compat_rel"></marker>
- <p>By default, the <c><![CDATA[erl_interface]]></c> library is only guaranteed
- to be compatible with other Erlang/OTP components from the same
- release as the <c><![CDATA[erl_interface]]></c> library itself. For example,
- <c><![CDATA[erl_interface]]></c> from the OTP R10 release is not compatible
- with an Erlang emulator from the OTP R9 release by default.</p>
- <p>A call to <c><![CDATA[erl_set_compat_rel(release_number)]]></c> sets the
- <c><![CDATA[erl_interface]]></c> library in compatibility mode of release
- <c><![CDATA[release_number]]></c>. Valid range of <c><![CDATA[release_number]]></c>
+ <p>By default, the <c>Erl_Interface</c> library is only
+ guaranteed to be compatible with other Erlang/OTP components from the
+ same release as the <c>Erl_Interface</c> library itself.
+ For example, <c>Erl_Interface</c> from Erlang/OTP R10
+ is not compatible
+ with an Erlang emulator from Erlang/OTP R9 by default.</p>
+ <p>A call to <c>erl_set_compat_rel(release_number)</c> sets
+ the <c>Erl_Interface</c> library in compatibility mode of
+ release <c>release_number</c>. Valid range of
+ <c>release_number</c>
is [7, current release]. This makes it possible to
communicate with Erlang/OTP components from earlier releases.</p>
<note>
<p>If this function is called, it may only be called once
- directly after the call to the
- <seealso marker="#erl_init">erl_init()</seealso> function.</p>
+ directly after the call to function
+ <seealso marker="#erl_init">erl_init()</seealso>.</p>
</note>
<warning>
<p>You may run into trouble if this feature is used
- carelessly. Always make sure that all communicating
+ carelessly. Always ensure that all communicating
components are either from the same Erlang/OTP release, or
from release X and release Y where all components
from release Y are in compatibility mode of release X.</p>
</warning>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_size(term)</nametext></name>
- <fsummary>Return the arity of a tuple or binary</fsummary>
+ <fsummary>Return the arity of a tuple or binary.</fsummary>
<type>
<v>ETERM *term;</v>
</type>
<desc>
- <p>Returns the arity of an Erlang tuple, or the
- number of bytes in an Erlang binary object. </p>
- <p><c><![CDATA[term]]></c> is an Erlang tuple or an Erlang binary object.</p>
- <p>The function returns the size of <c><![CDATA[term]]></c> as described
- above, or -1 if <c><![CDATA[term]]></c> is not one of the two supported
- types. </p>
+ <p>Returns either the arity of an Erlang tuple or the
+ number of bytes in an Erlang binary object.</p>
+ <p><c>term</c> is an Erlang tuple or an Erlang binary
+ object.</p>
+ <p>Returns the size of <c>term</c> as described
+ above, or <c>-1</c> if <c>term</c> is not one of the two
+ supported types.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_tl(list)</nametext></name>
- <fsummary>Extracts the tail from a list</fsummary>
+ <fsummary>Extract the tail from a list.</fsummary>
<type>
<v>ETERM *list;</v>
</type>
<desc>
<p>Extracts the tail from a list.</p>
- <p><c><![CDATA[list]]></c> is an Erlang term containing a list.</p>
- <p>The function returns an Erlang list corresponding to the
- original list minus the first element, or NULL pointer if
- <c><![CDATA[list]]></c> was not a list.</p>
+ <p><c>list</c> is an Erlang term containing a list.</p>
+ <p>Returns an Erlang list corresponding to the
+ original list minus the first element, or <c>NULL</c> pointer if
+ <c>list</c> was not a list.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_var_content(term, name)</nametext></name>
- <fsummary>Extracts the content of a variable</fsummary>
+ <fsummary>Extract the content of a variable.</fsummary>
<type>
<v>ETERM *term;</v>
<v>char *name;</v>
</type>
<desc>
- <p>This function returns the contents of the specified
- variable in an Erlang term.
- </p>
- <p><c><![CDATA[term]]></c> is an Erlang term. In order for this function
- to succeed, <c><![CDATA[term]]></c> must be an Erlang variable with the
- specified name, or it must be an Erlang list or tuple
- containing a variable with the specified name. Other Erlang
- types cannot contain variables.</p>
- <p><c><![CDATA[name]]></c> is the name of an Erlang variable.</p>
+ <p>Returns the contents of the specified variable in an Erlang term.</p>
+ <list type="bulleted">
+ <item><c>term</c> is an Erlang term. In order for this
+ function to succeed,
+ <c>term</c> must either be an Erlang variable with
+ the specified name, or it must be an Erlang list or tuple
+ containing a variable with the specified name. Other Erlang
+ types cannot contain variables.</item>
+ <item><c>name</c> is the name of an Erlang variable.
+ </item>
+ </list>
<p>Returns the Erlang object corresponding to the value of
- <c><![CDATA[name]]></c> in <c><![CDATA[term]]></c>. If no variable with the name
- <c><![CDATA[name]]></c> was found in <c><![CDATA[term]]></c>, or if <c><![CDATA[term]]></c> is
- not a valid Erlang term, NULL is returned.</p>
+ <c>name</c> in <c>term</c>. If no variable
+ with the name <c>name</c> is found in
+ <c>term</c>, or if <c>term</c> is
+ not a valid Erlang term, <c>NULL</c> is returned.</p>
</desc>
</func>
</funcs>
</cref>
-
diff --git a/lib/erl_interface/doc/src/erl_format.xml b/lib/erl_interface/doc/src/erl_format.xml
index 6e3ac4f0c9..5b8b7b5e78 100644
--- a/lib/erl_interface/doc/src/erl_format.xml
+++ b/lib/erl_interface/doc/src/erl_format.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>erl_format</title>
@@ -28,51 +28,42 @@
<docno></docno>
<approved>Bjarne D&auml;cker</approved>
<checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>961016</date>
+ <date>1996-10-16</date>
<rev>A</rev>
- <file>erl_format.sgml</file>
+ <file>erl_format.xml</file>
</header>
<lib>erl_format</lib>
- <libsummary>Create and Match Erlang Terms</libsummary>
+ <libsummary>Create and match Erlang terms.</libsummary>
<description>
- <p>This module contains two routines - one general function for
+ <p>This module contains two routines: one general function for
creating Erlang terms and one for pattern matching Erlang terms.</p>
</description>
+
<funcs>
<func>
- <name><ret>ETERM *</ret><nametext>erl_format(FormatStr, ... )</nametext></name>
- <fsummary>Creates an Erlang term</fsummary>
+ <name><ret>ETERM *</ret><nametext>erl_format(FormatStr, ...)</nametext></name>
+ <fsummary>Create an Erlang term.</fsummary>
<type>
<v>char *FormatStr;</v>
</type>
<desc>
- <p>This is a general function for creating Erlang terms using
+ <p>A general function for creating Erlang terms using
a format specifier and a corresponding set of arguments, much
- in the way <c><![CDATA[printf()]]></c> works.</p>
- <p><c><![CDATA[FormatStr]]></c> is a format specification string. The set
- of valid format specifiers is as follows:</p>
+ in the way <c>printf()</c> works.</p>
+ <p><c>FormatStr</c> is a format specification string.
+ The valid format specifiers are as follows:</p>
<list type="bulleted">
- <item>
- <p>~i - Integer</p>
- </item>
- <item>
- <p>~f - Floating point</p>
- </item>
- <item>
- <p>~a - Atom</p>
- </item>
- <item>
- <p>~s - String</p>
- </item>
- <item>
- <p>~w - Arbitrary Erlang term</p>
- </item>
+ <item><c>~i</c> - Integer</item>
+ <item><c>~f</c> - Floating point</item>
+ <item><c>~a</c> - Atom</item>
+ <item><c>~s</c> - String</item>
+ <item><c>~w</c> - Arbitrary Erlang term</item>
</list>
- <p>For each format specifier that appears in <c><![CDATA[FormatStr]]></c>,
+ <p>For each format specifier included in <c>FormatStr</c>,
there must be a corresponding argument following
- <c><![CDATA[FormatStr]]></c>. An Erlang term is built according to the
- <c><![CDATA[FormatStr]]></c> with values and Erlang terms substituted from
- the corresponding arguments and according to the individual
+ <c>FormatStr</c>. An Erlang term is built according to
+ <c>FormatStr</c> with values and Erlang terms substituted
+ from the corresponding arguments, and according to the individual
format specifiers. For example:</p>
<code type="none"><![CDATA[
erl_format("[{name,~a},{age,~i},{data,~w}]",
@@ -80,34 +71,40 @@ erl_format("[{name,~a},{age,~i},{data,~w}]",
21,
erl_format("[{adr,~s,~i}]","E-street",42));
]]></code>
- <p>This will create an <c><![CDATA[(ETERM *)]]></c> structure corresponding
- to the Erlang term:
- <c><![CDATA[[{name,madonna},{age,21},{data,[{adr,"E-street",42}]}]]]></c></p>
- <p>The function returns an Erlang term, or NULL if
- <c><![CDATA[FormatStr]]></c> does not describe a valid Erlang term.</p>
+ <p>This creates an <c>(ETERM *)</c> structure corresponding
+ to the Erlang term
+ <c>[{name,madonna},{age,21},{data,[{adr,"E-street",42}]}]</c></p>
+ <p>The function returns an Erlang term, or <c>NULL</c> if
+ <c>FormatStr</c> does not describe a valid Erlang
+ term.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_match(Pattern, Term)</nametext></name>
- <fsummary>Performs pattern matching</fsummary>
+ <fsummary>Perform pattern matching.</fsummary>
<type>
<v>ETERM *Pattern,*Term;</v>
</type>
<desc>
<p>This function is used to perform pattern matching similar
- to that done in Erlang. Refer to an Erlang manual for matching
- rules and more examples.</p>
- <p><c><![CDATA[Pattern]]></c> is an Erlang term, possibly containing unbound
- variables. </p>
- <p><c><![CDATA[Term]]></c> is an Erlang term that we wish to match against
- <c><![CDATA[Pattern]]></c>.</p>
- <p><c><![CDATA[Term]]></c> and <c><![CDATA[Pattern]]></c> are compared, and any
- unbound variables in <c><![CDATA[Pattern]]></c> are bound to corresponding
- values in <c><![CDATA[Term]]></c>. </p>
- <p>If <c><![CDATA[Term]]></c> and <c><![CDATA[Pattern]]></c> can be matched, the
- function returns a non-zero value and binds any unbound
- variables in <c><![CDATA[Pattern]]></c>. If <c><![CDATA[Term]]></c> <c><![CDATA[Pattern]]></c> do
- not match, the function returns 0. For example:</p>
+ to that done in Erlang. For matching rules and more examples, see
+ section <seealso marker="doc/reference_manual:patterns">
+ Pattern Matching</seealso> in the Erlang Reference Manual.</p>
+ <list type="bulleted">
+ <item><c>Pattern</c> is an Erlang term, possibly
+ containing unbound variables.</item>
+ <item><c>Term</c> is an Erlang term that we wish to match
+ against <c>Pattern</c>.</item>
+ </list>
+ <p><c>Term</c> and <c>Pattern</c> are compared
+ and any unbound variables in <c>Pattern</c> are bound to
+ corresponding values in <c>Term</c>.</p>
+ <p>If <c>Term</c> and <c>Pattern</c> can be
+ matched, the function returns a non-zero value and binds any unbound
+ variables in <c>Pattern</c>. If <c>Term</c>
+ and <c>Pattern</c> do
+ not match, <c>0</c> is returned. For example:</p>
<code type="none"><![CDATA[
ETERM *term, *pattern, *pattern2;
term1 = erl_format("{14,21}");
@@ -132,11 +129,10 @@ if (erl_match(pattern2, term2)) {
...
}
]]></code>
- <p><c><![CDATA[erl_var_content()]]></c> can be used to retrieve the
+ <p><c>erl_var_content()</c> can be used to retrieve the
content of any variables bound as a result of a call to
- <c><![CDATA[erl_match()]]></c>.</p>
+ <c>erl_match()</c>.</p>
</desc>
</func>
</funcs>
</cref>
-
diff --git a/lib/erl_interface/doc/src/erl_global.xml b/lib/erl_interface/doc/src/erl_global.xml
index d6bfffc69d..2fa0045adf 100644
--- a/lib/erl_interface/doc/src/erl_global.xml
+++ b/lib/erl_interface/doc/src/erl_global.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>erl_global</title>
@@ -28,115 +28,125 @@
<docno></docno>
<approved>Gordon Beaton</approved>
<checked>Gordon Beaton</checked>
- <date>980703</date>
+ <date>1998-07-03</date>
<rev>A</rev>
- <file>erl_global.sgml</file>
+ <file>erl_global.xml</file>
</header>
<lib>erl_global</lib>
- <libsummary>Access globally registered names</libsummary>
+ <libsummary>Access globally registered names.</libsummary>
<description>
<p>This module provides support for registering, looking
- up and unregistering names in the Erlang Global module. For more
- information, see the description of Global in the reference manual.</p>
- <p>Note that the functions below perform an RPC using an open file
- descriptor provided by the caller. This file descriptor must
- not be used for other traffic during the global operation or the
- function may receive unexpected data and fail.</p>
+ up, and unregistering names in the <c>global</c> module.
+ For more information, see
+ <seealso marker="kernel:global"><c>kernel:global</c></seealso>.</p>
+
+ <p>Notice that the functions below perform an RPC using an open file
+ descriptor provided by the caller. This file descriptor must
+ not be used for other traffic during the global operation, as the
+ function can then receive unexpected data and fail.</p>
</description>
+
<funcs>
<func>
<name><ret>char **</ret><nametext>erl_global_names(fd,count)</nametext></name>
- <fsummary>Obtain list of Global names</fsummary>
+ <fsummary>Obtain list of global names.</fsummary>
<type>
<v>int fd;</v>
<v>int *count;</v>
</type>
<desc>
- <p>Retrieve a list of all known global names.
- </p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.
- </p>
- <p><c><![CDATA[count]]></c> is the address of an integer, or NULL. If
- <c><![CDATA[count]]></c> is not NULL, it will be set by the function to
- the number of names found.
- </p>
+ <p>Retrieves a list of all known global names.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>count</c> is the address of an integer, or
+ <c>NULL</c>. If <c>count</c> is not <c>NULL</c>, it is
+ set by the function to the number of names found.</item>
+ </list>
<p>On success, the function returns an array of strings, each
- containing a single registered name, and sets <c><![CDATA[count]]></c> to
+ containing a single registered name, and sets
+ <c>count</c> to
the number of names found. The array is terminated
- by a single NULL pointer. On failure, the function returns
- NULL and <c><![CDATA[count]]></c> is not modified.
- </p>
+ by a single <c>NULL</c> pointer. On failure, the function returns
+ <c>NULL</c> and <c>count</c> is not modified.</p>
<note>
<p>It is the caller's responsibility to free the array
afterwards. It has been allocated by the function with a
- single call to <c><![CDATA[malloc()]]></c>, so a single <c><![CDATA[free()]]></c> is
- all that is necessary.</p>
+ single call to <c>malloc()</c>, so a single
+ <c>free()</c> is all that is necessary.</p>
</note>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_global_register(fd,name,pid)</nametext></name>
- <fsummary>Register a name in Global</fsummary>
+ <fsummary>Register a name in global.</fsummary>
<type>
<v>int fd;</v>
<v>const char *name;</v>
<v>ETERM *pid;</v>
</type>
<desc>
- <p>This function registers a name in Global.
- </p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.
- </p>
- <p><c><![CDATA[name]]></c> is the name to register in Global.
- </p>
- <p><c><![CDATA[pid]]></c> is the pid that should be associated with
- <c><![CDATA[name]]></c>. This is the value that Global will return when
- processes request the location of <c><![CDATA[name]]></c>.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
+ <p>Registers a name in <c>global</c>.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>name</c> is the name to register in
+ <c>global</c>.</item>
+ <item><c>pid</c> is the pid that is to be associated with
+ <c>name</c>. This value is returned by <c>global</c>
+ when processes request the location of <c>name</c>.
+ </item>
+ </list>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_global_unregister(fd,name)</nametext></name>
- <fsummary>Unregister a name in Global</fsummary>
+ <fsummary>Unregister a name from global.</fsummary>
<type>
<v>int fd;</v>
<v>const char *name;</v>
</type>
<desc>
- <p>This function unregisters a name from Global.
- </p>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.
- </p>
- <p><c><![CDATA[name]]></c> is the name to unregister from Global.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
+ <p>Unregisters a name from <c>global</c>.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>name</c> is the name to unregister from
+ <c>global</c>.</item>
+ </list>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_global_whereis(fd,name,node)</nametext></name>
- <fsummary>Look up a name in global</fsummary>
+ <fsummary>Look up a name in global.</fsummary>
<type>
<v>int fd;</v>
<v>const char *name;</v>
<v>char *node;</v>
</type>
<desc>
- <p><c><![CDATA[fd]]></c> is an open descriptor to an Erlang connection.
- </p>
- <p><c><![CDATA[name]]></c> is the name that is to be looked up in Global.
- </p>
- <p>If <c><![CDATA[node]]></c> is not NULL, it is a pointer to a buffer
- where the function can fill in the name of the node where
- <c><![CDATA[name]]></c> is found. <c><![CDATA[node]]></c> can be passed directly to
- <c><![CDATA[erl_connect()]]></c> if necessary.
- </p>
- <p>On success, the function returns an Erlang Pid containing the address
- of the given name, and node will be initialized to
- the nodename where <c><![CDATA[name]]></c> is found. On failure NULL will be
- returned and <c><![CDATA[node]]></c> will not be modified.</p>
+ <p>Looks up a name in <c>global</c>.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open descriptor to an Erlang
+ connection.</item>
+ <item><c>name</c> is the name that is to be looked up in
+ <c>global</c>.</item>
+ </list>
+ <p>If <c>node</c> is not <c>NULL</c>, it is a pointer to a
+ buffer where the function can fill in the name of the node where
+ <c>name</c> is found. <c>node</c> can be
+ passed directly to <c>erl_connect()</c> if necessary.</p>
+ <p>On success, the function returns an Erlang pid containing the address
+ of the specified name, and the node is initialized to
+ the node name where <c>name</c> is found. On failure,
+ <c>NULL</c> is returned and <c>node</c> is not
+ modified.</p>
</desc>
</func>
</funcs>
</cref>
-
diff --git a/lib/erl_interface/doc/src/erl_interface.xml b/lib/erl_interface/doc/src/erl_interface.xml
index a9d421bbeb..4e66655b39 100644
--- a/lib/erl_interface/doc/src/erl_interface.xml
+++ b/lib/erl_interface/doc/src/erl_interface.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>The Erl_Interface Library</title>
@@ -72,51 +72,51 @@ Eshell V4.7.4 (abort with ^G)
1> code:root_dir().
/usr/local/otp ]]></code>
<p>To compile your code, make sure that your C compiler knows where
- to find <c><![CDATA[erl_interface.h]]></c> by specifying an appropriate <c><![CDATA[-I]]></c>
- argument on the command line, or by adding it to the <c><![CDATA[CFLAGS]]></c>
- definition in your <c><![CDATA[Makefile]]></c>. The correct value for this path is
- <c><![CDATA[$OTPROOT/lib/erl_interface]]></c><em>Vsn</em><c><![CDATA[/include]]></c>, where <c><![CDATA[$OTPROOT]]></c> is the path
- reported by <c><![CDATA[code:root_dir/0]]></c> in the above example, and <em>Vsn</em> is
+ to find <c>erl_interface.h</c> by specifying an appropriate <c>-I</c>
+ argument on the command line, or by adding it to the <c>CFLAGS</c>
+ definition in your <c>Makefile</c>. The correct value for this path is
+ <c>$OTPROOT/lib/erl_interface</c><em>Vsn</em><c>/include</c>, where <c>$OTPROOT</c> is the path
+ reported by <c>code:root_dir/0</c> in the above example, and <em>Vsn</em> is
the version of the Erl_interface application, for example
- <c><![CDATA[erl_interface-3.2.3]]></c></p>
+ <c>erl_interface-3.2.3</c></p>
<code type="none"><![CDATA[
$ cc -c -I/usr/local/otp/lib/erl_interface-3.2.3/include myprog.c ]]></code>
<p>When linking, you will need to specify the path to
- <c><![CDATA[liberl_interface.a]]></c> and <c><![CDATA[libei.a]]></c> with
- <c><![CDATA[-L$OTPROOT/lib/erl_interface-3.2.3/lib]]></c>, and you will need to specify the
- name of the libraries with <c><![CDATA[-lerl_interface -lei]]></c>. You can do
- this on the command line or by adding the flags to the <c><![CDATA[LDFLAGS]]></c>
- definition in your <c><![CDATA[Makefile]]></c>.</p>
+ <c>liberl_interface.a</c> and <c>libei.a</c> with
+ <c>-L$OTPROOT/lib/erl_interface-3.2.3/lib</c>, and you will need to specify the
+ name of the libraries with <c>-lerl_interface -lei</c>. You can do
+ this on the command line or by adding the flags to the <c>LDFLAGS</c>
+ definition in your <c>Makefile</c>.</p>
<code type="none"><![CDATA[
$ ld -L/usr/local/otp/lib/erl_interface-3.2.3/
lib myprog.o -lerl_interface -lei -o myprog ]]></code>
<p>Also, on some systems it may be necessary to link with some
- additional libraries (e.g. <c><![CDATA[libnsl.a]]></c> and <c><![CDATA[libsocket.a]]></c> on
- Solaris, or <c><![CDATA[wsock32.lib]]></c> on Windows) in order to use the
+ additional libraries (e.g. <c>libnsl.a</c> and <c>libsocket.a</c> on
+ Solaris, or <c>wsock32.lib</c> on Windows) in order to use the
communication facilities of Erl_Interface.</p>
<p>If you are using Erl_Interface functions in a threaded
application based on POSIX threads or Solaris threads, then
Erl_Interface needs access to some of the synchronization
facilities in your threads package, and you will need to specify
additional compiler flags in order to indicate which of the packages
- you are using. Define <c><![CDATA[_REENTRANT]]></c> and either <c><![CDATA[STHREADS]]></c> or
- <c><![CDATA[PTHREADS]]></c>. The default is to use POSIX threads if
- <c><![CDATA[_REENTRANT]]></c> is specified.</p>
+ you are using. Define <c>_REENTRANT</c> and either <c>STHREADS</c> or
+ <c>PTHREADS</c>. The default is to use POSIX threads if
+ <c>_REENTRANT</c> is specified.</p>
<p>Note that both single threaded and default versions of the Erl_interface
and Ei libraries are provided. (The single threaded versions are named
- <c><![CDATA[liberl_interface_st]]></c> and <c><![CDATA[libei_st]]></c>). Whether the default
+ <c>liberl_interface_st</c> and <c>libei_st</c>). Whether the default
versions of the libraries have support for threads or not is determined by if
the platform in question has support for POSIX or Solaris threads. To check this,
- have a look in the <c><![CDATA[eidefs.mk]]></c> file in the erl_interface src directory.</p>
+ have a look in the <c>eidefs.mk</c> file in the erl_interface src directory.</p>
</section>
<section>
<title>Initializing the erl_interface Library</title>
<p>Before calling any of the other Erl_Interface functions, you
- must call <c><![CDATA[erl_init()]]></c> exactly once to initialize the library.
- <c><![CDATA[erl_init()]]></c> takes two arguments, however the arguments are no
+ must call <c>erl_init()</c> exactly once to initialize the library.
+ <c>erl_init()</c> takes two arguments, however the arguments are no
longer used by Erl_Interface, and should therefore be specified
- as <c><![CDATA[erl_init(NULL,0)]]></c>.</p>
+ as <c>erl_init(NULL,0)</c>.</p>
</section>
<section>
@@ -129,7 +129,7 @@ $ ld -L/usr/local/otp/lib/erl_interface-3.2.3/
number of C functions which create and manipulate Erlang data
structures. The library also contains an encode and a decode function.
The example below shows how to create and encode an Erlang tuple
- <c><![CDATA[{tobbe,3928}]]></c>:</p>
+ <c>{tobbe,3928}</c>:</p>
<code type="none"><![CDATA[
ETERM *arr[2], *tuple;
@@ -140,26 +140,26 @@ arr[0] = erl_mk_atom("tobbe");
arr[1] = erl_mk_integer(3928);
tuple = erl_mk_tuple(arr, 2);
i = erl_encode(tuple, buf); ]]></code>
- <p>Alternatively, you can use <c><![CDATA[erl_send()]]></c> and
- <c><![CDATA[erl_receive_msg]]></c>, which handle the encoding and decoding of
+ <p>Alternatively, you can use <c>erl_send()</c> and
+ <c>erl_receive_msg</c>, which handle the encoding and decoding of
messages transparently.</p>
<p>Refer to the Reference Manual for a complete description of the
following modules:</p>
<list type="bulleted">
- <item>the <c><![CDATA[erl_eterm]]></c> module for creating Erlang terms</item>
- <item>the <c><![CDATA[erl_marshal]]></c> module for encoding and decoding routines.</item>
+ <item>the <c>erl_eterm</c> module for creating Erlang terms</item>
+ <item>the <c>erl_marshal</c> module for encoding and decoding routines.</item>
</list>
</section>
<section>
<title>Building Terms and Patterns</title>
- <p>The previous example can be simplified by using
- <c><![CDATA[erl_format()]]></c> to create an Erlang term.</p>
+ <p>The previous example can be simplified by using
+ <c>erl_format()</c> to create an Erlang term.</p>
<code type="none"><![CDATA[
ETERM *ep;
ep = erl_format("{~a,~i}", "tobbe", 3928); ]]></code>
- <p>Refer to the Reference Manual, the <c><![CDATA[erl_format]]></c> module, for a
+ <p>Refer to the Reference Manual, the <c>erl_format</c> module, for a
full description of the different format directives. The following
example is more complex:</p>
<code type="none"><![CDATA[
@@ -171,10 +171,10 @@ ep = erl_format("[{name,~a},{age,~i},{data,~w}]",
erl_format("[{adr,~s,~i}]", "E-street", 42));
erl_free_compound(ep); ]]></code>
<p>As in previous examples, it is your responsibility to free the
- memory allocated for Erlang terms. In this example,
- <c><![CDATA[erl_free_compound()]]></c> ensures that the complete term pointed to
- by <c><![CDATA[ep]]></c> is released. This is necessary, because the pointer from
- the second call to <c><![CDATA[erl_format()]]></c> is lost. </p>
+ memory allocated for Erlang terms. In this example,
+ <c>erl_free_compound()</c> ensures that the complete term pointed to
+ by <c>ep</c> is released. This is necessary, because the pointer from
+ the second call to <c>erl_format()</c> is lost. </p>
<p>The following
example shows a slightly different solution:</p>
<code type="none"><![CDATA[
@@ -186,7 +186,7 @@ ep = erl_format("[{name,~a},{age,~i},{data,~w}]",
erl_free_term(ep);
erl_free_term(ep2); ]]></code>
<p>In this case, you free the two terms independently. The order in
- which you free the terms <c><![CDATA[ep]]></c> and <c><![CDATA[ep2]]></c> is not important,
+ which you free the terms <c>ep</c> and <c>ep2</c> is not important,
because the Erl_Interface library uses reference counting to
determine when it is safe to actually remove objects. </p>
<p>If you are not sure whether you have freed the terms properly, you
@@ -204,14 +204,14 @@ printf("length of freelist: %ld\
/* really free the freelist */
erl_eterm_release();
]]></code>
- <p>Refer to the Reference Manual, the <c><![CDATA[erl_malloc]]></c> module for more
+ <p>Refer to the Reference Manual, the <c>erl_malloc</c> module for more
information.</p>
</section>
<section>
<title>Pattern Matching</title>
<p>An Erlang pattern is a term that may contain unbound variables or
- <c><![CDATA["do not care"]]></c> symbols. Such a pattern can be matched against a
+ <c>"do not care"</c> symbols. Such a pattern can be matched against a
term and, if the match is successful, any unbound variables in the
pattern will be bound as a side effect. The content of a bound
variable can then be retrieved.</p>
@@ -219,13 +219,13 @@ erl_eterm_release();
ETERM *pattern;
pattern = erl_format("{madonna,Age,_}"); ]]></code>
- <p><c><![CDATA[erl_match()]]></c> is used to perform pattern matching. It takes a
+ <p><c>erl_match()</c> is used to perform pattern matching. It takes a
pattern and a term and tries to match them. As a side effect any unbound
variables in the pattern will be bound. In the following example, we
create a pattern with a variable <em>Age</em> which appears at two
positions in the tuple. The pattern match is performed as follows:</p>
<list type="ordered">
- <item><c><![CDATA[erl_match()]]></c> will bind the contents of
+ <item><c>erl_match()</c> will bind the contents of
<em>Age</em> to <em>21</em> the first time it reaches the variable</item>
<item>the second occurrence of <em>Age</em> will cause a test for
equality between the terms since <em>Age</em> is already bound to
@@ -248,14 +248,14 @@ if (erl_match(pattern, term)) {
}
erl_free_term(pattern);
erl_free_term(term); ]]></code>
- <p>Refer to the Reference Manual, the <c><![CDATA[erl_match()]]></c> function for
+ <p>Refer to the Reference Manual, the <c>erl_match()</c> function for
more information.</p>
</section>
<section>
<title>Connecting to a Distributed Erlang Node</title>
<p>In order to connect to a distributed Erlang node you need to first
- initialize the connection routine with <c><![CDATA[erl_connect_init()]]></c>,
+ initialize the connection routine with <c>erl_connect_init()</c>,
which stores information such as the host name, node name, and IP
address for later use:</p>
<code type="none"><![CDATA[
@@ -263,9 +263,9 @@ int identification_number = 99;
int creation=1;
char *cookie="a secret cookie string"; /* An example */
erl_connect_init(identification_number, cookie, creation); ]]></code>
- <p>Refer to the Reference Manual, the <c><![CDATA[erl_connect]]></c> module for more information.</p>
+ <p>Refer to the Reference Manual, the <c>erl_connect</c> module for more information.</p>
<p>After initialization, you set up the connection to the Erlang node.
- Use <c><![CDATA[erl_connect()]]></c> to specify the Erlang node you want to
+ Use <c>erl_connect()</c> to specify the Erlang node you want to
connect to. The following example sets up the connection and should
result in a valid socket file descriptor:</p>
<code type="none"><![CDATA[
@@ -273,45 +273,45 @@ int sockfd;
char *nodename="[email protected]"; /* An example */
if ((sockfd = erl_connect(nodename)) < 0)
erl_err_quit("ERROR: erl_connect failed"); ]]></code>
- <p><c><![CDATA[erl_err_quit()]]></c> prints the specified string and terminates
- the program. Refer to the Reference Manual, the <c><![CDATA[erl_error()]]></c>
+ <p><c>erl_err_quit()</c> prints the specified string and terminates
+ the program. Refer to the Reference Manual, the <c>erl_error()</c>
function for more information.</p>
</section>
<section>
<title>Using EPMD</title>
- <p><c><![CDATA[Epmd]]></c> is the Erlang Port Mapper Daemon. Distributed Erlang nodes
- register with <c><![CDATA[epmd]]></c> on the localhost to indicate to other nodes that
- they exist and can accept connections. <c><![CDATA[Epmd]]></c> maintains a register of
+ <p><c>Epmd</c> is the Erlang Port Mapper Daemon. Distributed Erlang nodes
+ register with <c>epmd</c> on the localhost to indicate to other nodes that
+ they exist and can accept connections. <c>Epmd</c> maintains a register of
node and port number information, and when a node wishes to connect to
- another node, it first contacts <c><![CDATA[epmd]]></c> in order to find out the correct
+ another node, it first contacts <c>epmd</c> in order to find out the correct
port number to connect to.</p>
- <p>When you use <c><![CDATA[erl_connect()]]></c> to connect to an Erlang node, a
- connection is first made to <c><![CDATA[epmd]]></c> and, if the node is known, a
+ <p>When you use <c>erl_connect()</c> to connect to an Erlang node, a
+ connection is first made to <c>epmd</c> and, if the node is known, a
connection is then made to the Erlang node.</p>
- <p>C nodes can also register themselves with <c><![CDATA[epmd]]></c> if they want other
+ <p>C nodes can also register themselves with <c>epmd</c> if they want other
nodes in the system to be able to find and connect to them.</p>
- <p>Before registering with <c><![CDATA[epmd]]></c>, you need to first create a listen socket
+ <p>Before registering with <c>epmd</c>, you need to first create a listen socket
and bind it to a port. Then:</p>
<code type="none"><![CDATA[
int pub;
pub = erl_publish(port); ]]></code>
- <p><c><![CDATA[pub]]></c> is a file descriptor now connected to <c><![CDATA[epmd]]></c>. <c><![CDATA[Epmd]]></c>
+ <p><c>pub</c> is a file descriptor now connected to <c>epmd</c>. <c>Epmd</c>
monitors the other end of the connection, and if it detects that the
connection has been closed, the node will be unregistered. So, if you
explicitly close the descriptor or if your node fails, it will be
- unregistered from <c><![CDATA[epmd]]></c>.</p>
+ unregistered from <c>epmd</c>.</p>
<p>Be aware that on some systems (such as VxWorks), a failed node will
not be detected by this mechanism since the operating system does not
automatically close descriptors that were left open when the node
- failed. If a node has failed in this way, <c><![CDATA[epmd]]></c> will prevent you from
+ failed. If a node has failed in this way, <c>epmd</c> will prevent you from
registering a new node with the old name, since it thinks that the old
name is still in use. In this case, you must unregister the name
explicitly:</p>
<code type="none"><![CDATA[
erl_unpublish(node); ]]></code>
- <p>This will cause <c><![CDATA[epmd]]></c> to close the connection from the far end. Note
+ <p>This will cause <c>epmd</c> to close the connection from the far end. Note
that if the name was in fact still in use by a node, the results of
this operation are unpredictable. Also, doing this does not cause the
local end of the connection to close, so resources may be consumed.</p>
@@ -321,8 +321,8 @@ erl_unpublish(node); ]]></code>
<title>Sending and Receiving Erlang Messages</title>
<p>Use one of the following two functions to send messages:</p>
<list type="bulleted">
- <item><c><![CDATA[erl_send()]]></c></item>
- <item><c><![CDATA[erl_reg_send()]]></c></item>
+ <item><c>erl_send()</c></item>
+ <item><c>erl_reg_send()</c></item>
</list>
<p>As in Erlang, it is possible to send messages to a
<em>Pid</em> or to a registered name. It is easier to send a
@@ -330,17 +330,17 @@ erl_unpublish(node); ]]></code>
a suitable <em>Pid</em>.</p>
<p>Use one of the following two functions to receive messages:</p>
<list type="bulleted">
- <item><c><![CDATA[erl_receive()]]></c></item>
- <item><c><![CDATA[erl_receive_msg()]]></c></item>
+ <item><c>erl_receive()</c></item>
+ <item><c>erl_receive_msg()</c></item>
</list>
- <p><c><![CDATA[erl_receive()]]></c> receives the message into a buffer, while
- <c><![CDATA[erl_receive_msg()]]></c> decodes the message into an Erlang term. </p>
+ <p><c>erl_receive()</c> receives the message into a buffer, while
+ <c>erl_receive_msg()</c> decodes the message into an Erlang term. </p>
<section>
<title>Example of Sending Messages</title>
- <p>In the following example, <c><![CDATA[{Pid, hello_world}]]></c> is
- sent to a registered process <c><![CDATA[my_server]]></c>. The message is encoded
- by <c><![CDATA[erl_send()]]></c>:</p>
+ <p>In the following example, <c>{Pid, hello_world}</c> is
+ sent to a registered process <c>my_server</c>. The message is encoded
+ by <c>erl_send()</c>:</p>
<code type="none"><![CDATA[
extern const char *erl_thisnodename(void);
extern short erl_thiscreation(void);
@@ -355,15 +355,15 @@ emsg = erl_mk_tuple(arr, 2);
erl_reg_send(sockfd, "my_server", emsg);
erl_free_term(emsg); ]]></code>
<p>The first element of the tuple that is sent is your own
- <em>Pid</em>. This enables <c><![CDATA[my_server]]></c> to reply. Refer to the
- Reference Manual, the <c><![CDATA[erl_connect]]></c> module for more information
+ <em>Pid</em>. This enables <c>my_server</c> to reply. Refer to the
+ Reference Manual, the <c>erl_connect</c> module for more information
about send primitives.</p>
</section>
<section>
<title>Example of Receiving Messages</title>
- <p>In this example <c><![CDATA[{Pid, Something}]]></c> is received. The
- received Pid is then used to return <c><![CDATA[{goodbye,Pid}]]></c></p>
+ <p>In this example <c>{Pid, Something}</c> is received. The
+ received Pid is then used to return <c>{goodbye,Pid}</c></p>
<code type="none"><![CDATA[
ETERM *arr[2], *answer;
int sockfd,rc;
@@ -383,18 +383,18 @@ if ((rc = erl_receive_msg(sockfd , buf, BUFSIZE, &emsg)) == ERL_MSG) {
<p>In order to provide robustness, a distributed Erlang node
occasionally polls all its connected neighbours in an attempt to
detect failed nodes or communication links. A node which receives such
- a message is expected to respond immediately with an <c><![CDATA[ERL_TICK]]></c> message.
- This is done automatically by <c><![CDATA[erl_receive()]]></c>, however when this
- has occurred <c><![CDATA[erl_receive]]></c> returns <c><![CDATA[ERL_TICK]]></c> to the caller
- without storing a message into the <c><![CDATA[ErlMessage]]></c> structure.</p>
+ a message is expected to respond immediately with an <c>ERL_TICK</c> message.
+ This is done automatically by <c>erl_receive()</c>, however when this
+ has occurred <c>erl_receive</c> returns <c>ERL_TICK</c> to the caller
+ without storing a message into the <c>ErlMessage</c> structure.</p>
<p>When a message has been received, it is the caller's responsibility
- to free the received message <c><![CDATA[emsg.msg]]></c> as well as <c><![CDATA[emsg.to]]></c>
- or <c><![CDATA[emsg.from]]></c>, depending on the type of message received.</p>
+ to free the received message <c>emsg.msg</c> as well as <c>emsg.to</c>
+ or <c>emsg.from</c>, depending on the type of message received.</p>
<p>Refer to the Reference Manual for additional information about the
following modules:</p>
<list type="bulleted">
- <item><c><![CDATA[erl_connect]]></c></item>
- <item><c><![CDATA[erl_eterm]]></c>.</item>
+ <item><c>erl_connect</c></item>
+ <item><c>erl_eterm</c>.</item>
</list>
</section>
</section>
@@ -421,12 +421,12 @@ if (!erl_match(ep, reply))
");
erl_free_term(ep);
erl_free_term(reply); ]]></code>
- <p><c><![CDATA[c:c/1]]></c> is called to compile the specified module on the
- remote node. <c><![CDATA[erl_match()]]></c> checks that the compilation was
- successful by testing for the expected <c><![CDATA[ok]]></c>.</p>
- <p>Refer to the Reference Manual, the <c><![CDATA[erl_connect]]></c> module for
- more information about <c><![CDATA[erl_rpc()]]></c>, and its companions
- <c><![CDATA[erl_rpc_to()]]></c> and <c><![CDATA[erl_rpc_from()]]></c>.</p>
+ <p><c>c:c/1</c> is called to compile the specified module on the
+ remote node. <c>erl_match()</c> checks that the compilation was
+ successful by testing for the expected <c>ok</c>.</p>
+ <p>Refer to the Reference Manual, the <c>erl_connect</c> module for
+ more information about <c>erl_rpc()</c>, and its companions
+ <c>erl_rpc_to()</c> and <c>erl_rpc_from()</c>.</p>
</section>
<section>
@@ -454,14 +454,14 @@ if (names)
",names[i]);
free(names); ]]></code>
- <p><c><![CDATA[erl_global_names()]]></c> allocates and returns a buffer containing
- all the names known to global. <c><![CDATA[count]]></c> will be initialized to
+ <p><c>erl_global_names()</c> allocates and returns a buffer containing
+ all the names known to global. <c>count</c> will be initialized to
indicate how many names are in the array. The array of strings in
names is terminated by a NULL pointer, so it is not necessary to use
- <c><![CDATA[count]]></c> to determine when the last name is reached.</p>
+ <c>count</c> to determine when the last name is reached.</p>
<p>It is the caller's responsibility to free the array.
- <c><![CDATA[erl_global_names()]]></c> allocates the array and all of the strings
- using a single call to <c><![CDATA[malloc()]]></c>, so <c><![CDATA[free(names)]]></c> is all
+ <c>erl_global_names()</c> allocates the array and all of the strings
+ using a single call to <c>malloc()</c>, so <c>free(names)</c> is all
that is necessary.</p>
<p>To look up one of the names:</p>
<code type="none"><![CDATA[
@@ -469,13 +469,13 @@ ETERM *pid;
char node[256];
pid = erl_global_whereis(fd,"schedule",node); ]]></code>
- <p>If <c><![CDATA["schedule"]]></c> is known to global, an Erlang pid is returned
+ <p>If <c>"schedule"</c> is known to global, an Erlang pid is returned
that can be used to send messages to the schedule service.
- Additionally, <c><![CDATA[node]]></c> will be initialized to contain the name of
+ Additionally, <c>node</c> will be initialized to contain the name of
the node where the service is registered, so that you can make a
- connection to it by simply passing the variable to <c><![CDATA[erl_connect()]]></c>.</p>
+ connection to it by simply passing the variable to <c>erl_connect()</c>.</p>
<p>Before registering a name, you should already have registered your
- port number with <c><![CDATA[epmd]]></c>. This is not strictly necessary, but if you
+ port number with <c>epmd</c>. This is not strictly necessary, but if you
neglect to do so, then other nodes wishing to communicate with your
service will be unable to find or connect to your process.</p>
<p>Create a pid that Erlang processes can use to communicate with your
@@ -485,9 +485,9 @@ ETERM *pid;
pid = erl_mk_pid(thisnode,14,0,0);
erl_global_register(fd,servicename,pid); ]]></code>
- <p>After registering the name, you should use <c><![CDATA[erl_accept()]]></c> to wait for
+ <p>After registering the name, you should use <c>erl_accept()</c> to wait for
incoming connections.</p>
- <p>Do not forget to free <c><![CDATA[pid]]></c> later with <c><![CDATA[erl_free_term()]]></c>!</p>
+ <p>Do not forget to free <c>pid</c> later with <c>erl_free_term()</c>!</p>
<p>To unregister a name:</p>
<code type="none"><![CDATA[
erl_global_unregister(fd,servicename); ]]></code>
@@ -500,7 +500,7 @@ erl_global_unregister(fd,servicename); ]]></code>
restoring them from a Mnesia table on an Erlang node. More detailed
information about the individual API functions can be found in the
Reference Manual.</p>
- <p>Keys are strings, i.e. 0-terminated arrays of characters, and values
+ <p>Keys are strings, i.e. <c>NULL</c>-terminated arrays of characters, and values
are arbitrary objects. Although integers and floating point numbers
are treated specially by the registry, you can store strings or binary
objects of any type as pointers.</p>
@@ -570,12 +570,12 @@ ei_reg_close(reg); ]]></code>
<title>Backing Up the Registry to Mnesia</title>
<p>The contents of a registry can be backed up to Mnesia on a "nearby"
Erlang node. You need to provide an open connection to the Erlang node
- (see <c><![CDATA[erl_connect()]]></c>). Also, Mnesia 3.0 or later must be running
+ (see <c>erl_connect()</c>). Also, Mnesia 3.0 or later must be running
on the Erlang node before the backup is initiated:</p>
<code type="none"><![CDATA[
ei_reg_dump(fd, reg, "mtab", dumpflags); ]]></code>
<p>The example above will backup the contents of the registry to the
- specified Mnesia table <c><![CDATA["mtab"]]></c>. Once a registry has been backed
+ specified Mnesia table <c>"mtab"</c>. Once a registry has been backed
up to Mnesia in this manner, additional backups will only affect
objects that have been modified since the most recent backup, i.e.
objects that have been created, changed or deleted. The backup
@@ -584,7 +584,7 @@ ei_reg_dump(fd, reg, "mtab", dumpflags); ]]></code>
<p>In the same manner, a registry can be restored from a Mnesia table:</p>
<code type="none"><![CDATA[
ei_reg_restore(fd, reg, "mtab"); ]]></code>
- <p>This will read the entire contents of <c><![CDATA["mtab"]]></c> into the specified
+ <p>This will read the entire contents of <c>"mtab"</c> into the specified
registry. After the restore, all of the objects in the registry will
be marked as unmodified, so a subsequent backup will only affect
objects that you have modified since the restore.</p>
@@ -600,8 +600,8 @@ ei_reg_restore(fd, reg, "mtab"); ]]></code>
<p>When string or binary objects are stored in the registry it is
important that a number of simple guidelines are followed. </p>
<p>Most importantly, the object must have been created with a single call
- to <c><![CDATA[malloc()]]></c> (or similar), so that it can later be removed by a
- single call to <c><![CDATA[free()]]></c>. Objects will be freed by the registry
+ to <c>malloc()</c> (or similar), so that it can later be removed by a
+ single call to <c>free()</c>. Objects will be freed by the registry
when it is closed, or when you assign a new value to an object that
previously contained a string or binary.</p>
<p>You should also be aware that if you store binary objects that are
@@ -618,9 +618,8 @@ ei_reg_restore(fd, reg, "mtab"); ]]></code>
you make, possibly causing it to be missed the next time you make a
Mnesia backup of the registry contents. This can be avoided if you
mark the object as dirty after any such changes with
- <c><![CDATA[ei_reg_markdirty()]]></c>, or pass appropriate flags to
- <c><![CDATA[ei_reg_dump()]]></c>.</p>
+ <c>ei_reg_markdirty()</c>, or pass appropriate flags to
+ <c>ei_reg_dump()</c>.</p>
</section>
</section>
</chapter>
-
diff --git a/lib/erl_interface/doc/src/erl_malloc.xml b/lib/erl_interface/doc/src/erl_malloc.xml
index 799c903b1a..c0eebc29e9 100644
--- a/lib/erl_interface/doc/src/erl_malloc.xml
+++ b/lib/erl_interface/doc/src/erl_malloc.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>erl_malloc</title>
@@ -28,174 +28,177 @@
<docno></docno>
<approved>Bjarne D&auml;cker</approved>
<checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>980703</date>
+ <date>1998-07-03</date>
<rev>A</rev>
- <file>erl_malloc.sgml</file>
+ <file>erl_malloc.xml</file>
</header>
<lib>erl_malloc</lib>
- <libsummary>Memory Allocation Functions</libsummary>
+ <libsummary>Memory allocation functions.</libsummary>
<description>
<p>This module provides functions for allocating and deallocating
memory.</p>
</description>
+
<funcs>
<func>
<name><ret>ETERM *</ret><nametext>erl_alloc_eterm(etype)</nametext></name>
- <fsummary>Allocates an ETERM structure</fsummary>
+ <fsummary>Allocate an ETERM structure.</fsummary>
<type>
<v>unsigned char etype;</v>
</type>
<desc>
- <p>This function allocates an <c><![CDATA[(ETERM)]]></c> structure.
- Specify <c><![CDATA[etype]]></c> as one of the following constants:</p>
+ <p>Allocates an <c>(ETERM)</c> structure.</p>
+ <p>Specify <c>etype</c> as one of the following
+ constants:</p>
<list type="bulleted">
- <item>
- <p>ERL_INTEGER</p>
+ <item><c>ERL_INTEGER</c>
</item>
- <item>
- <p>ERL_U_INTEGER <c><![CDATA[/* unsigned integer */]]></c></p>
+ <item><c>ERL_U_INTEGER</c> (unsigned integer)
</item>
- <item>
- <p>ERL_ATOM</p>
+ <item><c>ERL_ATOM</c>
</item>
- <item>
- <p>ERL_PID <c><![CDATA[/* Erlang process identifier */]]></c></p>
+ <item><c>ERL_PID</c> (Erlang process identifier)
</item>
- <item>
- <p>ERL_PORT</p>
+ <item><c>ERL_PORT</c>
</item>
- <item>
- <p>ERL_REF <c><![CDATA[/* Erlang reference */]]></c></p>
+ <item><c>ERL_REF</c> (Erlang reference)
</item>
- <item>
- <p>ERL_LIST</p>
+ <item><c>ERL_LIST</c>
</item>
- <item>
- <p>ERL_EMPTY_LIST</p>
+ <item><c>ERL_EMPTY_LIST</c>
</item>
- <item>
- <p>ERL_TUPLE</p>
+ <item><c>ERL_TUPLE</c>
</item>
- <item>
- <p>ERL_BINARY</p>
+ <item><c>ERL_BINARY</c>
</item>
- <item>
- <p>ERL_FLOAT</p>
+ <item><c>ERL_FLOAT</c>
</item>
- <item>
- <p>ERL_VARIABLE</p>
+ <item><c>ERL_VARIABLE</c>
</item>
- <item>
- <p>ERL_SMALL_BIG <c><![CDATA[/* bignum */]]></c></p>
+ <item><c>ERL_SMALL_BIG</c> (bignum)
</item>
- <item>
- <p>ERL_U_SMALL_BIG <c><![CDATA[/* bignum */]]></c></p>
+ <item><c>ERL_U_SMALL_BIG</c> (bignum)
</item>
</list>
- <p><c><![CDATA[ERL_SMALL_BIG]]></c> and <c><![CDATA[ERL_U_SMALL_BIG]]></c> are for
- creating Erlang <c><![CDATA[bignums]]></c>, which can contain integers of
- arbitrary size. The size of an integer in Erlang is machine
- dependent, but in general any integer larger than 2^28
- requires a bignum.</p>
+ <p><c>ERL_SMALL_BIG</c> and
+ <c>ERL_U_SMALL_BIG</c> are for
+ creating Erlang <c>bignums</c>, which can contain integers
+ of any size. The size of an integer in Erlang is machine-dependent,
+ but any integer &gt; 2^28 requires a bignum.</p>
</desc>
</func>
+
<func>
<name><ret>void</ret><nametext>erl_eterm_release(void)</nametext></name>
- <fsummary>Clears the ETERM freelist</fsummary>
+ <fsummary>Clear the ETERM freelist.</fsummary>
<desc>
- <p>Clears the
- freelist, where blocks are placed when they are
- released by <c><![CDATA[erl_free_term()]]></c> and
- <c><![CDATA[erl_free_compound()]]></c>. </p>
+ <p>Clears the freelist, where blocks are placed when they are
+ released by <c>erl_free_term()</c> and
+ <c>erl_free_compound()</c>.</p>
</desc>
</func>
+
<func>
<name><ret>void</ret><nametext>erl_eterm_statistics(allocated, freed)</nametext></name>
- <fsummary>Reports term allocation statistics</fsummary>
+ <fsummary>Report term allocation statistics.</fsummary>
<type>
<v>long *allocated;</v>
<v>long *freed;</v>
</type>
<desc>
- <p><c><![CDATA[allocated]]></c> and <c><![CDATA[freed]]></c> are initialized to
+ <p>Reports term allocation statistics.</p>
+ <p><c>allocated</c> and <c>freed</c> are
+ initialized to
contain information about the fix-allocator used to allocate
- ETERM components. <c><![CDATA[allocated]]></c> is the number of blocks
- currently allocated to ETERM objects. <c><![CDATA[freed]]></c> is the
- length of the freelist, where blocks are placed when they are
- released by <c><![CDATA[erl_free_term()]]></c> and
- <c><![CDATA[erl_free_compound()]]></c>. </p>
+ <c>ETERM</c> components.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>allocated</c> is the number of blocks currently
+ allocated to <c>ETERM</c> objects.</p>
+ </item>
+ <item>
+ <p><c>freed</c> is the length of the freelist, where
+ blocks are placed when they are
+ released by <c>erl_free_term()</c> and
+ <c>erl_free_compound()</c>.</p>
+ </item>
+ </list>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>erl_free_array(array, size)</nametext></name>
- <fsummary>Frees an array of ETERM structures</fsummary>
+ <name><ret>void</ret><nametext>erl_free(ptr)</nametext></name>
+ <fsummary>Free some memory.</fsummary>
<type>
- <v>ETERM **array;</v>
- <v>int size;</v>
+ <v>void *ptr;</v>
</type>
<desc>
- <p>This function frees an array of Erlang terms.</p>
- <p><c><![CDATA[array]]></c> is an array of ETERM* objects.
- </p>
- <p><c><![CDATA[size]]></c> is the number of terms in the array.</p>
+ <p>Calls the standard
+ <c>free()</c> function.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>erl_free_term(t)</nametext></name>
- <fsummary>Frees an ETERM structure</fsummary>
+ <name><ret>void</ret><nametext>erl_free_array(array, size)</nametext></name>
+ <fsummary>Free an array of ETERM structures.</fsummary>
<type>
- <v>ETERM *t;</v>
+ <v>ETERM **array;</v>
+ <v>int size;</v>
</type>
<desc>
- <p>Use this function to free an Erlang term.</p>
+ <p>Frees an array of Erlang terms.</p>
+ <list type="bulleted">
+ <item><c>array</c> is an array of ETERM* objects.</item>
+ <item><c>size</c> is the number of terms in the array.
+ </item>
+ </list>
</desc>
</func>
+
<func>
<name><ret>void</ret><nametext>erl_free_compound(t)</nametext></name>
- <fsummary>Frees an array of ETERM structures</fsummary>
+ <fsummary>Free an array of ETERM structures.</fsummary>
<type>
<v>ETERM *t;</v>
</type>
<desc>
<p>Normally it is the programmer's responsibility to free each
Erlang term that has been returned from any of the
- <c><![CDATA[erl_interface]]></c> functions. However since many of the
+ <c>Erl_Interface</c> functions. However, as many of the
functions that build new Erlang terms in fact share objects
- with other existing terms, it may be difficult for the
- programmer to maintain pointers to all such terms in order to
- free them individually.
- </p>
- <p><c><![CDATA[erl_free_compound()]]></c> will recursively free all of the
- sub-terms associated with a given Erlang term, regardless of
- whether we are still holding pointers to the sub-terms.
- </p>
- <p>There is an example in the User Manual under "Building
- Terms and Patterns"
- </p>
+ with other existing terms, it can be difficult for the
+ programmer to maintain pointers to all such terms to
+ free them individually.</p>
+ <p><c>erl_free_compound()</c> recursively frees all of the
+ subterms associated with a specified Erlang term, regardless of
+ whether we are still holding pointers to the subterms.</p>
+ <p>For an example, see section
+ <seealso marker="ei_users_guide#building_terms_and_patterns">Building Terms and Patterns</seealso>
+ in the User's Guide.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>erl_malloc(size)</nametext></name>
- <fsummary>Allocates some memory</fsummary>
+ <name><ret>void</ret><nametext>erl_free_term(t)</nametext></name>
+ <fsummary>Free an ETERM structure.</fsummary>
<type>
- <v>long size;</v>
+ <v>ETERM *t;</v>
</type>
<desc>
- <p>This function calls the standard
- <c><![CDATA[malloc()]]></c> function. </p>
+ <p>Frees an Erlang term.</p>
</desc>
</func>
+
<func>
- <name><ret>void</ret><nametext>erl_free(ptr)</nametext></name>
- <fsummary>Frees some memory</fsummary>
+ <name><ret>void</ret><nametext>erl_malloc(size)</nametext></name>
+ <fsummary>Allocate some memory.</fsummary>
<type>
- <v>void *ptr;</v>
+ <v>long size;</v>
</type>
<desc>
- <p>This function calls the standard
- <c><![CDATA[free()]]></c> function. </p>
+ <p>Calls the standard
+ <c>malloc()</c> function.</p>
</desc>
</func>
</funcs>
</cref>
-
diff --git a/lib/erl_interface/doc/src/erl_marshal.xml b/lib/erl_interface/doc/src/erl_marshal.xml
index 7c56089016..2ad658f78b 100644
--- a/lib/erl_interface/doc/src/erl_marshal.xml
+++ b/lib/erl_interface/doc/src/erl_marshal.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>erl_marshal</title>
@@ -28,246 +28,241 @@
<docno></docno>
<approved>Bjarne D&auml;cker</approved>
<checked>Torbj&ouml;rn T&ouml;rnkvist</checked>
- <date>980703</date>
+ <date>1998-07-03</date>
<rev>A</rev>
- <file>erl_marshal.sgml</file>
+ <file>erl_marshal.xml</file>
</header>
<lib>erl_marshal</lib>
- <libsummary>Encoding and Decoding of Erlang terms</libsummary>
+ <libsummary>Encoding and decoding of Erlang terms.</libsummary>
<description>
<p>This module contains functions for encoding Erlang terms into
a sequence of bytes, and for decoding Erlang terms from a
sequence of bytes.</p>
</description>
+
<funcs>
<func>
<name><ret>int</ret><nametext>erl_compare_ext(bufp1, bufp2)</nametext></name>
- <fsummary>Compares encoded byte sequences</fsummary>
+ <fsummary>Compare encoded byte sequences.</fsummary>
<type>
<v>unsigned char *bufp1,*bufp2;</v>
</type>
<desc>
- <p>This function compares two encoded terms.
- </p>
- <p><c><![CDATA[bufp1]]></c> is a buffer containing an encoded Erlang
- term term1.
- </p>
- <p><c><![CDATA[bufp2]]></c> is a buffer containing an encoded Erlang
- term term2.
- </p>
- <p>The function returns 0 if the terms are equal, -1 if term1
- is less than term2, or 1 if term2 is less than term1.
- </p>
+ <p>Compares two encoded terms.</p>
+ <list type="bulleted">
+ <item><c>bufp1</c> is a buffer containing an encoded
+ Erlang term term1.</item>
+ <item><c>bufp2</c> is a buffer containing an encoded
+ Erlang term term2.</item>
+ </list>
+ <p>Returns <c>0</c> if the terms are equal, <c>-1</c> if
+ <c>term1</c> &lt; <c>term2</c>, or <c>1</c> if <c>term2</c> &lt;
+ <c>term1</c>.</p>
</desc>
</func>
+
<func>
<name><ret>ETERM *</ret><nametext>erl_decode(bufp)</nametext></name>
<name><ret>ETERM *</ret><nametext>erl_decode_buf(bufpp)</nametext></name>
- <fsummary>Converts a term from Erlang external format</fsummary>
+ <fsummary>Convert a term from Erlang external format.</fsummary>
<type>
<v>unsigned char *bufp;</v>
<v>unsigned char **bufpp;</v>
</type>
<desc>
- <p><c><![CDATA[erl_decode()]]></c> and <c><![CDATA[erl_decode_buf()]]></c> decode
+ <p><c>erl_decode()</c> and
+ <c>erl_decode_buf()</c> decode
the contents of a buffer and return the corresponding
- Erlang term. <c><![CDATA[erl_decode_buf()]]></c> provides a simple
+ Erlang term. <c>erl_decode_buf()</c> provides a simple
mechanism for dealing with several encoded terms stored
consecutively in the buffer.</p>
- <p><c><![CDATA[bufp]]></c> is a pointer to a buffer containing one or
- more encoded Erlang terms.
- </p>
- <p><c><![CDATA[bufpp]]></c> is the address of a buffer pointer. The buffer
- contains one or more consecutively encoded Erlang terms.
- Following a successful call to <c><![CDATA[erl_decode_buf()]]></c>,
- <c><![CDATA[bufpp]]></c> will be updated so that it points to the next
- encoded term.
- </p>
- <p><c><![CDATA[erl_decode()]]></c> returns an Erlang term
- corresponding to the contents of <c><![CDATA[bufp]]></c> on success, or
- NULL on failure. <c><![CDATA[erl_decode_buf()]]></c> returns an Erlang
+ <list type="bulleted">
+ <item>
+ <p><c>bufp</c> is a pointer to a buffer containing one
+ or more encoded Erlang terms.</p>
+ </item>
+ <item>
+ <p><c>bufpp</c> is the address of a buffer pointer. The
+ buffer contains one or more consecutively encoded Erlang terms.
+ Following a successful call to
+ <c>erl_decode_buf()</c>, <c>bufpp</c> is
+ updated so that it points to the next encoded term.</p>
+ </item>
+ </list>
+ <p><c>erl_decode()</c> returns an Erlang term
+ corresponding to the contents of <c>bufp</c> on success,
+ otherwise <c>NULL</c>. <c>erl_decode_buf()</c>
+ returns an Erlang
term corresponding to the first of the consecutive terms in
- <c><![CDATA[bufpp]]></c> and moves <c><![CDATA[bufpp]]></c> forward to point to the
+ <c>bufpp</c> and moves <c>bufpp</c> forward
+ to point to the
next term in the buffer. On failure, each of the functions
- returns NULL.
- </p>
+ return <c>NULL</c>.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_encode(term, bufp)</nametext></name>
<name><ret>int</ret><nametext>erl_encode_buf(term, bufpp)</nametext></name>
- <fsummary>Converts a term into Erlang external format</fsummary>
+ <fsummary>Convert a term into Erlang external format.</fsummary>
<type>
<v>ETERM *term;</v>
<v>unsigned char *bufp;</v>
<v>unsigned char **bufpp;</v>
</type>
<desc>
- <p><c><![CDATA[erl_encode()]]></c> and <c><![CDATA[erl_encode_buf()]]></c> encode
+ <p><c>erl_encode()</c> and
+ <c>erl_encode_buf()</c> encode
Erlang terms into external format for storage or transmission.
- <c><![CDATA[erl_encode_buf()]]></c> provides a simple mechanism for
- encoding several terms consecutively in the same
- buffer.
- </p>
- <p><c>term</c> is an Erlang term to be encoded.
- </p>
- <p><c>bufp</c> is a pointer to a buffer containing one or
- more encoded Erlang terms.
- </p>
- <p><c>bufpp</c> is a pointer to a pointer to a buffer
- containing one or more consecutively encoded Erlang terms.
- Following a successful call to <c><![CDATA[erl_encode_buf()]]></c>,
- <c>bufpp</c> will be updated so that it points to the
- position for the next encoded term.
- </p>
- <p>
- These functions returns the number of bytes written to buffer
- if successful, otherwise returns 0.
- </p>
- <p>Note that no bounds checking is done on the buffer. It is
- the caller's responsibility to make sure that the buffer is
+ <c>erl_encode_buf()</c> provides a simple mechanism for
+ encoding several terms consecutively in the same buffer.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>term</c> is an Erlang term to be encoded.</p>
+ </item>
+ <item>
+ <p><c>bufp</c> is a pointer to a buffer containing one or
+ more encoded Erlang terms.</p>
+ </item>
+ <item>
+ <p><c>bufpp</c> is a pointer to a pointer to a buffer
+ containing one or more consecutively encoded Erlang terms.
+ Following a successful call to
+ <c>erl_encode_buf()</c>, <c>bufpp</c> is updated so
+ that it points to the
+ position for the next encoded term.</p>
+ </item>
+ </list>
+ <p>These functions return the number of bytes written to buffer
+ on success, otherwise <c>0</c>.</p>
+ <p>Notice that no bounds checking is done on the buffer. It is
+ the caller's responsibility to ensure that the buffer is
large enough to hold the encoded terms. You can either use a
- static buffer that is large enough to hold the terms you
- expect to need in your program, or use <c><![CDATA[erl_term_len()]]></c>
- to determine the exact requirements for a given term.
- </p>
+ static buffer that is large enough to hold the terms you expect
+ to need in your program, or use <c>erl_term_len()</c>
+ to determine the exact requirements for a given term.</p>
<p>The following can help you estimate the buffer
- requirements for a term. Note that this information is
- implementation specific, and may change in future versions.
- If you are unsure, use <c><![CDATA[erl_term_len()]]></c>.
- </p>
+ requirements for a term. Notice that this information is
+ implementation-specific, and can change in future versions.
+ If you are unsure, use <c>erl_term_len()</c>.</p>
<p>Erlang terms are encoded with a 1 byte tag that
identifies the type of object, a 2- or 4-byte length field,
- and then the data itself. Specifically:
- </p>
+ and then the data itself. Specifically:</p>
<taglist>
- <tag><c><![CDATA[Tuples]]></c></tag>
- <item>need 5 bytes, plus the space for each element.</item>
- <tag><c><![CDATA[Lists]]></c></tag>
- <item>need 5 bytes, plus the space for each element, and 1
- additional byte for the empty list at the end.</item>
- <tag><c><![CDATA[Strings and atoms]]></c></tag>
- <item>need 3 bytes, plus 1 byte for each character (the
- terminating 0 is not encoded). Really long strings (more
- than 64k characters) are encoded as lists. Atoms cannot
- contain more than 256 characters.</item>
- <tag><c><![CDATA[Integers]]></c></tag>
- <item>need 5 bytes.</item>
- <tag><c><![CDATA[Characters]]></c></tag>
- <item>(integers &lt; 256) need 2 bytes.</item>
- <tag><c><![CDATA[Floating point numbers]]></c></tag>
- <item>need 32 bytes.</item>
- <tag><c><![CDATA[Pids]]></c></tag>
- <item>need 10 bytes, plus the space for the node name, which
- is an atom.</item>
- <tag><c><![CDATA[Ports and Refs]]></c></tag>
- <item>need 6 bytes, plus the space for the node name, which
- is an atom.</item>
+ <tag><c>Tuples</c></tag>
+ <item>Need 5 bytes, plus the space for each element.</item>
+ <tag><c>Lists</c></tag>
+ <item>Need 5 bytes, plus the space for each element, and 1
+ more byte for the empty list at the end.</item>
+ <tag><c>Strings and atoms</c></tag>
+ <item>Need 3 bytes, plus 1 byte for each character (the
+ terminating 0 is not encoded). Really long strings (more
+ than 64k characters) are encoded as lists. Atoms cannot
+ contain more than 256 characters.</item>
+ <tag><c>Integers</c></tag>
+ <item>Need 5 bytes.</item>
+ <tag><c>Characters</c></tag>
+ <item>(Integers &lt; 256) need 2 bytes.</item>
+ <tag><c>Floating point numbers</c></tag>
+ <item>Need 32 bytes.</item>
+ <tag><c>Pids</c></tag>
+ <item>Need 10 bytes, plus the space for the node name, which
+ is an atom.</item>
+ <tag><c>Ports and Refs</c></tag>
+ <item>Need 6 bytes, plus the space for the node name, which
+ is an atom.</item>
</taglist>
- <p>The total space required will be the result calculated
- from the information above, plus 1 additional byte for a
- version identifier.
- </p>
+ <p>The total space required is the result calculated
+ from the information above, plus 1 more byte for a
+ version identifier.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_ext_size(bufp)</nametext></name>
- <fsummary>Counts elements in encoded term</fsummary>
+ <fsummary>Count elements in encoded term.</fsummary>
<type>
<v>unsigned char *bufp;</v>
</type>
<desc>
- <p>This function returns the number of elements in an
- encoded term.</p>
+ <p>Returns the number of elements in an encoded term.</p>
</desc>
</func>
+
<func>
<name><ret>unsigned char</ret><nametext>erl_ext_type(bufp)</nametext></name>
- <fsummary>Determines type of an encoded byte sequence</fsummary>
+ <fsummary>Determine type of an encoded byte sequence.</fsummary>
<type>
<v>unsigned char *bufp;</v>
</type>
<desc>
- <p>This function identifies and returns the type of Erlang term encoded
- in a buffer. It will skip a trailing <em>magic</em> identifier.
- Returns <c><![CDATA[0]]></c> if the type can't be determined or one of</p>
+ <p>Identifies and returns the type of Erlang term encoded
+ in a buffer. It skips a trailing <em>magic</em> identifier.</p>
+ <p>Returns <c>0</c> if the type cannot be determined or
+ one of:</p>
<list type="bulleted">
- <item>
- <p>ERL_INTEGER</p>
+ <item><c>ERL_INTEGER</c>
</item>
- <item>
- <p>ERL_ATOM</p>
+ <item><c>ERL_ATOM</c>
</item>
- <item>
- <p>ERL_PID <c><![CDATA[/* Erlang process identifier */]]></c></p>
+ <item><c>ERL_PID</c> (Erlang process identifier)
</item>
- <item>
- <p>ERL_PORT</p>
+ <item><c>ERL_PORT</c>
</item>
- <item>
- <p>ERL_REF <c><![CDATA[/* Erlang reference */]]></c></p>
+ <item><c>ERL_REF</c> (Erlang reference)
</item>
- <item>
- <p>ERL_EMPTY_LIST</p>
+ <item><c>ERL_EMPTY_LIST</c>
</item>
- <item>
- <p>ERL_LIST</p>
+ <item><c>ERL_LIST</c>
</item>
- <item>
- <p>ERL_TUPLE</p>
+ <item><c>ERL_TUPLE</c>
</item>
- <item>
- <p>ERL_FLOAT</p>
+ <item><c>ERL_FLOAT</c>
</item>
- <item>
- <p>ERL_BINARY</p>
+ <item><c>ERL_BINARY</c>
</item>
- <item>
- <p>ERL_FUNCTION</p>
+ <item><c>ERL_FUNCTION</c>
</item>
</list>
</desc>
</func>
+
<func>
<name><ret>unsigned char *</ret><nametext>erl_peek_ext(bufp, pos)</nametext></name>
- <fsummary>Steps over encoded term</fsummary>
+ <fsummary>Step over encoded term.</fsummary>
<type>
<v>unsigned char *bufp;</v>
<v>int pos;</v>
</type>
<desc>
<p>This function is used for stepping over one or more
- encoded terms in a buffer, in order to directly access a
- later term.
- </p>
- <p><c><![CDATA[bufp]]></c> is a pointer to a buffer containing one or
- more encoded Erlang terms.
- </p>
- <p><c><![CDATA[pos]]></c> indicates how many terms to step over in the
- buffer.
- </p>
- <p>The function returns a pointer to a sub-term that can be
- used in a subsequent call to <c><![CDATA[erl_decode()]]></c> in order to retrieve
- the term at that position. If there is no term, or <c><![CDATA[pos]]></c>
- would exceed the size of the terms in the buffer, NULL is returned.
- </p>
+ encoded terms in a buffer, to directly access later term.</p>
+ <list type="bulleted">
+ <item><c>bufp</c> is a pointer to a buffer containing one
+ or more encoded Erlang terms.</item>
+ <item><c>pos</c> indicates how many terms to step over in
+ the buffer.</item>
+ </list>
+ <p>Returns a pointer to a subterm that can be
+ used in a later call to <c>erl_decode()</c> to retrieve
+ the term at that position. If there is no term, or
+ <c>pos</c> would exceed the size of the terms in the
+ buffer, <c>NULL</c> is returned.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>erl_term_len(t)</nametext></name>
- <fsummary>Determines encoded size of term</fsummary>
+ <fsummary>Determine encoded size of term.</fsummary>
<type>
<v>ETERM *t;</v>
</type>
<desc>
- <p>This function determines the buffer space that would be
- needed by <c><![CDATA[t]]></c> if it were encoded into Erlang external
- format by <c><![CDATA[erl_encode()]]></c>.
- </p>
- <p>The size in bytes is returned.
- </p>
+ <p>Determines the buffer space that would be
+ needed by <c>t</c> if it were encoded into Erlang external
+ format by <c>erl_encode()</c>.</p>
+ <p>Returns the size in bytes.</p>
</desc>
</func>
</funcs>
</cref>
-
diff --git a/lib/erl_interface/doc/src/fascicules.xml b/lib/erl_interface/doc/src/fascicules.xml
deleted file mode 100644
index f7edd8a973..0000000000
--- a/lib/erl_interface/doc/src/fascicules.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part_ei" href="part_ei_frame.html" entry="no">
- EI User's Guide
- </fascicule>
- <fascicule file="ref_man_ei" href="ref_man_ei_frame.html" entry="yes">
- EI Library Reference
- </fascicule>
- <fascicule file="ref_man_erl_interface" href="ref_man_erl_interface_frame.html" entry="no">
- Erl_interface Library Reference
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="no">
- Command Reference
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index a69c5aac11..b5d8def655 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -31,6 +31,67 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.9.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Minor documentation update</p>
+ <p>
+ Own Id: OTP-14233 Aux Id: PR-1343 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Interface 3.9.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix <c>ei_connect_init</c> and <c>ei_connect_xinit</c> to
+ adjust the <c>creation</c> argument to be compatible with
+ nodes older than OTP-19.</p>
+ <p>
+ Own Id: OTP-13981</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Editorial documentation changes</p>
+ <p>
+ Own Id: OTP-13980</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Interface 3.9.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Look for .erlang.cookie in windows system directory if
+ HOMEDRIVE and HOMEPATH is not set. The same behaviour as
+ the VM.</p>
+ <p>
+ Own Id: OTP-13849</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.9</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/erl_interface/doc/src/part.xml b/lib/erl_interface/doc/src/part.xml
index d044e2b981..3b761df221 100644
--- a/lib/erl_interface/doc/src/part.xml
+++ b/lib/erl_interface/doc/src/part.xml
@@ -22,7 +22,7 @@
</legalnotice>
- <title>EI User's Guide</title>
+ <title>Erl_Interface User's Guide</title>
<prepared>Gordon Beaton</prepared>
<docno></docno>
<date>1998-11-30</date>
diff --git a/lib/erl_interface/doc/src/part_erl_interface.xml b/lib/erl_interface/doc/src/part_erl_interface.xml
index 2abe7ecd60..e256cfa193 100644
--- a/lib/erl_interface/doc/src/part_erl_interface.xml
+++ b/lib/erl_interface/doc/src/part_erl_interface.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>Erl_Interface User's Guide</title>
@@ -31,4 +31,3 @@
</header>
<xi:include href="erl_interface.xml"/>
</part>
-
diff --git a/lib/erl_interface/doc/src/ref_man.xml b/lib/erl_interface/doc/src/ref_man.xml
index 0cf060829b..1e20637cb7 100644
--- a/lib/erl_interface/doc/src/ref_man.xml
+++ b/lib/erl_interface/doc/src/ref_man.xml
@@ -11,7 +11,7 @@
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
@@ -19,9 +19,9 @@
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>Erl_Interface Command Reference</title>
+ <title>Erl_Interface Reference Manual</title>
<prepared>Gordon Beaton</prepared>
<docno></docno>
<date>1998-11.30</date>
@@ -29,17 +29,6 @@
<file>ref_man.xml</file>
</header>
<description>
- <p>The <c>ei</c> and <c>erl_interface</c> are <c>C</c> interface libraries for
- communication with <c>Erlang</c>.</p>
- <note>
- <p>By default, the <c>ei</c> and <c>erl_interface</c> libraries are only guaranteed
- to be compatible with other Erlang/OTP components from the same
- release as the libraries themself. See the documentation of the
- <seealso marker="ei#ei_set_compat_rel">ei_set_compat_rel()</seealso> and
- <seealso marker="erl_eterm#erl_set_compat_rel">erl_set_compat_rel()</seealso>
- functions on how to communicate with Erlang/OTP components from earlier
- releases.</p>
- </note>
</description>
<xi:include href="ei.xml"/>
<xi:include href="ei_connect.xml"/>
@@ -53,4 +42,3 @@
<xi:include href="erl_marshal.xml"/>
<xi:include href="erl_call.xml"/>
</application>
-
diff --git a/lib/erl_interface/doc/src/ref_man_ei.xml b/lib/erl_interface/doc/src/ref_man_ei.xml
index d24828c394..92ff9ed328 100644
--- a/lib/erl_interface/doc/src/ref_man_ei.xml
+++ b/lib/erl_interface/doc/src/ref_man_ei.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>EI Library Reference</title>
@@ -30,12 +30,12 @@
<file>ref_man_ei.xml</file>
</header>
<description>
- <p>The <c><![CDATA[ei]]></c> library is a <c><![CDATA[C]]></c> interface library for
- communication with <c><![CDATA[Erlang]]></c>.</p>
+ <p>The <c>ei</c> library is a <c>C</c> interface library for
+ communication with <c>Erlang</c>.</p>
<note>
- <p>By default, the <c><![CDATA[ei]]></c> library is only guaranteed
+ <p>By default, the <c>ei</c> library is only guaranteed
to be compatible with other Erlang/OTP components from the same
- release as the <c><![CDATA[ei]]></c> library itself. See the documentation of the
+ release as the <c>ei</c> library itself. See the documentation of the
<seealso marker="ei#ei_set_compat_rel">ei_set_compat_rel()</seealso>
function on how to communicate with Erlang/OTP components from earlier
releases.</p>
@@ -45,4 +45,3 @@
<xi:include href="ei_connect.xml"/>
<xi:include href="registry.xml"/>
</application>
-
diff --git a/lib/erl_interface/doc/src/ref_man_erl_interface.xml b/lib/erl_interface/doc/src/ref_man_erl_interface.xml
index fb39c5a7e4..4b1d0e9981 100644
--- a/lib/erl_interface/doc/src/ref_man_erl_interface.xml
+++ b/lib/erl_interface/doc/src/ref_man_erl_interface.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>Erl_Interface Library Reference</title>
@@ -50,4 +50,3 @@
<xi:include href="erl_malloc.xml"/>
<xi:include href="erl_marshal.xml"/>
</application>
-
diff --git a/lib/erl_interface/doc/src/registry.xml b/lib/erl_interface/doc/src/registry.xml
index 285a2402b8..6d70fb3475 100644
--- a/lib/erl_interface/doc/src/registry.xml
+++ b/lib/erl_interface/doc/src/registry.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>registry</title>
@@ -28,457 +28,585 @@
<docno></docno>
<approved>Gordon Beaton</approved>
<checked>Gordon Beaton</checked>
- <date>980707</date>
+ <date>1998-07-07</date>
<rev>A</rev>
- <file>registry.sgml</file>
+ <file>registry.xml</file>
</header>
<lib>registry</lib>
- <libsummary>Store and backup key-value pairs</libsummary>
+ <libsummary>Store and back up key-value pairs.</libsummary>
<description>
<p>This module provides support for storing key-value
pairs in a table known as a registry, backing up registries to
- Mnesia in an atomic manner, and later restoring the contents of a
- registry from Mnesia.</p>
+ <seealso marker="mnesia:mnesia">Mnesia</seealso>
+ in an atomic manner, and later restoring the contents of a
+ registry from <c>Mnesia</c>.</p>
</description>
+
<funcs>
<func>
- <name><ret>ei_reg *</ret><nametext>ei_reg_open(size)</nametext></name>
- <fsummary>Create and open a registry</fsummary>
+ <name><ret>int</ret><nametext>ei_reg_close(reg)</nametext></name>
+ <fsummary>Close a registry.</fsummary>
<type>
- <v>int size;</v>
+ <v>ei_reg *reg;</v>
</type>
<desc>
- <p>Open (create) a registry. The registry will be
- initially empty. Use <c><![CDATA[ei_reg_close()]]></c> to close the registry
- later.
- </p>
- <p><c><![CDATA[size]]></c> is the approximate number of objects you intend
- to store in the registry. Since the registry uses a hash table
- with collision chaining, there is no absolute upper limit on the
- number of objects that can be stored in it. However for reasons
- of efficiency, it is a good idea to choose a number that is
- appropriate for your needs. It is possible to use
- <c><![CDATA[ei_reg_resize()]]></c> to change the size later. Note that the
- number you provide will be increased to the nearest larger prime
- number.
- </p>
- <p>On success, an empty registry will be returned. On failure, NULL
- will be returned.</p>
+ <p>A registry that has previously been created with
+ <c>ei_reg_open()</c> is closed, and all the objects it
+ contains are freed.</p>
+ <p><c>reg</c> is the registry to close.</p>
+ <p>Returns <c>0</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_resize(reg,newsize)</nametext></name>
- <fsummary>Resize a registry</fsummary>
+ <name><ret>int</ret><nametext>ei_reg_delete(reg,key)</nametext></name>
+ <fsummary>Delete an object from the registry.</fsummary>
<type>
<v>ei_reg *reg;</v>
- <v>int newsize;</v>
+ <v>const char *key;</v>
</type>
<desc>
- <p>Change the size of a registry.
- </p>
- <p><c><![CDATA[newsize]]></c> is the new size to make the registry. The
- number will be increased to the nearest larger prime number.
- </p>
- <p>On success, the registry will be resized, all contents
- rehashed, and the function will return 0. On failure, the
- registry will be left unchanged and the function will return -1.</p>
+ <p>Deletes an object from the registry. The object is not
+ removed from the registry, it is only marked for later
+ removal so that on later backups to <c>Mnesia</c>, the
+ corresponding object can be removed from the <c>Mnesia</c> table as
+ well. If another object is later created with the same key, the
+ object will be reused. </p>
+ <p>The object is removed from the registry after a call to
+ <c>ei_reg_dump()</c> or <c>ei_reg_purge()</c>.
+ </p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry containing
+ <c>key</c>.</item>
+ <item><c>key</c> is the object to remove.</item>
+ </list>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_close(reg)</nametext></name>
- <fsummary>Close a registry </fsummary>
+ <name><ret>int</ret><nametext>ei_reg_dump(fd,reg,mntab,flags)</nametext></name>
+ <fsummary>Back up a registry to Mnesia.</fsummary>
<type>
+ <v>int fd;</v>
<v>ei_reg *reg;</v>
+ <v>const char *mntab;</v>
+ <v>int flags;</v>
</type>
<desc>
- <p>A registry that has previously been created with
- <c><![CDATA[ei_reg_open()]]></c> is closed, and all the objects it contains
- are freed.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry to close.
- </p>
- <p>The function returns 0.</p>
+ <p>Dumps the contents of a registry to a <c>Mnesia</c> table in an
+ atomic manner, that is, either all data or no data is updated.
+ If any errors are encountered while backing up
+ the data, the entire operation is aborted.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open connection to Erlang.
+ <c>Mnesia</c> 3.0 or later must be running on the Erlang node.
+ </item>
+ <item><c>reg</c> is the registry to back up.</item>
+ <item><c>mntab</c> is the name of the <c>Mnesia</c> table
+ where the backed up data is to be placed. If the table does not
+ exist, it is created automatically using configurable defaults.
+ For information about configuring this behavior, see
+ <seealso marker="mnesia:mnesia"><c>Mnesia</c></seealso>.</item>
+ </list>
+ <p>If <c>flags</c> is <c>0</c>, the backup includes only
+ those objects that have been created, modified, or deleted since the
+ last backup or restore (that is, an incremental backup). After the
+ backup, any objects that were marked dirty are now clean, and any
+ objects that had been marked for deletion are deleted.</p>
+ <p>Alternatively, setting flags to <c>EI_FORCE</c> causes a full
+ backup to be done, and <c>EI_NOPURGE</c> causes the deleted objects
+ to be left in the registry afterwards. These can be bitwise OR'ed
+ together if both behaviors are desired. If <c>EI_NOPURGE</c> was
+ specified, <c>ei_reg_purge()</c> can be used to
+ explicitly remove the deleted items from the registry later.</p>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_setival(reg,key,i)</nametext></name>
- <fsummary>Assign an integer object</fsummary>
+ <name><ret>double</ret><nametext>ei_reg_getfval(reg,key)</nametext></name>
+ <fsummary>Get a floating point object.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
- <v>int i;</v>
</type>
<desc>
- <p>Create a key-value pair with the specified <c><![CDATA[key]]></c> and integer
- value <c><![CDATA[i]]></c>. If an object already existed with the same
- <c><![CDATA[key]]></c>, the new value replaces the old one. If the previous
- value was a binary or string, it is freed with <c><![CDATA[free()]]></c>.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the object should be placed.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object.
- </p>
- <p><c><![CDATA[i]]></c> is the integer value to assign.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
+ <p>Gets the value associated with <c>key</c> in the
+ registry. The value must be a floating point type.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry where the object will be
+ looked up.</item>
+ <item><c>key</c> is the name of the object to look up.
+ </item>
+ </list>
+ <p>On success, the function returns the value associated with
+ <c>key</c>.
+ If the object is not found or if it is not a floating point
+ object, <c>-1.0</c> is returned. To avoid problems with in-band error
+ reporting (that is, if you cannot distinguish between <c>-1.0</c> and
+ a valid result), use the more general function
+ <c>ei_reg_getval()</c> instead.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_setfval(reg,key,f)</nametext></name>
- <fsummary>Assign a floating point object</fsummary>
+ <name><ret>int</ret><nametext>ei_reg_getival(reg,key)</nametext></name>
+ <fsummary>Get an integer object.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
- <v>double f;</v>
</type>
<desc>
- <p>Create a key-value pair with the specified <c><![CDATA[key]]></c> and
- floating point value <c><![CDATA[f]]></c>. If an object already existed with
- the same <c><![CDATA[key]]></c>, the new value replaces the old one. If the
- previous value was a binary or string, it is freed with <c><![CDATA[free()]]></c>.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the object should be placed.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object.
- </p>
- <p><c><![CDATA[f]]></c> is the floating point value to assign.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
+ <p>Gets the value associated with <c>key</c> in the
+ registry. The value must be an integer.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry where the object will be
+ looked up.</item>
+ <item><c>key</c> is the name of the object to look up.
+ </item>
+ </list>
+ <p>On success, the function returns the value associated with
+ <c>key</c>.
+ If the object is not found or if it is not an integer
+ object, <c>-1</c> is returned. To avoid problems with in-band error
+ reporting (that is, if you cannot distinguish between <c>-1</c> and a
+ valid result), use the more general function
+ <c>ei_reg_getval()</c> instead.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_setsval(reg,key,s)</nametext></name>
- <fsummary>Assign a string object</fsummary>
+ <name><ret>const void *</ret><nametext>ei_reg_getpval(reg,key,size)</nametext></name>
+ <fsummary>Get a binary object.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
- <v>const char *s;</v>
+ <v>int size;</v>
</type>
<desc>
- <p>Create a key-value pair with the specified <c><![CDATA[key]]></c> whose
- "value" is the specified string <c><![CDATA[s]]></c>. If an object already
- existed with the same <c><![CDATA[key]]></c>, the new value replaces the old
- one. If the previous value was a binary or string, it is freed
- with <c><![CDATA[free()]]></c>.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the object should be placed.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object.
- </p>
- <p><c><![CDATA[s]]></c> is the string to assign. The string itself
- must have been created through a single call to <c><![CDATA[malloc()]]></c> or
- similar function, so that the registry can later delete it if
- necessary by calling <c><![CDATA[free()]]></c>.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
+ <p>Gets the value associated with <c>key</c> in the
+ registry. The value must be a binary (pointer) type.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry where the object will be
+ looked up.</item>
+ <item><c>key</c> is the name of the object to look up.
+ </item>
+ <item><c>size</c> is initialized to contain the length in
+ bytes of the object, if it is found.</item>
+ </list>
+ <p>On success, the function returns the value associated with
+ <c>key</c> and indicates its length in
+ <c>size</c>.
+ If the object is not found or if it is not a binary object,
+ <c>NULL</c> is returned. To avoid problems with in-band error
+ reporting (that is, if you cannot distinguish between <c>NULL</c> and
+ a valid result), use the more general function
+ <c>ei_reg_getval()</c> instead.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_setpval(reg,key,p,size)</nametext></name>
- <fsummary>Assign a binary object</fsummary>
+ <name><ret>const char *</ret><nametext>ei_reg_getsval(reg,key)</nametext></name>
+ <fsummary>Get a string object.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
- <v>const void *p;</v>
- <v>int size;</v>
</type>
<desc>
- <p>Create a key-value pair with the specified <c><![CDATA[key]]></c> whose
- "value" is the binary object pointed to by <c><![CDATA[p]]></c>. If an
- object already existed with the same <c><![CDATA[key]]></c>, the new value
- replaces the old one. If the previous value was a binary or
- string, it is freed with <c><![CDATA[free()]]></c>.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the object should be placed.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object.
- </p>
- <p><c><![CDATA[p]]></c> is a pointer to the binary object. The object itself
- must have been created through a single call to <c><![CDATA[malloc()]]></c> or
- similar function, so that the registry can later delete it if
- necessary by calling <c><![CDATA[free()]]></c>.
- </p>
- <p><c><![CDATA[size]]></c> is the length in bytes of the binary object.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
+ <p>Gets the value associated with <c>key</c> in the
+ registry. The value must be a string.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry where the object will be
+ looked up.</item>
+ <item><c>key</c> is the name of the object to look up.
+ </item>
+ </list>
+ <p>On success, the function returns the value associated with
+ <c>key</c>. If the object is not found or if it is not a
+ string, <c>NULL</c> is returned. To avoid problems with in-band error
+ reporting (that is, if you cannot distinguish between <c>NULL</c> and
+ a valid result), use the more general function
+ <c>ei_reg_getval()</c> instead.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_setval(reg,key,flags,v,...)</nametext></name>
- <fsummary>Assign a value to any object type</fsummary>
+ <name><ret>int</ret><nametext>ei_reg_getval(reg,key,flags,v,...)</nametext></name>
+ <fsummary>Get any object.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
<v>int flags;</v>
- <v>v (see below)</v>
+ <v>void *v (see below)</v>
</type>
<desc>
- <p>Create a key-value pair with the specified <c><![CDATA[key]]></c> whose
- value is specified by <c><![CDATA[v]]></c>. If an object already
- existed with the same <c><![CDATA[key]]></c>, the new value replaces the old
- one. If the previous value was a binary or string, it is freed
- with <c><![CDATA[free()]]></c>.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the object should be placed.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object.
- </p>
- <p><c><![CDATA[flags]]></c> indicates the type of the object specified by
- <c><![CDATA[v]]></c>. Flags must be one of EI_INT, EI_FLT, EI_STR and
- EI_BIN, indicating whether <c><![CDATA[v]]></c> is <c><![CDATA[int]]></c>, <c><![CDATA[double]]></c>,
- <c><![CDATA[char*]]></c> or <c><![CDATA[void*]]></c>. If <c><![CDATA[flags]]></c> is EI_BIN, then a
- fifth argument <c><![CDATA[size]]></c> is required, indicating the size
- in bytes of the object pointed to by <c><![CDATA[v]]></c>.
- </p>
- <p>If you wish to store an arbitrary pointer in the registry,
- specify a <c><![CDATA[size]]></c> of 0. In this case, the object itself will
- not be transferred by an <c><![CDATA[ei_reg_dump()]]></c> operation, just
- the pointer value.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
+ <p>A general function for retrieving any kind of
+ object from the registry.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>reg</c> is the registry where the object will be
+ looked up.</p>
+ </item>
+ <item>
+ <p><c>key</c> is the name of the object to look up.</p>
+ </item>
+ <item>
+ <p><c>flags</c> indicates the type of object that you
+ are looking for. If <c>flags</c> is <c>0</c>, any
+ kind of object is returned.
+ If <c>flags</c> is <c>EI_INT</c>, <c>EI_FLT</c>,
+ <c>EI_STR</c>, or <c>EI_BIN</c>, then only values of
+ that kind are returned.</p>
+ <p>The buffer pointed to by <c>v</c>
+ must be large enough to hold the return data, that is, it must be
+ a pointer to one of <c>int</c>,
+ <c>double</c>, <c>char*</c>, or
+ <c>void*</c>, respectively.</p>
+ <p>If <c>flags</c> is <c>EI_BIN</c>, a fifth argument
+ <c>int *size</c> is required, so that the size of the
+ object can be returned.</p>
+ </item>
+ </list>
+ <p>On success, <c>v</c> (and <c>size</c> if the
+ object is binary) is initialized with the value associated
+ with <c>key</c>, and the function returns <c>EI_INT</c>,
+ <c>EI_FLT</c>, <c>EI_STR</c>, or <c>EI_BIN</c>, indicating the type
+ of object. On failure, <c>-1</c> is returned and the
+ arguments are not updated.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_getival(reg,key)</nametext></name>
- <fsummary>Get an integer object</fsummary>
+ <name><ret>int</ret><nametext>ei_reg_markdirty(reg,key)</nametext></name>
+ <fsummary>Mark an object as dirty.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
</type>
<desc>
- <p>Get the value associated with <c><![CDATA[key]]></c> in the
- registry. The value must be an integer.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the object will be looked
- up.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object to look up.
- </p>
- <p>On success, the function returns the value associated with <c><![CDATA[key]]></c>.
- If the object was not found or it was not an integer
- object, -1 is returned. To avoid problems with in-band error
- reporting (i.e. if you cannot distinguish between -1 and a
- valid result) use the more general function <c><![CDATA[ei_reg_getval()]]></c>
- instead.</p>
+ <p>Marks a registry object as dirty. This ensures that
+ it is included in the next backup to <c>Mnesia</c>. Normally this
+ operation is not necessary, as all of the normal registry
+ 'set' functions do this automatically. However, if you have
+ retrieved the value of a string or binary object from the
+ registry and modified the contents, then the change is
+ invisible to the registry and the object is assumed to be
+ unmodified. This function allows you to make such modifications
+ and then let the registry know about them.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry containing the object.
+ </item>
+ <item><c>key</c> is the name of the object to mark.
+ </item>
+ </list>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>double</ret><nametext>ei_reg_getfval(reg,key)</nametext></name>
- <fsummary>Get a floating point object</fsummary>
+ <name><ret>ei_reg *</ret><nametext>ei_reg_open(size)</nametext></name>
+ <fsummary>Create and open a registry.</fsummary>
+ <type>
+ <v>int size;</v>
+ </type>
+ <desc>
+ <p>Opens (creates) a registry, which initially is empty. To
+ close the registry later, use <c>ei_reg_close()</c>.</p>
+ <p><c>size</c> is the approximate number of objects you
+ intend to store in the registry. As the registry uses a hash table
+ with collision chaining, no absolute upper limit exists on the
+ number of objects that can be stored in it. However, for reasons
+ of efficiency, it is a good idea to choose a number that is
+ appropriate for your needs. To change the size later, use
+ <c>ei_reg_resize()</c>. Notice that the number
+ you provide is increased to the nearest larger prime number.</p>
+ <p>Returns an empty registry on success, otherwise <c>NULL</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_reg_purge(reg)</nametext></name>
+ <fsummary>Remove deleted objects.</fsummary>
<type>
<v>ei_reg *reg;</v>
- <v>const char *key;</v>
</type>
<desc>
- <p>Get the value associated with <c><![CDATA[key]]></c> in the
- registry. The value must be a floating point type.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the object will be looked
- up.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object to look up.
- </p>
- <p>On success, the function returns the value associated with <c><![CDATA[key]]></c>.
- If the object was not found or it was not a floating point
- object, -1.0 is returned. To avoid problems with in-band error
- reporting (i.e. if you cannot distinguish between -1.0 and a
- valid result) use the more general function <c><![CDATA[ei_reg_getval()]]></c>
- instead.</p>
+ <p>Removes all objects marked for deletion. When objects
+ are deleted with <c>ei_reg_delete()</c> they are not
+ removed from the registry, only marked for later removal.
+ On a later backup to <c>Mnesia</c>, the
+ objects can also be removed from the <c>Mnesia</c> table. If you are
+ not backing up to <c>Mnesia</c>, you may wish to remove the objects
+ manually with this function.</p>
+ <p><c>reg</c> is a registry containing objects marked for
+ deletion.</p>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>const char *</ret><nametext>ei_reg_getsval(reg,key)</nametext></name>
- <fsummary>Get a string object</fsummary>
+ <name><ret>int</ret><nametext>ei_reg_resize(reg,newsize)</nametext></name>
+ <fsummary>Resize a registry.</fsummary>
+ <type>
+ <v>ei_reg *reg;</v>
+ <v>int newsize;</v>
+ </type>
+ <desc>
+ <p>Changes the size of a registry.</p>
+ <p><c>newsize</c> is the new size to make the registry. The
+ number is increased to the nearest larger prime number.</p>
+ <p>On success, the registry is resized, all contents
+ rehashed, and <c>0</c> is returned. On failure, the
+ registry is left unchanged and <c>-1</c> is returned.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_reg_restore(fd,reg,mntab)</nametext></name>
+ <fsummary>Restore a registry from Mnesia.</fsummary>
+ <type>
+ <v>int fd;</v>
+ <v>ei_reg *reg;</v>
+ <v>const char *mntab;</v>
+ </type>
+ <desc>
+ <p>The contents of a <c>Mnesia</c> table are read into the registry.</p>
+ <list type="bulleted">
+ <item><c>fd</c> is an open connection to Erlang.
+ <c>Mnesia</c> 3.0 or later must be running on the Erlang node.
+ </item>
+ <item><c>reg</c> is the registry where the data is to be
+ placed.</item>
+ <item><c>mntab</c> is the name of the <c>Mnesia</c> table
+ to read data from.</item>
+ </list>
+ <p>Notice that only tables of a certain format can be
+ restored, that is, those that have been created and backed up to
+ with <c>ei_reg_dump()</c>. If the registry was not empty
+ before the operation, the contents of the table are added to the
+ contents of the registry. If the table contains objects with the
+ same keys as those already in the registry, the registry objects
+ are overwritten with the new values. If the registry
+ contains objects that were not in the table, they are
+ unchanged by this operation.</p>
+ <p>After the restore operation, the entire contents of the
+ registry is marked as unmodified. Notice that this includes any
+ objects that were modified before the restore and not
+ overwritten by the restore.</p>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>ei_reg_setfval(reg,key,f)</nametext></name>
+ <fsummary>Assign a floating point object.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
+ <v>double f;</v>
</type>
<desc>
- <p>Get the value associated with <c><![CDATA[key]]></c> in the
- registry. The value must be a string.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the object will be looked
- up.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object to look up.
- </p>
- <p>On success, the function returns the value associated with
- <c><![CDATA[key]]></c>. If the object was not found or it was not a string,
- NULL is returned. To avoid problems with in-band error
- reporting (i.e. if you cannot distinguish between NULL and a
- valid result) use the more general function <c><![CDATA[ei_reg_getval()]]></c>
- instead.</p>
+ <p>Creates a key-value pair with the specified <c>key</c>
+ and floating point value <c>f</c>. If an object already
+ exists with the same <c>key</c>, the new value replaces
+ the old one. If the previous value was a binary or string, it is
+ freed with <c>free()</c>.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry where the object is to be
+ placed.</item>
+ <item><c>key</c> is the object name.</item>
+ <item><c>f</c> is the floating point value to assign.
+ </item>
+ </list>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>const void *</ret><nametext>ei_reg_getpval(reg,key,size)</nametext></name>
- <fsummary>Get a binary object</fsummary>
+ <name><ret>int</ret><nametext>ei_reg_setival(reg,key,i)</nametext></name>
+ <fsummary>Assign an integer object.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
- <v>int size;</v>
+ <v>int i;</v>
</type>
<desc>
- <p>Get the value associated with <c><![CDATA[key]]></c> in the
- registry. The value must be a binary (pointer) type.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the object will be looked
- up.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object to look up.
- </p>
- <p><c><![CDATA[size]]></c> will be initialized to contain the length in
- bytes of the object, if it is found.
- </p>
- <p>On success, the function returns the value associated with
- <c><![CDATA[key]]></c> and indicates its length in <c><![CDATA[size]]></c>.
- If the object was not found or it was not a binary object,
- NULL is returned. To avoid problems with in-band error
- reporting (i.e. if you cannot distinguish between NULL and a
- valid result) use the more general function <c><![CDATA[ei_reg_getval()]]></c>
- instead.</p>
+ <p>Creates a key-value pair with the specified <c>key</c>
+ and integer value <c>i</c>. If an object already exists
+ with the same <c>key</c>, the new value replaces the old
+ one. If the previous value was a binary or string, it is freed with
+ <c>free()</c>.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry where the object is to be
+ placed.</item>
+ <item><c>key</c> is the object name.</item>
+ <item><c>i</c> is the integer value to assign.</item>
+ </list>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_getval(reg,key,flags,v,...)</nametext></name>
- <fsummary>Get any object</fsummary>
+ <name><ret>int</ret><nametext>ei_reg_setpval(reg,key,p,size)</nametext></name>
+ <fsummary>Assign a binary object.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
- <v>int flags;</v>
- <v>void *v (see below)</v>
+ <v>const void *p;</v>
+ <v>int size;</v>
</type>
<desc>
- <p>This is a general function for retrieving any kind of
- object from the registry.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the object will be looked
- up.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object to look up.
- </p>
- <p><c><![CDATA[flags]]></c> indicates the type of object that you are
- looking for. If <c><![CDATA[flags]]></c> is 0, then any kind of object will
- be returned. If <c><![CDATA[flags]]></c> is one of EI_INT, EI_FLT, EI_STR or
- EI_BIN, then only values of that kind will be returned. The
- buffer pointed to by <c><![CDATA[v]]></c> must be large enough to hold the return
- data, i.e. it must be a pointer to one of <c><![CDATA[int]]></c>,
- <c><![CDATA[double]]></c>, <c><![CDATA[char*]]></c> or <c><![CDATA[void*]]></c>, respectively. Also,
- if <c><![CDATA[flags]]></c> is EI_BIN, then a fifth argument <c><![CDATA[int *size]]></c> is required, so that the size of the object can be
- returned.
- </p>
- <p>If the function succeeds, <c><![CDATA[v]]></c> (and <c><![CDATA[size]]></c> if the
- object is binary) will be initialized with the value associated
- with <c><![CDATA[key]]></c>, and the function will return one of EI_INT,
- EI_FLT, EI_STR or EI_BIN, indicating the type of object. On failure the
- function will return -1 and the arguments will not be updated.</p>
+ <p>Creates a key-value pair with the specified <c>key</c>
+ whose "value" is the binary object pointed to by <c>p</c>.
+ If an object already exists with the same <c>key</c>,
+ the new value replaces the old one. If the previous value was a
+ binary or string, it is freed with <c>free()</c>.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry where the object is to be
+ placed.</item>
+ <item><c>key</c> is the object name.</item>
+ <item><c>p</c> is a pointer to the binary object. The
+ object itself must have been created through a single call to
+ <c>malloc()</c> or a similar function, so that the
+ registry can later delete it if necessary by calling
+ <c>free()</c>.</item>
+ <item><c>size</c> is the length in bytes of the binary
+ object.</item>
+ </list>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_markdirty(reg,key)</nametext></name>
- <fsummary>Mark an object as dirty </fsummary>
+ <name><ret>int</ret><nametext>ei_reg_setsval(reg,key,s)</nametext></name>
+ <fsummary>Assign a string object.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
+ <v>const char *s;</v>
</type>
<desc>
- <p>Mark a registry object as dirty. This will ensure that
- it is included in the next backup to Mnesia. Normally this
- operation will not be necessary since all of the normal registry
- 'set' functions do this automatically. However if you have
- retrieved the value of a string or binary object from the
- registry and modified the contents, then the change will be
- invisible to the registry and the object will be assumed to be
- unmodified. This function allows you to make such modifications
- and then let the registry know about them.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry containing the object.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object to mark.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
+ <p>Creates a key-value pair with the specified <c>key</c>
+ whose "value" is the specified string <c>s</c>. If an
+ object already exists with the same <c>key</c>, the new
+ value replaces the old one. If the previous value was a binary or
+ string, it is freed with <c>free()</c>.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry where the object is to be
+ placed.</item>
+ <item><c>key</c> is the object name.</item>
+ <item><c>s</c> is the string to assign. The string itself
+ must have been created through a single call to
+ <c>malloc()</c> or similar a function,
+ so that the registry can later delete it if
+ necessary by calling <c>free()</c>.</item>
+ </list>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
- <name><ret>int</ret><nametext>ei_reg_delete(reg,key)</nametext></name>
- <fsummary>Delete an object from the registry</fsummary>
+ <name><ret>int</ret><nametext>ei_reg_setval(reg,key,flags,v,...)</nametext></name>
+ <fsummary>Assign a value to any object type.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
+ <v>int flags;</v>
+ <v>v (see below)</v>
</type>
<desc>
- <p>Delete an object from the registry. The object is not
- actually removed from the registry, it is only marked for later
- removal so that on subsequent backups to Mnesia, the
- corresponding object can be removed from the Mnesia table as
- well. If another object is later created with the same key, the
- object will be reused.
- </p>
- <p>The object will be removed from the registry after a call to
- <c><![CDATA[ei_reg_dump()]]></c> or <c><![CDATA[ei_reg_purge()]]></c>.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry containing <c><![CDATA[key]]></c>.
- </p>
- <p><c><![CDATA[key]]></c> is the object to remove.
- </p>
- <p>If the object was found, the function returns 0 indicating
- success. Otherwise the function returns -1.</p>
+ <p>Creates a key-value pair with the specified <c>key</c>
+ whose value is specified by <c>v</c>. If an object already
+ exists with the same <c>key</c>, the new value replaces
+ the old one. If the previous value was a binary or string, it is freed
+ with <c>free()</c>.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>reg</c> is the registry where the object is to be
+ placed.</p>
+ </item>
+ <item>
+ <p><c>key</c> is the object name.</p>
+ </item>
+ <item>
+ <p><c>flags</c> indicates the type of the object
+ specified by <c>v</c>. Flags must be one of
+ <c>EI_INT</c>, <c>EI_FLT</c>, <c>EI_STR</c>, and <c>EI_BIN</c>,
+ indicating whether
+ <c>v</c> is <c>int</c>,
+ <c>double</c>, <c>char*</c>, or
+ <c>void*</c>.</p>
+ <p>If <c>flags</c> is <c>EI_BIN</c>, a fifth argument
+ <c>size</c> is required, indicating the size
+ in bytes of the object pointed to by <c>v</c>.</p>
+ </item>
+ </list>
+ <p>If you wish to store an arbitrary pointer in the registry,
+ specify a <c>size</c> of <c>0</c>. In this case, the
+ object itself is not transferred by an
+ <c>ei_reg_dump()</c> operation, only the pointer
+ value.</p>
+ <p>Returns <c>0</c> on success, otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_reg_stat(reg,key,obuf)</nametext></name>
- <fsummary>Get object information</fsummary>
+ <fsummary>Get object information.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>const char *key;</v>
<v>struct ei_reg_stat *obuf;</v>
</type>
<desc>
- <p>Return information about an object.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry containing the object.
- </p>
- <p><c><![CDATA[key]]></c> is the name of the object.
- </p>
- <p><c><![CDATA[obuf]]></c> is a pointer to an <c><![CDATA[ei_reg_stat]]></c> structure,
- defined below:
- </p>
+ <p>Returns information about an object.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry containing the object.
+ </item>
+ <item><c>key</c> is the object name.</item>
+ <item><c>obuf</c> is a pointer to an
+ <c>ei_reg_stat</c> structure, defined as follows:</item>
+ </list>
<code type="none"><![CDATA[
struct ei_reg_stat {
int attr;
int size;
};
]]></code>
- <p>In <c><![CDATA[attr]]></c> the object's attributes are stored as the logical
- OR of its type (one of EI_INT, EI_FLT, EI_BIN and EI_STR),
- whether it is marked for deletion (EI_DELET) and whether it has
- been modified since the last backup to Mnesia (EI_DIRTY).
- </p>
- <p>The <c><![CDATA[size]]></c> field indicates the size in bytes required to store
- EI_STR (including the terminating 0) and EI_BIN objects, or 0
- for EI_INT and EI_FLT.
- </p>
- <p>The function returns 0 and initializes <c><![CDATA[obuf]]></c> on
- success, or returns -1 on failure.</p>
+ <p>In <c>attr</c> the attributes of the object are stored
+ as the logical <em>OR</em> of its type (one of <c>EI_INT</c>,
+ <c>EI_FLT</c>, <c>EI_BIN</c>, and <c>EI_STR</c>),
+ whether it is marked for deletion (<c>EI_DELET</c>), and whether it
+ has been modified since the last backup to <c>Mnesia</c>
+ (<c>EI_DIRTY</c>).</p>
+ <p>Field <c>size</c> indicates the size in bytes required
+ to store <c>EI_STR</c> (including the terminating <c>0</c>) and
+ <c>EI_BIN</c> objects, or <c>0</c> for <c>EI_INT</c> and
+ <c>EI_FLT</c>.</p>
+ <p>Returns <c>0</c> and initializes <c>obuf</c> on success,
+ otherwise <c>-1</c>.</p>
</desc>
</func>
+
<func>
<name><ret>int</ret><nametext>ei_reg_tabstat(reg,obuf)</nametext></name>
- <fsummary>Get registry information</fsummary>
+ <fsummary>Get registry information.</fsummary>
<type>
<v>ei_reg *reg;</v>
<v>struct ei_reg_tabstat *obuf;</v>
</type>
<desc>
- <p>Return information about a registry. Using information
+ <p>Returns information about a registry. Using information
returned by this function, you can see whether the size of the
- registry is suitable for the amount of data it contains.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry to return information about.
- </p>
- <p><c><![CDATA[obuf]]></c> is a pointer to an <c><![CDATA[ei_reg_tabstat]]></c> structure,
- defined below:
- </p>
+ registry is suitable for the amount of data it contains.</p>
+ <list type="bulleted">
+ <item><c>reg</c> is the registry to return information
+ about.</item>
+ <item><c>obuf</c> is a pointer to an
+ <c>ei_reg_tabstat</c> structure, defined as follows:
+ </item>
+ </list>
<code type="none"><![CDATA[
struct ei_reg_tabstat {
int size;
@@ -487,126 +615,23 @@ struct ei_reg_tabstat {
int collisions;
};
]]></code>
- <p>The <c><![CDATA[size]]></c> field indicates the number of hash positions
+ <p>Field <c>size</c> indicates the number of hash positions
in the registry. This is the number you provided when you
created or last resized the registry, rounded up to the nearest
- prime.
- </p>
- <p><c><![CDATA[nelem]]></c> indicates the number of elements stored in the
- registry. It includes objects that are deleted but not purged.
- </p>
- <p><c><![CDATA[npos]]></c> indicates the number of unique positions that are
- occupied in the registry.
- </p>
- <p><c><![CDATA[collisions]]></c> indicates how many elements are sharing
- positions in the registry.
- </p>
- <p>On success, the function returns 0 and <c><![CDATA[obuf]]></c> is
- initialized to contain table statistics. On failure, the function
- returns -1.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_reg_dump(fd,reg,mntab,flags)</nametext></name>
- <fsummary>Back up a registry to Mnesia</fsummary>
- <type>
- <v>int fd;</v>
- <v>ei_reg *reg;</v>
- <v>const char *mntab;</v>
- <v>int flags;</v>
- </type>
- <desc>
- <p>Dump the contents of a registry to a Mnesia table in an
- atomic manner, i.e. either all data will be updated, or none of
- it will. If any errors are encountered while backing up
- the data, the entire operation is aborted.
- </p>
- <p><c><![CDATA[fd]]></c> is an open connection to Erlang.
- Mnesia 3.0 or later must be running on the Erlang node.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry to back up.
- </p>
- <p><c><![CDATA[mntab]]></c> is the name of the Mnesia table where the backed
- up data should be placed. If the table does not exist, it will
- be created automatically using configurable defaults. See your
- Mnesia documentation for information about configuring this
- behaviour.
- </p>
- <p>If <c><![CDATA[flags]]></c> is 0, the backup will include only those
- objects which have been created, modified or deleted since the
- last backup or restore (i.e. an incremental backup). After the
- backup, any objects that were marked dirty are now clean, and any
- objects that had been marked for deletion are deleted.
- </p>
- <p>Alternatively, setting flags to EI_FORCE will cause a full
- backup to be done, and EI_NOPURGE will cause the deleted objects
- to be left in the registry afterwards. These can be bitwise ORed
- together if both behaviours are desired. If EI_NOPURGE was
- specified, you can use <c><![CDATA[ei_reg_purge()]]></c> to explicitly remove
- the deleted items from the registry later.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_reg_restore(fd,reg,mntab)</nametext></name>
- <fsummary>Restore a registry from Mnesia</fsummary>
- <type>
- <v>int fd;</v>
- <v>ei_reg *reg;</v>
- <v>const char *mntab;</v>
- </type>
- <desc>
- <p>The contents of a Mnesia table are read into the
- registry.
- </p>
- <p><c><![CDATA[fd]]></c> is an open connection to Erlang.
- Mnesia 3.0 or later must be running on the Erlang node.
- </p>
- <p><c><![CDATA[reg]]></c> is the registry where the data should be placed.
- </p>
- <p><c><![CDATA[mntab]]></c> is the name of the Mnesia table to read data
- from.
- </p>
- <p>Note that only tables of a certain format can be
- restored, i.e. those that have been created and backed up to
- with <c><![CDATA[ei_reg_dump()]]></c>. If the registry was not empty before
- the operation, then the contents of the table are added to the
- contents of the registry. If the table contains objects with the
- same keys as those already in the registry, the registry objects
- will be overwritten with the new values. If the registry
- contains objects that were not in the table, they will be
- unchanged by this operation.
- </p>
- <p>After the restore operation, the entire contents of the
- registry is marked as unmodified. Note that this includes any
- objects that were modified before the restore and not
- overwritten by the restore.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
- </desc>
- </func>
- <func>
- <name><ret>int</ret><nametext>ei_reg_purge(reg)</nametext></name>
- <fsummary>Remove deleted objects</fsummary>
- <type>
- <v>ei_reg *reg;</v>
- </type>
- <desc>
- <p>Remove all objects marked for deletion. When objects
- are deleted with <c><![CDATA[ei_reg_delete()]]></c> they are not actually
- removed from the registry, only marked for later removal. This
- is so that on a subsequent backup to Mnesia, the
- objects can also be removed from the Mnesia table. If you are
- not backing up to Mnesia then you may wish to remove the objects
- manually with this function.
- </p>
- <p><c><![CDATA[reg]]></c> is a registry containing objects marked for
- deletion.
- </p>
- <p>The function returns 0 on success, or -1 on failure.</p>
+ prime number.</p>
+ <list type="bulleted">
+ <item><c>nelem</c> indicates the number of elements stored
+ in the registry. It includes objects that are deleted but not
+ purged.</item>
+ <item><c>npos</c> indicates the number of unique positions
+ that are occupied in the registry.</item>
+ <item><c>collisions</c> indicates how many elements are
+ sharing positions in the registry.</item>
+ </list>
+ <p>On success, <c>0</c> is returned and
+ <c>obuf</c> is initialized to contain table statistics,
+ otherwise <c>-1</c> is returned.</p>
</desc>
</func>
</funcs>
</cref>
-
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index 6dc51adee1..c193fd804a 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -423,7 +423,7 @@ int ei_connect_xinit(ei_cnode* ec, const char *thishostname,
}
#endif /* _REENTRANT */
- ec->creation = creation;
+ ec->creation = creation & 0x3; /* 2 bits */
if (cookie) {
if (strlen(cookie) >= sizeof(ec->ei_connect_cookie)) {
@@ -462,7 +462,7 @@ int ei_connect_xinit(ei_cnode* ec, const char *thishostname,
strcpy(ec->self.node,thisnodename);
ec->self.num = 0;
ec->self.serial = 0;
- ec->self.creation = creation;
+ ec->self.creation = creation & 0x3; /* 2 bits */
if ((dbglevel = getenv("EI_TRACELEVEL")) != NULL ||
(dbglevel = getenv("ERL_DEBUG_DIST")) != NULL)
@@ -1708,28 +1708,36 @@ error:
static int get_home(char *buf, int size)
{
- char* homedrive;
- char* homepath;
-
#ifdef __WIN32__
- homedrive = getenv("HOMEDRIVE");
- homepath = getenv("HOMEPATH");
-#else
- homedrive = "";
- homepath = getenv("HOME");
-#endif
+ char* homedrive = getenv("HOMEDRIVE");
+ char* homepath = getenv("HOMEPATH");
- if (!homedrive || !homepath) {
- buf[0] = '.';
- buf[1] = '\0';
- return 1;
- } else if (strlen(homedrive)+strlen(homepath) < size-1) {
+ if (homedrive && homepath) {
+ if (strlen(homedrive)+strlen(homepath) >= size)
+ return 0;
strcpy(buf, homedrive);
strcat(buf, homepath);
return 1;
}
-
- return 0;
+ else {
+ int len = GetWindowsDirectory(buf, size);
+ if (len) {
+ return (len < size);
+ }
+ }
+#else
+ char* homepath = getenv("HOME");
+ if (homepath) {
+ if (strlen(homepath) >= size)
+ return 0;
+ strcpy(buf, homepath);
+ return 1;
+ }
+#endif
+
+ buf[0] = '.';
+ buf[1] = '\0';
+ return 1;
}
diff --git a/lib/erl_interface/src/misc/ei_portio.h b/lib/erl_interface/src/misc/ei_portio.h
index fbb61b0ccf..bded811a35 100644
--- a/lib/erl_interface/src/misc/ei_portio.h
+++ b/lib/erl_interface/src/misc/ei_portio.h
@@ -21,7 +21,7 @@
*/
#ifndef _EI_PORTIO_H
#define _EI_PORTIO_H
-#if !defined(__WIN32__) || !defined(VXWORKS)
+#if !defined(__WIN32__) && !defined(VXWORKS)
#ifdef HAVE_WRITEV
/* Declaration of struct iovec *iov should be visible in this scope. */
#include <sys/uio.h>
diff --git a/lib/erl_interface/src/prog/erl_start.c b/lib/erl_interface/src/prog/erl_start.c
index d8f0632341..670a5900c9 100644
--- a/lib/erl_interface/src/prog/erl_start.c
+++ b/lib/erl_interface/src/prog/erl_start.c
@@ -17,7 +17,6 @@
*
* %CopyrightEnd%
*
-
*/
/* An exception from using eidef.h, use config.h directly */
@@ -45,7 +44,7 @@
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
-#include <netinet/tcp.h>
+#include <netinet/tcp.h>
#include <symLib.h>
#include <sysSymTbl.h>
#include <sysLib.h>
@@ -117,9 +116,9 @@ static int unique_id(void);
static unsigned long spawn_erlang_epmd(ei_cnode *ec,
char *alive,
Erl_IpAddr adr,
- int flags,
- char *erl_or_epmd,
- char *args[],
+ int flags,
+ char *erl_or_epmd,
+ char *args[],
int port,
int is_erlang);
#else
@@ -161,10 +160,10 @@ int erl_start_sys(ei_cnode *ec, char *alive, Erl_IpAddr adr, int flags,
r = ERL_SYS_ERROR;
goto done;
}
-
+
memset(&addr,0,sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = 0;
if (bind(sockd,(struct sockaddr *)&addr,sizeof(addr))<0) {
@@ -179,12 +178,12 @@ int erl_start_sys(ei_cnode *ec, char *alive, Erl_IpAddr adr, int flags,
listen(sockd,5);
#if defined(VXWORKS) || defined(__WIN32__)
- if((pid = spawn_erlang_epmd(ec,alive,adr,flags,erl,args,port,1))
+ if((pid = spawn_erlang_epmd(ec,alive,adr,flags,erl,args,port,1))
== 0)
return ERL_SYS_ERROR;
timeout.tv_usec = 0;
timeout.tv_sec = 10; /* ignoring ERL_START_TIME */
- if((r = wait_for_erlang(sockd,unique_id(),&timeout))
+ if((r = wait_for_erlang(sockd,unique_id(),&timeout))
== ERL_TIMEOUT) {
#if defined(VXWORKS)
taskDelete((int) pid);
@@ -262,7 +261,7 @@ done:
static int unique_id(void){
#if defined(VXWORKS)
return taskIdSelf();
-#else
+#else
return (int) GetCurrentThreadId();
#endif
}
@@ -285,7 +284,7 @@ static int enquote_args(char **oargs, char ***qargs){
for(len=0;oargs[len] != NULL; ++len)
;
args = malloc(sizeof(char *) * (len + 1));
-
+
for(i = 0; i < len; ++i){
qwhole = strchr(oargs[i],' ') != NULL;
extra = qwhole * 2;
@@ -337,16 +336,16 @@ static FUNCPTR lookup_function(char *symname){
static unsigned long spawn_erlang_epmd(ei_cnode *ec,
char *alive,
Erl_IpAddr adr,
- int flags,
- char *erl_or_epmd,
- char *args[],
+ int flags,
+ char *erl_or_epmd,
+ char *args[],
int port,
int is_erlang)
{
#if defined(VXWORKS)
FUNCPTR erlfunc;
#else /* Windows */
- STARTUPINFO sinfo;
+ STARTUPINFO sinfo;
SECURITY_ATTRIBUTES sa;
PROCESS_INFORMATION pinfo;
#endif
@@ -364,7 +363,7 @@ static unsigned long spawn_erlang_epmd(ei_cnode *ec,
if(is_erlang){
get_addr(ei_thishostname(ec), &myaddr);
#if defined(VXWORKS)
- inet_ntoa_b(myaddr, iaddrbuf);
+ inet_ntoa_b(myaddr, iaddrbuf);
#else /* Windows */
if((ptr = inet_ntoa(myaddr)) == NULL)
return 0;
@@ -372,7 +371,7 @@ static unsigned long spawn_erlang_epmd(ei_cnode *ec,
strcpy(iaddrbuf,ptr);
#endif
}
- if ((flags & ERL_START_REMOTE) ||
+ if ((flags & ERL_START_REMOTE) ||
(is_erlang && (hisaddr->s_addr != myaddr.s_addr))) {
return 0;
} else {
@@ -382,19 +381,17 @@ static unsigned long spawn_erlang_epmd(ei_cnode *ec,
#if !defined(VXWORKS)
/* On VxWorks, we dont actually run a command,
we call start_erl() */
- if(!erl_or_epmd)
+ if(!erl_or_epmd)
#endif
erl_or_epmd = (is_erlang) ? DEF_ERL_COMMAND :
DEF_EPMD_COMMAND;
if(is_erlang){
name_format = (flags & ERL_START_LONG) ? ERL_NAME_FMT :
ERL_SNAME_FMT;
- cmdlen +=
+ cmdlen +=
strlen(erl_or_epmd) + (*erl_or_epmd != '\0') +
strlen(name_format) + 1 + strlen(alive) +
- strlen(ERL_REPLY_FMT) + 1 + strlen(iaddrbuf) +
- 2 * FORMATTED_INT_LEN +
- 1;
+ strlen(ERL_REPLY_FMT) + 1 + strlen(iaddrbuf) + 2 * FORMATTED_INT_LEN + 1;
ptr = cmdbuf = malloc(cmdlen);
if(*erl_or_epmd != '\0')
ptr += sprintf(ptr,"%s ",erl_or_epmd);
@@ -484,11 +481,11 @@ static unsigned long spawn_erlang_epmd(ei_cnode *ec,
* arguments we use here.
*/
static int exec_erlang(ei_cnode *ec,
- char *alive,
+ char *alive,
Erl_IpAddr adr,
- int flags,
- char *erl,
- char *args[],
+ int flags,
+ char *erl,
+ char *args[],
int port)
{
#if !defined(__WIN32__) && !defined(VXWORKS)
@@ -498,8 +495,11 @@ static int exec_erlang(ei_cnode *ec,
char argbuf[BUFSIZ];
struct in_addr myaddr;
struct in_addr *hisaddr = (struct in_addr *)adr;
-
- get_addr(ei_thishostname(ec), &myaddr);
+
+ if (!get_addr(ei_thishostname(ec), &myaddr)) {
+ fprintf(stderr,"erl_call: failed to find hostname\r\n");
+ return ERL_SYS_ERROR;
+ }
/* on this host? */
/* compare ip addresses, unless forced by flag setting to use rsh */
@@ -525,8 +525,8 @@ static int exec_erlang(ei_cnode *ec,
/* *must* be noinput or node (seems to) hang... */
/* long or short names? */
- sprintf(&argbuf[len], "-noinput %s %s ",
- ((flags & ERL_START_LONG) ? "-name" : "-sname"),
+ sprintf(&argbuf[len], "-noinput %s %s ",
+ ((flags & ERL_START_LONG) ? "-name" : "-sname"),
alive);
len = strlen(argbuf);
@@ -572,7 +572,7 @@ static int exec_erlang(ei_cnode *ec,
fprintf(stderr,"\n\n===== Log started ======\n%s \n",ctime(&t));
fprintf(stderr,"erl_call: %s %s %s\n",argv[0],argv[1],argv[2]);
}
- }
+ }
/* start the system */
execvp(argv[0], argv);
@@ -609,7 +609,7 @@ static void gettimeofday(struct timeval *now, void *dummy){
now->tv_usec = ((ctick - (now->tv_sec * rate))*1000000)/rate;
}
#endif
-
+
/* wait for the remote system to reply */
/*
@@ -648,7 +648,7 @@ static int wait_for_erlang(int sockd, int magic, struct timeval *timeout)
"will timeout at %ld.%06ld\n",
now.tv_sec,now.tv_usec,stop_time.tv_sec,stop_time.tv_usec);
#endif
-
+
while (1) {
FD_ZERO(&rdset);
FD_SET(sockd,&rdset);
@@ -662,7 +662,7 @@ static int wait_for_erlang(int sockd, int magic, struct timeval *timeout)
to.tv_sec--;
}
if (to.tv_sec < 0) return ERL_TIMEOUT;
-
+
#ifdef DEBUG
fprintf(stderr,"erl_call: debug remaining to timeout: %ld.%06ld\n",
to.tv_sec,to.tv_usec);
@@ -690,7 +690,7 @@ static int wait_for_erlang(int sockd, int magic, struct timeval *timeout)
if (FD_ISSET(sockd,&rdset)) {
if ((fd = accept(sockd,(struct sockaddr *)&peer,&len)) < 0)
return ERL_SYS_ERROR;
-
+
/* now get sign-on message and terminate it */
#if defined(__WIN32__)
if ((n=recv(fd,buf,16,0)) >= 0) buf[n]=0x0;
@@ -703,7 +703,6 @@ static int wait_for_erlang(int sockd, int magic, struct timeval *timeout)
fprintf(stderr,"erl_call: debug got %d, expected %d\n",
atoi(buf),magic);
#endif
-
if (atoi(buf) == magic) return 0; /* success */
} /* if FD_SET */
} /* switch */
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index 33705d1e8b..563694a0c1 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1,2 +1,2 @@
-EI_VSN = 3.9
+EI_VSN = 3.9.3
ERL_INTERFACE_VSN = $(EI_VSN)
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index 88602e8222..8509f44ffc 100644
--- a/lib/eunit/doc/src/notes.xml
+++ b/lib/eunit/doc/src/notes.xml
@@ -33,6 +33,39 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The address to the FSF in the license header has been
+ updated.</p>
+ <p>
+ Own Id: OTP-14084</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Eunit 2.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When asserts were moved out to a separate header file,
+ the automatic enabling of asserts when testing is enabled
+ stopped working.</p>
+ <p>
+ Own Id: OTP-13892</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl
index 7fd6c206a4..0f7a15f669 100644
--- a/lib/eunit/include/eunit.hrl
+++ b/lib/eunit/include/eunit.hrl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% Copyright (C) 2004-2006 Mickaël Rémond, Richard Carlsson
@@ -51,7 +51,9 @@
%% note that the main switch used within this file is NOTEST; however,
%% both TEST and EUNIT may be used to check whether testing is enabled
-ifndef(NOTEST).
--undef(NOASSERT). % testing requires that assertions are enabled
+-ifndef(ASSERT).
+-define(ASSERT, true). % testing requires that assertions are enabled
+-endif.
-ifndef(TEST).
-define(TEST, true).
-endif.
diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl
index fbfd123c43..1009d76d5b 100644
--- a/lib/eunit/src/eunit.erl
+++ b/lib/eunit/src/eunit.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 2004-2009 Mickaël Rémond, Richard Carlsson
diff --git a/lib/eunit/src/eunit_autoexport.erl b/lib/eunit/src/eunit_autoexport.erl
index 7bb78f5ea8..a3eb64f9c8 100644
--- a/lib/eunit/src/eunit_autoexport.erl
+++ b/lib/eunit/src/eunit_autoexport.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/eunit/src/eunit_data.erl b/lib/eunit/src/eunit_data.erl
index cc002cb449..cea7bcd5c5 100644
--- a/lib/eunit/src/eunit_data.erl
+++ b/lib/eunit/src/eunit_data.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl
index 9dbb4835f8..b19f50b990 100644
--- a/lib/eunit/src/eunit_lib.erl
+++ b/lib/eunit/src/eunit_lib.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 2004-2007 Mickaël Rémond, Richard Carlsson
diff --git a/lib/eunit/src/eunit_proc.erl b/lib/eunit/src/eunit_proc.erl
index 8bdf94c877..c6bb8bdbb3 100644
--- a/lib/eunit/src/eunit_proc.erl
+++ b/lib/eunit/src/eunit_proc.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/eunit/src/eunit_serial.erl b/lib/eunit/src/eunit_serial.erl
index da76064a53..bbda4400da 100644
--- a/lib/eunit/src/eunit_serial.erl
+++ b/lib/eunit/src/eunit_serial.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/eunit/src/eunit_server.erl b/lib/eunit/src/eunit_server.erl
index 387976eba1..e767822543 100644
--- a/lib/eunit/src/eunit_server.erl
+++ b/lib/eunit/src/eunit_server.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/eunit/src/eunit_striptests.erl b/lib/eunit/src/eunit_striptests.erl
index c6ade389ba..ae1a834662 100644
--- a/lib/eunit/src/eunit_striptests.erl
+++ b/lib/eunit/src/eunit_striptests.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl
index 1b468551d8..a70b8bfd4c 100644
--- a/lib/eunit/src/eunit_surefire.erl
+++ b/lib/eunit/src/eunit_surefire.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Mickaël Rémond <[email protected]>
diff --git a/lib/eunit/src/eunit_test.erl b/lib/eunit/src/eunit_test.erl
index 62d30b1930..f2b9fa46f2 100644
--- a/lib/eunit/src/eunit_test.erl
+++ b/lib/eunit/src/eunit_test.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/eunit/src/eunit_tests.erl b/lib/eunit/src/eunit_tests.erl
index 5dee1cb49e..7aadefb29f 100644
--- a/lib/eunit/src/eunit_tests.erl
+++ b/lib/eunit/src/eunit_tests.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/eunit/src/eunit_tty.erl b/lib/eunit/src/eunit_tty.erl
index f604ca5ba3..1fbc4edd45 100644
--- a/lib/eunit/src/eunit_tty.erl
+++ b/lib/eunit/src/eunit_tty.erl
@@ -10,7 +10,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @author Richard Carlsson <[email protected]>
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index b551ee6eb6..7eee75ee10 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.3
+EUNIT_VSN = 2.3.2
diff --git a/lib/gs/doc/src/Makefile b/lib/gs/doc/src/Makefile
index b270bc84fe..a24ec77e31 100644
--- a/lib/gs/doc/src/Makefile
+++ b/lib/gs/doc/src/Makefile
@@ -148,7 +148,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
diff --git a/lib/gs/doc/src/notes.xml b/lib/gs/doc/src/notes.xml
index 20188c75e2..4f3f0645a5 100644
--- a/lib/gs/doc/src/notes.xml
+++ b/lib/gs/doc/src/notes.xml
@@ -31,7 +31,23 @@
</header>
<p>This document describes the changes made to the GS application.</p>
- <section><title>GS 1.6.1</title>
+ <section><title>GS 1.6.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>GS 1.6.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/gs/vsn.mk b/lib/gs/vsn.mk
index c762507bab..d2c5d15a96 100644
--- a/lib/gs/vsn.mk
+++ b/lib/gs/vsn.mk
@@ -1,2 +1,2 @@
-GS_VSN = 1.6.1
+GS_VSN = 1.6.2
diff --git a/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl b/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl
index 5451f1fe7d..b1f7bd7572 100644
--- a/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl
+++ b/lib/hipe/amd64/hipe_amd64_ra_sse2_postconditions.erl
@@ -87,22 +87,29 @@ do_fp_unop(I, TempMap) ->
%%% Fix an fmove op.
do_fmove(I, TempMap) ->
#fmove{src=Src,dst=Dst} = I,
- case is_mem_opnd(Dst, TempMap) and is_mem_opnd(Src, TempMap) of
+ case
+ (is_mem_opnd(Src, TempMap) andalso is_mem_opnd(Dst, TempMap))
+ orelse (is_mem_opnd(Src, TempMap) andalso (not is_float_temp(Dst)))
+ orelse ((not is_float_temp(Src)) andalso is_mem_opnd(Dst, TempMap))
+ of
true ->
- Tmp = clone(Src),
+ Tmp = spill_temp(double),
{[#fmove{src=Src, dst=Tmp},I#fmove{src=Tmp,dst=Dst}],
true};
false ->
{[I], false}
end.
+is_float_temp(#x86_temp{type=Type}) -> Type =:= double;
+is_float_temp(#x86_mem{}) -> false.
+
%%% Check if an operand denotes a memory cell (mem or pseudo).
is_mem_opnd(Opnd, TempMap) ->
R =
case Opnd of
#x86_mem{} -> true;
- #x86_temp{} ->
+ #x86_temp{type=double} ->
Reg = hipe_x86:temp_reg(Opnd),
case hipe_x86:temp_is_allocatable(Opnd) of
true ->
@@ -176,6 +183,9 @@ clone(Dst) ->
#x86_mem{} -> hipe_x86:mem_type(Dst);
#x86_temp{} -> hipe_x86:temp_type(Dst)
end,
+ spill_temp(Type).
+
+spill_temp(Type) ->
hipe_x86:mk_new_temp(Type).
%%% Make a certain reg into a clone of Dst
diff --git a/lib/hipe/arm/hipe_arm_registers.erl b/lib/hipe/arm/hipe_arm_registers.erl
index 24cd929d41..dcf039676b 100644
--- a/lib/hipe/arm/hipe_arm_registers.erl
+++ b/lib/hipe/arm/hipe_arm_registers.erl
@@ -67,6 +67,8 @@
-define(R15, 15).
-define(LAST_PRECOLOURED, 15). % must handle both GPR and FPR ranges
+-define(LR, ?R14).
+
-define(ARG0, ?R1).
-define(ARG1, ?R2).
-define(ARG2, ?R3).
@@ -114,7 +116,7 @@ stack_pointer() -> ?STACK_POINTER.
proc_pointer() -> ?PROC_POINTER.
-lr() -> ?R14.
+lr() -> ?LR.
pc() -> ?R15.
@@ -198,7 +200,9 @@ call_clobbered() -> % does the RA strip the type or not?
].
tailcall_clobbered() -> % tailcall crapola needs one temp
- [{?TEMP1,tagged},{?TEMP1,untagged}].
+ [{?TEMP1,tagged},{?TEMP1,untagged}
+ ,{?LR,tagged},{?LR,untagged}
+ ].
live_at_return() ->
[%%{?LR,untagged},
diff --git a/lib/hipe/arm/hipe_rtl_to_arm.erl b/lib/hipe/arm/hipe_rtl_to_arm.erl
index ad5a559995..93342aba33 100644
--- a/lib/hipe/arm/hipe_rtl_to_arm.erl
+++ b/lib/hipe/arm/hipe_rtl_to_arm.erl
@@ -148,10 +148,11 @@ mk_shift_ir(S, Dst, Src1, ShiftOp, Src2) ->
mk_li(Tmp, Src1,
mk_shift_rr(S, Dst, Tmp, ShiftOp, Src2)).
-mk_shift_ri(S, Dst, Src1, ShiftOp, Src2) when is_integer(Src2) ->
- if Src2 >= 0, Src2 < 32 -> ok;
- true -> io:format("~w: excessive immediate shift ~w\n", [?MODULE,Src2])
- end,
+mk_shift_ri(S, Dst, Src1, ShiftOp, 0)
+ when ShiftOp =:= lsl; ShiftOp =:= lsr; ShiftOp =:= asr ->
+ [hipe_arm:mk_move(S, Dst, Src1)];
+mk_shift_ri(S, Dst, Src1, ShiftOp, Src2)
+ when is_integer(Src2), Src2 > 0, Src2 < 32 ->
Am1 = {Src1,ShiftOp,Src2},
[hipe_arm:mk_move(S, Dst, Am1)].
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 9453ca6c6f..230fce2e68 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -124,7 +124,7 @@
t_map_entries/2,
t_map_put/3,
t_map_update/3,
- map_pairwise_merge/3
+ t_map_pairwise_merge/4
]).
-ifdef(DO_ERL_BIF_TYPES_TEST).
@@ -1689,10 +1689,10 @@ type(maps, merge, 2, Xs, 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(
+ t_map(t_map_pairwise_merge(
fun(K, _, _, mandatory, V) -> {K, mandatory, V};
(K, MNess, VA, optional, VB) -> {K, MNess, t_sup(VA,VB)}
- end, MapA, MapB),
+ end, MapA, MapB, Opaques),
t_sup(ADefK, BDefK), t_sup(ADefV, BDefV))
end, Opaques);
type(maps, put, 3, Xs, Opaques) ->
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 7826dada9d..a5a3e8c136 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2017. 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.
@@ -159,6 +159,7 @@
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_pairwise_merge/4,
t_map_put/2, t_map_put/3,
t_matchstate/0,
t_matchstate/2,
@@ -219,8 +220,7 @@
is_erl_type/1,
atom_to_string/1,
var_table__new/0,
- cache__new/0,
- map_pairwise_merge/3
+ cache__new/0
]).
%%-define(DO_ERL_TYPES_TEST, true).
@@ -236,7 +236,8 @@
-export([t_is_identifier/1]).
-endif.
--export_type([erl_type/0, opaques/0, type_table/0, var_table/0, cache/0]).
+-export_type([erl_type/0, opaques/0, type_table/0, mod_records/0,
+ var_table/0, cache/0]).
%%-define(DEBUG, true).
@@ -379,8 +380,9 @@
-type type_value() :: {{module(), {file:name(), erl_anno:line()},
erl_parse:abstract_type(), ArgNames :: [atom()]},
erl_type()}.
--type type_table() :: dict:dict(record_key() | type_key(),
- record_value() | type_value()).
+-type type_table() :: #{record_key() | type_key() =>
+ record_value() | type_value()}.
+-type mod_records() :: dict:dict(module(), type_table()).
-opaque var_table() :: #{atom() => erl_type()}.
@@ -494,9 +496,9 @@ t_contains_opaque(?function(Domain, Range), Opaques) ->
t_contains_opaque(Domain, Opaques)
orelse t_contains_opaque(Range, Opaques);
t_contains_opaque(?identifier(_Types), _Opaques) -> false;
-t_contains_opaque(?integer(_Types), _Opaques) -> false;
t_contains_opaque(?int_range(_From, _To), _Opaques) -> false;
t_contains_opaque(?int_set(_Set), _Opaques) -> false;
+t_contains_opaque(?integer(_Types), _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) ->
@@ -524,7 +526,8 @@ list_contains_opaque(List, Opaques) ->
lists:any(fun(E) -> t_contains_opaque(E, Opaques) end, List).
%% t_find_opaque_mismatch/2 of two types should only be used if their
-%% t_inf is t_none() due to some opaque type violation.
+%% t_inf is t_none() due to some opaque type violation. However,
+%% 'error' is returned if a structure mismatch is found.
%%
%% The first argument of the function is the pattern and its second
%% argument the type we are matching against the pattern.
@@ -533,22 +536,30 @@ list_contains_opaque(List, Opaques) ->
'error' | {'ok', erl_type(), erl_type()}.
t_find_opaque_mismatch(T1, T2, Opaques) ->
- t_find_opaque_mismatch(T1, T2, T2, Opaques).
+ catch t_find_opaque_mismatch(T1, T2, T2, Opaques).
t_find_opaque_mismatch(?any, _Type, _TopType, _Opaques) -> error;
-t_find_opaque_mismatch(?none, _Type, _TopType, _Opaques) -> error;
+t_find_opaque_mismatch(?none, _Type, _TopType, _Opaques) -> throw(error);
t_find_opaque_mismatch(?list(T1, Tl1, _), ?list(T2, Tl2, _), TopType, Opaques) ->
t_find_opaque_mismatch_ordlists([T1, Tl1], [T2, Tl2], TopType, Opaques);
t_find_opaque_mismatch(T1, ?opaque(_) = T2, TopType, Opaques) ->
case is_opaque_type(T2, Opaques) of
- false -> {ok, TopType, T2};
+ false ->
+ case t_is_opaque(T1) andalso compatible_opaque_types(T1, T2) =/= [] of
+ true -> error;
+ false -> {ok, TopType, T2}
+ end;
true ->
t_find_opaque_mismatch(T1, t_opaque_structure(T2), TopType, Opaques)
end;
t_find_opaque_mismatch(?opaque(_) = T1, T2, TopType, Opaques) ->
%% The generated message is somewhat misleading:
case is_opaque_type(T1, Opaques) of
- false -> {ok, TopType, T1};
+ false ->
+ case t_is_opaque(T2) andalso compatible_opaque_types(T1, T2) =/= [] of
+ true -> error;
+ false -> {ok, TopType, T1}
+ end;
true ->
t_find_opaque_mismatch(t_opaque_structure(T1), T2, TopType, Opaques)
end;
@@ -564,7 +575,11 @@ t_find_opaque_mismatch(?tuple(_, _, _) = T1, ?tuple_set(_) = T2,
t_find_opaque_mismatch_lists(Tuples1, Tuples2, TopType, Opaques);
t_find_opaque_mismatch(T1, ?union(U2), TopType, Opaques) ->
t_find_opaque_mismatch_lists([T1], U2, TopType, Opaques);
-t_find_opaque_mismatch(_T1, _T2, _TopType, _Opaques) -> error.
+t_find_opaque_mismatch(T1, T2, _TopType, Opaques) ->
+ case t_is_none(t_inf(T1, T2, Opaques)) of
+ false -> error;
+ true -> throw(error)
+ end.
t_find_opaque_mismatch_ordlists(L1, L2, TopType, Opaques) ->
List = lists:zipwith(fun(T1, T2) ->
@@ -573,10 +588,11 @@ t_find_opaque_mismatch_ordlists(L1, L2, TopType, Opaques) ->
t_find_opaque_mismatch_list(List).
t_find_opaque_mismatch_lists(L1, L2, _TopType, Opaques) ->
- List = [t_find_opaque_mismatch(T1, T2, T2, Opaques) || T1 <- L1, T2 <- L2],
+ List = [catch t_find_opaque_mismatch(T1, T2, T2, Opaques) ||
+ T1 <- L1, T2 <- L2],
t_find_opaque_mismatch_list(List).
-t_find_opaque_mismatch_list([]) -> error;
+t_find_opaque_mismatch_list([]) -> throw(error);
t_find_opaque_mismatch_list([H|T]) ->
case H of
{ok, _T1, _T2} -> H;
@@ -611,9 +627,13 @@ t_decorate_with_opaque(T1, T2, Opaques) ->
false -> T1;
true ->
R = decorate(T1, T, Opaques),
- ?debug(case catch t_is_equal(t_unopaque(R), t_unopaque(T1)) of
- true -> ok;
- false ->
+ ?debug(case catch
+ not t_is_equal(t_unopaque(R), t_unopaque(T1))
+ orelse
+ t_is_equal(T1, T) andalso not t_is_equal(T1, R)
+ of
+ false -> ok;
+ _ ->
io:format("T1 = ~p,\n", [T1]),
io:format("T2 = ~p,\n", [T2]),
io:format("O = ~p,\n", [Opaques]),
@@ -642,7 +662,6 @@ decorate(?tuple_set(List), ?tuple_set(L), Opaques) ->
decorate(?union(List), T, Opaques) when T =/= ?any ->
?union(L) = force_union(T),
union_decorate(List, L, Opaques);
-decorate(?opaque(_)=T, _, _Opaques) -> T;
decorate(T, ?union(L), Opaques) when T =/= ?any ->
?union(List) = force_union(T),
union_decorate(List, L, Opaques);
@@ -656,7 +675,7 @@ decorate_with_opaque(Type, ?opaque(Set2), Opaques) ->
case decoration(set_to_list(Set2), Type, Opaques, [], false) of
{[], false} -> Type;
{List, All} when List =/= [] ->
- NewType = ?opaque(ordsets:from_list(List)),
+ NewType = sup_opaque(List),
case All of
true -> NewType;
false -> t_sup(NewType, Type)
@@ -670,9 +689,10 @@ decoration([#opaque{struct = S} = Opaque|OpaqueTypes], Type, Opaques,
case not IsOpaque orelse t_is_none(I) of
true -> decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes0, All);
false ->
- NewOpaque = Opaque#opaque{struct = decorate(I, S, Opaques)},
+ NewI = decorate(I, S, Opaques),
+ NewOpaque = combine(NewI, [Opaque]),
NewAll = All orelse t_is_equal(I, Type),
- NewOpaqueTypes = [NewOpaque|NewOpaqueTypes0],
+ NewOpaqueTypes = NewOpaque ++ NewOpaqueTypes0,
decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes, NewAll)
end;
decoration([], _Type, _Opaques, NewOpaqueTypes, All) ->
@@ -745,16 +765,16 @@ decorate_tuples_in_sets([], _L, _Opaques, Acc) ->
-spec t_opaque_from_records(type_table()) -> [erl_type()].
-t_opaque_from_records(RecDict) ->
- OpaqueRecDict =
- dict:filter(fun(Key, _Value) ->
+t_opaque_from_records(RecMap) ->
+ OpaqueRecMap =
+ maps:filter(fun(Key, _Value) ->
case Key of
{opaque, _Name, _Arity} -> true;
_ -> false
end
- end, RecDict),
- OpaqueTypeDict =
- dict:map(fun({opaque, Name, _Arity},
+ end, RecMap),
+ OpaqueTypeMap =
+ maps:map(fun({opaque, Name, _Arity},
{{Module, _FileLine, _Form, ArgNames}, _Type}) ->
%% Args = args_to_types(ArgNames),
%% List = lists:zip(ArgNames, Args),
@@ -763,8 +783,8 @@ t_opaque_from_records(RecDict) ->
Rep = t_any(), % not used for anything right now
Args = [t_any() || _ <- ArgNames],
t_opaque(Module, Name, Args, Rep)
- end, OpaqueRecDict),
- [OpaqueType || {_Key, OpaqueType} <- dict:to_list(OpaqueTypeDict)].
+ end, OpaqueRecMap),
+ [OpaqueType || {_Key, OpaqueType} <- maps:to_list(OpaqueTypeMap)].
%% Decompose opaque instances of type arg2 to structured types, in arg1
%% XXX: Same as t_unopaque
@@ -798,10 +818,6 @@ list_struct_from_opaque(Types, Opaques) ->
[t_struct_from_opaque(Type, Opaques) || Type <- Types].
%%-----------------------------------------------------------------------------
-
--type mod_records() :: dict:dict(module(), type_table()).
-
-%%-----------------------------------------------------------------------------
%% Unit type. Signals non termination.
%%
@@ -1664,10 +1680,12 @@ t_map(Pairs0, DefK0, DefV0) ->
%% 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])
+ catch error:badarg -> error(badarg, [Pairs0,DefK0,DefV0])
end,
- ?map(Pairs, DefK, DefV).
+ case map_pairs_are_none(Pairs) of
+ true -> ?none;
+ false -> ?map(Pairs, DefK, DefV)
+ end.
normalise_map_optionals([], _, _) -> [];
normalise_map_optionals([E={K,?opt,?none}|T], DefK, DefV) ->
@@ -1684,7 +1702,6 @@ normalise_map_optionals([E={K,?opt,V}|T], DefK, DefV) ->
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);
@@ -1697,6 +1714,10 @@ validate_map_elements([{K,_,_}]) ->
end;
validate_map_elements([]) -> true.
+map_pairs_are_none([]) -> false;
+map_pairs_are_none([{_,?mand,?none}|_]) -> true;
+map_pairs_are_none([_|Ps]) -> map_pairs_are_none(Ps).
+
-spec t_is_map(erl_type()) -> boolean().
t_is_map(Type) ->
@@ -1763,13 +1784,26 @@ mapdict_insert(E1={K1,_,_}, [E2={K2,_,_}|T]) when K1 > K2 ->
[E2|mapdict_insert(E1, T)];
mapdict_insert(E={_,_,_}, T) -> [E|T].
+-type map_pairwise_merge_fun() :: fun((erl_type(),
+ t_map_mandatoriness(), erl_type(),
+ t_map_mandatoriness(), erl_type())
+ -> t_map_pair() | false).
+
+-spec t_map_pairwise_merge(map_pairwise_merge_fun(), erl_type(), erl_type(),
+ opaques()) -> t_map_dict().
+t_map_pairwise_merge(F, MapA, MapB, Opaques) ->
+ do_opaque(MapA, Opaques,
+ fun(UMapA) ->
+ do_opaque(MapB, Opaques,
+ fun(UMapB) ->
+ map_pairwise_merge(F, UMapA, UMapB)
+ end)
+ end).
+
%% 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().
+-spec map_pairwise_merge(map_pairwise_merge_fun(), 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).
@@ -2223,16 +2257,21 @@ t_has_var_list([]) -> false.
-spec t_collect_vars(erl_type()) -> [erl_type()].
t_collect_vars(T) ->
- t_collect_vars(T, []).
+ Vs = t_collect_vars(T, maps:new()),
+ [V || {V, _} <- maps:to_list(Vs)].
+
+-type ctab() :: #{erl_type() => 'any'}.
--spec t_collect_vars(erl_type(), [erl_type()]) -> [erl_type()].
+-spec t_collect_vars(erl_type(), ctab()) -> ctab().
t_collect_vars(?var(_) = Var, Acc) ->
- ordsets:add_element(Var, Acc);
+ maps:put(Var, any, Acc);
t_collect_vars(?function(Domain, Range), Acc) ->
- ordsets:union(t_collect_vars(Domain, Acc), t_collect_vars(Range, []));
+ Acc1 = t_collect_vars(Domain, Acc),
+ t_collect_vars(Range, Acc1);
t_collect_vars(?list(Contents, Termination, _), Acc) ->
- ordsets:union(t_collect_vars(Contents, Acc), t_collect_vars(Termination, []));
+ Acc1 = t_collect_vars(Contents, Acc),
+ t_collect_vars(Termination, Acc1);
t_collect_vars(?product(Types), Acc) ->
t_collect_vars_list(Types, Acc);
t_collect_vars(?tuple(?any, ?any, ?any), Acc) ->
@@ -2833,12 +2872,7 @@ t_inf(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, _Opaques) ->
%% 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_map(Pairs, t_inf(ADefK, BDefK), t_inf(ADefV, BDefV));
t_inf(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2), _Opaques) ->
?matchstate(t_inf(Pres1, Pres2), t_inf(Slots1, Slots2));
t_inf(?nil, ?nil, _Opaques) -> ?nil;
@@ -2978,27 +3012,21 @@ inf_collect(_T1, [], _Opaques, OpL) ->
OpL.
combine(S, T1, T2) ->
- #opaque{mod = Mod1, name = Name1, args = Args1} = T1,
- #opaque{mod = Mod2, name = Name2, args = Args2} = T2,
- Comb1 = comb(Mod1, Name1, Args1, S, T1),
- case is_compat_opaque_names({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) of
- true -> Comb1;
- false -> Comb1 ++ comb(Mod2, Name2, Args2, S, T2)
+ case is_compat_opaque_names(T1, T2) of
+ true -> combine(S, [T1]);
+ false -> combine(S, [T1, T2])
end.
-comb(Mod, Name, Args, S, T) ->
- case can_combine_opaque_names(Mod, Name, Args, S) of
- true ->
- ?opaque(Set) = S,
- Set;
- false ->
- [T#opaque{struct = S}]
- end.
+combine(?opaque(Set), Ts) ->
+ [comb2(O, T) || O <- Set, T <- Ts];
+combine(S, Ts) ->
+ [T#opaque{struct = S} || T <- Ts].
-can_combine_opaque_names(Mod1, Name1, Args1,
- ?opaque([#opaque{mod = Mod2, name = Name2, args = Args2}])) ->
- is_compat_opaque_names({Mod1, Name1, Args1}, {Mod2, Name2, Args2});
-can_combine_opaque_names(_, _, _, _) -> false.
+comb2(O, T) ->
+ case is_compat_opaque_names(O, T) of
+ true -> O;
+ false -> T#opaque{struct = ?opaque(set_singleton(O))}
+ end.
%% Combining two lists this way can be very time consuming...
%% Note: two parameterized opaque types are not the same if their
@@ -3007,32 +3035,27 @@ inf_opaque(Set1, Set2, Opaques) ->
List1 = inf_look_up(Set1, Opaques),
List2 = inf_look_up(Set2, Opaques),
List0 = [combine(Inf, T1, T2) ||
- {Is1, ModNameArgs1, T1} <- List1,
- {Is2, ModNameArgs2, T2} <- List2,
- not t_is_none(Inf = inf_opaque_types(Is1, ModNameArgs1, T1,
- Is2, ModNameArgs2, T2,
- Opaques))],
- List = lists:sort(lists:append(List0)),
+ {Is1, T1} <- List1,
+ {Is2, T2} <- List2,
+ not t_is_none(Inf = inf_opaque_types(Is1, T1, Is2, T2, Opaques))],
+ List = lists:append(List0),
sup_opaque(List).
%% Optimization: do just one lookup.
inf_look_up(Set, Opaques) ->
- [{Opaques =:= 'universe' orelse inf_is_opaque_type2(T, Opaques),
- {M, N, Args}, T} ||
- #opaque{mod = M, name = N, args = Args} = T <- set_to_list(Set)].
+ [{Opaques =:= 'universe' orelse inf_is_opaque_type2(T, Opaques), T} ||
+ T <- set_to_list(Set)].
inf_is_opaque_type2(T, {match, Opaques}) ->
is_opaque_type2(T, Opaques);
inf_is_opaque_type2(T, Opaques) ->
is_opaque_type2(T, Opaques).
-inf_opaque_types(IsOpaque1, ModNameArgs1, T1,
- IsOpaque2, ModNameArgs2, T2, Opaques) ->
+inf_opaque_types(IsOpaque1, T1, IsOpaque2, T2, Opaques) ->
#opaque{struct = S1}=T1,
#opaque{struct = S2}=T2,
case
- Opaques =:= 'universe' orelse
- is_compat_opaque_names(ModNameArgs1, ModNameArgs2)
+ Opaques =:= 'universe' orelse is_compat_opaque_names(T1, T2)
of
true -> t_inf(S1, S2, Opaques);
false ->
@@ -3046,98 +3069,109 @@ inf_opaque_types(IsOpaque1, ModNameArgs1, T1,
end
end.
-is_compat_opaque_names(ModNameArgs, ModNameArgs) -> true;
-is_compat_opaque_names({Mod,Name,Args1}, {Mod,Name,Args2}) ->
- is_compat_args(Args1, Args2);
-is_compat_opaque_names(_, _) -> false.
+compatible_opaque_types(?opaque(Es1), ?opaque(Es2)) ->
+ [{O1, O2} || O1 <- Es1, O2 <- Es2, is_compat_opaque_names(O1, O2)].
+
+is_compat_opaque_names(Opaque1, Opaque2) ->
+ #opaque{mod = Mod1, name = Name1, args = Args1} = Opaque1,
+ #opaque{mod = Mod2, name = Name2, args = Args2} = Opaque2,
+ case {{Mod1, Name1, Args1}, {Mod2, Name2, Args2}} of
+ {ModNameArgs, ModNameArgs} -> true;
+ {{Mod, Name, Args1}, {Mod, Name, Args2}} ->
+ is_compat_args(Args1, Args2);
+ _ -> false
+ end.
is_compat_args([A1|Args1], [A2|Args2]) ->
is_compat_arg(A1, A2) andalso is_compat_args(Args1, Args2);
is_compat_args([], []) -> true;
is_compat_args(_, _) -> false.
-is_compat_arg(A1, A2) ->
- is_specialization(A1, A2) orelse is_specialization(A2, A1).
-
--spec is_specialization(erl_type(), erl_type()) -> boolean().
-
-%% Returns true if the first argument is a specialization of the
-%% second argument in the sense that every type is a specialization of
-%% any(). For example, {_,_} is a specialization of any(), but not of
-%% tuple(). Does not handle variables, but any() and unions (sort of).
-
-is_specialization(T, T) -> true;
-is_specialization(_, ?any) -> true;
-is_specialization(?any, _) -> false;
-is_specialization(?function(Domain1, Range1), ?function(Domain2, Range2)) ->
- (is_specialization(Domain1, Domain2) andalso
- is_specialization(Range1, Range2));
-is_specialization(?list(Contents1, Termination1, Size1),
- ?list(Contents2, Termination2, Size2)) ->
+-spec is_compat_arg(erl_type(), erl_type()) -> boolean().
+
+%% The intention is that 'true' is to be returned iff one of the
+%% arguments is a specialization of the other argument in the sense
+%% that every type is a specialization of any(). For example, {_,_} is
+%% a specialization of any(), but not of tuple(). Does not handle
+%% variables, but any() and unions (sort of). However, the
+%% implementation is more relaxed as any() is compatible to anything.
+
+is_compat_arg(T, T) -> true;
+is_compat_arg(_, ?any) -> true;
+is_compat_arg(?any, _) -> true;
+is_compat_arg(?function(Domain1, Range1), ?function(Domain2, Range2)) ->
+ (is_compat_arg(Domain1, Domain2) andalso
+ is_compat_arg(Range1, Range2));
+is_compat_arg(?list(Contents1, Termination1, Size1),
+ ?list(Contents2, Termination2, Size2)) ->
(Size1 =:= Size2 andalso
- is_specialization(Contents1, Contents2) andalso
- is_specialization(Termination1, Termination2));
-is_specialization(?product(Types1), ?product(Types2)) ->
- specialization_list(Types1, Types2);
-is_specialization(?tuple(?any, ?any, ?any), ?tuple(_, _, _)) -> false;
-is_specialization(?tuple(_, _, _), ?tuple(?any, ?any, ?any)) -> false;
-is_specialization(?tuple(Elements1, Arity, _),
- ?tuple(Elements2, Arity, _)) when Arity =/= ?any ->
- specialization_list(Elements1, Elements2);
-is_specialization(?tuple_set([{Arity, List}]),
- ?tuple(Elements2, Arity, _)) when Arity =/= ?any ->
- specialization_list(sup_tuple_elements(List), Elements2);
-is_specialization(?tuple(Elements1, Arity, _),
- ?tuple_set([{Arity, List}])) when Arity =/= ?any ->
- specialization_list(Elements1, sup_tuple_elements(List));
-is_specialization(?tuple_set(List1), ?tuple_set(List2)) ->
+ is_compat_arg(Contents1, Contents2) andalso
+ is_compat_arg(Termination1, Termination2));
+is_compat_arg(?product(Types1), ?product(Types2)) ->
+ is_compat_list(Types1, Types2);
+is_compat_arg(?map(Pairs1, DefK1, DefV1), ?map(Pairs2, DefK2, DefV2)) ->
+ (is_compat_list(Pairs1, Pairs2) andalso
+ is_compat_arg(DefK1, DefK2) andalso
+ is_compat_arg(DefV1, DefV2));
+is_compat_arg(?tuple(?any, ?any, ?any), ?tuple(_, _, _)) -> false;
+is_compat_arg(?tuple(_, _, _), ?tuple(?any, ?any, ?any)) -> false;
+is_compat_arg(?tuple(Elements1, Arity, _),
+ ?tuple(Elements2, Arity, _)) when Arity =/= ?any ->
+ is_compat_list(Elements1, Elements2);
+is_compat_arg(?tuple_set([{Arity, List}]),
+ ?tuple(Elements2, Arity, _)) when Arity =/= ?any ->
+ is_compat_list(sup_tuple_elements(List), Elements2);
+is_compat_arg(?tuple(Elements1, Arity, _),
+ ?tuple_set([{Arity, List}])) when Arity =/= ?any ->
+ is_compat_list(Elements1, sup_tuple_elements(List));
+is_compat_arg(?tuple_set(List1), ?tuple_set(List2)) ->
try
- specialization_list_list([sup_tuple_elements(T) || {_Arity, T} <- List1],
- [sup_tuple_elements(T) || {_Arity, T} <- List2])
+ is_compat_list_list([sup_tuple_elements(T) || {_Arity, T} <- List1],
+ [sup_tuple_elements(T) || {_Arity, T} <- List2])
catch _:_ -> false
end;
-is_specialization(?union(List1)=T1, ?union(List2)=T2) ->
- case specialization_union2(T1, T2) of
- {yes, Type1, Type2} -> is_specialization(Type1, Type2);
- no -> specialization_list(List1, List2)
+is_compat_arg(?opaque(_) = T1, T2) ->
+ is_compat_arg(t_opaque_structure(T1), T2);
+is_compat_arg(T1, ?opaque(_) = T2) ->
+ is_compat_arg(T1, t_opaque_structure(T2));
+is_compat_arg(?union(List1)=T1, ?union(List2)=T2) ->
+ case is_compat_union2(T1, T2) of
+ {yes, Type1, Type2} -> is_compat_arg(Type1, Type2);
+ no -> is_compat_list(List1, List2)
end;
-is_specialization(?union(List), T2) ->
+is_compat_arg(?union(List), T2) ->
case unify_union(List) of
- {yes, Type} -> is_specialization(Type, T2);
+ {yes, Type} -> is_compat_arg(Type, T2);
no -> false
end;
-is_specialization(T1, ?union(List)) ->
+is_compat_arg(T1, ?union(List)) ->
case unify_union(List) of
- {yes, Type} -> is_specialization(T1, Type);
+ {yes, Type} -> is_compat_arg(T1, Type);
no -> false
end;
-is_specialization(?opaque(_) = T1, T2) ->
- is_specialization(t_opaque_structure(T1), T2);
-is_specialization(T1, ?opaque(_) = T2) ->
- is_specialization(T1, t_opaque_structure(T2));
-is_specialization(?var(_), _) -> exit(error);
-is_specialization(_, ?var(_)) -> exit(error);
-is_specialization(?none, _) -> false;
-is_specialization(_, ?none) -> false;
-is_specialization(?unit, _) -> false;
-is_specialization(_, ?unit) -> false;
-is_specialization(#c{}, #c{}) -> false.
-
-specialization_list_list(LL1, LL2) ->
- length(LL1) =:= length(LL2) andalso specialization_list_list1(LL1, LL2).
-
-specialization_list_list1([], []) -> true;
-specialization_list_list1([L1|LL1], [L2|LL2]) ->
- specialization_list(L1, L2) andalso specialization_list_list1(LL1, LL2).
-
-specialization_list(L1, L2) ->
- length(L1) =:= length(L2) andalso specialization_list1(L1, L2).
-
-specialization_list1([], []) -> true;
-specialization_list1([T1|L1], [T2|L2]) ->
- is_specialization(T1, T2) andalso specialization_list1(L1, L2).
-
-specialization_union2(?union(List1)=T1, ?union(List2)=T2) ->
+is_compat_arg(?var(_), _) -> exit(error);
+is_compat_arg(_, ?var(_)) -> exit(error);
+is_compat_arg(?none, _) -> false;
+is_compat_arg(_, ?none) -> false;
+is_compat_arg(?unit, _) -> false;
+is_compat_arg(_, ?unit) -> false;
+is_compat_arg(#c{}, #c{}) -> false.
+
+is_compat_list_list(LL1, LL2) ->
+ length(LL1) =:= length(LL2) andalso is_compat_list_list1(LL1, LL2).
+
+is_compat_list_list1([], []) -> true;
+is_compat_list_list1([L1|LL1], [L2|LL2]) ->
+ is_compat_list(L1, L2) andalso is_compat_list_list1(LL1, LL2).
+
+is_compat_list(L1, L2) ->
+ length(L1) =:= length(L2) andalso is_compat_list1(L1, L2).
+
+is_compat_list1([], []) -> true;
+is_compat_list1([T1|L1], [T2|L2]) ->
+ is_compat_arg(T1, T2) andalso is_compat_list1(L1, L2).
+
+is_compat_union2(?union(List1)=T1, ?union(List2)=T2) ->
case {unify_union(List1), unify_union(List2)} of
{{yes, Type1}, {yes, Type2}} -> {yes, Type1, Type2};
{{yes, Type1}, no} -> {yes, Type1, T2};
@@ -4170,7 +4204,7 @@ t_map(Fun, T) ->
-spec t_to_string(erl_type()) -> string().
t_to_string(T) ->
- t_to_string(T, dict:new()).
+ t_to_string(T, maps:new()).
-spec t_to_string(erl_type(), type_table()) -> string().
@@ -4366,7 +4400,7 @@ record_field_diffs_to_string(?tuple([_|Fs], Arity, Tag), RecDict) ->
string:join(FieldDiffs, " and ").
field_diffs([F|Fs], [{FName, _Abstr, DefType}|FDefs], RecDict, Acc) ->
- %% Don't care about opaqueness for now.
+ %% Don't care about opacity for now.
NewAcc =
case not t_is_none(t_inf(F, DefType)) of
true -> Acc;
@@ -4469,28 +4503,31 @@ t_from_form1(Form, ET, Site, MR, V, C) ->
vtab = V,
tnames = TypeNames},
L = ?EXPAND_LIMIT,
- {T1, L1, C1} = from_form(Form, State, ?EXPAND_DEPTH, L, C),
+ {T0, L0, C0} = from_form(Form, State, ?EXPAND_DEPTH, L, C),
if
- L1 =< 0 ->
- from_form_loop(Form, State, 1, L, C1);
+ L0 =< 0 ->
+ {T1, _, C1} = from_form(Form, State, 1, L, C0),
+ from_form_loop(Form, State, 2, L, C1, T1);
true ->
- {T1, C1}
+ {T0, C0}
end.
initial_typenames({type, _MTA}=Site) -> [Site];
initial_typenames({spec, _MFA}) -> [];
initial_typenames({record, _MRA}) -> [].
-from_form_loop(Form, State, D, Limit, C) ->
+from_form_loop(Form, State, D, Limit, C, T0) ->
{T1, L1, C1} = from_form(Form, State, D, Limit, C),
Delta = Limit - L1,
if
- %% Save some time by assuming next depth will exceed the limit.
+ L1 =< 0 ->
+ {T0, C1};
Delta * 8 > Limit ->
+ %% Save some time by assuming next depth will exceed the limit.
{T1, C1};
true ->
D1 = D + 1,
- from_form_loop(Form, State, D1, Limit, C1)
+ from_form_loop(Form, State, D1, Limit, C1, T1)
end.
-spec from_form(parse_form(),
@@ -4528,6 +4565,8 @@ from_form({atom, _L, Atom}, _S, _D, L, C) ->
{t_atom(Atom), L, C};
from_form({integer, _L, Int}, _S, _D, L, C) ->
{t_integer(Int), L, C};
+from_form({char, _L, Char}, _S, _D, L, C) ->
+ {t_integer(Char), L, C};
from_form({op, _L, _Op, _Arg} = Op, _S, _D, L, C) ->
case erl_eval:partial_eval(Op) of
{integer, _, Val} ->
@@ -4749,7 +4788,7 @@ type_from_form1(Name, Args, ArgsLen, R, TypeName, TypeNames, S, D, L, C) ->
{Rep, L2, C2} = recur_limit(Fun, D, L1, TypeName, TypeNames),
Rep1 = choose_opaque_type(Rep, Type),
Rep2 = case cannot_have_opaque(Rep1, TypeName, TypeNames) of
- true -> Rep1;
+ true -> Rep;
false ->
ArgTypes2 = subst_all_vars_to_any_list(ArgTypes),
t_opaque(Module, Name, ArgTypes2, Rep1)
@@ -4821,7 +4860,7 @@ remote_from_form1(RemMod, Name, Args, ArgsLen, RemDict, RemType, TypeNames,
NewRep1 = choose_opaque_type(NewRep, Type),
NewRep2 =
case cannot_have_opaque(NewRep1, RemType, TypeNames) of
- true -> NewRep1;
+ true -> NewRep;
false ->
ArgTypes2 = subst_all_vars_to_any_list(ArgTypes),
t_opaque(Mod, Name, ArgTypes2, NewRep1)
@@ -5042,6 +5081,7 @@ check_record_fields({remote_type, _L, [{atom, _, _}, {atom, _, _}, Args]},
list_check_record_fields(Args, S, C);
check_record_fields({atom, _L, _}, _S, C) -> C;
check_record_fields({integer, _L, _}, _S, C) -> C;
+check_record_fields({char, _L, _}, _S, C) -> C;
check_record_fields({op, _L, _Op, _Arg}, _S, C) -> C;
check_record_fields({op, _L, _Op, _Arg1, _Arg2}, _S, C) -> C;
check_record_fields({type, _L, tuple, any}, _S, C) -> C;
@@ -5143,6 +5183,7 @@ t_form_to_string({var, _L, Name}) -> atom_to_list(Name);
t_form_to_string({atom, _L, Atom}) ->
io_lib:write_string(atom_to_list(Atom), $'); % To quote or not to quote... '
t_form_to_string({integer, _L, Int}) -> integer_to_list(Int);
+t_form_to_string({char, _L, Char}) -> integer_to_list(Char);
t_form_to_string({op, _L, _Op, _Arg} = Op) ->
case erl_eval:partial_eval(Op) of
{integer, _, _} = Int -> t_form_to_string(Int);
@@ -5225,7 +5266,7 @@ t_form_to_string({type, _L, union, Args}) ->
t_form_to_string({type, _L, Name, []} = T) ->
try
M = mod,
- D0 = dict:new(),
+ D0 = maps:new(),
MR = dict:from_list([{M, D0}]),
Site = {type, {M,Name,0}},
V = var_table__new(),
@@ -5289,8 +5330,8 @@ is_erl_type(_) -> false.
-spec lookup_record(atom(), type_table()) ->
'error' | {'ok', [{atom(), parse_form(), erl_type()}]}.
-lookup_record(Tag, RecDict) when is_atom(Tag) ->
- case dict:find({record, Tag}, RecDict) of
+lookup_record(Tag, Table) when is_atom(Tag) ->
+ case maps:find({record, Tag}, Table) of
{ok, {_FileLine, [{_Arity, Fields}]}} ->
{ok, Fields};
{ok, {_FileLine, List}} when is_list(List) ->
@@ -5304,18 +5345,18 @@ lookup_record(Tag, RecDict) when is_atom(Tag) ->
-spec lookup_record(atom(), arity(), type_table()) ->
'error' | {'ok', [{atom(), parse_form(), erl_type()}]}.
-lookup_record(Tag, Arity, RecDict) when is_atom(Tag) ->
- case dict:find({record, Tag}, RecDict) of
+lookup_record(Tag, Arity, Table) when is_atom(Tag) ->
+ case maps:find({record, Tag}, Table) of
{ok, {_FileLine, [{Arity, Fields}]}} -> {ok, Fields};
{ok, {_FileLine, OrdDict}} -> orddict:find(Arity, OrdDict);
error -> error
end.
-spec lookup_type(_, _, _) -> {'type' | 'opaque', type_value()} | 'error'.
-lookup_type(Name, Arity, RecDict) ->
- case dict:find({type, Name, Arity}, RecDict) of
+lookup_type(Name, Arity, Table) ->
+ case maps:find({type, Name, Arity}, Table) of
error ->
- case dict:find({opaque, Name, Arity}, RecDict) of
+ case maps:find({opaque, Name, Arity}, Table) of
error -> error;
{ok, Found} -> {opaque, Found}
end;
@@ -5325,8 +5366,8 @@ lookup_type(Name, Arity, RecDict) ->
-spec type_is_defined('type' | 'opaque', atom(), arity(), type_table()) ->
boolean().
-type_is_defined(TypeOrOpaque, Name, Arity, RecDict) ->
- dict:is_key({TypeOrOpaque, Name, Arity}, RecDict).
+type_is_defined(TypeOrOpaque, Name, Arity, Table) ->
+ maps:is_key({TypeOrOpaque, Name, Arity}, Table).
cannot_have_opaque(Type, TypeName, TypeNames) ->
t_is_none(Type) orelse is_recursive(TypeName, TypeNames).
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index e2a1524be6..627794aee8 100644
--- a/lib/hipe/doc/src/notes.xml
+++ b/lib/hipe/doc/src/notes.xml
@@ -31,6 +31,111 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.15.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix a bug concerning parameterized opaque types. </p>
+ <p>
+ Own Id: OTP-14130</p>
+ </item>
+ <item>
+ <p>
+ Fixed xml issues in old release notes</p>
+ <p>
+ Own Id: OTP-14269</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Hipe 3.15.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix flow control bug in hipe compiler that may cause
+ compile time crash.</p>
+ <p>
+ Own Id: OTP-13965 Aux Id: PR-1253 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug in native compilation of bitstring pattern
+ matching causing erroneous runtime matching result. The
+ bug only affects code containing constant-valued segments
+ whose size is expressed in bits; it is triggered when the
+ pattern matching against these segments fails (i.e., when
+ the next clause needs to be tried).</p>
+ <p>
+ Own Id: OTP-14005</p>
+ </item>
+ <item>
+ <p>
+ Workaround in HiPE LLVM backend for a bug in LLVM 3.9.
+ The bug could cause LLVM-compiled modules to be rejected
+ during loading with a badarg exception in
+ hipe_bifs:enter_sdecs/1, but also cause corruption or
+ segmentation faults i runtime.</p>
+ <p>
+ Own Id: OTP-14027 Aux Id: ERL-292, PR-1237 </p>
+ </item>
+ <item>
+ <p>
+ Fix a bug in HiPE LLVM backend involving incorrect type
+ tests of atoms sometimes causing incorrect behaviour or
+ even segfaults.</p>
+ <p>
+ Own Id: OTP-14028 Aux Id: PR-1237 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Hipe 3.15.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed various hipe compiler backend bugs affecting mostly
+ ARM and SPARC.</p>
+ <p>
+ Own Id: OTP-13846 Aux Id: PR-1146 </p>
+ </item>
+ <item>
+ <p>
+ Fixed some Dialyzer warnings and code cleanup for the
+ Sparc compiler backend.</p>
+ <p>
+ Own Id: OTP-13861 Aux Id: PR-1148 </p>
+ </item>
+ <item>
+ <p> Fix erl_bif_types opaque bug. </p>
+ <p>
+ Own Id: OTP-13878 Aux Id: PR-1161, ERL-249 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix erl_types opaque match order</p>
+ <p>
+ Own Id: OTP-13876</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.15.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -45,12 +150,12 @@
</item>
<item>
<p>
- Various fixes and improvements to the HiPE LLVM backend.
+ Various fixes and improvements to the HiPE LLVM backend.</p>
<list> <item>Add support for LLVM 3.7 and 3.8 in the
HiPE/LLVM x86_64 backend</item> <item>Reinstate support
for the LLVM backend on x86 (works OK for LLVM 3.5 to 3.7
-- LLVM 3.8 has a bug that prevents it from generating
- correct native code on x86)</item> </list></p>
+ correct native code on x86)</item> </list>
<p>
Own Id: OTP-13626</p>
</item>
@@ -106,7 +211,7 @@
<item>
<p>
Fix various binary construction inconsistencies for hipe
- compiled code. <list> <item>Passing bad field sizes to
+ compiled code.</p> <list> <item>Passing bad field sizes to
binary constructions would throw <c>badarith</c> rather
than <c>badarg</c>. Worse, in guards, when the unit size
of the field was 1, the exception would leak rather than
@@ -126,7 +231,7 @@
missing check for unit size match when inserting a
binary. For example, a faulty expression like
<c>&lt;&lt;&lt;&lt;1:7&gt;&gt;/binary&gt;&gt;</c> would
- succeed.</item> </list></p>
+ succeed.</item> </list>
<p>
Own Id: OTP-13272</p>
</item>
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
index 224aacd8d7..3386523206 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -763,32 +763,10 @@ trans_fun([{test,bs_test_unit,{f,Lbl},[Ms,Unit]}|
[MsVar], [], Env, Instructions);
trans_fun([{test,bs_match_string,{f,Lbl},[Ms,BitSize,Bin]}|
Instructions], Env) ->
- True = mk_label(new),
- FalseLabName = map_label(Lbl),
- TrueLabName = hipe_icode:label_name(True),
+ %% the current match buffer
MsVar = mk_var(Ms),
- TmpVar = mk_var(new),
- ByteSize = BitSize div 8,
- ExtraBits = BitSize rem 8,
- WordSize = hipe_rtl_arch:word_size(),
- if ExtraBits =:= 0 ->
- trans_op_call({hipe_bs_primop,{bs_match_string,Bin,ByteSize}}, Lbl,
- [MsVar], [MsVar], Env, Instructions);
- BitSize =< ((WordSize * 8) - 5) ->
- <<Int:BitSize, _/bits>> = Bin,
- {I1,Env1} = trans_one_op_call({hipe_bs_primop,{bs_get_integer,BitSize,0}}, Lbl,
- [MsVar], [TmpVar, MsVar], Env),
- I2 = hipe_icode:mk_type([TmpVar], {integer,Int}, TrueLabName, FalseLabName),
- I1 ++ [I2,True] ++ trans_fun(Instructions, Env1);
- true ->
- <<RealBin:ByteSize/binary, Int:ExtraBits, _/bits>> = Bin,
- {I1,Env1} = trans_one_op_call({hipe_bs_primop,{bs_match_string,RealBin,ByteSize}}, Lbl,
- [MsVar], [MsVar], Env),
- {I2,Env2} = trans_one_op_call({hipe_bs_primop,{bs_get_integer,ExtraBits,0}}, Lbl,
- [MsVar], [TmpVar, MsVar], Env1),
- I3 = hipe_icode:mk_type([TmpVar], {integer,Int}, TrueLabName, FalseLabName),
- I1 ++ I2 ++ [I3,True] ++ trans_fun(Instructions, Env2)
- end;
+ Primop = {hipe_bs_primop, {bs_match_string, Bin, BitSize}},
+ trans_op_call(Primop, Lbl, [MsVar], [MsVar], Env, Instructions);
trans_fun([{bs_context_to_binary,Var}|Instructions], Env) ->
%% the current match buffer
IVars = [trans_arg(Var)],
diff --git a/lib/hipe/icode/hipe_icode.erl b/lib/hipe/icode/hipe_icode.erl
index 78508dff22..13a0bf6141 100644
--- a/lib/hipe/icode/hipe_icode.erl
+++ b/lib/hipe/icode/hipe_icode.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
+%% 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.
diff --git a/lib/hipe/icode/hipe_icode_call_elim.erl b/lib/hipe/icode/hipe_icode_call_elim.erl
index 6a22133962..71c709a7d1 100644
--- a/lib/hipe/icode/hipe_icode_call_elim.erl
+++ b/lib/hipe/icode/hipe_icode_call_elim.erl
@@ -46,7 +46,8 @@ cfg(IcodeSSA) ->
-spec elim_insn(icode_instr()) -> icode_instr().
elim_insn(Insn=#icode_call{'fun'={_,_,_}=MFA, args=Args, type=remote,
dstlist=[Dst=#icode_variable{
- annotation={type_anno, RetType, _}}]}) ->
+ annotation={type_anno, RetType, _}}],
+ continuation=[], fail_label=[]}) ->
Opaques = 'universe',
case erl_types:t_is_singleton(RetType, Opaques) of
true ->
diff --git a/lib/hipe/icode/hipe_icode_primops.erl b/lib/hipe/icode/hipe_icode_primops.erl
index cee37b6a57..2a141c514e 100644
--- a/lib/hipe/icode/hipe_icode_primops.erl
+++ b/lib/hipe/icode/hipe_icode_primops.erl
@@ -287,8 +287,8 @@ pp(Dev, Op) ->
io:format(Dev, "bs_start_match<~w>", [Max]);
{{bs_start_match, Type}, Max} ->
io:format(Dev, "bs_start_match<~w,~w>", [Type,Max]);
- {bs_match_string, String, SizeInBytes} ->
- io:format(Dev, "bs_match_string<~w, ~w>", [String, SizeInBytes]);
+ {bs_match_string, String, SizeInBits} ->
+ io:format(Dev, "bs_match_string<~w, ~w>", [String, SizeInBits]);
{bs_get_integer, Size, Flags} ->
io:format(Dev, "bs_get_integer<~w, ~w>", [Size, Flags]);
{bs_get_float, Size, Flags} ->
@@ -596,10 +596,10 @@ type(Primop, Args) ->
erl_types:t_subtract(Type, erl_types:t_matchstate()),
erl_types:t_matchstate_slot(
erl_types:t_inf(Type, erl_types:t_matchstate()), 0));
- {hipe_bs_primop, {bs_match_string,_,Bytes}} ->
+ {hipe_bs_primop, {bs_match_string,_,Bits}} ->
[MatchState] = Args,
BinType = erl_types:t_matchstate_present(MatchState),
- NewBinType = match_bin(erl_types:t_bitstr(0, Bytes*8), BinType),
+ NewBinType = match_bin(erl_types:t_bitstr(0, Bits), BinType),
erl_types:t_matchstate_update_present(NewBinType, MatchState);
{hipe_bs_primop, {bs_test_unit,Unit}} ->
[MatchState] = Args,
diff --git a/lib/hipe/llvm/hipe_llvm_main.erl b/lib/hipe/llvm/hipe_llvm_main.erl
index 476d6fb49c..7f70826046 100644
--- a/lib/hipe/llvm/hipe_llvm_main.erl
+++ b/lib/hipe/llvm/hipe_llvm_main.erl
@@ -84,7 +84,7 @@ compile_with_llvm(FunName, Arity, LLVMCode, Options, UseBuffer) ->
__ = file:close(File_llvm),
%% Invoke LLVM compiler tools to produce an object file
llvm_opt(Dir, Filename, Options),
- llvm_llc(Dir, Filename, Options),
+ llvm_llc(Dir, Filename, Ver, Options),
compile(Dir, Filename, "gcc"), %%FIXME: use llc -filetype=obj and skip this!
{ok, Dir, Dir ++ Filename ++ ".o"}.
@@ -103,12 +103,14 @@ llvm_opt(Dir, Filename, Options) ->
%% @doc Invoke llc tool to compile the bitcode to object file
%% (_name.bc -> _name.o).
-llvm_llc(Dir, Filename, Options) ->
+llvm_llc(Dir, Filename, Ver, Options) ->
Source = Dir ++ Filename ++ ".bc",
OptLevel = trans_optlev_flag(llc, Options),
+ VerFlags = llc_ver_flags(Ver),
Align = find_stack_alignment(),
LlcFlags = [OptLevel, "-code-model=medium", "-stack-alignment=" ++ Align
- , "-tailcallopt", "-filetype=asm"], %%FIXME
+ , "-tailcallopt", "-filetype=asm" %FIXME
+ | VerFlags],
Command = "llc " ++ fix_opts(LlcFlags) ++ " " ++ Source,
%% io:format("LLC: ~s~n", [Command]),
case os:cmd(Command) of
@@ -153,6 +155,12 @@ trans_optlev_flag(Tool, Options) ->
undefined -> "-O2"
end.
+llc_ver_flags(Ver = {_, _}) when Ver >= {3,9} ->
+ %% Works around a bug in the x86-call-frame-opt pass (as of LLVM 3.9) that
+ %% break the garbage collection stack descriptors.
+ ["-no-x86-call-frame-opt"];
+llc_ver_flags({_, _}) -> [].
+
%%------------------------------------------------------------------------------
%% Functions to manage Relocations
%%------------------------------------------------------------------------------
diff --git a/lib/hipe/llvm/hipe_rtl_to_llvm.erl b/lib/hipe/llvm/hipe_rtl_to_llvm.erl
index 66b2e10fb8..2179f7f765 100644
--- a/lib/hipe/llvm/hipe_rtl_to_llvm.erl
+++ b/lib/hipe/llvm/hipe_rtl_to_llvm.erl
@@ -10,7 +10,8 @@
-include("../rtl/hipe_literals.hrl").
-include("hipe_llvm_arch.hrl").
--define(WORD_WIDTH, (?bytes_to_bits(hipe_rtl_arch:word_size()))).
+-define(BITS_IN_WORD, (?bytes_to_bits(hipe_rtl_arch:word_size()))).
+-define(BITS_IN_BYTE, (?bytes_to_bits(1))).
-define(BRANCH_META_TAKEN, "0").
-define(BRANCH_META_NOT_TAKEN, "1").
-define(FIRST_FREE_META_NO, 2).
@@ -95,9 +96,9 @@ do_alloca_stack([], _, _, Acc) ->
Acc;
do_alloca_stack([D|Ds], Params, Roots, Acc) ->
{Name, _I} = trans_dst(D),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
- ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)),
case hipe_rtl:is_var(D) of
true ->
Num = hipe_rtl:var_index(D),
@@ -233,7 +234,7 @@ trans_alu(I, Relocs) ->
{Src1, I1} = trans_src(hipe_rtl:alu_src1(I)),
{Src2, I2} = trans_src(hipe_rtl:alu_src2(I)),
Op = trans_op(hipe_rtl:alu_op(I)),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
I3 = hipe_llvm:mk_operation(TmpDst, Op, WordTy, Src1, Src2, []),
I4 = store_stack_dst(TmpDst, RtlDst),
{[I4, I3, I2, I1], Relocs}.
@@ -258,7 +259,7 @@ trans_alub_overflow(I, Sign, Relocs) ->
TmpDst = mk_temp(),
Name = trans_alub_op(I, Sign),
NewRelocs = relocs_store(Name, {call, {llvm, Name, 2}}, Relocs),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
ReturnType = hipe_llvm:mk_struct([WordTy, hipe_llvm:mk_int(1)]),
T1 = mk_temp(),
I3 = hipe_llvm:mk_call(T1, false, [], [], ReturnType, "@" ++ Name,
@@ -320,7 +321,7 @@ trans_alub_no_overflow(I, Relocs) ->
{Dst, I2} = trans_src(hipe_rtl:alub_dst(I)),
Cond = trans_rel_op(hipe_rtl:alub_cond(I)),
T3 = mk_temp(),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
I5 = hipe_llvm:mk_icmp(T3, Cond, WordTy, Dst, "0"),
%% br
Metadata = branch_metadata(hipe_rtl:alub_pred(I)),
@@ -338,7 +339,7 @@ trans_branch(I, Relocs) ->
Cond = trans_rel_op(hipe_rtl:branch_cond(I)),
%% icmp
T1 = mk_temp(),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
I3 = hipe_llvm:mk_icmp(T1, Cond, WordTy, Src1, Src2),
%% br
True_label = mk_jump_label(hipe_rtl:branch_true_label(I)),
@@ -366,7 +367,7 @@ trans_call(I, Relocs) ->
{Name, I3, Relocs2} =
trans_call_name(RtlCallName, Relocs1, CallArgs, FinalArgs),
T1 = mk_temp(),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
I4 =
case hipe_rtl:call_fail(I) of
@@ -450,7 +451,7 @@ trans_call_name(RtlCallName, Relocs, CallArgs, FinalArgs) ->
%% order to make the call
TT1 = mk_temp(),
{RegName, II1} = trans_src(Reg),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
II2 =
hipe_llvm:mk_conversion(TT1, inttoptr, WordTy, RegName, WordTyPtr),
@@ -503,7 +504,7 @@ trans_enter(I, Relocs) ->
{Name, I2, NewRelocs} =
trans_call_name(hipe_rtl:enter_fun(I), Relocs, CallArgs, FinalArgs),
T1 = mk_temp(),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
I3 = hipe_llvm:mk_call(T1, true, "cc 11", [], FunRetTy, Name, FinalArgs, []),
I4 = hipe_llvm:mk_ret([{FunRetTy, T1}]),
@@ -518,7 +519,7 @@ trans_fconv(I, Relocs) ->
TmpDst = mk_temp(),
{Src, I1} = trans_float_src(hipe_rtl:fconv_src(I)),
FloatTy = hipe_llvm:mk_double(),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
I2 = hipe_llvm:mk_conversion(TmpDst, sitofp, WordTy, Src, FloatTy),
I3 = store_float_stack(TmpDst, RtlDst),
{[I3, I2, I1], Relocs}.
@@ -538,7 +539,7 @@ trans_fload(I, Relocs) ->
{Src, I1} = trans_float_src(RtlSrc),
{Offset, I2} = trans_float_src(_Offset),
T1 = mk_temp(),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
FloatTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_double()),
I3 = hipe_llvm:mk_operation(T1, add, WordTy, Src, Offset, []),
T2 = mk_temp(),
@@ -619,7 +620,7 @@ trans_fstore(I, Relocs) ->
trans_fstore_reg(I, Relocs) ->
{Base, I0} = trans_reg(hipe_rtl:fstore_base(I), dst),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
FloatTy = hipe_llvm:mk_double(),
FloatTyPtr = hipe_llvm:mk_pointer(FloatTy),
@@ -659,7 +660,7 @@ trans_load(I, Relocs) ->
{Src, I1} = trans_src(hipe_rtl:load_src(I)),
{Offset, I2} = trans_src(hipe_rtl:load_offset(I)),
T1 = mk_temp(),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
I3 = hipe_llvm:mk_operation(T1, add, WordTy, Src, Offset, []),
%%----------------------------------------------------------------
@@ -737,7 +738,7 @@ trans_move(I, Relocs) ->
%% return
%%
trans_return(I, Relocs) ->
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
{VarRet, I1} =
case hipe_rtl:return_varlist(I) of
[] ->
@@ -777,7 +778,7 @@ trans_store(I, Relocs) ->
{Offset, I2} = trans_src(hipe_rtl:store_offset(I)),
{Value, I3} = trans_src(hipe_rtl:store_src(I)),
T1 = mk_temp(),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
I4 = hipe_llvm:mk_operation(T1, add, WordTy, Base, Offset, []),
I5 =
@@ -811,14 +812,14 @@ trans_switch(I, Relocs, Data) ->
JumpLabels = [mk_jump_label(L) || L <- Labels],
SortOrder = hipe_rtl:switch_sort_order(I),
NrLabels = length(Labels),
- ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)),
TableType = hipe_llvm:mk_array(NrLabels, ByteTyPtr),
TableTypeP = hipe_llvm:mk_pointer(TableType),
TypedJumpLabels = [{hipe_llvm:mk_label_type(), X} || X <- JumpLabels],
T1 = mk_temp(),
{Src2, []} = trans_dst(RtlSrc),
TableName = "table_" ++ tl(Src2),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
I2 = hipe_llvm:mk_getelementptr(T1, TableTypeP, "@"++TableName,
[{WordTy, "0"}, {WordTy, Src}], false),
T2 = mk_temp(),
@@ -933,7 +934,7 @@ create_fail_blocks(Label, FailLabels, Acc) ->
false ->
Acc;
{value, {Label, FailLabel, SpAdj}, RestFailLabels} ->
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
I1 = hipe_llvm:mk_label(FailLabel),
LP = hipe_llvm:mk_landingpad(),
I2 =
@@ -962,7 +963,7 @@ create_fail_blocks(Label, FailLabels, Acc) ->
%% @doc Convert RTL argument list to LLVM argument list.
trans_args(ArgList) ->
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
MakeArg =
fun(A) ->
{Name, I1} = trans_src(A),
@@ -972,13 +973,13 @@ trans_args(ArgList) ->
%% @doc Convert a list of Precoloured registers to LLVM argument list.
fix_reg_args(ArgList) ->
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
[{WordTy, A} || A <- ArgList].
%% @doc Load Precoloured registers.
load_fixed_regs(RegList) ->
Names = [mk_temp_reg(R) || R <- RegList],
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
Fun1 =
fun (X, Y) ->
@@ -991,7 +992,7 @@ load_fixed_regs(RegList) ->
store_fixed_regs(RegList, Name) ->
Names = [mk_temp_reg(R) || R <- RegList],
Indexes = lists:seq(0, erlang:length(RegList) - 1),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
Fun1 =
@@ -1060,7 +1061,7 @@ mk_temp_reg(Name) ->
store_stack_dst(TempDst, Dst) ->
{Dst2, II1} = trans_dst(Dst),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
II2 = hipe_llvm:mk_store(WordTy, TempDst, WordTyPtr, Dst2, [], [], false),
[II2, II1].
@@ -1078,7 +1079,7 @@ trans_float_src(Src) ->
Name = "@DL" ++ integer_to_list(hipe_rtl:const_label_label(Src)),
T1 = mk_temp(),
%% XXX: Hardcoded offset
- ByteTy = hipe_llvm:mk_int(8),
+ ByteTy = hipe_llvm:mk_int(?BITS_IN_BYTE),
ByteTyPtr = hipe_llvm:mk_pointer(ByteTy),
I1 = hipe_llvm:mk_getelementptr(T1, ByteTyPtr, Name,
[{ByteTy, integer_to_list(?FLOAT_OFFSET)}], true),
@@ -1094,7 +1095,7 @@ trans_float_src(Src) ->
end.
trans_src(A) ->
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
case hipe_rtl:is_imm(A) of
true ->
@@ -1197,7 +1198,7 @@ map_precoloured_reg(Index) ->
fix_reg_dst(Register) ->
case Register of
{Name, Offset} -> %% Case of %fcalls, %hplim
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
pointer_from_reg(Name, WordTy, Offset);
Name -> %% Case of %p and %hp
{Name, []}
@@ -1205,7 +1206,7 @@ fix_reg_dst(Register) ->
%% @doc Load precoloured src register.
fix_reg_src(Register) ->
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
case Register of
{Name, Offset} -> %% Case of %fcalls, %hplim
@@ -1327,10 +1328,10 @@ insn_dst(I) ->
llvm_type_from_size(Size) ->
case Size of
- byte -> hipe_llvm:mk_int(8);
+ byte -> hipe_llvm:mk_int(?BITS_IN_BYTE);
int16 -> hipe_llvm:mk_int(16);
int32 -> hipe_llvm:mk_int(32);
- word -> hipe_llvm:mk_int(64)
+ word -> hipe_llvm:mk_int(?BITS_IN_WORD)
end.
%% @doc Create definition for the compiled function. The parameters that are
@@ -1360,13 +1361,13 @@ create_function_definition(Fun, Params, Code, LocalVars) ->
lists:flatten([EntryLabel, ExceptionSync, I2, LocalVars, StoredParams, I3]),
Final_Code = EntryBlock ++ Code,
FunctionOptions = [nounwind, noredzone, list_to_atom("gc \"erlang\"")],
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
hipe_llvm:mk_fun_def([], [], "cc 11", [], FunRetTy, FunctionName, Args,
FunctionOptions, [], Final_Code).
header_params(Params) ->
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
[{WordTy, "%v" ++ integer_to_list(hipe_rtl:var_index(P))} || P <- Params].
store_params(Params) ->
@@ -1375,7 +1376,7 @@ store_params(Params) ->
Index = hipe_rtl:var_index(X),
{Name, _} = trans_dst(X),
ParamName = "%v" ++ integer_to_list(Index),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
hipe_llvm:mk_store(WordTy, ParamName, WordTyPtr, Name, [], [], false)
end,
@@ -1392,11 +1393,11 @@ fixed_registers() ->
end.
header_regs(Registers) ->
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
[{WordTy, "%" ++ X ++ "_in"} || X <- Registers].
load_regs(Registers) ->
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
WordTyPtr = hipe_llvm:mk_pointer(WordTy),
Fun1 =
fun(X) ->
@@ -1496,28 +1497,30 @@ seperate_relocs([R|Rs], CallAcc, AtomAcc, ClosureAcc, LabelAcc, JmpTableAcc) ->
%% @doc External declaration of an atom.
declare_atom({AtomName, _}) ->
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
- hipe_llvm:mk_const_decl("@" ++ AtomName, "external constant", WordTy, "").
+ %% The type has to be byte, or a backend might assume the constant is aligned
+ %% and incorrectly optimise away type tests
+ ByteTy = hipe_llvm:mk_int(?BITS_IN_BYTE),
+ hipe_llvm:mk_const_decl("@" ++ AtomName, "external constant", ByteTy, "").
%% @doc Creation of local variable for an atom.
load_atom({AtomName, _}) ->
Dst = "%" ++ AtomName ++ "_var",
Name = "@" ++ AtomName,
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
- WordTyPtr = hipe_llvm:mk_pointer(WordTy),
- hipe_llvm:mk_conversion(Dst, ptrtoint, WordTyPtr, Name, WordTy).
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)),
+ hipe_llvm:mk_conversion(Dst, ptrtoint, ByteTyPtr, Name, WordTy).
%% @doc External declaration of a closure.
declare_closure({ClosureName, _})->
- ByteTy = hipe_llvm:mk_int(8),
+ ByteTy = hipe_llvm:mk_int(?BITS_IN_BYTE),
hipe_llvm:mk_const_decl("@" ++ ClosureName, "external constant", ByteTy, "").
%% @doc Creation of local variable for a closure.
load_closure({ClosureName, _})->
Dst = "%" ++ ClosureName ++ "_var",
Name = "@" ++ ClosureName,
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
- ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)),
hipe_llvm:mk_conversion(Dst, ptrtoint, ByteTyPtr, Name, WordTy).
%% @doc Declaration of a local variable for a switch jump table.
@@ -1548,7 +1551,7 @@ declare_closure_labels(ClosureLabels, Relocs, Fun) ->
List3 = string:join(List2, ",\n"),
List4 = "[\n" ++ List3 ++ "\n]\n",
NrLabels = length(LabelList),
- ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)),
TableType = hipe_llvm:mk_array(NrLabels, ByteTyPtr),
ConstDecl =
hipe_llvm:mk_const_decl("@table_closures", "constant", TableType, List4),
@@ -1563,7 +1566,7 @@ is_external_call(_, _) -> true.
call_to_decl({Name, {call, MFA}}) ->
{M, _F, A} = MFA,
CConv = "cc 11",
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
{Type, Args} =
case M of
@@ -1578,14 +1581,14 @@ call_to_decl({Name, {call, MFA}}) ->
%% @doc These functions are always declared, even if not used.
fixed_fun_decl() ->
- ByteTy = hipe_llvm:mk_int(8),
+ ByteTy = hipe_llvm:mk_int(?BITS_IN_BYTE),
ByteTyPtr = hipe_llvm:mk_pointer(ByteTy),
LandPad = hipe_llvm:mk_fun_decl([], [], [], [], hipe_llvm:mk_int(32),
"@__gcc_personality_v0", [hipe_llvm:mk_int(32), hipe_llvm:mk_int(64),
ByteTyPtr, ByteTyPtr], []),
GCROOTDecl = hipe_llvm:mk_fun_decl([], [], [], [], hipe_llvm:mk_void(),
"@llvm.gcroot", [hipe_llvm:mk_pointer(ByteTyPtr), ByteTyPtr], []),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
FixPinnedRegs = hipe_llvm:mk_fun_decl([], [], [], [], FunRetTy,
"@hipe_bifs.llvm_fix_pinned_regs.0", [], []),
@@ -1599,7 +1602,7 @@ fixed_fun_decl() ->
%% values, add the offset and convert them again to pointers.
declare_constant(Label) ->
Name = "@DL" ++ integer_to_list(Label),
- ByteTy = hipe_llvm:mk_int(8),
+ ByteTy = hipe_llvm:mk_int(?BITS_IN_BYTE),
hipe_llvm:mk_const_decl(Name, "external constant", ByteTy, "").
%% @doc Load a constant is achieved by converting a pointer to an integer of
@@ -1607,8 +1610,8 @@ declare_constant(Label) ->
load_constant(Label) ->
Dst = "%DL" ++ integer_to_list(Label) ++ "_var",
Name = "@DL" ++ integer_to_list(Label),
- WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
- ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ WordTy = hipe_llvm:mk_int(?BITS_IN_WORD),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)),
hipe_llvm:mk_conversion(Dst, ptrtoint, ByteTyPtr, Name, WordTy).
%% @doc Store external constants and calls to dictionary.
diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl
index 6c525dd143..cc0148a80e 100644
--- a/lib/hipe/main/hipe.erl
+++ b/lib/hipe/main/hipe.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
+%% 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.
@@ -635,44 +635,51 @@ run_compiler(Name, DisasmFun, IcodeFun, Opts0) ->
Opts = expand_basic_options(Opts0 ++ ?COMPILE_DEFAULTS),
?when_option(verbose, Opts, ?debug_msg("Compiling: ~p\n",[Name])),
?option_start_time("Compile", Opts),
- Res = run_compiler_1(DisasmFun, IcodeFun, Opts),
+ Res = run_compiler_1(Name, DisasmFun, IcodeFun, Opts),
?option_stop_time("Compile", Opts),
Res.
-run_compiler_1(DisasmFun, IcodeFun, Options) ->
+run_compiler_1(Name, DisasmFun, IcodeFun, Options) ->
Parent = self(),
{trap_exit,TrapExit} = process_info(Parent, trap_exit),
%% Spawn a compilation process CompProc. In case this process gets
%% killed, the trap_exit flag is restored to that of the Parent process.
process_flag(trap_exit, true),
- CompProc = spawn_link(fun () ->
- %% Compiler process
- set_architecture(Options),
- pre_init(Options),
- %% The full option expansion is not done
- %% until the DisasmFun returns.
- {Code, CompOpts} = DisasmFun(Options),
- Opts0 = expand_options(Options ++ CompOpts,
- get(hipe_target_arch)),
- Opts =
- case proplists:get_bool(to_llvm, Opts0) andalso
- not llvm_support_available() of
- true ->
- ?error_msg("No LLVM version 3.4 or greater "
- "found in $PATH; aborting "
- "native code compilation.\n", []),
- ?EXIT(cant_find_required_llvm_version);
- false ->
- Opts0
- end,
- check_options(Opts),
- ?when_option(verbose, Options,
- ?debug_msg("Options: ~p.\n",[Opts])),
- init(Opts),
- {Icode, WholeModule} = IcodeFun(Code, Opts),
- CompRes = compile_finish(Icode, WholeModule, Opts),
- compiler_return(CompRes, Parent)
- end),
+ CompProc =
+ spawn_link(
+ fun () ->
+ try
+ %% Compiler process
+ set_architecture(Options),
+ pre_init(Options),
+ %% The full option expansion is not done
+ %% until the DisasmFun returns.
+ {Code, CompOpts} = DisasmFun(Options),
+ Opts0 = expand_options(Options ++ CompOpts,
+ get(hipe_target_arch)),
+ Opts =
+ case proplists:get_bool(to_llvm, Opts0) andalso
+ not llvm_support_available() of
+ true ->
+ ?error_msg("No LLVM version 3.4 or greater "
+ "found in $PATH; aborting "
+ "native code compilation.\n", []),
+ ?EXIT(cant_find_required_llvm_version);
+ false ->
+ Opts0
+ end,
+ check_options(Opts),
+ ?when_option(verbose, Options,
+ ?debug_msg("Options: ~p.\n",[Opts])),
+ init(Opts),
+ {Icode, WholeModule} = IcodeFun(Code, Opts),
+ CompRes = compile_finish(Icode, WholeModule, Opts),
+ compiler_return(CompRes, Parent)
+ catch error:Error ->
+ print_crash_message(Name, Error),
+ exit(Error)
+ end
+ end),
Timeout = case proplists:get_value(timeout, Options) of
N when is_integer(N), N >= 0 -> N;
undefined -> ?DEFAULT_TIMEOUT;
@@ -691,7 +698,7 @@ run_compiler_1(DisasmFun, IcodeFun, Options) ->
exit(CompProc, kill),
receive {'EXIT', CompProc, _} -> ok end,
flush(),
- ?error_msg("ERROR: Compilation timed out.\n",[]),
+ ?error_msg("ERROR: Compilation of ~w timed out.\n",[Name]),
exit(timed_out)
end,
Result = receive {CompProc, Res} -> Res end,
@@ -844,11 +851,25 @@ finalize_fun_sequential({MFA, Icode}, Opts, Servers) ->
catch
error:Error ->
?when_option(verbose, Opts, ?debug_untagged_msg("\n", [])),
- ErrorInfo = {Error, erlang:get_stacktrace()},
- ?error_msg("ERROR: ~p~n", [ErrorInfo]),
- ?EXIT(ErrorInfo)
+ print_crash_message(MFA, Error),
+ exit(Error)
end.
+print_crash_message(What, Error) ->
+ StackFun = fun(_,_,_) -> false end,
+ FormatFun = fun (Term, _) -> io_lib:format("~p", [Term]) end,
+ StackTrace = lib:format_stacktrace(1, erlang:get_stacktrace(),
+ StackFun, FormatFun),
+ WhatS = case What of
+ {M,F,A} -> io_lib:format("~w:~w/~w", [M,F,A]);
+ Mod -> io_lib:format("~w", [Mod])
+ end,
+ ?error_msg("INTERNAL ERROR~n"
+ "while compiling ~s~n"
+ "crash reason: ~p~n"
+ "~s~n",
+ [WhatS, Error, StackTrace]).
+
pp_server_start(Opts) ->
set_architecture(Opts),
garbage_collect(),
diff --git a/lib/hipe/main/hipe.hrl.src b/lib/hipe/main/hipe.hrl.src
index 53b59f88f0..b001743038 100644
--- a/lib/hipe/main/hipe.hrl.src
+++ b/lib/hipe/main/hipe.hrl.src
@@ -1,8 +1,8 @@
-%% -*- erlang-indent-level: 2 -*-
+%% -*- mode: erlang; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
+%% 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.
@@ -70,20 +70,24 @@
code_server:info_msg(?MSGTAG ++ Msg, Args)).
-define(untagged_msg(Msg, Args),
code_server:info_msg(Msg, Args)).
+-define(untagged_error_msg(Msg, Args),
+ code_server:error_msg(Msg, Args)).
-else.
-define(msg(Msg, Args),
io:format(?MSGTAG ++ Msg, Args)).
-define(untagged_msg(Msg, Args),
io:format(Msg, Args)).
+-define(untagged_error_msg(Msg, Args),
+ io:format(Msg, Args)).
-endif.
%%
%% Define error and warning messages.
%%
-define(error_msg(Msg, Args),
- code_server:error_msg(?MSGTAG ++
+ ?untagged_error_msg(?MSGTAG ++
"Error: [~s:~w]: " ++ Msg,
- [?MODULE,?LINE|Args])).
+ [?MODULE,?LINE|Args])).
-define(WARNING_MSG(Msg, Args),
?msg("Warning: [~s:~w]: " ++ Msg, [?MODULE,?LINE|Args])).
diff --git a/lib/hipe/main/hipe_main.erl b/lib/hipe/main/hipe_main.erl
index 4b89feb48a..8737560ad5 100644
--- a/lib/hipe/main/hipe_main.erl
+++ b/lib/hipe/main/hipe_main.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
+%% 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.
diff --git a/lib/hipe/ppc/hipe_ppc.erl b/lib/hipe/ppc/hipe_ppc.erl
index 0fa96162f6..380e791bc1 100644
--- a/lib/hipe/ppc/hipe_ppc.erl
+++ b/lib/hipe/ppc/hipe_ppc.erl
@@ -167,8 +167,10 @@ temp_is_precoloured(#ppc_temp{reg=Reg,type=Type}) ->
_ -> hipe_ppc_registers:is_precoloured_gpr(Reg)
end.
-mk_simm16(Value) -> #ppc_simm16{value=Value}.
-mk_uimm16(Value) -> #ppc_uimm16{value=Value}.
+mk_simm16(Value) when Value >= -(1 bsl 15), Value < (1 bsl 15) ->
+ #ppc_simm16{value=Value}.
+mk_uimm16(Value) when Value >= 0, Value < (1 bsl 16) ->
+ #ppc_uimm16{value=Value}.
mk_mfa(M, F, A) -> #ppc_mfa{m=M, f=F, a=A}.
@@ -240,7 +242,11 @@ mk_li(Dst, Value, Tail) -> % Dst can be R0
Value =< 16#7FFFFFFF ->
mk_li32(Dst, R0, Value, Tail);
true ->
- Highest = (Value bsr 48), % Value@highest
+ Highest = case (Value bsr 48) of % Value@highest
+ TopBitSet when TopBitSet >= (1 bsl 15) ->
+ TopBitSet - (1 bsl 16); % encoder needs it to be negative
+ FitsSimm16 -> FitsSimm16
+ end,
Higher = (Value bsr 32) band 16#FFFF, % Value@higher
High = (Value bsr 16) band 16#FFFF, % Value@h
Low = Value band 16#FFFF, % Value@l
diff --git a/lib/hipe/ppc/hipe_ppc_assemble.erl b/lib/hipe/ppc/hipe_ppc_assemble.erl
index ff9da01b11..d89ff6235c 100644
--- a/lib/hipe/ppc/hipe_ppc_assemble.erl
+++ b/lib/hipe/ppc/hipe_ppc_assemble.erl
@@ -175,7 +175,8 @@ do_slwi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 32 ->
{Dst, Src1, {sh,N}, {mb,0}, {me,31-N}}.
do_srwi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 32 ->
- {Dst, Src1, {sh,32-N}, {mb,N}, {me,31}}.
+ %% SH should be 0 (not 32) when N is 0
+ {Dst, Src1, {sh,(32-N) band 31}, {mb,N}, {me,31}}.
do_srawi_src2({uimm,N}) when is_integer(N), 0 =< N, N < 32 -> {sh,N}.
@@ -184,7 +185,8 @@ do_sldi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 64 ->
{Dst, Src1, {sh6,N}, {me6,63-N}}.
do_srdi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 64 ->
- {Dst, Src1, {sh6,64-N}, {mb6,N}}.
+ %% SH should be 0 (not 64) when N is 0
+ {Dst, Src1, {sh6,(64-N) band 63}, {mb6,N}}.
do_sradi_src2({uimm,N}) when is_integer(N), 0 =< N, N < 64 -> {sh6,N}.
@@ -246,6 +248,7 @@ do_load(I) ->
case LdOp of
'ld' -> do_disp_ds(Disp);
'ldu' -> do_disp_ds(Disp);
+ 'lwa' -> do_disp_ds(Disp);
_ -> do_disp(Disp)
end,
NewBase = do_reg(Base),
diff --git a/lib/hipe/rtl/hipe_rtl_binary.erl b/lib/hipe/rtl/hipe_rtl_binary.erl
index fb9c0c196d..9b400f4c93 100644
--- a/lib/hipe/rtl/hipe_rtl_binary.erl
+++ b/lib/hipe/rtl/hipe_rtl_binary.erl
@@ -19,7 +19,7 @@
%%% %CopyrightEnd%
%%%
%%%-------------------------------------------------------------------
-%%% File : hipe_rtl_binary_2.erl
+%%% File : hipe_rtl_binary.erl
%%% Author : Per Gustafsson <[email protected]>
%%% Description :
%%%
diff --git a/lib/hipe/rtl/hipe_rtl_binary_match.erl b/lib/hipe/rtl/hipe_rtl_binary_match.erl
index 528672b893..d999cd2743 100644
--- a/lib/hipe/rtl/hipe_rtl_binary_match.erl
+++ b/lib/hipe/rtl/hipe_rtl_binary_match.erl
@@ -270,24 +270,23 @@ gen_rtl({bs_save, Slot}, [NewMs], [Ms], TrueLblName, _FalseLblName) ->
set_field_from_term({matchstate, {saveoffset, Slot}}, Ms, Offset),
hipe_rtl:mk_goto(TrueLblName)];
%% ----- bs_match_string -----
-gen_rtl({bs_match_string, String, ByteSize}, Dst, [Ms],
+gen_rtl({bs_match_string, String, BitSize}, Dst, [Ms],
TrueLblName, FalseLblName) ->
{[Offset, BinSize, Base], Instrs} =
extract_matchstate_vars([offset, binsize, base], Ms),
[SuccessLbl, ALbl, ULbl] = create_lbls(3),
[NewOffset, BitOffset] = create_gcsafe_regs(2),
- Unit = hipe_rtl_arch:word_size() - 1,
- Loops = ByteSize div Unit,
- Init =
+ Unit = (hipe_rtl_arch:word_size() - 1) * ?BYTE_SIZE,
+ Init =
[Instrs,
opt_update_ms(Dst, Ms),
- check_size(Offset, hipe_rtl:mk_imm(ByteSize*?BYTE_SIZE), BinSize,
+ check_size(Offset, hipe_rtl:mk_imm(BitSize), BinSize,
NewOffset, hipe_rtl:label_name(SuccessLbl), FalseLblName),
SuccessLbl],
SplitCode =
[hipe_rtl:mk_alub(BitOffset, Offset, 'and', hipe_rtl:mk_imm(?LOW_BITS), eq,
hipe_rtl:label_name(ALbl), hipe_rtl:label_name(ULbl))],
- Loops = ByteSize div Unit,
+ Loops = BitSize div Unit,
SkipSize = Loops * Unit,
{ACode1, UCode1} =
case Loops of
@@ -297,9 +296,9 @@ gen_rtl({bs_match_string, String, ByteSize}, Dst, [Ms],
create_loops(Loops, Unit, String, Base,
Offset, BitOffset, FalseLblName)
end,
- <<_:SkipSize/binary, RestString/binary>> = String,
+ <<_:SkipSize/bits, RestString/bits>> = String,
{ACode2, UCode2} =
- case ByteSize rem Unit of
+ case BitSize rem Unit of
0 ->
{[], []};
Rem ->
@@ -393,12 +392,12 @@ validate_unicode_retract_c_code(Src, Ms, TrueLblName, FalseLblName) ->
create_loops(Loops, Unit, String, Base, Offset, BitOffset, FalseLblName) ->
[Reg] = create_gcsafe_regs(1),
AlignedFun = fun(Value) ->
- [get_int_to_reg(Reg, Unit*?BYTE_SIZE, Base, Offset, 'srl',
+ [get_int_to_reg(Reg, Unit, Base, Offset, 'srl',
{unsigned, big}),
update_and_test(Reg, Unit, Offset, Value, FalseLblName)]
end,
UnAlignedFun = fun(Value) ->
- [get_unaligned_int_to_reg(Reg, Unit*?BYTE_SIZE,
+ [get_unaligned_int_to_reg(Reg, Unit,
Base, Offset, BitOffset,
'srl', {unsigned, big})|
update_and_test(Reg, Unit, Offset, Value, FalseLblName)]
@@ -406,31 +405,31 @@ create_loops(Loops, Unit, String, Base, Offset, BitOffset, FalseLblName) ->
{create_loops(Loops, Unit, String, AlignedFun),
create_loops(Loops, Unit, String, UnAlignedFun)}.
-create_rests(Rem, String, Base, Offset, BitOffset, FalseLblName) ->
+create_rests(RemBits, String, Base, Offset, BitOffset, FalseLblName) ->
[Reg] = create_gcsafe_regs(1),
AlignedFun = fun(Value) ->
- [get_int_to_reg(Reg, Rem*?BYTE_SIZE, Base, Offset, 'srl',
+ [get_int_to_reg(Reg, RemBits, Base, Offset, 'srl',
{unsigned, big})|
just_test(Reg, Value, FalseLblName)]
end,
UnAlignedFun = fun(Value) ->
- [get_unaligned_int_to_reg(Reg, Rem*?BYTE_SIZE,
+ [get_unaligned_int_to_reg(Reg, RemBits,
Base, Offset, BitOffset,
'srl', {unsigned, big})|
just_test(Reg, Value, FalseLblName)]
end,
- {create_loops(1, Rem, String, AlignedFun),
- create_loops(1, Rem, String, UnAlignedFun)}.
+ {create_loops(1, RemBits, String, AlignedFun),
+ create_loops(1, RemBits, String, UnAlignedFun)}.
create_loops(0, _Unit, _String, _IntFun) ->
[];
create_loops(N, Unit, String, IntFun) ->
- {Value, RestString} = get_value(Unit,String),
+ {Value, RestString} = get_value(Unit, String),
[IntFun(Value),
create_loops(N-1, Unit, RestString, IntFun)].
update_and_test(Reg, Unit, Offset, Value, FalseLblName) ->
- [add_to_offset(Offset, Offset, hipe_rtl:mk_imm(Unit*?BYTE_SIZE), FalseLblName),
+ [add_to_offset(Offset, Offset, hipe_rtl:mk_imm(Unit), FalseLblName),
just_test(Reg, Value, FalseLblName)].
just_test(Reg, Value, FalseLblName) ->
@@ -439,8 +438,8 @@ just_test(Reg, Value, FalseLblName) ->
hipe_rtl:label_name(ContLbl), FalseLblName),
ContLbl].
-get_value(N,String) ->
- <<I:N/integer-unit:8, Rest/binary>> = String,
+get_value(N, String) ->
+ <<I:N, Rest/bits>> = String,
{I, Rest}.
make_int_gc_code(I) when is_integer(I) ->
diff --git a/lib/hipe/sparc/hipe_rtl_to_sparc.erl b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
index eef5ba8d96..f9c043eafe 100644
--- a/lib/hipe/sparc/hipe_rtl_to_sparc.erl
+++ b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
@@ -750,13 +750,25 @@ xaluop_commutes(XAluOp) ->
xaluop_is_shift(XAluOp) ->
case XAluOp of
+ 'add' -> false;
+ 'addcc' -> false;
+ 'and' -> false;
+ 'andcc' -> false;
+ 'cmpcc' -> false;
+ 'ldsb' -> false;
+ 'ldub' -> false;
+ 'lduw' -> false;
+ 'or' -> false;
'sll' -> true;
- 'srl' -> true;
+ %% 'sllx' -> true;
+ 'smul' -> false;
'sra' -> true;
- 'sllx' -> true;
- 'srlx' -> true;
- 'srax' -> true;
- _ -> false
+ %% 'srax' -> true;
+ 'srl' -> true;
+ %% 'srlx' -> true;
+ 'sub' -> false;
+ 'subcc' -> false;
+ 'xor' -> false
end.
%%% Convert an extended SPARC AluOp back to a plain AluOp.
@@ -764,9 +776,23 @@ xaluop_is_shift(XAluOp) ->
xaluop_normalise(XAluOp) ->
case XAluOp of
- 'cmp' -> 'sub';
+ 'add' -> 'add';
+ 'addcc' -> 'addcc';
+ 'and' -> 'and';
+ 'andcc' -> 'andcc';
+ %% 'cmp' -> 'sub';
'cmpcc' -> 'subcc';
- _ -> XAluOp
+ 'ldsb' -> 'ldsb';
+ 'ldub' -> 'ldub';
+ 'lduw' -> 'lduw';
+ 'or' -> 'or';
+ 'sll' -> 'sll';
+ 'smul' -> 'smul';
+ 'sra' -> 'sra';
+ 'srl' -> 'srl';
+ 'sub' -> 'sub';
+ 'subcc' -> 'subcc';
+ 'xor' -> 'xor'
end.
%%% Convert an RTL condition code.
diff --git a/lib/hipe/sparc/hipe_sparc_registers.erl b/lib/hipe/sparc/hipe_sparc_registers.erl
index 884215702b..6681a10070 100644
--- a/lib/hipe/sparc/hipe_sparc_registers.erl
+++ b/lib/hipe/sparc/hipe_sparc_registers.erl
@@ -86,6 +86,8 @@
-define(I7, 31).
-define(LAST_PRECOLOURED,31). % must handle both GRP and FPR ranges
+-define(RA, ?O7).
+
-define(ARG0, ?O1).
-define(ARG1, ?O2).
-define(ARG2, ?O3).
@@ -174,7 +176,7 @@ stack_pointer() -> ?STACK_POINTER.
proc_pointer() -> ?PROC_POINTER.
-return_address() -> ?O7.
+return_address() -> ?RA.
g0() -> ?G0.
@@ -283,7 +285,9 @@ call_clobbered() -> % does the RA strip the type or not?
].
tailcall_clobbered() -> % tailcall crapola needs one temp
- [{?TEMP1,tagged},{?TEMP1,untagged}].
+ [{?TEMP1,tagged},{?TEMP1,untagged}
+ ,{?RA,tagged},{?RA,untagged}
+ ].
live_at_return() ->
[{?HEAP_POINTER,untagged},
diff --git a/lib/hipe/test/basic_SUITE_data/basic_num_bif.erl b/lib/hipe/test/basic_SUITE_data/basic_num_bif.erl
new file mode 100644
index 0000000000..807c4b0d0d
--- /dev/null
+++ b/lib/hipe/test/basic_SUITE_data/basic_num_bif.erl
@@ -0,0 +1,217 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%-------------------------------------------------------------------
+%%% File : basic_num_bif.erl
+%%% Description : Taken from the compiler test suite
+%%%-------------------------------------------------------------------
+-module(basic_num_bif).
+
+-export([test/0]).
+
+%% Tests optimization of the BIFs:
+%% abs/1
+%% float/1
+%% float_to_list/1
+%% integer_to_list/1
+%% list_to_float/1
+%% list_to_integer/1
+%% round/1
+%% trunc/1
+
+test() ->
+ Funs = [fun t_abs/0, fun t_float/0,
+ fun t_float_to_list/0, fun t_integer_to_list/0,
+ fun t_list_to_float_safe/0, fun t_list_to_float_risky/0,
+ fun t_list_to_integer/0, fun t_round/0, fun t_trunc/0],
+ lists:foreach(fun (F) -> ok = F() end, Funs).
+
+t_abs() ->
+ %% Floats.
+ 5.5 = abs(5.5),
+ 0.0 = abs(0.0),
+ 100.0 = abs(-100.0),
+ %% Integers.
+ 5 = abs(5),
+ 0 = abs(0),
+ 100 = abs(-100),
+ %% The largest smallnum. OTP-3190.
+ X = (1 bsl 27) - 1,
+ X = abs(X),
+ X = abs(X-1)+1,
+ X = abs(X+1)-1,
+ X = abs(-X),
+ X = abs(-X-1)-1,
+ X = abs(-X+1)+1,
+ %% Bignums.
+ BigNum = 13984792374983749,
+ BigNum = abs(BigNum),
+ BigNum = abs(-BigNum),
+ ok.
+
+t_float() ->
+ 0.0 = float(0),
+ 2.5 = float(2.5),
+ 0.0 = float(0.0),
+ -100.55 = float(-100.55),
+ 42.0 = float(42),
+ -100.0 = float(-100),
+ %% Bignums.
+ 4294967305.0 = float(4294967305),
+ -4294967305.0 = float(-4294967305),
+ %% Extremely big bignums.
+ Big = list_to_integer(lists:duplicate(2000, $1)),
+ {'EXIT', {badarg, _}} = (catch float(Big)),
+ ok.
+
+%% Tests float_to_list/1.
+
+t_float_to_list() ->
+ test_ftl("0.0e+0", 0.0),
+ test_ftl("2.5e+1", 25.0),
+ test_ftl("2.5e+0", 2.5),
+ test_ftl("2.5e-1", 0.25),
+ test_ftl("-3.5e+17", -350.0e15),
+ ok.
+
+test_ftl(Expect, Float) ->
+ %% No on the next line -- we want the line number from t_float_to_list.
+ Expect = remove_zeros(lists:reverse(float_to_list(Float)), []).
+
+%% Removes any non-significant zeros in a floating point number.
+%% Example: 2.500000e+01 -> 2.5e+1
+
+remove_zeros([$+, $e|Rest], [$0, X|Result]) ->
+ remove_zeros([$+, $e|Rest], [X|Result]);
+remove_zeros([$-, $e|Rest], [$0, X|Result]) ->
+ remove_zeros([$-, $e|Rest], [X|Result]);
+remove_zeros([$0, $.|Rest], [$e|Result]) ->
+ remove_zeros(Rest, [$., $0, $e|Result]);
+remove_zeros([$0|Rest], [$e|Result]) ->
+ remove_zeros(Rest, [$e|Result]);
+remove_zeros([Char|Rest], Result) ->
+ remove_zeros(Rest, [Char|Result]);
+remove_zeros([], Result) ->
+ Result.
+
+%% Tests integer_to_list/1.
+
+t_integer_to_list() ->
+ "0" = integer_to_list(0),
+ "42" = integer_to_list(42),
+ "-42" = integer_to_list(-42),
+ "-42" = integer_to_list(-42),
+ "32768" = integer_to_list(32768),
+ "268435455" = integer_to_list(268435455),
+ "-268435455" = integer_to_list(-268435455),
+ "123456932798748738738" = integer_to_list(123456932798748738738),
+ Big_List = lists:duplicate(2000, $1),
+ Big = list_to_integer(Big_List),
+ Big_List = integer_to_list(Big),
+ ok.
+
+%% Tests list_to_float/1.
+
+t_list_to_float_safe() ->
+ 0.0 = list_to_float("0.0"),
+ 0.0 = list_to_float("-0.0"),
+ 0.5 = list_to_float("0.5"),
+ -0.5 = list_to_float("-0.5"),
+ 100.0 = list_to_float("1.0e2"),
+ 127.5 = list_to_float("127.5"),
+ -199.5 = list_to_float("-199.5"),
+ ok.
+
+%% This might crash the emulator...
+%% (Known to crash the Unix version of Erlang 4.4.1)
+
+t_list_to_float_risky() ->
+ Many_Ones = lists:duplicate(25000, $1),
+ _ = list_to_float("2."++Many_Ones),
+ {'EXIT', {badarg, _}} = (catch list_to_float("2"++Many_Ones)),
+ ok.
+
+%% Tests list_to_integer/1.
+
+t_list_to_integer() ->
+ 0 = list_to_integer("0"),
+ 0 = list_to_integer("00"),
+ 0 = list_to_integer("-0"),
+ 1 = list_to_integer("1"),
+ -1 = list_to_integer("-1"),
+ 42 = list_to_integer("42"),
+ -12 = list_to_integer("-12"),
+ 32768 = list_to_integer("32768"),
+ 268435455 = list_to_integer("268435455"),
+ -268435455 = list_to_integer("-268435455"),
+ %% Bignums.
+ 123456932798748738738 = list_to_integer("123456932798748738738"),
+ _ = list_to_integer(lists:duplicate(2000, $1)),
+ ok.
+
+%% Tests round/1.
+
+t_round() ->
+ 0 = round(0.0),
+ 0 = round(0.4),
+ 1 = round(0.5),
+ 0 = round(-0.4),
+ -1 = round(-0.5),
+ 255 = round(255.3),
+ 256 = round(255.6),
+ -1033 = round(-1033.3),
+ -1034 = round(-1033.6),
+ %% OTP-3722:
+ X = (1 bsl 27) - 1,
+ MX = -X,
+ MXm1 = -X-1,
+ MXp1 = -X+1,
+ F = X + 0.0,
+ X = round(F),
+ X = round(F+1)-1,
+ X = round(F-1)+1,
+ MX = round(-F),
+ MXm1 = round(-F-1),
+ MXp1 = round(-F+1),
+ X = round(F+0.1),
+ X = round(F+1+0.1)-1,
+ X = round(F-1+0.1)+1,
+ MX = round(-F+0.1),
+ MXm1 = round(-F-1+0.1),
+ MXp1 = round(-F+1+0.1),
+ X = round(F-0.1),
+ X = round(F+1-0.1)-1,
+ X = round(F-1-0.1)+1,
+ MX = round(-F-0.1),
+ MXm1 = round(-F-1-0.1),
+ MXp1 = round(-F+1-0.1),
+ 0.5 = abs(round(F+0.5)-(F+0.5)),
+ 0.5 = abs(round(F-0.5)-(F-0.5)),
+ 0.5 = abs(round(-F-0.5)-(-F-0.5)),
+ 0.5 = abs(round(-F+0.5)-(-F+0.5)),
+ %% Bignums.
+ 4294967296 = round(4294967296.1),
+ 4294967297 = round(4294967296.9),
+ -4294967296 = -round(4294967296.1),
+ -4294967297 = -round(4294967296.9),
+ ok.
+
+t_trunc() ->
+ 0 = trunc(0.0),
+ 5 = trunc(5.3333),
+ -10 = trunc(-10.978987),
+ %% The largest smallnum, converted to float (OTP-3722):
+ X = (1 bsl 27) - 1,
+ F = X + 0.0,
+ %% io:format("X = ~p/~w/~w, F = ~p/~w/~w, trunc(F) = ~p/~w/~w~n",
+ %% [X, X, binary_to_list(term_to_binary(X)),
+ %% F, F, binary_to_list(term_to_binary(F)),
+ %% trunc(F), trunc(F), binary_to_list(term_to_binary(trunc(F)))]),
+ X = trunc(F),
+ X = trunc(F+1)-1,
+ X = trunc(F-1)+1,
+ X = -trunc(-F),
+ X = -trunc(-F-1)-1,
+ X = -trunc(-F+1)+1,
+ %% Bignums.
+ 4294967305 = trunc(4294967305.7),
+ -4294967305 = trunc(-4294967305.7),
+ ok.
diff --git a/lib/hipe/test/bs_SUITE_data/bs_pmatch_bugs.erl b/lib/hipe/test/bs_SUITE_data/bs_pmatch_bugs.erl
index b280705a47..d9f3278b45 100644
--- a/lib/hipe/test/bs_SUITE_data/bs_pmatch_bugs.erl
+++ b/lib/hipe/test/bs_SUITE_data/bs_pmatch_bugs.erl
@@ -9,6 +9,7 @@ test() ->
<<49,50,51>> = lex_digits1(Bin, 1, []),
<<49,50,51>> = lex_digits2(Bin, 1, []),
ok = var_bind_bug(<<1, 2, 3, 4, 5, 6, 7, 8>>),
+ ok = bs_match_string_bug(),
ok.
%%--------------------------------------------------------------------
@@ -65,3 +66,50 @@ var_bind_bug(<<A:1/binary, B:8/integer, _C:B/binary, _Rest/binary>>) ->
B -> wrong;
_ -> ok
end.
+
+%%--------------------------------------------------------------------
+%% From: Andreas Schultz
+%% Date: 2/11/2016
+%%
+%% Either HiPE is messing up binary matches in some cases or I'm not
+%% seeing the problem. ... <SNIP PROGRAM - CLEANED UP VERSION BELOW>
+%% With Erlang 19.1.3 the HiPE compiled version behaves differently
+%% than the non-HiPE version: ... <SNIP TEST RUNS>
+%% So, do I do something wrong here or is this a legitimate HiPE bug?
+%%
+%% Yes, this was a legitimate HiPE bug: The BEAM to ICode tranaslation
+%% of the bs_match_string instruction, written long ago for binaries
+%% (i.e., with byte-sized strings), tried to do a `clever' translation
+%% of even bit-sized strings using a HiPE primop that took a `Size'
+%% argument expressed in *bytes*. ICode is not really the place to do
+%% such a thing, and moreover there is really no reason for the HiPE
+%% primop not to take a Size argument expressed in *bits* instead.
+%% The bug was fixed by changing the `Size' argument to be in bits,
+%% postponing the translation of the bs_match_string primop until RTL
+%% and doing a proper translation using bit-sized quantities there.
+%%--------------------------------------------------------------------
+
+bs_match_string_bug() ->
+ ok = test0(<<50>>),
+ Bin = data(),
+ ok = test1(Bin),
+ ok = test2(Bin),
+ ok.
+
+%% Minimal test case showing the problem matching with strings
+test0(<<6:5, 0:1, 0:2>>) -> weird;
+test0(<<6:5, _:1, _:2>>) -> ok;
+test0(_) -> default.
+
+data() -> <<50,16,0>>.
+
+%% This was the problematic test case in HiPE: 'default' was returned
+test1(<<1:3, 1:1, _:1, 0:1, 0:1, 0:1, _/binary>>) -> weird;
+test1(<<1:3, 1:1, _:1, _:1, _:1, _:1, _/binary>>) -> ok;
+test1(_) -> default.
+
+%% This variation of test1/1 above worked OK, even in HiPE
+test2(<<1:3, 1:1, _:1, A:1, B:1, C:1, _/binary>>)
+ when A =:= 1; B =:= 1; C =:= 1 -> ok;
+test2(<<1:3, 1:1, _:1, 0:1, 0:1, 0:1, _/binary>>) -> weird;
+test2(_) -> default.
diff --git a/lib/hipe/test/hipe_SUITE.erl b/lib/hipe/test/hipe_SUITE.erl
index a5b3924aa8..b9adb660f2 100644
--- a/lib/hipe/test/hipe_SUITE.erl
+++ b/lib/hipe/test/hipe_SUITE.erl
@@ -16,7 +16,11 @@
%%
-module(hipe_SUITE).
--compile([export_all]).
+-export([all/0, groups/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2,
+ app/0, app/1, appup/0, appup/1]).
+
-include_lib("common_test/include/ct.hrl").
all() ->
diff --git a/lib/hipe/test/hipe_testsuite_driver.erl b/lib/hipe/test/hipe_testsuite_driver.erl
index 03ec7adfd0..a3048d907e 100644
--- a/lib/hipe/test/hipe_testsuite_driver.erl
+++ b/lib/hipe/test/hipe_testsuite_driver.erl
@@ -99,7 +99,7 @@ write_suite(Suite) ->
write_header(#suite{suitename = SuiteName, outputfile = OutputFile,
testcases = TestCases}) ->
Exports = format_export(TestCases),
- TimeLimit = 3, %% with 1 or 2 it fails on some slow machines...
+ TimeLimit = 5, %% 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/hipe/test/maps_SUITE_data/maps_redundant_branch_is_key.erl b/lib/hipe/test/maps_SUITE_data/maps_redundant_branch_is_key.erl
new file mode 100644
index 0000000000..17c3acd6af
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_redundant_branch_is_key.erl
@@ -0,0 +1,14 @@
+-module(maps_redundant_branch_is_key).
+-export([test/0]).
+
+test() ->
+ ok = thingy(#{a => 1}),
+ ok = thingy(#{a => 2}),
+ ok.
+
+thingy(Map) ->
+ try
+ #{a := _} = Map,
+ ok
+ catch _ -> error
+ end.
diff --git a/lib/hipe/test/opt_verify_SUITE.erl b/lib/hipe/test/opt_verify_SUITE.erl
index 61952e81d7..86083fa02b 100644
--- a/lib/hipe/test/opt_verify_SUITE.erl
+++ b/lib/hipe/test/opt_verify_SUITE.erl
@@ -1,6 +1,9 @@
-module(opt_verify_SUITE).
--compile([export_all]).
+-export([all/0, groups/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2,
+ call_elim/0, call_elim/1]).
all() ->
[call_elim].
@@ -23,23 +26,6 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-call_elim_test_file(Config, FileName, Option) ->
- PrivDir = test_server:lookup_config(priv_dir, Config),
- TempOut = test_server:temp_name(filename:join(PrivDir, "call_elim_out")),
- {ok, TestCase} = compile:file(FileName),
- {ok, TestCase} = hipe:c(TestCase, [Option, {pp_range_icode, {file, TempOut}}]),
- {ok, Icode} = file:read_file(TempOut),
- ok = file:delete(TempOut),
- Icode.
-
-substring_count(Icode, Substring) ->
- substring_count(Icode, Substring, 0).
-substring_count(Icode, Substring, N) ->
- case string:str(Icode, Substring) of
- 0 -> N;
- I -> substring_count(lists:nthtail(I, Icode), Substring, N+1)
- end.
-
call_elim() ->
[{doc, "Test that the call elimination optimization pass is ok"}].
call_elim(Config) ->
@@ -60,3 +46,20 @@ call_elim(Config) ->
Icode6 = call_elim_test_file(Config, F3, no_icode_call_elim),
3 = substring_count(binary:bin_to_list(Icode6), "is_key"),
ok.
+
+call_elim_test_file(Config, FileName, Option) ->
+ PrivDir = test_server:lookup_config(priv_dir, Config),
+ TempOut = test_server:temp_name(filename:join(PrivDir, "call_elim_out")),
+ {ok, TestCase} = compile:file(FileName),
+ {ok, TestCase} = hipe:c(TestCase, [Option, {pp_range_icode, {file, TempOut}}]),
+ {ok, Icode} = file:read_file(TempOut),
+ ok = file:delete(TempOut),
+ Icode.
+
+substring_count(Icode, Substring) ->
+ substring_count(Icode, Substring, 0).
+substring_count(Icode, Substring, N) ->
+ case string:str(Icode, Substring) of
+ 0 -> N;
+ I -> substring_count(lists:nthtail(I, Icode), Substring, N+1)
+ end.
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index e61c1a042c..172d976931 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.15.1
+HIPE_VSN = 3.15.4
diff --git a/lib/hipe/x86/hipe_x86_postpass.erl b/lib/hipe/x86/hipe_x86_postpass.erl
index 939baeccec..4515822a34 100644
--- a/lib/hipe/x86/hipe_x86_postpass.erl
+++ b/lib/hipe/x86/hipe_x86_postpass.erl
@@ -95,7 +95,8 @@ peep([I=#move{src=#x86_temp{reg=Src}, dst=#x86_temp{reg=Dst}},
%% ElimBinALMDouble
%% ----------------
-peep([Move=#move{src=Src, dst=Dst}, Alu=#alu{src=Src, dst=Dst}|Insns], Res, Lst) ->
+peep([Move=#move{src=Src, dst=Dst}, Alu=#alu{src=Src, dst=Dst}|Insns], Res, Lst)
+ when not is_record(Dst, x86_mem) ->
peep([Alu#alu{src=Dst}|Insns], [Move|Res], [elimBinALMDouble|Lst]);
diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile
index c9691df7af..19f12ac6b9 100644
--- a/lib/ic/doc/src/Makefile
+++ b/lib/ic/doc/src/Makefile
@@ -225,7 +225,7 @@ release_docs_spec: docs
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3"
diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml
index 08b02bc4a4..ea8bf758cf 100644
--- a/lib/ic/doc/src/notes.xml
+++ b/lib/ic/doc/src/notes.xml
@@ -31,7 +31,29 @@
<file>notes.xml</file>
</header>
- <section><title>IC 4.4.1</title>
+ <section><title>IC 4.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct bugs when path to mib or idl spec files
+ contains UTF-8 characters. </p>
+ <p>
+ Own Id: OTP-13718 Aux Id: ERL-179 </p>
+ </item>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>IC 4.4.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/ic/src/ic_codegen.erl b/lib/ic/src/ic_codegen.erl
index adad021da1..a3f141f606 100644
--- a/lib/ic/src/ic_codegen.erl
+++ b/lib/ic/src/ic_codegen.erl
@@ -245,8 +245,8 @@ emit_stub_head(G, F1, Name, java) ->
stub_header(G, Name) ->
["Implementation stub file",
"",
- io_lib:format("Target: ~s", [Name]),
- io_lib:format("Source: ~s", [ic_genobj:idlfile(G)]),
+ io_lib:format("Target: ~ts", [Name]),
+ io_lib:format("Source: ~ts", [ic_genobj:idlfile(G)]),
io_lib:format("IC vsn: ~s", [?COMPILERVSN]),
"",
"This file is automatically generated. DO NOT EDIT IT."].
@@ -298,8 +298,8 @@ emit_hrl_head(G, Fd, Name, c_server) ->
hrl_header(G, Name) ->
["",
- io_lib:format("Target: ~s", [Name]),
- io_lib:format("Source: ~s", [ic_genobj:idlfile(G)]),
+ io_lib:format("Target: ~ts", [Name]),
+ io_lib:format("Source: ~ts", [ic_genobj:idlfile(G)]),
io_lib:format("IC vsn: ~s", [?COMPILERVSN]),
"",
"This file is automatically generated. DO NOT EDIT IT."].
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index 7d00ae0170..f0e5e7c266 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1 +1 @@
-IC_VSN = 4.4.1
+IC_VSN = 4.4.2
diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml
index aeda961714..65b3dcde95 100644
--- a/lib/inets/doc/src/http_server.xml
+++ b/lib/inets/doc/src/http_server.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2015</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -40,8 +40,8 @@
<item>Secure Sockets Layer (SSL)</item>
<item>Erlang Scripting Interface (ESI)</item>
<item>Common Gateway Interface (CGI)</item>
- <item>User Authentication (using <c>Mnesia</c>,
- <c>Dets</c> or plain text database)</item>
+ <item>User Authentication (using Mnesia,
+ Dets or plain text database)</item>
<item>Common Logfile Format (with or without disk_log(3) support)</item>
<item>URL Aliasing</item>
<item>Action Mappings</item>
@@ -563,7 +563,7 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[
<title>mod_auth - User Authentication</title>
<p>The <seealso marker="mod_auth">mod_auth(3)</seealso>
module provides for basic user authentication using
- textual files, <c>Dets</c> databases as well as <c>Mnesia</c> databases.</p>
+ textual files, Dets databases as well as Mnesia databases.</p>
<p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
@@ -580,15 +580,15 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[
<section>
<title>Mnesia As Authentication Database</title>
- <p>If <c>Mnesia</c> is used as storage method, <c>Mnesia</c> must be
- started before the HTTP server. The first time <c>Mnesia</c> is
+ <p>If Mnesia is used as storage method, Mnesia must be
+ started before the HTTP server. The first time Mnesia is
started, the schema and the tables must be created before
- <c>Mnesia</c> is started. A simple example of a module with two
- functions that creates and start <c>Mnesia</c> is provided
+ Mnesia is started. A simple example of a module with two
+ functions that creates and start Mnesia is provided
here. Function <c>first_start/0</c> is to be used the first
time. It creates the schema and the tables.
<c>start/0</c> is to be used in consecutive startups.
- <c>start/0</c> starts <c>Mnesia</c> and waits for the tables to
+ <c>start/0</c> starts Mnesia and waits for the tables to
be initiated. This function must only be used when the
schema and the tables are already created.</p>
@@ -616,25 +616,25 @@ start() ->
mnesia:start(),
mnesia:wait_for_tables([httpd_user, httpd_group], 60000). </code>
- <p>To create the <c>Mnesia</c> tables, we use two records defined in
+ <p>To create the Mnesia tables, we use two records defined in
<c>mod_auth.hrl</c>, so that file must be included. <c>first_start/0</c>
creates a schema that specifies on which nodes the database is to reside.
- Then it starts <c>Mnesia</c> and creates the tables. The first argument
+ Then it starts Mnesia and creates the tables. The first argument
is the name of the tables, the second argument is a list of options of
how to create the table, see
- <seealso marker="mnesia:mnesia"><c>mnesia</c></seealso>, documentation for
+ <seealso marker="mnesia:mnesia"><c>mnesia(3)</c></seealso>, documentation for
more information. As the implementation of the <c>mod_auth_mnesia</c>
saves one row for each user, the type must be <c>bag</c>.
When the schema and the tables are created, function
<seealso marker="mnesia:mnesia#start-0">mnesia:start/0</seealso>
- is used to start <c>Mnesia</c> and
- waits for the tables to be loaded. <c>Mnesia</c> uses the
+ is used to start Mnesia and
+ waits for the tables to be loaded. Mnesia uses the
directory specified as <c>mnesia_dir</c> at startup if specified,
- otherwise <c>Mnesia</c> uses the current directory. For security
- reasons, ensure that the <c>Mnesia</c> tables are stored outside
+ otherwise Mnesia uses the current directory. For security
+ reasons, ensure that the Mnesia tables are stored outside
the document tree of the HTTP server. If they are placed in the
directory which it protects, clients can download the tables.
- Only the <c>Dets</c> and <c>Mnesia</c> storage
+ Only the Dets and Mnesia storage
methods allow writing of dynamic user data to disk. <c>plain</c> is
a read only method.</p>
</section>
@@ -669,7 +669,7 @@ start() ->
<section>
<title>mod_disk_log - Logging Using Disk_Log.</title>
<p>Standard logging using the "Common Logfile Format" and
- <seealso marker="kernel:disk_log">kernel:disk_log(3)</seealso>.</p>
+ <seealso marker="kernel:disk_log">disk_log(3)</seealso>.</p>
<p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml
index 8e0301c520..696b7dfa31 100644
--- a/lib/inets/doc/src/http_uri.xml
+++ b/lib/inets/doc/src/http_uri.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2012</year><year>2015</year>
+ <year>2012</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -118,7 +118,7 @@
<v>Option = {ipv6_host_with_brackets, boolean()} |
{scheme_defaults, scheme_defaults()} |
{fragment, boolean()} |
- {schema_validation_fun, fun()}]</v>
+ {scheme_validation_fun, fun()}]</v>
<v>Result = {Scheme, UserInfo, Host, Port, Path, Query} |
{Scheme, UserInfo, Host, Port, Path, Query, Fragment}</v>
<v>UserInfo = user_info()</v>
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index ca9b268a03..4217b3c4fb 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2015</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -68,11 +68,11 @@
this module:</p>
<p><c>boolean() = true | false</c></p>
<p><c>string()</c> = list of ASCII characters</p>
- <p><c>request_id() = ref()</c></p>
+ <p><c>request_id() = reference()</c></p>
<p><c>profile() = atom()</c></p>
<p><c>path() = string()</c> representing a file path or directory path</p>
<p><c>ip_address()</c> = See the
- <seealso marker="kernel:inet">inet(3)</seealso> manual page in <c>Kernel</c>.</p>
+ <seealso marker="kernel:inet">inet(3)</seealso> manual page in Kernel.</p>
<p><c>socket_opt()</c> = See the options used by
<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> <c>gen_tcp(3)</c> and
<seealso marker="ssl:ssl">ssl(3)</seealso> connect(s)</p>
@@ -83,7 +83,7 @@
<title>HTTP DATA TYPES</title>
<p>Type definitions related to HTTP:</p>
- <p><c>method() = head | get | put | post | trace | options | delete</c></p>
+ <p><c>method() = head | get | put | post | trace | options | delete | patch</c></p>
<taglist>
<tag><c>request()</c></tag>
<item><p>= <c>{url(), headers()}</c></p>
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 62b92b8356..d74635fc01 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2015</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -455,7 +455,7 @@ text/plain asc txt</pre>
directory. Several files can be given, in which case the server
returns the first it finds, for example:</p>
- <code>{directory_index, ["index.hml", "welcome.html"]}</code>
+ <code>{directory_index, ["index.html", "welcome.html"]}</code>
<p>Access to http://your.server.org/docs/ would return
http://your.server.org/docs/index.html or
@@ -711,7 +711,7 @@ text/plain asc txt</pre>
<item>
<p>Sets the type of authentication database that is used for the
directory. The key difference between the different methods is
- that dynamic data can be saved when <c>Mnesia</c> and <c>Dets</c>
+ that dynamic data can be saved when Mnesia and Dets
are used.
This property is called <c>AuthDbType</c> in the Apache-like
configuration files.</p>
@@ -731,10 +731,10 @@ text/plain asc txt</pre>
<code> ragnar:s7Xxv7
edward:wwjau8 </code>
- <p>If the <c>Dets</c> storage method is used, the user database is
- maintained by <c>Dets</c> and must not be edited by hand. Use the
+ <p>If the Dets storage method is used, the user database is
+ maintained by Dets and must not be edited by hand. Use the
API functions in module <c>mod_auth</c> to create/edit the user
- database. This directive is ignored if the <c>Mnesia</c>
+ database. This directive is ignored if the Mnesia
storage method is used. For security reasons, ensure that
<c>auth_user_file</c> is stored outside the document tree of the web
server. If it is placed in the directory that it protects,
@@ -753,10 +753,10 @@ text/plain asc txt</pre>
<code>group1: bob joe ante</code>
- <p>If the <c>Dets</c> storage method is used, the group database is
- maintained by <c>Dets</c> and must not be edited by hand. Use the
+ <p>If the Dets storage method is used, the group database is
+ maintained by Dets and must not be edited by hand. Use the
API for module <c>mod_auth</c> to create/edit the group database.
- This directive is ignored if the <c>Mnesia</c> storage method is used.
+ This directive is ignored if the Mnesia storage method is used.
For security reasons, ensure that the <c>auth_group_file</c> is
stored outside the document tree of the web server. If it is
placed in the directory that it protects, clients
diff --git a/lib/inets/doc/src/mod_auth.xml b/lib/inets/doc/src/mod_auth.xml
index 4b7088b2c5..c4f844622b 100644
--- a/lib/inets/doc/src/mod_auth.xml
+++ b/lib/inets/doc/src/mod_auth.xml
@@ -33,7 +33,7 @@
<modulesummary>User authentication using text files, Dets, or Mnesia database.</modulesummary>
<description>
<p>This module provides for basic user authentication using
- textual files, <c>Dets</c> databases, or <c>Mnesia</c> databases.</p>
+ textual files, Dets databases, or Mnesia databases.</p>
</description>
<funcs>
diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml
index 8279fdc824..46cc796c8a 100644
--- a/lib/inets/doc/src/mod_esi.xml
+++ b/lib/inets/doc/src/mod_esi.xml
@@ -61,13 +61,13 @@
<tag><c>{server_port, integer()}</c></tag>
<item><p>Servers port number.</p></item>
- <tag><c>{request_method, "GET | "PUT" | "DELETE | "POST" | "PATCH"}</c></tag>
+ <tag><c>{request_method, "GET | "PUT" | "DELETE" | "POST" | "PATCH"}</c></tag>
<item><p>HTTP request method.</p></item>
<tag><c>{remote_adress, inet:ip_address()} </c></tag>
<item><p>The clients ip address.</p></item>
- <tag><c>{peer_cert, undefined | no_peercert | DER:binary()</c></tag>
+ <tag><c>{peer_cert, undefined | no_peercert | DER:binary()}</c></tag>
<item>
<p>For TLS connections where client certificates are used this will
be an ASN.1 DER-encoded X509-certificate as an Erlang binary.
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 5321203511..a3e4893ae2 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2002</year><year>2016</year>
+ <year>2002</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,7 +33,221 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.3</title>
+ <section><title>Inets 6.3.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The close of a chunked file reception crashed in a
+ certain timing sequence.</p>
+ <p>
+ Own Id: OTP-14391 Aux Id: seq13306 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added missing release note for inets-6.3.7</p>
+ <p>
+ Own Id: OTP-14383</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a bug in ftp that made further operations after a
+ recv_chunk operation impossible.</p>
+ <p>
+ Own Id: OTP-14242</p>
+ </item>
+
+ <item>
+ <p>Make default port, 80 and 443, implicit in automatic redirection.
+ </p>
+ <p> Own Id: OTP-14301
+ </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Chunk size decoding could fail. The symptom was that
+ chunk decoding sometimes failed depending on timing of
+ the received stream. If chunk size was split into two
+ different packets decoding would fail.</p>
+ <p>
+ Own Id: OTP-13571 Aux Id: ERL-116 </p>
+ </item>
+ <item>
+ <p>
+ Prevent httpc user process to hang if httpc_handler
+ process terminates unexpectedly</p>
+ <p>
+ Own Id: OTP-14091</p>
+ </item>
+ <item>
+ <p>
+ Correct Host header, to include port number, when
+ redirecting requests.</p>
+ <p>
+ Own Id: OTP-14097</p>
+ </item>
+ <item>
+ <p>
+ Shutdown gracefully on connection or TLS handshake errors</p>
+ <p>
+ Own Id: OTP-14173 Aux Id: seq13262 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correct misstakes in ftp client introduced in inets-6.3.4</p>
+ <p>
+ Own Id: OTP-14203 Aux Id: OTP-13982 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixes a bug that makes the ftp client end up in bad state
+ if there is a multi line response from the server and the
+ response number is in the message being sent.</p>
+ <p>
+ Own Id: OTP-13960 Aux Id: PR1196 </p>
+ </item>
+ <item>
+ <p>
+ The ftp client could stop consuming messages when the
+ multiline response handling was corrected.</p>
+ <p>
+ Own Id: OTP-13967</p>
+ </item>
+ <item>
+ <p>
+ Fix keep-alive https through proxy connections so that
+ all requests, following the first one, will run as
+ expected instead of failing.</p>
+ <p>
+ Own Id: OTP-14041</p>
+ </item>
+ <item>
+ <p>
+ Fix bug from commit
+ fdfda2fab0921d409789174556582db28141448e that could make
+ listing of group members in mod_auth callbacks fail.</p>
+ <p>
+ Own Id: OTP-14082</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Update behavior of httpc:request to match RFC-7231</p>
+ <p>
+ Own Id: OTP-13902</p>
+ </item>
+ <item>
+ <p>
+ Fixed dialyzer warnings as well as some white-space
+ issues. Thanks to Kostis.</p>
+ <p>
+ Own Id: OTP-13982 Aux Id: PR-1207 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The legacy option 'inet6fb4' for inets had stopped
+ working. This bug has now been corrected. Fix by Edwin
+ Fine in bugs.erlang.org ERL-200 and Github PR#1132.</p>
+ <p>
+ Own Id: OTP-13776 Aux Id: ERL-200 PR-1132 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ PUT and DELETE support has been added to mod_esi</p>
+ <p>
+ Own Id: OTP-13688 Aux Id: seq13149 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A debug message was accidently left enabled in the ftp
+ client.</p>
+ <p>
+ Own Id: OTP-13712 Aux Id: seq13143 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index bbf25f8e90..e0430654eb 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. 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.
@@ -100,14 +100,21 @@
ftp_extension = ?FTP_EXT_DEFAULT
}).
+-record(recv_chunk_closing, {
+ dconn_closed = false,
+ pos_compl_received = false,
+ client_called_us = false
+ }).
+
-type shortage_reason() :: 'etnospc' | 'epnospc'.
-type restriction_reason() :: 'epath' | 'efnamena' | 'elogin' | 'enotbinary'.
-type common_reason() :: 'econn' | 'eclosed' | term().
-type file_write_error_reason() :: term(). % See file:write for more info
-%%-define(DBG(F,A), 'n/a').
--define(DBG(F,A), io:format(F,A)).
+-define(DBG(F,A), 'n/a').
+%%-define(DBG(F,A), io:format(F,A)).
+%%-define(DBG(F,A), ct:pal("~p:~p " ++ if is_list(F) -> F; is_atom(F) -> atom_to_list(F) end, [?MODULE,?LINE|A])).
%%%=========================================================================
%%% API - CLIENT FUNCTIONS
@@ -1095,7 +1102,7 @@ init(Options) ->
erlang:monitor(process, Client),
%% Make sure inet is started
- inet_db:start(),
+ _ = inet_db:start(),
%% Where are we
{ok, Dir} = file:get_cwd(),
@@ -1105,15 +1112,17 @@ init(Options) ->
trace ->
dbg:tracer(),
dbg:p(all, [call]),
- dbg:tpl(ftp, [{'_', [], [{return_trace}]}]),
- dbg:tpl(ftp_response, [{'_', [], [{return_trace}]}]),
- dbg:tpl(ftp_progress, [{'_', [], [{return_trace}]}]);
+ {ok, _} = dbg:tpl(ftp, [{'_', [], [{return_trace}]}]),
+ {ok, _} = dbg:tpl(ftp_response, [{'_', [], [{return_trace}]}]),
+ {ok, _} = dbg:tpl(ftp_progress, [{'_', [], [{return_trace}]}]),
+ ok;
debug ->
dbg:tracer(),
dbg:p(all, [call]),
- dbg:tp(ftp, [{'_', [], [{return_trace}]}]),
- dbg:tp(ftp_response, [{'_', [], [{return_trace}]}]),
- dbg:tp(ftp_progress, [{'_', [], [{return_trace}]}]);
+ {ok, _} = dbg:tp(ftp, [{'_', [], [{return_trace}]}]),
+ {ok, _} = dbg:tp(ftp_response, [{'_', [], [{return_trace}]}]),
+ {ok, _} = dbg:tp(ftp_progress, [{'_', [], [{return_trace}]}]),
+ ok;
_ ->
%% Keep silent
ok
@@ -1295,8 +1304,7 @@ handle_call({_,{rmdir, Dir}}, From, #state{chunk = false} = State) ->
activate_ctrl_connection(State),
{noreply, State#state{client = From}};
-handle_call({_,{type, Type}}, From, #state{chunk = false}
- = State) ->
+handle_call({_,{type, Type}}, From, #state{chunk = false} = State) ->
case Type of
ascii ->
send_ctrl_message(State, mk_cmd("TYPE A", [])),
@@ -1341,6 +1349,25 @@ handle_call({_,{recv_chunk_start, RemoteFile}}, From, #state{chunk = false}
handle_call({_, recv_chunk}, _, #state{chunk = false} = State) ->
{reply, {error, "ftp:recv_chunk_start/2 not called"}, State};
+handle_call({_, recv_chunk}, _From, #state{chunk = true,
+ caller = #recv_chunk_closing{dconn_closed = true,
+ pos_compl_received = true
+ }
+ } = State0) ->
+ %% The ftp:recv_chunk call was the last event we waited for, finnish and clean up
+ ?DBG("recv_chunk_closing ftp:recv_chunk, last event",[]),
+ activate_ctrl_connection(State0),
+ {reply, ok, State0#state{caller = undefined,
+ chunk = false,
+ client = undefined}};
+
+handle_call({_, recv_chunk}, From, #state{chunk = true,
+ caller = #recv_chunk_closing{} = R
+ } = State) ->
+ %% Waiting for more, don't care what
+ ?DBG("recv_chunk_closing ftp:recv_chunk, get more",[]),
+ {noreply, State#state{client = From, caller = R#recv_chunk_closing{client_called_us=true}}};
+
handle_call({_, recv_chunk}, From, #state{chunk = true} = State0) ->
State = activate_data_connection(State0),
{noreply, State#state{client = From, caller = recv_chunk}};
@@ -1454,7 +1481,7 @@ handle_info({Trpt, Socket, Data},
#state{dsock = {Trpt,Socket},
caller = {recv_file, Fd}} = State0) when Trpt==tcp;Trpt==ssl ->
?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]),
- file_write(binary_to_list(Data), Fd),
+ ok = file_write(binary_to_list(Data), Fd),
progress_report({binary, Data}, State0),
State = activate_data_connection(State0),
{noreply, State};
@@ -1473,24 +1500,29 @@ handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}} = State0) when T
Data/binary>>}};
handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket},
- caller = {recv_file, Fd}}
- = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
+ caller = {recv_file, Fd}} = State)
+ when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
file_close(Fd),
progress_report({transfer_size, 0}, State),
activate_ctrl_connection(State),
+ ?DBG("Data channel close",[]),
{noreply, State#state{dsock = undefined, data = <<>>}};
-handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, client = From,
- caller = recv_chunk}
- = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
- gen_server:reply(From, ok),
- {noreply, State#state{dsock = undefined, client = undefined,
- data = <<>>, caller = undefined,
- chunk = false}};
+handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket},
+ client = Client,
+ caller = recv_chunk} = State)
+ when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
+ ?DBG("Data channel close recv_chunk",[]),
+ activate_ctrl_connection(State),
+ {noreply, State#state{dsock = undefined,
+ caller = #recv_chunk_closing{dconn_closed = true,
+ client_called_us = Client =/= undefined}
+ }};
handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, caller = recv_bin,
data = Data} = State)
when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
+ ?DBG("Data channel close",[]),
activate_ctrl_connection(State),
{noreply, State#state{dsock = undefined, data = <<>>,
caller = {recv_bin, Data}}};
@@ -1498,6 +1530,7 @@ handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, caller = recv_bin,
handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, data = Data,
caller = {handle_dir_result, Dir}}
= State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
+ ?DBG("Data channel close",[]),
activate_ctrl_connection(State),
{noreply, State#state{dsock = undefined,
caller = {handle_dir_result, Dir, Data},
@@ -1599,13 +1632,13 @@ terminate(normal, State) ->
%% If terminate reason =/= normal the progress reporting process will
%% be killed by the exit signal.
progress_report(stop, State),
- do_termiante({error, econn}, State);
+ do_terminate({error, econn}, State);
terminate(Reason, State) ->
Report = io_lib:format("Ftp connection closed due to: ~p~n", [Reason]),
error_logger:error_report(Report),
- do_termiante({error, eclosed}, State).
+ do_terminate({error, eclosed}, State).
-do_termiante(ErrorMsg, State) ->
+do_terminate(ErrorMsg, State) ->
close_data_connection(State),
close_ctrl_connection(State),
case State#state.client of
@@ -2044,6 +2077,30 @@ handle_ctrl_result({pos_prel, _}, #state{client = From,
end;
%%--------------------------------------------------------------------------
+%% File handling - chunk_transfer complete
+
+handle_ctrl_result({pos_compl, _}, #state{client = From,
+ caller = #recv_chunk_closing{dconn_closed = true,
+ client_called_us = true,
+ pos_compl_received = false
+ }}
+ = State0) when From =/= undefined ->
+ %% The pos_compl was the last event we waited for, finnish and clean up
+ ?DBG("recv_chunk_closing pos_compl, last event",[]),
+ gen_server:reply(From, ok),
+ activate_ctrl_connection(State0),
+ {noreply, State0#state{caller = undefined,
+ chunk = false,
+ client = undefined}};
+
+handle_ctrl_result({pos_compl, _}, #state{caller = #recv_chunk_closing{}=R}
+ = State0) ->
+ %% Waiting for more, don't care what
+ ?DBG("recv_chunk_closing pos_compl, wait more",[]),
+ {noreply, State0#state{caller = R#recv_chunk_closing{pos_compl_received=true}}};
+
+
+%%--------------------------------------------------------------------------
%% File handling - recv_file
handle_ctrl_result({pos_prel, _}, #state{caller = {recv_file, _}} = State0) ->
case accept_data_connection(State0) of
@@ -2099,7 +2156,7 @@ handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}}
%%--------------------------------------------------------------------------
%% Default
-handle_ctrl_result({Status, Lines}, #state{client = From} = State)
+handle_ctrl_result({Status, _Lines}, #state{client = From} = State)
when From =/= undefined ->
ctrl_result_response(Status, State, {error, Status}).
@@ -2220,16 +2277,16 @@ setup_data_connection(#state{mode = active,
{ok, Port} = inet:port(LSock),
case FtpExt of
false ->
- {IP1, IP2, IP3, IP4} = IP,
- {Port1, Port2} = {Port div 256, Port rem 256},
- send_ctrl_message(State,
- mk_cmd("PORT ~w,~w,~w,~w,~w,~w",
- [IP1, IP2, IP3, IP4, Port1, Port2]));
- true ->
- IpAddress = inet_parse:ntoa(IP),
- Cmd = mk_cmd("EPRT |1|~s|~p|", [IpAddress, Port]),
- send_ctrl_message(State, Cmd)
- end,
+ {IP1, IP2, IP3, IP4} = IP,
+ {Port1, Port2} = {Port div 256, Port rem 256},
+ send_ctrl_message(State,
+ mk_cmd("PORT ~w,~w,~w,~w,~w,~w",
+ [IP1, IP2, IP3, IP4, Port1, Port2]));
+ true ->
+ IpAddress = inet_parse:ntoa(IP),
+ Cmd = mk_cmd("EPRT |1|~s|~p|", [IpAddress, Port]),
+ send_ctrl_message(State, Cmd)
+ end,
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection,
{LSock, Caller}}}}
@@ -2337,7 +2394,7 @@ accept_data_connection(#state{mode = passive} = State) ->
send_ctrl_message(_S=#state{csock = Socket, verbose = Verbose}, Message) ->
verbose(lists:flatten(Message),Verbose,send),
?DBG('<--ctrl ~p ---- ~s~p~n',[Socket,Message,_S]),
- send_message(Socket, Message).
+ _ = send_message(Socket, Message).
send_data_message(_S=#state{dsock = Socket}, Message) ->
?DBG('<==data ~p ==== ~s~n~p~n',[Socket,Message,_S]),
@@ -2358,24 +2415,34 @@ send_message({tcp, Socket}, Message) ->
send_message({ssl, Socket}, Message) ->
ssl:send(Socket, Message).
-activate_ctrl_connection(#state{csock = Socket, ctrl_data = {<<>>, _, _}}) ->
- activate_connection(Socket);
-activate_ctrl_connection(#state{csock = Socket}) ->
+activate_ctrl_connection(#state{csock = CSock, ctrl_data = {<<>>, _, _}}) ->
+ activate_connection(CSock);
+activate_ctrl_connection(#state{csock = CSock}) ->
+ activate_connection(CSock),
%% We have already received at least part of the next control message,
%% that has been saved in ctrl_data, process this first.
- self() ! {tcp, unwrap_socket(Socket), <<>>}.
-
-unwrap_socket({tcp,Socket}) -> Socket;
-unwrap_socket({ssl,Socket}) -> Socket;
-unwrap_socket(Socket) -> Socket.
-
+ self() ! {socket_type(CSock), unwrap_socket(CSock), <<>>},
+ ok.
-activate_data_connection(#state{dsock = Socket} = State) ->
- activate_connection(Socket),
+activate_data_connection(#state{dsock = DSock} = State) ->
+ activate_connection(DSock),
State.
-activate_connection({tcp, Socket}) -> inet:setopts(Socket, [{active, once}]);
-activate_connection({ssl, Socket}) -> ssl:setopts(Socket, [{active, once}]).
+activate_connection(Socket) ->
+ ignore_return_value(
+ case socket_type(Socket) of
+ tcp -> inet:setopts(unwrap_socket(Socket), [{active, once}]);
+ ssl -> ssl:setopts(unwrap_socket(Socket), [{active, once}])
+ end).
+
+
+ignore_return_value(_) -> ok.
+
+unwrap_socket({tcp,Socket}) -> Socket;
+unwrap_socket({ssl,Socket}) -> Socket.
+
+socket_type({tcp,_Socket}) -> tcp;
+socket_type({ssl,_Socket}) -> ssl.
close_ctrl_connection(#state{csock = undefined}) -> ok;
close_ctrl_connection(#state{csock = Socket}) -> close_connection(Socket).
@@ -2383,16 +2450,16 @@ close_ctrl_connection(#state{csock = Socket}) -> close_connection(Socket).
close_data_connection(#state{dsock = undefined}) -> ok;
close_data_connection(#state{dsock = Socket}) -> close_connection(Socket).
-close_connection({lsock,Socket}) -> gen_tcp:close(Socket);
-close_connection({tcp, Socket}) -> gen_tcp:close(Socket);
-close_connection({ssl, Socket}) -> ssl:close(Socket).
+close_connection({lsock,Socket}) -> ignore_return_value( gen_tcp:close(Socket) );
+close_connection({tcp, Socket}) -> ignore_return_value( gen_tcp:close(Socket) );
+close_connection({ssl, Socket}) -> ignore_return_value( ssl:close(Socket) ).
-%% ------------ FILE HANDELING ----------------------------------------
+%% ------------ FILE HANDLING ----------------------------------------
send_file(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Fd) ->
{noreply, State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_file, Fd}}};
send_file(State, Fd) ->
case file_read(Fd) of
- {ok, N, Bin} when N > 0->
+ {ok, N, Bin} when N > 0 ->
send_data_message(State, Bin),
progress_report({binary, Bin}, State),
send_file(State, Fd);
@@ -2412,7 +2479,7 @@ file_open(File, Option) ->
file:open(File, [raw, binary, Option]).
file_close(Fd) ->
- file:close(Fd).
+ ignore_return_value( file:close(Fd) ).
file_read(Fd) ->
case file:read(Fd, ?FILE_BUFSIZE) of
@@ -2504,7 +2571,7 @@ progress_report(stop, #state{progress = ProgressPid}) ->
ftp_progress:stop(ProgressPid);
progress_report({binary, Data}, #state{progress = ProgressPid}) ->
ftp_progress:report(ProgressPid, {transfer_size, size(Data)});
-progress_report(Report, #state{progress = ProgressPid}) ->
+progress_report(Report, #state{progress = ProgressPid}) ->
ftp_progress:report(ProgressPid, Report).
diff --git a/lib/inets/src/ftp/ftp_progress.erl b/lib/inets/src/ftp/ftp_progress.erl
index 68185a222d..a6263e5cd7 100644
--- a/lib/inets/src/ftp/ftp_progress.erl
+++ b/lib/inets/src/ftp/ftp_progress.erl
@@ -36,11 +36,11 @@
-include_lib("kernel/include/file.hrl").
-record(progress, {
- file, % string()
- cb_module, % atom()
- cb_function, % atom()
- init_progress_term, % term()
- current_progress_term % term()
+ file :: string() | 'undefined',
+ cb_module :: module(),
+ cb_function :: atom(),
+ init_progress_term :: term(),
+ current_progress_term :: term()
}).
%%%=========================================================================
@@ -53,13 +53,15 @@
%% Description: Starts the progress report process unless progress reporting
%% should not be performed.
%%--------------------------------------------------------------------------
+-type options() :: 'ignore' | {module(), atom(), term()}.
+-spec start_link(options()) -> 'ignore' | pid().
start_link(ignore) ->
ignore;
start_link(Options) ->
spawn_link(?MODULE, init, [Options]).
%%--------------------------------------------------------------------------
-%% report_progress(Pid, Report) -> _
+%% report_progress(Pid, Report) -> ok
%% Pid = pid()
%% Report = {local_file, File} | {remote_file, File} |
%% {transfer_size, Size}
@@ -68,17 +70,23 @@ start_link(Options) ->
%% Description: Reports progress to the reporting process that calls the
%% user defined callback function.
%%--------------------------------------------------------------------------
+-type report() :: {'local_file', string()} | {'remote_file', string()}
+ | {'transfer_size', non_neg_integer()}.
+-spec report(pid(), report()) -> 'ok'.
report(Pid, Report) ->
- Pid ! {progress_report, Report}.
+ Pid ! {progress_report, Report},
+ ok.
%%--------------------------------------------------------------------------
-%% stop(Pid) -> _
+%% stop(Pid) -> ok
%% Pid = pid()
%%
%% Description:
%%--------------------------------------------------------------------------
+-spec stop(pid()) -> 'ok'.
stop(Pid) ->
- Pid ! stop.
+ Pid ! stop,
+ ok.
%%%=========================================================================
%%% Internal functions
diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/inets/src/ftp/ftp_response.erl
index 7533bc4550..d54d97dc91 100644
--- a/lib/inets/src/ftp/ftp_response.erl
+++ b/lib/inets/src/ftp/ftp_response.erl
@@ -90,19 +90,23 @@ parse_lines(<<C1, C2, C3, ?WHITE_SPACE, Bin/binary>>, Lines, start) ->
parse_lines(Bin, [?WHITE_SPACE, C3, C2, C1 | Lines], finish);
%% Last line found
-parse_lines(<<C1, C2, C3, ?WHITE_SPACE, Rest/binary>>, Lines, {C1, C2, C3}) ->
- parse_lines(Rest, [?WHITE_SPACE, C3, C2, C1 | Lines], finish);
+parse_lines(<<?CR, ?LF, C1, C2, C3, ?WHITE_SPACE, Rest/binary>>, Lines, {C1, C2, C3}) ->
+ parse_lines(Rest, [?WHITE_SPACE, C3, C2, C1, ?LF, ?CR | Lines], finish);
%% Potential end found wait for more data
-parse_lines(<<C1, C2, C3>> = Bin, Lines, {C1, C2, C3}) ->
+parse_lines(<<?CR, ?LF, C1, C2, C3>> = Bin, Lines, {C1, C2, C3}) ->
{continue, {Bin, Lines, {C1, C2, C3}}};
%% Intermidate line begining with status code
-parse_lines(<<C1, C2, C3, Rest/binary>>, Lines, {C1, C2, C3}) ->
- parse_lines(Rest, [C3, C2, C1 | Lines], {C1, C2, C3});
+parse_lines(<<?CR, ?LF, C1, C2, C3, Rest/binary>>, Lines, {C1, C2, C3}) ->
+ parse_lines(Rest, [C3, C2, C1, ?LF, ?CR | Lines], {C1, C2, C3});
%% Potential last line wait for more data
-parse_lines(<<C1, C2>> = Data, Lines, {C1, C2, _} = StatusCode) ->
+parse_lines(<<?CR, ?LF, C1, C2>> = Data, Lines, {C1, C2, _} = StatusCode) ->
{continue, {Data, Lines, StatusCode}};
-parse_lines(<<C1>> = Data, Lines, {C1, _, _} = StatusCode) ->
+parse_lines(<<?CR, ?LF, C1>> = Data, Lines, {C1, _, _} = StatusCode) ->
+ {continue, {Data, Lines, StatusCode}};
+parse_lines(<<?CR, ?LF>> = Data, Lines, {_,_,_} = StatusCode) ->
+ {continue, {Data, Lines, StatusCode}};
+parse_lines(<<?LF>> = Data, Lines, {_,_,_} = StatusCode) ->
{continue, {Data, Lines, StatusCode}};
parse_lines(<<>> = Data, Lines, {_,_,_} = StatusCode) ->
{continue, {Data, Lines, StatusCode}};
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index 91d87289a2..418b6247b0 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -147,6 +147,26 @@ request(Method, Request, HttpOptions, Options) ->
request(Method, Request, HttpOptions, Options, default_profile()).
request(Method,
+ {Url, Headers, ContentType, TupleBody},
+ HTTPOptions, Options, Profile)
+ when ((Method =:= post) orelse (Method =:= patch) orelse (Method =:= put) orelse (Method =:= delete))
+ andalso (is_atom(Profile) orelse is_pid(Profile)) andalso
+ is_list(ContentType) andalso is_tuple(TupleBody)->
+ case check_body_gen(TupleBody) of
+ ok ->
+ do_request(Method, {Url, Headers, ContentType, TupleBody}, HTTPOptions, Options, Profile);
+ Error ->
+ Error
+ end;
+request(Method,
+ {Url, Headers, ContentType, Body},
+ HTTPOptions, Options, Profile)
+ when ((Method =:= post) orelse (Method =:= patch) orelse (Method =:= put) orelse (Method =:= delete))
+ andalso (is_atom(Profile) orelse is_pid(Profile)) andalso
+ is_list(ContentType) andalso (is_list(Body) orelse is_binary(Body)) ->
+ do_request(Method, {Url, Headers, ContentType, Body}, HTTPOptions, Options, Profile);
+
+request(Method,
{Url, Headers},
HTTPOptions, Options, Profile)
when (Method =:= options) orelse
@@ -155,12 +175,6 @@ request(Method,
(Method =:= delete) orelse
(Method =:= trace) andalso
(is_atom(Profile) orelse is_pid(Profile)) ->
- ?hcrt("request", [{method, Method},
- {url, Url},
- {headers, Headers},
- {http_options, HTTPOptions},
- {options, Options},
- {profile, Profile}]),
case uri_parse(Url, Options) of
{error, Reason} ->
{error, Reason};
@@ -172,21 +186,9 @@ request(Method,
handle_request(Method, Url, ParsedUrl, Headers, [], [],
HTTPOptions, Options, Profile)
end
- end;
-
-request(Method,
- {Url, Headers, ContentType, Body},
- HTTPOptions, Options, Profile)
- when ((Method =:= post) orelse (Method =:= patch) orelse (Method =:= put) orelse
- (Method =:= delete)) andalso (is_atom(Profile) orelse is_pid(Profile)) ->
- ?hcrt("request", [{method, Method},
- {url, Url},
- {headers, Headers},
- {content_type, ContentType},
- {body, Body},
- {http_options, HTTPOptions},
- {options, Options},
- {profile, Profile}]),
+ end.
+
+do_request(Method, {Url, Headers, ContentType, Body}, HTTPOptions, Options, Profile) ->
case uri_parse(Url, Options) of
{error, Reason} ->
{error, Reason};
@@ -196,7 +198,6 @@ request(Method,
HTTPOptions, Options, Profile)
end.
-
%%--------------------------------------------------------------------------
%% cancel_request(RequestId) -> ok
%% cancel_request(RequestId, Profile) -> ok
@@ -209,7 +210,6 @@ cancel_request(RequestId) ->
cancel_request(RequestId, Profile)
when is_atom(Profile) orelse is_pid(Profile) ->
- ?hcrt("cancel request", [{request_id, RequestId}, {profile, Profile}]),
httpc_manager:cancel_request(RequestId, profile_name(Profile)).
@@ -232,7 +232,6 @@ cancel_request(RequestId, Profile)
set_options(Options) ->
set_options(Options, default_profile()).
set_options(Options, Profile) when is_atom(Profile) orelse is_pid(Profile) ->
- ?hcrt("set options", [{options, Options}, {profile, Profile}]),
case validate_options(Options) of
{ok, Opts} ->
httpc_manager:set_options(Opts, profile_name(Profile));
@@ -272,7 +271,6 @@ get_options(all = _Options, Profile) ->
get_options(Options, Profile)
when (is_list(Options) andalso
(is_atom(Profile) orelse is_pid(Profile))) ->
- ?hcrt("get options", [{options, Options}, {profile, Profile}]),
case Options -- get_options() of
[] ->
try
@@ -314,9 +312,6 @@ store_cookies(SetCookieHeaders, Url) ->
store_cookies(SetCookieHeaders, Url, Profile)
when is_atom(Profile) orelse is_pid(Profile) ->
- ?hcrt("store cookies", [{set_cookie_headers, SetCookieHeaders},
- {url, Url},
- {profile, Profile}]),
try
begin
%% Since the Address part is not actually used
@@ -353,9 +348,6 @@ cookie_header(Url, Opts) when is_list(Opts) ->
cookie_header(Url, Opts, Profile)
when (is_list(Opts) andalso (is_atom(Profile) orelse is_pid(Profile))) ->
- ?hcrt("cookie header", [{url, Url},
- {opts, Opts},
- {profile, Profile}]),
try
begin
httpc_manager:which_cookies(Url, Opts, profile_name(Profile))
@@ -398,7 +390,6 @@ which_sessions() ->
which_sessions(default_profile()).
which_sessions(Profile) ->
- ?hcrt("which sessions", [{profile, Profile}]),
try
begin
httpc_manager:which_sessions(profile_name(Profile))
@@ -419,7 +410,6 @@ info() ->
info(default_profile()).
info(Profile) ->
- ?hcrt("info", [{profile, Profile}]),
try
begin
httpc_manager:info(profile_name(Profile))
@@ -440,7 +430,6 @@ reset_cookies() ->
reset_cookies(default_profile()).
reset_cookies(Profile) ->
- ?hcrt("reset cookies", [{profile, Profile}]),
try
begin
httpc_manager:reset_cookies(profile_name(Profile))
@@ -458,7 +447,6 @@ reset_cookies(Profile) ->
%% same behavior as active once for sockets.
%%-------------------------------------------------------------------------
stream_next(Pid) ->
- ?hcrt("stream next", [{handler, Pid}]),
httpc_handler:stream_next(Pid).
@@ -466,7 +454,6 @@ stream_next(Pid) ->
%%% Behaviour callbacks
%%%========================================================================
start_standalone(PropList) ->
- ?hcrt("start standalone", [{proplist, PropList}]),
case proplists:get_value(profile, PropList) of
undefined ->
{error, no_profile};
@@ -477,14 +464,11 @@ start_standalone(PropList) ->
end.
start_service(Config) ->
- ?hcrt("start service", [{config, Config}]),
httpc_profile_sup:start_child(Config).
stop_service(Profile) when is_atom(Profile) ->
- ?hcrt("stop service", [{profile, Profile}]),
httpc_profile_sup:stop_child(Profile);
stop_service(Pid) when is_pid(Pid) ->
- ?hcrt("stop service", [{pid, Pid}]),
case service_info(Pid) of
{ok, [{profile, Profile}]} ->
stop_service(Profile);
@@ -510,7 +494,6 @@ service_info(Pid) ->
%%%========================================================================
%%% Internal functions
%%%========================================================================
-
handle_request(Method, Url,
{Scheme, UserInfo, Host, Port, Path, Query},
Headers0, ContentType, Body0,
@@ -521,9 +504,6 @@ handle_request(Method, Url,
try
begin
- ?hcrt("begin processing", [{started, Started},
- {new_headers, NewHeaders0}]),
-
{NewHeaders, Body} =
case Body0 of
{chunkify, ProcessBody, Acc}
@@ -544,7 +524,7 @@ handle_request(Method, Url,
Options = request_options(Options0),
Sync = proplists:get_value(sync, Options),
Stream = proplists:get_value(stream, Options),
- Host2 = header_host(Scheme, Host, Port),
+ Host2 = http_request:normalize_host(Scheme, Host, Port),
HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions),
Receiver = proplists:get_value(receiver, Options),
SocketOpts = proplists:get_value(socket_opts, Options),
@@ -575,16 +555,13 @@ handle_request(Method, Url,
{ok, RequestId} ->
handle_answer(RequestId, Sync, Options);
{error, Reason} ->
- ?hcrd("request failed", [{reason, Reason}]),
{error, Reason}
end
end
catch
error:{noproc, _} ->
- ?hcrv("noproc", [{profile, Profile}]),
{error, {not_started, Profile}};
throw:Error ->
- ?hcrv("throw", [{error, Error}]),
Error
end.
@@ -620,15 +597,10 @@ handle_answer(RequestId, false, _) ->
handle_answer(RequestId, true, Options) ->
receive
{http, {RequestId, saved_to_file}} ->
- ?hcrt("received saved-to-file", [{request_id, RequestId}]),
{ok, saved_to_file};
{http, {RequestId, {_,_,_} = Result}} ->
- ?hcrt("received answer", [{request_id, RequestId},
- {result, Result}]),
return_answer(Options, Result);
{http, {RequestId, {error, Reason}}} ->
- ?hcrt("received error", [{request_id, RequestId},
- {reason, Reason}]),
{error, Reason}
end.
@@ -1063,14 +1035,6 @@ bad_option(Option, BadValue) ->
throw({error, {bad_option, Option, BadValue}}).
-header_host(https, Host, 443 = _Port) ->
- Host;
-header_host(http, Host, 80 = _Port) ->
- Host;
-header_host(_Scheme, Host, Port) ->
- Host ++ ":" ++ integer_to_list(Port).
-
-
header_record(NewHeaders, Host, #http_options{version = Version}) ->
header_record(NewHeaders, #http_request_h{}, Host, Version).
@@ -1257,18 +1221,14 @@ child_name(Pid, [{Name, Pid} | _]) ->
child_name(Pid, [_ | Children]) ->
child_name(Pid, Children).
-%% d(F) ->
-%% d(F, []).
-
-%% d(F, A) ->
-%% d(get(dbg), F, A).
-
-%% d(true, F, A) ->
-%% io:format(user, "~w:~w:" ++ F ++ "~n", [self(), ?MODULE | A]);
-%% d(_, _, _) ->
-%% ok.
-
host_address(Host, false) ->
Host;
host_address(Host, true) ->
string:strip(string:strip(Host, right, $]), left, $[).
+
+check_body_gen({Fun, _}) when is_function(Fun) ->
+ ok;
+check_body_gen({chunkify, Fun, _}) when is_function(Fun) ->
+ ok;
+check_body_gen(Gen) ->
+ {error, {bad_body_generator, Gen}}.
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index d1c52dcc78..c99200777b 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -32,7 +32,6 @@
%% Internal Application API
-export([
start_link/4,
- %% connect_and_send/2,
send/2,
cancel/2,
stream_next/1,
@@ -45,28 +44,30 @@
-record(timers,
{
- request_timers = [], % [ref()]
- queue_timer % ref()
+ request_timers = [] :: [reference()],
+ queue_timer :: reference() | 'undefined'
}).
+-type session_failed() :: {'connect_failed',term()} | {'send_failed',term()}.
+
-record(state,
{
- request, % #request{}
- session, % #session{}
+ request :: request() | 'undefined',
+ session :: session() | session_failed() | 'undefined',
status_line, % {Version, StatusCode, ReasonPharse}
- headers, % #http_response_h{}
- body, % binary()
+ headers :: http_response_h() | 'undefined',
+ body :: binary() | 'undefined',
mfa, % {Module, Function, Args}
- pipeline = queue:new(), % queue:queue()
- keep_alive = queue:new(), % queue:queue()
+ pipeline = queue:new() :: queue:queue(),
+ keep_alive = queue:new() :: queue:queue(),
status, % undefined | new | pipeline | keep_alive | close | {ssl_tunnel, Request}
canceled = [], % [RequestId]
- max_header_size = nolimit, % nolimit | integer()
- max_body_size = nolimit, % nolimit | integer()
- options, % #options{}
- timers = #timers{}, % #timers{}
- profile_name, % atom() - id of httpc_manager process.
- once = inactive % inactive | once
+ max_header_size = nolimit :: nolimit | integer(),
+ max_body_size = nolimit :: nolimit | integer(),
+ options :: options(),
+ timers = #timers{} :: #timers{},
+ profile_name :: atom(), % id of httpc_manager process.
+ once = inactive :: 'inactive' | 'once'
}).
@@ -113,7 +114,7 @@ send(Request, Pid) ->
%%--------------------------------------------------------------------
%% Function: cancel(RequestId, Pid) -> ok
-%% RequestId = ref()
+%% RequestId = reference()
%% Pid = pid() - the pid of the http-request handler process.
%%
%% Description: Cancels a request. Intended to be called by the httpc
@@ -163,14 +164,12 @@ info(Pid) ->
%%--------------------------------------------------------------------
%% Request should not be streamed
stream(BodyPart, #request{stream = none} = Request, _) ->
- ?hcrt("stream - none", []),
{false, BodyPart, Request};
%% Stream to caller
stream(BodyPart, #request{stream = Self} = Request, Code)
when ?IS_STREAMED(Code) andalso
((Self =:= self) orelse (Self =:= {self, once})) ->
- ?hcrt("stream - self", [{stream, Self}, {code, Code}]),
httpc_response:send(Request#request.from,
{Request#request.id, stream, BodyPart}),
{true, <<>>, Request};
@@ -180,10 +179,8 @@ stream(BodyPart, #request{stream = Self} = Request, Code)
%% We keep this for backward compatibillity...
stream(BodyPart, #request{stream = Filename} = Request, Code)
when ?IS_STREAMED(Code) andalso is_list(Filename) ->
- ?hcrt("stream - filename", [{stream, Filename}, {code, Code}]),
case file:open(Filename, [write, raw, append, delayed_write]) of
{ok, Fd} ->
- ?hcrt("stream - file open ok", [{fd, Fd}]),
stream(BodyPart, Request#request{stream = Fd}, 200);
{error, Reason} ->
exit({stream_to_file_failed, Reason})
@@ -192,7 +189,6 @@ stream(BodyPart, #request{stream = Filename} = Request, Code)
%% Stream to file
stream(BodyPart, #request{stream = Fd} = Request, Code)
when ?IS_STREAMED(Code) ->
- ?hcrt("stream to file", [{stream, Fd}, {code, Code}]),
case file:write(Fd, BodyPart) of
ok ->
{true, <<>>, Request};
@@ -201,7 +197,6 @@ stream(BodyPart, #request{stream = Fd} = Request, Code)
end;
stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed
- ?hcrt("stream - ignore", [{request, Request}]),
{false, BodyPart, Request}.
@@ -255,22 +250,148 @@ init([Parent, Request, Options, ProfileName]) ->
%% {stop, Reason, State} (terminate/2 is called)
%% Description: Handling call messages
%%--------------------------------------------------------------------
-handle_call(#request{address = Addr} = Request, _,
+handle_call(Request, From, State) ->
+ try do_handle_call(Request, From, State) of
+ Result ->
+ Result
+ catch
+ _:Reason ->
+ {stop, {shutdown, Reason} , State}
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast(Msg, State) ->
+ try do_handle_cast(Msg, State) of
+ Result ->
+ Result
+ catch
+ _:Reason ->
+ {stop, {shutdown, Reason} , State}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info(Info, State) ->
+ try do_handle_info(Info, State) of
+ Result ->
+ Result
+ catch
+ _:Reason ->
+ {stop, {shutdown, Reason} , State}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> _ (ignored by gen_server)
+%% Description: Shutdown the httpc_handler
+%%--------------------------------------------------------------------
+
+%% Init error there is no socket to be closed.
+terminate(normal,
+ #state{request = Request,
+ session = {send_failed, _} = Reason} = State) ->
+ maybe_send_answer(Request,
+ httpc_response:error(Request, Reason),
+ State),
+ ok;
+
+terminate(normal,
+ #state{request = Request,
+ session = {connect_failed, _} = Reason} = State) ->
+ maybe_send_answer(Request,
+ httpc_response:error(Request, Reason),
+ State),
+ ok;
+
+terminate(normal, #state{session = undefined}) ->
+ ok;
+
+%% Init error sending, no session information has been setup but
+%% there is a socket that needs closing.
+terminate(normal,
+ #state{session = #session{id = undefined} = Session}) ->
+ close_socket(Session);
+
+%% Socket closed remotely
+terminate(normal,
+ #state{session = #session{socket = {remote_close, Socket},
+ socket_type = SocketType,
+ id = Id},
+ profile_name = ProfileName,
+ request = Request,
+ timers = Timers,
+ pipeline = Pipeline,
+ keep_alive = KeepAlive} = State) ->
+ %% Clobber session
+ (catch httpc_manager:delete_session(Id, ProfileName)),
+
+ maybe_retry_queue(Pipeline, State),
+ maybe_retry_queue(KeepAlive, State),
+
+ %% Cancel timers
+ cancel_timers(Timers),
+
+ %% Maybe deliver answers to requests
+ deliver_answer(Request),
+
+ %% And, just in case, close our side (**really** overkill)
+ http_transport:close(SocketType, Socket);
+
+terminate(_Reason, #state{session = #session{id = Id,
+ socket = Socket,
+ socket_type = SocketType},
+ request = undefined,
+ profile_name = ProfileName,
+ timers = Timers,
+ pipeline = Pipeline,
+ keep_alive = KeepAlive} = State) ->
+
+ %% Clobber session
+ (catch httpc_manager:delete_session(Id, ProfileName)),
+
+ maybe_retry_queue(Pipeline, State),
+ maybe_retry_queue(KeepAlive, State),
+
+ cancel_timer(Timers#timers.queue_timer, timeout_queue),
+ http_transport:close(SocketType, Socket);
+
+terminate(_Reason, #state{request = undefined}) ->
+ ok;
+
+terminate(Reason, #state{request = Request} = State) ->
+ NewState = maybe_send_answer(Request,
+ httpc_response:error(Request, Reason),
+ State),
+ terminate(Reason, NewState#state{request = undefined}).
+
+%%--------------------------------------------------------------------
+%% Func: code_change(_OldVsn, State, Extra) -> {ok, NewState}
+%% Purpose: Convert process state when code is changed
+%%--------------------------------------------------------------------
+
+code_change(_, State, _) ->
+ {ok, State}.
+
+%%%--------------------------------------------------------------------
+%%% Internal functions
+%%%--------------------------------------------------------------------
+do_handle_call(#request{address = Addr} = Request, _,
#state{status = Status,
session = #session{type = pipeline} = Session,
timers = Timers,
options = #options{proxy = Proxy} = _Options,
profile_name = ProfileName} = State0)
when Status =/= undefined ->
-
- ?hcrv("new request on a pipeline session",
- [{request, Request},
- {profile, ProfileName},
- {status, Status},
- {timers, Timers}]),
-
Address = handle_proxy(Addr, Proxy),
-
case httpc_request:send(Address, Session, Request) of
ok ->
@@ -285,9 +406,8 @@ handle_call(#request{address = Addr} = Request, _,
case State0#state.request of
#request{} = OldRequest -> %% Old request not yet finished
- ?hcrd("old request still not finished", []),
%% Make sure to use the new value of timers in state
- NewTimers = State1#state.timers,
+ NewTimers = State1#state.timers,
NewPipeline = queue:in(Request, State1#state.pipeline),
NewSession =
Session#session{queue_length =
@@ -295,7 +415,6 @@ handle_call(#request{address = Addr} = Request, _,
queue:len(NewPipeline) + 1,
client_close = ClientClose},
insert_session(NewSession, ProfileName),
- ?hcrd("session updated", []),
{reply, ok, State1#state{
request = OldRequest,
pipeline = NewPipeline,
@@ -304,7 +423,6 @@ handle_call(#request{address = Addr} = Request, _,
undefined ->
%% Note: tcp-message receiving has already been
%% activated by handle_pipeline/2.
- ?hcrd("no current request", []),
cancel_timer(Timers#timers.queue_timer,
timeout_queue),
NewSession =
@@ -312,18 +430,16 @@ handle_call(#request{address = Addr} = Request, _,
client_close = ClientClose},
httpc_manager:insert_session(NewSession, ProfileName),
NewTimers = Timers#timers{queue_timer = undefined},
- ?hcrd("session created", []),
State = init_wait_for_response_state(Request, State1#state{session = NewSession,
timers = NewTimers}),
{reply, ok, State}
end;
{error, Reason} ->
- ?hcri("failed sending request", [{reason, Reason}]),
NewPipeline = queue:in(Request, State0#state.pipeline),
- {stop, shutdown, {pipeline_failed, Reason}, State0#state{pipeline = NewPipeline}}
+ {stop, {shutdown, {pipeline_failed, Reason}}, State0#state{pipeline = NewPipeline}}
end;
-handle_call(#request{address = Addr} = Request, _,
+do_handle_call(#request{address = Addr} = Request, _,
#state{status = Status,
session = #session{type = keep_alive} = Session,
timers = Timers,
@@ -331,17 +447,11 @@ handle_call(#request{address = Addr} = Request, _,
profile_name = ProfileName} = State0)
when Status =/= undefined ->
- ?hcrv("new request on a keep-alive session",
- [{request, Request},
- {profile, ProfileName},
- {status, Status}]),
-
ClientClose = httpc_request:is_client_closing(Request#request.headers),
case State0#state.request of
#request{} -> %% Old request not yet finished
%% Make sure to use the new value of timers in state
- ?hcrd("old request still not finished", []),
NewKeepAlive = queue:in(Request, State0#state.keep_alive),
NewSession =
Session#session{queue_length =
@@ -349,13 +459,11 @@ handle_call(#request{address = Addr} = Request, _,
queue:len(NewKeepAlive) + 1,
client_close = ClientClose},
insert_session(NewSession, ProfileName),
- ?hcrd("session updated", []),
{reply, ok, State0#state{keep_alive = NewKeepAlive,
session = NewSession}};
undefined ->
%% Note: tcp-message receiving has already been
%% activated by handle_pipeline/2.
- ?hcrd("no current request", []),
cancel_timer(Timers#timers.queue_timer,
timeout_queue),
NewTimers = Timers#timers{queue_timer = undefined},
@@ -363,8 +471,6 @@ handle_call(#request{address = Addr} = Request, _,
Address = handle_proxy(Addr, Proxy),
case httpc_request:send(Address, Session, Request) of
ok ->
- ?hcrd("request sent", []),
-
%% Activate the request time out for the new request
State2 =
activate_request_timeout(State1#state{request = Request}),
@@ -375,22 +481,13 @@ handle_call(#request{address = Addr} = Request, _,
State = init_wait_for_response_state(Request, State2#state{session = NewSession}),
{reply, ok, State};
{error, Reason} ->
- ?hcri("failed sending request", [{reason, Reason}]),
- {stop, shutdown, {keepalive_failed, Reason}, State1}
+ {stop, {shutdown, {keepalive_failed, Reason}}, State1}
end
end;
-
-handle_call(info, _, State) ->
+do_handle_call(info, _, State) ->
Info = handler_info(State),
{reply, Info, State}.
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State} (terminate/2 is called)
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-
%% When the request in process has been canceled the handler process is
%% stopped and the pipelined requests will be reissued or remaining
%% requests will be sent on a new connection. This is is
@@ -403,145 +500,102 @@ handle_call(info, _, State) ->
%% handle_keep_alive_queue/2 on the other hand will just skip the
%% request as if it was never issued as in this case the request will
%% not have been sent.
-handle_cast({cancel, RequestId},
+do_handle_cast({cancel, RequestId},
#state{request = #request{id = RequestId} = Request,
- profile_name = ProfileName,
canceled = Canceled} = State) ->
- ?hcrv("cancel current request", [{request_id, RequestId},
- {profile, ProfileName},
- {canceled, Canceled}]),
{stop, normal,
State#state{canceled = [RequestId | Canceled],
request = Request#request{from = answer_sent}}};
-handle_cast({cancel, RequestId},
- #state{profile_name = ProfileName,
- request = #request{id = CurrId},
- canceled = Canceled} = State) ->
- ?hcrv("cancel", [{request_id, RequestId},
- {curr_req_id, CurrId},
- {profile, ProfileName},
- {canceled, Canceled}]),
+do_handle_cast({cancel, RequestId},
+ #state{request = #request{},
+ canceled = Canceled} = State) ->
{noreply, State#state{canceled = [RequestId | Canceled]}};
-handle_cast({cancel, RequestId},
- #state{profile_name = ProfileName,
- request = undefined,
- canceled = Canceled} = State) ->
- ?hcrv("cancel", [{request_id, RequestId},
- {curr_req_id, undefined},
- {profile, ProfileName},
- {canceled, Canceled}]),
+do_handle_cast({cancel, _},
+ #state{request = undefined} = State) ->
{noreply, State};
-
-handle_cast(stream_next, #state{session = Session} = State) ->
+do_handle_cast(stream_next, #state{session = Session} = State) ->
activate_once(Session),
%% Inactivate the #state.once here because we don't want
%% next_body_chunk/1 to activate the socket twice.
{noreply, State#state{once = inactive}}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State} (terminate/2 is called)
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-handle_info({Proto, _Socket, Data},
+do_handle_info({Proto, _Socket, Data},
#state{mfa = {Module, Function, Args},
- request = #request{method = Method,
- stream = Stream} = Request,
+ request = #request{method = Method} = Request,
session = Session,
status_line = StatusLine} = State)
when (Proto =:= tcp) orelse
(Proto =:= ssl) orelse
(Proto =:= httpc_handler) ->
- ?hcri("received data", [{proto, Proto},
- {module, Module},
- {function, Function},
- {method, Method},
- {stream, Stream},
- {session, Session},
- {status_line, StatusLine}]),
-
- FinalResult =
- try Module:Function([Data | Args]) of
- {ok, Result} ->
- ?hcrd("data processed - ok", []),
- handle_http_msg(Result, State);
- {_, whole_body, _} when Method =:= head ->
- ?hcrd("data processed - whole body", []),
- handle_response(State#state{body = <<>>});
- {Module, whole_body, [Body, Length]} ->
- ?hcrd("data processed - whole body", [{length, Length}]),
- {_, Code, _} = StatusLine,
- {Streamed, NewBody, NewRequest} = stream(Body, Request, Code),
- %% When we stream we will not keep the already
- %% streamed data, that would be a waste of memory.
- NewLength =
- case Streamed of
- false ->
- Length;
- true ->
- Length - size(Body)
- end,
-
- NewState = next_body_chunk(State, Code),
- NewMFA = {Module, whole_body, [NewBody, NewLength]},
- {noreply, NewState#state{mfa = NewMFA,
- request = NewRequest}};
- {Module, decode_size,
- [TotalChunk, HexList,
+ try Module:Function([Data | Args]) of
+ {ok, Result} ->
+ handle_http_msg(Result, State);
+ {_, whole_body, _} when Method =:= head ->
+ handle_response(State#state{body = <<>>});
+ {Module, whole_body, [Body, Length]} ->
+ {_, Code, _} = StatusLine,
+ {Streamed, NewBody, NewRequest} = stream(Body, Request, Code),
+ %% When we stream we will not keep the already
+ %% streamed data, that would be a waste of memory.
+ NewLength =
+ case Streamed of
+ false ->
+ Length;
+ true ->
+ Length - size(Body)
+ end,
+
+ NewState = next_body_chunk(State, Code),
+ NewMFA = {Module, whole_body, [NewBody, NewLength]},
+ {noreply, NewState#state{mfa = NewMFA,
+ request = NewRequest}};
+ {Module, decode_size,
+ [TotalChunk, HexList, AccHeaderSize,
{MaxBodySize, BodySoFar, AccLength, MaxHeaderSize}]}
- when BodySoFar =/= <<>> ->
- ?hcrd("data processed - decode_size", []),
- %% The response body is chunk-encoded. Steal decoded
- %% chunks as much as possible to stream.
- {_, Code, _} = StatusLine,
- {_, NewBody, NewRequest} = stream(BodySoFar, Request, Code),
- NewState = next_body_chunk(State, Code),
- NewMFA = {Module, decode_size,
- [TotalChunk, HexList,
+ when BodySoFar =/= <<>> ->
+ %% The response body is chunk-encoded. Steal decoded
+ %% chunks as much as possible to stream.
+ {_, Code, _} = StatusLine,
+ {_, NewBody, NewRequest} = stream(BodySoFar, Request, Code),
+ NewState = next_body_chunk(State, Code),
+ NewMFA = {Module, decode_size,
+ [TotalChunk, HexList, AccHeaderSize,
{MaxBodySize, NewBody, AccLength, MaxHeaderSize}]},
+ {noreply, NewState#state{mfa = NewMFA,
+ request = NewRequest}};
+ {Module, decode_data,
+ [ChunkSize, TotalChunk,
+ {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize}]}
+ when TotalChunk =/= <<>> orelse BodySoFar =/= <<>> ->
+ %% The response body is chunk-encoded. Steal decoded
+ %% chunks as much as possible to stream.
+ ChunkSizeToSteal = min(ChunkSize, byte_size(TotalChunk)),
+ <<StolenChunk:ChunkSizeToSteal/binary, NewTotalChunk/binary>> = TotalChunk,
+ StolenBody = <<BodySoFar/binary, StolenChunk/binary>>,
+ NewChunkSize = ChunkSize - ChunkSizeToSteal,
+ {_, Code, _} = StatusLine,
+
+ {_, NewBody, NewRequest} = stream(StolenBody, Request, Code),
+ NewState = next_body_chunk(State, Code),
+ NewMFA = {Module, decode_data,
+ [NewChunkSize, NewTotalChunk,
+ {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]},
{noreply, NewState#state{mfa = NewMFA,
request = NewRequest}};
- {Module, decode_data,
- [ChunkSize, TotalChunk,
- {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize}]}
- when TotalChunk =/= <<>> orelse BodySoFar =/= <<>> ->
- ?hcrd("data processed - decode_data", []),
- %% The response body is chunk-encoded. Steal decoded
- %% chunks as much as possible to stream.
- ChunkSizeToSteal = min(ChunkSize, byte_size(TotalChunk)),
- <<StolenChunk:ChunkSizeToSteal/binary, NewTotalChunk/binary>> = TotalChunk,
- StolenBody = <<BodySoFar/binary, StolenChunk/binary>>,
- NewChunkSize = ChunkSize - ChunkSizeToSteal,
- {_, Code, _} = StatusLine,
-
- {_, NewBody, NewRequest} = stream(StolenBody, Request, Code),
- NewState = next_body_chunk(State, Code),
- NewMFA = {Module, decode_data,
- [NewChunkSize, NewTotalChunk,
- {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]},
- {noreply, NewState#state{mfa = NewMFA,
- request = NewRequest}};
- NewMFA ->
- ?hcrd("data processed - new mfa", []),
- activate_once(Session),
- {noreply, State#state{mfa = NewMFA}}
- catch
- _:_Reason ->
- ?hcrd("data processing exit", [{exit, _Reason}]),
- ClientReason = {could_not_parse_as_http, Data},
- ClientErrMsg = httpc_response:error(Request, ClientReason),
- NewState = answer_request(Request, ClientErrMsg, State),
- {stop, normal, NewState}
- end,
- ?hcri("data processed", [{final_result, FinalResult}]),
- FinalResult;
-
+ NewMFA ->
+ activate_once(Session),
+ {noreply, State#state{mfa = NewMFA}}
+ catch
+ _:Reason ->
+ ClientReason = {could_not_parse_as_http, Data},
+ ClientErrMsg = httpc_response:error(Request, ClientReason),
+ NewState = answer_request(Request, ClientErrMsg, State),
+ {stop, {shutdown, Reason}, NewState}
+ end;
-handle_info({Proto, Socket, Data},
+do_handle_info({Proto, Socket, Data},
#state{mfa = MFA,
request = Request,
session = Session,
@@ -566,200 +620,107 @@ handle_info({Proto, Socket, Data},
{noreply, State};
-
%% The Server may close the connection to indicate that the
%% whole body is now sent instead of sending an length
%% indicator.
-handle_info({tcp_closed, _}, State = #state{mfa = {_, whole_body, Args}}) ->
+do_handle_info({tcp_closed, _}, State = #state{mfa = {_, whole_body, Args}}) ->
handle_response(State#state{body = hd(Args)});
-handle_info({ssl_closed, _}, State = #state{mfa = {_, whole_body, Args}}) ->
+do_handle_info({ssl_closed, _}, State = #state{mfa = {_, whole_body, Args}}) ->
handle_response(State#state{body = hd(Args)});
%%% Server closes idle pipeline
-handle_info({tcp_closed, _}, State = #state{request = undefined}) ->
+do_handle_info({tcp_closed, _}, State = #state{request = undefined}) ->
{stop, normal, State};
-handle_info({ssl_closed, _}, State = #state{request = undefined}) ->
+do_handle_info({ssl_closed, _}, State = #state{request = undefined}) ->
{stop, normal, State};
%%% Error cases
-handle_info({tcp_closed, _}, #state{session = Session0} = State) ->
+do_handle_info({tcp_closed, _}, #state{session = Session0} = State) ->
Socket = Session0#session.socket,
Session = Session0#session{socket = {remote_close, Socket}},
%% {stop, session_remotly_closed, State};
{stop, normal, State#state{session = Session}};
-handle_info({ssl_closed, _}, #state{session = Session0} = State) ->
+do_handle_info({ssl_closed, _}, #state{session = Session0} = State) ->
Socket = Session0#session.socket,
Session = Session0#session{socket = {remote_close, Socket}},
%% {stop, session_remotly_closed, State};
{stop, normal, State#state{session = Session}};
-handle_info({tcp_error, _, _} = Reason, State) ->
+do_handle_info({tcp_error, _, _} = Reason, State) ->
{stop, Reason, State};
-handle_info({ssl_error, _, _} = Reason, State) ->
+do_handle_info({ssl_error, _, _} = Reason, State) ->
{stop, Reason, State};
%% Timeouts
%% Internally, to a request handling process, a request timeout is
%% seen as a canceled request.
-handle_info({timeout, RequestId},
+do_handle_info({timeout, RequestId},
#state{request = #request{id = RequestId} = Request,
canceled = Canceled,
profile_name = ProfileName} = State) ->
- ?hcri("timeout of current request", [{id, RequestId}]),
httpc_response:send(Request#request.from,
httpc_response:error(Request, timeout)),
httpc_manager:request_done(RequestId, ProfileName),
- ?hcrv("response (timeout) sent - now terminate", []),
{stop, normal,
State#state{request = Request#request{from = answer_sent},
canceled = [RequestId | Canceled]}};
-handle_info({timeout, RequestId},
+do_handle_info({timeout, RequestId},
#state{canceled = Canceled,
profile_name = ProfileName} = State) ->
- ?hcri("timeout", [{id, RequestId}]),
Filter =
fun(#request{id = Id, from = From} = Request) when Id =:= RequestId ->
- ?hcrv("found request", [{id, Id}, {from, From}]),
%% Notify the owner
httpc_response:send(From,
httpc_response:error(Request, timeout)),
httpc_manager:request_done(RequestId, ProfileName),
- ?hcrv("response (timeout) sent", []),
[Request#request{from = answer_sent}];
(_) ->
true
end,
case State#state.status of
pipeline ->
- ?hcrd("pipeline", []),
Pipeline = queue:filter(Filter, State#state.pipeline),
{noreply, State#state{canceled = [RequestId | Canceled],
pipeline = Pipeline}};
keep_alive ->
- ?hcrd("keep_alive", []),
KeepAlive = queue:filter(Filter, State#state.keep_alive),
{noreply, State#state{canceled = [RequestId | Canceled],
keep_alive = KeepAlive}}
end;
-handle_info(timeout_queue, State = #state{request = undefined}) ->
+do_handle_info(timeout_queue, State = #state{request = undefined}) ->
{stop, normal, State};
%% Timing was such as the queue_timeout was not canceled!
-handle_info(timeout_queue, #state{timers = Timers} = State) ->
+do_handle_info(timeout_queue, #state{timers = Timers} = State) ->
{noreply, State#state{timers =
Timers#timers{queue_timer = undefined}}};
%% Setting up the connection to the server somehow failed.
-handle_info({init_error, Tag, ClientErrMsg},
+do_handle_info({init_error, Reason, ClientErrMsg},
State = #state{request = Request}) ->
- ?hcrv("init error", [{tag, Tag}, {client_error, ClientErrMsg}]),
NewState = answer_request(Request, ClientErrMsg, State),
- {stop, normal, NewState};
-
+ {stop, {shutdown, Reason}, NewState};
%%% httpc_manager process dies.
-handle_info({'EXIT', _, _}, State = #state{request = undefined}) ->
+do_handle_info({'EXIT', _, _}, State = #state{request = undefined}) ->
{stop, normal, State};
%%Try to finish the current request anyway,
%% there is a fairly high probability that it can be done successfully.
%% Then close the connection, hopefully a new manager is started that
%% can retry requests in the pipeline.
-handle_info({'EXIT', _, _}, State) ->
+do_handle_info({'EXIT', _, _}, State) ->
{noreply, State#state{status = close}}.
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> _ (ignored by gen_server)
-%% Description: Shutdown the httpc_handler
-%%--------------------------------------------------------------------
-
-%% Init error there is no socket to be closed.
-terminate(normal,
- #state{request = Request,
- session = {send_failed, AReason} = Reason} = State) ->
- ?hcrd("terminate", [{send_reason, AReason}, {request, Request}]),
- maybe_send_answer(Request,
- httpc_response:error(Request, Reason),
- State),
- ok;
-
-terminate(normal,
- #state{request = Request,
- session = {connect_failed, AReason} = Reason} = State) ->
- ?hcrd("terminate", [{connect_reason, AReason}, {request, Request}]),
- maybe_send_answer(Request,
- httpc_response:error(Request, Reason),
- State),
- ok;
-
-terminate(normal, #state{session = undefined}) ->
- ok;
-
-%% Init error sending, no session information has been setup but
-%% there is a socket that needs closing.
-terminate(normal,
- #state{session = #session{id = undefined} = Session}) ->
- close_socket(Session);
-
-%% Socket closed remotely
-terminate(normal,
- #state{session = #session{socket = {remote_close, Socket},
- socket_type = SocketType,
- id = Id},
- profile_name = ProfileName,
- request = Request,
- timers = Timers,
- pipeline = Pipeline,
- keep_alive = KeepAlive} = State) ->
- ?hcrt("terminate(normal) - remote close",
- [{id, Id}, {profile, ProfileName}]),
-
- %% Clobber session
- (catch httpc_manager:delete_session(Id, ProfileName)),
-
- maybe_retry_queue(Pipeline, State),
- maybe_retry_queue(KeepAlive, State),
-
- %% Cancel timers
- cancel_timers(Timers),
-
- %% Maybe deliver answers to requests
- deliver_answer(Request),
-
- %% And, just in case, close our side (**really** overkill)
- http_transport:close(SocketType, Socket);
-
-terminate(Reason, #state{session = #session{id = Id,
- socket = Socket,
- socket_type = SocketType},
- request = undefined,
- profile_name = ProfileName,
- timers = Timers,
- pipeline = Pipeline,
- keep_alive = KeepAlive} = State) ->
- ?hcrt("terminate",
- [{id, Id}, {profile, ProfileName}, {reason, Reason}]),
-
- %% Clobber session
- (catch httpc_manager:delete_session(Id, ProfileName)),
-
- maybe_retry_queue(Pipeline, State),
- maybe_retry_queue(KeepAlive, State),
-
- cancel_timer(Timers#timers.queue_timer, timeout_queue),
- http_transport:close(SocketType, Socket);
+call(Msg, Pid) ->
+ call(Msg, Pid, infinity).
-terminate(Reason, #state{request = undefined}) ->
- ?hcrt("terminate", [{reason, Reason}]),
- ok;
+call(Msg, Pid, Timeout) ->
+ gen_server:call(Pid, Msg, Timeout).
-terminate(Reason, #state{request = Request} = State) ->
- ?hcrd("terminate", [{reason, Reason}, {request, Request}]),
- NewState = maybe_send_answer(Request,
- httpc_response:error(Request, Reason),
- State),
- terminate(Reason, NewState#state{request = undefined}).
+cast(Msg, Pid) ->
+ gen_server:cast(Pid, Msg).
maybe_retry_queue(Q, State) ->
case queue:is_empty(Q) of
@@ -774,86 +735,13 @@ maybe_send_answer(#request{from = answer_sent}, _Reason, State) ->
maybe_send_answer(Request, Answer, State) ->
answer_request(Request, Answer, State).
-deliver_answer(#request{id = Id, from = From} = Request)
+deliver_answer(#request{from = From} = Request)
when is_pid(From) ->
Response = httpc_response:error(Request, socket_closed_remotely),
- ?hcrd("deliver answer", [{id, Id}, {from, From}, {response, Response}]),
httpc_response:send(From, Response);
-deliver_answer(Request) ->
- ?hcrd("skip deliver answer", [{request, Request}]),
+deliver_answer(_Request) ->
ok.
-
-%%--------------------------------------------------------------------
-%% Func: code_change(_OldVsn, State, Extra) -> {ok, NewState}
-%% Purpose: Convert process state when code is changed
-%%--------------------------------------------------------------------
-
-code_change(_,
- #state{session = OldSession,
- profile_name = ProfileName} = State,
- upgrade_from_pre_5_8_1) ->
- case OldSession of
- {session,
- Id, ClientClose, Scheme, Socket, SocketType, QueueLen, Type} ->
- NewSession = #session{id = Id,
- client_close = ClientClose,
- scheme = Scheme,
- socket = Socket,
- socket_type = SocketType,
- queue_length = QueueLen,
- type = Type},
- insert_session(NewSession, ProfileName),
- {ok, State#state{session = NewSession}};
- _ ->
- {ok, State}
- end;
-
-code_change(_,
- #state{session = OldSession,
- profile_name = ProfileName} = State,
- downgrade_to_pre_5_8_1) ->
- case OldSession of
- #session{id = Id,
- client_close = ClientClose,
- scheme = Scheme,
- socket = Socket,
- socket_type = SocketType,
- queue_length = QueueLen,
- type = Type} ->
- NewSession = {session,
- Id, ClientClose, Scheme, Socket, SocketType,
- QueueLen, Type},
- insert_session(NewSession, ProfileName),
- {ok, State#state{session = NewSession}};
- _ ->
- {ok, State}
- end;
-
-code_change(_, State, _) ->
- {ok, State}.
-
-
-%% new_http_options({http_options, TimeOut, AutoRedirect, SslOpts,
-%% Auth, Relaxed}) ->
-%% {http_options, "HTTP/1.1", TimeOut, AutoRedirect, SslOpts,
-%% Auth, Relaxed}.
-
-%% old_http_options({http_options, _, TimeOut, AutoRedirect,
-%% SslOpts, Auth, Relaxed}) ->
-%% {http_options, TimeOut, AutoRedirect, SslOpts, Auth, Relaxed}.
-
-%% new_queue(Queue, Fun) ->
-%% List = queue:to_list(Queue),
-%% NewList =
-%% lists:map(fun(Request) ->
-%% Settings =
-%% Fun(Request#request.settings),
-%% Request#request{settings = Settings}
-%% end, List),
-%% queue:from_list(NewList).
-
-
%%%--------------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -911,31 +799,25 @@ connect(SocketType, ToAddress,
connect_and_send_first_request(Address, Request, #state{options = Options} = State) ->
SocketType = socket_type(Request),
ConnTimeout = (Request#request.settings)#http_options.connect_timeout,
- ?hcri("connect",
- [{address, Address}, {request, Request}, {options, Options}]),
case connect(SocketType, Address, Options, ConnTimeout) of
{ok, Socket} ->
ClientClose =
- httpc_request:is_client_closing(
- Request#request.headers),
+ httpc_request:is_client_closing(
+ Request#request.headers),
SessionType = httpc_manager:session_type(Options),
SocketType = socket_type(Request),
Session = #session{id = {Request#request.address, self()},
scheme = Request#request.scheme,
socket = Socket,
- socket_type = SocketType,
- client_close = ClientClose,
- type = SessionType},
- ?hcri("connected - now send first request", [{socket, Socket}]),
-
+ socket_type = SocketType,
+ client_close = ClientClose,
+ type = SessionType},
case httpc_request:send(Address, Session, Request) of
ok ->
- ?hcri("first request sent", []),
TmpState = State#state{request = Request,
session = Session,
mfa = init_mfa(Request, State),
- status_line =
- init_status_line(Request),
+ status_line = init_status_line(Request),
headers = undefined,
body = undefined,
status = new},
@@ -947,8 +829,7 @@ connect_and_send_first_request(Address, Request, #state{options = Options} = Sta
self() ! {init_error, error_sending,
httpc_response:error(Request, Reason)},
{ok, State#state{request = Request,
- session =
- #session{socket = Socket}}}
+ session = #session{socket = Socket}}}
end;
{error, Reason} ->
self() ! {init_error, error_connecting,
@@ -990,12 +871,6 @@ handler_info(#state{request = Request,
options = _Options,
timers = _Timers} = _State) ->
- ?hcrt("handler info", [{request, Request},
- {session, Session},
- {pipeline, Pipeline},
- {keep_alive, KeepAlive},
- {status, Status}]),
-
%% Info about the current request
RequestInfo =
case Request of
@@ -1006,8 +881,6 @@ handler_info(#state{request = Request,
[{id, Id}, {started, ReqStarted}]
end,
- ?hcrt("handler info", [{request_info, RequestInfo}]),
-
%% Info about the current session/socket
SessionType = Session#session.type,
QueueLen = case SessionType of
@@ -1020,22 +893,12 @@ handler_info(#state{request = Request,
Socket = Session#session.socket,
SocketType = Session#session.socket_type,
- ?hcrt("handler info", [{session_type, SessionType},
- {queue_length, QueueLen},
- {scheme, Scheme},
- {socket, Socket}]),
-
SocketOpts = http_transport:getopts(SocketType, Socket),
SocketStats = http_transport:getstat(SocketType, Socket),
Remote = http_transport:peername(SocketType, Socket),
Local = http_transport:sockname(SocketType, Socket),
- ?hcrt("handler info", [{remote, Remote},
- {local, Local},
- {socket_opts, SocketOpts},
- {socket_stats, SocketStats}]),
-
SocketInfo = [{remote, Remote},
{local, Local},
{socket_opts, SocketOpts},
@@ -1055,7 +918,6 @@ handler_info(#state{request = Request,
handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body},
State = #state{request = Request}) ->
- ?hcrt("handle_http_msg", [{headers, Headers}]),
case Headers#http_response_h.'content-type' of
"multipart/byteranges" ++ _Param ->
exit({not_yet_implemented, multypart_byteranges});
@@ -1069,15 +931,12 @@ handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body},
end;
handle_http_msg({ChunkedHeaders, Body},
#state{status_line = {_, Code, _}, headers = Headers} = State) ->
- ?hcrt("handle_http_msg",
- [{chunked_headers, ChunkedHeaders}, {headers, Headers}]),
NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
{_, NewBody, NewRequest} = stream(Body, State#state.request, Code),
handle_response(State#state{headers = NewHeaders,
body = NewBody,
request = NewRequest});
handle_http_msg(Body, #state{status_line = {_,Code, _}} = State) ->
- ?hcrt("handle_http_msg", [{code, Code}]),
{_, NewBody, NewRequest} = stream(Body, State#state.request, Code),
handle_response(State#state{body = NewBody, request = NewRequest}).
@@ -1092,41 +951,28 @@ handle_http_body(_, #state{status = {ssl_tunnel, Request},
{stop, normal, NewState};
handle_http_body(<<>>, #state{status_line = {_,304, _}} = State) ->
- ?hcrt("handle_http_body - 304", []),
handle_response(State#state{body = <<>>});
handle_http_body(<<>>, #state{status_line = {_,204, _}} = State) ->
- ?hcrt("handle_http_body - 204", []),
handle_response(State#state{body = <<>>});
handle_http_body(<<>>, #state{request = #request{method = head}} = State) ->
- ?hcrt("handle_http_body - head", []),
handle_response(State#state{body = <<>>});
handle_http_body(Body, #state{headers = Headers,
max_body_size = MaxBodySize,
status_line = {_,Code, _},
request = Request} = State) ->
- ?hcrt("handle_http_body",
- [{max_body_size, MaxBodySize}, {headers, Headers}, {code, Code}]),
TransferEnc = Headers#http_response_h.'transfer-encoding',
case case_insensitive_header(TransferEnc) of
"chunked" ->
- ?hcrt("handle_http_body - chunked", []),
try http_chunk:decode(Body, State#state.max_body_size,
State#state.max_header_size) of
{Module, Function, Args} ->
- ?hcrt("handle_http_body - new mfa",
- [{module, Module},
- {function, Function},
- {args, Args}]),
NewState = next_body_chunk(State, Code),
{noreply, NewState#state{mfa =
{Module, Function, Args}}};
{ok, {ChunkedHeaders, NewBody}} ->
- ?hcrt("handle_http_body - new body",
- [{chunked_headers, ChunkedHeaders},
- {new_body, NewBody}]),
NewHeaders = http_chunk:handle_headers(Headers,
ChunkedHeaders),
case Body of
@@ -1148,7 +994,6 @@ handle_http_body(Body, #state{headers = Headers,
{stop, normal, NewState}
end;
Enc when Enc =:= "identity"; Enc =:= undefined ->
- ?hcrt("handle_http_body - identity", []),
Length =
list_to_integer(Headers#http_response_h.'content-length'),
case ((Length =< MaxBodySize) orelse (MaxBodySize =:= nolimit)) of
@@ -1172,7 +1017,6 @@ handle_http_body(Body, #state{headers = Headers,
{stop, normal, NewState}
end;
Encoding when is_list(Encoding) ->
- ?hcrt("handle_http_body - other", [{encoding, Encoding}]),
NewState = answer_request(Request,
httpc_response:error(Request,
unknown_encoding),
@@ -1193,18 +1037,10 @@ handle_response(#state{request = Request,
options = Options,
profile_name = ProfileName} = State)
when Status =/= new ->
-
- ?hcrd("handle response", [{profile, ProfileName},
- {status, Status},
- {request, Request},
- {session, Session},
- {status_line, StatusLine}]),
-
handle_cookies(Headers, Request, Options, ProfileName),
case httpc_response:result({StatusLine, Headers, Body}, Request) of
%% 100-continue
continue ->
- ?hcrd("handle response - continue", []),
%% Send request body
{_, RequestBody} = Request#request.content,
send_raw(Session, RequestBody),
@@ -1221,7 +1057,6 @@ handle_response(#state{request = Request,
%% Ignore unexpected 100-continue response and receive the
%% actual response that the server will send right away.
{ignore, Data} ->
- ?hcrd("handle response - ignore", [{data, Data}]),
Relaxed = (Request#request.settings)#http_options.relaxed,
MFA = {httpc_response, parse,
[State#state.max_header_size, Relaxed]},
@@ -1235,23 +1070,17 @@ handle_response(#state{request = Request,
%% obsolete and the manager will create a new request
%% with the same id as the current.
{redirect, NewRequest, Data} ->
- ?hcrt("handle response - redirect",
- [{new_request, NewRequest}, {data, Data}]),
ok = httpc_manager:redirect_request(NewRequest, ProfileName),
handle_queue(State#state{request = undefined}, Data);
{retry, TimeNewRequest, Data} ->
- ?hcrt("handle response - retry",
- [{time_new_request, TimeNewRequest}, {data, Data}]),
ok = httpc_manager:retry_request(TimeNewRequest, ProfileName),
handle_queue(State#state{request = undefined}, Data);
{ok, Msg, Data} ->
- ?hcrd("handle response - ok", []),
stream_remaining_body(Body, Request, StatusLine),
end_stream(StatusLine, Request),
NewState = maybe_send_answer(Request, Msg, State),
handle_queue(NewState, Data);
{stop, Msg} ->
- ?hcrd("handle response - stop", [{msg, Msg}]),
end_stream(StatusLine, Request),
NewState = maybe_send_answer(Request, Msg, State),
{stop, normal, NewState}
@@ -1286,28 +1115,19 @@ handle_pipeline(#state{status = pipeline,
profile_name = ProfileName,
options = #options{pipeline_timeout = TimeOut}} = State,
Data) ->
-
- ?hcrd("handle pipeline", [{profile, ProfileName},
- {session, Session},
- {timeout, TimeOut}]),
-
case queue:out(State#state.pipeline) of
{empty, _} ->
- ?hcrd("pipeline queue empty", []),
handle_empty_queue(Session, ProfileName, TimeOut, State);
{{value, NextRequest}, Pipeline} ->
- ?hcrd("pipeline queue non-empty", []),
case lists:member(NextRequest#request.id,
State#state.canceled) of
true ->
- ?hcrv("next request had been cancelled", []),
%% See comment for handle_cast({cancel, RequestId})
{stop, normal,
State#state{request =
NextRequest#request{from = answer_sent},
pipeline = Pipeline}};
false ->
- ?hcrv("next request", [{request, NextRequest}]),
NewSession =
Session#session{queue_length =
%% Queue + current
@@ -1324,25 +1144,16 @@ handle_keep_alive_queue(#state{status = keep_alive,
options = #options{keep_alive_timeout = TimeOut,
proxy = Proxy}} = State,
Data) ->
-
- ?hcrd("handle keep_alive", [{profile, ProfileName},
- {session, Session},
- {timeout, TimeOut}]),
-
case queue:out(State#state.keep_alive) of
{empty, _} ->
- ?hcrd("keep_alive queue empty", []),
handle_empty_queue(Session, ProfileName, TimeOut, State);
{{value, NextRequest}, KeepAlive} ->
- ?hcrd("keep_alive queue non-empty", []),
case lists:member(NextRequest#request.id,
State#state.canceled) of
true ->
- ?hcrv("next request has already been canceled", []),
handle_keep_alive_queue(
State#state{keep_alive = KeepAlive}, Data);
false ->
- ?hcrv("next request", [{request, NextRequest}]),
#request{address = Addr} = NextRequest,
Address = handle_proxy(Addr, Proxy),
case httpc_request:send(Address, Session, NextRequest) of
@@ -1355,7 +1166,6 @@ handle_keep_alive_queue(#state{status = keep_alive,
end
end
end.
-
handle_empty_queue(Session, ProfileName, TimeOut, State) ->
%% The server may choose too terminate an idle pipline| keep_alive session
%% in this case we want to receive the close message
@@ -1391,7 +1201,6 @@ init_wait_for_response_state(Request, State) ->
status_line = undefined,
headers = undefined,
body = undefined}.
-
gather_data(<<>>, Session, State) ->
activate_once(Session),
{noreply, State};
@@ -1422,10 +1231,6 @@ activate_request_timeout(
State;
_ ->
ReqId = Request#request.id,
- ?hcrt("activate request timer",
- [{request_id, ReqId},
- {time_consumed, t() - Request#request.started},
- {timeout, Timeout}]),
Msg = {timeout, ReqId},
Ref = erlang:send_after(Timeout, self(), Msg),
Request2 = Request#request{timer = Ref},
@@ -1468,10 +1273,6 @@ try_to_enable_pipeline_or_keep_alive(
status_line = {Version, _, _},
headers = Headers,
profile_name = ProfileName} = State) ->
- ?hcrd("try to enable pipeline or keep-alive",
- [{version, Version},
- {headers, Headers},
- {session, Session}]),
case is_keep_alive_enabled_server(Version, Headers) andalso
is_keep_alive_connection(Headers, Session) of
true ->
@@ -1496,7 +1297,6 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg,
#state{session = Session,
timers = Timers,
profile_name = ProfileName} = State) ->
- ?hcrt("answer request", [{request, Request}, {msg, Msg}]),
httpc_response:send(From, Msg),
RequestTimers = Timers#timers.request_timers,
TimerRef =
@@ -1643,42 +1443,32 @@ socket_type(#request{scheme = http}) ->
ip_comm;
socket_type(#request{scheme = https, settings = Settings}) ->
Settings#http_options.ssl.
-%% socket_type(http) ->
-%% ip_comm;
-%% socket_type(https) ->
-%% {ssl1, []}. %% Dummy value ok for ex setopts that does not use this value
start_stream({_Version, _Code, _ReasonPhrase}, _Headers,
#request{stream = none} = Request) ->
- ?hcrt("start stream - none", []),
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, Headers,
#request{stream = self} = Request)
when ?IS_STREAMED(Code) ->
- ?hcrt("start stream - self", [{code, Code}]),
Msg = httpc_response:stream_start(Headers, Request, ignore),
httpc_response:send(Request#request.from, Msg),
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, Headers,
#request{stream = {self, once}} = Request)
when ?IS_STREAMED(Code) ->
- ?hcrt("start stream - self:once", [{code, Code}]),
Msg = httpc_response:stream_start(Headers, Request, self()),
httpc_response:send(Request#request.from, Msg),
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, _Headers,
#request{stream = Filename} = Request)
when ?IS_STREAMED(Code) andalso is_list(Filename) ->
- ?hcrt("start stream", [{code, Code}, {filename, Filename}]),
case file:open(Filename, [write, raw, append, delayed_write]) of
{ok, Fd} ->
- ?hcri("start stream - file open ok", [{fd, Fd}]),
{ok, Request#request{stream = Fd}};
{error, Reason} ->
exit({stream_to_file_failed, Reason})
end;
start_stream(_StatusLine, _Headers, Request) ->
- ?hcrt("start stream - no op", []),
{ok, Request}.
stream_remaining_body(<<>>, _, _) ->
@@ -1689,16 +1479,12 @@ stream_remaining_body(Body, Request, {_, Code, _}) ->
%% Note the end stream message is handled by httpc_response and will
%% be sent by answer_request
end_stream(_, #request{stream = none}) ->
- ?hcrt("end stream - none", []),
ok;
end_stream(_, #request{stream = self}) ->
- ?hcrt("end stream - self", []),
ok;
end_stream(_, #request{stream = {self, once}}) ->
- ?hcrt("end stream - self:once", []),
ok;
end_stream({_,200,_}, #request{stream = Fd}) ->
- ?hcrt("end stream - 200", [{stream, Fd}]),
case file:close(Fd) of
ok ->
ok;
@@ -1706,15 +1492,13 @@ end_stream({_,200,_}, #request{stream = Fd}) ->
file:close(Fd)
end;
end_stream({_,206,_}, #request{stream = Fd}) ->
- ?hcrt("end stream - 206", [{stream, Fd}]),
case file:close(Fd) of
ok ->
ok;
{error, enospc} -> % Could be due to delayed_write
file:close(Fd)
end;
-end_stream(SL, R) ->
- ?hcrt("end stream", [{status_line, SL}, {request, R}]),
+end_stream(_, _) ->
ok.
@@ -1743,11 +1527,8 @@ handle_verbose(trace) ->
handle_verbose(_) ->
ok.
-
-
send_raw(#session{socket = Socket, socket_type = SocketType},
{ProcessBody, Acc}) when is_function(ProcessBody, 1) ->
- ?hcrt("send raw", [{acc, Acc}]),
send_raw(SocketType, Socket, ProcessBody, Acc);
send_raw(#session{socket = Socket, socket_type = SocketType}, Body) ->
http_transport:send(SocketType, Socket, Body).
@@ -1758,7 +1539,6 @@ send_raw(SocketType, Socket, ProcessBody, Acc) ->
ok;
{ok, Data, NewAcc} ->
DataBin = iolist_to_binary(Data),
- ?hcrd("send", [{data, DataBin}]),
case http_transport:send(SocketType, Socket, DataBin) of
ok ->
send_raw(SocketType, Socket, ProcessBody, NewAcc);
@@ -1790,14 +1570,16 @@ tls_tunnel(Address, Request, #state{session = #session{socket = Socket,
tls_tunnel_request(#request{headers = Headers,
settings = Options,
+ id = RequestId,
+ from = From,
address = {Host, Port}= Adress,
ipv6_host_with_brackets = IPV6}) ->
URI = Host ++":" ++ integer_to_list(Port),
#request{
- id = make_ref(),
- from = self(),
+ id = RequestId,
+ from = From,
scheme = http, %% Use tcp-first and then upgrade!
address = Adress,
path = URI,
@@ -1887,10 +1669,13 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
httpc_manager:update_session(ProfileName, SessionId, Pos, Value)
end
catch
- error:undef -> % This could happen during code upgrade
+ error:undef -> %% This could happen during code upgrade
Session2 = erlang:setelement(Pos, Session, Value),
insert_session(Session2, ProfileName);
- T:E ->
+ error:badarg ->
+ exit(normal); %% Manager has been shutdown
+ T:E ->
+ %% Unexpected this must be an error!
Stacktrace = erlang:get_stacktrace(),
error_logger:error_msg("Failed updating session: "
"~n ProfileName: ~p"
@@ -1919,16 +1704,3 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
end.
-%% ---------------------------------------------------------------------
-
-call(Msg, Pid) ->
- Timeout = infinity,
- call(Msg, Pid, Timeout).
-call(Msg, Pid, Timeout) ->
- gen_server:call(Pid, Msg, Timeout).
-
-cast(Msg, Pid) ->
- gen_server:cast(Pid, Msg).
-
-t() ->
- http_util:timestamp().
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index f4e69cc1fa..5f8c70f28d 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -43,32 +43,32 @@
%%% HTTP Client per request settings
-record(http_options,
{
- %% string() - "HTTP/1.1" | "HTTP/1.0" | "HTTP/0.9"
- version,
+ %% "HTTP/1.1" | "HTTP/1.0" | "HTTP/0.9"
+ version :: 'undefined' | string(),
- %% integer() | infinity - ms before a request times out
- timeout = ?HTTP_REQUEST_TIMEOUT,
+ %% ms before a request times out
+ timeout = ?HTTP_REQUEST_TIMEOUT :: timeout(),
- %% bool() - true if auto redirect on 30x response
- autoredirect = true,
+ %% true if auto redirect on 30x response
+ autoredirect = true :: boolean(),
%% ssl socket options
- ssl = [],
+ ssl = [],
%% {User, Password} = {string(), string()}
proxy_auth,
- %% bool() - true if not strictly std compliant
- relaxed = false,
+ %% true if not strictly std compliant
+ relaxed = false :: boolean(),
%% integer() - ms before a connect times out
- connect_timeout = ?HTTP_REQUEST_CTIMEOUT,
-
- %% bool() - Use %-encoding rfc 2396
- url_encode
+ connect_timeout = ?HTTP_REQUEST_CTIMEOUT :: timeout(),
+ %% Use %-encoding rfc 2396
+ url_encode :: 'undefined' | boolean()
}
).
+-type http_options() :: #http_options{}.
%%% HTTP Client per profile setting.
-record(options,
@@ -82,18 +82,19 @@
keep_alive_timeout = ?HTTP_KEEP_ALIVE_TIMEOUT, % Used when pipeline_timeout = 0
max_sessions = ?HTTP_MAX_TCP_SESSIONS,
cookies = disabled, % enabled | disabled | verify
- verbose = false,
+ verbose = false, % boolean(),
ipfamily = inet, % inet | inet6 | inet6fb4
ip = default, % specify local interface
port = default, % specify local port
socket_opts = [] % other socket options
}
).
+-type options() :: #options{}.
%%% All data associated to a specific HTTP request
-record(request,
{
- id, % ref() - Request Id
+ id :: 'undefined' | reference(), % Request Id
from, % pid() - Caller
redircount = 0,% Number of redirects made for this request
scheme, % http | https
@@ -103,7 +104,7 @@
method, % atom() - HTTP request Method
headers, % #http_request_h{}
content, % {ContentType, Body} - Current HTTP request
- settings, % #http_options{} - User defined settings
+ settings :: http_options(), % User defined settings
abs_uri, % string() ex: "http://www.erlang.org"
userinfo, % string() - optinal "<userinfo>@<host>:<port>"
stream, % boolean() - stream async reply?
@@ -112,20 +113,19 @@
% for testing purposes.
started, % integer() > 0 - When we started processing the
% request
- timer, % undefined | ref()
+ timer :: undefined | reference(),
socket_opts, % undefined | [socket_option()]
ipv6_host_with_brackets % boolean()
}
- ).
-
+ ).
+-type request() :: #request{}.
-record(session,
{
%% {{Host, Port}, HandlerPid}
id,
- %% true | false
- client_close,
+ client_close :: 'undefined' | boolean(),
%% http (HTTP/TCP) | https (HTTP/SSL/TCP)
scheme,
@@ -140,14 +140,13 @@
queue_length = 1,
%% pipeline | keep_alive (wait for response before sending new request)
- type,
+ type :: 'undefined' | 'pipeline' | 'keep_alive',
- %% true | false
%% This will be true, when a response has been received for
%% the first request. See type above.
- available = false
+ available = false :: boolean()
}).
-
+-type session() :: #session{}.
-record(http_cookie,
{
@@ -162,7 +161,7 @@
secure = false,
version = "0"
}).
-
+-type http_cookie() :: #http_cookie{}.
%% -record(parsed_uri,
%% {
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index 4cb6f005ad..a63864493f 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -137,7 +137,7 @@ redirect_request(Request, ProfileName) ->
%%--------------------------------------------------------------------
%% Function: cancel_request(RequestId, ProfileName) -> ok
-%% RequestId - ref()
+%% RequestId - reference()
%% ProfileName = atom()
%%
%% Description: Cancels the request with <RequestId>.
@@ -148,7 +148,7 @@ cancel_request(RequestId, ProfileName) ->
%%--------------------------------------------------------------------
%% Function: request_done(RequestId, ProfileName) -> ok
-%% RequestId - ref()
+%% RequestId - reference()
%% ProfileName = atom()
%%
%% Description: Inform tha manager that a request has been completed.
diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl
index e8d020c165..89872a3831 100644
--- a/lib/inets/src/http_client/httpc_request.erl
+++ b/lib/inets/src/http_client/httpc_request.erl
@@ -88,9 +88,11 @@ send(SendAddr, Socket, SocketType,
case Address of
SendAddr ->
{TmpHdrs2, Path ++ Query};
- _Proxy ->
+ _Proxy when SocketType == ip_comm ->
TmpHdrs3 = handle_proxy(HttpOptions, TmpHdrs2),
- {TmpHdrs3, AbsUri}
+ {TmpHdrs3, AbsUri};
+ _ ->
+ {TmpHdrs2, Path ++ Query}
end,
FinalHeaders =
diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl
index 91256fa6a2..5fbceb8ad0 100644
--- a/lib/inets/src/http_client/httpc_response.erl
+++ b/lib/inets/src/http_client/httpc_response.erl
@@ -110,27 +110,30 @@ result(Response = {{_, 300, _}, _, _},
redirect(Response, Request);
result(Response = {{_, Code, _}, _, _},
+ Request = #request{settings =
+ #http_options{autoredirect = true},
+ method = post}) when (Code =:= 301) orelse
+ (Code =:= 302) orelse
+ (Code =:= 303) ->
+ redirect(Response, Request#request{method = get});
+result(Response = {{_, Code, _}, _, _},
+ Request = #request{settings =
+ #http_options{autoredirect = true},
+ method = post}) when (Code =:= 307) ->
+ redirect(Response, Request);
+result(Response = {{_, Code, _}, _, _},
Request = #request{settings =
#http_options{autoredirect = true},
- method = head}) when (Code =:= 301) orelse
+ method = Method}) when (Code =:= 301) orelse
(Code =:= 302) orelse
(Code =:= 303) orelse
(Code =:= 307) ->
- redirect(Response, Request);
-result(Response = {{_, Code, _}, _, _},
- Request = #request{settings =
- #http_options{autoredirect = true},
- method = get}) when (Code =:= 301) orelse
- (Code =:= 302) orelse
- (Code =:= 303) orelse
- (Code =:= 307) ->
- redirect(Response, Request);
-result(Response = {{_, 303, _}, _, _},
- Request = #request{settings =
- #http_options{autoredirect = true},
- method = post}) ->
- redirect(Response, Request#request{method = get});
-
+ case lists:member(Method, [get, head, options, trace]) of
+ true ->
+ redirect(Response, Request);
+ false ->
+ transparent(Response, Request)
+ end;
result(Response = {{_,503,_}, _, _}, Request) ->
status_service_unavailable(Response, Request);
@@ -359,8 +362,9 @@ redirect(Response = {StatusLine, Headers, Body}, Request) ->
{ok, error(Request, Reason), Data};
%% Automatic redirection
{ok, {Scheme, _, Host, Port, Path, Query}} ->
+ HostPort = http_request:normalize_host(Scheme, Host, Port),
NewHeaders =
- (Request#request.headers)#http_request_h{host = Host},
+ (Request#request.headers)#http_request_h{host = HostPort},
NewRequest =
Request#request{redircount =
Request#request.redircount+1,
diff --git a/lib/inets/src/http_lib/http_internal.hrl b/lib/inets/src/http_lib/http_internal.hrl
index ae92b5df8f..ca1dad07cd 100644
--- a/lib/inets/src/http_lib/http_internal.hrl
+++ b/lib/inets/src/http_lib/http_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -71,7 +71,7 @@
'last-modified',
other=[] % list() - Key/Value list with other headers
}).
-
+-type http_response_h() :: #http_response_h{}.
%%% Request headers
-record(http_request_h,{
@@ -118,5 +118,6 @@
'last-modified',
other=[] % list() - Key/Value list with other headers
}).
+-type http_request_h() :: #http_request_h{}.
-endif. % -ifdef(http_internal_hrl).
diff --git a/lib/inets/src/http_lib/http_request.erl b/lib/inets/src/http_lib/http_request.erl
index c77b616f0d..4c50edb5ef 100644
--- a/lib/inets/src/http_lib/http_request.erl
+++ b/lib/inets/src/http_lib/http_request.erl
@@ -22,7 +22,7 @@
-include("http_internal.hrl").
--export([headers/2, http_headers/1, is_absolut_uri/1, key_value/1]).
+-export([headers/2, http_headers/1, is_absolut_uri/1, key_value/1, normalize_host/3]).
key_value(KeyValueStr) ->
@@ -85,6 +85,22 @@ is_absolut_uri("https://" ++ _) ->
is_absolut_uri(_) ->
false.
+%%-------------------------------------------------------------------------
+%% normalize_host(Scheme, Host, Port) -> string()
+%% Scheme - http | https
+%% Host - string()
+%% Port - integer()
+%%
+%% Description: returns a normalized Host header value, with the port
+%% number omitted for well-known ports
+%%-------------------------------------------------------------------------
+normalize_host(https, Host, 443 = _Port) ->
+ Host;
+normalize_host(http, Host, 80 = _Port) ->
+ Host;
+normalize_host(_Scheme, Host, Port) ->
+ Host ++ ":" ++ integer_to_list(Port).
+
%%%========================================================================
%%% Internal functions
%%%========================================================================
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index e5182ca23c..9e54f2b2c5 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -395,7 +395,8 @@ validate_properties(Properties) ->
%% That is, if property A depends on property B.
%% The only sunch preperty at this time is bind_address that depends
%% on ipfamily.
-validate_properties2(Properties) ->
+validate_properties2(Properties0) ->
+ Properties = fix_ipfamily(Properties0),
case proplists:get_value(bind_address, Properties) of
undefined ->
case proplists:get_value(sock_type, Properties, ip_comm) of
@@ -422,6 +423,15 @@ validate_properties2(Properties) ->
end
end.
+fix_ipfamily(Properties) ->
+ case proplists:get_value(ipfamily, Properties) of
+ undefined ->
+ Properties;
+ IpFamily ->
+ NewProps = proplists:delete(ipfamily, Properties),
+ [{ipfamily, validate_ipfamily(IpFamily)} | NewProps]
+ end.
+
add_inet_defaults(Properties) ->
case proplists:get_value(ipfamily, Properties) of
undefined ->
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 424d269859..c893b10dca 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -20,7 +20,7 @@
%%
-module(httpd_example).
-export([print/1]).
--export([get/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
+-export([get/2, put/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
-export([newformat/3]).
%% These are used by the inets test-suite
@@ -59,6 +59,11 @@ get(_Env,[]) ->
get(Env,Input) ->
default(Env,Input).
+put(Env,{Input,_Body}) ->
+ default(Env,Input);
+put(Env,Input) ->
+ default(Env,Input).
+
get_bin(_Env,_Input) ->
[list_to_binary(header()),
list_to_binary(top("GET Example")),
@@ -94,7 +99,7 @@ default(Env,Input) ->
io_lib:format("~p",[httpd:parse_query(Input)]),"\n",
footer()].
-peer(Env, Input) ->
+peer(Env, _Input) ->
Header =
case proplists:get_value(peer_cert, Env) of
undefined ->
@@ -161,7 +166,7 @@ sleep(T) -> receive after T -> ok end.
%% ------------------------------------------------------
-chunk_timeout(SessionID, _, StrInt) ->
+chunk_timeout(SessionID, _, _StrInt) ->
mod_esi:deliver(SessionID, "Tranfer-Encoding:chunked/html\r\n\r\n"),
mod_esi:deliver(SessionID, top("Test chunk encoding timeout")),
timer:sleep(20000),
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index 071fa94ef6..538d52b98d 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. 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.
@@ -37,18 +37,19 @@
-include("httpd_internal.hrl").
-define(HANDSHAKE_TIMEOUT, 5000).
+
-record(state, {mod, %% #mod{}
manager, %% pid()
status, %% accept | busy | blocked
mfa, %% {Module, Function, Args}
max_keep_alive_request = infinity, %% integer() | infinity
- response_sent = false, %% true | false
- timeout, %% infinity | integer() > 0
- timer, %% ref() - Request timer
- headers, %% #http_request_h{}
+ response_sent = false :: boolean(),
+ timeout, %% infinity | integer() > 0
+ timer :: 'undefined' | reference(), % Request timer
+ headers, %% #http_request_h{}
body, %% binary()
data, %% The total data received in bits, checked after 10s
- byte_limit %% Bit limit per second before kick out
+ byte_limit %% Bit limit per second before kick out
}).
%%====================================================================
@@ -240,9 +241,9 @@ handle_info({tcp_closed, _}, State) ->
handle_info({ssl_closed, _}, State) ->
{stop, normal, State};
handle_info({tcp_error, _, _} = Reason, State) ->
- {stop, Reason, State};
+ {stop, {shutdown, Reason}, State};
handle_info({ssl_error, _, _} = Reason, State) ->
- {stop, Reason, State};
+ {stop, {shutdown, Reason}, State};
%% Timeouts
handle_info(timeout, #state{mfa = {_, parse, _}} = State) ->
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 1374b7e85e..effa273e92 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/inets/src/http_server/mod_auth_server.erl b/lib/inets/src/http_server/mod_auth_server.erl
index 93d8145821..90d9ee34b1 100644
--- a/lib/inets/src/http_server/mod_auth_server.erl
+++ b/lib/inets/src/http_server/mod_auth_server.erl
@@ -128,7 +128,7 @@ list_group_members(Addr, Port, Dir, Group, Password) ->
list_group_members(Addr, Port, ?DEFAULT_PROFILE, Dir, Group, Password).
list_group_members(Addr, Port, Profile, Dir, Group, Password) ->
Name = make_name(Addr, Port, Profile),
- Req = {list_group_members, Addr, Port, Dir, Group, Password},
+ Req = {list_group_members, Addr, Port, Profile, Dir, Group, Password},
call(Name, Req).
delete_group(Addr, Port, Dir, GroupName, Password) ->
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index 2800250727..b21af1418c 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -241,7 +241,7 @@ alias_match_str(Alias, eval_script_alias) ->
%%------------------------ Erl mechanism --------------------------------
erl(#mod{method = Method} = ModData, ESIBody, Modules)
- when (Method =:= "GET") orelse (Method =:= "HEAD") ->
+ when (Method =:= "GET") orelse (Method =:= "HEAD") orelse (Method =:= "DELETE") ->
?hdrt("erl", [{method, Method}]),
case httpd_util:split(ESIBody,":|%3A|/",2) of
{ok, [ModuleName, FuncAndInput]} ->
@@ -264,35 +264,32 @@ erl(#mod{method = Method} = ModData, ESIBody, Modules)
{proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]}
end;
-erl(#mod{request_uri = ReqUri,
- method = "PUT",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, put}]),
- {proceed, [{status,{501,{"PUT", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method PUT")}}|
- Data]};
-
-erl(#mod{request_uri = ReqUri,
- method = "DELETE",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, delete}]),
- {proceed,[{status,{501,{"DELETE", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method DELETE")}}|
- Data]};
-
-erl(#mod{request_uri = ReqUri,
- method = "PATCH",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, patch}]),
- {proceed, [{status,{501,{"PATCH", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method PATCH")}}|
- Data]};
+erl(#mod{method = "PUT", entity_body = Body} = ModData,
+ ESIBody, Modules) ->
+ case httpd_util:split(ESIBody,":|%3A|/",2) of
+ {ok, [ModuleName, FuncAndInput]} ->
+ case httpd_util:split(FuncAndInput,"[\?/]",2) of
+ {ok, [FunctionName, Input]} ->
+ generate_webpage(ModData, ESIBody, Modules,
+ list_to_atom(ModuleName),
+ FunctionName, {Input,Body},
+ [{entity_body, Body} |
+ script_elements(FuncAndInput, Input)]);
+ {ok, [FunctionName]} ->
+ generate_webpage(ModData, ESIBody, Modules,
+ list_to_atom(ModuleName),
+ FunctionName, {undefined,Body},
+ [{entity_body, Body} |
+ script_elements(FuncAndInput, "")]);
+ {ok, BadRequest} ->
+ {proceed,[{status,{400,none, BadRequest}} |
+ ModData#mod.data]}
+ end;
+ {ok, BadRequest} ->
+ {proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]}
+ end;
-erl(#mod{method = "POST",
- entity_body = Body} = ModData, ESIBody, Modules) ->
+erl(#mod{method = "POST", entity_body = Body} = ModData, ESIBody, Modules) ->
?hdrt("erl", [{method, post}]),
case httpd_util:split(ESIBody,":|%3A|/",2) of
{ok,[ModuleName, Function]} ->
@@ -301,7 +298,16 @@ erl(#mod{method = "POST",
Function, Body, [{entity_body, Body}]);
{ok, BadRequest} ->
{proceed,[{status, {400, none, BadRequest}} | ModData#mod.data]}
- end.
+ end;
+
+erl(#mod{request_uri = ReqUri,
+ method = "PATCH",
+ http_version = Version,
+ data = Data}, _ESIBody, _Modules) ->
+ ?hdrt("erl", [{method, patch}]),
+ {proceed, [{status,{501,{"PATCH", ReqUri, Version},
+ ?NICE("Erl mechanism doesn't support method PATCH")}}|
+ Data]}.
generate_webpage(ModData, ESIBody, [all], Module, FunctionName,
Input, ScriptElements) ->
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index 3a31daeb20..f9ad8709d9 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2017. 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.
@@ -18,10 +18,14 @@
%% %CopyrightEnd%
{"%VSN%",
[
+ {<<"6.2.4">>, [{load_module, httpd_request_handler,
+ soft_purge, soft_purge, []}]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
],
[
+ {<<"6.2.4">>, [{load_module, httpd_request_handler,
+ soft_purge, soft_purge, []}]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
]
diff --git a/lib/inets/src/inets_app/inets_lib.erl b/lib/inets/src/inets_app/inets_lib.erl
index 8993be29e4..3fae376a9f 100644
--- a/lib/inets/src/inets_app/inets_lib.erl
+++ b/lib/inets/src/inets_app/inets_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-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.
diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl
index e2dec0c42a..1a79cb58ed 100644
--- a/lib/inets/test/ftp_SUITE.erl
+++ b/lib/inets/test/ftp_SUITE.erl
@@ -93,8 +93,11 @@ ftp_tests()->
append_chunk,
recv,
recv_3,
- recv_bin,
+ recv_bin,
+ recv_bin_twice,
recv_chunk,
+ recv_chunk_twice,
+ recv_chunk_three_times,
type,
quote,
error_elogin,
@@ -533,15 +536,19 @@ append_chunk(Config0) ->
recv() ->
[{doc, "Receive a file using recv/2"}].
recv(Config0) ->
- File = "f_dst.txt",
+ File1 = "f_dst1.txt",
+ File2 = "f_dst2.txt",
SrcDir = "a_dir",
- Contents = <<"ftp_SUITE test ...">>,
- Config = set_state([reset, {mkfile,[SrcDir,File],Contents}], Config0),
+ Contents1 = <<"1 ftp_SUITE test ...">>,
+ Contents2 = <<"2 ftp_SUITE test ...">>,
+ Config = set_state([reset, {mkfile,[SrcDir,File1],Contents1}, {mkfile,[SrcDir,File2],Contents2}], Config0),
Pid = proplists:get_value(ftp, Config),
ok = ftp:cd(Pid, id2ftp(SrcDir,Config)),
ok = ftp:lcd(Pid, id2ftp("",Config)),
- ok = ftp:recv(Pid, File),
- chk_file(File, Contents, Config),
+ ok = ftp:recv(Pid, File1),
+ chk_file(File1, Contents1, Config),
+ ok = ftp:recv(Pid, File2),
+ chk_file(File2, Contents2, Config),
{error,epath} = ftp:recv(Pid, "non_existing_file"),
ok.
@@ -572,6 +579,25 @@ recv_bin(Config0) ->
ok.
%%-------------------------------------------------------------------------
+recv_bin_twice() ->
+ [{doc, "Receive two files as a binaries."}].
+recv_bin_twice(Config0) ->
+ File1 = "f_dst1.txt",
+ File2 = "f_dst2.txt",
+ Contents1 = <<"1 ftp_SUITE test ...">>,
+ Contents2 = <<"2 ftp_SUITE test ...">>,
+ Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}], Config0),
+ ct:log("First transfer",[]),
+ Pid = proplists:get_value(ftp, Config),
+ {ok,Received1} = ftp:recv_bin(Pid, id2ftp(File1,Config)),
+ find_diff(Received1, Contents1),
+ ct:log("Second transfer",[]),
+ {ok,Received2} = ftp:recv_bin(Pid, id2ftp(File2,Config)),
+ find_diff(Received2, Contents2),
+ ct:log("Transfers ready!",[]),
+ {error,epath} = ftp:recv_bin(Pid, id2ftp("non_existing_file",Config)),
+ ok.
+%%-------------------------------------------------------------------------
recv_chunk() ->
[{doc, "Receive a file using chunk-wise."}].
recv_chunk(Config0) ->
@@ -584,13 +610,74 @@ recv_chunk(Config0) ->
{ok, ReceivedContents, _Ncunks} = recv_chunk(Pid, <<>>),
find_diff(ReceivedContents, Contents).
+recv_chunk_twice() ->
+ [{doc, "Receive two files using chunk-wise."}].
+recv_chunk_twice(Config0) ->
+ File1 = "big_file1.txt",
+ File2 = "big_file2.txt",
+ Contents1 = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ),
+ Contents2 = crypto:strong_rand_bytes(1200),
+ Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
+ ok = ftp:recv_chunk_start(Pid, id2ftp(File1,Config)),
+ {ok, ReceivedContents1, _Ncunks1} = recv_chunk(Pid, <<>>),
+ ok = ftp:recv_chunk_start(Pid, id2ftp(File2,Config)),
+ {ok, ReceivedContents2, _Ncunks2} = recv_chunk(Pid, <<>>),
+ find_diff(ReceivedContents1, Contents1),
+ find_diff(ReceivedContents2, Contents2).
+
+recv_chunk_three_times() ->
+ [{doc, "Receive two files using chunk-wise."},
+ {timetrap,{seconds,120}}].
+recv_chunk_three_times(Config0) ->
+ File1 = "big_file1.txt",
+ File2 = "big_file2.txt",
+ File3 = "big_file3.txt",
+ Contents1 = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ),
+ Contents2 = crypto:strong_rand_bytes(1200),
+ Contents3 = list_to_binary( lists:duplicate(1000, lists:seq(255,0,-1)) ),
+
+ Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}, {mkfile,File3,Contents3}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
+
+ ok = ftp:recv_chunk_start(Pid, id2ftp(File1,Config)),
+ {ok, ReceivedContents1, Nchunks1} = recv_chunk(Pid, <<>>),
+
+ ok = ftp:recv_chunk_start(Pid, id2ftp(File2,Config)),
+ {ok, ReceivedContents2, _Nchunks2} = recv_chunk(Pid, <<>>),
+
+ ok = ftp:recv_chunk_start(Pid, id2ftp(File3,Config)),
+ {ok, ReceivedContents3, _Nchunks3} = recv_chunk(Pid, <<>>, 10000, 0, Nchunks1),
+
+ find_diff(ReceivedContents1, Contents1),
+ find_diff(ReceivedContents2, Contents2),
+ find_diff(ReceivedContents3, Contents3).
+
+
+
recv_chunk(Pid, Acc) ->
- recv_chunk(Pid, Acc, 0).
+ recv_chunk(Pid, Acc, 0, 0, undefined).
+
+
+
+%% ExpectNchunks :: integer() | undefined
+recv_chunk(Pid, Acc, DelayMilliSec, N, ExpectNchunks) when N+1 < ExpectNchunks ->
+ %% for all I in integer(), I < undefined
+ recv_chunk1(Pid, Acc, DelayMilliSec, N, ExpectNchunks);
+
+recv_chunk(Pid, Acc, DelayMilliSec, N, ExpectNchunks) ->
+ %% N >= ExpectNchunks-1
+ timer:sleep(DelayMilliSec),
+ recv_chunk1(Pid, Acc, DelayMilliSec, N, ExpectNchunks).
+
-recv_chunk(Pid, Acc, N) ->
+recv_chunk1(Pid, Acc, DelayMilliSec, N, ExpectNchunks) ->
+ ct:log("Call ftp:recv_chunk",[]),
case ftp:recv_chunk(Pid) of
ok -> {ok, Acc, N};
- {ok, Bin} -> recv_chunk(Pid, <<Acc/binary, Bin/binary>>, N+1);
+ {ok, Bin} -> recv_chunk(Pid, <<Acc/binary, Bin/binary>>, DelayMilliSec, N+1, ExpectNchunks);
Error -> {Error, N}
end.
diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl
index a33b31f46f..95d594a44b 100644
--- a/lib/inets/test/ftp_format_SUITE.erl
+++ b/lib/inets/test/ftp_format_SUITE.erl
@@ -38,8 +38,8 @@ all() ->
groups() ->
[{ftp_response, [],
[ftp_150, ftp_200, ftp_220, ftp_226, ftp_257, ftp_331,
- ftp_425, ftp_other_status_codes, ftp_multiple_lines,
- ftp_multipel_ctrl_messages]}].
+ ftp_425, ftp_other_status_codes, ftp_multiple_lines_status_in_msg,
+ ftp_multiple_lines, ftp_multipel_ctrl_messages]}].
init_per_suite(Config) ->
Config.
@@ -141,6 +141,15 @@ ftp_425(Config) when is_list(Config) ->
{trans_neg_compl, _} = ftp_response:interpret(Msg),
ok.
+ftp_multiple_lines_status_in_msg() ->
+ [{doc, "check that multiple lines gets parsed correct, even if we have "
+ " the status code within the msg being sent"}].
+ftp_multiple_lines_status_in_msg(Config) when is_list(Config) ->
+ ML = "230-User usr-230 is logged in\r\n" ++
+ "230 OK. Current directory is /\r\n",
+ {ok, ML, <<>>} = ftp_response:parse_lines(list_to_binary(ML), [], start),
+ ok.
+
ftp_multiple_lines() ->
[{doc, "Especially check multiple lines devided in significant places"}].
ftp_multiple_lines(Config) when is_list(Config) ->
diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl
index a2b463e98c..4e10a97f58 100644
--- a/lib/inets/test/http_format_SUITE.erl
+++ b/lib/inets/test/http_format_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
+%% 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.
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 932567ec55..8aea38037d 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
+%% 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.
@@ -88,7 +88,8 @@ real_requests()->
stream_through_mfa,
streaming_error,
inet_opts,
- invalid_headers
+ invalid_headers,
+ invalid_body
].
only_simulated() ->
@@ -125,7 +126,9 @@ only_simulated() ->
redirect_see_other,
redirect_temporary_redirect,
port_in_host_header,
- relaxed
+ redirect_port_in_host_header,
+ relaxed,
+ multipart_chunks
].
misc() ->
@@ -500,10 +503,11 @@ redirect_multiple_choises(Config) when is_list(Config) ->
httpc:request(get, {URL300, []}, [{autoredirect, false}], []).
%%-------------------------------------------------------------------------
redirect_moved_permanently() ->
- [{doc, "If the 301 status code is received in response to a request other "
- "than GET or HEAD, the user agent MUST NOT automatically redirect the request "
- "unless it can be confirmed by the user, since this might change "
- "the conditions under which the request was issued."}].
+ [{doc, "The server SHOULD generate a Location header field in the response "
+ "containing a preferred URI reference for the new permanent URI. The user "
+ "agent MAY use the Location field value for automatic redirection. The server's "
+ "response payload usually contains a short hypertext note with a "
+ "hyperlink to the new URI(s)."}].
redirect_moved_permanently(Config) when is_list(Config) ->
URL301 = url(group_name(Config), "/301.html", Config),
@@ -514,15 +518,16 @@ redirect_moved_permanently(Config) when is_list(Config) ->
{ok, {{_,200,_}, [_ | _], []}}
= httpc:request(head, {URL301, []}, [], []),
- {ok, {{_,301,_}, [_ | _], [_|_]}}
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
= httpc:request(post, {URL301, [],"text/plain", "foobar"},
[], []).
%%-------------------------------------------------------------------------
redirect_found() ->
- [{doc," If the 302 status code is received in response to a request other "
- "than GET or HEAD, the user agent MUST NOT automatically redirect the "
- "request unless it can be confirmed by the user, since this might change "
- "the conditions under which the request was issued."}].
+ [{doc, "The server SHOULD generate a Location header field in the response "
+ "containing a URI reference for the different URI. The user agent MAY "
+ "use the Location field value for automatic redirection. The server's "
+ "response payload usually contains a short hypertext note with a "
+ "hyperlink to the different URI(s)."}].
redirect_found(Config) when is_list(Config) ->
URL302 = url(group_name(Config), "/302.html", Config),
@@ -533,14 +538,14 @@ redirect_found(Config) when is_list(Config) ->
{ok, {{_,200,_}, [_ | _], []}}
= httpc:request(head, {URL302, []}, [], []),
- {ok, {{_,302,_}, [_ | _], [_|_]}}
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
= httpc:request(post, {URL302, [],"text/plain", "foobar"},
[], []).
%%-------------------------------------------------------------------------
redirect_see_other() ->
[{doc, "The different URI SHOULD be given by the Location field in the response. "
"Unless the request method was HEAD, the entity of the response SHOULD contain a short "
- "hypertext note with a hyperlink to the new URI(s). "}].
+ "hypertext note with a hyperlink to the new URI(s)."}].
redirect_see_other(Config) when is_list(Config) ->
URL303 = url(group_name(Config), "/303.html", Config),
@@ -556,10 +561,11 @@ redirect_see_other(Config) when is_list(Config) ->
[], []).
%%-------------------------------------------------------------------------
redirect_temporary_redirect() ->
- [{doc," If the 307 status code is received in response to a request other "
- "than GET or HEAD, the user agent MUST NOT automatically redirect the request "
- "unless it can be confirmed by the user, since this might change "
- "the conditions under which the request was issued."}].
+ [{doc, "The server SHOULD generate a Location header field in the response "
+ "containing a URI reference for the different URI. The user agent MAY "
+ "use the Location field value for automatic redirection. The server's "
+ "response payload usually contains a short hypertext note with a "
+ "hyperlink to the different URI(s)."}].
redirect_temporary_redirect(Config) when is_list(Config) ->
URL307 = url(group_name(Config), "/307.html", Config),
@@ -570,7 +576,7 @@ redirect_temporary_redirect(Config) when is_list(Config) ->
{ok, {{_,200,_}, [_ | _], []}}
= httpc:request(head, {URL307, []}, [], []),
- {ok, {{_,307,_}, [_ | _], [_|_]}}
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
= httpc:request(post, {URL307, [],"text/plain", "foobar"},
[], []).
@@ -997,10 +1003,25 @@ invalid_headers(Config) ->
Request = {url(group_name(Config), "/dummy.html", Config), [{"cookie", undefined}]},
{error, _} = httpc:request(get, Request, [], []).
+%%-------------------------------------------------------------------------
+
+invalid_body(Config) ->
+ URL = url(group_name(Config), "/dummy.html", Config),
+ try
+ httpc:request(post, {URL, [], <<"text/plain">>, "foobar"},
+ [], []),
+ ct:fail(accepted_invalid_input)
+ catch
+ error:function_clause ->
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
remote_socket_close(Config) when is_list(Config) ->
URL = url(group_name(Config), "/just_close.html", Config),
{error, socket_closed_remotely} = httpc:request(URL).
+
%%-------------------------------------------------------------------------
remote_socket_close_async(Config) when is_list(Config) ->
@@ -1099,8 +1120,21 @@ port_in_host_header(Config) when is_list(Config) ->
Request = {url(group_name(Config), "/ensure_host_header_with_port.html", Config), []},
{ok, {{_, 200, _}, _, Body}} = httpc:request(get, Request, [], []),
inets_test_lib:check_body(Body).
+%%-------------------------------------------------------------------------
+redirect_port_in_host_header(Config) when is_list(Config) ->
+
+ Request = {url(group_name(Config), "/redirect_ensure_host_header_with_port.html", Config), []},
+ {ok, {{_, 200, _}, _, Body}} = httpc:request(get, Request, [], []),
+ inets_test_lib:check_body(Body).
%%-------------------------------------------------------------------------
+multipart_chunks(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/multipart_chunks.html", Config), []},
+ {ok, Ref} = httpc:request(get, Request, [], [{sync, false}, {stream, self}]),
+ ok = receive_stream_n(Ref, 10),
+ httpc:cancel_request(Ref).
+
+%%-------------------------------------------------------------------------
timeout_memory_leak() ->
[{doc, "Check OTP-8739"}].
timeout_memory_leak(Config) when is_list(Config) ->
@@ -1395,7 +1429,7 @@ dummy_server(Caller, SocketType, Inet, Extra) ->
end.
dummy_server_init(Caller, ip_comm, Inet, _) ->
- BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}],
+ BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {keepalive, true}, {active, false}],
{ok, ListenSocket} = gen_tcp:listen(0, [Inet | BaseOpts]),
{ok, Port} = inet:port(ListenSocket),
Caller ! {port, Port},
@@ -1677,6 +1711,12 @@ handle_uri(_,"/ensure_host_header_with_port.html",_,Headers,_,_) ->
"HTTP/1.1 500 Internal Server Error\r\n" ++
"Content-Length:" ++ Len ++ "\r\n\r\n" ++ B
end;
+handle_uri(_,"/redirect_ensure_host_header_with_port.html",Port,_,Socket,_) ->
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/ensure_host_header_with_port.html",
+ "HTTP/1.1 302 Found \r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:0\r\n\r\n";
handle_uri(_,"/300.html",Port,_,Socket,_) ->
NewUri = url_start(Socket) ++
@@ -1965,6 +2005,16 @@ handle_uri(_,"/missing_CR.html",_,_,_,_) ->
"Content-Length:32\r\n\n" ++
"<HTML><BODY>foobar</BODY></HTML>";
+handle_uri(_,"/multipart_chunks.html",_,_,Socket,_) ->
+ Head = "HTTP/1.1 200 ok\r\n" ++
+ "Transfer-Encoding:chunked\r\n" ++
+ "Date: " ++ httpd_util:rfc1123_date() ++ "\r\n"
+ "Connection: Keep-Alive\r\n" ++
+ "Content-Type: multipart/x-mixed-replace; boundary=chunk_boundary\r\n" ++
+ "\r\n",
+ send(Socket, Head),
+ send_multipart_chunks(Socket),
+ http_chunk:encode_last();
handle_uri("HEAD",_,_,_,_,_) ->
"HTTP/1.1 200 ok\r\n" ++
"Content-Length:0\r\n\r\n";
@@ -2261,3 +2311,21 @@ otp_8739_dummy_server_main(_Parent, ListenSocket) ->
Error ->
exit(Error)
end.
+
+send_multipart_chunks(Socket) ->
+ send(Socket, http_chunk:encode("--chunk_boundary\r\n")),
+ send(Socket, http_chunk:encode("Content-Type: text/plain\r\nContent-Length: 4\r\n\r\n")),
+ send(Socket, http_chunk:encode("test\r\n")),
+ ct:sleep(500),
+ send_multipart_chunks(Socket).
+
+receive_stream_n(_, 0) ->
+ ok;
+receive_stream_n(Ref, N) ->
+ receive
+ {http, {Ref, stream_start, _}} ->
+ receive_stream_n(Ref, N);
+ {http, {Ref,stream, Data}} ->
+ ct:pal("Data: ~p", [Data]),
+ receive_stream_n(Ref, N-1)
+ end.
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 3194b5ad3d..aae4ce5256 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2013-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.
@@ -119,8 +119,10 @@ groups() ->
]},
{htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]},
{security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code
- {http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test,
- trace, range, if_modified_since, mod_esi_chunk_timeout] ++ http_head() ++ http_get() ++ load()},
+ {http_1_1, [],
+ [host, chunked, expect, cgi, cgi_chunked_encoding_test,
+ trace, range, if_modified_since, mod_esi_chunk_timeout,
+ esi_put] ++ http_head() ++ http_get() ++ load()},
{http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
{http_0_9, [], http_head() ++ http_get() ++ load()}
].
@@ -283,20 +285,50 @@ init_per_testcase(Case, Config) when Case == host; Case == trace ->
http_1_1 ->
httpd_1_1
end,
- [{version_cb, Cb} | proplists:delete(version_cb, Config)];
+ dbg(
+ Case,
+ [{version_cb, Cb} | proplists:delete(version_cb, Config)],
+ init);
init_per_testcase(range, Config) ->
ct:timetrap({seconds, 20}),
DocRoot = proplists:get_value(doc_root, Config),
create_range_data(DocRoot),
- Config;
+ dbg(range, Config, init);
-init_per_testcase(_, Config) ->
+init_per_testcase(Case, Config) ->
ct:timetrap({seconds, 20}),
- Config.
-
-end_per_testcase(_Case, _Config) ->
- ok.
+ dbg(Case, Config, init).
+
+end_per_testcase(Case, Config) ->
+ dbg(Case, Config, 'end').
+
+
+dbg(Case, Config, Status) ->
+ Cases = [esi_put],
+ case lists:member(Case, Cases) of
+ true ->
+ case Status of
+ init ->
+ dbg:tracer(),
+ dbg:p(all, c),
+ dbg:tpl(httpd_example, cx),
+ dbg:tpl(mod_esi, generate_webpage, cx),
+ io:format("dbg: started~n"),
+ Config;
+ 'end' ->
+ io:format("dbg: stopped~n"),
+ dbg:stop_clear(),
+ ok
+ end;
+ false ->
+ case Status of
+ init ->
+ Config;
+ 'end' ->
+ ok
+ end
+ end.
%%-------------------------------------------------------------------------
%% Test cases starts here.
@@ -489,6 +521,9 @@ do_auth_api(AuthPrefix, Config) ->
"two", "group1"),
add_group_member(Node, ServerRoot, Port, AuthPrefix,
"secret", "Aladdin", "group2"),
+ {ok, Members} = list_group_members(Node, ServerRoot, Port, AuthPrefix, "secret", "group1"),
+ true = lists:member("one", Members),
+ true = lists:member("two", Members),
ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
"one", "onePassword", Version, Host),
Config, [{statuscode, 200}]),
@@ -765,6 +800,14 @@ esi(Config) when is_list(Config) ->
ok = http_status("GET /cgi-bin/erl/httpd_example:peer ",
Config, [{statuscode, 200},
{header, "peer-cert-exist", peer(Config)}]).
+
+%%-------------------------------------------------------------------------
+esi_put() ->
+ [{doc, "Test mod_esi PUT"}].
+
+esi_put(Config) when is_list(Config) ->
+ ok = http_status("PUT /cgi-bin/erl/httpd_example/put/123342234123 ",
+ Config, [{statuscode, 200}]).
%%-------------------------------------------------------------------------
mod_esi_chunk_timeout(Config) when is_list(Config) ->
@@ -2115,6 +2158,10 @@ add_group_member(Node, Root, Port, AuthPrefix, Dir, User, Group) ->
Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
rpc:call(Node, mod_auth, add_group_member, [Group, User, Addr, Port,
Directory]).
+list_group_members(Node, Root, Port, AuthPrefix, Dir, Group) ->
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, list_group_members, [Group, [{port, Port}, {dir, Directory}]]).
+
getaddr() ->
{ok,HostName} = inet:gethostname(),
{ok,{A1,A2,A3,A4}} = inet:getaddr(HostName,inet),
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index 5eaf3a28a0..38b8229389 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -212,11 +212,19 @@ start_httpd(Config) when is_list(Config) ->
Pids0 = [ServicePid || {_, ServicePid} <- inets:services()],
true = lists:member(Pid0, Pids0),
[_|_] = inets:services_info(),
-
inets:stop(httpd, Pid0),
ct:sleep(500),
+ Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
+ false = lists:member(Pid0, Pids1),
+ {ok, Pid0b} =
+ inets:start(httpd, [{port, 0}, {ipfamily, inet6fb4} | HttpdConf]),
+ Pids0b = [ServicePid || {_, ServicePid} <- inets:services()],
+ true = lists:member(Pid0b, Pids0b),
+ [_|_] = inets:services_info(),
+ inets:stop(httpd, Pid0b),
+ ct:sleep(500),
Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
- false = lists:member(Pid0, Pids1),
+ false = lists:member(Pid0b, Pids1),
{ok, Pid1} =
inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf],
stand_alone),
diff --git a/lib/inets/test/inets_socketwrap_SUITE.erl b/lib/inets/test/inets_socketwrap_SUITE.erl
index 18df995215..7ea7e08ed1 100644
--- a/lib/inets/test/inets_socketwrap_SUITE.erl
+++ b/lib/inets/test/inets_socketwrap_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 5b8b1463c8..1e664337e6 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -33,7 +33,7 @@ suite() ->
all() ->
[default_tree, ftpc_worker, tftpd_worker,
- httpd_subtree, httpd_subtree_profile,
+ httpd_config, httpd_subtree, httpd_subtree_profile,
httpc_subtree].
groups() ->
@@ -52,9 +52,32 @@ end_per_suite(_) ->
inets:stop(),
ok.
-init_per_testcase(httpd_subtree, Config) ->
+init_per_testcase(httpd_config = TC, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Dir = filename:join(PrivDir, TC),
+ ok = file:make_dir(Dir),
+
+ FallbackConfig = [{port, 0},
+ {server_name,"www.test"},
+ {modules, [mod_get]},
+ {server_root, Dir},
+ {document_root, Dir},
+ {bind_address, any},
+ {ipfamily, inet6fb4}],
+ try
+ inets:stop(),
+ inets:start(),
+ inets:start(httpd, FallbackConfig),
+ Config
+ catch
+ _:Reason ->
+ inets:stop(),
+ exit({failed_starting_inets, Reason})
+ end;
+
+init_per_testcase(httpd_subtree = TC, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Dir = filename:join(PrivDir, "root"),
+ Dir = filename:join(PrivDir, TC),
ok = file:make_dir(Dir),
SimpleConfig = [{port, 0},
@@ -75,9 +98,9 @@ init_per_testcase(httpd_subtree, Config) ->
exit({failed_starting_inets, Reason})
end;
-init_per_testcase(httpd_subtree_profile, Config) ->
+init_per_testcase(httpd_subtree_profile = TC, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Dir = filename:join(PrivDir, "root"),
+ Dir = filename:join(PrivDir, TC),
ok = file:make_dir(Dir),
SimpleConfig = [{port, 0},
@@ -193,6 +216,11 @@ tftpd_worker(Config) when is_list(Config) ->
[] = supervisor:which_children(tftp_sup),
ok.
+httpd_config() ->
+ [{doc, "Makes sure the httpd config works for inet6fb4."}].
+httpd_config(Config) when is_list(Config) ->
+ do_httpd_subtree(Config, default).
+
httpd_subtree() ->
[{doc, "Makes sure the httpd sub tree is correct."}].
httpd_subtree(Config) when is_list(Config) ->
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 543e0d44fd..758cef7ac4 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2016. All Rights Reserved.
+# Copyright Ericsson AB 2001-2017. 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,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.3
+INETS_VSN = 6.3.9
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile
index cd1e61a795..508c8e01b5 100644
--- a/lib/jinterface/doc/src/Makefile
+++ b/lib/jinterface/doc/src/Makefile
@@ -166,7 +166,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html/java/$(JAVA_PKG_PATH)"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- (/bin/cp -rf ../html "$(RELSYSDIR)/doc")
+ ($(CP) -rf ../html "$(RELSYSDIR)/doc")
# $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
# "$(RELSYSDIR)/doc/html"
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index 3151fc4b5e..30f607c357 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the Jinterface application.</p>
+<section><title>Jinterface 1.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Update build scripts to not make assumtions about where
+ env, cp and perl are located.</p>
+ <p>
+ Own Id: OTP-13800</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Jinterface 1.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index 752b34e78c..c29d9df3d7 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1 +1 @@
-JINTERFACE_VSN = 1.7
+JINTERFACE_VSN = 1.7.1
diff --git a/lib/kernel/doc/src/app.xml b/lib/kernel/doc/src/app.xml
index 5e0da409a3..d2e9390d7e 100644
--- a/lib/kernel/doc/src/app.xml
+++ b/lib/kernel/doc/src/app.xml
@@ -151,7 +151,7 @@ ApplicationVersion = string()</code>
application is allowed to be started. <c>systools</c> uses
this list to generate correct start scripts. Defaults to
the empty list, but notice that all applications have
- dependencies to (at least) <c>Kernel</c> and <c>STDLIB</c>.</p>
+ dependencies to (at least) Kernel and STDLIB.</p>
</item>
<tag><c>env</c></tag>
<item>
@@ -171,7 +171,7 @@ ApplicationVersion = string()</code>
implemented as a supervision tree, otherwise the application
controller does not know how to start it. <c>mod</c>
can be omitted for applications without processes, typically
- code libraries, for example, <c>STDLIB</c>.</p>
+ code libraries, for example, STDLIB.</p>
</item>
<tag><c>start_phases</c></tag>
<item>
@@ -236,7 +236,7 @@ ApplicationVersion = string()</code>
<section>
<title>See Also</title>
<p><seealso marker="application"><c>application(3)</c></seealso>,
- <seealso marker="sasl:systools"><c>sasl:systools(3)</c></seealso></p>
+ <seealso marker="sasl:systools"><c>systools(3)</c></seealso></p>
</section>
</fileref>
diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml
index 8d33aa86e7..886286b76d 100644
--- a/lib/kernel/doc/src/application.xml
+++ b/lib/kernel/doc/src/application.xml
@@ -200,7 +200,7 @@
<seealso marker="app"><c>app(4)</c></seealso>.</p>
<p>If <c><anno>Distributed</anno> == {<anno>Application</anno>,[<anno>Time</anno>,]<anno>Nodes</anno>}</c>,
the application becomes distributed. The argument overrides
- the value for the application in the <c>Kernel</c> configuration
+ the value for the application in the Kernel configuration
parameter <c>distributed</c>. <c><anno>Application</anno></c> must be
the application name (same as in the first argument).
If a node crashes and <c><anno>Time</anno></c> is specified,
@@ -221,7 +221,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
the application is to be started at <c>cp2@cave</c>
or <c>cp3@cave</c>.</p>
<p>If <c>Distributed == default</c>, the value for
- the application in the <c>Kernel</c> configuration parameter
+ the application in the Kernel configuration parameter
<c>distributed</c> is used.</p>
</desc>
</func>
@@ -267,7 +267,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
started, <c>Application</c> is started as well.</p>
<p>By default, all applications are loaded with permission
<c>true</c> on all nodes. The permission can be configured
- using the <c>Kernel</c> configuration parameter <c>permissions</c>.</p>
+ using the Kernel configuration parameter <c>permissions</c>.</p>
</desc>
</func>
<func>
diff --git a/lib/kernel/doc/src/auth.xml b/lib/kernel/doc/src/auth.xml
index 03f983b96d..5901446960 100644
--- a/lib/kernel/doc/src/auth.xml
+++ b/lib/kernel/doc/src/auth.xml
@@ -47,7 +47,7 @@
<desc>
<p>Use
<seealso marker="erts:erlang#erlang:get_cookie/0"><c>erlang:get_cookie()</c></seealso>
- in <c>ERTS</c> instead.</p>
+ in ERTS instead.</p>
</desc>
</func>
<func>
@@ -59,7 +59,7 @@
<desc>
<p>Use
<seealso marker="erts:erlang#erlang:set_cookie/2"><c>erlang:set_cookie(node(), <anno>Cookie</anno>)</c>
- in <c>ERTS</c></seealso> instead.</p>
+ in ERTS</seealso> instead.</p>
</desc>
</func>
<func>
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index d3611d6a03..3143cdc825 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -86,11 +86,11 @@
an <c>ebin</c> directory are ignored.</p>
<p>All application directories found in the additional directories
appears before the standard OTP applications, except for the
- <c>Kernel</c> and <c>STDLIB</c> applications, which are placed before
+ Kernel and STDLIB applications, which are placed before
any additional applications. In other words, modules found in any
of the additional library directories override modules with
- the same name in OTP, except for modules in <c>Kernel</c> and
- <c>STDLIB</c>.</p>
+ the same name in OTP, except for modules in Kernel and
+ STDLIB.</p>
<p>Environment variable <c>ERL_LIBS</c> (if defined) is to contain
a colon-separated (for Unix-like systems) or semicolon-separated
(for Windows) list of additional libraries.</p>
@@ -151,7 +151,7 @@ zip:create("mnesia-4.4.7.ez",
<c>$OTPROOT/lib/mnesia.ez/mnesia/ebin</c> or
<c>$OTPROOT/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c>.</p>
- <p>The code server uses module <c>erl_prim_loader</c> in <c>ERTS</c>
+ <p>The code server uses module <c>erl_prim_loader</c> in ERTS
(possibly through <c>erl_boot_server</c>) to read code files from
archives. However, the functions in <c>erl_prim_loader</c> can also be
used by other applications to read files from archives. For
@@ -382,9 +382,14 @@ zip:create("mnesia-4.4.7.ez",
<name name="add_pathsa" arity="1"/>
<fsummary>Add directories to the beginning of the code path.</fsummary>
<desc>
- <p>Adds the directories in <c><anno>Dirs</anno></c> to the beginning of
- the code path. If a <c><anno>Dir</anno></c> exists, it is removed
- from the old position in the code path.</p>
+ <p>Traverses <c><anno>Dirs</anno></c> and adds
+ each <c><anno>Dir</anno></c> to the beginning of the code
+ path. This means that the order of <c><anno>Dirs</anno></c>
+ is reversed in the resulting code path. For example, if you
+ add <c>[Dir1,Dir2]</c>, the resulting path will
+ be <c>[Dir2,Dir1|OldCodePath]</c>.</p>
+ <p>If a <c><anno>Dir</anno></c> already exists in the code
+ path, it is removed from the old position.</p>
<p>Always returns <c>ok</c>, regardless of the validity of each
individual <c><anno>Dir</anno></c>.</p>
</desc>
@@ -651,6 +656,11 @@ ok = code:finish_loading(Prepared),
<p>Purges the code for <c><anno>Module</anno></c>, that is, removes code
marked as old. If some processes still linger in the old code,
these processes are killed before the code is removed.</p>
+ <note><p>As of ERTS version 9.0, a process is only considered
+ to be lingering in the code if it has direct references to the code.
+ For more information see documentation of
+ <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>,
+ which is used in order to determine this.</p></note>
<p>Returns <c>true</c> if successful and any process is needed to
be killed, otherwise <c>false</c>.</p>
</desc>
@@ -661,6 +671,11 @@ ok = code:finish_loading(Prepared),
<desc>
<p>Purges the code for <c><anno>Module</anno></c>, that is, removes code
marked as old, but only if no processes linger in it.</p>
+ <note><p>As of ERTS version 9.0, a process is only considered
+ to be lingering in the code if it has direct references to the code.
+ For more information see documentation of
+ <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>,
+ which is used in order to determine this.</p></note>
<p>Returns <c>false</c> if the module cannot be purged because
of processes lingering in old code, otherwise <c>true</c>.</p>
</desc>
@@ -678,9 +693,9 @@ ok = code:finish_loading(Prepared),
<p>Normally, <c><anno>Loaded</anno></c> is the absolute filename
<c>Filename</c> from which the code is obtained. If the module
is preloaded (see
- <seealso marker="sasl:script"><c>sasl:script(4)</c></seealso>),
+ <seealso marker="sasl:script"><c>script(4)</c></seealso>),
<c>Loaded==preloaded</c>. If the module is Cover-compiled (see
- <seealso marker="tools:cover"><c>tools:cover(3)</c></seealso>),
+ <seealso marker="tools:cover"><c>cover(3)</c></seealso>),
<c>Loaded==cover_compiled</c>.</p>
</desc>
</func>
diff --git a/lib/kernel/doc/src/erl_boot_server.xml b/lib/kernel/doc/src/erl_boot_server.xml
index 897365f9b9..4109251387 100644
--- a/lib/kernel/doc/src/erl_boot_server.xml
+++ b/lib/kernel/doc/src/erl_boot_server.xml
@@ -38,13 +38,13 @@
command-line flag <c>-loader inet</c>. All hosts specified
with command-line flag <c>-hosts Host</c> must have one
instance of this server running.</p>
- <p>This server can be started with the <c>Kernel</c> configuration
+ <p>This server can be started with the Kernel configuration
parameter <c>start_boot_server</c>.</p>
<p>The <c>erl_boot_server</c> can read regular files and
files in archives. See <seealso marker="code"><c>code(3)</c></seealso>
and
<seealso marker="erts:erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>
- in <c>ERTS</c>.</p>
+ in ERTS.</p>
<warning><p>The support for loading code from archive files is
experimental. It is released before it is ready
to obtain early feedback. The file format, semantics,
diff --git a/lib/kernel/doc/src/erl_ddll.xml b/lib/kernel/doc/src/erl_ddll.xml
index a5ce58ef3e..75114e015c 100644
--- a/lib/kernel/doc/src/erl_ddll.xml
+++ b/lib/kernel/doc/src/erl_ddll.xml
@@ -201,7 +201,7 @@
<desc>
<p>Removes a driver monitor in much the same way as
<seealso marker="erts:erlang#erlang:demonitor/1"><c>erlang:demonitor/1</c></seealso>
- in <c>ERTS</c>
+ in ERTS
does with process monitors. For details about how to create
driver monitors, see
<seealso marker="#monitor/2"><c>monitor/2</c></seealso>,
@@ -431,7 +431,7 @@
<p>Creates a driver monitor and works in many
ways as
<seealso marker="erts:erlang#erlang:monitor/2"><c>erlang:monitor/2</c></seealso>
- in <c>ERTS</c>,
+ in ERTS,
does for processes. When a driver changes state, the monitor
results in a monitor message that is sent to the calling
process. <c><anno>MonitorRef</anno></c> returned by this function is
@@ -745,7 +745,7 @@
<p>This parameter is the name of the driver
to be used in subsequent calls to function
<seealso marker="erts:erlang#open_port/2"><c>erlang:open_port</c></seealso>
- in <c>ERTS</c>.
+ in ERTS.
The name can be specified as an <c>iolist()</c> or
an <c>atom()</c>. The name specified when loading is used
to find the object file (with the help of <c><anno>Path</anno></c>
diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index a8273e59e2..814e8eac46 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -33,7 +33,7 @@
<description>
<p>The Erlang <em>error logger</em> is an event manager (see
<seealso marker="doc/design_principles:des_princ">OTP Design Principles</seealso> and
- <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>),
+ <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>),
registered as <c>error_logger</c>. Errors, warnings, and info events
are sent to the error logger from the Erlang runtime system and
the different Erlang/OTP applications. The events are, by default,
@@ -44,12 +44,12 @@
executing.</p>
<p>Initially, <c>error_logger</c> has only a primitive event
handler, which buffers and prints the raw event messages. During
- system startup, the <c>Kernel</c> application replaces this with a
+ system startup, the Kernel application replaces this with a
<em>standard event handler</em>, by default one that writes
- nicely formatted output to the terminal. <c>Kernel</c> can also be
+ nicely formatted output to the terminal. Kernel can also be
configured so that events are logged to a file instead, or not logged at all,
see <seealso marker="kernel_app"><c>kernel(6)</c></seealso>.</p>
- <p>Also the <c>SASL</c> application, if started, adds its own event
+ <p>Also the SASL application, if started, adds its own event
handler, which by default writes supervisor, crash, and progress
reports to the terminal. See
<seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso>.</p>
@@ -58,9 +58,9 @@
User-defined event handlers can be added to handle application-specific
events, see
<seealso marker="#add_report_handler/1"><c>add_report_handler/1,2</c></seealso>.
- Also, a useful event handler is provided in <c>STDLIB</c> for multi-file
+ Also, a useful event handler is provided in STDLIB for multi-file
logging of events, see
- <seealso marker="stdlib:log_mf_h"><c>stdlib:log_mf_h(3)</c></seealso>.</p>
+ <seealso marker="stdlib:log_mf_h"><c>log_mf_h(3)</c></seealso>.</p>
<p>Warning events were introduced in Erlang/OTP R9C and are enabled
by default as from Erlang/OTP 18.0. To retain backwards compatibility
with existing user-defined event handlers, the warning events can be
@@ -82,7 +82,7 @@
<p>Adds a new event handler to the error logger. The event
handler must be implemented as a <c>gen_event</c> callback
module, see
- <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p>
+ <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.</p>
<p><c><anno>Handler</anno></c> is typically the name of the callback module
and <c><anno>Args</anno></c> is an optional term (defaults to []) passed
to the initialization callback function <c><anno>Handler</anno>:init/1</c>.
@@ -97,7 +97,7 @@
<desc>
<p>Deletes an event handler from the error logger by calling
<c>gen_event:delete_handler(error_logger, <anno>Handler</anno>, [])</c>,
- see <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p>
+ see <seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -110,7 +110,7 @@
The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments
are the same as the arguments of
<seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso>
- in <c>STDLIB</c>.
+ in STDLIB.
The event is handled by the standard event handler.</p>
<p><em>Example:</em></p>
<pre>
@@ -171,7 +171,7 @@ ok</pre>
The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments
are the same as the arguments of
<seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso>
- in <c>STDLIB</c>. The event is handled by the standard event handler.</p>
+ in STDLIB. The event is handled by the standard event handler.</p>
<p><em>Example:</em></p>
<pre>
1> <input>error_logger:info_msg("Something happened in ~p~n", [a_module]).</input>
@@ -235,7 +235,7 @@ ok</pre>
<p>Enables or disables printout of standard events to a file.</p>
<p>This is done by adding or deleting the standard event handler
for output to file. Thus, calling this function overrides
- the value of the <c>Kernel</c> <c>error_logger</c> configuration
+ the value of the Kernel <c>error_logger</c> configuration
parameter.</p>
<p>Enabling file logging can be used together with calling
<c>tty(false)</c>, to have a silent system where
@@ -274,7 +274,7 @@ ok</pre>
to the terminal.</p>
<p>This is done by adding or deleting the standard event handler
for output to the terminal. Thus, calling this function overrides
- the value of the <c>Kernel</c> <c>error_logger</c> configuration parameter.</p>
+ the value of the Kernel <c>error_logger</c> configuration parameter.</p>
</desc>
</func>
<func>
@@ -323,7 +323,7 @@ ok</pre>
The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments
are the same as the arguments of
<seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso>
- in <c>STDLIB</c>.
+ in STDLIB.
The event is handled by the standard event handler. It is tagged
as an error, warning, or info, see
<seealso marker="#warning_map/0"><c>warning_map/0</c></seealso>.</p>
@@ -416,8 +416,8 @@ ok</pre>
</section>
<section>
<title>See Also</title>
- <p><seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>,
- <seealso marker="stdlib:log_mf_h"><c>stdlib:log_mf_h(3)</c></seealso>
+ <p><seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>,
+ <seealso marker="stdlib:log_mf_h"><c>log_mf_h(3)</c></seealso>
<seealso marker="kernel_app"><c>kernel(6)</c></seealso>
<seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso></p>
</section>
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 7d86c3ebcb..b674b3ca93 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -79,7 +79,7 @@
<seealso marker="#list_dir_all"><c>list_dir_all/1</c></seealso> and
<seealso marker="#read_link_all"><c>read_link_all/1</c></seealso>.</p>
- <p>See also section <seealso marker="stdlib:unicode_usage#notes-about-raw-filenames">Notes About Raw Filenames</seealso> in the <c>STDLIB</c> User´s Giude.</p>
+ <p>See also section <seealso marker="stdlib:unicode_usage#notes-about-raw-filenames">Notes About Raw Filenames</seealso> in the STDLIB User's Guide.</p>
</description>
@@ -277,7 +277,7 @@ f.txt: {person, "kalle", 25}.
{ok,[{person,"kalle",25},{person,"pelle",30}]}</pre>
<p>The encoding of <c><anno>Filename</anno></c> can be set
by a comment, as described in
- <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p>
+ <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -445,7 +445,7 @@ f.txt: {person, "kalle", 25}.
</taglist>
<p>The encoding of <c><anno>Filename</anno></c> can be set
by a comment, as described in
- <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p>
+ <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -455,7 +455,7 @@ f.txt: {person, "kalle", 25}.
<p>The same as <c>eval/1</c>, but the variable bindings
<c><anno>Bindings</anno></c> are used in the evaluation. For information
about the variable bindings, see
- <seealso marker="stdlib:erl_eval"><c>stdlib:erl_eval(3)</c></seealso>.</p>
+ <seealso marker="stdlib:erl_eval"><c>erl_eval(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -830,7 +830,7 @@ f.txt: {person, "kalle", 25}.
this module (<c>file</c>) for reading and writing data as the interfaces
provided here work with byte-oriented data. Using other (Unicode)
encodings makes the
- <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> functions
+ <seealso marker="stdlib:io"><c>io(3)</c></seealso> functions
<c>get_chars</c>, <c>get_line</c>, and <c>put_chars</c> more suitable,
as they can work with the full Unicode range.</p>
<p>If data is sent to an <c>io_device()</c> in a format that cannot be
@@ -847,7 +847,7 @@ f.txt: {person, "kalle", 25}.
that is,
<seealso marker="#read/2"><c>read/2</c></seealso> are
returned "as is". If module
- <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> is used for
+ <seealso marker="stdlib:io"><c>io(3)</c></seealso> is used for
writing, the file can only cope with Unicode characters up to code point
255 (the ISO Latin-1 range).</p>
</item>
@@ -861,7 +861,7 @@ f.txt: {person, "kalle", 25}.
the file lies beyond the ISO Latin-1 range (0..255), but failure occurs
if the data contains Unicode code points beyond that range. The file is
best read with the functions in the Unicode aware module
- <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso>.</p>
+ <seealso marker="stdlib:io"><c>io(3)</c></seealso>.</p>
<p>Bytes written to the file by any means are translated to UTF-8 encoding
before being stored on the disk file.</p>
</item>
@@ -891,7 +891,7 @@ f.txt: {person, "kalle", 25}.
So a file can be analyzed in latin1 encoding for, for example, a BOM,
positioned beyond the BOM and then be set for the right encoding before
further reading. For functions identifying BOMs, see module
- <seealso marker="stdlib:unicode"><c>stdlib:unicode(3)</c></seealso>. </p>
+ <seealso marker="stdlib:unicode"><c>unicode(3)</c></seealso>. </p>
<p>This option is not allowed on <c>raw</c> files.</p>
</item>
<tag><c>ram</c></tag>
@@ -932,7 +932,7 @@ f.txt: {person, "kalle", 25}.
closed and the process itself is terminated.
An <c><anno>IoDevice</anno></c> returned from this call can be used
as an argument to the I/O functions (see
- <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso>).</p>
+ <seealso marker="stdlib:io"><c>io(3)</c></seealso>).</p>
<note>
<p>In previous versions of <c>file</c>, modes were specified
as one of the atoms <c>read</c>, <c>write</c>, or
@@ -1055,7 +1055,7 @@ f.txt: {person, "kalle", 25}.
</taglist>
<p>The encoding of <c><anno>Filename</anno></c> can be set
by a comment as described in
- <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p>
+ <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -1128,7 +1128,7 @@ f.txt: {person, "kalle", 25}.
</taglist>
<p>The encoding of <c><anno>Filename</anno></c> can be set
by a comment as described in
- <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p>
+ <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -1389,7 +1389,7 @@ f.txt: {person, "kalle", 25}.
<c>{ok, <anno>FileInfo</anno>}</c> if successful, otherwise
<c>{error, <anno>Reason</anno>}</c>. <c><anno>FileInfo</anno></c>
is a record
- <c>file_info</c>, defined in the <c>Kernel</c> include file
+ <c>file_info</c>, defined in the Kernel include file
<c>file.hrl</c>. Include the following directive in the module
from which the function is called:</p>
<code type="none">
@@ -1477,8 +1477,8 @@ f.txt: {person, "kalle", 25}.
<tag><c>16#400</c></tag>
<item><p>set group id on execution</p></item>
</taglist>
- <p>On Unix platforms, the following bits
- can also be set:</p>
+ <p>On Unix platforms, other bits than those listed above
+ may be set.</p>
</item>
<tag><c>links = integer() >= 0</c></tag>
<item>
@@ -1552,7 +1552,7 @@ f.txt: {person, "kalle", 25}.
raw line-oriented reading.</p>
<p>If <c>encoding</c> is set to something else than <c>latin1</c>, the
<c>read_line/1</c> call fails if the data contains characters larger than 255,
- why module <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> is to be
+ why module <seealso marker="stdlib:io"><c>io(3)</c></seealso> is to be
preferred when reading such a file.</p>
<p>The function returns:</p>
<taglist>
@@ -1970,7 +1970,7 @@ f.txt: {person, "kalle", 25}.
<p>Changes file information. Returns <c>ok</c> if successful,
otherwise <c>{error, <anno>Reason</anno>}</c>.
<c><anno>FileInfo</anno></c> is a record
- <c>file_info</c>, defined in the <c>Kernel</c> include file
+ <c>file_info</c>, defined in the Kernel include file
<c>file.hrl</c>. Include the following directive in the module
from which the function is called:</p>
<code type="none">
@@ -2042,8 +2042,8 @@ f.txt: {person, "kalle", 25}.
<tag><c>16#400</c></tag>
<item><p>Set group id on execution</p></item>
</taglist>
- <p>On Unix platforms, the following bits
- can also be set.</p>
+ <p>On Unix platforms, other bits than those listed above
+ may be set.</p>
</item>
<tag><c>uid = integer() >= 0</c></tag>
<item>
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index 83242c2df8..e97db20062 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -216,7 +216,7 @@ do_recv(Sock, Bs) ->
time-out in milliseconds. Defaults to <c>infinity</c>.</p>
<note>
<p>The default values for options specified to <c>connect</c> can
- be affected by the <c>Kernel</c> configuration parameter
+ be affected by the Kernel configuration parameter
<c>inet_default_connect_options</c>. For details, see
<seealso marker="inet"><c>inet(3)</c></seealso>.</p>
</note>
@@ -231,7 +231,11 @@ do_recv(Sock, Bs) ->
<c><anno>Socket</anno></c>. The controlling process is the process
that receives messages from the socket. If called by any other
process than the current controlling process,
- <c>{error, not_owner}</c> is returned.</p>
+ <c>{error, not_owner}</c> is returned. If the process identified
+ by <c><anno>Pid</anno></c> is not an existing local pid,
+ <c>{error, badarg}</c> is returned. <c>{error, badarg}</c> may also
+ be returned in some cases when <c><anno>Socket</anno></c> is closed
+ during the execution of this function.</p>
<p>If the socket is set in active mode, this function
will transfer any messages in the mailbox of the caller
to the new controlling process.
@@ -293,7 +297,7 @@ do_recv(Sock, Bs) ->
<seealso marker="#accept/1"><c>accept/1,2</c></seealso>.</p>
<note>
<p>The default values for options specified to <c>listen</c> can
- be affected by the <c>Kernel</c> configuration parameter
+ be affected by the Kernel configuration parameter
<c>inet_default_listen_options</c>. For details, see
<seealso marker="inet"><c>inet(3)</c></seealso>.</p>
</note>
@@ -307,7 +311,7 @@ do_recv(Sock, Bs) ->
<type_desc variable="HttpPacket">See the description of
<c>HttpPacket</c> in
<seealso marker="erts:erlang#decode_packet/3"><c>erlang:decode_packet/3</c></seealso>
- in <c>ERTS</c>.
+ in ERTS.
</type_desc>
<desc>
<p>Receives a packet from a socket in passive
diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml
index 3f88a0272d..f79566ef71 100644
--- a/lib/kernel/doc/src/gen_udp.xml
+++ b/lib/kernel/doc/src/gen_udp.xml
@@ -68,7 +68,11 @@
<c><anno>Socket</anno></c>. The controlling process is the process
that receives messages from the socket. If called by any other
process than the current controlling process,
- <c>{error, not_owner}</c> is returned.</p>
+ <c>{error, not_owner}</c> is returned. If the process identified
+ by <c><anno>Pid</anno></c> is not an existing local pid,
+ <c>{error, badarg}</c> is returned. <c>{error, badarg}</c> may also
+ be returned in some cases when <c><anno>Socket</anno></c> is closed
+ during the execution of this function.</p>
</desc>
</func>
diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml
index 864f8facac..5b5b71e521 100644
--- a/lib/kernel/doc/src/heart.xml
+++ b/lib/kernel/doc/src/heart.xml
@@ -37,10 +37,7 @@
the <c>heart</c> port program is to check that the Erlang runtime system
it is supervising is still running. If the port program has not
received any heartbeats within <c>HEART_BEAT_TIMEOUT</c> seconds
- (defaults to 60 seconds), the system can be rebooted. Also, if
- the system is equipped with a hardware watchdog timer and is
- running Solaris, the watchdog can be used to supervise the entire
- system.</p>
+ (defaults to 60 seconds), the system can be rebooted.</p>
<p>An Erlang runtime system to be monitored by a heart program
is to be started with command-line flag <c>-heart</c> (see
also <seealso marker="erts:erl"><c>erl(1)</c></seealso>).
@@ -51,17 +48,13 @@
or a terminated Erlang runtime system, environment variable
<c>HEART_COMMAND</c> must be set before the system is started.
If this variable is not set, a warning text is printed but
- the system does not reboot. However, if the hardware watchdog is
- used, it still triggers a reboot <c>HEART_BEAT_BOOT_DELAY</c>
- seconds later (defaults to 60 seconds).</p>
+ the system does not reboot.</p>
<p>To reboot on Windows, <c>HEART_COMMAND</c> can be
set to <c>heart -shutdown</c> (included in the Erlang delivery)
or to any other suitable program that can activate a reboot.</p>
- <p>The hardware watchdog is not started under Solaris if
- environment variable <c>HW_WD_DISABLE</c> is set.</p>
- <p>The environment variables <c>HEART_BEAT_TIMEOUT</c> and
- <c>HEART_BEAT_BOOT_DELAY</c> can be used to configure the heart
- time-outs; they can be set in the operating system shell before Erlang
+ <p>The environment variable <c>HEART_BEAT_TIMEOUT</c>
+ can be used to configure the heart
+ time-outs; it can be set in the operating system shell before Erlang
is started or be specified at the command line:</p>
<pre>
% <input>erl -heart -env HEART_BEAT_TIMEOUT 30 ...</input></pre>
@@ -83,7 +76,7 @@
<c><![CDATA[SIGKILL]]></c>:</p>
<pre>
% <input>erl -heart -env HEART_KILL_SIGNAL SIGABRT ...</input></pre>
- <p> If heart should <b>not</b> kill the Erlang runtime system, this can be indicated
+ <p> If heart should <em>not</em> kill the Erlang runtime system, this can be indicated
using the environment variable <c><![CDATA[HEART_NO_KILL=TRUE]]></c>.
This can be useful if the command executed by heart takes care of this,
for example as part of a specific cleanup sequence.
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index c0dce2f50c..4c4a5c39cb 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -36,7 +36,7 @@
<seealso marker="erts:inet_cfg">ERTS User's Guide:
Inet Configuration</seealso> for more information about how to
configure an Erlang runtime system for IP communication.</p>
- <p>The following two <c>Kernel</c> configuration parameters affect the
+ <p>The following two Kernel configuration parameters affect the
behavior of all sockets opened on an Erlang node:</p>
<list type="bulleted">
<item><p><c>inet_default_connect_options</c> can contain a list of
@@ -48,7 +48,7 @@
<p>When <c>accept</c> is issued, the values of the listening socket options
are inherited. No such application variable is therefore needed for
<c>accept</c>.</p>
- <p>Using the <c>Kernel</c> configuration parameters above, one
+ <p>Using the Kernel configuration parameters above, one
can set default options for all TCP sockets on a node, but use this
with care. Options such as <c>{delay_send,true}</c> can be
specified in this way. The following is an example of starting an Erlang
@@ -95,7 +95,7 @@ fe80::204:acff:fe17:bf38
<datatype>
<name name="hostent"/>
<desc>
- <p>The record is defined in the <c>Kernel</c> include file
+ <p>The record is defined in the Kernel include file
<c>"inet.hrl"</c>.</p>
<p>Add the following directive to the module:</p>
<code>
@@ -151,6 +151,12 @@ fe80::204:acff:fe17:bf38
<name name="socket_address"/>
</datatype>
<datatype>
+ <name name="socket_getopt"/>
+ </datatype>
+ <datatype>
+ <name name="socket_setopt"/>
+ </datatype>
+ <datatype>
<name name="returned_non_ip_address"/>
<desc>
<p>
@@ -327,8 +333,6 @@ fe80::204:acff:fe17:bf38
<func>
<name name="getopts" arity="2"/>
<fsummary>Get one or more options for a socket.</fsummary>
- <type name="socket_getopt"/>
- <type name="socket_setopt"/>
<desc>
<p>Gets one or more options for a socket. For a list of available
options, see
@@ -387,7 +391,7 @@ get_tcpi_sacked(Sock) ->
<<_:28/binary,TcpiSacked:32/native,_/binary>> = Info,
TcpiSacked.]]></code>
<p>Preferably, you would check the machine type, the operating system,
- and the <c>Kernel</c> version before executing anything similar to
+ and the Kernel version before executing anything similar to
this code.</p>
</desc>
</func>
@@ -580,7 +584,6 @@ get_tcpi_sacked(Sock) ->
<func>
<name name="setopts" arity="2"/>
<fsummary>Set one or more options for a socket.</fsummary>
- <type name="socket_setopt"/>
<desc>
<p>Sets one or more options for a socket.</p>
<p>The following options are available:</p>
@@ -658,7 +661,7 @@ get_tcpi_sacked(Sock) ->
<p>The size of the user-level software buffer used by
the driver. Not to be confused with options <c>sndbuf</c>
and <c>recbuf</c>, which correspond to the
- <c>Kernel</c> socket buffers. It is recommended
+ Kernel socket buffers. It is recommended
to have <c>val(buffer) &gt;= max(val(sndbuf),val(recbuf))</c> to
avoid performance issues because of unnecessary copying.
<c>val(buffer)</c> is automatically set to the above
@@ -717,7 +720,7 @@ get_tcpi_sacked(Sock) ->
<p>The socket message queue is set to a busy
state when the amount of data on the message
queue reaches this limit. Notice that this limit only
- concerns data that has not yet reached the <c>ERTS</c> internal
+ concerns data that has not yet reached the ERTS internal
socket implementation. Defaults to 8 kB.</p>
<p>Senders of data to the socket are suspended if
either the socket message queue is busy or the socket
@@ -733,7 +736,7 @@ get_tcpi_sacked(Sock) ->
<tag><c>{high_watermark, Size}</c> (TCP/IP sockets)</tag>
<item>
<p>The socket is set to a busy state when the amount
- of data queued internally by the <c>ERTS</c> socket implementation
+ of data queued internally by the ERTS socket implementation
reaches this limit. Defaults to 8 kB.</p>
<p>Senders of data to the socket are suspended if
either the socket message queue is busy or the socket
@@ -813,7 +816,7 @@ get_tcpi_sacked(Sock) ->
socket message queue is set in a not busy state when
the amount of data queued in the message queue falls
below this limit. Notice that this limit only concerns data
- that has not yet reached the <c>ERTS</c> internal socket
+ that has not yet reached the ERTS internal socket
implementation. Defaults to 4 kB.</p>
<p>Senders that are suspended because of either a
busy message queue or a busy socket are resumed
@@ -831,7 +834,7 @@ get_tcpi_sacked(Sock) ->
<item>
<p>If the socket is in a busy state, the socket is
set in a not busy state when the amount of data
- queued internally by the <c>ERTS</c> socket implementation
+ queued internally by the ERTS socket implementation
falls below this limit. Defaults to 4 kB.</p>
<p>Senders that are suspended because of a
busy message queue or a busy socket are resumed
@@ -951,7 +954,7 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp</code>
are returned with the format according to <c>HttpPacket</c>
described in
<seealso marker="erts:erlang#decode_packet/3">
- <c>erlang:decode_packet/3</c></seealso> in <c>ERTS</c>.
+ <c>erlang:decode_packet/3</c></seealso> in ERTS.
A socket in passive
mode returns <c>{ok, HttpPacket}</c> from <c>gen_tcp:recv</c>
while an active socket sends messages like
@@ -984,11 +987,6 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp</code>
<p>Sets the line delimiting character for line-oriented protocols
(<c>line</c>). Defaults to <c>$\n</c>.</p>
</item>
- <tag><c>{priority, Priority}</c></tag>
- <item>
- <p>Sets the protocol-defined priority for all packets to be sent
- on this socket.</p>
- </item>
<tag><c>{raw, Protocol, OptionNum, ValueBin}</c></tag>
<item>
<p>See below.</p>
@@ -1089,6 +1087,15 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp</code>
The option is ignored on platforms where it is not
implemented. Use with caution.</p>
</item>
+ <tag><c>{tclass, Integer}</c></tag>
+ <item>
+ <p>
+ Sets <c>IPV6_TCLASS IP</c> level options on platforms
+ where this is implemented. The behavior and allowed range
+ varies between different systems.
+ The option is ignored on platforms where it is not
+ implemented. Use with caution.</p>
+ </item>
</taglist>
<p>In addition to these options, <em>raw</em>
option specifications can be used. The raw options are
@@ -1127,7 +1134,7 @@ inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code>
can respond differently to this kind of option
manipulation. Use with care.</p>
<p>Notice that the default options for TCP/IP sockets can be
- changed with the <c>Kernel</c> configuration parameters mentioned in
+ changed with the Kernel configuration parameters mentioned in
the beginning of this manual page.</p>
</desc>
</func>
diff --git a/lib/kernel/doc/src/init_stub.xml b/lib/kernel/doc/src/init_stub.xml
index df89b174ca..1297c8264d 100644
--- a/lib/kernel/doc/src/init_stub.xml
+++ b/lib/kernel/doc/src/init_stub.xml
@@ -34,6 +34,6 @@
<modulesummary>Coordination of system startup.</modulesummary>
<description>
<p>This module is moved to the
- <seealso marker="erts:init"><c>ERTS</c></seealso> application.</p>
+ <seealso marker="erts:init">ERTS</seealso> application.</p>
</description>
</erlref>
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index 9e6fb60bb7..df681a505f 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -31,12 +31,12 @@
<app>kernel</app>
<appsummary>The Kernel application.</appsummary>
<description>
- <p>The <c>Kernel</c> application has all the code necessary to run
+ <p>The Kernel application has all the code necessary to run
the Erlang runtime system: file servers, code servers,
and so on.</p>
- <p>The <c>Kernel</c> application is the first application started. It is
+ <p>The Kernel application is the first application started. It is
mandatory in the sense that the minimal system based on
- Erlang/OTP consists of <c>Kernel</c> and <c>STDLIB</c>. <c>Kernel</c>
+ Erlang/OTP consists of Kernel and STDLIB. Kernel
contains the following functional areas:</p>
<list type="bulleted">
<item>Start, stop, supervision, configuration, and distribution of applications</item>
@@ -53,13 +53,13 @@
<section>
<title>Error Logger Event Handlers</title>
<p>Two standard error logger event handlers are defined in
- the <c>Kernel</c> application. These are described in
+ the Kernel application. These are described in
<seealso marker="error_logger"><c>error_logger(3)</c></seealso>.</p>
</section>
<section>
<title>Configuration</title>
- <p>The following configuration parameters are defined for the <c>Kernel</c>
+ <p>The following configuration parameters are defined for the Kernel
application. For more information about configuration parameters,
see file <seealso marker="app"><c>app(4)</c></seealso>.</p>
<taglist>
@@ -162,8 +162,8 @@
depth to which terms are printed by the error logger event
handlers included in OTP. This
configuration parameter is used by the two event handlers
- defined by the <c>Kernel</c> application and the two event
- handlers in the <c>SASL</c> application.
+ defined by the Kernel application and the two event
+ handlers in the SASL application.
(If you have implemented your own error handlers, this configuration
parameter has no effect on them.)</p>
@@ -173,7 +173,7 @@
<c>~P</c> and <c>~W</c>, respectively, and <c>Depth</c> is
used as the depth parameter. For details, see
<seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso>
- in <c>STDLIB</c>.</p>
+ in STDLIB.</p>
<note><p>A reasonable starting value for <c>Depth</c> is
<c>30</c>. We recommend to test crashing various processes in your
@@ -217,12 +217,14 @@
</item>
<tag><c>{inet_dist_listen_options, Opts}</c></tag>
<item>
+ <marker id="inet_dist_listen_options"></marker>
<p>Defines a list of extra socket options to be used when opening the
listening socket for a distributed Erlang node.
See <seealso marker="gen_tcp#listen/2"><c>gen_tcp:listen/2</c></seealso>.</p>
</item>
<tag><c>{inet_dist_connect_options, Opts}</c></tag>
<item>
+ <marker id="inet_dist_connect_options"></marker>
<p>Defines a list of extra socket options to be used when connecting to
other distributed Erlang nodes.
See <seealso marker="gen_tcp#connect/4"><c>gen_tcp:connect/4</c></seealso>.</p>
@@ -239,7 +241,7 @@
<p>The name (string) of an Inet user configuration file. For details,
see section
<seealso marker="erts:inet_cfg"><c>Inet Configuration</c></seealso>
- in the <c>ERTS</c> User's Guide.</p>
+ in the ERTS User's Guide.</p>
</item>
<tag><c>net_setuptime = SetupTime</c></tag>
<item>
@@ -358,7 +360,7 @@ MaxT = TickTime + TickTime / 4</code>
<tag><c>start_timer = true | false</c></tag>
<item>
<p>Starts the <c>timer_server</c> if the parameter is
- <c>true</c> (see <seealso marker="stdlib:timer"><c>stdlib:timer(3)</c></seealso>).
+ <c>true</c> (see <seealso marker="stdlib:timer"><c>timer(3)</c></seealso>).
This parameter is to be set to <c>true</c> in an embedded system
using this service.</p>
<p>Defaults to <c>false</c>.</p>
diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml
index f48a534d4f..4e2b0c69db 100644
--- a/lib/kernel/doc/src/net_kernel.xml
+++ b/lib/kernel/doc/src/net_kernel.xml
@@ -55,7 +55,7 @@ $ <input>erl -sname foobar</input></pre>
<seealso marker="erts:erl"><c>erl</c></seealso>.</p>
<p>Normally, connections are established automatically when
another node is referenced. This functionality can be disabled
- by setting <c>Kernel</c> configuration parameter
+ by setting Kernel configuration parameter
<c>dist_auto_connect</c> to <c>false</c>, see
<seealso marker="kernel_app"><c>kernel(6)</c></seealso>. In this case,
connections must be established explicitly by calling
@@ -116,6 +116,21 @@ $ <input>erl -sname foobar</input></pre>
</func>
<func>
+ <name name="getopts" arity="2"/>
+ <fsummary>Get distribution socket options.</fsummary>
+ <desc>
+ <p>Get one or more options for the distribution socket
+ connected to <c><anno>Node</anno></c>.</p>
+ <p>If <c><anno>Node</anno></c> is a connected node
+ the return value is the same as from
+ <seealso marker="inet#getopts/2"><c>inet:getopts(Sock, Options)</c></seealso>
+ where <c>Sock</c> is the distribution socket for <c><anno>Node</anno></c>.</p>
+ <p>Returns <c>ignored</c> if the local node is not alive or
+ <c>{error, noconnection}</c> if <c><anno>Node</anno></c> is not connected.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="monitor_nodes" arity="1"/>
<name name="monitor_nodes" arity="2"/>
<fsummary>Subscribe to node status change messages.</fsummary>
@@ -131,7 +146,7 @@ $ <input>erl -sname foobar</input></pre>
are stopped. Two
option lists are considered the same if they contain the same
set of options.</p>
- <p>As from <c>Kernel</c> version 2.11.4, and <c>ERTS</c> version
+ <p>As from Kernel version 2.11.4, and ERTS version
5.5.4, the following is guaranteed:</p>
<list type="bulleted">
<item><p><c>nodeup</c> messages are delivered before delivery
@@ -141,13 +156,13 @@ $ <input>erl -sname foobar</input></pre>
messages from the remote node that have been passed
through the connection have been delivered.</p></item>
</list>
- <p>Notice that this is <em>not</em> guaranteed for <c>Kernel</c>
+ <p>Notice that this is <em>not</em> guaranteed for Kernel
versions before 2.11.4.</p>
- <p>As from <c>Kernel</c> version 2.11.4, subscriptions can also be
+ <p>As from Kernel version 2.11.4, subscriptions can also be
made before the <c>net_kernel</c> server is started, that is,
<c>net_kernel:monitor_nodes/[1,2]</c> does not return
<c>ignored</c>.</p>
- <p>As from <c>Kernel</c> version 2.13, and <c>ERTS</c> version
+ <p>As from Kernel version 2.13, and ERTS version
5.7, the following is guaranteed:</p>
<list type="bulleted">
<item><p><c>nodeup</c> messages are delivered after the
@@ -157,7 +172,7 @@ $ <input>erl -sname foobar</input></pre>
corresponding node has disappeared in results from
<c>erlang:nodes/X</c>.</p></item>
</list>
- <p>Notice that this is <em>not</em> guaranteed for <c>Kernel</c>
+ <p>Notice that this is <em>not</em> guaranteed for Kernel
versions before 2.13.</p>
<p>The format of the node status change messages depends on
<c><anno>Options</anno></c>. If <c><anno>Options</anno></c> is
@@ -289,6 +304,27 @@ $ <input>erl -sname foobar</input></pre>
</func>
<func>
+ <name name="setopts" arity="2"/>
+ <fsummary>Set distribution socket options.</fsummary>
+ <desc>
+ <p>Set one or more options for distribution sockets.
+ Argument <c><anno>Node</anno></c> can be either one node name
+ or the atom <c>new</c> to affect the distribution sockets of all
+ future connected nodes.</p>
+ <p>The return value is the same as from
+ <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>
+ or <c>{error, noconnection}</c> if <c><anno>Node</anno></c> is not
+ a connected node or <c>new</c>.</p>
+ <p>If <c><anno>Node</anno></c> is <c>new</c> the <c><anno>Options</anno></c>
+ will then also be added to kernel configration parameters
+ <seealso marker="kernel:kernel_app#inet_dist_listen_options">inet_dist_listen_options</seealso>
+ and
+ <seealso marker="kernel:kernel_app#inet_dist_connect_options">inet_dist_connect_options</seealso>.</p>
+ <p>Returns <c>ignored</c> if the local node is not alive.</p>
+ </desc>
+ </func>
+
+ <func>
<name>start([Name]) -> {ok, pid()} | {error, Reason}</name>
<name>start([Name, NameType]) -> {ok, pid()} | {error, Reason}</name>
<name>start([Name, NameType, Ticktime]) -> {ok, pid()} | {error, Reason}</name>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index f37433110c..c6d7b00e24 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,201 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 5.2.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add <c>os:cmd/2</c> that takes an options map as the
+ second argument.</p>
+ <p>
+ Add <c>max_size</c> as an option to <c>os:cmd/2</c> that
+ control the maximum size of the result that
+ <c>os:cmd/2</c> will return.</p>
+ <p>
+ Own Id: OTP-14823</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a race during cleanup of os:cmd that would cause
+ os:cmd to hang indefinitely.</p>
+ <p>
+ Own Id: OTP-14232 Aux Id: seq13275 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>The functions in the '<c>file</c>' module that take a
+ list of paths (e.g. <c>file:path_consult/2</c>) will now
+ continue to search in the path if the path contains
+ something that is not a directory.</p>
+ <p>
+ Own Id: OTP-14191</p>
+ </item>
+ <item>
+ <p>Two OTP processes that are known to receive many
+ messages are 'rex' (used by 'rpc') and 'error_logger'.
+ Those processes will now store unprocessed messages
+ outside the process heap, which will potentially decrease
+ the cost of garbage collections.</p>
+ <p>
+ Own Id: OTP-14192</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>code:add_pathsa/1</c> and command line option
+ <c>-pa</c> both revert the given list of directories when
+ adding it at the beginning of the code path. This is now
+ documented.</p>
+ <p>
+ Own Id: OTP-13920 Aux Id: ERL-267 </p>
+ </item>
+ <item>
+ <p>
+ Add lost runtime dependency to erts-8.1. This should have
+ been done in kernel-5.1 (OTP-19.1) as it cannot run
+ without at least erts-8.1 (OTP-19.1).</p>
+ <p>
+ Own Id: OTP-14003</p>
+ </item>
+ <item>
+ <p>
+ Type and doc for gen_{tcp,udp,sctp}:controlling_process/2
+ has been improved.</p>
+ <p>
+ Own Id: OTP-14022 Aux Id: PR-1208 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a memory leak when calling
+ seq_trace:get_system_tracer().</p>
+ <p>
+ Own Id: OTP-13742</p>
+ </item>
+ <item>
+ <p>
+ Fix for the problem that when adding the ebin directory
+ of an application to the code path, the
+ <c>code:priv_dir/1</c> function returns an incorrect path
+ to the priv directory of the same application.</p>
+ <p>
+ Own Id: OTP-13758 Aux Id: ERL-195 </p>
+ </item>
+ <item>
+ <p>
+ Fix code_server crash when adding code paths of two
+ levels.</p>
+ <p>
+ Own Id: OTP-13765 Aux Id: ERL-194 </p>
+ </item>
+ <item>
+ <p>
+ Respect -proto_dist switch while connection to EPMD</p>
+ <p>
+ Own Id: OTP-13770 Aux Id: PR-1129 </p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug where init:stop could deadlock if a process
+ with infinite shutdown timeout (e.g. a supervisor)
+ attempted to load code while terminating.</p>
+ <p>
+ Own Id: OTP-13802</p>
+ </item>
+ <item>
+ <p>
+ Close stdin of commands run in os:cmd. This is a
+ backwards compatiblity fix that restores the behaviour of
+ pre 19.0 os:cmd.</p>
+ <p>
+ Own Id: OTP-13867 Aux Id: seq13178 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add <c>net_kernel:setopts/2</c> and
+ <c>net_kernel:getopts/2</c> to control options for
+ distribution sockets in runtime.</p>
+ <p>
+ Own Id: OTP-13564</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When calling os:cmd from a process that has set trap_exit
+ to true an 'EXIT' message would be left in the message
+ queue. This bug was introduced in kernel vsn 5.0.1.</p>
+ <p>
+ Own Id: OTP-13813</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a os:cmd bug where creating a background job using
+ &amp; would cause os:cmd to hang until the background job
+ terminated or closed its stdout and stderr file
+ descriptors. This bug has existed from kernel 5.0.</p>
+ <p>
+ Own Id: OTP-13741</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 5.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -3447,7 +3642,7 @@
types (for instance, <c>ensure_loaded/1</c> now only
accepts an atom as documented; it used to accept a string
too).</p>
- <p><c>Dialyzer</c> will generally emit warnings for any
+ <p>Dialyzer will generally emit warnings for any
calls that use undocumented argument types. Even if the
call happens to still work in R12B, you should correct
your code. A future release will adhere to the
diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml
index 739ac35d2a..a6a63448b7 100644
--- a/lib/kernel/doc/src/os.xml
+++ b/lib/kernel/doc/src/os.xml
@@ -38,17 +38,35 @@
most platforms.</p>
</description>
+ <datatypes>
+ <datatype>
+ <name name="os_command"/>
+ </datatype>
+ <datatype>
+ <name name="os_command_opts"/>
+ <desc>
+ <p>Options for <seealso marker="#cmd/2"><c>os:cmd/2</c></seealso></p>
+ <taglist>
+ <tag><c>max_size</c></tag>
+ <item>
+ <p>The maximum size of the data returned by the <c>os:cmd</c> call.
+ See the <seealso marker="#cmd/2"><c>os:cmd/2</c></seealso>
+ documentation for more details.</p>
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
+ </datatypes>
+
<funcs>
<func>
<name name="cmd" arity="1"/>
+ <name name="cmd" arity="2"/>
<fsummary>Execute a command in a shell of the target OS.</fsummary>
<desc>
<p>Executes <c><anno>Command</anno></c> in a command shell of the
- target OS,
- captures the standard output of the command, and returns this
- result as a string. This function is a replacement of
- the previous function <c>unix:cmd/1</c>; they are equivalent on a
- Unix platform.</p>
+ target OS, captures the standard output of the command,
+ and returns this result as a string.</p>
<p><em>Examples:</em></p>
<code type="none">
LsOut = os:cmd("ls"), % on unix platform
@@ -57,6 +75,21 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
called from another program (for example, <c>os:cmd/1</c>)
can differ, compared with the standard output of the command
when called directly from an OS command shell.</p>
+ <p><c>os:cmd/2</c> was added in kernel-5.5 (OTP-20.2.1). It makes it
+ possible to pass an options map as the second argument in order to
+ control the behaviour of <c>os:cmd</c>. The possible options are:
+ </p>
+ <taglist>
+ <tag><c>max_size</c></tag>
+ <item>
+ <p>The maximum size of the data returned by the <c>os:cmd</c> call.
+ This option is a safety feature that should be used when the command
+ executed can return a very large, possibly infinite, result.</p>
+ <code type="none">
+> os:cmd("cat /dev/zero", #{ max_size => 20 }).
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]</code>
+ </item>
+ </taglist>
</desc>
</func>
diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml
index 8cad9fe4fc..adec2d9520 100644
--- a/lib/kernel/doc/src/rpc.xml
+++ b/lib/kernel/doc/src/rpc.xml
@@ -88,6 +88,12 @@
to retrieve the value of evaluating <c>apply(<anno>Module</anno>,
<anno>Function</anno>, <anno>Args</anno>)</c> on node
<c><anno>Node</anno></c>.</p>
+ <note>
+ <p><seealso marker="#yield/1"><c>yield/1</c></seealso> and
+ <seealso marker="#nb_yield/1"><c>nb_yield/1,2</c></seealso>
+ must be called by the same process from which this function
+ was made otherwise they will never yield correctly.</p>
+ </note>
</desc>
</func>
@@ -299,6 +305,11 @@
the tuple <c>{value, <anno>Val</anno>}</c> when the computation is
finished, or <c>timeout</c> when <c><anno>Timeout</anno></c>
milliseconds has elapsed.</p>
+ <note>
+ <p>This function must be called by the same process from which
+ <seealso marker="#async_call/4"><c>async_call/4</c></seealso>
+ was made otherwise it will only return <c>timeout</c>.</p>
+ </note>
</desc>
</func>
@@ -320,7 +331,7 @@
<fsummary>Information about a process.</fsummary>
<desc>
<p>Location transparent version of the BIF
- <seealso marker="erts:erlang#process_info/1"><c>erlang:process_info/1</c></seealso> in <c>ERTS</c>.</p>
+ <seealso marker="erts:erlang#process_info/1"><c>erlang:process_info/1</c></seealso> in ERTS.</p>
</desc>
</func>
@@ -330,7 +341,7 @@
<fsummary>Information about a process.</fsummary>
<desc>
<p>Location transparent version of the BIF
- <seealso marker="erts:erlang#process_info/2"><c>erlang:process_info/2</c></seealso> in <c>ERTS</c>.</p>
+ <seealso marker="erts:erlang#process_info/2"><c>erlang:process_info/2</c></seealso> in ERTS.</p>
</desc>
</func>
@@ -407,6 +418,11 @@
If the answer is available, it is
returned immediately. Otherwise, the calling process is
suspended until the answer arrives from <c>Node</c>.</p>
+ <note>
+ <p>This function must be called by the same process from which
+ <seealso marker="#async_call/4"><c>async_call/4</c></seealso>
+ was made otherwise it will never return.</p>
+ </note>
</desc>
</func>
</funcs>
diff --git a/lib/kernel/doc/src/zlib_stub.xml b/lib/kernel/doc/src/zlib_stub.xml
index b111581b10..9ab9c4eb62 100644
--- a/lib/kernel/doc/src/zlib_stub.xml
+++ b/lib/kernel/doc/src/zlib_stub.xml
@@ -34,6 +34,6 @@
<modulesummary>Zlib compression interface.</modulesummary>
<description>
<p>This module is moved to the
- <seealso marker="erts:zlib"><c>ERTS</c></seealso> application.</p>
+ <seealso marker="erts:zlib">ERTS</seealso> application.</p>
</description>
</erlref>
diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl
index 43e50d4325..320e916c04 100644
--- a/lib/kernel/include/dist_util.hrl
+++ b/lib/kernel/include/dist_util.hrl
@@ -63,7 +63,7 @@
f_getll, %% Get low level port or pid.
f_address, %% The address of the "socket",
%% generated from Socket,Node
- %% These two are used in the tick loop,
+ %% These three are used in the tick loop,
%% so they are not fun's to avoid holding old code.
mf_tick, %% Takes the socket as parameters and
%% sends a tick, this is no fun, it
@@ -74,7 +74,11 @@
%% {ok, RecvCnt, SendCnt, SendPend} for
%% a given socket. This is a {M,F},
%% returning {error, Reason on failure}
- request_type = normal
+ request_type = normal,
+
+ %% New in kernel-5.1 (OTP 19.1):
+ mf_setopts, %% netkernel:setopts on active connection
+ mf_getopts %% netkernel:getopts on active connection
}).
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 6174136507..59b26176bf 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -135,10 +135,14 @@ split_paths([], _S, Path, Paths) ->
-spec call(term()) -> term().
call(Req) ->
+ Ref = erlang:monitor(process, ?MODULE),
?MODULE ! {code_call, self(), Req},
receive
{?MODULE, Reply} ->
- Reply
+ erlang:demonitor(Ref,[flush]),
+ Reply;
+ {'DOWN',Ref,process,_,_} ->
+ exit({'DOWN',code_server,Req})
end.
reply(Pid, Res) ->
@@ -807,7 +811,13 @@ clear_namedb([], _) ->
%% Dir must be a complete pathname (not only a name).
insert_dir(Dir, Db) ->
Splitted = filename:split(Dir),
- Name = get_name_from_splitted(Splitted),
+ case get_name_from_splitted(Splitted) of
+ Name when Name /= "ebin", Name /= "." ->
+ Name;
+ _ ->
+ SplittedAbsName = filename:split(absname(Dir)),
+ Name = get_name_from_splitted(SplittedAbsName)
+ end,
AppDir = filename:join(del_ebin_1(Splitted)),
do_insert_name(Name, AppDir, Db).
@@ -933,15 +943,25 @@ del_ebin(Dir) ->
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.
+ case filename:basename(Parent) of
+ [] ->
+ %% Parent is the root directory
[Parent,App];
- Archive ->
- %% Archive.
- [Archive]
+ _ ->
+ Ext = archive_extension(),
+ case filename:basename(Parent, Ext) of
+ Parent ->
+ %% Plain directory.
+ [Parent,App];
+ Archive ->
+ %% Archive.
+ [Archive]
+ end
end;
+del_ebin_1(Path = [_App,"ebin"]) ->
+ del_ebin_1(filename:split(absname(filename:join(Path))));
+del_ebin_1(["ebin"]) ->
+ del_ebin_1(filename:split(absname("ebin")));
del_ebin_1([H|T]) ->
[H|del_ebin_1(T)];
del_ebin_1([]) ->
@@ -1372,11 +1392,10 @@ finish_on_load(PidRef, OnLoadRes, #state{on_load=OnLoad0}=St0) ->
finish_on_load_1(Mod, OnLoadRes, Waiting, St) ->
Keep = OnLoadRes =:= ok,
- erlang:finish_after_on_load(Mod, Keep),
+ erts_code_purger: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 ->
{module,Mod}
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index 47d0c1b861..8d2fc4d4b7 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -143,7 +143,11 @@ handshake_other_started(#hs_data{request_type=ReqType}=HSData0) ->
ChallengeB = recv_challenge_reply(HSData, ChallengeA, MyCookie),
send_challenge_ack(HSData, gen_digest(ChallengeB, HisCookie)),
?debug({dist_util, self(), accept_connection, Node}),
- connection(HSData).
+ connection(HSData);
+
+handshake_other_started(OldHsData) when element(1,OldHsData) =:= hs_data ->
+ handshake_other_started(convert_old_hsdata(OldHsData)).
+
%%
%% check if connecting node is allowed to connect
@@ -330,7 +334,20 @@ handshake_we_started(#hs_data{request_type=ReqType,
gen_digest(ChallengeA,HisCookie)),
reset_timer(NewHSData#hs_data.timer),
recv_challenge_ack(NewHSData, MyChallenge, MyCookie),
- connection(NewHSData).
+ connection(NewHSData);
+
+handshake_we_started(OldHsData) when element(1,OldHsData) =:= hs_data ->
+ handshake_we_started(convert_old_hsdata(OldHsData)).
+
+convert_old_hsdata({hs_data, KP, ON, TN, S, T, TF, A, OV, OF, OS, FS, FR,
+ FS_PRE, FS_POST, FG, FA, MFT, MFG, RT}) ->
+ #hs_data{
+ kernel_pid = KP, other_node = ON, this_node = TN, socket = S, timer = T,
+ this_flags = TF, allowed = A, other_version = OV, other_flags = OF,
+ other_started = OS, f_send = FS, f_recv = FR, f_setopts_pre_nodeup = FS_PRE,
+ f_setopts_post_nodeup = FS_POST, f_getll = FG, f_address = FA,
+ mf_tick = MFT, mf_getstat = MFG, request_type = RT}.
+
%% --------------------------------------------------------------
%% The connection has been established.
@@ -350,15 +367,15 @@ connection(#hs_data{other_node = Node,
mark_nodeup(HSData,Address),
case FPostNodeup(Socket) of
ok ->
- con_loop(HSData#hs_data.kernel_pid,
- Node,
- Socket,
- Address,
- HSData#hs_data.this_node,
- PType,
- #tick{},
- HSData#hs_data.mf_tick,
- HSData#hs_data.mf_getstat);
+ con_loop({HSData#hs_data.kernel_pid,
+ Node,
+ Socket,
+ PType,
+ HSData#hs_data.mf_tick,
+ HSData#hs_data.mf_getstat,
+ HSData#hs_data.mf_setopts,
+ HSData#hs_data.mf_getopts},
+ #tick{});
_ ->
?shutdown2(Node, connection_setup_failed)
end;
@@ -454,8 +471,8 @@ mark_nodeup(#hs_data{kernel_pid = Kernel,
?shutdown(Node)
end.
-con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode, Type, Tick, MFTick, MFGetstat) ->
+con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=ConData,
+ Tick) ->
receive
{tcp_closed, Socket} ->
?shutdown2(Node, connection_closed);
@@ -468,15 +485,12 @@ con_loop(Kernel, Node, Socket, TcpAddress,
_ ->
ignore_it
end,
- con_loop(Kernel, Node, Socket, TcpAddress, MyNode, Type,
- Tick, MFTick, MFGetstat);
+ con_loop(ConData, Tick);
{Kernel, tick} ->
case send_tick(Socket, Tick, Type,
MFTick, MFGetstat) of
{ok, NewTick} ->
- con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode, Type, NewTick, MFTick,
- MFGetstat);
+ con_loop(ConData, NewTick);
{error, not_responding} ->
error_msg("** Node ~p not responding **~n"
"** Removing (timedout) connection **~n",
@@ -489,13 +503,24 @@ con_loop(Kernel, Node, Socket, TcpAddress,
case MFGetstat(Socket) of
{ok, Read, Write, _} ->
From ! {self(), get_status, {ok, Read, Write}},
- con_loop(Kernel, Node, Socket, TcpAddress,
- MyNode,
- Type, Tick,
- MFTick, MFGetstat);
+ con_loop(ConData, Tick);
_ ->
?shutdown2(Node, get_status_failed)
- end
+ end;
+ {From, Ref, {setopts, Opts}} ->
+ Ret = case MFSetOpts of
+ undefined -> {error, enotsup};
+ _ -> MFSetOpts(Socket, Opts)
+ end,
+ From ! {Ref, Ret},
+ con_loop(ConData, Tick);
+ {From, Ref, {getopts, Opts}} ->
+ Ret = case MFGetOpts of
+ undefined -> {error, enotsup};
+ _ -> MFGetOpts(Socket, Opts)
+ end,
+ From ! {Ref, Ret},
+ con_loop(ConData, Tick)
end.
diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index f8ef4a475d..7bc9e2ede3 100644
--- a/lib/kernel/src/erl_epmd.erl
+++ b/lib/kernel/src/erl_epmd.erl
@@ -103,6 +103,10 @@ names(EpmdAddr) ->
register_node(Name, PortNo) ->
register_node(Name, PortNo, inet).
+register_node(Name, PortNo, inet_tcp) ->
+ register_node(Name, PortNo, inet);
+register_node(Name, PortNo, inet6_tcp) ->
+ register_node(Name, PortNo, inet6);
register_node(Name, PortNo, Family) ->
gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity).
@@ -403,8 +407,6 @@ select_best_version(L1, _H1, _L2, H2) when L1 > H2 ->
0;
select_best_version(_L1, H1, L2, _H2) when L2 > H1 ->
0;
-select_best_version(_L1, H1, L2, _H2) when L2 > H1 ->
- 0;
select_best_version(_L1, H1, _L2, H2) ->
erlang:min(H1, H2).
diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl
index 3523f680a3..81f1bf8d97 100644
--- a/lib/kernel/src/error_logger.erl
+++ b/lib/kernel/src/error_logger.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -360,8 +360,12 @@ init(Max) when is_integer(Max) ->
%% go back.
init({go_back, _PostState}) ->
{ok, {?buffer_size, 0, []}};
-init(_) -> %% Start and just relay to other
- {ok, []}. %% node if node(GLeader) =/= node().
+init(_) ->
+ %% The error logger process may receive a huge amount of
+ %% messages. Make sure that they are stored off heap to
+ %% avoid exessive GCs.
+ process_flag(message_queue_data, off_heap),
+ {ok, []}.
-spec handle_event(term(), state()) -> {'ok', state()}.
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index 58b601e456..6d94f7770f 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -1413,7 +1413,7 @@ path_open_first([Path|Rest], Name, Mode, LastError) ->
case open(FileName, Mode) of
{ok, Fd} ->
{ok, Fd, FileName};
- {error, enoent} ->
+ {error, Reason} when Reason =:= enoent; Reason =:= enotdir ->
path_open_first(Rest, Name, Mode, LastError);
Error ->
Error
diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl
index b133e6fed4..a6aa0edd15 100644
--- a/lib/kernel/src/gen_sctp.erl
+++ b/lib/kernel/src/gen_sctp.erl
@@ -439,7 +439,7 @@ error_string(X) ->
-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when
Socket :: sctp_socket(),
Pid :: pid(),
- Reason :: closed | not_owner | inet:posix().
+ Reason :: closed | not_owner | badarg | inet:posix().
controlling_process(S, Pid) when is_port(S), is_pid(Pid) ->
inet:udp_controlling_process(S, Pid);
diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl
index 1a21541b7c..ac61dbc792 100644
--- a/lib/kernel/src/gen_tcp.erl
+++ b/lib/kernel/src/gen_tcp.erl
@@ -320,7 +320,7 @@ unrecv(S, Data) when is_port(S) ->
-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when
Socket :: socket(),
Pid :: pid(),
- Reason :: closed | not_owner | inet:posix().
+ Reason :: closed | not_owner | badarg | inet:posix().
controlling_process(S, NewOwner) ->
case inet_db:lookup_socket(S) of
diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl
index 98d2f0bcfb..3121544719 100644
--- a/lib/kernel/src/gen_udp.erl
+++ b/lib/kernel/src/gen_udp.erl
@@ -195,7 +195,7 @@ connect(S, Address, Port) when is_port(S) ->
-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when
Socket :: socket(),
Pid :: pid(),
- Reason :: closed | not_owner | inet:posix().
+ Reason :: closed | not_owner | badarg | inet:posix().
controlling_process(S, NewOwner) ->
inet:udp_controlling_process(S, NewOwner).
diff --git a/lib/kernel/src/global_group.erl b/lib/kernel/src/global_group.erl
index 8ac0bd9551..f5ead2a4c5 100644
--- a/lib/kernel/src/global_group.erl
+++ b/lib/kernel/src/global_group.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2015. All Rights Reserved.
+%% 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.
diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl
index eea78aabdf..8fa48d56fb 100644
--- a/lib/kernel/src/heart.erl
+++ b/lib/kernel/src/heart.erl
@@ -198,16 +198,11 @@ start_portprogram() ->
end.
get_heart_timeouts() ->
- HeartOpts = case os:getenv("HEART_BEAT_TIMEOUT") of
- false -> "";
- H when is_list(H) ->
- "-ht " ++ H
- end,
- HeartOpts ++ case os:getenv("HEART_BEAT_BOOT_DELAY") of
- false -> "";
- W when is_list(W) ->
- " -wt " ++ W
- end.
+ case os:getenv("HEART_BEAT_TIMEOUT") of
+ false -> "";
+ H when is_list(H) ->
+ "-ht " ++ H
+ end.
check_start_heart() ->
case init:get_argument(heart) of
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index a91a6ed517..f5c13ecdd7 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -75,6 +75,7 @@
-export_type([address_family/0, hostent/0, hostname/0, ip4_address/0,
ip6_address/0, ip_address/0, port_number/0,
local_address/0, socket_address/0, returned_non_ip_address/0,
+ socket_setopt/0, socket_getopt/0,
posix/0, socket/0, stat_option/0]).
%% imports
-import(lists, [append/1, duplicate/2, filter/2, foldl/3]).
@@ -676,7 +677,7 @@ parse_strict_address(Addr) ->
%% Return a list of available options
options() ->
[
- tos, priority, reuseaddr, keepalive, dontroute, linger,
+ tos, tclass, priority, reuseaddr, keepalive, dontroute, linger,
broadcast, sndbuf, recbuf, nodelay, ipv6_v6only,
buffer, header, active, packet, deliver, mode,
multicast_if, multicast_ttl, multicast_loop,
@@ -697,7 +698,7 @@ stats() ->
%% Available options for tcp:connect
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
connect_options() ->
- [tos, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
+ [tos, tclass, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
header, active, packet, packet_size, buffer, mode, deliver, line_delimiter,
exit_on_close, high_watermark, low_watermark, high_msgq_watermark,
low_msgq_watermark, send_timeout, send_timeout_close, delay_send, raw,
@@ -765,7 +766,7 @@ con_add(Name, Val, #connect_opts{} = R, Opts, AllOpts) ->
%% Available options for tcp:listen
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
listen_options() ->
- [tos, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
+ [tos, tclass, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
header, active, packet, buffer, mode, deliver, backlog, ipv6_v6only,
exit_on_close, high_watermark, low_watermark, high_msgq_watermark,
low_msgq_watermark, send_timeout, send_timeout_close, delay_send,
@@ -845,7 +846,7 @@ tcp_module_1(Opts, Address) ->
%% Available options for udp:open
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
udp_options() ->
- [tos, priority, reuseaddr, sndbuf, recbuf, header, active, buffer, mode,
+ [tos, tclass, priority, reuseaddr, sndbuf, recbuf, header, active, buffer, mode,
deliver, ipv6_v6only,
broadcast, dontroute, multicast_if, multicast_ttl, multicast_loop,
add_membership, drop_membership, read_packets,raw,
@@ -916,7 +917,7 @@ udp_module(Opts) ->
% (*) passing of open FDs ("fdopen") is not supported.
sctp_options() ->
[ % The following are generic inet options supported for SCTP sockets:
- mode, active, buffer, tos, priority, dontroute, reuseaddr, linger, sndbuf,
+ mode, active, buffer, tos, tclass, priority, dontroute, reuseaddr, linger, sndbuf,
recbuf, ipv6_v6only, high_msgq_watermark, low_msgq_watermark,
% Other options are SCTP-specific (though they may be similar to their
diff --git a/lib/kernel/src/inet6_tcp_dist.erl b/lib/kernel/src/inet6_tcp_dist.erl
index 3aa61973af..9b6c2745d5 100644
--- a/lib/kernel/src/inet6_tcp_dist.erl
+++ b/lib/kernel/src/inet6_tcp_dist.erl
@@ -24,6 +24,7 @@
-export([listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+-export([setopts/2, getopts/2]).
%% ------------------------------------------------------------
%% Select this protocol based on node name
@@ -72,3 +73,9 @@ close(Socket) ->
is_node_name(Node) when is_atom(Node) ->
inet_tcp_dist:is_node_name(Node).
+
+setopts(S, Opts) ->
+ inet_tcp_dist:setopts(S, Opts).
+
+getopts(S, Opts) ->
+ inet_tcp_dist:getopts(S, Opts).
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index c8a8962e78..4e8f59a3b9 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -153,6 +153,7 @@
-define(INET_LOPT_NETNS, 38).
-define(INET_LOPT_TCP_SHOW_ECONNRESET, 39).
-define(INET_LOPT_LINE_DELIM, 40).
+-define(INET_OPT_TCLASS, 41).
% Specific SCTP options: separate range:
-define(SCTP_OPT_RTOINFO, 100).
-define(SCTP_OPT_ASSOCINFO, 101).
diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
index f91d7ef7c3..8c8fe86811 100644
--- a/lib/kernel/src/inet_tcp_dist.erl
+++ b/lib/kernel/src/inet_tcp_dist.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,13 +24,16 @@
-export([listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+%% Optional
+-export([setopts/2, getopts/2]).
+
%% Generalized dist API
-export([gen_listen/2, gen_accept/2, gen_accept_connection/6,
gen_setup/6, gen_select/2]).
%% internal exports
--export([accept_loop/3,do_accept/7,do_setup/7,getstat/1]).
+-export([accept_loop/3,do_accept/7,do_setup/7,getstat/1,tick/2]).
-import(error_logger,[error_msg/2]).
@@ -74,7 +77,7 @@ gen_listen(Driver, Name) ->
TcpAddress = get_tcp_address(Driver, Socket),
{_,Port} = TcpAddress#net_address.address,
ErlEpmd = net_kernel:epmd_module(),
- case ErlEpmd:register_node(Name, Port) of
+ case ErlEpmd:register_node(Name, Port, Driver) of
{ok, Creation} ->
{ok, {Socket, TcpAddress, Creation}};
Error ->
@@ -215,8 +218,10 @@ do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
inet:getll(S)
end,
f_address = fun(S, Node) -> get_remote_id(Driver, S, Node) end,
- mf_tick = fun(S) -> tick(Driver, S) end,
- mf_getstat = fun ?MODULE:getstat/1
+ mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end,
+ mf_getstat = fun ?MODULE:getstat/1,
+ mf_setopts = fun ?MODULE:setopts/2,
+ mf_getopts = fun ?MODULE:getopts/2
},
dist_util:handshake_other_started(HSData);
{false,IP} ->
@@ -320,6 +325,7 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
{packet, 4},
nodelay()])
end,
+
f_getll = fun inet:getll/1,
f_address =
fun(_,_) ->
@@ -329,9 +335,11 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
protocol = tcp,
family = AddressFamily}
end,
- mf_tick = fun(S) -> tick(Driver, S) end,
+ mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end,
mf_getstat = fun ?MODULE:getstat/1,
- request_type = Type
+ request_type = Type,
+ mf_setopts = fun ?MODULE:setopts/2,
+ mf_getopts = fun ?MODULE:getopts/2
},
dist_util:handshake_we_started(HSData);
_ ->
@@ -492,3 +500,12 @@ split_stat([], R, W, P) ->
{ok, R, W, P}.
+setopts(S, Opts) ->
+ case [Opt || {K,_}=Opt <- Opts,
+ K =:= active orelse K =:= deliver orelse K =:= packet] of
+ [] -> inet:setopts(S,Opts);
+ Opts1 -> {error, {badopts,Opts1}}
+ end.
+
+getopts(S, Opts) ->
+ inet:getopts(S, Opts).
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 56d1699656..d184223524 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -118,6 +118,6 @@
{applications, []},
{env, [{error_logger, tty}]},
{mod, {kernel, []}},
- {runtime_dependencies, ["erts-8.0", "stdlib-3.0", "sasl-3.0"]}
+ {runtime_dependencies, ["erts-8.1", "stdlib-3.0", "sasl-3.0"]}
]
}.
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index d16e200cb3..e1173cd2f4 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2017. 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.
@@ -18,9 +18,9 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.*
%% Down to - max one major revision back
- [{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.*
}.
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index ac19f4935b..0c679e7349 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -59,6 +59,8 @@
connect_node/1,
monitor_nodes/1,
monitor_nodes/2,
+ setopts/2,
+ getopts/2,
start/1,
stop/0]).
@@ -111,7 +113,7 @@
}).
-record(listen, {
- listen, %% listen pid
+ listen, %% listen socket
accept, %% accepting pid
address, %% #net_address
module %% proto module
@@ -384,7 +386,7 @@ init({Name, LongOrShortNames, TickT, CleanHalt}) ->
connections =
ets:new(sys_dist,[named_table,
protected,
- {keypos, 2}]),
+ {keypos, #connection.node}]),
listen = Listeners,
allowed = [],
verbose = 0
@@ -554,6 +556,38 @@ handle_call({new_ticktime,_T,_TP},
#state{tick = #tick_change{time = T}} = State) ->
async_reply({reply, {ongoing_change_to, T}, State}, From);
+handle_call({setopts, new, Opts}, From, State) ->
+ Ret = setopts_new(Opts, State),
+ async_reply({reply, Ret, State}, From);
+
+handle_call({setopts, Node, Opts}, From, State) ->
+ Return =
+ case ets:lookup(sys_dist, Node) of
+ [Conn] when Conn#connection.state =:= up ->
+ case call_owner(Conn#connection.owner, {setopts, Opts}) of
+ {ok, Ret} -> Ret;
+ _ -> {error, noconnection}
+ end;
+
+ _ ->
+ {error, noconnection}
+ end,
+ async_reply({reply, Return, State}, From);
+
+handle_call({getopts, Node, Opts}, From, State) ->
+ Return =
+ case ets:lookup(sys_dist, Node) of
+ [Conn] when Conn#connection.state =:= up ->
+ case call_owner(Conn#connection.owner, {getopts, Opts}) of
+ {ok, Ret} -> Ret;
+ _ -> {error, noconnection}
+ end;
+
+ _ ->
+ {error, noconnection}
+ end,
+ async_reply({reply, Return, State}, From);
+
handle_call(_Msg, _From, State) ->
{noreply, State}.
@@ -1608,3 +1642,93 @@ async_gen_server_reply(From, Msg) ->
{'EXIT', _} ->
ok
end.
+
+call_owner(Owner, Msg) ->
+ Mref = monitor(process, Owner),
+ Owner ! {self(), Mref, Msg},
+ receive
+ {Mref, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {ok, Reply};
+ {'DOWN', Mref, _, _, _} ->
+ error
+ end.
+
+
+-spec setopts(Node, Options) -> ok | {error, Reason} | ignored when
+ Node :: node() | new,
+ Options :: [inet:socket_setopt()],
+ Reason :: inet:posix() | noconnection.
+
+setopts(Node, Opts) when is_atom(Node), is_list(Opts) ->
+ request({setopts, Node, Opts}).
+
+setopts_new(Opts, State) ->
+ %% First try setopts on listening socket(s)
+ %% Bail out on failure.
+ %% If successful, we are pretty sure Opts are ok
+ %% and we continue with config params and pending connections.
+ case setopts_on_listen(Opts, State#state.listen) of
+ ok ->
+ setopts_new_1(Opts);
+ Fail -> Fail
+ end.
+
+setopts_on_listen(_, []) -> ok;
+setopts_on_listen(Opts, [#listen {listen = LSocket, module = Mod} | T]) ->
+ try Mod:setopts(LSocket, Opts) of
+ ok ->
+ setopts_on_listen(Opts, T);
+ Fail -> Fail
+ catch
+ error:undef -> {error, enotsup}
+ end.
+
+setopts_new_1(Opts) ->
+ ConnectOpts = case application:get_env(kernel, inet_dist_connect_options) of
+ {ok, CO} -> CO;
+ _ -> []
+ end,
+ application:set_env(kernel, inet_dist_connect_options,
+ merge_opts(Opts,ConnectOpts)),
+ ListenOpts = case application:get_env(kernel, inet_dist_listen_options) of
+ {ok, LO} -> LO;
+ _ -> []
+ end,
+ application:set_env(kernel, inet_dist_listen_options,
+ merge_opts(Opts, ListenOpts)),
+ case lists:keyfind(nodelay, 1, Opts) of
+ {nodelay, ND} when is_boolean(ND) ->
+ application:set_env(kernel, dist_nodelay, ND);
+ _ -> ignore
+ end,
+
+ %% Update any pending connections
+ PendingConns = ets:select(sys_dist, [{'_',
+ [{'=/=',{element,#connection.state,'$_'},up}],
+ ['$_']}]),
+ lists:foreach(fun(#connection{state = pending, owner = Owner}) ->
+ call_owner(Owner, {setopts, Opts});
+ (#connection{state = up_pending, pending_owner = Owner}) ->
+ call_owner(Owner, {setopts, Opts});
+ (_) -> ignore
+ end, PendingConns),
+ ok.
+
+merge_opts([], B) ->
+ B;
+merge_opts([H|T], B0) ->
+ {Key, _} = H,
+ B1 = lists:filter(fun({K,_}) -> K =/= Key end, B0),
+ merge_opts(T, [H | B1]).
+
+-spec getopts(Node, Options) ->
+ {'ok', OptionValues} | {'error', Reason} | ignored when
+ Node :: node(),
+ Options :: [inet:socket_getopt()],
+ OptionValues :: [inet:socket_setopt()],
+ Reason :: inet:posix() | noconnection.
+
+getopts(Node, Opts) when is_atom(Node), is_list(Opts) ->
+ request({getopts, Node, Opts}).
+
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index f0ad26b1f2..92ac401b72 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. 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.
@@ -21,7 +21,7 @@
%% Provides a common operating system interface.
--export([type/0, version/0, cmd/1, find_executable/1, find_executable/2]).
+-export([type/0, version/0, cmd/1, cmd/2, find_executable/1, find_executable/2]).
-include("file.hrl").
@@ -32,6 +32,11 @@
putenv/2, system_time/0, system_time/1,
timestamp/0, unsetenv/1]).
+-type os_command() :: atom() | io_lib:chars().
+-type os_command_opts() :: #{ max_size => non_neg_integer() | infinity }.
+
+-export_type([os_command/0, os_command_opts/0]).
+
-spec getenv() -> [string()].
getenv() -> erlang:nif_error(undef).
@@ -223,14 +228,22 @@ extensions() ->
%% Executes the given command in the default shell for the operating system.
-spec cmd(Command) -> string() when
- Command :: atom() | io_lib:chars().
+ Command :: os_command().
cmd(Cmd) ->
+ cmd(Cmd, #{ }).
+
+-spec cmd(Command, Options) -> string() when
+ Command :: os_command(),
+ Options :: os_command_opts().
+cmd(Cmd, Opts) ->
validate(Cmd),
- {SpawnCmd, SpawnOpts, SpawnInput} = mk_cmd(os:type(), Cmd),
+ {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd),
Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout,
- stream, in, eof, hide | SpawnOpts]),
+ stream, in, hide | SpawnOpts]),
+ MonRef = erlang:monitor(port, Port),
true = port_command(Port, SpawnInput),
- Bytes = get_data(Port, []),
+ Bytes = get_data(Port, MonRef, Eot, [], 0, maps:get(max_size, Opts, infinity)),
+ demonitor(MonRef, [flush]),
String = unicode:characters_to_list(Bytes),
if %% Convert to unicode list if possible otherwise return bytes
is_list(String) -> String;
@@ -243,7 +256,7 @@ mk_cmd({win32,Wtype}, Cmd) ->
{false,_} -> lists:concat(["cmd /c", Cmd]);
{Cspec,_} -> lists:concat([Cspec," /c",Cmd])
end,
- {Command, [], []};
+ {Command, [], [], <<>>};
mk_cmd(OsType,Cmd) when is_atom(Cmd) ->
mk_cmd(OsType, atom_to_list(Cmd));
mk_cmd(_,Cmd) ->
@@ -252,7 +265,20 @@ mk_cmd(_,Cmd) ->
{"/bin/sh -s unix:cmd", [out],
%% We insert a new line after the command, in case the command
%% contains a comment character.
- ["(", unicode:characters_to_binary(Cmd), "\n); exit\n"]}.
+ %%
+ %% The </dev/null closes stdin, which means that programs
+ %% that use a closed stdin as an termination indicator works.
+ %% An example of such a program is 'more'.
+ %%
+ %% The "echo ^D" is used to indicate that the program has executed
+ %% and we should return any output we have gotten. We cannot use
+ %% termination of the child or closing of stdin/stdout as then
+ %% starting background jobs from os:cmd will block os:cmd.
+ %%
+ %% I tried changing this to be "better", but got bombarded with
+ %% backwards incompatibility bug reports, so leave this as it is.
+ ["(", unicode:characters_to_binary(Cmd), "\n) </dev/null; echo \"\^D\"\n"],
+ <<$\^D>>}.
validate(Atom) when is_atom(Atom) ->
ok;
@@ -267,21 +293,54 @@ validate1([List|Rest]) when is_list(List) ->
validate1([]) ->
ok.
-get_data(Port, Sofar) ->
+get_data(Port, MonRef, Eot, Sofar, Size, Max) ->
receive
{Port, {data, Bytes}} ->
- get_data(Port, [Sofar,Bytes]);
- {Port, eof} ->
- Port ! {self(), close},
- receive
- {Port, closed} ->
- true
- end,
- receive
- {'EXIT', Port, _} ->
- ok
- after 1 -> % force context switch
- ok
- end,
+ case eot(Bytes, Eot, Size, Max) of
+ more ->
+ get_data(Port, MonRef, Eot, [Sofar, Bytes],
+ Size + byte_size(Bytes), Max);
+ Last ->
+ catch port_close(Port),
+ flush_until_down(Port, MonRef),
+ iolist_to_binary([Sofar, Last])
+ end;
+ {'DOWN', MonRef, _, _, _} ->
+ flush_exit(Port),
iolist_to_binary(Sofar)
end.
+
+eot(Bs, <<>>, Size, Max) when Size + byte_size(Bs) < Max ->
+ more;
+eot(Bs, <<>>, Size, Max) ->
+ binary:part(Bs, {0, Max - Size});
+eot(Bs, Eot, Size, Max) ->
+ case binary:match(Bs, Eot) of
+ {Pos, _} when Size + Pos < Max ->
+ binary:part(Bs,{0, Pos});
+ _ ->
+ eot(Bs, <<>>, Size, Max)
+ end.
+
+%% When port_close returns we know that all the
+%% messages sent have been sent and that the
+%% DOWN message is after them all.
+flush_until_down(Port, MonRef) ->
+ receive
+ {Port, {data, _Bytes}} ->
+ flush_until_down(Port, MonRef);
+ {'DOWN', MonRef, _, _, _} ->
+ flush_exit(Port)
+ end.
+
+%% The exit signal is always delivered before
+%% the down signal, so we can be sure that if there
+%% was an exit message sent, it will be in the
+%% mailbox now.
+flush_exit(Port) ->
+ receive
+ {'EXIT', Port, _} ->
+ ok
+ after 0 ->
+ ok
+ end.
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index 21bff02214..0e0b7dffa3 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -67,17 +67,27 @@
%%------------------------------------------------------------------------
+
+%% The rex server may receive a huge amount of
+%% messages. Make sure that they are stored off heap to
+%% avoid exessive GCs.
+
+-define(SPAWN_OPTS, [{spawn_opt,[{message_queue_data,off_heap}]}]).
+
%% Remote execution and broadcasting facility
-spec start() -> {'ok', pid()} | 'ignore' | {'error', term()}.
start() ->
- gen_server:start({local,?NAME}, ?MODULE, [], []).
+ gen_server:start({local,?NAME}, ?MODULE, [], ?SPAWN_OPTS).
-spec start_link() -> {'ok', pid()} | 'ignore' | {'error', term()}.
start_link() ->
- gen_server:start_link({local,?NAME}, ?MODULE, [], []).
+ %% The rex server process may receive a huge amount of
+ %% messages. Make sure that they are stored off heap to
+ %% avoid exessive GCs.
+ gen_server:start_link({local,?NAME}, ?MODULE, [], ?SPAWN_OPTS).
-spec stop() -> term().
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 8da67c89f8..a5a5da4684 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -35,7 +35,9 @@
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, on_load_update/1,
+ on_load_trace_on_load/1,
on_load_purge/1, on_load_self_call/1, on_load_pending/1,
+ on_load_deleted/1,
big_boot_embedded/1,
native_early_modules/1, get_mode/1,
normalized_paths/1]).
@@ -64,12 +66,15 @@ 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_update,
+ on_load_binary, on_load_embedded, on_load_errors,
+ {group, sequence},
on_load_purge, on_load_self_call, on_load_pending,
+ on_load_deleted,
big_boot_embedded, native_early_modules, get_mode, normalized_paths].
-groups() ->
- [].
+%% These need to run in order
+groups() -> [{sequence, [sequence], [on_load_update,
+ on_load_trace_on_load]}].
init_per_group(_GroupName, Config) ->
Config.
@@ -586,20 +591,28 @@ sticky_compiler(Files, PrivDir) ->
[R || R <- Rets, R =/= ok].
do_sticky_compile(Mod, Dir) ->
- %% Make sure that the module is loaded. A module being sticky
- %% only prevents it from begin reloaded, not from being loaded
- %% from the wrong place to begin with.
- Mod = Mod:module_info(module),
- File = filename:append(Dir, atom_to_list(Mod)),
- Src = io_lib:format("-module(~s).\n"
- "-export([test/1]).\n"
- "test(me) -> fail.\n", [Mod]),
- ok = file:write_file(File++".erl", Src),
- case c:c(File, [{outdir,Dir}]) of
- {ok,Module} ->
- Module:test(me);
- {error,sticky_directory} ->
- ok
+ case code:is_sticky(Mod) of
+ true ->
+ %% Make sure that the module is loaded. A module being sticky
+ %% only prevents it from begin reloaded, not from being loaded
+ %% from the wrong place to begin with.
+ Mod = Mod:module_info(module),
+ File = filename:append(Dir, atom_to_list(Mod)),
+ Src = io_lib:format("-module(~s).\n"
+ "-export([test/1]).\n"
+ "test(me) -> fail.\n", [Mod]),
+ ok = file:write_file(File++".erl", Src),
+ case c:c(File, [{outdir,Dir}]) of
+ {ok,Module} ->
+ Module:test(me);
+ {error,sticky_directory} ->
+ ok
+ end;
+ false ->
+ %% For some reason the module is not sticky
+ %% could be that the .erlang file has
+ %% unstuck it?
+ {Mod, is_not_sticky}
end.
%% Test that the -pa and -pz options work as expected.
@@ -1446,7 +1459,7 @@ do_on_load_error(ReturnValue) ->
{undef,[{on_load_error,main,[],_}|_]} = Exit
end.
-on_load_update(_Config) ->
+on_load_update(Config) ->
{Mod,Code1} = on_load_update_code(1),
{module,Mod} = code:load_binary(Mod, "", Code1),
42 = Mod:a(),
@@ -1456,7 +1469,7 @@ on_load_update(_Config) ->
{Mod,Code2} = on_load_update_code(2),
{error,on_load_failure} = code:load_binary(Mod, "", Code2),
42 = Mod:a(),
- 100 = Mod:b(99),
+ 78 = Mod:b(77),
{'EXIT',{undef,_}} = (catch Mod:never()),
4 = erlang:trace_pattern({Mod,'_','_'}, false),
@@ -1467,6 +1480,9 @@ on_load_update(_Config) ->
{'EXIT',{undef,_}} = (catch Mod:b(10)),
{'EXIT',{undef,_}} = (catch Mod:never()),
+ code:purge(Mod),
+ code:delete(Mod),
+ code:purge(Mod),
ok.
on_load_update_code(Version) ->
@@ -1498,6 +1514,40 @@ on_load_update_code_1(3, Mod) ->
"f() -> ok.\n",
"c() -> 100.\n"]).
+%% Test -on_load while trace feature 'on_load' is enabled (OTP-14612)
+on_load_trace_on_load(Config) ->
+ Papa = self(),
+ Tracer = spawn_link(fun F() -> receive M -> Papa ! M end, F() end),
+ {tracer,[]} = erlang:trace_info(self(),tracer),
+ erlang:trace(self(), true, [call, {tracer, Tracer}]),
+ erlang:trace_pattern(on_load, true, []),
+ on_load_update(Config),
+ erlang:trace_pattern(on_load, false, []),
+ erlang:trace(self(), false, [call]),
+
+ %% WE GET TRACES FOR CALLS TO UNDEFINED FUNCTIONS ???
+ %% Remove filter when that is fixed.
+ Ms = lists:filter(fun({trace,Papa,call,
+ {error_handler,undefined_function,
+ [on_load_update_code,_,_]}})
+ -> false;
+ (_) -> true
+ end,
+ flush()),
+
+ [{trace, Papa, call, {on_load_update_code, a, []}},
+ {trace, Papa, call, {on_load_update_code, b, [99]}},
+ {trace, Papa, call, {on_load_update_code, c, []}}] = Ms,
+
+ exit(Tracer, normal),
+ ok.
+
+flush() ->
+ receive M -> [M | flush()]
+ after 100 -> []
+ end.
+
+
on_load_purge(_Config) ->
Mod = ?FUNCTION_NAME,
register(Mod, self()),
@@ -1602,6 +1652,98 @@ on_load_pending(_Config) ->
ok = Mod:t(),
ok.
+on_load_deleted(_Config) ->
+ Mod = ?FUNCTION_NAME,
+
+ R0 = fun() ->
+ Tree = ?Q(["-module('@Mod@').\n",
+ "-on_load(f/0).\n",
+ "f() -> ok.\n"]),
+ merl:print(Tree),
+ {ok,Mod,Code} = merl:compile(Tree),
+ {module,Mod} = code:load_binary(Mod, "", Code)
+ end,
+ delete_before_reload(Mod, R0),
+ delete_before_reload(Mod, R0),
+
+ R1 = fun() ->
+ Tree = ?Q(["-module('@Mod@').\n",
+ "-on_load(f/0).\n",
+ "f() -> fail.\n"]),
+ merl:print(Tree),
+ {ok,Mod,Code} = merl:compile(Tree),
+ {error,on_load_failure} = code:load_binary(Mod, "", Code)
+ end,
+ delete_before_reload(Mod, R1),
+ delete_before_reload(Mod, R1),
+
+ OtherMod = list_to_atom(lists:concat([Mod,"_42"])),
+ OtherTree = ?Q(["-module('@OtherMod@').\n"]),
+ merl:print(OtherTree),
+ {ok,OtherMod,OtherCode} = merl:compile(OtherTree),
+
+ R2 = fun() ->
+ RegName = 'on_load__registered_name',
+ Tree = ?Q(["-module('@Mod@').\n",
+ "-on_load(f/0).\n",
+ "f() ->\n",
+ " register('@RegName@', self()),\n",
+ " receive _ -> ok end.\n"]),
+ merl:print(Tree),
+ {ok,Mod,Code} = merl:compile(Tree),
+ spawn(fun() ->
+ {module,Mod} = code:load_binary(Mod, "", Code)
+ end),
+ receive after 1 -> ok end,
+ {module,OtherMod} = code:load_binary(OtherMod, "",
+ OtherCode),
+ RegName ! stop
+ end,
+ delete_before_reload(Mod, R2),
+
+ ok.
+
+delete_before_reload(Mod, Reload) ->
+ false = check_old_code(Mod),
+
+ Tree1 = ?Q(["-module('@Mod@').\n",
+ "-export([f/1]).\n",
+ "f(Parent) ->\n",
+ " register('@Mod@', self()),\n",
+ " Parent ! started,\n",
+ " receive _ -> ok end.\n"]),
+ merl:print(Tree1),
+ {ok,Mod,Code1} = merl:compile(Tree1),
+
+ Self = self(),
+ spawn(fun() ->
+ {module,Mod} = code:load_binary(Mod, "", Code1),
+ Mod:f(Self)
+ end),
+ receive started -> ok end,
+
+ true = code:delete(Mod),
+ true = check_old_code(Mod),
+
+ Reload(),
+
+ %% When loading the the module with the -on_load() function,
+ %% the reference to the old code would be lost. Make sure that
+ %% the old code is remembered and is still preventing the
+ %% purge.
+ false = code:soft_purge(Mod),
+
+ %% Get rid of the old code.
+ Mod ! stop,
+ receive after 1 -> ok end,
+ true = code:soft_purge(Mod),
+
+ %% Unload the version of the module with the -on_load() function.
+ true = code:delete(Mod),
+ true = code:soft_purge(Mod),
+
+ ok.
+
%% Test that the native code of early loaded modules is loaded.
native_early_modules(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index eb58e92224..e43be77428 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -25,6 +25,7 @@
init_per_group/2,end_per_group/2]).
-export([tick/1, tick_change/1, illegal_nodenames/1, hidden_node/1,
+ setopts/1,
table_waste/1, net_setuptime/1,
inet_dist_options_options/1,
@@ -42,6 +43,8 @@
-export([get_socket_priorities/0,
tick_cli_test/1, tick_cli_test1/1,
tick_serv_test/2, tick_serv_test1/1,
+ run_remote_test/1,
+ setopts_do/2,
keep_conn/1, time_ping/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -66,6 +69,7 @@ suite() ->
all() ->
[tick, tick_change, illegal_nodenames, hidden_node,
+ setopts,
table_waste, net_setuptime, inet_dist_options_options,
{group, monitor_nodes}].
@@ -282,6 +286,165 @@ tick_cli_test1(Node) ->
end
end.
+setopts(Config) when is_list(Config) ->
+ register(setopts_regname, self()),
+ [N1,N2,N3,N4] = get_nodenames(4, setopts),
+
+ {_N1F,Port1} = start_node_unconnected(N1, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()), "1", "ping"]),
+ 0 = wait_for_port_exit(Port1),
+
+ {_N2F,Port2} = start_node_unconnected(N2, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()), "2", "ping"]),
+ 0 = wait_for_port_exit(Port2),
+
+ {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]),
+ {ok, LTcpPort} = inet:port(LSock),
+
+ {N3F,Port3} = start_node_unconnected(N3, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()),
+ "1", integer_to_list(LTcpPort)]),
+ wait_and_connect(LSock, N3F, Port3),
+ 0 = wait_for_port_exit(Port3),
+
+ {N4F,Port4} = start_node_unconnected(N4, ?MODULE, run_remote_test,
+ ["setopts_do", atom_to_list(node()),
+ "2", integer_to_list(LTcpPort)]),
+ wait_and_connect(LSock, N4F, Port4),
+ 0 = wait_for_port_exit(Port4),
+
+ ok.
+
+wait_and_connect(LSock, NodeName, NodePort) ->
+ {ok, Sock} = gen_tcp:accept(LSock),
+ {ok, "Connect please"} = gen_tcp:recv(Sock, 0),
+ flush_from_port(NodePort),
+ pong = net_adm:ping(NodeName),
+ gen_tcp:send(Sock, "Connect done"),
+ gen_tcp:close(Sock).
+
+
+flush_from_port(Port) ->
+ flush_from_port(Port, 10).
+
+flush_from_port(Port, Timeout) ->
+ receive
+ {Port,{data,String}} ->
+ io:format("~p: ~s\n", [Port, String]),
+ flush_from_port(Port, Timeout)
+ after Timeout ->
+ timeout
+ end.
+
+wait_for_port_exit(Port) ->
+ case (receive M -> M end) of
+ {Port,{exit_status,Status}} ->
+ Status;
+ {Port,{data,String}} ->
+ io:format("~p: ~s\n", [Port, String]),
+ wait_for_port_exit(Port)
+ end.
+
+run_remote_test([FuncStr, TestNodeStr | Args]) ->
+ Status = try
+ io:format("Node ~p started~n", [node()]),
+ TestNode = list_to_atom(TestNodeStr),
+ io:format("Node ~p spawning function ~p~n", [node(), FuncStr]),
+ {Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]),
+ io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]),
+ receive
+ {'DOWN', Ref, process, Pid, normal} ->
+ 0;
+ Other ->
+ io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]),
+ 1
+ end
+ catch
+ C:E ->
+ io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n",
+ [node(), C, E, erlang:get_stacktrace()]),
+ 2
+ end,
+ io:format("Node ~p doing halt(~p).\n",[node(), Status]),
+ erlang:halt(Status).
+
+% Do the actual test on the remote node
+setopts_do(TestNode, [OptNr, ConnectData]) ->
+ [] = nodes(),
+ {Opt, Val} = opt_from_nr(OptNr),
+ ok = net_kernel:setopts(new, [{Opt, Val}]),
+
+ [] = nodes(),
+ {error, noconnection} = net_kernel:getopts(TestNode, [Opt]),
+
+ case ConnectData of
+ "ping" -> % We connect
+ net_adm:ping(TestNode);
+ TcpPort -> % Other connect
+ {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort),
+ [{active,false},{packet,2}]),
+ ok = gen_tcp:send(Sock, "Connect please"),
+ {ok, "Connect done"} = gen_tcp:recv(Sock, 0),
+ gen_tcp:close(Sock)
+ end,
+ [TestNode] = nodes(),
+ {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
+ {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]),
+
+ NewVal = change_val(Val),
+ ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]),
+ {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]),
+
+ ok = net_kernel:setopts(TestNode, [{Opt, Val}]),
+ {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
+
+ ok.
+
+opt_from_nr("1") -> {nodelay, true};
+opt_from_nr("2") -> {nodelay, false}.
+
+change_val(true) -> false;
+change_val(false) -> true.
+
+start_node_unconnected(Name, Mod, Func, Args) ->
+ FullName = full_node_name(Name),
+ CmdLine = mk_node_cmdline(Name,Mod,Func,Args),
+ io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
+ case open_port({spawn, CmdLine}, [exit_status]) of
+ Port when is_port(Port) ->
+ {FullName, Port};
+ Error ->
+ exit({failed_to_start_node, FullName, Error})
+ end.
+
+full_node_name(PreName) ->
+ HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
+ atom_to_list(node())),
+ list_to_atom(atom_to_list(PreName) ++ HostSuffix).
+
+mk_node_cmdline(Name,Mod,Func,Args) ->
+ Static = "-noinput",
+ Pa = filename:dirname(code:which(?MODULE)),
+ Prog = case catch init:get_argument(progname) of
+ {ok,[[P]]} -> P;
+ _ -> exit(no_progname_argument_found)
+ end,
+ NameSw = case net_kernel:longnames() of
+ false -> "-sname ";
+ true -> "-name ";
+ _ -> exit(not_distributed_node)
+ end,
+ {ok, Pwd} = file:get_cwd(),
+ NameStr = atom_to_list(Name),
+ Prog ++ " "
+ ++ Static ++ " "
+ ++ NameSw ++ " " ++ NameStr
+ ++ " -pa " ++ Pa
+ ++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr
+ ++ " -setcookie " ++ atom_to_list(erlang:get_cookie())
+ ++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func)
+ ++ " " ++ string:join(Args, " ").
+
%% OTP-4255.
tick_change(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/error_logger_SUITE.erl b/lib/kernel/test/error_logger_SUITE.erl
index b6e7551741..2d26a7246c 100644
--- a/lib/kernel/test/error_logger_SUITE.erl
+++ b/lib/kernel/test/error_logger_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -30,6 +30,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
+ off_heap/1,
error_report/1, info_report/1, error/1, info/1,
emulator/1, tty/1, logfile/1, add/1, delete/1]).
@@ -45,7 +46,7 @@ suite() ->
{timetrap,{minutes,1}}].
all() ->
- [error_report, info_report, error, info, emulator, tty,
+ [off_heap, error_report, info_report, error, info, emulator, tty,
logfile, add, delete].
groups() ->
@@ -66,6 +67,16 @@ end_per_group(_GroupName, Config) ->
%%-----------------------------------------------------------------
+off_heap(_Config) ->
+ %% The error_logger process may receive a huge amount of
+ %% messages. Make sure that they are stored off heap to
+ %% avoid exessive GCs.
+ MQD = message_queue_data,
+ {MQD,off_heap} = process_info(whereis(error_logger), MQD),
+ ok.
+
+%%-----------------------------------------------------------------
+
error_report(Config) when is_list(Config) ->
error_logger:add_report_handler(?MODULE, self()),
Rep1 = [{tag1,"data1"},{tag2,data2},{tag3,3}],
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 5f049c6f99..c37d114a58 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -493,22 +493,13 @@ read_write_file(Config) when is_list(Config) ->
%% Try writing and reading back some term
SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]},
- ok = ?FILE_MODULE:write_file(Name,term_to_binary(SomeTerm)),
- {ok,Bin1} = ?FILE_MODULE:read_file(Name),
- SomeTerm = binary_to_term(Bin1),
+ Bin1 = term_to_binary(SomeTerm),
+ ok = do_read_write_file(Name, Bin1),
%% Try a "null" term
NullTerm = [],
- ok = ?FILE_MODULE:write_file(Name,term_to_binary(NullTerm)),
- {ok,Bin2} = ?FILE_MODULE:read_file(Name),
- NullTerm = binary_to_term(Bin2),
-
- %% Try some "complicated" types
- BigNum = 123456789012345678901234567890,
- ComplTerm = {self(),make_ref(),BigNum,3.14159},
- ok = ?FILE_MODULE:write_file(Name,term_to_binary(ComplTerm)),
- {ok,Bin3} = ?FILE_MODULE:read_file(Name),
- ComplTerm = binary_to_term(Bin3),
+ Bin2 = term_to_binary(NullTerm),
+ ok = do_read_write_file(Name, Bin2),
%% Try reading a nonexistent file
Name2 = filename:join(RootDir,
@@ -519,25 +510,42 @@ read_write_file(Config) when is_list(Config) ->
{error, enoent} = ?FILE_MODULE:read_file(''),
%% Try writing to a bad filename
- {error, enoent} =
- ?FILE_MODULE:write_file("",term_to_binary(NullTerm)),
+ {error, enoent} = do_read_write_file("", Bin2),
%% Try writing something else than a binary
- {error, badarg} = ?FILE_MODULE:write_file(Name,{1,2,3}),
- {error, badarg} = ?FILE_MODULE:write_file(Name,self()),
+ {error, badarg} = do_read_write_file(Name, {1,2,3}),
+ {error, badarg} = do_read_write_file(Name, self()),
%% Some non-term binaries
- ok = ?FILE_MODULE:write_file(Name,[]),
- {ok,Bin4} = ?FILE_MODULE:read_file(Name),
- 0 = byte_size(Bin4),
+ ok = do_read_write_file(Name, []),
- ok = ?FILE_MODULE:write_file(Name,[Bin1,[],[[Bin2]]]),
- {ok,Bin5} = ?FILE_MODULE:read_file(Name),
- {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)),
+ %% Write some iolists
+ ok = do_read_write_file(Name, [Bin1,[],[[Bin2]]]),
+ ok = do_read_write_file(Name, ["string",<<"binary">>]),
+ ok = do_read_write_file(Name, "pure string"),
[] = flush(),
ok.
+do_read_write_file(Name, Data) ->
+ case ?FILE_MODULE:write_file(Name, Data) of
+ ok ->
+ BinData = iolist_to_binary(Data),
+ {ok,BinData} = ?FILE_MODULE:read_file(Name),
+
+ ok = ?FILE_MODULE:write_file(Name, Data, []),
+ {ok,BinData} = ?FILE_MODULE:read_file(Name),
+
+ ok = ?FILE_MODULE:write_file(Name, Data, [raw]),
+ {ok,BinData} = ?FILE_MODULE:read_file(Name),
+
+ ok;
+ {error,_}=Res ->
+ Res = ?FILE_MODULE:write_file(Name, Data, []),
+ Res = ?FILE_MODULE:write_file(Name, Data, [raw]),
+ Res
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index f836b2aa94..620ab235a0 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -117,7 +117,11 @@ xfer_min(Config) when is_list(Config) ->
Stream = 0,
Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
Loopback = {127,0,0,1},
+ StatOpts =
+ [recv_avg,recv_cnt,recv_max,recv_oct,
+ send_avg,send_cnt,send_max,send_oct],
{ok,Sb} = gen_sctp:open([{type,seqpacket}]),
+ {ok,SbStat1} = inet:getstat(Sb, StatOpts),
{ok,Pb} = inet:port(Sb),
ok = gen_sctp:listen(Sb, true),
@@ -212,6 +216,8 @@ xfer_min(Config) when is_list(Config) ->
assoc_id=SbAssocId}} =
recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
ok = gen_sctp:close(Sa),
+ {ok,SbStat2} = inet:getstat(Sb, StatOpts),
+ [] = filter_stat_eq(SbStat1, SbStat2),
ok = gen_sctp:close(Sb),
receive
@@ -220,6 +226,18 @@ xfer_min(Config) when is_list(Config) ->
end,
ok.
+filter_stat_eq([], []) ->
+ [];
+filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) ->
+ if
+ Val1 == Val2 ->
+ [Stat|filter_stat_eq(SbStat1, SbStat2)];
+ true ->
+ filter_stat_eq(SbStat1, SbStat2)
+ end.
+
+
+
%% Minimal data transfer in active mode.
xfer_active(Config) when is_list(Config) ->
Timeout = 2000,
@@ -383,26 +401,28 @@ def_sndrcvinfo(Config) when is_list(Config) ->
assoc_id=S2AssocId} = S2AssocChange =
log_ok(gen_sctp:connect(S2, Loopback, P1, [])),
?LOGVAR(S2AssocChange),
- case recv_event(log_ok(gen_sctp:recv(S1))) of
- {Loopback,P2,
- #sctp_assoc_change{
- state=comm_up,
- error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId);
- {Loopback,P2,
- #sctp_paddr_change{
- state=addr_confirmed,
- error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId),
+ S1AssocId =
+ case recv_event(log_ok(gen_sctp:recv(S1))) of
{Loopback,P2,
#sctp_assoc_change{
state=comm_up,
error=0,
- assoc_id=S1AssocId}} =
- recv_event(log_ok(gen_sctp:recv(S1)))
- end,
+ assoc_id=AssocId}} ->
+ AssocId;
+ {Loopback,P2,
+ #sctp_paddr_change{
+ state=addr_confirmed,
+ error=0,
+ assoc_id=AssocId}} ->
+ {Loopback,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ error=0,
+ assoc_id=AssocId}} =
+ recv_event(log_ok(gen_sctp:recv(S1))),
+ AssocId
+ end,
+ ?LOGVAR(S1AssocId),
#sctp_sndrcvinfo{
ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} =
@@ -1055,6 +1075,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
Addr = {127,0,0,1},
Stream = 0,
Timeout = 333,
+ StartTime = timestamp(),
S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
?LOGVAR(S1),
P1 = socket_call(S1, get_port),
@@ -1077,7 +1098,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId2}}} -> AssocId2
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
?LOGVAR(S2Ai),
S1Ai =
@@ -1087,7 +1108,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
?LOGVAR(S1Ai),
%%
@@ -1095,13 +1116,13 @@ peeloff(Config, SockOpts) when is_list(Config) ->
receive
{S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}),
receive
{S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout),
@@ -1120,31 +1141,31 @@ peeloff(Config, SockOpts) when is_list(Config) ->
receive
{S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2,S3])
+ socket_bailout([S1,S2,S3], StartTime)
end,
socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}),
receive
{S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2,S3])
+ socket_bailout([S1,S2,S3], StartTime)
end,
%%
inet:i(sctp),
- socket_close_verbose(S1),
- socket_close_verbose(S2),
+ socket_close_verbose(S1, StartTime),
+ socket_close_verbose(S2, StartTime),
receive
{S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} ->
match_unless_solaris(S3Ai, S3Ai_X)
after Timeout ->
- socket_bailout([S3])
+ socket_bailout([S3], StartTime)
end,
receive
{S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp,
assoc_id=S3Ai}}} -> ok
after Timeout ->
- socket_bailout([S3])
+ socket_bailout([S3], StartTime)
end,
- socket_close_verbose(S3),
+ socket_close_verbose(S3, StartTime),
[] = flush(),
ok.
@@ -1156,6 +1177,7 @@ buffers(Config) when is_list(Config) ->
Addr = {127,0,0,1},
Stream = 1,
Timeout = 3333,
+ StartTime = timestamp(),
S1 = socket_open([{ip,Addr}], Timeout),
?LOGVAR(S1),
P1 = socket_call(S1, get_port),
@@ -1174,7 +1196,7 @@ buffers(Config) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId2}}} -> AssocId2
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
S1Ai =
receive
@@ -1183,7 +1205,7 @@ buffers(Config) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
socket_call(S1, {setopts,[{recbuf,Limit}]}),
@@ -1197,22 +1219,22 @@ buffers(Config) when is_list(Config) ->
receive
{S1,{Addr,P2,S1Ai,Stream,Data}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
- socket_close_verbose(S1),
+ socket_close_verbose(S1, StartTime),
receive
{S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok
after Timeout ->
- socket_bailout([S2])
+ socket_bailout([S2], StartTime)
end,
receive
{S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp,
assoc_id=S2Ai}}} -> ok
after Timeout ->
- socket_bailout([S2])
+ socket_bailout([S2], StartTime)
end,
- socket_close_verbose(S2),
+ socket_close_verbose(S2, StartTime),
[] = flush(),
ok.
@@ -1521,8 +1543,8 @@ socket_peeloff(Socket, AssocId, SocketOpts, Timeout) ->
end,
s_start(Starter, Timeout).
-socket_close_verbose(S) ->
- History = socket_history(socket_close(S)),
+socket_close_verbose(S, StartTime) ->
+ History = socket_history(socket_close(S), StartTime),
io:format("socket_close ~p:~n ~p.~n", [S,History]),
History.
@@ -1535,19 +1557,19 @@ socket_call(S, Request) ->
%% socket_get(S, Key) ->
%% s_req(S, {get,Key}).
-socket_bailout([S|Ss]) ->
- History = socket_history(socket_close(S)),
+socket_bailout([S|Ss], StartTime) ->
+ History = socket_history(socket_close(S), StartTime),
io:format("bailout ~p:~n ~p.~n", [S,History]),
- socket_bailout(Ss);
-socket_bailout([]) ->
+ socket_bailout(Ss, StartTime);
+socket_bailout([], _) ->
io:format("flush: ~p.~n", [flush()]),
ct:fail(socket_bailout).
-socket_history({State,Flush}) ->
+socket_history({State,Flush}, StartTime) ->
{lists:keysort(
2,
lists:flatten(
- [[{Key,Val} || Val <- Vals]
+ [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals]
|| {Key,Vals} <- gb_trees:to_list(State)])),
Flush}.
@@ -1610,14 +1632,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
{Parent,Ref,exit} ->
ok = gen_sctp:close(Socket),
Key = exit,
- Val = {now(),Socket},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, Socket, State),
Parent ! {self(),Ref,{NewState,flush()}};
{Parent,Ref,{Msg}} ->
Result = Handler(Msg),
Key = req,
- Val = {now(),{Msg,Result}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Msg,Result}, State),
Parent ! {self(),Ref,Result},
s_loop(Socket, Timeout, Parent, Handler, NewState);
%% {Parent,Ref,{get,Key}} ->
@@ -1627,16 +1647,15 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
{[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}}
when not is_tuple(Data) ->
case gb_get({assoc_change,AssocId}, State) of
- [{_,{Addr,Port,
- #sctp_assoc_change{
- state=comm_up,
- inbound_streams=Is}}}|_]
+ [{Addr,Port,
+ #sctp_assoc_change{
+ state=comm_up,
+ inbound_streams=Is}}|_]
when 0 =< Stream, Stream < Is-> ok;
[] -> ok
end,
Key = {msg,AssocId,Stream},
- Val = {now(),{Addr,Port,SRI,Data}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SRI,Data}, State),
Parent ! {self(),{Addr,Port,AssocId,Stream,Data}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1647,13 +1666,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
Key = {assoc_change,AssocId},
- Val = {now(),{Addr,Port,SAC}},
case {gb_get(Key, State),St} of
{[],_} -> ok;
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
when St =:= comm_lost; St =:= shutdown_comp -> ok
end,
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SAC}, State),
Parent ! {self(),{Addr,Port,SAC}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1667,14 +1685,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case {gb_get({assoc_change,AssocId}, State),St} of
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
when St =:= addr_available;
St =:= addr_confirmed -> ok;
{[],addr_confirmed} -> ok
end,
Key = {paddr_change,AssocId},
- Val = {now(),{Addr,Port,SPC}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SPC}, State),
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
{sctp,Socket,Addr,Port,
@@ -1684,12 +1701,11 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case gb_get({assoc_change,AssocId}, State) of
- [{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_] -> ok;
+ [{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok;
[] -> ok
end,
Key = {shutdown_event,AssocId},
- Val = {now(),{Addr,Port}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port}, State),
Parent ! {self(), {Addr,Port,SSE}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1707,11 +1723,12 @@ again(Socket) ->
end.
gb_push(Key, Val, GBT) ->
+ TS = timestamp(),
case gb_trees:lookup(Key, GBT) of
none ->
- gb_trees:insert(Key, [Val], GBT);
+ gb_trees:insert(Key, [{TS,Val}], GBT);
{value,V} ->
- gb_trees:update(Key, [Val|V], GBT)
+ gb_trees:update(Key, [{TS,Val}|V], GBT)
end.
gb_get(Key, GBT) ->
@@ -1719,7 +1736,7 @@ gb_get(Key, GBT) ->
none ->
[];
{value,V} ->
- V
+ [Val || {_TS,Val} <- V]
end.
match_unless_solaris(A, B) ->
@@ -1727,3 +1744,6 @@ match_unless_solaris(A, B) ->
{unix,sunos} -> B;
_ -> A = B
end.
+
+timestamp() ->
+ erlang:monotonic_time().
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index 77ec89b4f4..92a74465b7 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -38,7 +38,7 @@
t_local_basic/1, t_local_unbound/1, t_local_fdopen/1,
t_local_fdopen_listen/1, t_local_fdopen_listen_unbound/1,
t_local_fdopen_connect/1, t_local_fdopen_connect_unbound/1,
- t_local_abstract/1]).
+ t_local_abstract/1, t_accept_inet6_tclass/1]).
-export([getsockfd/0,closesockfd/1]).
@@ -50,6 +50,7 @@ all() ->
[{group, t_accept}, {group, t_connect}, {group, t_recv},
t_shutdown_write, t_shutdown_both, t_shutdown_error,
t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6,
+ t_accept_inet6_tclass,
{group, t_local}].
groups() ->
@@ -521,6 +522,24 @@ local_handshake(S, SAddr, C, CAddr) ->
SData = ok(gen_tcp:recv(C, length(SData))),
ok.
+t_accept_inet6_tclass(Config) when is_list(Config) ->
+ TClassOpt = {tclass,8#56 bsl 2}, % Expedited forwarding
+ case gen_tcp:listen(0, [inet6,TClassOpt]) of
+ {ok,L} ->
+ LPort = ok(inet:port(L)),
+ Loopback = {0,0,0,0,0,0,0,1},
+ Sa = ok(gen_tcp:connect(Loopback, LPort, [])),
+ Sb = ok(gen_tcp:accept(L)),
+ [TClassOpt] = ok(inet:getopts(Sb, [tclass])),
+ ok = gen_tcp:close(Sb),
+ ok = gen_tcp:close(Sa),
+ ok = gen_tcp:close(L),
+ ok;
+ {error,_} ->
+ {skip,"IPv6 TCLASS not supported"}
+ end.
+
+
%%% Utilities
%% Calls M:F/length(A), which should return a timeout error, and complete
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 6b64c83fc2..5e08a5ea62 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -52,7 +52,7 @@
active_once_closed/1, send_timeout/1, send_timeout_active/1,
otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1,
wrapping_oct/0, wrapping_oct/1,
- otp_9389/1]).
+ otp_9389/1, otp_13939/1]).
%% Internal exports.
-export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1,
@@ -3014,3 +3014,42 @@ ok({ok,V}) -> V.
get_hostname(Name) ->
"@"++Host = lists:dropwhile(fun(C) -> C =/= $@ end, atom_to_list(Name)),
Host.
+
+otp_13939(doc) ->
+ ["Check that writing to a remotely closed socket doesn't block forever "
+ "when exit_on_close is false."];
+otp_13939(suite) ->
+ [];
+otp_13939(Config) when is_list(Config) ->
+ {Pid, Ref} = spawn_opt(
+ fun() ->
+ {ok, Listener} = gen_tcp:listen(0, [{exit_on_close, false}]),
+ {ok, Port} = inet:port(Listener),
+
+ spawn_link(
+ fun() ->
+ {ok, Client} = gen_tcp:connect("localhost", Port,
+ [{active, false}]),
+ ok = gen_tcp:close(Client)
+ end),
+
+ {ok, Accepted} = gen_tcp:accept(Listener),
+
+ ok = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>),
+
+ %% The bug surfaces when there's a delay between the send
+ %% operations; inet:getstat is a red herring.
+ timer:sleep(100),
+
+ {error, Code} = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>),
+ ct:pal("gen_tcp:send returned ~p~n", [Code])
+ end, [link, monitor]),
+
+ receive
+ {'DOWN', Ref, process, Pid, normal} ->
+ ok
+ after 1000 ->
+ demonitor(Ref, [flush]),
+ exit(Pid, normal),
+ ct:fail("Server process blocked on send.")
+ end.
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index 1370e23195..2b59eb2bfe 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -27,7 +27,8 @@
-export([get_arguments/1, get_argument/1, boot_var/1, restart/1,
many_restarts/0, many_restarts/1,
get_plain_arguments/1,
- reboot/1, stop_status/1, stop/1, get_status/1, script_id/1]).
+ reboot/1, stop_status/1, stop/1, get_status/1, script_id/1,
+ find_system_processes/0]).
-export([boot1/1, boot2/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -355,12 +356,16 @@ wait_for(N,Node,EHPid) ->
restart(Config) when is_list(Config) ->
Args = args(),
+ Pa = " -pa " ++ filename:dirname(code:which(?MODULE)),
+
%% Currently test_server:start_node cannot be used. The restarted
%% node immediately halts due to the implementation of
%% test_server:start_node.
- {ok, Node} = loose_node:start(init_test, Args, ?DEFAULT_TIMEOUT_SEC),
+ {ok, Node} = loose_node:start(init_test, Args ++ Pa, ?DEFAULT_TIMEOUT_SEC),
%% Ok, the node is up, now the real test test begins.
erlang:monitor_node(Node, true),
+ SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []),
+ [InitPid, PurgerPid, LitCollectorPid, DirtyCodePid] = SysProcs0,
InitPid = rpc:call(Node, erlang, whereis, [init]),
PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]),
Procs = rpc:call(Node, erlang, processes, []),
@@ -375,6 +380,9 @@ restart(Config) when is_list(Config) ->
end,
ok = wait_restart(30, Node),
+ SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []),
+ [InitPid1, PurgerPid1, LitCollectorPid1, DirtyCodePid1] = SysProcs1,
+
%% Still the same init process!
InitPid1 = rpc:call(Node, erlang, whereis, [init]),
InitP = pid_to_list(InitPid),
@@ -385,8 +393,24 @@ restart(Config) when is_list(Config) ->
PurgerP = pid_to_list(PurgerPid),
PurgerP = pid_to_list(PurgerPid1),
+ %% and same literal area collector process!
+ case LitCollectorPid of
+ undefined -> undefined = LitCollectorPid1;
+ _ ->
+ LitCollectorP = pid_to_list(LitCollectorPid),
+ LitCollectorP = pid_to_list(LitCollectorPid1)
+ end,
+
+ %% and same dirty process code checker process!
+ case DirtyCodePid of
+ undefined -> undefined = DirtyCodePid1;
+ _ ->
+ DirtyCodeP = pid_to_list(DirtyCodePid),
+ DirtyCodeP = pid_to_list(DirtyCodePid1)
+ end,
+
NewProcs0 = rpc:call(Node, erlang, processes, []),
- NewProcs = NewProcs0 -- [InitPid1, PurgerPid1],
+ NewProcs = NewProcs0 -- SysProcs1,
case check_processes(NewProcs, MaxPid) of
true ->
ok;
@@ -406,6 +430,37 @@ restart(Config) when is_list(Config) ->
loose_node:stop(Node),
ok.
+-record(sys_procs, {init,
+ code_purger,
+ literal_collector,
+ dirty_proc_checker}).
+
+find_system_processes() ->
+ find_system_procs(processes(), #sys_procs{}).
+
+find_system_procs([], SysProcs) ->
+ [SysProcs#sys_procs.init,
+ SysProcs#sys_procs.code_purger,
+ SysProcs#sys_procs.literal_collector,
+ SysProcs#sys_procs.dirty_proc_checker];
+find_system_procs([P|Ps], SysProcs) ->
+ case process_info(P, initial_call) of
+ {initial_call,{otp_ring0,start,2}} ->
+ undefined = SysProcs#sys_procs.init,
+ find_system_procs(Ps, SysProcs#sys_procs{init = P});
+ {initial_call,{erts_code_purger,start,0}} ->
+ undefined = SysProcs#sys_procs.code_purger,
+ find_system_procs(Ps, SysProcs#sys_procs{code_purger = P});
+ {initial_call,{erts_literal_area_collector,start,0}} ->
+ undefined = SysProcs#sys_procs.literal_collector,
+ find_system_procs(Ps, SysProcs#sys_procs{literal_collector = P});
+ {initial_call,{erts_dirty_process_code_checker,start,0}} ->
+ undefined = SysProcs#sys_procs.dirty_proc_checker,
+ find_system_procs(Ps, SysProcs#sys_procs{dirty_proc_checker = P});
+ _ ->
+ find_system_procs(Ps, SysProcs)
+ end.
+
wait_restart(0, _Node) ->
ct:fail(not_restarted);
wait_restart(N, Node) ->
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 2a1e5016ec..d7f0463289 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -24,7 +24,9 @@
init_per_testcase/2,end_per_testcase/2]).
-export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1,
find_executable/1, unix_comment_in_command/1, deep_list_command/1,
- large_output_command/1, perf_counter_api/1]).
+ large_output_command/1, background_command/0, background_command/1,
+ message_leak/1, close_stdin/0, close_stdin/1, max_size_command/1,
+ perf_counter_api/1]).
-include_lib("common_test/include/ct.hrl").
@@ -35,7 +37,8 @@ suite() ->
all() ->
[space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command,
find_executable, unix_comment_in_command, deep_list_command,
- large_output_command, perf_counter_api].
+ large_output_command, background_command, message_leak,
+ close_stdin, max_size_command, perf_counter_api].
groups() ->
[].
@@ -52,6 +55,14 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(TC, Config)
+ when TC =:= background_command; TC =:= close_stdin ->
+ case os:type() of
+ {win32, _} ->
+ {skip,"Should not work on windows"};
+ _ ->
+ Config
+ end;
init_per_testcase(_TC,Config) ->
Config.
@@ -261,13 +272,69 @@ deep_list_command(Config) when is_list(Config) ->
%% FYI: [$e, $c, "ho"] =:= io_lib:format("ec~s", ["ho"])
ok.
-%% Test to take sure that the correct data is
+%% Test to make sure that the correct data is
%% received when doing large commands.
large_output_command(Config) when is_list(Config) ->
%% Maximum allowed on windows is 8192, so we test well below that
AAA = lists:duplicate(7000, $a),
comp(AAA,os:cmd("echo " ++ AAA)).
+%% Test that it is possible on unix to start a background task using os:cmd.
+background_command() ->
+ [{timetrap, {seconds, 5}}].
+background_command(_Config) ->
+ %% This testcase fails when the os:cmd takes
+ %% longer then the 5 second timeout
+ os:cmd("sleep 10&").
+
+%% Test that message does not leak to the calling process
+message_leak(_Config) ->
+ process_flag(trap_exit, true),
+
+ os:cmd("echo hello"),
+ [] = receive_all(),
+
+ case os:type() of
+ {unix, _} ->
+ os:cmd("for i in $(seq 1 100); do echo hello; done&"),
+ [] = receive_all();
+ _ ->
+ ok % Cannot background on non-unix
+ end,
+
+ process_flag(trap_exit, false).
+
+%% Test that os:cmd closes stdin of the program that is executed
+close_stdin() ->
+ [{timetrap, {seconds, 5}}].
+close_stdin(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ Fds = filename:join(DataDir, "my_fds"),
+
+ "-1" = os:cmd(Fds).
+
+max_size_command(_Config) ->
+
+ Res20 = os:cmd("cat /dev/zero", #{ max_size => 20 }),
+ 20 = length(Res20),
+
+ Res0 = os:cmd("cat /dev/zero", #{ max_size => 0 }),
+ 0 = length(Res0),
+
+ Res32768 = os:cmd("cat /dev/zero", #{ max_size => 32768 }),
+ 32768 = length(Res32768),
+
+ ResHello = string_trim(os:cmd("echo hello", #{ max_size => 20 })),
+ 5 = length(ResHello).
+
+string_trim(S) ->
+ lists:reverse(string_trim_left(lists:reverse(string_trim_left(S)))).
+
+string_trim_left([C | T]) when C =:= $\s; C =:= $\n; C =:= $\t; C =:= $\r ->
+ string_trim_left(T);
+string_trim_left(S) ->
+ S.
+
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/kernel/test/os_SUITE_data/Makefile.src b/lib/kernel/test/os_SUITE_data/Makefile.src
index 912d0cbcb1..f83f781411 100644
--- a/lib/kernel/test/os_SUITE_data/Makefile.src
+++ b/lib/kernel/test/os_SUITE_data/Makefile.src
@@ -3,7 +3,7 @@ LD = @LD@
CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@
CROSSLDFLAGS = @CROSSLDFLAGS@
-PROGS = my_echo@exe@
+PROGS = my_echo@exe@ my_fds@exe@
all: $(PROGS)
@@ -12,3 +12,9 @@ my_echo@exe@: my_echo@obj@
my_echo@obj@: my_echo.c
$(CC) -c -o my_echo@obj@ $(CFLAGS) my_echo.c
+
+my_fds@exe@: my_fds@obj@
+ $(LD) $(CROSSLDFLAGS) -o my_fds my_fds@obj@ @LIBS@
+
+my_fds@obj@: my_fds.c
+ $(CC) -c -o my_fds@obj@ $(CFLAGS) my_fds.c
diff --git a/lib/kernel/test/os_SUITE_data/my_fds.c b/lib/kernel/test/os_SUITE_data/my_fds.c
new file mode 100644
index 0000000000..704a4d1e1d
--- /dev/null
+++ b/lib/kernel/test/os_SUITE_data/my_fds.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int
+main(int argc, char** argv)
+{
+ char buff[1];
+ int res = read(stdin, buff, 1);
+ printf("%d", res);
+}
diff --git a/lib/kernel/test/rpc_SUITE.erl b/lib/kernel/test/rpc_SUITE.erl
index 1c72ddc87f..a89a7600a2 100644
--- a/lib/kernel/test/rpc_SUITE.erl
+++ b/lib/kernel/test/rpc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. 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.
@@ -21,7 +21,8 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([call/1, block_call/1, multicall/1, multicall_timeout/1,
+-export([off_heap/1,
+ call/1, block_call/1, multicall/1, multicall_timeout/1,
multicall_dies/1, multicall_node_dies/1,
called_dies/1, called_node_dies/1,
called_throws/1, call_benchmark/1, async_call/1]).
@@ -35,7 +36,7 @@ suite() ->
{timetrap,{minutes,2}}].
all() ->
- [call, block_call, multicall, multicall_timeout,
+ [off_heap, call, block_call, multicall, multicall_timeout,
multicall_dies, multicall_node_dies, called_dies,
called_node_dies, called_throws, call_benchmark,
async_call].
@@ -55,6 +56,13 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+off_heap(_Config) ->
+ %% The rex server process may receive a huge amount of
+ %% messages. Make sure that they are stored off heap to
+ %% avoid exessive GCs.
+ MQD = message_queue_data,
+ {MQD,off_heap} = process_info(whereis(rex), MQD),
+ ok.
%% Test different rpc calls.
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index e7d422d03c..ff8fd8ab63 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 5.0
+KERNEL_VSN = 5.2.0.1
diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src
index 46da79cfe3..9c73ab8072 100644
--- a/lib/megaco/src/app/megaco.appup.src
+++ b/lib/megaco/src/app/megaco.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
+%% 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.
diff --git a/lib/megaco/test/megaco_app_test.erl b/lib/megaco/test/megaco_app_test.erl
index 346a123c66..981d93f5dd 100644
--- a/lib/megaco/test/megaco_app_test.erl
+++ b/lib/megaco/test/megaco_app_test.erl
@@ -17,8 +17,6 @@
%%
%% %CopyrightEnd%
%%
-
-%%
%%----------------------------------------------------------------------
%% Purpose: Verify the application specifics of the Megaco application
%%----------------------------------------------------------------------
@@ -26,296 +24,26 @@
-compile(export_all).
--include("megaco_test_lib.hrl").
-
-
-t() -> megaco_test_lib:t(?MODULE).
-t(Case) -> megaco_test_lib:t({?MODULE, Case}).
-
-
-%% Test server callbacks
-init_per_testcase(undef_funcs = Case, Config) ->
- NewConfig = [{tc_timeout, ?MINUTES(10)} | Config],
- megaco_test_lib:init_per_testcase(Case, NewConfig);
-init_per_testcase(Case, Config) ->
- megaco_test_lib:init_per_testcase(Case, Config).
-
-end_per_testcase(Case, Config) ->
- megaco_test_lib:end_per_testcase(Case, Config).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-include_lib("common_test/include/ct.hrl").
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
all() ->
[
- fields,
- modules,
- exportall,
- app_depend,
- undef_funcs
+ app,
+ appup
].
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-init_per_suite(suite) -> [];
-init_per_suite(doc) -> [];
-init_per_suite(Config) when is_list(Config) ->
- case is_app(megaco) of
- {ok, AppFile} ->
- io:format("AppFile: ~n~p~n", [AppFile]),
- case megaco_flex_scanner:is_enabled() of
- true ->
- case megaco_flex_scanner:is_reentrant_enabled() of
- true ->
- io:format("~nMegaco reentrant flex scanner enabled~n~n", []);
- false ->
- io:format("~nMegaco non-reentrant flex scanner enabled~n~n", [])
- end;
- false ->
- io:format("~nMegaco flex scanner disabled~n~n", [])
- end,
- megaco:print_version_info(),
- [{app_file, AppFile}|Config];
- {error, Reason} ->
- fail(Reason)
- end.
-
-is_app(App) ->
- LibDir = code:lib_dir(App),
- File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]),
- case file:consult(File) of
- {ok, [{application, App, AppFile}]} ->
- {ok, AppFile};
- {error, {LineNo, Mod, Code}} ->
- IoList = lists:concat([File, ":", LineNo, ": ",
- Mod:format_error(Code)]),
- {error, list_to_atom(lists:flatten(IoList))};
- Error ->
- {error, {invalid_format, Error}}
- end.
-
-
-end_per_suite(suite) -> [];
-end_per_suite(doc) -> [];
-end_per_suite(Config) when is_list(Config) ->
- Config.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-fields(suite) ->
- [];
-fields(doc) ->
- [];
-fields(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Fields = [vsn, description, modules, registered, applications],
- case check_fields(Fields, AppFile, []) of
- [] ->
- ok;
- Missing ->
- fail({missing_fields, Missing})
- end.
-
-check_fields([], _AppFile, Missing) ->
- Missing;
-check_fields([Field|Fields], AppFile, Missing) ->
- check_fields(Fields, AppFile, check_field(Field, AppFile, Missing)).
-
-check_field(Name, AppFile, Missing) ->
- io:format("checking field: ~p~n", [Name]),
- case lists:keymember(Name, 1, AppFile) of
- true ->
- Missing;
- false ->
- [Name|Missing]
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-modules(suite) ->
- [];
-modules(doc) ->
- [];
-modules(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Mods = key1search(modules, AppFile),
- EbinList = get_ebin_mods(megaco),
- case missing_modules(Mods, EbinList, []) of
- [] ->
- ok;
- Missing ->
- throw({error, {missing_modules, Missing}})
- end,
- case extra_modules(Mods, EbinList, []) of
- [] ->
- ok;
- Extra ->
- throw({error, {extra_modules, Extra}})
- end,
- {ok, Mods}.
-
-get_ebin_mods(App) ->
- LibDir = code:lib_dir(App),
- EbinDir = filename:join([LibDir,"ebin"]),
- {ok, Files0} = file:list_dir(EbinDir),
- Files1 = [lists:reverse(File) || File <- Files0],
- [list_to_atom(lists:reverse(Name)) || [$m,$a,$e,$b,$.|Name] <- Files1].
-
-
-missing_modules([], _Ebins, Missing) ->
- Missing;
-missing_modules([Mod|Mods], Ebins, Missing) ->
- case lists:member(Mod, Ebins) of
- true ->
- missing_modules(Mods, Ebins, Missing);
- false ->
- io:format("missing module: ~p~n", [Mod]),
- missing_modules(Mods, Ebins, [Mod|Missing])
- end.
-
-
-extra_modules(_Mods, [], Extra) ->
- Extra;
-extra_modules(Mods, [Mod|Ebins], Extra) ->
- case lists:member(Mod, Mods) of
- true ->
- extra_modules(Mods, Ebins, Extra);
- false ->
- io:format("supefluous module: ~p~n", [Mod]),
- extra_modules(Mods, Ebins, [Mod|Extra])
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-exportall(suite) ->
- [];
-exportall(doc) ->
- [];
-exportall(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Mods = key1search(modules, AppFile),
- check_export_all(Mods).
-
-
-check_export_all([]) ->
- ok;
-check_export_all([Mod|Mods]) ->
- case (catch apply(Mod, module_info, [compile])) of
- {'EXIT', {undef, _}} ->
- check_export_all(Mods);
- O ->
- case lists:keysearch(options, 1, O) of
- false ->
- check_export_all(Mods);
- {value, {options, List}} ->
- case lists:member(export_all, List) of
- true ->
- throw({error, {export_all, Mod}});
- false ->
- check_export_all(Mods)
- end
- end
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-app_depend(suite) ->
- [];
-app_depend(doc) ->
- [];
-app_depend(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Apps = key1search(applications, AppFile),
- check_apps(Apps).
-
-
-check_apps([]) ->
- ok;
-check_apps([App|Apps]) ->
- case is_app(App) of
- {ok, _} ->
- check_apps(Apps);
- Error ->
- throw({error, {missing_app, {App, Error}}})
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-undef_funcs(suite) ->
- [];
-undef_funcs(doc) ->
- [];
-undef_funcs(Config) when is_list(Config) ->
- App = megaco,
- AppFile = key1search(app_file, Config),
- Mods = key1search(modules, AppFile),
- Root = code:root_dir(),
- LibDir = code:lib_dir(App),
- EbinDir = filename:join([LibDir,"ebin"]),
- XRefTestName = undef_funcs_make_name(App, xref_test_name),
- {ok, XRef} = xref:start(XRefTestName),
- ok = xref:set_default(XRef,
- [{verbose,false},{warnings,false}]),
- XRefName = undef_funcs_make_name(App, xref_name),
- {ok, XRefName} = xref:add_release(XRef, Root, {name,XRefName}),
- {ok, App} = xref:replace_application(XRef, App, EbinDir),
- {ok, Undefs} = xref:analyze(XRef, undefined_function_calls),
- xref:stop(XRef),
- analyze_undefined_function_calls(Undefs, Mods, []).
-
-analyze_undefined_function_calls([], _, []) ->
- ok;
-analyze_undefined_function_calls([], _, AppUndefs) ->
- exit({suite_failed, {undefined_function_calls, AppUndefs}});
-analyze_undefined_function_calls([{{Mod, _F, _A}, _C} = AppUndef|Undefs],
- AppModules, AppUndefs) ->
- %% Check that this module is our's
- case lists:member(Mod,AppModules) of
- true ->
- {Calling,Called} = AppUndef,
- {Mod1,Func1,Ar1} = Calling,
- {Mod2,Func2,Ar2} = Called,
- io:format("undefined function call: "
- "~n ~w:~w/~w calls ~w:~w/~w~n",
- [Mod1,Func1,Ar1,Mod2,Func2,Ar2]),
- analyze_undefined_function_calls(Undefs, AppModules,
- [AppUndef|AppUndefs]);
- false ->
- io:format("dropping ~p~n", [Mod]),
- analyze_undefined_function_calls(Undefs, AppModules, AppUndefs)
- end.
-
-%% This function is used simply to avoid cut-and-paste errors later...
-undef_funcs_make_name(App, PostFix) ->
- list_to_atom(atom_to_list(App) ++ "_" ++ atom_to_list(PostFix)).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-fail(Reason) ->
- exit({suite_failed, Reason}).
-
-key1search(Key, L) ->
- case lists:keysearch(Key, 1, L) of
- undefined ->
- fail({not_found, Key, L});
- {value, {Key, Value}} ->
- Value
- end.
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+app() ->
+ [{doc, "Test that the megaco app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = test_server:app_test(megaco).
+%%--------------------------------------------------------------------
+appup() ->
+ [{doc, "Test that the megaco appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = test_server:appup_test(megaco).
diff --git a/lib/megaco/test/megaco_appup_test.erl b/lib/megaco/test/megaco_appup_test.erl
index 325e7a6096..8dc3ad51a0 100644
--- a/lib/megaco/test/megaco_appup_test.erl
+++ b/lib/megaco/test/megaco_appup_test.erl
@@ -17,8 +17,6 @@
%%
%% %CopyrightEnd%
%%
-
-%%
%%----------------------------------------------------------------------
%% Purpose: Verify the application specifics of the Megaco application
%%----------------------------------------------------------------------
@@ -27,26 +25,18 @@
-compile(export_all).
-compile({no_auto_import,[error/1]}).
+-include_lib("common_test/include/ct.hrl").
-include("megaco_test_lib.hrl").
--define(APPLICATION, megaco).
-
-t() -> megaco_test_lib:t(?MODULE).
-t(Case) -> megaco_test_lib:t({?MODULE, Case}).
-
-
-%% Test server callbacks
-init_per_testcase(Case, Config) ->
- megaco_test_lib:init_per_testcase(Case, Config).
-
-end_per_testcase(Case, Config) ->
- megaco_test_lib:end_per_testcase(Case, Config).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all() ->
- [appup].
+ Cases =
+ [
+ appup_file
+ ],
+ Cases.
groups() ->
[].
@@ -64,16 +54,9 @@ end_per_group(_GroupName, Config) ->
init_per_suite(suite) -> [];
init_per_suite(doc) -> [];
init_per_suite(Config) when is_list(Config) ->
- AppFile = file_name(?APPLICATION, ".app"),
- AppupFile = file_name(?APPLICATION, ".appup"),
- [{app_file, AppFile}, {appup_file, AppupFile}|Config].
+ Config.
-file_name(App, Ext) ->
- LibDir = code:lib_dir(App),
- filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]).
-
-
end_per_suite(suite) -> [];
end_per_suite(doc) -> [];
end_per_suite(Config) when is_list(Config) ->
@@ -82,468 +65,17 @@ end_per_suite(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-appup(suite) ->
- [];
-appup(doc) ->
- "perform a simple check of the appup file";
-appup(Config) when is_list(Config) ->
- AppupFile = key1search(appup_file, Config),
- AppFile = key1search(app_file, Config),
- Modules = modules(AppFile),
- check_appup(AppupFile, Modules).
-
-modules(File) ->
- case file:consult(File) of
- {ok, [{application,megaco,Info}]} ->
- case lists:keysearch(modules,1,Info) of
- {value, {modules, Modules}} ->
- Modules;
- false ->
- fail({bad_appinfo, Info})
- end;
- Error ->
- fail({bad_appfile, Error})
- end.
-
-
-check_appup(AppupFile, Modules) ->
- case file:consult(AppupFile) of
- {ok, [{V, UpFrom, DownTo}]} ->
- check_appup(V, UpFrom, DownTo, Modules);
- Else ->
- fail({bad_appupfile, Else})
- end.
-
-
-check_appup(V, UpFrom, DownTo, Modules) ->
- check_version(V),
- check_depends(up, UpFrom, Modules),
- check_depends(down, DownTo, Modules),
- check_module_subset(UpFrom),
- check_module_subset(DownTo),
- ok.
-
-
-check_depends(_, [], _) ->
- ok;
-check_depends(UpDown, [Dep|Deps], Modules) ->
- check_depend(UpDown, Dep, Modules),
- check_depends(UpDown, Deps, Modules).
-
-
-check_depend(up = UpDown, {add_application, ?APPLICATION} = Instr, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instruction: ~p"
- "~n Modules: ~p", [UpDown, Instr, Modules]),
- ok;
-check_depend(down = UpDown, {remove_application, ?APPLICATION} = Instr,
- Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instruction: ~p"
- "~n Modules: ~p", [UpDown, Instr, Modules]),
- ok;
-check_depend(UpDown, {V, Instructions}, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n V: ~p"
- "~n Modules: ~p", [UpDown, V, Modules]),
- check_version(V),
- case check_instructions(UpDown,
- Instructions, Instructions, [], [], Modules) of
- {_Good, []} ->
- ok;
- {_, Bad} ->
- fail({bad_instructions, Bad, UpDown})
- end.
-
-
-check_instructions(_, [], _, Good, Bad, _) ->
- {lists:reverse(Good), lists:reverse(Bad)};
-check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) ->
- d("check_instructions(~w) -> entry with"
- "~n Instr: ~p", [UpDown,Instr]),
- case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of
- ok ->
- check_instructions(UpDown, Instrs, AllInstr,
- [Instr|Good], Bad, Modules);
- {error, Reason} ->
- d("check_instructions(~w) -> bad instruction: "
- "~n Reason: ~p", [UpDown,Reason]),
- check_instructions(UpDown, Instrs, AllInstr, Good,
- [{Instr, Reason}|Bad], Modules)
- end.
-
-%% A new module is added
-check_instruction(up, {add_module, Module}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when up-add_module instruction with"
- "~n Module: ~p", [Module]),
- check_module(Module, Modules);
-
-%% An old module is re-added
-check_instruction(down, {add_module, Module}, _, Modules)
- when is_atom(Module) ->
- d("check_instruction -> entry when down-add_module instruction with"
- "~n Module: ~p", [Module]),
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- ok;
- ok ->
- error({existing_readded_module, Module})
- end;
-
-%% Removing a module on upgrade:
-%% - the module has been removed from the app-file.
-%% - check that no module depends on this (removed) module
-check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules)
- when is_atom(Module) andalso is_atom(Pre) andalso is_atom(Post) ->
- d("check_instruction -> entry when up-remove instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p", [Module, Pre, Post]),
- case (catch check_module(Module, Modules)) of
- {error, {unknown_module, Module, Modules}} ->
- check_purge(Pre),
- check_purge(Post);
- ok ->
- error({existing_removed_module, Module})
- end;
-
-%% Removing a module on downgrade: the module exist
-%% in the app-file.
-check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules)
- when is_atom(Module) andalso is_atom(Pre) andalso is_atom(Post) ->
- d("check_instruction -> entry when down-remove instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p", [Module, Pre, Post]),
- case (catch check_module(Module, Modules)) of
- ok ->
- check_purge(Pre),
- check_purge(Post),
- check_no_remove_depends(Module, AllInstr);
- {error, {unknown_module, Module, Modules}} ->
- error({nonexisting_removed_module, Module})
- end;
-
-check_instruction(_, {load_module, Module, Pre, Post, Depend},
- AllInstr, Modules)
- when is_atom(Module) andalso is_atom(Pre) andalso is_atom(Post) andalso is_list(Depend) ->
- d("check_instruction -> entry when load_module instruction with"
- "~n Module: ~p"
- "~n Pre: ~p"
- "~n Post: ~p"
- "~n Depend: ~p", [Module, Pre, Post, Depend]),
- check_module(Module, Modules),
- check_module_depend(Module, Depend, Modules),
- check_module_depend(Module, Depend, updated_modules(AllInstr, [])),
- check_purge(Pre),
- check_purge(Post);
-
-check_instruction(_, {update, Module, Change, Pre, Post, Depend},
- AllInstr, Modules)
- when is_atom(Module) andalso is_atom(Pre) andalso is_atom(Post) andalso is_list(Depend) ->
- d("check_instruction -> entry when update instruction with"
- "~n Module: ~p"
- "~n Change: ~p"
- "~n Pre: ~p"
- "~n Post: ~p"
- "~n Depend: ~p", [Module, Change, Pre, Post, Depend]),
- check_module(Module, Modules),
- check_module_depend(Module, Depend, Modules),
- check_module_depend(Module, Depend, updated_modules(AllInstr, [])),
- check_change(Change),
- check_purge(Pre),
- check_purge(Post);
-
-check_instruction(_, {update, Module, supervisor}, _, Modules)
- when is_atom(Module) ->
- check_module(Module, Modules);
-
-check_instruction(_, {apply, {Module, Function, Args}}, _, Modules)
- when is_atom(Module) andalso is_atom(Function) andalso is_list(Args) ->
- d("check_instruction -> entry when down-apply instruction with"
- "~n Module: ~p"
- "~n Function: ~p"
- "~n Args: ~p", [Module, Function, Args]),
- check_module(Module, Modules),
- check_apply(Module, Function, Args);
-
-check_instruction(_, {restart_application, ?APPLICATION}, _AllInstr, _Modules) ->
- ok;
-
-check_instruction(_, Instr, _AllInstr, _Modules) ->
- d("check_instruction -> entry when unknown instruction with"
- "~n Instr: ~p", [Instr]),
- error({error, {unknown_instruction, Instr}}).
-
-
-%% If Module X depends on Module Y, then module Y must have an update
-%% instruction of some sort (otherwise the depend is faulty).
-updated_modules([], Modules) ->
- d("update_modules -> entry when done with"
- "~n Modules: ~p", [Modules]),
- Modules;
-updated_modules([Instr|Instrs], Modules) ->
- d("update_modules -> entry with"
- "~n Instr: ~p"
- "~n Modules: ~p", [Instr,Modules]),
- Module = instruction_module(Instr),
- d("update_modules -> Module: ~p", [Module]),
- updated_modules(Instrs, [Module|Modules]).
-
-instruction_module({add_module, Module}) ->
- Module;
-instruction_module({remove, {Module, _, _}}) ->
- Module;
-instruction_module({load_module, Module, _, _, _}) ->
- Module;
-instruction_module({update, Module, _, _, _, _}) ->
- Module;
-instruction_module({apply, {Module, _, _}}) ->
- Module;
-instruction_module(Instr) ->
- d("instruction_module -> entry when unknown instruction with"
- "~n Instr: ~p", [Instr]),
- error({error, {unknown_instruction, Instr}}).
-
-
-%% Check that the modules handled in an instruction set for version X
-%% is a subset of the instruction set for version X-1.
-check_module_subset(Instructions) ->
- %% io:format("check_module_subset -> "
- %% "~n Instructions: ~p"
- %% "~n", [Instructions]),
- do_check_module_subset(modules_of(Instructions)).
-
-do_check_module_subset([]) ->
- ok;
-do_check_module_subset([_]) ->
- ok;
-do_check_module_subset([{_V1, Mods1}|T]) ->
- %% io:format("do_check_module_subset -> "
- %% "~n V1: ~p"
- %% "~n Mods1: ~p"
- %% "~n T: ~p"
- %% "~n", [_V1, Mods1, T]),
- {V2, Mods2} = hd(T),
- %% Check that the modules in V1 is a subset of V2
- case do_check_module_subset2(Mods1, Mods2) of
- ok ->
- do_check_module_subset(T);
- {error, Modules} ->
- fail({subset_missing_instructions, V2, Modules})
- end.
-
-do_check_module_subset2(Mods1, Mods2) ->
- do_check_module_subset2(Mods1, Mods2, []).
-
-do_check_module_subset2([], _, []) ->
- ok;
-do_check_module_subset2([], _, Acc) ->
- {error, lists:reverse(Acc)};
-do_check_module_subset2([Mod|Mods], Mods2, Acc) ->
- case lists:member(Mod, Mods2) of
- true ->
- do_check_module_subset2(Mods, Mods2, Acc);
- false ->
- do_check_module_subset2(Mods, Mods2, [Mod|Acc])
- end.
-
-
-modules_of(Instructions) ->
- modules_of(Instructions, []).
-
-modules_of([], Acc) ->
- lists:reverse(Acc);
-modules_of([{V,Instructions}|T], Acc) ->
- %% io:format("modules_of -> "
- %% "~n V: ~p"
- %% "~n Instructions: ~p"
- %% "~n", [V, Instructions]),
- case modules_of2(Instructions, []) of
- Mods when is_list(Mods) ->
- %% io:format("modules_of -> "
- %% "~n Mods: ~p"
- %% "~n", [Mods]),
- modules_of(T, [{V, Mods}|Acc]);
- skip ->
- %% io:format("modules_of -> skip"
- %% "~n", []),
- modules_of(T, Acc)
- end.
-
-modules_of2([], Acc) ->
- lists:reverse(Acc);
-modules_of2([Instr|Instructions], Acc) ->
- case module_of(Instr) of
- {value, Mod} ->
- modules_of2(Instructions, [Mod|Acc]);
- skip ->
- skip;
- false ->
- modules_of2(Instructions, Acc)
- end.
-
-module_of({add_module, Module}) ->
- {value, Module};
-module_of({remove, {Module, _Pre, _Post}}) ->
- {value, Module};
-module_of({load_module, Module, _Pre, _Post, _Depend}) ->
- {value, Module};
-module_of({update, Module, _Change, _Pre, _Post, _Depend}) ->
- {value, Module};
-module_of({restart_application, _App}) ->
- skip;
-module_of(_) ->
- false.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% The version is a string consting of numbers separated by dots: "."
-%% Example: "3.3.3"
-%%
-check_version(V) when is_list(V) ->
- case do_check_version(string:tokens(V, [$.])) of
- ok ->
- ok;
- {error, BadVersionPart} ->
- throw({error, {bad_version, V, BadVersionPart}})
- end;
-check_version(V) ->
- error({bad_version, V}).
-
-do_check_version([]) ->
- ok;
-do_check_version([H|T]) ->
- case (catch list_to_integer(H)) of
- I when is_integer(I) ->
- do_check_version(T);
- _ ->
- {error, H}
- end.
-
-check_module(M, Modules) when is_atom(M) ->
- case lists:member(M,Modules) of
- true ->
- ok;
- false ->
- error({unknown_module, M, Modules})
- end;
-check_module(M, _) ->
- error({bad_module, M}).
-
-
-check_module_depend(M, [], _) when is_atom(M) ->
- d("check_module_depend -> entry with"
- "~n M: ~p", [M]),
- ok;
-check_module_depend(M, Deps, Modules) when is_atom(M) andalso is_list(Deps) ->
- d("check_module_depend -> entry with"
- "~n M: ~p"
- "~n Deps: ~p"
- "~n Modules: ~p", [M, Deps, Modules]),
- case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of
- [] ->
- ok;
- Unknown ->
- error({unknown_depend_modules, Unknown})
- end;
-check_module_depend(_M, D, _Modules) ->
- d("check_module_depend -> entry when bad depend with"
- "~n D: ~p", [D]),
- error({bad_depend, D}).
-
-
-check_no_remove_depends(_Module, []) ->
- ok;
-check_no_remove_depends(Module, [Instr|Instrs]) ->
- check_no_remove_depend(Module, Instr),
- check_no_remove_depends(Module, Instrs).
-
-check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) ->
- case lists:member(Module, Depend) of
- true ->
- error({removed_module_in_depend, load_module, Mod, Module});
- false ->
- ok
- end;
-check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) ->
- case lists:member(Module, Depend) of
- true ->
- error({removed_module_in_depend, update, Mod, Module});
- false ->
- ok
- end;
-check_no_remove_depend(_, _) ->
- ok.
-
-
-check_change(soft) ->
- ok;
-check_change({advanced, _Something}) ->
- ok;
-check_change(Change) ->
- error({bad_change, Change}).
-
-
-check_purge(soft_purge) ->
- ok;
-check_purge(brutal_purge) ->
- ok;
-check_purge(Purge) ->
- error({bad_purge, Purge}).
-
-
-check_apply(Module, Function, Args) ->
- case (catch Module:module_info()) of
- Info when is_list(Info) ->
- check_exported(Function, Args, Info);
- {'EXIT', {undef, _}} ->
- error({not_existing_module, Module})
- end.
-
-check_exported(Function, Args, Info) ->
- case lists:keysearch(exports, 1, Info) of
- {value, {exports, FuncList}} ->
- Arity = length(Args),
- Arities = [A || {F, A} <- FuncList, F == Function],
- case lists:member(Arity, Arities) of
- true ->
- ok;
- false ->
- error({not_exported_function, Function, Arity})
- end;
- _ ->
- error({bad_export, Info})
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-error(Reason) ->
- throw({error, Reason}).
-
-fail(Reason) ->
- exit({suite_failed, Reason}).
+%% Test server callbacks
+init_per_testcase(_Case, Config) when is_list(Config) ->
+ Config.
-key1search(Key, L) ->
- case lists:keysearch(Key, 1, L) of
- undefined ->
- fail({not_found, Key, L});
- {value, {Key, Value}} ->
- Value
- end.
+end_per_testcase(_Case, Config) when is_list(Config) ->
+ Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-d(F, A) ->
- d(false, F, A).
+%% Perform a simple check of the appup file
+appup_file(Config) when is_list(Config) ->
+ ok = ?t:appup_test(megaco).
-d(true, F, A) ->
- io:format(F ++ "~n", A);
-d(_, _, _) ->
- ok.
-
-
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index b95cd66a81..0d40f863b2 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2015. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/lib/mnesia/doc/src/Mnesia_chap1.xml b/lib/mnesia/doc/src/Mnesia_chap1.xml
index 9dfeb5efe4..035e934ed2 100644
--- a/lib/mnesia/doc/src/Mnesia_chap1.xml
+++ b/lib/mnesia/doc/src/Mnesia_chap1.xml
@@ -32,38 +32,38 @@
<rev>C</rev>
<file>Mnesia_chap1.xml</file>
</header>
- <p>The <c>Mnesia</c> application provides a heavy duty real-time
+ <p>The Mnesia application provides a heavy duty real-time
distributed database.</p>
<section>
<title>Scope</title>
<p>This User's Guide describes how to
- build <c>Mnesia</c> database applications, and how to integrate
- and use the <c>Mnesia</c> database management system with
+ build Mnesia database applications, and how to integrate
+ and use the Mnesia database management system with
OTP. Programming constructs are described, and numerous
programming examples are included to illustrate the use of
- <c>Mnesia</c>.</p>
+ Mnesia.</p>
<p>This User's Guide is organized as follows:</p>
<list type="bulleted">
<item><seealso marker="Mnesia_overview">Mnesia</seealso>
provides an introduction to
- <c>Mnesia</c>.
+ Mnesia.
</item>
<item><seealso marker="Mnesia_chap2">Getting Started</seealso>
- introduces <c>Mnesia</c> with an example database. Examples
+ introduces Mnesia with an example database. Examples
are included how to start an Erlang session, specify a
- <c>Mnesia</c> database directory, initialize a database
- schema, start <c>Mnesia</c>, and create tables. Initial
+ Mnesia database directory, initialize a database
+ schema, start Mnesia, and create tables. Initial
prototyping of record definitions is also discussed.
</item>
<item><seealso marker="Mnesia_chap3">Build a Mnesia
Database</seealso> more formally describes the steps
- introduced in the previous section, namely the <c>Mnesia</c>
- functions that define a database schema, start <c>Mnesia</c>,
+ introduced in the previous section, namely the Mnesia
+ functions that define a database schema, start Mnesia,
and create the required tables.
</item>
<item><seealso marker="Mnesia_chap4">Transactions and Other Access Contexts</seealso>
- describes the transactions properties that make <c>Mnesia</c> into
+ describes the transactions properties that make Mnesia into
a fault tolerant, real-time distributed database management
system. This section also describes the concept of locking
to ensure consistency in tables, and "dirty
@@ -76,16 +76,16 @@
features include indexing, checkpoints, distribution and fault
tolerance, disc-less nodes, replication manipulation, local
content tables, concurrency, and object-based programming in
- <c>Mnesia</c>.
+ Mnesia.
</item>
<item><seealso marker="Mnesia_chap7">Mnesia System
Information</seealso> describes the files contained in the
- <c>Mnesia</c> database directory, database configuration data,
+ Mnesia database directory, database configuration data,
core and table dumps, as well as the important subject of
backup, fall-back, and disaster recovery principles.
</item>
<item><seealso marker="Mnesia_chap8">Combine Mnesia with
- SNMP</seealso> is a short section that outlines <c>Mnesia</c>
+ SNMP</seealso> is a short section that outlines Mnesia
integrated with SNMP.
</item>
<item><seealso marker="Mnesia_App_A">Appendix A: Backup
diff --git a/lib/mnesia/doc/src/Mnesia_chap8.xml b/lib/mnesia/doc/src/Mnesia_chap8.xml
index f1a469e315..4a2eed84d7 100644
--- a/lib/mnesia/doc/src/Mnesia_chap8.xml
+++ b/lib/mnesia/doc/src/Mnesia_chap8.xml
@@ -51,11 +51,11 @@
</item>
</list>
<p>All these approaches have different advantages and
- disadvantages. <c>Mnesia</c> applications can easily be opened to
+ disadvantages. Mnesia applications can easily be opened to
the SNMP protocol. A direct 1-to-1 mapping can be established
- between <c>Mnesia</c> tables and SNMP tables. This means
- that a <c>Mnesia</c> table can be configured to be <em>both</em>
- a <c>Mnesia</c> table and an SNMP table. A number of functions to
+ between Mnesia tables and SNMP tables. This means
+ that a Mnesia table can be configured to be <em>both</em>
+ a Mnesia table and an SNMP table. A number of functions to
control this behavior are described in the Reference Manual.</p>
</section>
</chapter>
diff --git a/lib/mnesia/doc/src/Mnesia_overview.xml b/lib/mnesia/doc/src/Mnesia_overview.xml
index d2d597b85d..63f2309284 100644
--- a/lib/mnesia/doc/src/Mnesia_overview.xml
+++ b/lib/mnesia/doc/src/Mnesia_overview.xml
@@ -39,14 +39,14 @@
high level of fault tolerance that is required in many nonstop
systems, combined with requirements on the DBMS to run in the same
address space as the application, have led us to implement a new
- DBMS, called <c>Mnesia</c>.</p>
- <p><c>Mnesia</c> is implemented in, and tightly connected to Erlang.
+ DBMS, called Mnesia.</p>
+ <p>Mnesia is implemented in, and tightly connected to Erlang.
It provides the functionality that is necessary for the
implementation of fault tolerant telecommunications systems.</p>
- <p><c>Mnesia</c> is a multiuser distributed DBMS specially made for
+ <p>Mnesia is a multiuser distributed DBMS specially made for
industrial telecommunications applications written in Erlang,
which is also the intended target language.
- <c>Mnesia</c> tries to address all the data
+ Mnesia tries to address all the data
management issues required for typical telecommunications systems.
It has a number of features that are not normally found in traditional
databases.</p>
@@ -54,7 +54,7 @@
from the features provided by traditional DBMSs. The applications now
implemented in Erlang need a mixture of a broad range
of features, which generally are not satisfied by traditional DBMSs.
- <c>Mnesia</c> is designed with requirements like the following in
+ Mnesia is designed with requirements like the following in
mind:</p>
<list type="ordered">
<item>Fast real-time key/value lookup
@@ -71,9 +71,9 @@
<item>Complex objects
</item>
</list>
- <p><c>Mnesia</c> is designed with the typical data management problems
- of telecommunications applications in mind. This sets <c>Mnesia</c>
- apart from most other DBMS. Hence <c>Mnesia</c>
+ <p>Mnesia is designed with the typical data management problems
+ of telecommunications applications in mind. This sets Mnesia
+ apart from most other DBMS. Hence Mnesia
combines many concepts found in traditional databases such as
transactions and queries with concepts found in data management
systems for telecommunications applications, for example:</p>
@@ -86,7 +86,7 @@
suspending it.
</item>
</list>
- <p><c>Mnesia</c> is also interesting because of its tight coupling to
+ <p>Mnesia is also interesting because of its tight coupling to
Erlang, thus almost turning Erlang into a database programming
language. This has many benefits, the foremost is that
the impedance mismatch between the data format used by the DBMS
@@ -97,7 +97,7 @@
<title>Mnesia Database Management System (DBMS)</title>
<section>
<title>Features</title>
- <p><c>Mnesia</c> contains the following features that combine to
+ <p>Mnesia contains the following features that combine to
produce a fault-tolerant, distributed DBMS written in Erlang:
</p>
<list type="bulleted">
@@ -118,7 +118,7 @@
functions can be called within one transaction.
</item>
<item>Several transactions can run concurrently, and their execution
- is fully synchronized by the DBMS. <c>Mnesia</c> ensures that no
+ is fully synchronized by the DBMS. Mnesia ensures that no
two processes manipulate data simultaneously.
</item>
<item>Transactions can be assigned the property of being executed on
@@ -132,29 +132,29 @@
<section>
<title>Add-On Application</title>
- <p>Query List Comprehension (QLC) can be used with <c>Mnesia</c>
+ <p>Query List Comprehension (QLC) can be used with Mnesia
to produce specialized functions that enhance the operational
- ability of <c>Mnesia</c>. QLC has its own documentation as part
+ ability of Mnesia. QLC has its own documentation as part
of the OTP documentation set. The main features of QLC
- when used with <c>Mnesia</c> are as follows:</p>
+ when used with Mnesia are as follows:</p>
<list type="bulleted">
- <item>QLC can optimize the query compiler for the <c>Mnesia</c>
+ <item>QLC can optimize the query compiler for the Mnesia
DBMS, essentially making the DBMS more efficient.
</item>
<item>QLC can be used as a database programming
- language for <c>Mnesia</c>. It includes a notation called "list
+ language for Mnesia. It includes a notation called "list
comprehensions" and can be used to make complex database
queries over a set of tables.
</item>
</list>
<p>For information about QLC, see the
<seealso marker="stdlib:qlc">qlc</seealso> manual page
- in <c>STDLIB</c>.</p>
+ in STDLIB.</p>
</section>
<section>
<title>When to Use Mnesia</title>
- <p>Use <c>Mnesia</c> with the following types of applications:</p>
+ <p>Use Mnesia with the following types of applications:</p>
<list type="bulleted">
<item>Applications that need to replicate data.
</item>
@@ -166,7 +166,7 @@
<item>Applications that use soft real-time characteristics.
</item>
</list>
- <p><c>Mnesia</c> is not as appropriate with the
+ <p>Mnesia is not as appropriate with the
following types of applications:</p>
<list type="bulleted">
<item>Programs that process plain text or binary data files.
@@ -176,14 +176,14 @@
library module <c>dets</c>, which is a disc-based version
of the module <c>ets</c>. For information about <c>dets</c>,
see the <seealso marker="stdlib:dets">dets</seealso>
- manual page in <c>STDLIB</c>.
+ manual page in STDLIB.
</item>
<item>Applications that need disc logging facilities.
Those applications can
use the module <c>disk_log</c> by preference. For
information about <c>disk_log</c>, see the
<seealso marker="kernel:disk_log">disk_log</seealso>
- manual page in <c>Kernel</c>.
+ manual page in Kernel.
</item>
<item>Hard real-time systems.
</item>
diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml
index b35214cde9..621b6047ee 100644
--- a/lib/mnesia/doc/src/mnesia.xml
+++ b/lib/mnesia/doc/src/mnesia.xml
@@ -37,7 +37,7 @@
<description>
<p>The following are some of the most important and attractive
- capabilities provided by <c>Mnesia</c>:</p>
+ capabilities provided by Mnesia:</p>
<list type="bulleted">
<item>A relational/object hybrid data model that is suitable
for telecommunications applications.
@@ -62,15 +62,15 @@
reconfigured at runtime without stopping the system.
</item>
</list>
- <p>This Reference Manual describes the <c>Mnesia</c> API. This
- includes functions that define and manipulate <c>Mnesia</c>
+ <p>This Reference Manual describes the Mnesia API. This
+ includes functions that define and manipulate Mnesia
tables.</p>
<p>All functions in this Reference Manual can be used in any
combination with queries using the list comprehension notation.
For information about the query notation, see the
<seealso marker="stdlib:qlc">qlc</seealso>
- manual page in <c>STDLIB</c>.</p>
- <p>Data in <c>Mnesia</c> is organized as a set of tables. Each table
+ manual page in STDLIB.</p>
+ <p>Data in Mnesia is organized as a set of tables. Each table
has a name that must be an atom. Each table is made up of
Erlang records. The user is responsible for the record
definitions. Each table also has a set of properties. The
@@ -121,14 +121,14 @@
<item>
<p><c>index</c>. This is a list of attribute names, or
integers, which specify the tuple positions on which
- <c>Mnesia</c> is to build and maintain an extra index
+ Mnesia is to build and maintain an extra index
table.</p>
</item>
<item>
<p><c>local_content</c>. When an application requires
tables whose contents are local to each node,
<c>local_content</c> tables can be used. The table name
- is known to all <c>Mnesia</c> nodes, but its content is
+ is known to all Mnesia nodes, but its content is
unique on each node. This means that access to such a table
must be done locally. Set field <c>local_content</c> to
<c>true</c> to enable the <c>local_content</c>
@@ -143,7 +143,7 @@
avoid inconsistencies because of network splits.</p>
</item>
<item>
- <p><c>snmp</c>. Each (set-based) <c>Mnesia</c> table can be
+ <p><c>snmp</c>. Each (set-based) Mnesia table can be
automatically turned into a Simple Network Management
Protocol (SNMP) ordered table as well.
This property specifies the types of the SNMP keys.</p>
@@ -174,7 +174,7 @@
copy of each modified record during the transaction. During
iteration, that is, <c>mnesia:fold[lr]/4</c>,
<c>mnesia:next/2</c>, <c>mnesia:prev/2</c>, and
- <c>mnesia:snmp_get_next_index/2</c>, <c>Mnesia</c>
+ <c>mnesia:snmp_get_next_index/2</c>, Mnesia
compensates for every written or deleted record, which can
reduce the performance.</p>
<p>If possible, avoid writing or deleting records in the same
@@ -188,7 +188,7 @@
<desc>
<p>Makes the transaction silently
return the tuple <c>{aborted, Reason}</c>.
- Termination of a <c>Mnesia</c> transaction means that
+ Termination of a Mnesia transaction means that
an exception is thrown to an enclosing <c>catch</c>.
Thus, the expression <c>catch mnesia:abort(x)</c> does
not terminate the transaction.</p>
@@ -324,7 +324,7 @@
<c>mnesia:ets</c>. Argument <c>AccessMod</c>
is the name of a callback module, which implements the
<c>mnesia_access</c> behavior.</p>
- <p><c>Mnesia</c> forwards calls to the following functions:</p>
+ <p>Mnesia forwards calls to the following functions:</p>
<list type="bulleted">
<item>mnesia:lock/2 (read_lock_table/1, write_lock_table/1)
</item>
@@ -391,15 +391,15 @@
</item>
</list>
<p><c>ActivityId</c> is a record that represents the identity
- of the enclosing <c>Mnesia</c> activity. The first field
+ of the enclosing Mnesia activity. The first field
(obtained with <c>element(1, ActivityId)</c>) contains an
atom, which can be interpreted as the activity type:
<c>ets</c>, <c>async_dirty</c>, <c>sync_dirty</c>, or
<c>tid</c>. <c>tid</c> means that the activity is a
transaction. The structure of the rest of the identity
- record is internal to <c>Mnesia</c>.</p>
+ record is internal to Mnesia.</p>
<p><c>Opaque</c> is an opaque data structure that is internal
- to <c>Mnesia</c>.</p>
+ to Mnesia.</p>
</desc>
</func>
<func>
@@ -458,7 +458,7 @@ mnesia:add_table_index(person, age)</code>
<desc>
<marker id="async_dirty"></marker>
<p>Calls the <c>Fun</c> in a context that is not protected by
- a transaction. The <c>Mnesia</c> function calls performed in
+ a transaction. The Mnesia function calls performed in
the <c>Fun</c> are mapped to the corresponding dirty
functions. This still involves logging, replication, and
subscriptions, but there is no locking, local transaction
@@ -467,7 +467,7 @@ mnesia:add_table_index(person, age)</code>
for normal <c>mnesia:dirty_*</c> operations, the operations
are performed semi-asynchronously. For details, see
<c>mnesia:activity/4</c> and the User's Guide.</p>
- <p>The <c>Mnesia</c> tables can be manipulated without
+ <p>The Mnesia tables can be manipulated without
using transactions. This has some serious disadvantages, but
is considerably faster, as the transaction manager is not
involved and no locks are set. A dirty operation does,
@@ -480,7 +480,7 @@ mnesia:add_table_index(person, age)</code>
read records dirty than within a transaction.</p>
<p>Depending on the application, it can be a good idea to use
the dirty functions for certain operations. Almost all
- <c>Mnesia</c> functions that can be called within
+ Mnesia functions that can be called within
transactions have a dirty equivalent, which is much more
efficient.</p>
<p>However, notice that there is a risk that the database can
@@ -497,7 +497,7 @@ mnesia:add_table_index(person, age)</code>
<fsummary>Backs up all tables in the database.</fsummary>
<desc>
<marker id="backup"></marker>
- <p>Activates a new checkpoint covering all <c>Mnesia</c> tables,
+ <p>Activates a new checkpoint covering all Mnesia tables,
including the schema, with maximum degree of redundancy, and
performs a backup using <c>backup_checkpoint/2/3</c>. The
default value of the backup callback module <c>BackupMod</c>
@@ -529,16 +529,16 @@ mnesia:add_table_index(person, age)</code>
<taglist>
<tag><c>extra_db_nodes</c></tag>
<item>
- <p><c>Value</c> is a list of nodes that <c>Mnesia</c>
+ <p><c>Value</c> is a list of nodes that Mnesia
is to try to connect to. <c>ReturnValue</c> is those
- nodes in <c>Value</c> that <c>Mnesia</c> is connected
+ nodes in <c>Value</c> that Mnesia is connected
to.</p>
<p>Notice that this function must only be used to connect
to newly started RAM nodes (N.D.R.S.N.) with an empty
schema. If, for example, this function is used after
the network has been partitioned, it can lead to
inconsistent tables.</p>
- <p>Notice that <c>Mnesia</c> can be connected to other
+ <p>Notice that Mnesia can be connected to other
nodes than those returned in <c>ReturnValue</c>.</p>
</item>
<tag><c>dc_dump_limit</c></tag>
@@ -548,7 +548,7 @@ mnesia:add_table_index(person, age)</code>
Configuration Parameters</seealso>. <c>ReturnValue</c>
is the new value. Notice that this configuration
parameter is not persistent. It is lost when
- <c>Mnesia</c> has stopped.</p>
+ Mnesia has stopped.</p>
</item>
</taglist>
</desc>
@@ -562,9 +562,9 @@ mnesia:add_table_index(person, age)</code>
<c>read_write</c> but it can also be set to the atom
<c>read_only</c>. If <c>AccessMode</c> is set to
<c>read_only</c>, updates to the table cannot be
- performed. At startup, <c>Mnesia</c> always loads
+ performed. At startup, Mnesia always loads
<c>read_only</c> tables locally regardless of when and if
- <c>Mnesia</c> is terminated on other nodes.</p>
+ Mnesia is terminated on other nodes.</p>
</desc>
</func>
<func>
@@ -620,13 +620,13 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
<desc>
<marker id="create_schema"></marker>
<p>Creates a new database on disc. Various files are
- created in the local <c>Mnesia</c> directory of each node.
+ created in the local Mnesia directory of each node.
Notice that the directory must be unique for each node.
Two nodes must never share the same directory. If possible,
use a local disc device to improve performance.</p>
<p><c>mnesia:create_schema/1</c> fails if any of the
Erlang nodes given as <c>DiscNodes</c> are not alive, if
- <c>Mnesia</c> is running on any of the nodes, or if any
+ Mnesia is running on any of the nodes, or if any
of the nodes already have a schema. Use
<c>mnesia:delete_schema/1</c> to get rid of old faulty
schemas.</p>
@@ -638,10 +638,10 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
</func>
<func>
<name>create_table(Name, TabDef) -> {atomic, ok} | {aborted, Reason}</name>
- <fsummary>Creates a <c>Mnesia</c> table called <c>Name</c>with properties as described by argument <c>TabDef</c>.</fsummary>
+ <fsummary>Creates a Mnesia table called <c>Name</c>with properties as described by argument <c>TabDef</c>.</fsummary>
<desc>
<marker id="create_table"></marker>
- <p>Creates a <c>Mnesia</c> table called
+ <p>Creates a Mnesia table called
<c>Name</c> according to argument <c>TabDef</c>. This
list must be a list of <c>{Item, Value}</c> tuples,
where the following values are allowed:</p>
@@ -652,8 +652,8 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
set to the atom <c>read_only</c>. If <c>AccessMode</c>
is set to <c>read_only</c>, updates to the table
cannot be performed.</p>
- <p>At startup, <c>Mnesia</c> always loads <c>read_only</c>
- table locally regardless of when and if <c>Mnesia</c> is
+ <p>At startup, Mnesia always loads <c>read_only</c>
+ table locally regardless of when and if Mnesia is
terminated on other nodes. This argument returns the
access mode of the table. The access mode can be
<c>read_only</c> or <c>read_write</c>.</p>
@@ -693,7 +693,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
<item>
<p><c>{index, Intlist}</c>, where
<c>Intlist</c> is a list of attribute names (atoms) or
- record fields for which <c>Mnesia</c> is to build and
+ record fields for which Mnesia is to build and
maintain an extra index table. The <c>qlc</c> query
compiler <em>may</em> be able to optimize queries
if there are indexes available.</p>
@@ -735,10 +735,10 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
<c>mnesia:create_table/2</c>, the table is immediately
accessible by SNMP. Therefore applications that use
SNMP to manipulate and control the system can be
- designed easily, since <c>Mnesia</c> provides a
+ designed easily, since Mnesia provides a
direct mapping between the logical tables that make up
an SNMP control application and the physical data that
- makes up a <c>Mnesia</c> table.</p>
+ makes up a Mnesia table.</p>
</item>
<item>
<p><c>{storage_properties, [{Backend, Properties}]</c>
@@ -746,7 +746,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code>
<c>Backend</c> can currently be <c>ets</c> or <c>dets</c>.
<c>Properties</c> is a list of options sent to the
back end storage during table creation. <c>Properties</c>
- cannot contain properties already used by <c>Mnesia</c>,
+ cannot contain properties already used by Mnesia,
such as <c>type</c> or <c>named_table</c>.</p>
<p>For example:</p>
<code type="none">
@@ -776,7 +776,7 @@ mnesia:create_table(table, [{ram_copies, [node()]}, {disc_only_copies, nodes()},
mnesia:create_table(person,
[{ram_copies, [N1, N2]},
{attributes, record_info(fields, person)}]).</code>
- <p>If it is required that <c>Mnesia</c> must build and
+ <p>If it is required that Mnesia must build and
maintain an extra index table on attribute <c>address</c>
of all the <c>person</c> records that are inserted in the
table, the following code would be issued:</p>
@@ -819,8 +819,8 @@ mnesia:create_table(person,
When the last replica is deleted with this
function, the table disappears entirely.</p>
<p>This function can also be used to delete a replica of
- the table named <c>schema</c>. The <c>Mnesia</c> node is
- then removed. Notice that <c>Mnesia</c> must be
+ the table named <c>schema</c>. The Mnesia node is
+ then removed. Notice that Mnesia must be
stopped on the node first.</p>
</desc>
</func>
@@ -891,9 +891,9 @@ mnesia:create_table(person,
<c>mnesia:create_schema/1</c>.
<c>mnesia:delete_schema/1</c> fails if any of the Erlang
nodes given as <c>DiscNodes</c> are not alive, or if
- <c>Mnesia</c> is running on any of the nodes.</p>
+ Mnesia is running on any of the nodes.</p>
<p>After the database is deleted, it can still be possible
- to start <c>Mnesia</c> as a disc-less node. This depends
+ to start Mnesia as a disc-less node. This depends
on how configuration parameter <c>schema_location</c> is
set.</p>
<warning>
@@ -1100,7 +1100,7 @@ mnesia:create_table(person,
<name>dirty_update_counter(Tab, Key, Incr) -> NewVal | exit({aborted, Reason})</name>
<fsummary>Dirty update of a counter record.</fsummary>
<desc>
- <p><c>Mnesia</c> has no special counter records. However,
+ <p>Mnesia has no special counter records. However,
records of the form <c>{Tab, Key, Integer}</c> can be used
as (possibly disc-resident) counters when <c>Tab</c> is a
<c>set</c>. This function updates a counter with a positive
@@ -1147,7 +1147,7 @@ mnesia:create_table(person,
<desc>
<marker id="dump_log"></marker>
<p>Performs a user-initiated dump of the local log file.
- This is usually not necessary, as <c>Mnesia</c> by default
+ This is usually not necessary, as Mnesia by default
manages this automatically. See configuration parameters
<seealso marker="#dump_log_time_threshold">dump_log_time_threshold</seealso>
and
@@ -1172,7 +1172,7 @@ mnesia:create_table(person,
<fsummary>Dumps local tables into a text file.</fsummary>
<desc>
<marker id="dump_to_textfile"></marker>
- <p>Dumps all local tables of a <c>Mnesia</c> system into a
+ <p>Dumps all local tables of a Mnesia system into a
text file, which can be edited (by a normal text editor)
and then be reloaded with
<c>mnesia:load_textfile/1</c>. Only use this function for
@@ -1182,10 +1182,10 @@ mnesia:create_table(person,
</func>
<func>
<name>error_description(Error) -> String</name>
- <fsummary>Returns a string describing a particular <c>Mnesia</c> error.</fsummary>
+ <fsummary>Returns a string describing a particular Mnesia error.</fsummary>
<desc>
<marker id="error_description"></marker>
- <p>All <c>Mnesia</c> transactions, including all the schema
+ <p>All Mnesia transactions, including all the schema
update functions, either return value <c>{atomic, Val}</c>
or the tuple <c>{aborted, Reason}</c>. <c>Reason</c> can
be either of the atoms in the following list. The
@@ -1264,8 +1264,8 @@ mnesia:create_table(person,
<desc>
<marker id="ets"></marker>
<p>Calls the <c>Fun</c> in a raw context that is not protected by
- a transaction. The <c>Mnesia</c> function call is performed in
- the <c>Fun</c> and performed directly on the local <c>ets</c>
+ a transaction. The Mnesia function call is performed in
+ the <c>Fun</c> and performed directly on the local ETS
tables on the assumption that the local storage type is
<c>ram_copies</c> and the tables are not replicated to other
nodes. Subscriptions are not triggered and checkpoints are
@@ -1321,13 +1321,13 @@ mnesia:create_table(person,
<fsummary>Forces a table to be loaded into the system.</fsummary>
<desc>
<marker id="force_load_table"></marker>
- <p>The <c>Mnesia</c> algorithm for table load can lead to a
+ <p>The Mnesia algorithm for table load can lead to a
situation where a table cannot be loaded. This situation
- occurs when a node is started and <c>Mnesia</c> concludes, or
+ occurs when a node is started and Mnesia concludes, or
suspects, that another copy of the table was active after
this local copy became inactive because of a system crash.</p>
<p>If this situation is not acceptable, this function can be
- used to override the strategy of the <c>Mnesia</c> table
+ used to override the strategy of the Mnesia table
load algorithm. This can lead to a situation where some
transaction effects are lost with an inconsistent database as
result, but for some applications high availability is more
@@ -1402,9 +1402,9 @@ mnesia:create_table(person,
<desc>
<marker id="info"></marker>
<p>Prints system information on the terminal.
- This function can be used even if <c>Mnesia</c> is not
+ This function can be used even if Mnesia is not
started. However, more information is displayed if
- <c>Mnesia</c> is started.</p>
+ Mnesia is started.</p>
</desc>
</func>
<func>
@@ -1431,7 +1431,7 @@ mnesia:create_table(person,
<p>Installs a backup as fallback. The fallback is used to
restore the database at the next startup. Installation of
fallbacks requires Erlang to be operational on all the
- involved nodes, but it does not matter if <c>Mnesia</c>
+ involved nodes, but it does not matter if Mnesia
is running or not. The installation of the fallback fails
if the local node is not one of the disc-resident nodes
in the backup.</p>
@@ -1465,14 +1465,14 @@ mnesia:create_table(person,
<p><c>{mnesia_dir, AlternateDir}</c>.
This argument is only valid if the scope of the
installation is <c>local</c>. Normally the installation
- of a fallback is targeted to the <c>Mnesia</c> directory,
+ of a fallback is targeted to the Mnesia directory,
as configured with configuration parameter
<c>-mnesia dir</c>. But by explicitly supplying an
<c>AlternateDir</c>, the fallback is installed there
- regardless of the <c>Mnesia</c> directory configuration
+ regardless of the Mnesia directory configuration
parameter setting. After installation of a fallback on
- an alternative <c>Mnesia</c> directory, that directory
- is fully prepared for use as an active <c>Mnesia</c>
+ an alternative Mnesia directory, that directory
+ is fully prepared for use as an active Mnesia
directory.</p>
<p>This is a dangerous feature that must be
used with care. By unintentional mixing of directories,
@@ -1509,7 +1509,7 @@ mnesia:create_table(person,
<marker id="load_textfile"></marker>
<p>Loads a series of definitions and data found in the
text file (generated with <c>mnesia:dump_to_textfile/1</c>)
- into <c>Mnesia</c>. This function also starts <c>Mnesia</c>
+ into Mnesia. This function also starts Mnesia
and possibly creates a new schema. This function is
intended for educational purposes only. It is recommended
to use other functions to deal with real backups.</p>
@@ -1558,7 +1558,7 @@ mnesia:create_table(person,
</taglist>
<p>Conflicting lock requests are automatically queued if there
is no risk of a deadlock. Otherwise the transaction must be
- terminated and executed again. <c>Mnesia</c> does this
+ terminated and executed again. Mnesia does this
automatically as long as the upper limit of the maximum
<c>retries</c> is not reached. For details, see
<c>mnesia:transaction/3</c>.</p>
@@ -1726,19 +1726,19 @@ mnesia:create_table(person,
</func>
<func>
<name>report_event(Event) -> ok</name>
- <fsummary>Reports a user event to the <c>Mnesia</c> event handler.</fsummary>
+ <fsummary>Reports a user event to the Mnesia event handler.</fsummary>
<desc>
<marker id="report_event"></marker>
- <p>When tracing a system of <c>Mnesia</c> applications it is
- useful to be able to interleave <c>Mnesia</c> own events with
+ <p>When tracing a system of Mnesia applications it is
+ useful to be able to interleave Mnesia own events with
application-related events that give information about the
application context.</p>
<p>Whenever the application begins a
- new and demanding <c>Mnesia</c> task, or if it enters a new
+ new and demanding Mnesia task, or if it enters a new
interesting phase in its execution, it can be a good idea to
use <c>mnesia:report_event/1</c>. <c>Event</c> can be
any term and generates a <c>{mnesia_user, Event}</c> event
- for any processes that subscribe to <c>Mnesia</c> system
+ for any processes that subscribe to Mnesia system
events.</p>
</desc>
</func>
@@ -1748,7 +1748,7 @@ mnesia:create_table(person,
<desc>
<marker id="restore"></marker>
<p>With this function, tables can be restored online from a
- backup without restarting <c>Mnesia</c>.
+ backup without restarting Mnesia.
<c>Opaque</c> is forwarded to the backup module.
<c>Args</c> is a list of the following tuples:</p>
<list type="bulleted">
@@ -1873,7 +1873,7 @@ mnesia:create_table(person,
<p>For a complete description of <c>select</c>, see the
<seealso marker="erts:index">ERTS</seealso> User's Guide and the
<seealso marker="stdlib:ets">ets</seealso> manual page in
- <c>STDLIB</c>.</p>
+ STDLIB.</p>
<p>For example, to find the names of all male persons older
than 30 in table <c>Tab</c>:</p>
<code type="none">
@@ -1920,10 +1920,10 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
</func>
<func>
<name>set_debug_level(Level) -> OldLevel</name>
- <fsummary>Changes the internal debug level of <c>Mnesia</c>.</fsummary>
+ <fsummary>Changes the internal debug level of Mnesia.</fsummary>
<desc>
<marker id="set_debug_level"></marker>
- <p>Changes the internal debug level of <c>Mnesia</c>.
+ <p>Changes the internal debug level of Mnesia.
For details, see
<seealso marker="#configuration_parameters">Section
Configuration Parameters</seealso>.</p>
@@ -1934,7 +1934,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
<fsummary>Sets the master nodes for all tables.</fsummary>
<desc>
<marker id="set_master_nodes_1"></marker>
- <p>For each table <c>Mnesia</c> determines its replica nodes
+ <p>For each table Mnesia determines its replica nodes
(<c>TabNodes</c>) and starts
<c>mnesia:set_master_nodes(Tab, TabMasterNodes)</c>. where
<c>TabMasterNodes</c> is the intersection of
@@ -1952,16 +1952,16 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
that can have caused an inconsistent database, it can use the
function <c>mnesia:set_master_nodes(Tab, MasterNodes)</c> to
define from which nodes each table is to be loaded.
- At startup, the <c>Mnesia</c> normal table load algorithm is
+ At startup, the Mnesia normal table load algorithm is
bypassed and the table is loaded from one of the master nodes
- defined for the table, regardless of when and if <c>Mnesia</c>
+ defined for the table, regardless of when and if Mnesia
terminated on other nodes. <c>MasterNodes</c> can only
contain nodes where the table has a replica. If the
<c>MasterNodes</c> list is empty, the master node recovery
mechanism for the particular table is reset, and the
normal load mechanism is used at the next restart.</p>
<p>The master node setting is always local. It can be
- changed regardless if <c>Mnesia</c> is started or not.</p>
+ changed regardless if Mnesia is started or not.</p>
<p>The database can also become inconsistent if
configuration parameter <c>max_wait_for_decision</c> is used
or if <c>mnesia:force_load_table/1</c> is used.</p>
@@ -1976,7 +1976,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
</func>
<func>
<name>snmp_get_mnesia_key(Tab, RowIndex) -> {ok, Key} | undefined</name>
- <fsummary>Gets the corresponding <c>Mnesia</c> key from an SNMP index.</fsummary>
+ <fsummary>Gets the corresponding Mnesia key from an SNMP index.</fsummary>
<type>
<v>Tab ::= atom()</v>
<v>RowIndex ::= [integer()]</v>
@@ -1984,7 +1984,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
<v>key() ::= integer() | string() | [integer()]</v>
</type>
<desc>
- <p>Transforms an SNMP index to the corresponding <c>Mnesia</c>
+ <p>Transforms an SNMP index to the corresponding Mnesia
key. If the SNMP table has multiple keys, the key is a tuple
of the key columns.</p>
</desc>
@@ -2020,7 +2020,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
</func>
<func>
<name>snmp_open_table(Tab, SnmpStruct) -> {aborted, R} | {atomic, ok}</name>
- <fsummary>Organizes a <c>Mnesia</c> table as an SNMP table.</fsummary>
+ <fsummary>Organizes a Mnesia table as an SNMP table.</fsummary>
<type>
<v>Tab ::= atom()</v>
<v>SnmpStruct ::= [{key, type()}]</v>
@@ -2029,14 +2029,14 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code>
</type>
<desc>
<p>A direct one-to-one mapping can be established between
- <c>Mnesia</c> tables and SNMP tables. Many telecommunication
+ Mnesia tables and SNMP tables. Many telecommunication
applications are controlled and monitored by the SNMP
- protocol. This connection between <c>Mnesia</c> and SNMP
+ protocol. This connection between Mnesia and SNMP
makes it simple and convenient to achieve this mapping.</p>
<p>Argument <c>SnmpStruct</c> is a list of SNMP
information. Currently, the only information needed is
information about the key types in the table. Multiple
- keys cannot be handled in <c>Mnesia</c>, but many SNMP
+ keys cannot be handled in Mnesia, but many SNMP
tables have multiple keys. Therefore, the following
convention is used: if a table has multiple keys, these must
always be stored as a tuple of the keys. Information about
@@ -2069,39 +2069,39 @@ mnesia:create_table(employee,
<p>When a table is SNMP ordered, modifications are more
expensive than usual, O(logN). Also, more memory is used.</p>
<p>Notice that only the lexicographical SNMP ordering is
- implemented in <c>Mnesia</c>, not the actual SNMP monitoring.</p>
+ implemented in Mnesia, not the actual SNMP monitoring.</p>
</desc>
</func>
<func>
<name>start() -> ok | {error, Reason}</name>
- <fsummary>Starts a local <c>Mnesia</c> system.</fsummary>
+ <fsummary>Starts a local Mnesia system.</fsummary>
<desc>
<marker id="start"></marker>
- <p>The startup procedure for a set of <c>Mnesia</c> nodes is a
- fairly complicated operation. A <c>Mnesia</c> system consists
- of a set of nodes, with <c>Mnesia</c> started locally on all
+ <p>The startup procedure for a set of Mnesia nodes is a
+ fairly complicated operation. A Mnesia system consists
+ of a set of nodes, with Mnesia started locally on all
participating nodes. Normally, each node has a directory where
- all the <c>Mnesia</c> files are written. This directory is
- referred to as the <c>Mnesia</c> directory. <c>Mnesia</c> can
+ all the Mnesia files are written. This directory is
+ referred to as the Mnesia directory. Mnesia can
also be started on disc-less nodes. For more information
about disc-less nodes, see <c>mnesia:create_schema/1</c>
and the User's Guide.</p>
- <p>The set of nodes that makes up a <c>Mnesia</c> system is kept
- in a schema. <c>Mnesia</c> nodes can be added to or removed
+ <p>The set of nodes that makes up a Mnesia system is kept
+ in a schema. Mnesia nodes can be added to or removed
from the schema. The initial schema is normally created on
disc with the function <c>mnesia:create_schema/1</c>. On
disc-less nodes, a tiny default schema is generated each time
- <c>Mnesia</c> is started. During the startup procedure,
- <c>Mnesia</c> exchanges schema information between the nodes
+ Mnesia is started. During the startup procedure,
+ Mnesia exchanges schema information between the nodes
to verify that the table definitions are compatible.</p>
<p>Each schema has a unique cookie, which can be regarded as a
unique schema identifier. The cookie must be the same on all
- nodes where <c>Mnesia</c> is supposed to run. For details,
+ nodes where Mnesia is supposed to run. For details,
see the User's Guide.</p>
- <p>The schema file and all other files that <c>Mnesia</c>
- needs are kept in the <c>Mnesia</c> directory. The
+ <p>The schema file and all other files that Mnesia
+ needs are kept in the Mnesia directory. The
command-line option <c>-mnesia dir Dir</c> can be used to
- specify the location of this directory to the <c>Mnesia</c>
+ specify the location of this directory to the Mnesia
system. If no such command-line option is found, the name
of the directory defaults to <c>Mnesia.Node</c>.</p>
<p><c>application:start(mnesia)</c> can also be used.</p>
@@ -2109,10 +2109,10 @@ mnesia:create_table(employee,
</func>
<func>
<name>stop() -> stopped</name>
- <fsummary>Stops <c>Mnesia</c> locally.</fsummary>
+ <fsummary>Stops Mnesia locally.</fsummary>
<desc>
<marker id="stop"></marker>
- <p>Stops <c>Mnesia</c> locally on the current node.</p>
+ <p>Stops Mnesia locally on the current node.</p>
<p><c>application:stop(mnesia)</c> can also be used.</p>
</desc>
</func>
@@ -2132,7 +2132,7 @@ mnesia:create_table(employee,
<desc>
<marker id="sync_dirty"></marker>
<p>Calls the <c>Fun</c> in a context that is not protected by
- a transaction. The <c>Mnesia</c> function calls performed in
+ a transaction. The Mnesia function calls performed in
the <c>Fun</c> are mapped to the corresponding dirty functions.
It is performed in almost the same context as
<c>mnesia:async_dirty/1,2</c>. The difference is that the
@@ -2167,10 +2167,10 @@ mnesia:create_table(employee,
</func>
<func>
<name>system_info(InfoKey) -> Info | exit({aborted, Reason})</name>
- <fsummary>Returns information about the <c>Mnesia</c> system.</fsummary>
+ <fsummary>Returns information about the Mnesia system.</fsummary>
<desc>
<marker id="system_info"></marker>
- <p>Returns information about the <c>Mnesia</c> system, such as
+ <p>Returns information about the Mnesia system, such as
transaction statistics, <c>db_nodes</c>, and configuration
parameters. The valid keys are as follows:</p>
<list type="bulleted">
@@ -2188,7 +2188,7 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>auto_repair</c>. Returns <c>true</c> or <c>false</c>
- to indicate if <c>Mnesia</c> is configured to start the
+ to indicate if Mnesia is configured to start the
auto-repair facility on corrupted disc files.</p>
</item>
<item>
@@ -2209,20 +2209,20 @@ mnesia:create_table(employee,
in the list of nodes if they explicitly have been added
to the schema, for example, with
<c>mnesia:add_table_copy/3</c>. The function can be
- started even if <c>Mnesia</c> is not yet running.</p>
+ started even if Mnesia is not yet running.</p>
</item>
<item>
<p><c>debug</c>. Returns the current debug level of
- <c>Mnesia</c>.</p>
+ Mnesia.</p>
</item>
<item>
- <p><c>directory</c>. Returns the name of the <c>Mnesia</c>
- directory. It can be called even if <c>Mnesia</c> is
+ <p><c>directory</c>. Returns the name of the Mnesia
+ directory. It can be called even if Mnesia is
not yet running.</p>
</item>
<item>
<p><c>dump_log_load_regulation</c>. Returns a boolean that
- tells if <c>Mnesia</c> is configured to regulate the
+ tells if Mnesia is configured to regulate the
dumper process load.</p>
<p>This feature is temporary and will be removed in future
releases.</p>
@@ -2233,9 +2233,9 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>dump_log_update_in_place</c>. Returns a boolean that
- tells if <c>Mnesia</c> is configured to perform the
- updates in the <c>dets</c> files directly, or if the
- updates are to be performed in a copy of the <c>dets</c>
+ tells if Mnesia is configured to perform the
+ updates in the Dets files directly, or if the
+ updates are to be performed in a copy of the Dets
files.</p>
</item>
<item>
@@ -2253,13 +2253,13 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>held_locks</c>. Returns a list of all
- locks held by the local <c>Mnesia</c> lock manager.</p>
+ locks held by the local Mnesia lock manager.</p>
</item>
<item>
<p><c>is_running</c>. Returns <c>yes</c> or <c>no</c> to
- indicate if <c>Mnesia</c> is running. It can
+ indicate if Mnesia is running. It can
also return <c>starting</c> or <c>stopping</c>. Can be
- called even if <c>Mnesia</c> is not yet running.</p>
+ called even if Mnesia is not yet running.</p>
</item>
<item>
<p><c>local_tables</c>. Returns a list
@@ -2272,7 +2272,7 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>log_version</c>. Returns the version
- number of the <c>Mnesia</c> transaction log format.</p>
+ number of the Mnesia transaction log format.</p>
</item>
<item>
<p><c>master_node_tables</c>. Returns a
@@ -2280,25 +2280,25 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>protocol_version</c>. Returns the version number of
- the <c>Mnesia</c> inter-process communication protocol.</p>
+ the Mnesia inter-process communication protocol.</p>
</item>
<item>
<p><c>running_db_nodes</c>. Returns a list of nodes where
- <c>Mnesia</c> currently is running. This function can be
- called even if <c>Mnesia</c> is not yet running, but it
+ Mnesia currently is running. This function can be
+ called even if Mnesia is not yet running, but it
then has slightly different semantics.</p>
- <p>If <c>Mnesia</c> is down on the local node, the function
+ <p>If Mnesia is down on the local node, the function
returns those other <c>db_nodes</c> and
<c>extra_db_nodes</c> that for the moment are
operational.</p>
- <p>If <c>Mnesia</c> is started, the function returns
- those nodes that <c>Mnesia</c> on the local node is fully
- connected to. Only those nodes that <c>Mnesia</c> has
+ <p>If Mnesia is started, the function returns
+ those nodes that Mnesia on the local node is fully
+ connected to. Only those nodes that Mnesia has
exchanged schema information with are included as
<c>running_db_nodes</c>. After the merge of schemas, the
- local <c>Mnesia</c> system is fully operable and
+ local Mnesia system is fully operable and
applications can perform access of remote replicas.
- Before the schema merge, <c>Mnesia</c> only operates
+ Before the schema merge, Mnesia only operates
locally. Sometimes there are more nodes included in the
<c>running_db_nodes</c> list than all <c>db_nodes</c>
and <c>extra_db_nodes</c> together.</p>
@@ -2322,17 +2322,17 @@ mnesia:create_table(employee,
<item>
<p><c>transaction_failures</c>. Returns a
number that indicates how many transactions have
- failed since <c>Mnesia</c> was started.</p>
+ failed since Mnesia was started.</p>
</item>
<item>
<p><c>transaction_commits</c>. Returns a
number that indicates how many transactions have
- terminated successfully since <c>Mnesia</c> was started.</p>
+ terminated successfully since Mnesia was started.</p>
</item>
<item>
<p><c>transaction_restarts</c>. Returns a
number that indicates how many transactions have been
- restarted since <c>Mnesia</c> was started.</p>
+ restarted since Mnesia was started.</p>
</item>
<item>
<p><c>transaction_log_writes</c>.
@@ -2342,12 +2342,12 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>use_dir</c>. Returns a boolean that indicates if
- the <c>Mnesia</c> directory is used or not. Can be
- started even if <c>Mnesia</c> is not yet running.</p>
+ the Mnesia directory is used or not. Can be
+ started even if Mnesia is not yet running.</p>
</item>
<item>
<p><c>version</c>. Returns the current
- version number of <c>Mnesia</c>.</p>
+ version number of Mnesia.</p>
</item>
</list>
</desc>
@@ -2359,13 +2359,13 @@ mnesia:create_table(employee,
<marker id="table"></marker>
<p>Returns a Query List Comprehension (QLC) query handle,
see the <seealso marker="stdlib:qlc">qlc(3)</seealso>
- manual page in <c>STDLIB</c>. The module <c>qlc</c>
- implements a query language that can use <c>Mnesia</c>
+ manual page in STDLIB. The module <c>qlc</c>
+ implements a query language that can use Mnesia
tables as sources of data. Calling
<c>mnesia:table/1,2</c> is the means to make the
<c>mnesia</c> table <c>Tab</c> usable to QLC.</p>
- <p><c>Option</c> can contain <c>Mnesia</c>
- options or QLC options. <c>Mnesia</c> recognizes the
+ <p><c>Option</c> can contain Mnesia
+ options or QLC options. Mnesia recognizes the
following options (any other option is forwarded to
QLC).</p>
<list type="bulleted">
@@ -2374,7 +2374,7 @@ mnesia:create_table(employee,
</item>
<item><c>{n_objects,Number}</c>, where <c>n_objects</c>
specifies (roughly) the number of objects returned
- from <c>Mnesia</c> to QLC. Queries to remote tables
+ from Mnesia to QLC. Queries to remote tables
can need a larger chunk to reduce network overhead.
By default, <c>100</c> objects at a time are returned.
</item>
@@ -2413,7 +2413,7 @@ mnesia:create_table(employee,
<desc>
<marker id="table_info"></marker>
<p>The <c>table_info/2</c> function takes two arguments.
- The first is the name of a <c>Mnesia</c> table.
+ The first is the name of a Mnesia table.
The second is one of the following keys:</p>
<list type="bulleted">
<item>
@@ -2465,7 +2465,7 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>load_node</c>. Returns the name of
- the node that <c>Mnesia</c> loaded the table from. The
+ the node that Mnesia loaded the table from. The
structure of the returned value is unspecified, but
can be useful for debugging purposes.</p>
</item>
@@ -2476,7 +2476,7 @@ mnesia:create_table(employee,
</item>
<item>
<p><c>load_reason</c>. Returns the
- reason of why <c>Mnesia</c> decided to load the table.
+ reason of why Mnesia decided to load the table.
The structure of the returned value is unspecified, but
can be useful for debugging purposes.</p>
</item>
@@ -2620,7 +2620,7 @@ raise(Name, Amount) ->
several processes running on different nodes can concurrently
execute the function <c>raise/2</c> without interfering
with each other.</p>
- <p>Since <c>Mnesia</c> detects deadlocks, a transaction can be
+ <p>Since Mnesia detects deadlocks, a transaction can be
restarted any number of times. This function attempts a
restart as specified in <c>Retries</c>. <c>Retries</c> must
be an integer greater than 0 or the atom <c>infinity</c>.
@@ -2643,7 +2643,7 @@ raise(Name, Amount) ->
<p><c>NewAttributeList</c> and <c>NewRecordName</c>
specify the attributes and the new record type of the
converted table. Table name always remains unchanged. If
- <c>record_name</c> is changed, only the <c>Mnesia</c>
+ <c>record_name</c> is changed, only the Mnesia
functions that use table identifiers work, for example,
<c>mnesia:write/3</c> works, but not <c>mnesia:write/1</c>.</p>
</desc>
@@ -2706,7 +2706,7 @@ raise(Name, Amount) ->
distributed operation that is either performed on all
nodes with disc resident schema, or none. Uninstallation of
fallbacks requires Erlang to be operational on all
- involved nodes, but it does not matter if <c>Mnesia</c> is
+ involved nodes, but it does not matter if Mnesia is
running or not. Which nodes that are considered as
disc-resident nodes is determined from the schema
information in the local fallback.</p>
@@ -2792,28 +2792,28 @@ raise(Name, Amount) ->
<section>
<title>Configuration Parameters</title>
<marker id="configuration_parameters"></marker>
- <p><c>Mnesia</c> reads the following application configuration
+ <p>Mnesia reads the following application configuration
parameters:</p>
<list type="bulleted">
<item>
<p><c>-mnesia access_module Module</c>. The name of the
- <c>Mnesia</c> activity access callback module. Default is
+ Mnesia activity access callback module. Default is
<c>mnesia</c>.</p>
</item>
<item>
<p><c>-mnesia auto_repair true | false</c>. This flag
- controls if <c>Mnesia</c> automatically tries to repair
+ controls if Mnesia automatically tries to repair
files that have not been properly closed. Default is
<c>true</c>.</p>
</item>
<item>
<p><c>-mnesia backup_module Module</c>. The name of the
- <c>Mnesia</c> backup callback module. Default is
+ Mnesia backup callback module. Default is
<c>mnesia_backup</c>.</p>
</item>
<item>
<p><c>-mnesia debug Level</c>. Controls the debug level
- of <c>Mnesia</c>. The possible values are as follows:</p>
+ of Mnesia. The possible values are as follows:</p>
<taglist>
<tag><c>none</c></tag>
<item>
@@ -2825,7 +2825,7 @@ raise(Name, Amount) ->
events generate <c>{mnesia_info, Format, Args}</c>
system events. Processes can subscribe to these events with
<c>mnesia:subscribe/1</c>. The events are always sent to
- the <c>Mnesia</c> event handler.</p>
+ the Mnesia event handler.</p>
</item>
<tag><c>debug</c></tag>
<item>
@@ -2834,15 +2834,15 @@ raise(Name, Amount) ->
<c>{mnesia_info, Format, Args}</c> system events.
Processes can subscribe to these events with
<c>mnesia:subscribe/1</c>. The events are always sent to
- the <c>Mnesia</c> event handler. On this debug level,
- the <c>Mnesia</c> event handler starts subscribing to
+ the Mnesia event handler. On this debug level,
+ the Mnesia event handler starts subscribing to
updates in the schema table.</p>
</item>
<tag><c>trace</c></tag>
<item>
<p>Activates all events at the debug level. On this
- level, the <c>Mnesia</c> event handler starts subscribing
- to updates on all <c>Mnesia</c> tables. This level is
+ level, the Mnesia event handler starts subscribing
+ to updates on all Mnesia tables. This level is
intended only for debugging small toy systems, as many
large events can be generated.</p>
</item>
@@ -2856,7 +2856,7 @@ raise(Name, Amount) ->
</item>
<item>
<p><c>-mnesia core_dir Directory</c>. The name of the
- directory where <c>Mnesia</c> core files is stored, or
+ directory where Mnesia core files is stored, or
false. Setting it implies that also RAM-only nodes
generate a core file if a crash occurs.</p>
</item>
@@ -2870,9 +2870,9 @@ raise(Name, Amount) ->
</item>
<item>
<p><c>-mnesia dir Directory</c>. The name of the directory
- where all <c>Mnesia</c> data is stored. The directory name
+ where all Mnesia data is stored. The directory name
must be unique for the current node. Two nodes must never
- share the the same <c>Mnesia</c> directory. The results
+ share the the same Mnesia directory. The results
are unpredictable.</p>
</item>
<item>
@@ -2915,44 +2915,44 @@ raise(Name, Amount) ->
</item>
<item>
<p><c>-mnesia event_module Module</c>. The name of the
- <c>Mnesia</c> event handler callback module. Default is
+ Mnesia event handler callback module. Default is
<c>mnesia_event</c>.</p>
</item>
<item>
<p><c>-mnesia extra_db_nodes Nodes</c> specifies a list of
nodes, in addition to the ones found in the schema, with
- which <c>Mnesia</c> is also to establish contact. Default
+ which Mnesia is also to establish contact. Default
is <c>[]</c> (empty list).</p>
</item>
<item>
<p><c>-mnesia fallback_error_function {UserModule, UserFunc}</c>.
Specifies a user-supplied callback function, which is
- called if a fallback is installed and <c>Mnesia</c> goes
- down on another node. <c>Mnesia</c> calls the function
+ called if a fallback is installed and Mnesia goes
+ down on another node. Mnesia calls the function
with one argument, the name of the dying node, for example,
- <c>UserModule:UserFunc(DyingNode)</c>. <c>Mnesia</c> must
+ <c>UserModule:UserFunc(DyingNode)</c>. Mnesia must
be restarted, otherwise the database can be inconsistent.
- The default behavior is to terminate <c>Mnesia</c>.</p>
+ The default behavior is to terminate Mnesia.</p>
</item>
<item>
<p><c>-mnesia max_wait_for_decision Timeout</c>. Specifies
- how long <c>Mnesia</c> waits for other nodes to share their
+ how long Mnesia waits for other nodes to share their
knowledge about the outcome of an unclear transaction. By
default, <c>Timeout</c> is set to the atom <c>infinity</c>.
- This implies that if <c>Mnesia</c> upon startup detects
+ This implies that if Mnesia upon startup detects
a "heavyweight transaction" whose outcome is unclear, the
- local <c>Mnesia</c> waits until <c>Mnesia</c> is started
+ local Mnesia waits until Mnesia is started
on some (in the worst case all) of the other nodes that were
involved in the interrupted transaction. This is a rare
- situation, but if it occurs, <c>Mnesia</c> does not guess if
+ situation, but if it occurs, Mnesia does not guess if
the transaction on the other nodes was committed or
- terminated. <c>Mnesia</c> waits until it knows the outcome
+ terminated. Mnesia waits until it knows the outcome
and then acts accordingly.</p>
<p>If <c>Timeout</c> is set to an integer value in
- milliseconds, <c>Mnesia</c> forces "heavyweight transactions"
+ milliseconds, Mnesia forces "heavyweight transactions"
to be finished, even if the outcome of the transaction for
the moment is unclear. After <c>Timeout</c> milliseconds,
- <c>Mnesia</c> commits or terminates the transaction and
+ Mnesia commits or terminates the transaction and
continues with the startup. This can lead to a situation
where the transaction is committed on some nodes and
terminated on other nodes. If the transaction is a
@@ -2976,14 +2976,14 @@ raise(Name, Amount) ->
</item>
<item>
<p><c>-mnesia schema_location Loc</c>. Controls where
- <c>Mnesia</c> looks for its schema. Parameter
+ Mnesia looks for its schema. Parameter
<c>Loc</c> can be one of the following atoms:</p>
<taglist>
<tag><c>disc</c></tag>
<item>
<p>Mandatory disc. The schema is assumed to be located
- in the <c>Mnesia</c> directory. If the schema cannot
- be found, <c>Mnesia</c> refuses to start. This is the
+ in the Mnesia directory. If the schema cannot
+ be found, Mnesia refuses to start. This is the
old behavior.</p>
</item>
<tag><c>ram</c></tag>
@@ -3001,10 +3001,10 @@ raise(Name, Amount) ->
<tag><c>opt_disc</c></tag>
<item>
<p>Optional disc. The schema can reside on disc or in
- RAM. If the schema is found on disc, <c>Mnesia</c>
+ RAM. If the schema is found on disc, Mnesia
starts as a disc-based node and the storage type of
the schema table is <c>disc_copies</c>. If no schema is
- found on disc, <c>Mnesia</c> starts as a disc-less node
+ found on disc, Mnesia starts as a disc-less node
and the storage type of the schema table is
<c>ram_copies</c>. Default value for the application
parameter is <c>opt_disc</c>.</p>
@@ -3012,7 +3012,7 @@ raise(Name, Amount) ->
</taglist>
</item>
</list>
- <p>First, the <c>SASL</c> application parameters are checked,
+ <p>First, the SASL application parameters are checked,
then the command-line flags are checked, and finally, the
default value is chosen.</p>
</section>
diff --git a/lib/mnesia/doc/src/mnesia_frag_hash.xml b/lib/mnesia/doc/src/mnesia_frag_hash.xml
index 95f5f8aa07..51b32129b6 100644
--- a/lib/mnesia/doc/src/mnesia_frag_hash.xml
+++ b/lib/mnesia/doc/src/mnesia_frag_hash.xml
@@ -87,13 +87,13 @@
the new one.</p>
<p><c>NewState</c> is stored as <c>hash_state</c> among the
other <c>frag_properties</c>.</p>
- <p>As a part of the <c>add_frag</c> procedure, <c>Mnesia</c> iterates
+ <p>As a part of the <c>add_frag</c> procedure, Mnesia iterates
over all fragments corresponding to the <c>IterFrags</c> numbers
and starts <c>key_to_frag_number(NewState,RecordKey)</c> for
each record. If the new fragment differs from the old
fragment, the record is moved to the new fragment.</p>
<p>As the <c>add_frag</c> procedure is a part of a schema
- transaction, <c>Mnesia</c> acquires write locks on the
+ transaction, Mnesia acquires write locks on the
affected tables. That is, both the fragments corresponding
to <c>IterFrags</c> and those corresponding to
<c>AdditionalLockFrags</c>.</p>
@@ -112,7 +112,7 @@
<desc>
<p><c>NewState</c> is stored as <c>hash_state</c> among the
other <c>frag_properties</c>.</p>
- <p>As a part of the <c>del_frag</c> procedure, <c>Mnesia</c> iterates
+ <p>As a part of the <c>del_frag</c> procedure, Mnesia iterates
over all fragments corresponding to the <c>IterFrags</c> numbers
and starts <c>key_to_frag_number(NewState,RecordKey)</c> for
each record. If the new fragment differs from the old
@@ -120,7 +120,7 @@
<p>Notice that all records in the last fragment must be moved to
another fragment, as the entire fragment is deleted.</p>
<p>As the <c>del_frag</c> procedure is a part of a schema
- transaction, <c>Mnesia</c> acquires write locks on the
+ transaction, Mnesia acquires write locks on the
affected tables. That is, both the fragments corresponding
to <c>IterFrags</c> and those corresponding to
<c>AdditionalLockFrags</c>.</p>
@@ -134,7 +134,7 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Starts whenever <c>Mnesia</c> needs to determine
+ <p>Starts whenever Mnesia needs to determine
which fragment a certain record belongs to. It is typically
started at <c>read</c>, <c>write</c>, and <c>delete</c>.</p>
</desc>
@@ -149,7 +149,7 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>This function is called whenever <c>Mnesia</c> needs to determine
+ <p>This function is called whenever Mnesia needs to determine
which fragments that need to be searched for a <c>MatchSpec</c>.
It is typically called by <c>select</c> and
<c>match_object</c>.</p>
diff --git a/lib/mnesia/doc/src/mnesia_registry.xml b/lib/mnesia/doc/src/mnesia_registry.xml
index cd778ae072..a76f716981 100644
--- a/lib/mnesia/doc/src/mnesia_registry.xml
+++ b/lib/mnesia/doc/src/mnesia_registry.xml
@@ -38,20 +38,20 @@
<modulesummary>Dump support for registries in erl_interface.</modulesummary>
<description>
<p>This module is usually part of the <c>erl_interface</c>
- application, but is currently part of the <c>Mnesia</c>
+ application, but is currently part of the Mnesia
application.</p>
<p>This module is mainly intended for internal use within OTP,
but it has two functions that are exported for public use.</p>
<p>On C-nodes, <c>erl_interface</c> has support for registry
tables. These tables reside in RAM on the C-node, but can also
- be dumped into <c>Mnesia</c> tables. By default, the dumping
+ be dumped into Mnesia tables. By default, the dumping
of registry tables through <c>erl_interface</c> causes a
- corresponding <c>Mnesia</c> table to be created with
+ corresponding Mnesia table to be created with
<c>mnesia_registry:create_table/1</c>, if necessary.</p>
<p>Tables that are created with these functions can be
- administered as all other <c>Mnesia</c> tables. They can be
+ administered as all other Mnesia tables. They can be
included in backups, replicas can be added, and so on.
- The tables are normal <c>Mnesia</c> tables owned by the user
+ The tables are normal Mnesia tables owned by the user
of the corresponding <c>erl_interface</c> registries.</p>
</description>
@@ -68,7 +68,7 @@
that is, <c>{ram_copies,[node()]}</c> or
<c>{disc_copies,[node()]}</c>.</p>
<p>This function is used by <c>erl_interface</c> to
- create the <c>Mnesia</c> table if it does not already
+ create the Mnesia table if it does not already
exist.</p>
</desc>
</func>
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 7d8e8d0c44..a20640e1be 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -39,7 +39,94 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.14</title>
+ <section><title>Mnesia 4.14.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix backup error handling, the real failure reason was
+ not returned.</p>
+ <p>
+ Own Id: OTP-14776 Aux Id: ERIERL-103 </p>
+ </item>
+ <item>
+ <p>
+ Removed a quadratic behavior in startup. This change
+ implies that backend plugins (if used) must be set when
+ the schema is created or via configuration parameters
+ before mnesia is started.</p>
+ <p>
+ Own Id: OTP-14829 Aux Id: ERIERL-84 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.14.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed crash in checkpoint handling when table was deleted
+ during backup.</p>
+ <p>
+ Own Id: OTP-14167</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.14.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A continuation returned by mnesia:select/[14] should be
+ reusable in different, non-transactional activities.</p>
+ <p>
+ Own Id: OTP-13944 Aux Id: PR-1184 </p>
+ </item>
+ <item>
+ <p>
+ Fixed crash when calling block_table multiple times.
+ Could happen when having locks for a long time and
+ restarting mnesia.</p>
+ <p>
+ Own Id: OTP-13970 Aux Id: Seq-13198 </p>
+ </item>
+ <item>
+ <p>
+ Change mnesia_tm process to have off-heap messages since
+ mnesia_tm can be the receiver of many non-synchronized
+ message from other nodes.</p>
+ <p>
+ Own Id: OTP-14074</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.14.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Correct some minor documentation issues. </p>
+ <p>
+ Own Id: OTP-13891</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.14</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/mnesia/doc/src/part.xml b/lib/mnesia/doc/src/part.xml
index 101bdb29d4..d3ffe93937 100644
--- a/lib/mnesia/doc/src/part.xml
+++ b/lib/mnesia/doc/src/part.xml
@@ -30,7 +30,7 @@
<file>part.sgml</file>
</header>
<description>
- <p>The <c>Mnesia</c> application is a distributed Database Management
+ <p>The Mnesia application is a distributed Database Management
System (DBMS), appropriate for telecommunications applications and other
Erlang applications, which require continuous operation and exhibit soft
real-time properties.</p>
diff --git a/lib/mnesia/doc/src/ref_man.xml b/lib/mnesia/doc/src/ref_man.xml
index 662f0d61d6..7fb71b9c45 100644
--- a/lib/mnesia/doc/src/ref_man.xml
+++ b/lib/mnesia/doc/src/ref_man.xml
@@ -33,7 +33,7 @@
<file>refman.sgml</file>
</header>
<description>
- <p>The <c>Mnesia</c> application is a distributed Database Management
+ <p>The Mnesia application is a distributed Database Management
System (DBMS), appropriate for telecommunications applications and other
Erlang applications, which require continuous operation and exhibit soft
real-time properties.</p>
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index 9586adbf93..34efeca4fa 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -1409,8 +1409,14 @@ select_cont(Tid,_,State=#mnesia_select{tid=Tid,written=[]}) ->
select_state(dirty_sel_cont(State),State);
select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid}) ->
trans_select(dirty_sel_cont(State), State);
-select_cont(_Tid2,_,#mnesia_select{tid=_Tid1}) -> % Missmatching tids
+select_cont(Tid2,_,#mnesia_select{tid=_Tid1})
+ when element(1,Tid2) == tid -> % Mismatching tids
abort(wrong_transaction);
+select_cont(Tid,Ts,State=#mnesia_select{}) ->
+ % Repair mismatching tids in non-transactional contexts
+ RepairedState = State#mnesia_select{tid = Tid, written = [],
+ spec = undefined, type = undefined},
+ select_cont(Tid,Ts,RepairedState);
select_cont(_,_,Cont) ->
abort({badarg, Cont}).
@@ -1876,9 +1882,10 @@ any_table_info(Tab, Item) when is_atom(Tab) ->
[] ->
abort({no_exists, Tab, Item});
Props ->
- lists:map(fun({setorbag, Type}) -> {type, Type};
- (Prop) -> Prop end,
- Props)
+ Rename = fun ({setorbag, Type}) -> {type, Type};
+ (Prop) -> Prop
+ end,
+ lists:sort(lists:map(Rename, Props))
end;
name ->
Tab;
diff --git a/lib/mnesia/src/mnesia_checkpoint.erl b/lib/mnesia/src/mnesia_checkpoint.erl
index 9eb939e8d3..fc626940b4 100644
--- a/lib/mnesia/src/mnesia_checkpoint.erl
+++ b/lib/mnesia/src/mnesia_checkpoint.erl
@@ -909,7 +909,7 @@ retainer_loop(Cp = #checkpoint_args{name=Name}) ->
retainer_loop(Cp2);
{From, {iter_end, Iter}} ->
- retainer_fixtable(Iter#iter.oid_tab, false),
+ ?SAFE(retainer_fixtable(Iter#iter.oid_tab, false)),
Iters = Cp#checkpoint_args.iterators -- [Iter],
reply(From, Name, ok),
retainer_loop(Cp#checkpoint_args{iterators = Iters});
@@ -971,7 +971,8 @@ do_stop(Cp) ->
unset({checkpoint, Name}),
lists:foreach(fun deactivate_tab/1, Cp#checkpoint_args.retainers),
Iters = Cp#checkpoint_args.iterators,
- lists:foreach(fun(I) -> retainer_fixtable(I#iter.oid_tab, false) end, Iters).
+ [?SAFE(retainer_fixtable(Tab, false)) || #iter{main_tab=Tab} <- Iters],
+ ok.
deactivate_tab(R) ->
Name = R#retainer.cp_name,
@@ -1151,7 +1152,7 @@ do_change_copy(Cp, Tab, FromType, ToType) ->
Cp#checkpoint_args{retainers = Rs, nodes = writers(Rs)}.
check_iter(From, Iter) when Iter#iter.pid == From ->
- retainer_fixtable(Iter#iter.oid_tab, false),
+ ?SAFE(retainer_fixtable(Iter#iter.oid_tab, false)),
false;
check_iter(_From, _Iter) ->
true.
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index 4791e2e290..17b47c059e 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -1703,9 +1703,10 @@ add_active_replica(Tab, Node, Cs = #cstruct{}) ->
block_table(Tab) ->
Var = {Tab, where_to_commit},
- Old = val(Var),
- New = {blocked, Old},
- set(Var, New). % where_to_commit
+ case is_tab_blocked(val(Var)) of
+ {true, _} -> ok;
+ {false, W2C} -> set(Var, mark_blocked_tab(true, W2C))
+ end.
unblock_table(Tab) ->
call({unblock_table, Tab}).
diff --git a/lib/mnesia/src/mnesia_event.erl b/lib/mnesia/src/mnesia_event.erl
index 7320d381ea..6f7531245f 100644
--- a/lib/mnesia/src/mnesia_event.erl
+++ b/lib/mnesia/src/mnesia_event.erl
@@ -114,7 +114,8 @@ handle_table_event({Oper, Record, TransId}, State) ->
handle_system_event({mnesia_checkpoint_activated, _Checkpoint}, State) ->
{ok, State};
-handle_system_event({mnesia_checkpoint_deactivated, _Checkpoint}, State) ->
+handle_system_event({mnesia_checkpoint_deactivated, Checkpoint}, State) ->
+ report_error("Checkpoint '~p' has been deactivated, last table copy deleted.\n",[Checkpoint]),
{ok, State};
handle_system_event({mnesia_up, Node}, State) ->
diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl
index 71e5829c87..c710470a2c 100644
--- a/lib/mnesia/src/mnesia_loader.erl
+++ b/lib/mnesia/src/mnesia_loader.erl
@@ -342,9 +342,12 @@ spawned_receiver(ReplyTo,Tab,Storage,Cs, SenderPid,TabSize,DetsData, Init) ->
Done = do_init_table(Tab,Storage,Cs,
SenderPid,TabSize,DetsData,
ReplyTo, Init),
- ReplyTo ! {self(),Done},
- unlink(ReplyTo),
- unlink(whereis(mnesia_controller)),
+ try
+ ReplyTo ! {self(),Done},
+ unlink(ReplyTo),
+ unlink(whereis(mnesia_controller))
+ catch _:_ -> ok %% avoid error reports when stopping down mnesia
+ end,
exit(normal).
wait_on_load_complete(Pid) ->
@@ -916,9 +919,15 @@ send_packet(_N, _Pid, _Chunk, DataState) ->
finish_copy(Pid, Tab, Storage, RemoteS, NeedLock) ->
RecNode = node(Pid),
DatBin = dat2bin(Tab, Storage, RemoteS),
+ Node = node(Pid),
Trans =
fun() ->
NeedLock andalso mnesia:read_lock_table(Tab),
+ %% Check that receiver is still alive
+ receive {copier_done, Node} ->
+ throw(receiver_died)
+ after 0 -> ok
+ end,
A = val({Tab, access_mode}),
mnesia_controller:sync_and_block_table_whereabouts(Tab, RecNode, RemoteS, A),
cleanup_tab_copier(Pid, Storage, Tab),
@@ -927,7 +936,7 @@ finish_copy(Pid, Tab, Storage, RemoteS, NeedLock) ->
receive
{Pid, no_more} -> % Dont bother about the spurious 'more' message
no_more;
- {copier_done, Node} when Node == node(Pid)->
+ {copier_done, Node} ->
verbose("Tab receiver ~p crashed (more): ~p~n", [Tab, Node]),
receiver_died
end
diff --git a/lib/mnesia/src/mnesia_log.erl b/lib/mnesia/src/mnesia_log.erl
index 9536effd42..685d512acf 100644
--- a/lib/mnesia/src/mnesia_log.erl
+++ b/lib/mnesia/src/mnesia_log.erl
@@ -752,8 +752,8 @@ abort_write(B, What, Args, Reason) ->
Opaque = B#backup_args.opaque,
dbg_out("Failed to perform backup. M=~p:F=~p:A=~p -> ~p~n",
[Mod, What, Args, Reason]),
- try apply(Mod, abort_write, [Opaque]) of
- {ok, _Res} -> throw({error, Reason})
+ try {ok, _Res} = apply(Mod, abort_write, [Opaque]) of
+ _ -> throw({error, Reason})
catch _:Other ->
error("Failed to abort backup. ~p:~p~p -> ~p~n",
[Mod, abort_write, [Opaque], Other]),
diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl
index 0e4017e4c3..bb5e38f7c1 100644
--- a/lib/mnesia/src/mnesia_schema.erl
+++ b/lib/mnesia/src/mnesia_schema.erl
@@ -952,19 +952,9 @@ 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
+ case dirty_read_table_property(schema, Key) of
+ undefined -> [];
+ {_, Types} -> Types
end.
get_ext_types_disc() ->
diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl
index b116b48312..305bf14bcf 100644
--- a/lib/mnesia/src/mnesia_tm.erl
+++ b/lib/mnesia/src/mnesia_tm.erl
@@ -80,6 +80,7 @@ start() ->
init(Parent) ->
register(?MODULE, self()),
process_flag(trap_exit, true),
+ process_flag(message_queue_data, off_heap),
%% Initialize the schema
IgnoreFallback = mnesia_monitor:get_env(ignore_fallback_at_startup),
@@ -950,7 +951,7 @@ return_abort(Fun, Args, Reason) ->
if
Level == 1 ->
mnesia_locker:async_release_tid(Nodes, Tid),
- ?MODULE ! {delete_transaction, Tid},
+ ?SAFE(?MODULE ! {delete_transaction, Tid}),
erase(mnesia_activity_state),
flush_downs(),
?SAFE(unlink(whereis(?MODULE))),
diff --git a/lib/mnesia/test/ext_test.erl b/lib/mnesia/test/ext_test.erl
index 45ddb148bc..3d1cc40c09 100644
--- a/lib/mnesia/test/ext_test.erl
+++ b/lib/mnesia/test/ext_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. 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.
diff --git a/lib/mnesia/test/mnesia_evil_backup.erl b/lib/mnesia/test/mnesia_evil_backup.erl
index e745ec9b04..044cf501fd 100644
--- a/lib/mnesia/test/mnesia_evil_backup.erl
+++ b/lib/mnesia/test/mnesia_evil_backup.erl
@@ -723,18 +723,18 @@ bup_records(File, Mod) ->
exit(Reason)
end.
-sops_with_checkpoint(doc) ->
+sops_with_checkpoint(doc) ->
["Test schema operations during a checkpoint"];
sops_with_checkpoint(suite) -> [];
sops_with_checkpoint(Config) when is_list(Config) ->
- Ns = ?acquire_nodes(2, Config),
-
+ Ns = [N1,N2] = ?acquire_nodes(2, Config),
+
?match({ok, cp1, Ns}, mnesia:activate_checkpoint([{name, cp1},{max,mnesia:system_info(tables)}])),
- Tab = tab,
+ Tab = tab,
?match({atomic, ok}, mnesia:create_table(Tab, [{disc_copies,Ns}])),
OldRecs = [{Tab, K, -K} || K <- lists:seq(1, 5)],
[mnesia:dirty_write(R) || R <- OldRecs],
-
+
?match({ok, cp2, Ns}, mnesia:activate_checkpoint([{name, cp2},{max,mnesia:system_info(tables)}])),
File1 = "cp1_delete_me.BUP",
?match(ok, mnesia:dirty_write({Tab,6,-6})),
@@ -742,16 +742,16 @@ sops_with_checkpoint(Config) when is_list(Config) ->
?match(ok, mnesia:dirty_write({Tab,7,-7})),
File2 = "cp2_delete_me.BUP",
?match(ok, mnesia:backup_checkpoint(cp2, File2)),
-
+
?match(ok, mnesia:deactivate_checkpoint(cp1)),
?match(ok, mnesia:backup_checkpoint(cp2, File1)),
?match(ok, mnesia:dirty_write({Tab,8,-8})),
-
+
?match({atomic,ok}, mnesia:delete_table(Tab)),
?match({error,_}, mnesia:backup_checkpoint(cp2, File2)),
?match({'EXIT',_}, mnesia:dirty_write({Tab,9,-9})),
- ?match({atomic,_}, mnesia:restore(File1, [{default_op, recreate_tables}])),
+ ?match({atomic,_}, mnesia:restore(File1, [{default_op, recreate_tables}])),
Test = fun(N) when N > 5 -> ?error("To many records in backup ~p ~n", [N]);
(N) -> case mnesia:dirty_read(Tab,N) of
[{Tab,N,B}] when -B =:= N -> ok;
@@ -759,8 +759,29 @@ sops_with_checkpoint(Config) when is_list(Config) ->
end
end,
[Test(N) || N <- mnesia:dirty_all_keys(Tab)],
- ?match({aborted,enoent}, mnesia:restore(File2, [{default_op, recreate_tables}])),
-
+ ?match({aborted,enoent}, mnesia:restore(File2, [{default_op, recreate_tables}])),
+
+ %% Mnesia crashes when deleting a table during backup
+ ?match([], mnesia_test_lib:stop_mnesia([N2])),
+ Tab2 = ram,
+ ?match({atomic, ok}, mnesia:create_table(Tab2, [{ram_copies,[N1]}])),
+ ?match({ok, cp3, _}, mnesia:activate_checkpoint([{name, cp3},
+ {ram_overrides_dump,true},
+ {min,[Tab2]}])),
+ Write = fun Loop (N) ->
+ case N > 0 of
+ true ->
+ mnesia:dirty_write({Tab2, N+100, N+100}),
+ Loop(N-1);
+ false ->
+ ok
+ end
+ end,
+ ok = Write(100000),
+ spawn_link(fun() -> ?match({atomic, ok},mnesia:delete_table(Tab2)) end),
+
+ %% We don't check result here, depends on timing of above call
+ mnesia:backup_checkpoint(cp3, File2),
file:delete(File1), file:delete(File2),
- ?verify_mnesia(Ns, []).
+ ?verify_mnesia([N1], [N2]).
diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl
index 6e84a27ec9..0fabdc7929 100644
--- a/lib/mnesia/test/mnesia_test_lib.erl
+++ b/lib/mnesia/test/mnesia_test_lib.erl
@@ -263,6 +263,7 @@ slave_start_link(Host, Name, Retries) ->
Path = code:get_path(),
ok = rpc:call(NewNode, file, set_cwd, [Cwd]),
true = rpc:call(NewNode, code, set_path, [Path]),
+ ok = rpc:call(NewNode, error_logger, tty, [false]),
spawn_link(NewNode, ?MODULE, slave_sup, []),
rpc:multicall([node() | nodes()], global, sync, []),
{ok, NewNode};
diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl
index aa50ee4cb1..4ed73ea859 100644
--- a/lib/mnesia/test/mnesia_trans_access_test.erl
+++ b/lib/mnesia/test/mnesia_trans_access_test.erl
@@ -307,6 +307,7 @@ select14(Config) when is_list(Config) ->
%% Some Helpers
Trans = fun(Fun) -> mnesia:transaction(Fun) end,
+ Dirty = fun(Fun) -> mnesia:async_dirty(Fun) end,
LoopHelp = fun('$end_of_table',_) -> [];
({Recs,Cont},Fun) ->
Sel = mnesia:select(Cont),
@@ -334,8 +335,13 @@ select14(Config) when is_list(Config) ->
?match({atomic, [OneRec]}, Trans(fun() -> Loop(Tab, OnePat) end)),
?match({atomic, All}, Trans(fun() -> Loop(Tab, AllPat) end)),
- {atomic,{_, Cont}} = Trans(fun() -> mnesia:select(Tab, OnePat, 1, read) end),
- ?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(Cont) end)),
+ {atomic,{_, ContOne}} = Trans(fun() -> mnesia:select(Tab, OnePat, 1, read) end),
+ ?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(ContOne) end)),
+ ?match('$end_of_table', Dirty(fun() -> mnesia:select(ContOne) end)),
+
+ {atomic,{_, ContAll}} = Trans(fun() -> mnesia:select(Tab, AllPat, 1, read) end),
+ ?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(ContAll) end)),
+ ?match({[_], _}, Dirty(fun() -> mnesia:select(ContAll) end)),
?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, {match, '$1', 2},1,read) end)),
?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, [{'_', [], '$1'}],1,read) end)),
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index fb4200f62d..b1e8568490 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.14
+MNESIA_VSN = 4.14.3.1
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index 505d0dcc89..79e2b2b9db 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -32,6 +32,134 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 2.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ etop erroneously reported the average scheduler
+ utilization since the tool was first started instead of
+ the scheduler utilization since last update. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-14090 Aux Id: seq13232 </p>
+ </item>
+ <item>
+ <p>
+ crashdump_viewer crashed when the 'Slogan' had more than
+ one line. This is now corrected.</p>
+ <p>
+ Own Id: OTP-14093 Aux Id: ERL-318 </p>
+ </item>
+ <item>
+ <p>
+ When clicking an HTML-link to a port before the port tab
+ has been opened for the first time, observer would crash
+ since port info is not initiated. This is now corrected.</p>
+ <p>
+ Own Id: OTP-14151 Aux Id: PR-1296 </p>
+ </item>
+ <item>
+ <p>The dialyzer and observer applications will now use a
+ portable way to find the home directory. That means that
+ there is no longer any need to manually set the HOME
+ environment variable on Windows.</p>
+ <p>
+ Own Id: OTP-14249 Aux Id: ERL-161 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Observer 2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The shell script (priv/bin/cdv) and bat file
+ (priv/bin/cdv.bat) which can be used for starting
+ crashdump_viewer both started a distributed erlang node.
+ This would cause any attempt at starting a second
+ instance of the crashdump_viewer to fail. To solve this
+ problem, cdv and cdv.bat now use non-distributed nodes
+ when starting the crashdump_viewer.</p>
+ <p>
+ Own Id: OTP-14010</p>
+ </item>
+ <item>
+ <p>
+ A bug caused the number of buckets to be shown in the
+ 'Objects' column, and the number of objects to be shown
+ in the 'Memory' column for ets table in crashdump_viewer.
+ This is now corrected.</p>
+ <p>
+ Own Id: OTP-14064</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add option <c>queue_size</c> to ttb:tracer/2. This sets
+ the maximum queue size for the IP trace driver which is
+ used when tracing to shell and/or <c>{local,File}</c>.</p>
+ <p>
+ The default value for <c>queue_size</c> is specified by
+ <c>dbg</c>, and it is now changed from 50 to 200.</p>
+ <p>
+ Own Id: OTP-13829 Aux Id: seq13171 </p>
+ </item>
+ <item>
+ <p>
+ The port information page is updated to show more
+ information per port.</p>
+ <p>
+ Own Id: OTP-13948 Aux Id: ERL-272 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Observer 2.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed error handling in observer when mnesia tables was
+ requested and not available.</p>
+ <p>
+ Own Id: OTP-13845 Aux Id: ERL-237 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Observer 2.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a crash happening when observing another node, who
+ have a different number of schedulers than the current
+ one.</p>
+ <p>
+ Own Id: OTP-13702 Aux Id: ERL-171 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 2.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml
index 94ecef24b4..7cd15e15d3 100644
--- a/lib/observer/doc/src/ttb.xml
+++ b/lib/observer/doc/src/ttb.xml
@@ -114,7 +114,8 @@ ttb:p(all, call).</input></pre>
<v>Opt = {file,Client} | {handler, FormatHandler} | {process_info,PI} |
shell | {shell, ShellSpec} | {timer, TimerSpec} |
{overload_check, {MSec, Module, Function}} |
- {flush, MSec} | resume | {resume, FetchTimeout}</v>
+ {flush, MSec} | resume | {resume, FetchTimeout} |
+ {queue_size, QueueSize}</v>
<v>TimerSpec = MSec | {MSec, StopOpts}</v>
<v>MSec = FetchTimeout = integer()</v>
<v>Module = Function = atom() </v>
@@ -126,6 +127,7 @@ ttb:p(all, call).</input></pre>
<v>FormatHandler = See format/2</v>
<v>PI = true | false </v>
<v>ShellSpec = true | false | only</v>
+ <v>QueueSize = non_neg_integer()</v>
</type>
<desc>
<p>Starts a file trace port on all specified nodes
@@ -147,6 +149,18 @@ ttb:p(all, call).</input></pre>
<c>Client</c> must be <c>{local, File}</c>. All
trace information is then sent to the trace control node where
it is written to file.</p></item>
+ <tag><c>queue_size</c></tag>
+ <item><p>When tracing to shell or <c>{local,File}</c>, an ip
+ trace driver is used internally. The ip trace driver has a
+ queue of maximum <c>QueueSize</c> messages waiting to be
+ delivered. If the driver cannot deliver messages as fast as
+ they are produced, the queue size might be exceeded and
+ messages are dropped. This parameter is optional, and is
+ only useful if many <c>{drop,N}</c> trace messages are
+ received by the trace handler. It has no meaning if shell
+ or <c>{local,File}</c> is not used. See
+ <seealso marker="runtime_tools:dbg#trace_port/2">dbg:trace_port/2</seealso>
+ for more information about the ip trace driver.</p></item>
<tag><c>process_info</c></tag>
<item><p>Indicates if process
information is to be collected. If <c>PI = true</c> (which is
@@ -485,7 +499,7 @@ ttb:p(all, call).</input></pre>
<p>For a description of the <c>match_spec()</c> syntax,
see section
<seealso marker="erts:match_spec"><c>Match Specifications in Erlang</c></seealso>
- in <c>ERTS</c>, which explains the general match specification "language".
+ in ERTS, which explains the general match specification "language".
</p>
<note>
<p>The <em>system tracer</em> for sequential tracing is
diff --git a/lib/observer/priv/bin/cdv b/lib/observer/priv/bin/cdv
index 1c44785ac2..d14fd47e41 100755
--- a/lib/observer/priv/bin/cdv
+++ b/lib/observer/priv/bin/cdv
@@ -1,4 +1,4 @@
#!/bin/sh
-erl -sname cdv -noinput -s crashdump_viewer script_start $@
+erl -noinput -s crashdump_viewer script_start $@
diff --git a/lib/observer/priv/bin/cdv.bat b/lib/observer/priv/bin/cdv.bat
index efa8bf8687..18136a30d6 100644
--- a/lib/observer/priv/bin/cdv.bat
+++ b/lib/observer/priv/bin/cdv.bat
@@ -1,2 +1,2 @@
@ECHO OFF
-CALL werl -sname cdv -s crashdump_viewer script_start %*
+CALL werl -s crashdump_viewer script_start %*
diff --git a/lib/observer/priv/crashdump_viewer.tool b/lib/observer/priv/crashdump_viewer.tool
deleted file mode 100644
index b6bd6bbdef..0000000000
--- a/lib/observer/priv/crashdump_viewer.tool
+++ /dev/null
@@ -1,2 +0,0 @@
-{version,"1.2"}.
-[{config_func,{crashdump_viewer,configData,[]}}].
diff --git a/lib/observer/priv/crashdump_viewer/collapsd.gif b/lib/observer/priv/crashdump_viewer/collapsd.gif
deleted file mode 100644
index 0b90c08a9a..0000000000
--- a/lib/observer/priv/crashdump_viewer/collapsd.gif
+++ /dev/null
Binary files differ
diff --git a/lib/observer/priv/crashdump_viewer/exploded.gif b/lib/observer/priv/crashdump_viewer/exploded.gif
deleted file mode 100644
index e3ab5ca2e9..0000000000
--- a/lib/observer/priv/crashdump_viewer/exploded.gif
+++ /dev/null
Binary files differ
diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile
index dd7831fa2b..ff2bcbdb99 100644
--- a/lib/observer/src/Makefile
+++ b/lib/observer/src/Makefile
@@ -92,7 +92,7 @@ EXAMPLE_FILES= multitrace.erl
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
PRIVDIR= ../priv
-WEBTOOLFILES= $(PRIVDIR)/crashdump_viewer.tool $(PRIVDIR)/erlang_observer.png
+PNGFILES= $(PRIVDIR)/erlang_observer.png
BINDIR= $(PRIVDIR)/bin
ifeq ($(findstring win32,$(TARGET)),win32)
WIN32_EXECUTABLES= $(BINDIR)/etop.bat $(BINDIR)/cdv.bat
@@ -103,10 +103,6 @@ EXECUTABLES= \
$(BINDIR)/etop \
$(BINDIR)/cdv \
$(WIN32_EXECUTABLES)
-CDVDIR= $(PRIVDIR)/crashdump_viewer
-GIF_FILES= \
- $(CDVDIR)/collapsd.gif \
- $(CDVDIR)/exploded.gif
APP_FILE= observer.app
@@ -163,9 +159,7 @@ release_spec: opt
$(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin"
$(INSTALL_DIR) "$(RELSYSDIR)/priv/bin"
$(INSTALL_SCRIPT) $(EXECUTABLES) "$(RELSYSDIR)/priv/bin"
- $(INSTALL_DIR) "$(RELSYSDIR)/priv/crashdump_viewer"
- $(INSTALL_DATA) $(WEBTOOLFILES) "$(RELSYSDIR)/priv"
- $(INSTALL_DATA) $(GIF_FILES) "$(RELSYSDIR)/priv/crashdump_viewer"
+ $(INSTALL_DATA) $(PNGFILES) "$(RELSYSDIR)/priv"
release_docs_spec:
diff --git a/lib/observer/src/cdv_bin_cb.erl b/lib/observer/src/cdv_bin_cb.erl
index 0cea1fdcf0..5472d36a6f 100644
--- a/lib/observer/src/cdv_bin_cb.erl
+++ b/lib/observer/src/cdv_bin_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. 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.
@@ -58,7 +58,7 @@ binary_to_term_fun(Bin) ->
try binary_to_term(Bin) of
Term -> plain_html(io_lib:format("~p",[Term]))
catch error:badarg ->
- Warning = "This binary can not be coverted to an Erlang term",
+ Warning = "This binary can not be converted to an Erlang term",
observer_html_lib:warning(Warning)
end
end.
diff --git a/lib/observer/src/cdv_detail_wx.erl b/lib/observer/src/cdv_detail_wx.erl
index 44f121f359..27057fd27f 100644
--- a/lib/observer/src/cdv_detail_wx.erl
+++ b/lib/observer/src/cdv_detail_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. 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.
@@ -55,7 +55,7 @@ init([Id, Data, ParentFrame, Callback, Parent]) ->
end,
{stop,normal};
{info,Info} ->
- observer_lib:display_info_dialog(Info),
+ observer_lib:display_info_dialog(ParentFrame,Info),
{stop,normal}
end.
diff --git a/lib/observer/src/cdv_ets_cb.erl b/lib/observer/src/cdv_ets_cb.erl
index 52a90b093b..ddd2d42df6 100644
--- a/lib/observer/src/cdv_ets_cb.erl
+++ b/lib/observer/src/cdv_ets_cb.erl
@@ -34,10 +34,8 @@
-define(COL_NAME, ?COL_ID+1).
-define(COL_SLOT, ?COL_NAME+1).
-define(COL_OWNER, ?COL_SLOT+1).
--define(COL_BUCK, ?COL_OWNER+1).
--define(COL_OBJ, ?COL_BUCK+1).
+-define(COL_OBJ, ?COL_OWNER+1).
-define(COL_MEM, ?COL_OBJ+1).
--define(COL_TYPE, ?COL_MEM+1).
%% Callbacks for cdv_virtual_list_wx
col_to_elem(id) -> col_to_elem(?COL_ID);
@@ -45,8 +43,6 @@ col_to_elem(?COL_ID) -> #ets_table.id;
col_to_elem(?COL_NAME) -> #ets_table.name;
col_to_elem(?COL_SLOT) -> #ets_table.slot;
col_to_elem(?COL_OWNER) -> #ets_table.pid;
-col_to_elem(?COL_TYPE) -> #ets_table.data_type;
-col_to_elem(?COL_BUCK) -> #ets_table.buckets;
col_to_elem(?COL_OBJ) -> #ets_table.size;
col_to_elem(?COL_MEM) -> #ets_table.memory.
@@ -57,7 +53,6 @@ col_spec() ->
{"Owner", ?wxLIST_FORMAT_CENTRE, 120},
{"Objects", ?wxLIST_FORMAT_RIGHT, 80},
{"Memory", ?wxLIST_FORMAT_RIGHT, 80}
-% {"Type", ?wxLIST_FORMAT_LEFT, 50}
].
get_info(Owner) ->
diff --git a/lib/observer/src/cdv_mem_cb.erl b/lib/observer/src/cdv_mem_cb.erl
index ba972d6963..abeddc7335 100644
--- a/lib/observer/src/cdv_mem_cb.erl
+++ b/lib/observer/src/cdv_mem_cb.erl
@@ -77,6 +77,10 @@ fix_alloc([{Title,Columns,Data}|Tables]) ->
fix_alloc(Tables)];
fix_alloc([{Title,[{_,V}|_]=Data}|Tables]) ->
fix_alloc([{Title,lists:duplicate(length(V),[]),Data}|Tables]);
+fix_alloc([{"",[]}|Tables]) -> % no name and no data, probably truncated dump
+ fix_alloc(Tables);
+fix_alloc([{Title,[]=Data}|Tables]) -> % no data, probably truncated dump
+ fix_alloc([{Title,[],Data}|Tables]);
fix_alloc([]) ->
[].
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index 9268dac180..13e73f027d 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -90,6 +90,7 @@
%% All possible tags - use macros in order to avoid misspelling in the code
+-define(abort,abort).
-define(allocated_areas,allocated_areas).
-define(allocator,allocator).
-define(atoms,atoms).
@@ -321,8 +322,16 @@ handle_call(general_info,_From,State=#state{file=File}) ->
NumAtoms = GenInfo#general_info.num_atoms,
WS = parse_vsn_str(GenInfo#general_info.system_vsn,4),
TW = case get(truncated) of
- true -> ["WARNING: The crash dump is truncated. "
- "Some information might be missing."];
+ true ->
+ case get(truncated_reason) of
+ undefined ->
+ ["WARNING: The crash dump is truncated. "
+ "Some information might be missing."];
+ Reason ->
+ ["WARNING: The crash dump is truncated "
+ "("++Reason++"). "
+ "Some information might be missing."]
+ end;
false -> []
end,
ets:insert(cdv_reg_proc_table,
@@ -515,8 +524,15 @@ truncated_warning([Tag|Tags]) ->
false -> truncated_warning(Tags)
end.
truncated_warning() ->
- ["WARNING: The crash dump is truncated here. "
- "Some information might be missing."].
+ case get(truncated_reason) of
+ undefined ->
+ ["WARNING: The crash dump is truncated here. "
+ "Some information might be missing."];
+ Reason ->
+ ["WARNING: The crash dump is truncated here "
+ "("++Reason++"). "
+ "Some information might be missing."]
+ end.
truncated_here(Tag) ->
case get(truncated) of
@@ -692,6 +708,7 @@ val(Fd, NoExist) ->
{eof,[]} -> NoExist;
[] -> NoExist;
{eof,Val} -> Val;
+ "=abort:"++_ -> NoExist;
Val -> Val
end.
@@ -787,7 +804,7 @@ do_read_file(File) ->
?erl_crash_dump ->
reset_index_table(),
insert_index(Tag,Id,N1+1),
- put(last_tag,{Tag,""}),
+ put_last_tag(Tag,""),
indexify(Fd,Rest,N1),
end_progress(),
check_if_truncated(),
@@ -831,7 +848,7 @@ indexify(Fd,Bin,N) ->
<<_:Pos/binary,TagAndRest/binary>> = Bin,
{Tag,Id,Rest,N1} = tag(Fd,TagAndRest,N+Pos),
insert_index(Tag,Id,N1+1), % +1 to get past newline
- put(last_tag,{Tag,Id}),
+ put_last_tag(Tag,Id),
indexify(Fd,Rest,N1);
nomatch ->
case progress_read(Fd) of
@@ -911,7 +928,10 @@ general_info(File) ->
WholeLine -> WholeLine
end,
- GI = get_general_info(Fd,#general_info{created=Created}),
+ {Slogan,SysVsn} = get_slogan_and_sysvsn(Fd,[]),
+ GI = get_general_info(Fd,#general_info{created=Created,
+ slogan=Slogan,
+ system_vsn=SysVsn}),
{MemTot,MemMax} =
case lookup_index(?memory) of
@@ -965,12 +985,20 @@ general_info(File) ->
mem_max=MemMax,
instr_info=InstrInfo}.
+get_slogan_and_sysvsn(Fd,Acc) ->
+ case val(Fd,eof) of
+ "Slogan: " ++ SloganPart when Acc==[] ->
+ get_slogan_and_sysvsn(Fd,[SloganPart]);
+ "System version: " ++ SystemVsn ->
+ {lists:append(lists:reverse(Acc)),SystemVsn};
+ eof ->
+ {lists:append(lists:reverse(Acc)),"-1"};
+ SloganPart ->
+ get_slogan_and_sysvsn(Fd,[[$\n|SloganPart]|Acc])
+ end.
+
get_general_info(Fd,GenInfo) ->
case line_head(Fd) of
- "Slogan" ->
- get_general_info(Fd,GenInfo#general_info{slogan=val(Fd)});
- "System version" ->
- get_general_info(Fd,GenInfo#general_info{system_vsn=val(Fd)});
"Compiled" ->
get_general_info(Fd,GenInfo#general_info{compile_time=val(Fd)});
"Taints" ->
@@ -1174,7 +1202,11 @@ parse_link_list("{from,"++Str,Links,Monitors,MonitoredBy) ->
parse_link_list(", "++Rest,Links,Monitors,MonitoredBy) ->
parse_link_list(Rest,Links,Monitors,MonitoredBy);
parse_link_list([],Links,Monitors,MonitoredBy) ->
- {lists:reverse(Links),lists:reverse(Monitors),lists:reverse(MonitoredBy)}.
+ {lists:reverse(Links),lists:reverse(Monitors),lists:reverse(MonitoredBy)};
+parse_link_list(Unexpected,Links,Monitors,MonitoredBy) ->
+ io:format("WARNING: found unexpected data in link list:~n~s~n",[Unexpected]),
+ parse_link_list([],Links,Monitors,MonitoredBy).
+
parse_port(Str) ->
{Port,Rest} = parse_link(Str,[]),
@@ -1813,16 +1845,16 @@ main_modinfo(_Fd,LM,_LineHead) ->
all_modinfo(Fd,LM,LineHead) ->
case LineHead of
"Current attributes" ->
- Str = hex_to_str(val(Fd)),
+ Str = hex_to_str(val(Fd,"")),
LM#loaded_mod{current_attrib=Str};
"Current compilation info" ->
- Str = hex_to_str(val(Fd)),
+ Str = hex_to_str(val(Fd,"")),
LM#loaded_mod{current_comp_info=Str};
"Old attributes" ->
- Str = hex_to_str(val(Fd)),
+ Str = hex_to_str(val(Fd,"")),
LM#loaded_mod{old_attrib=Str};
"Old compilation info" ->
- Str = hex_to_str(val(Fd)),
+ Str = hex_to_str(val(Fd,"")),
LM#loaded_mod{old_comp_info=Str};
Other ->
unexpected(Fd,Other,"loaded modules info"),
@@ -1848,7 +1880,12 @@ hex_to_term([],Acc) ->
Bin};
Term ->
Term
- end.
+ end;
+hex_to_term(Rest,Acc) ->
+ {"WARNING: The term is probably truncated!",
+ "I can not convert hex to term.",
+ Rest,list_to_binary(lists:reverse(Acc))}.
+
hex_to_dec("F") -> 15;
hex_to_dec("E") -> 14;
@@ -2159,7 +2196,8 @@ sort_allocator_types([{Name,Data}|Allocators],Acc,DoTotal) ->
Type =
case string:tokens(Name,"[]") of
[T,_Id] -> T;
- [Name] -> Name
+ [Name] -> Name;
+ Other -> Other
end,
TypeData = proplists:get_value(Type,Acc,[]),
{NewTypeData,NewDoTotal} = sort_type_data(Type,Data,TypeData,DoTotal),
@@ -2686,6 +2724,7 @@ count_index(Tag) ->
%%-----------------------------------------------------------------
%% Convert tags read from crashdump to atoms used as first part of key
%% in cdv_dump_index_table
+tag_to_atom("abort") -> ?abort;
tag_to_atom("allocated_areas") -> ?allocated_areas;
tag_to_atom("allocator") -> ?allocator;
tag_to_atom("atoms") -> ?atoms;
@@ -2720,6 +2759,14 @@ tag_to_atom(UnknownTag) ->
list_to_atom(UnknownTag).
%%%-----------------------------------------------------------------
+%%% Store last tag for use when truncated, and reason if aborted
+put_last_tag(?abort,Reason) ->
+ %% Don't overwrite the real last tag
+ put(truncated_reason,Reason);
+put_last_tag(Tag,Id) ->
+ put(last_tag,{Tag,Id}).
+
+%%%-----------------------------------------------------------------
%%% Fetch next chunk from crashdump file
lookup_and_parse_index(File,What,ParseFun,Str) when is_list(File) ->
Indices = lookup_index(What),
@@ -2815,7 +2862,16 @@ collect(Pids,Acc) ->
update_progress(),
collect(Pids,Acc);
{'DOWN', _Ref, process, Pid, {pmap_done,Result}} ->
- collect(lists:delete(Pid,Pids),[Result|Acc])
+ collect(lists:delete(Pid,Pids),[Result|Acc]);
+ {'DOWN', _Ref, process, Pid, _Error} ->
+ Warning =
+ "WARNING: an error occured while parsing data.\n" ++
+ case get(truncated) of
+ true -> "This might be because the dump is truncated.\n";
+ false -> ""
+ end,
+ io:format(Warning),
+ collect(lists:delete(Pid,Pids),Acc)
end.
%%%-----------------------------------------------------------------
diff --git a/lib/observer/src/etop.erl b/lib/observer/src/etop.erl
index fcb900960b..925f4456bb 100644
--- a/lib/observer/src/etop.erl
+++ b/lib/observer/src/etop.erl
@@ -23,7 +23,7 @@
-export([start/0, start/1, config/2, stop/0, dump/1, help/0]).
%% Internal
-export([update/1]).
--export([loadinfo/1, meminfo/2, getopt/2]).
+-export([loadinfo/2, meminfo/2, getopt/2]).
-include("etop.hrl").
-include("etop_defs.hrl").
@@ -319,18 +319,18 @@ output(graphical) -> exit({deprecated, "Use observer instead"});
output(text) -> etop_txt.
-loadinfo(SysI) ->
+loadinfo(SysI,Prev) ->
#etop_info{n_procs = Procs,
run_queue = RQ,
now = Now,
wall_clock = WC,
runtime = RT} = SysI,
- Cpu = calculate_cpu_utilization(WC,RT),
+ Cpu = calculate_cpu_utilization(WC,RT,Prev#etop_info.runtime),
Clock = io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w",
tuple_to_list(element(2,calendar:now_to_datetime(Now)))),
{Cpu,Procs,RQ,Clock}.
-calculate_cpu_utilization({_,WC},{_,RT}) ->
+calculate_cpu_utilization({_,WC},{_,RT},_) ->
%% Old version of observer_backend, using statistics(wall_clock)
%% and statistics(runtime)
case {WC,RT} of
@@ -341,15 +341,23 @@ calculate_cpu_utilization({_,WC},{_,RT}) ->
_ ->
round(100*RT/WC)
end;
-calculate_cpu_utilization(_,undefined) ->
+calculate_cpu_utilization(_,undefined,_) ->
%% First time collecting - no cpu utilization has been measured
%% since scheduler_wall_time flag is not yet on
0;
-calculate_cpu_utilization(_,RTInfo) ->
+calculate_cpu_utilization(WC,RTInfo,undefined) ->
+ %% Second time collecting - RTInfo shows scheduler_wall_time since
+ %% flag was set to true. Faking previous values by setting
+ %% everything to zero.
+ ZeroRT = [{Id,0,0} || {Id,_,_} <- RTInfo],
+ calculate_cpu_utilization(WC,RTInfo,ZeroRT);
+calculate_cpu_utilization(_,RTInfo,PrevRTInfo) ->
%% New version of observer_backend, using statistics(scheduler_wall_time)
- Sum = lists:foldl(fun({_,A,T},{AAcc,TAcc}) -> {A+AAcc,T+TAcc} end,
+ Sum = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}},{AAcc,TAcc}) ->
+ {(A1 - A0)+AAcc,(T1 - T0)+TAcc}
+ end,
{0,0},
- RTInfo),
+ lists:zip(PrevRTInfo,RTInfo)),
case Sum of
{0,0} ->
0;
diff --git a/lib/observer/src/etop_txt.erl b/lib/observer/src/etop_txt.erl
index 3b4c176478..6b8f9df24f 100644
--- a/lib/observer/src/etop_txt.erl
+++ b/lib/observer/src/etop_txt.erl
@@ -22,35 +22,35 @@
%%-compile(export_all).
-export([init/1,stop/1]).
--export([do_update/3]).
+-export([do_update/4]).
-include("etop.hrl").
-include("etop_defs.hrl").
--import(etop,[loadinfo/1,meminfo/2]).
+-import(etop,[loadinfo/2,meminfo/2]).
-define(PROCFORM,"~-15w~-20s~8w~8w~8w~8w ~-20s~n").
stop(Pid) -> Pid ! stop.
init(Config) ->
- loop(Config).
+ loop(#etop_info{},Config).
-loop(Config) ->
- Info = do_update(Config),
+loop(Prev,Config) ->
+ Info = do_update(Prev,Config),
receive
stop -> stopped;
- {dump,Fd} -> do_update(Fd,Info,Config), loop(Config);
- {config,_,Config1} -> loop(Config1)
- after Config#opts.intv -> loop(Config)
+ {dump,Fd} -> do_update(Fd,Info,Prev,Config), loop(Info,Config);
+ {config,_,Config1} -> loop(Info,Config1)
+ after Config#opts.intv -> loop(Info,Config)
end.
-do_update(Config) ->
+do_update(Prev,Config) ->
Info = etop:update(Config),
- do_update(standard_io,Info,Config).
+ do_update(standard_io,Info,Prev,Config).
-do_update(Fd,Info,Config) ->
- {Cpu,NProcs,RQ,Clock} = loadinfo(Info),
+do_update(Fd,Info,Prev,Config) ->
+ {Cpu,NProcs,RQ,Clock} = loadinfo(Info,Prev),
io:nl(Fd),
writedoubleline(Fd),
case Info#etop_info.memi of
diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl
index 77609b11ce..ca54080e15 100644
--- a/lib/observer/src/observer_alloc_wx.erl
+++ b/lib/observer/src/observer_alloc_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-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.
diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl
index 936b2783e2..d41cb8ca42 100644
--- a/lib/observer/src/observer_app_wx.erl
+++ b/lib/observer/src/observer_app_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. 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.
@@ -191,8 +191,8 @@ handle_event(#wx{event=#wxMouse{type=Type, x=X0, y=Y0}},
end;
handle_event(#wx{event=#wxCommand{type=command_menu_selected}},
- State = #state{sel=undefined}) ->
- observer_lib:display_info_dialog("Select process first"),
+ State = #state{panel=Panel,sel=undefined}) ->
+ observer_lib:display_info_dialog(Panel,"Select process first"),
{noreply, State};
handle_event(#wx{id=?ID_PROC_INFO, event=#wxCommand{type=command_menu_selected}},
@@ -205,7 +205,7 @@ handle_event(#wx{id=?ID_PROC_MSG, event=#wxCommand{type=command_menu_selected}},
case observer_lib:user_term(Panel, "Enter message", "") of
cancel -> ok;
{ok, Term} -> Pid ! Term;
- {error, Error} -> observer_lib:display_info_dialog(Error)
+ {error, Error} -> observer_lib:display_info_dialog(Panel,Error)
end,
{noreply, State};
@@ -214,7 +214,7 @@ handle_event(#wx{id=?ID_PROC_KILL, event=#wxCommand{type=command_menu_selected}}
case observer_lib:user_term(Panel, "Enter Exit Reason", "kill") of
cancel -> ok;
{ok, Term} -> exit(Pid, Term);
- {error, Error} -> observer_lib:display_info_dialog(Error)
+ {error, Error} -> observer_lib:display_info_dialog(Panel,Error)
end,
{noreply, State};
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 7c1337025f..7ce4cf45c7 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. 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.
@@ -20,12 +20,12 @@
-module(observer_lib).
-export([get_wx_parent/1,
- display_info_dialog/1, display_yes_no_dialog/1,
+ display_info_dialog/2, display_yes_no_dialog/1,
display_progress_dialog/2, destroy_progress_dialog/0,
wait_for_progress/0, report_progress/1,
user_term/3, user_term_multiline/3,
interval_dialog/4, start_timer/1, stop_timer/1,
- display_info/2, fill_info/2, update_info/2, to_str/1,
+ display_info/2, display_info/3, fill_info/2, update_info/2, to_str/1,
create_menus/3, create_menu_item/3,
create_attrs/0,
set_listctrl_col_size/2,
@@ -105,10 +105,10 @@ setup_timer(Bool, {Timer, Old}) ->
timer:cancel(Timer),
setup_timer(Bool, {false, Old}).
-display_info_dialog(Str) ->
- display_info_dialog("",Str).
-display_info_dialog(Title,Str) ->
- Dlg = wxMessageDialog:new(wx:null(), Str, [{caption,Title}]),
+display_info_dialog(Parent,Str) ->
+ display_info_dialog(Parent,"",Str).
+display_info_dialog(Parent,Title,Str) ->
+ Dlg = wxMessageDialog:new(Parent, Str, [{caption,Title}]),
wxMessageDialog:showModal(Dlg),
wxMessageDialog:destroy(Dlg),
ok.
@@ -124,6 +124,11 @@ display_info(Frame, Info) ->
Panel = wxPanel:new(Frame),
wxWindow:setBackgroundStyle(Panel, ?wxBG_STYLE_SYSTEM),
Sizer = wxBoxSizer:new(?wxVERTICAL),
+ InfoFs = display_info(Panel, Sizer, Info),
+ wxWindow:setSizerAndFit(Panel, Sizer),
+ {Panel, Sizer, InfoFs}.
+
+display_info(Panel, Sizer, Info) ->
wxSizer:addSpacer(Sizer, 5),
Add = fun(BoxInfo) ->
case create_box(Panel, BoxInfo) of
@@ -136,9 +141,7 @@ display_info(Frame, Info) ->
[]
end
end,
- InfoFs = [Add(I) || I <- Info],
- wxWindow:setSizerAndFit(Panel, Sizer),
- {Panel, Sizer, InfoFs}.
+ [Add(I) || I <- Info].
fill_info([{dynamic, Key}|Rest], Data)
when is_atom(Key); is_function(Key) ->
@@ -254,6 +257,11 @@ to_str({func, {F,A}}) when is_atom(F), is_integer(A) ->
lists:concat([F, "/", A]);
to_str({func, {F,'_'}}) when is_atom(F) ->
atom_to_list(F);
+to_str({inet, Addr}) ->
+ case inet:ntoa(Addr) of
+ {error,einval} -> to_str(Addr);
+ AddrStr -> AddrStr
+ end;
to_str({{format,Fun},Value}) when is_function(Fun) ->
Fun(Value);
to_str({A, B}) when is_atom(A), is_atom(B) ->
@@ -453,14 +461,16 @@ create_box(Parent, Data) ->
link_entry(Panel,Value);
_ ->
Value = to_str(Value0),
- case length(Value) > 100 of
- true ->
- Shown = lists:sublist(Value, 80),
+ case string:sub_word(lists:sublist(Value, 80),1,$\n) of
+ Value ->
+ %% Short string, no newlines - show all
+ wxStaticText:new(Panel, ?wxID_ANY, Value);
+ Shown ->
+ %% Long or with newlines,
+ %% use tooltip to show all
TCtrl = wxStaticText:new(Panel, ?wxID_ANY, [Shown,"..."]),
wxWindow:setToolTip(TCtrl,wxToolTip:new(Value)),
- TCtrl;
- false ->
- wxStaticText:new(Panel, ?wxID_ANY, Value)
+ TCtrl
end
end,
wxSizer:add(Line, 10, 0), % space of size 10 horisontally
@@ -714,7 +724,7 @@ progress_loop(Title,PD,Caller) ->
if is_list(Reason) -> Reason;
true -> file:format_error(Reason)
end,
- display_info_dialog("Crashdump Viewer Error",FailMsg),
+ display_info_dialog(PD,"Crashdump Viewer Error",FailMsg),
Caller ! error,
unregister(?progress_handler),
unlink(Caller);
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index 1010a6af4c..b0ead42e3f 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -235,12 +235,14 @@ terminate(_Event, #state{appmon=Pid}) ->
code_change(_, _, State) ->
State.
-restart_fetcher(Node, #state{appmon=Old, panel=Panel, time=#ti{fetch=Freq}=Ti}=State) ->
+restart_fetcher(Node, #state{appmon=Old, panel=Panel, time=#ti{fetch=Freq}=Ti, wins=Wins0}=State) ->
catch Old ! exit,
Me = self(),
Pid = spawn_link(Node, observer_backend, fetch_stats, [Me, round(1000/Freq)]),
wxWindow:refresh(Panel),
- precalc(State#state{active=true, appmon=Pid, samples=reset_data(), time=Ti#ti{tick=0}}).
+ Wins = [W#win{state=undefined} || W <- Wins0],
+ precalc(State#state{active=true, appmon=Pid, samples=reset_data(),
+ wins=Wins, time=Ti#ti{tick=0}}).
reset_data() ->
{0, queue:new()}.
@@ -253,18 +255,25 @@ add_data(Stats, {N, Q}, Wins, _, Active) ->
add_data_1([#win{state={_,St}}|_]=Wins0, Last, N, {Drop, Q}, Active)
when St /= undefined ->
- {Wins, Stat} =
- lists:mapfoldl(fun(Win0, Entry) ->
- {Win1,Stat} = add_data_2(Win0, Last, Entry),
- case Active of
- true ->
- Win = add_data_3(Win1, N, Drop, Stat, Q),
- {Win, Stat};
- false ->
- {Win1, Stat}
- end
- end, #{}, Wins0),
- {Wins, {N,queue:in(Stat#{}, Q)}};
+ try
+ {Wins, Stat} =
+ lists:mapfoldl(fun(Win0, Entry) ->
+ {Win1,Stat} = add_data_2(Win0, Last, Entry),
+ case Active of
+ true ->
+ Win = add_data_3(Win1, N, Drop, Stat, Q),
+ {Win, Stat};
+ false ->
+ {Win1, Stat}
+ end
+ end, #{}, Wins0),
+ {Wins, {N,queue:in(Stat#{}, Q)}}
+ catch no_scheduler_change ->
+ {[Win#win{state=init_data(Id, Last),
+ info = info(Id, Last)}
+ || #win{name=Id}=Win <- Wins0], {0,queue:new()}}
+ end;
+
add_data_1(Wins, Stats, 1, {_, Q}, _) ->
{[Win#win{state=init_data(Id, Stats),
info = info(Id, Stats)}
@@ -374,8 +383,8 @@ lmax(MState, Values, State) ->
init_data(runq, {stats, _, T0, _, _}) -> {mk_max(),lists:sort(T0)};
init_data(io, {stats, _, _, {{_,In0}, {_,Out0}}, _}) -> {mk_max(), {In0,Out0}};
init_data(memory, _) -> {mk_max(), info(memory, undefined)};
-init_data(alloc, _) -> {mk_max(), ok};
-init_data(utilz, _) -> {mk_max(), ok}.
+init_data(alloc, _) -> {mk_max(), unused};
+init_data(utilz, _) -> {mk_max(), unused}.
info(runq, {stats, _, T0, _, _}) -> lists:seq(1, length(T0));
info(memory, _) -> [total, processes, atom, binary, code, ets];
@@ -401,15 +410,16 @@ collect_data(memory, {stats, _, _, _, MemInfo}, {Max, MemTypes}) ->
collect_data(alloc, MemInfo, Max) ->
Vs = [Carrier || {_Type,_Block,Carrier} <- MemInfo],
Sample = list_to_tuple(Vs),
- {Sample, lmax(Max, Vs, Sample)};
+ {Sample, {lmax(Max, Vs, Sample),unused}};
collect_data(utilz, MemInfo, Max) ->
Vs = [round(100*Block/Carrier) || {_Type,Block,Carrier} <- MemInfo],
Sample = list_to_tuple(Vs),
- {Sample, lmax(Max,Vs,Sample)}.
+ {Sample, {lmax(Max,Vs,Sample),unused}}.
calc_delta([{Id, WN, TN}|Ss], [{Id, WP, TP}|Ps]) ->
[100*(WN-WP) div (TN-TP)|calc_delta(Ss, Ps)];
-calc_delta([], []) -> [].
+calc_delta([], []) -> [];
+calc_delta(_, _) -> throw(no_scheduler_change).
precalc(#state{samples=Data0, paint=Paint, time=Ti, wins=Wins0}=State) ->
Wins = [precalc(Ti, Data0, Paint, Win) || Win <- Wins0],
diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl
index 3b788642cc..5b9e5c5671 100644
--- a/lib/observer/src/observer_port_wx.erl
+++ b/lib/observer/src/observer_port_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. 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.
@@ -52,7 +52,13 @@
slot,
id_str,
links,
- monitors}).
+ monitors,
+ monitored_by,
+ parallelism,
+ locking,
+ queue_size,
+ memory,
+ inet}).
-record(opt, {sort_key=2,
sort_incr=true
@@ -261,10 +267,19 @@ handle_cast(Event, _State) ->
error({unhandled_cast, Event}).
handle_info({portinfo_open, PortIdStr},
- State = #state{grid=Grid, ports=Ports, open_wins=Opened}) ->
- Port = lists:keyfind(PortIdStr,#port.id_str,Ports),
- NewOpened = display_port_info(Grid, Port, Opened),
- {noreply, State#state{open_wins = NewOpened}};
+ State = #state{node=Node, grid=Grid, opt=Opt, open_wins=Opened}) ->
+ Ports0 = get_ports(Node),
+ Ports = update_grid(Grid, Opt, Ports0),
+ Port = lists:keyfind(PortIdStr, #port.id_str, Ports),
+ NewOpened =
+ case Port of
+ false ->
+ self() ! {error,"No such port: " ++ PortIdStr},
+ Opened;
+ _ ->
+ display_port_info(Grid, Port, Opened)
+ end,
+ {noreply, State#state{ports=Ports, open_wins=NewOpened}};
handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt,
ports=OldPorts}) ->
@@ -290,8 +305,9 @@ handle_info(not_active, State = #state{timer = Timer0}) ->
Timer = observer_lib:stop_timer(Timer0),
{noreply, State#state{timer=Timer}};
-handle_info({error, Error}, State) ->
- handle_error(Error),
+handle_info({error, Error}, #state{panel=Panel} = State) ->
+ Str = io_lib:format("ERROR: ~s~n",[Error]),
+ observer_lib:display_info_dialog(Panel, Str),
{noreply, State};
handle_info(_Event, State) ->
@@ -358,7 +374,13 @@ list_to_portrec(PL) ->
links = proplists:get_value(links, PL, []),
name = proplists:get_value(registered_name, PL, []),
monitors = proplists:get_value(monitors, PL, []),
- controls = proplists:get_value(name, PL)}.
+ monitored_by = proplists:get_value(monitored_by, PL, []),
+ controls = proplists:get_value(name, PL),
+ parallelism = proplists:get_value(parallelism, PL),
+ locking = proplists:get_value(locking, PL),
+ queue_size = proplists:get_value(queue_size, PL, 0),
+ memory = proplists:get_value(memory, PL, 0),
+ inet = proplists:get_value(inet, PL, [])}.
portrec_to_list(#port{id = Id,
slot = Slot,
@@ -366,14 +388,26 @@ portrec_to_list(#port{id = Id,
links = Links,
name = Name,
monitors = Monitors,
- controls = Controls}) ->
+ monitored_by = MonitoredBy,
+ controls = Controls,
+ parallelism = Parallelism,
+ locking = Locking,
+ queue_size = QueueSize,
+ memory = Memory,
+ inet = Inet}) ->
[{id,Id},
{slot,Slot},
{connected,Connected},
{links,Links},
{name,Name},
{monitors,Monitors},
- {controls,Controls}].
+ {monitored_by,MonitoredBy},
+ {controls,Controls},
+ {parallelism,Parallelism},
+ {locking,Locking},
+ {queue_size,QueueSize},
+ {memory,Memory} |
+ Inet].
display_port_info(Parent, PortRec, Opened) ->
PortIdStr = PortRec#port.id_str,
@@ -391,40 +425,92 @@ do_display_port_info(Parent0, PortRec) ->
Title = "Port Info: " ++ PortRec#port.id_str,
Frame = wxMiniFrame:new(Parent, ?wxID_ANY, Title,
[{style, ?wxSYSTEM_MENU bor ?wxCAPTION
- bor ?wxCLOSE_BOX bor ?wxRESIZE_BORDER}]),
-
+ bor ?wxCLOSE_BOX bor ?wxRESIZE_BORDER},
+ {size,{600,400}}]),
+ ScrolledWin = wxScrolledWindow:new(Frame,[{style,?wxHSCROLL bor ?wxVSCROLL}]),
+ wxScrolledWindow:enableScrolling(ScrolledWin,true,true),
+ wxScrolledWindow:setScrollbars(ScrolledWin,20,20,0,0),
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ wxWindow:setSizer(ScrolledWin,Sizer),
Port = portrec_to_list(PortRec),
Fields0 = port_info_fields(Port),
- {_FPanel, _Sizer, _UpFields} = observer_lib:display_info(Frame, Fields0),
+ _UpFields = observer_lib:display_info(ScrolledWin, Sizer, Fields0),
wxFrame:center(Frame),
wxFrame:connect(Frame, close_window, [{skip, true}]),
wxFrame:show(Frame),
Frame.
-port_info_fields(Port) ->
+
+port_info_fields(Port0) ->
+ {InetStruct,Port} = inet_extra_fields(Port0),
Struct =
[{"Overview",
- [{"Name", name},
+ [{"Registered Name", name},
{"Connected", {click,connected}},
{"Slot", slot},
- {"Controls", controls}]},
+ {"Controls", controls},
+ {"Parallelism", parallelism},
+ {"Locking", locking},
+ {"Queue Size", {bytes,queue_size}},
+ {"Memory", {bytes,memory}}]},
{scroll_boxes,
[{"Links",1,{click,links}},
- {"Monitors",1,{click,filter_monitor_info()}}]}],
+ {"Monitors",1,{click,filter_monitor_info()}},
+ {"Monitored by",1,{click,monitored_by}}]} | InetStruct],
observer_lib:fill_info(Struct, Port).
+inet_extra_fields(Port) ->
+ Statistics = proplists:get_value(statistics,Port,[]),
+ Options = proplists:get_value(options,Port,[]),
+ Struct =
+ case proplists:get_value(controls,Port) of
+ Inet when Inet=="tcp_inet"; Inet=="udp_inet"; Inet=="sctp_inet" ->
+ [{"Inet",
+ [{"Local Address", {inet,local_address}},
+ {"Local Port Number", local_port},
+ {"Remote Address", {inet,remote_address}},
+ {"Remote Port Number", remote_port}]},
+ {"Statistics",
+ [stat_name_and_unit(Key) || {Key,_} <- Statistics]},
+ {"Options",
+ [{atom_to_list(Key),Key} || {Key,_} <- Options]}];
+ _ ->
+ []
+ end,
+ Port1 = lists:keydelete(statistics,1,Port),
+ Port2 = lists:keydelete(options,1,Port1),
+ {Struct,Port2 ++ Statistics ++ Options}.
+
+stat_name_and_unit(recv_avg) ->
+ {"Average package size received", {bytes,recv_avg}};
+stat_name_and_unit(recv_cnt) ->
+ {"Number of packets received", recv_cnt};
+stat_name_and_unit(recv_dvi) ->
+ {"Average packet size deviation received", {bytes,recv_dvi}};
+stat_name_and_unit(recv_max) ->
+ {"Largest packet received", {bytes,recv_max}};
+stat_name_and_unit(recv_oct) ->
+ {"Total received", {bytes,recv_oct}};
+stat_name_and_unit(send_avg) ->
+ {"Average packet size sent", {bytes, send_avg}};
+stat_name_and_unit(send_cnt) ->
+ {"Number of packets sent", send_cnt};
+stat_name_and_unit(send_max) ->
+ {"Largest packet sent", {bytes, send_max}};
+stat_name_and_unit(send_oct) ->
+ {"Total sent", {bytes, send_oct}};
+stat_name_and_unit(send_pend) ->
+ {"Data waiting to be sent from driver", {bytes,send_pend}};
+stat_name_and_unit(Key) ->
+ {atom_to_list(Key), Key}.
+
filter_monitor_info() ->
fun(Data) ->
Ms = proplists:get_value(monitors, Data),
[Pid || {process, Pid} <- Ms]
end.
-
-handle_error(Foo) ->
- Str = io_lib:format("ERROR: ~s~n",[Foo]),
- observer_lib:display_info_dialog(Str).
-
update_grid(Grid, Opt, Ports) ->
wx:batch(fun() -> update_grid2(Grid, Opt, Ports) end).
update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) ->
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index ee6829b847..f07b9e295a 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -511,7 +511,13 @@ table_holder(#holder{info=Info, attrs=Attrs,
table_holder(S0);
{dump, Fd} ->
EtopInfo = (S0#holder.etop)#etop_info{procinfo=array:to_list(Info)},
- etop_txt:do_update(Fd, EtopInfo, #opts{node=Node}),
+ %% The empty #etop_info{} below is a dummy previous info
+ %% value. It is used by etop to calculate the scheduler
+ %% utilization since last update. When dumping to file,
+ %% there is no previous measurement to use, so we just add
+ %% a dummy here, and the value shown will be since the
+ %% tool was started.
+ etop_txt:do_update(Fd, EtopInfo, #etop_info{}, #opts{node=Node}),
file:close(Fd),
table_holder(S0);
stop ->
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 620979dcc9..8d19d77488 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. 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.
@@ -92,7 +92,7 @@ init([Pid, ParentFrame, Parent]) ->
observer_wx:return_to_localnode(ParentFrame, node(Pid)),
{stop, badrpc};
process_undefined ->
- observer_lib:display_info_dialog("No such alive process"),
+ observer_lib:display_info_dialog(ParentFrame,"No such alive process"),
{stop, normal}
end.
@@ -434,7 +434,7 @@ get_gc_info(Arg) ->
filter_monitor_info() ->
fun(Data) ->
Ms = proplists:get_value(monitors, Data),
- [Pid || {process, Pid} <- Ms]
+ [Id || {_Type, Id} <- Ms] % Type is process or port
end.
stringify_bins(Data) ->
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index 59f6443551..bf2afee2a5 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. 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.
@@ -124,6 +124,8 @@ handle_event(#wx{id=Id}, State = #state{node=Node, grid=Grid, opt=Opt0})
end,
case get_tables2(Node, Opt) of
Error = {error, _} ->
+ Id =:= ?ID_MNESIA andalso
+ wxMenuBar:check(observer_wx:get_menubar(), ?ID_ETS, true),
self() ! Error,
{noreply, State};
Tables ->
@@ -217,22 +219,33 @@ handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt,
{noreply, State#state{tabs=Tabs}}
end;
-handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt,
+handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt0,
timer=Timer0}) ->
- Tables = get_tables(Node, Opt),
+ {Tables, Opt} = case Opt0#opt.type =:= mnesia andalso get_tables2(Node, Opt0) of
+ Ts when is_list(Ts) ->
+ {Ts, Opt0};
+ _ -> % false or error getting mnesia tables
+ Opt1 = Opt0#opt{type=ets},
+ {get_tables(Node, Opt1), Opt1}
+ end,
Tabs = update_grid(Grid, Opt, Tables),
wxWindow:setFocus(Grid),
create_menus(Parent, Opt),
Timer = observer_lib:start_timer(Timer0),
- {noreply, State#state{node=Node, tabs=Tabs, timer=Timer}};
+ {noreply, State#state{node=Node, tabs=Tabs, timer=Timer, opt=Opt}};
handle_info(not_active, State = #state{timer = Timer0}) ->
Timer = observer_lib:stop_timer(Timer0),
{noreply, State#state{timer=Timer}};
-handle_info({error, Error}, State) ->
- handle_error(Error),
- {noreply, State};
+handle_info({error, Error}, #state{panel=Panel,opt=Opt}=State) ->
+ Str = io_lib:format("ERROR: ~s~n",[Error]),
+ observer_lib:display_info_dialog(Panel,Str),
+ case Opt#opt.type of
+ mnesia -> wxMenuBar:check(observer_wx:get_menubar(), ?ID_ETS, true);
+ _ -> ok
+ end,
+ {noreply, State#state{opt=Opt#opt{type=ets}}};
handle_info(_Event, State) ->
{noreply, State}.
@@ -353,10 +366,6 @@ list_to_strings([A]) -> integer_to_list(A);
list_to_strings([A|B]) ->
integer_to_list(A) ++ " ," ++ list_to_strings(B).
-handle_error(Foo) ->
- Str = io_lib:format("ERROR: ~s~n",[Foo]),
- observer_lib:display_info_dialog(Str).
-
update_grid(Grid, Opt, Tables) ->
wx:batch(fun() -> update_grid2(Grid, Opt, Tables) end).
update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Tables) ->
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 301bb4b32f..738a00180f 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. 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.
@@ -21,7 +21,7 @@
-behaviour(wx_object).
-export([start/0, stop/0]).
--export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0,
+-export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0, get_menubar/0,
set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]).
-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3,
@@ -94,6 +94,9 @@ get_tracer() ->
get_active_node() ->
wx_object:call(observer, get_active_node).
+get_menubar() ->
+ wx_object:call(observer, get_menubar).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init(_Args) ->
@@ -391,6 +394,9 @@ handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) ->
handle_call(get_active_node, _From, State=#state{node=Node}) ->
{reply, Node, State};
+handle_call(get_menubar, _From, State=#state{menubar=MenuBar}) ->
+ {reply, MenuBar, State};
+
handle_call(stop, From, State) ->
stop_servers(State),
{noreply, State#state{reply_to=From}};
@@ -461,10 +467,10 @@ handle_info(_Info, State) ->
stop_servers(#state{node=Node, log=LogOn, sys_panel=Sys, pro_panel=Procs, tv_panel=TVs,
trace_panel=Trace, app_panel=Apps, perf_panel=Perfs,
- allc_panel=Alloc} = _State) ->
+ allc_panel=Alloc, port_panel=Ports} = _State) ->
LogOn andalso rpc:block_call(Node, rb, stop, []),
Me = self(),
- Tabs = [Sys, Procs, TVs, Trace, Apps, Perfs, Alloc],
+ Tabs = [Sys, Procs, Ports, TVs, Trace, Apps, Perfs, Alloc],
Stop = fun() ->
try
_ = [wx_object:stop(Panel) || Panel <- Tabs],
@@ -574,9 +580,10 @@ get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys,
pid2panel(Pid, #state{pro_panel=Pro, sys_panel=Sys,
tv_panel=Tv, trace_panel=Trace, app_panel=App,
- perf_panel=Perf, allc_panel=Alloc}) ->
+ perf_panel=Perf, allc_panel=Alloc, port_panel=Port}) ->
case Pid of
Pro -> "Processes";
+ Port -> "Ports";
Sys -> "System";
Tv -> "Table Viewer" ;
Trace -> ?TRACE_STR;
@@ -629,7 +636,8 @@ create_connect_dialog(connect, #state{frame = Frame}) ->
wxWindow:setSizerAndFit(Dialog, VSizer),
wxSizer:setSizeHints(VSizer, Dialog),
- CookiePath = filename:join(os:getenv("HOME"), ".erlang.cookie"),
+ {ok,[[HomeDir]]} = init:get_argument(home),
+ CookiePath = filename:join(HomeDir, ".erlang.cookie"),
DefaultCookie = case filelib:is_file(CookiePath) of
true ->
{ok, Bin} = file:read_file(CookiePath),
diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl
index ac6c4572eb..87a50e046b 100644
--- a/lib/observer/src/ttb.erl
+++ b/lib/observer/src/ttb.erl
@@ -78,6 +78,11 @@ do_tracer(Nodes0,PI,Client,Traci) ->
do_tracer(Clients,PI,Traci) ->
Shell = proplists:get_value(shell, Traci, false),
+ IpPortSpec =
+ case proplists:get_value(queue_size, Traci) of
+ undefined -> 0;
+ QS -> {0,QS}
+ end,
DefShell = fun(Trace) -> dbg:dhandler(Trace, standard_io) end,
{ClientSucc,Succ} =
lists:foldl(
@@ -98,7 +103,7 @@ do_tracer(Clients,PI,Traci) ->
[_,H] = string:tokens(atom_to_list(N),"@"),
H
end,
- case catch dbg:tracer(N,port,dbg:trace_port(ip,0)) of
+ case catch dbg:tracer(N,port,dbg:trace_port(ip,IpPortSpec)) of
{ok,N} ->
{ok,Port} = dbg:trace_port_control(N,get_listen_port),
{ok,T} = dbg:get_tracer(N),
@@ -160,6 +165,8 @@ opt([{resume,MSec}|O],{PI,Client,Traci}) ->
opt(O,{PI,Client,[{resume, {true, MSec}}|Traci]});
opt([{flush,MSec}|O],{PI,Client,Traci}) ->
opt(O,{PI,Client,[{flush, MSec}|Traci]});
+opt([{queue_size,QueueSize}|O],{PI,Client,Traci}) ->
+ opt(O,{PI,Client,[{queue_size,QueueSize}|Traci]});
opt([],Opt) ->
ensure_opt(Opt).
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 73ed890802..8df69c6624 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -420,6 +420,10 @@ special(File,Procs) ->
%% ".trunc" ->
%% %% ????
%% ok;
+ ".trunc.bytes" ->
+ {ok,_,[TW]} = crashdump_viewer:general_info(),
+ {match,_} = re:run(TW,"CRASH DUMP SIZE LIMIT REACHED"),
+ ok;
_ ->
ok
end,
@@ -481,7 +485,11 @@ do_create_dumps(DataDir,Rel) ->
current ->
CD3 = dump_with_args(DataDir,Rel,"instr","+Mim true"),
CD4 = dump_with_strange_module_name(DataDir,Rel,"strangemodname"),
- {[CD1,CD2,CD3,CD4], DosDump};
+ Bytes = rand:uniform(300000) + 100,
+ CD5 = dump_with_args(DataDir,Rel,"trunc.bytes",
+ "-env ERL_CRASH_DUMP_BYTES " ++
+ integer_to_list(Bytes)),
+ {[CD1,CD2,CD3,CD4,CD5], DosDump};
_ ->
{[CD1,CD2], DosDump}
end.
diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl
index 4c882ad951..41726b1521 100644
--- a/lib/observer/test/observer_SUITE.erl
+++ b/lib/observer/test/observer_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -34,7 +34,8 @@
%% Test cases
-export([app_file/1, appup_file/1,
- basic/1, process_win/1, table_win/1
+ basic/1, process_win/1, table_win/1,
+ port_win_when_tab_not_initiated/1
]).
%% Default timetrap timeout (set in init_per_testcase)
@@ -49,7 +50,8 @@ groups() ->
[{gui, [],
[basic,
process_win,
- table_win
+ table_win,
+ port_win_when_tab_not_initiated
]
}].
@@ -299,6 +301,17 @@ table_win(Config) when is_list(Config) ->
observer:stop(),
ok.
+%% Test PR-1296/OTP-14151
+%% Clicking a link to a port before the port tab has been activated the
+%% first time crashes observer.
+port_win_when_tab_not_initiated(Config) ->
+ {ok,Port} = gen_tcp:listen(0,[]),
+ ok = observer:start(),
+ Notebook = setup_whitebox_testing(),
+ observer ! {open_link,erlang:port_to_list(Port)},
+ timer:sleep(1000),
+ observer:stop(),
+ ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index f214810199..ca9ad72473 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 2.2
+OBSERVER_VSN = 2.3.1
diff --git a/lib/odbc/configure.in b/lib/odbc/configure.in
index cb2b23d534..2dec6e5abf 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -65,7 +65,7 @@ dnl ---------------------------------------------------------------------
dnl Special windows stuff regarding CFLAGS and details in the environment...
dnl ---------------------------------------------------------------------
LM_WINDOWS_ENVIRONMENT
-
+
AC_PROG_MAKE_SET
AC_CHECK_PROGS(LD, ld.sh)
@@ -136,8 +136,8 @@ AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_addr], [], [],
dnl Checks for library functions.
AC_CHECK_FUNCS([memset socket])
-
-# ODBC
+
+# ODBC
$RM -f "$ERL_TOP/lib/odbc/SKIP"
LM_CHECK_THR_LIB
@@ -146,24 +146,24 @@ AC_SUBST(THR_LIBS)
odbc_lib_link_success=no
AC_SUBST(TARGET_FLAGS)
- case $host_os in
- darwin1[[0-4]].*|darwin[[0-9]].*)
+ case $host_os in
+ darwin1[[0-9]].*)
TARGET_FLAGS="-DUNIX"
if test ! -d "$with_odbc" || test "$with_odbc" = "yes"; then
- ODBC_LIB= -L"/usr/lib"
- ODBC_INCLUDE="-I/usr/lib/include"
+ ODBC_LIB= -L"/usr/local/lib"
+ ODBC_INCLUDE="-I/usr/local/include"
else
ODBC_LIB=-L"$with_odbc/lib"
ODBC_INCLUDE="-I$with_odbc/include"
fi
-
- AC_CHECK_LIB(iodbc, SQLAllocHandle,[ODBC_LIB="$ODBC_LIB -liodbc"; odbc_lib_link_success=yes])
+
+ AC_CHECK_LIB(iodbc, SQLAllocHandle,[ODBC_LIB="$ODBC_LIB -lodbc"; odbc_lib_link_success=yes])
;;
win32|cygwin)
TARGET_FLAGS="-DWIN32"
AC_CHECK_LIB(ws2_32, main)
if test ! -d "$with_odbc" || test "$with_odbc" = "yes"; then
- ODBC_LIB=""
+ ODBC_LIB=""
ODBC_INCLUDE=""
else
ODBC_LIB=-L"$with_odbc/lib"
@@ -196,7 +196,7 @@ AC_SUBST(TARGET_FLAGS)
elif test -d "${libdir}/64/."; then
libdir="${libdir}/64"
fi
- fi
+ fi
ODBC_LIB="-L$libdir"
ODBC_INCLUDE="-I$erl_xcomp_isysroot$rdir/include"
break
@@ -207,7 +207,7 @@ AC_SUBST(TARGET_FLAGS)
echo "No odbc library found" > "$ERL_TOP/lib/odbc/SKIP"
else
AC_MSG_RESULT($ODBC_LIB)
- AC_CHECK_LIB(odbc, SQLAllocHandle,[ODBC_LIB="$ODBC_LIB -lodbc"; odbc_lib_link_success=yes])
+ AC_CHECK_LIB(odbc, SQLAllocHandle,[ODBC_LIB="$ODBC_LIB -lodbc"; odbc_lib_link_success=yes])
fi
;;
diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml
index d3dd39616f..cc25a21c74 100644
--- a/lib/odbc/doc/src/notes.xml
+++ b/lib/odbc/doc/src/notes.xml
@@ -32,7 +32,44 @@
<p>This document describes the changes made to the odbc application.
</p>
- <section><title>ODBC 2.11.2</title>
+ <section><title>ODBC 2.12</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Change configure to skip odbc for old MACs, the change in
+ PR-1227 is not backwards compatible with old MACs, and we
+ do not see a need to continue support for such old
+ versions. However it is still possible to make it work on
+ such machines using the --with-odbc configure option.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14083</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.11.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ ODBC build configure has been updated to accept Mac OS X
+ El Capitan. Fixed by Lee Bannard.</p>
+ <p>
+ Own Id: OTP-13781</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.11.2</title>
<section><title>Improvements and New Features</title>
<list>
@@ -585,7 +622,7 @@
also been extended. </item><item> The <c>configure</c>
scripts of <c>erl_interface</c> and <c>odbc</c> now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl
index 5727c1ca50..261dfc6f20 100644
--- a/lib/odbc/test/odbc_connect_SUITE.erl
+++ b/lib/odbc/test/odbc_connect_SUITE.erl
@@ -89,6 +89,7 @@ init_per_suite(Config) when is_list(Config) ->
[{auto_commit, off}] ++ odbc_test_lib:platform_options()) of
{ok, Ref} ->
odbc:disconnect(Ref),
+ ct:timetrap(?default_timeout),
[{tableName, odbc_test_lib:unique_table_name()} | Config];
_ ->
{skip, "ODBC is not properly setup"}
@@ -129,11 +130,8 @@ init_per_testcase(_TestCase, Config) ->
init_per_testcase_common(Config).
init_per_testcase_common(Config) ->
- test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]),
- Dog = test_server:timetrap(?default_timeout),
- Temp = lists:keydelete(connection_ref, 1, Config),
- NewConfig = lists:keydelete(watchdog, 1, Temp),
- [{watchdog, Dog} | NewConfig].
+ ct:pal("ODBCINI = ~p~n", [os:getenv("ODBCINI")]),
+ lists:keydelete(connection_ref, 1, Config).
%%--------------------------------------------------------------------
%% Function: end_per_testcase(Case, Config) -> _
@@ -153,25 +151,22 @@ end_per_testcase(_TestCase, Config) ->
end_per_testcase_common(Config).
end_per_testcase_common(Config) ->
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
Result = odbc:sql_query(Ref, "DROP TABLE " ++ Table),
io:format("Drop table: ~p ~p~n", [Table, Result]),
- odbc:disconnect(Ref),
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog).
+ odbc:disconnect(Ref).
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-commit(doc)->
- ["Test the use of explicit commit"];
-commit(suite) -> [];
+commit()->
+ [{doc,"Test the use of explicit commit"}].
commit(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
{updated, _} =
@@ -205,14 +200,13 @@ commit(Config) ->
ok = odbc:disconnect(Ref).
%%-------------------------------------------------------------------------
-rollback(doc)->
- ["Test the use of explicit rollback"];
-rollback(suite) -> [];
+rollback()->
+ [{doc,"Test the use of explicit rollback"}].
rollback(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
@@ -245,9 +239,8 @@ rollback(Config) ->
ok = odbc:disconnect(Ref).
%%-------------------------------------------------------------------------
-not_explicit_commit(doc) ->
- ["Test what happens if you try using commit on a auto_commit connection."];
-not_explicit_commit(suite) -> [];
+not_explicit_commit() ->
+ [{doc,"Test what happens if you try using commit on a auto_commit connection."}].
not_explicit_commit(_Config) ->
{ok, Ref} =
odbc:connect(?RDBMS:connection_string(), [{auto_commit, on}] ++
@@ -256,19 +249,17 @@ not_explicit_commit(_Config) ->
ok = odbc:disconnect(Ref).
%%-------------------------------------------------------------------------
-not_exist_db(doc) ->
- ["Tests valid data format but invalid data in the connection parameters."];
-not_exist_db(suite) -> [];
+not_exist_db() ->
+ [{doc,"Tests valid data format but invalid data in the connection parameters."}].
not_exist_db(_Config) ->
{error, _} = odbc:connect("DSN=foo;UID=bar;PWD=foobar",
odbc_test_lib:platform_options()),
%% So that the odbc control server can be stoped "in the correct way"
- test_server:sleep(100).
+ ct:sleep(100).
%%-------------------------------------------------------------------------
-no_c_executable(doc) ->
- "Test what happens if the port-program can not be found";
-no_c_executable(suite) -> [];
+no_c_executable() ->
+ [{doc,"Test what happens if the port-program can not be found"}].
no_c_executable(_Config) ->
process_flag(trap_exit, true),
Dir = filename:nativename(filename:join(code:priv_dir(odbc),
@@ -293,9 +284,8 @@ no_c_executable(_Config) ->
end.
%%------------------------------------------------------------------------
-port_dies(doc) ->
- "Tests what happens if the port program dies";
-port_dies(suite) -> [];
+port_dies() ->
+ [{doc,"Tests what happens if the port program dies"}].
port_dies(_Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
{status, _} = process_info(Ref, status),
@@ -307,7 +297,7 @@ port_dies(_Config) ->
%% Wait for exit_status from port 5000 ms (will not get a exit
%% status in this case), then wait a little longer to make sure
%% the port and the controlprocess has had time to terminate.
- test_server:sleep(10000),
+ ct:sleep(10000),
undefined = process_info(Ref, status);
[] ->
ct:fail([erlang:port_info(P, name) || P <- erlang:ports()])
@@ -315,9 +305,8 @@ port_dies(_Config) ->
%%-------------------------------------------------------------------------
-control_process_dies(doc) ->
- "Tests what happens if the Erlang control process dies";
-control_process_dies(suite) -> [];
+control_process_dies() ->
+ [{doc,"Tests what happens if the Erlang control process dies"}].
control_process_dies(_Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
process_flag(trap_exit, true),
@@ -326,7 +315,7 @@ control_process_dies(_Config) ->
[Port] ->
{connected, Ref} = erlang:port_info(Port, connected),
exit(Ref, kill),
- test_server:sleep(500),
+ ct:sleep(500),
undefined = erlang:port_info(Port, connected);
%% Check for c-program still running, how?
[] ->
@@ -334,9 +323,8 @@ control_process_dies(_Config) ->
end.
%%-------------------------------------------------------------------------
-client_dies_normal(doc) ->
- ["Client dies with reason normal."];
-client_dies_normal(suite) -> [];
+client_dies_normal() ->
+ [{doc,"Client dies with reason normal."}].
client_dies_normal(Config) when is_list(Config) ->
Pid = spawn(?MODULE, client_normal, [self()]),
@@ -352,7 +340,7 @@ client_dies_normal(Config) when is_list(Config) ->
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
ok
after 5000 ->
- test_server:fail(control_process_not_stopped)
+ ct:fail(control_process_not_stopped)
end.
client_normal(Pid) ->
@@ -366,9 +354,8 @@ client_normal(Pid) ->
%%-------------------------------------------------------------------------
-client_dies_timeout(doc) ->
- ["Client dies with reason timeout."];
-client_dies_timeout(suite) -> [];
+client_dies_timeout() ->
+ [{doc,"Client dies with reason timeout."}].
client_dies_timeout(Config) when is_list(Config) ->
Pid = spawn(?MODULE, client_timeout, [self()]),
@@ -384,7 +371,7 @@ client_dies_timeout(Config) when is_list(Config) ->
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
ok
after 5000 ->
- test_server:fail(control_process_not_stopped)
+ ct:fail(control_process_not_stopped)
end.
client_timeout(Pid) ->
@@ -398,9 +385,8 @@ client_timeout(Pid) ->
%%-------------------------------------------------------------------------
-client_dies_error(doc) ->
- ["Client dies with reason error."];
-client_dies_error(suite) -> [];
+client_dies_error() ->
+ [{doc,"Client dies with reason error."}].
client_dies_error(Config) when is_list(Config) ->
Pid = spawn(?MODULE, client_error, [self()]),
@@ -416,7 +402,7 @@ client_dies_error(Config) when is_list(Config) ->
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
ok
after 5000 ->
- test_server:fail(control_process_not_stopped)
+ ct:fail(control_process_not_stopped)
end.
client_error(Pid) ->
@@ -430,9 +416,8 @@ client_error(Pid) ->
%%-------------------------------------------------------------------------
-connect_timeout(doc) ->
- ["Test the timeout for the connect function."];
-connect_timeout(suite) -> [];
+connect_timeout() ->
+ [{doc,"Test the timeout for the connect function."}].
connect_timeout(Config) when is_list(Config) ->
{'EXIT',timeout} = (catch odbc:connect(?RDBMS:connection_string(),
[{timeout, 0}] ++
@@ -442,10 +427,9 @@ connect_timeout(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-connect_port_timeout(doc) ->
- ["Test the timeout for the port program to connect back to the odbc "
- "application within the connect function."];
-connect_port_timeout(suite) -> [];
+connect_port_timeout() ->
+ [{"Test the timeout for the port program to connect back to the odbc "
+ "application within the connect function."}].
connect_port_timeout(Config) when is_list(Config) ->
%% Application environment var 'port_timeout' has been set to 0 by
%% init_per_testcase/2.
@@ -453,15 +437,14 @@ connect_port_timeout(Config) when is_list(Config) ->
odbc_test_lib:platform_options()).
%%-------------------------------------------------------------------------
-timeout(doc) ->
- ["Test that timeouts don't cause unwanted behavior sush as receiving"
- " an anwser to a previously tiemed out query."];
-timeout(suite) -> [];
+timeout() ->
+ [{"Test that timeouts don't cause unwanted behavior sush as receiving"
+ " an anwser to a previously tiemed out query."}].
timeout(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}]),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
@@ -512,7 +495,7 @@ update_table_timeout(Table, TimeOut, Pid) ->
{'EXIT', timeout} ->
Pid ! timout_occurred;
{updated, 1} ->
- test_server:fail(database_locker_failed)
+ ct:fail(database_locker_failed)
end,
receive
@@ -537,15 +520,14 @@ update_table_timeout(Table, TimeOut, Pid) ->
ok = odbc:disconnect(Ref).
%%-------------------------------------------------------------------------
-many_timeouts(doc) ->
- ["Tests that many consecutive timeouts lead to that the connection "
- "is shutdown."];
-many_timeouts(suite) -> [];
+many_timeouts() ->
+ [{doc, "Tests that many consecutive timeouts lead to that the connection "
+ "is shutdown."}].
many_timeouts(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
{updated, _} =
@@ -592,19 +574,18 @@ loop_many_timouts(Ref, UpdateQuery, TimeOut) ->
{'EXIT',timeout} ->
loop_many_timouts(Ref, UpdateQuery, TimeOut);
{updated, 1} ->
- test_server:fail(database_locker_failed);
+ ct:fail(database_locker_failed);
{error, connection_closed} ->
ok
end.
%%-------------------------------------------------------------------------
-timeout_reset(doc) ->
- ["Check that the number of consecutive timouts is reset to 0 when "
- "a successful call to the database is made."];
-timeout_reset(suite) -> [];
+timeout_reset() ->
+ [{doc, "Check that the number of consecutive timouts is reset to 0 when "
+ "a successful call to the database is made."}].
timeout_reset(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
{updated, _} =
@@ -688,21 +669,20 @@ loop_timout_reset(Ref, UpdateQuery, TimeOut, NumTimeouts) ->
loop_timout_reset(Ref, UpdateQuery,
TimeOut, NumTimeouts - 1);
{updated, 1} ->
- test_server:fail(database_locker_failed);
+ ct:fail(database_locker_failed);
{error, connection_closed} ->
- test_server:fail(connection_closed_premature)
+ ct:fail(connection_closed_premature)
end.
%%-------------------------------------------------------------------------
-disconnect_on_timeout(doc) ->
- ["Check that disconnect after a time out works properly"];
-disconnect_on_timeout(suite) -> [];
+disconnect_on_timeout() ->
+ [{doc,"Check that disconnect after a time out works properly"}].
disconnect_on_timeout(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
{updated, _} =
@@ -726,7 +706,7 @@ disconnect_on_timeout(Config) when is_list(Config) ->
ok ->
ok = odbc:commit(Ref, commit);
nok ->
- test_server:fail(database_locker_failed)
+ ct:fail(database_locker_failed)
end.
update_table_disconnect_on_timeout(Table, TimeOut, Pid) ->
@@ -744,14 +724,13 @@ update_table_disconnect_on_timeout(Table, TimeOut, Pid) ->
end.
%%-------------------------------------------------------------------------
-connection_closed(doc) ->
- ["Checks that you get an appropriate error message if you try to"
- " use a connection that has been closed"];
-connection_closed(suite) -> [];
+connection_closed() ->
+ [{doc, "Checks that you get an appropriate error message if you try to"
+ " use a connection that has been closed"}].
connection_closed(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -771,14 +750,13 @@ connection_closed(Config) when is_list(Config) ->
{error, connection_closed} = odbc:commit(Ref, commit).
%%-------------------------------------------------------------------------
-disable_scrollable_cursors(doc) ->
- ["Test disabling of scrollable cursors."];
-disable_scrollable_cursors(suite) -> [];
+disable_scrollable_cursors() ->
+ [{doc,"Test disabling of scrollable cursors."}].
disable_scrollable_cursors(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{scrollable_cursors, off}]),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -792,10 +770,10 @@ disable_scrollable_cursors(Config) when is_list(Config) ->
NextResult = ?RDBMS:selected_ID(1, next),
- test_server:format("Expected: ~p~n", [NextResult]),
+ ct:pal("Expected: ~p~n", [NextResult]),
Result = odbc:next(Ref),
- test_server:format("Got: ~p~n", [Result]),
+ ct:pal("Got: ~p~n", [Result]),
NextResult = Result,
{error, scrollable_cursors_disabled} = odbc:first(Ref),
@@ -809,15 +787,14 @@ disable_scrollable_cursors(Config) when is_list(Config) ->
{selected, _ColNames,[]} = odbc:select(Ref, next, 1).
%%-------------------------------------------------------------------------
-return_rows_as_lists(doc)->
- ["Test the option that a row may be returned as a list instead "
- "of a tuple. Too be somewhat backward compatible."];
-return_rows_as_lists(suite) -> [];
+return_rows_as_lists()->
+ [{doc,"Test the option that a row may be returned as a list instead "
+ "of a tuple. Too be somewhat backward compatible."}].
return_rows_as_lists(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{tuple_row, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -854,29 +831,28 @@ return_rows_as_lists(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-api_missuse(doc)->
- ["Test that behaviour of the control process if the api is abused"];
-api_missuse(suite) -> [];
+api_missuse()->
+ [{doc,"Test that behaviour of the control process if the api is abused"}].
api_missuse(Config) when is_list(Config)->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
%% Serious programming fault, connetion will be shut down
gen_server:call(Ref, {self(), foobar, 10}, infinity),
- test_server:sleep(10),
+ ct:sleep(10),
undefined = process_info(Ref, status),
{ok, Ref2} = odbc:connect(?RDBMS:connection_string(),
odbc_test_lib:platform_options()),
%% Serious programming fault, connetion will be shut down
gen_server:cast(Ref2, {self(), foobar, 10}),
- test_server:sleep(10),
+ ct:sleep(10),
undefined = process_info(Ref2, status),
{ok, Ref3} = odbc:connect(?RDBMS:connection_string(),
odbc_test_lib:platform_options()),
%% Could be an innocent misstake the connection lives.
Ref3 ! foobar,
- test_server:sleep(10),
+ ct:sleep(10),
{status, _} = process_info(Ref3, status).
transaction_support_str(mysql) ->
@@ -886,13 +862,13 @@ transaction_support_str(_) ->
%%-------------------------------------------------------------------------
-extended_errors(doc)->
- ["Test the extended errors connection option: When off; the old behaviour of just an error "
- "string is returned on error. When on, the error string is replaced by a 3 element tuple "
- "that also exposes underlying ODBC provider error codes."];
-extended_errors(suite) -> [];
+extended_errors()->
+ [{doc,
+ "Test the extended errors connection option: When off; the old behaviour of just an error "
+ "string is returned on error. When on, the error string is replaced by a 3 element tuple "
+ "that also exposes underlying ODBC provider error codes."}].
extended_errors(Config) when is_list(Config)->
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
{updated, _} = odbc:sql_query(Ref, "create table " ++ Table ++" ( id integer, data varchar(10))"),
diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl
index c88c00725e..a3a4bc78eb 100644
--- a/lib/odbc/test/odbc_data_type_SUITE.erl
+++ b/lib/odbc/test/odbc_data_type_SUITE.erl
@@ -120,6 +120,7 @@ init_per_suite(Config) when is_list(Config) ->
false ->
case (catch odbc:start()) of
ok ->
+ ct:timetrap(?default_timeout),
[{tableName, odbc_test_lib:unique_table_name()}| Config];
_ ->
{skip, "ODBC not startable"}
@@ -191,23 +192,22 @@ init_per_testcase(Case, Config) ->
common_init_per_testcase(Case, Config) ->
PlatformOptions = odbc_test_lib:platform_options(),
- case atom_to_list(Case) of
- "binary" ++ _ ->
- {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
- [{binary_strings, on}] ++ PlatformOptions);
- LCase when LCase == "utf8";
- LCase == "nchar";
- LCase == "nvarchar" ->
- {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
- [{binary_strings, on}] ++ PlatformOptions);
- _ ->
- {ok, Ref} = odbc:connect(?RDBMS:connection_string(), PlatformOptions)
- end,
+ {ok, Ref} =
+ case atom_to_list(Case) of
+ "binary" ++ _ ->
+ odbc:connect(?RDBMS:connection_string(),
+ [{binary_strings, on}] ++ PlatformOptions);
+ LCase when LCase == "utf8";
+ LCase == "nchar";
+ LCase == "nvarchar" ->
+ odbc:connect(?RDBMS:connection_string(),
+ [{binary_strings, on}] ++ PlatformOptions);
+ _ ->
+ odbc:connect(?RDBMS:connection_string(), PlatformOptions)
+ end,
odbc_test_lib:strict(Ref, ?RDBMS),
- Dog = test_server:timetrap(?default_timeout),
- Temp = lists:keydelete(connection_ref, 1, Config),
- NewConfig = lists:keydelete(watchdog, 1, Temp),
- [{watchdog, Dog}, {connection_ref, Ref} | NewConfig].
+ NewConfig = lists:keydelete(connection_ref, 1, Config),
+ [{connection_ref, Ref} | NewConfig].
is_fixed_upper_limit(mysql) ->
false;
@@ -231,28 +231,23 @@ is_supported_bit(_) ->
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
end_per_testcase(_TestCase, Config) ->
- Ref = ?config(connection_ref, Config),
+ Ref = proplists:get_value(connection_ref, Config),
ok = odbc:disconnect(Ref),
%% Clean up if needed
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
odbc:sql_query(NewRef, "DROP TABLE " ++ Table),
- odbc:disconnect(NewRef),
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
+ odbc:disconnect(NewRef).
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-char_fixed_lower_limit(doc) ->
- ["Tests fixed length char data type lower boundaries."];
-char_fixed_lower_limit(suite) ->
- [];
+char_fixed_lower_limit() ->
+ [{doc,"Tests fixed length char data type lower boundaries."}].
char_fixed_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Below limit
{error, _} =
@@ -287,18 +282,16 @@ char_fixed_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-char_fixed_upper_limit(doc) ->
- ["Tests fixed length char data type upper boundaries."];
-char_fixed_upper_limit(suite) ->
- [];
+char_fixed_upper_limit() ->
+ [{doc,"Tests fixed length char data type upper boundaries."}].
char_fixed_upper_limit(Config) when is_list(Config) ->
case ?RDBMS of
postgres ->
{skip, "Limit unknown"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Upper limit
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -337,14 +330,12 @@ char_fixed_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-char_fixed_padding(doc) ->
- ["Tests that data that is shorter than the given size is padded "
- "with blanks."];
-char_fixed_padding(suite) ->
- [];
+char_fixed_padding() ->
+ [{doc, "Tests that data that is shorter than the given size is padded "
+ "with blanks."}].
char_fixed_padding(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Data should be padded with blanks
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -365,13 +356,11 @@ char_fixed_padding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-varchar_lower_limit(doc) ->
- ["Tests variable length char data type lower boundaries."];
-varchar_lower_limit(suite) ->
- [];
+varchar_lower_limit() ->
+ [{doc,"Tests variable length char data type lower boundaries."}].
varchar_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Below limit
{error, _} =
@@ -405,13 +394,11 @@ varchar_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-varchar_upper_limit(doc) ->
- ["Tests variable length char data type upper boundaries."];
-varchar_upper_limit(suite) ->
- [];
+varchar_upper_limit() ->
+ [{doc,"Tests variable length char data type upper boundaries."}].
varchar_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
case ?RDBMS of
oracle ->
@@ -455,14 +442,12 @@ varchar_upper_limit(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-varchar_no_padding(doc) ->
- ["Tests that data that is shorter than the given max size is not padded "
- "with blanks."];
-varchar_no_padding(suite) ->
- [];
+varchar_no_padding() ->
+ [{doc, "Tests that data that is shorter than the given max size is not padded "
+ "with blanks."}].
varchar_no_padding(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Data should NOT be padded with blanks
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -481,13 +466,11 @@ varchar_no_padding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-text_lower_limit(doc) ->
- ["Tests 'long' char data type lower boundaries."];
-text_lower_limit(suite) ->
- [];
+text_lower_limit() ->
+ [{doc,"Tests 'long' char data type lower boundaries."}].
text_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -504,15 +487,13 @@ text_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-text_upper_limit(doc) ->
- [];
-text_upper_limit(suite) ->
- [];
+text_upper_limit() ->
+ [{doc,"Tests 'text' char data type upper boundaries."}].
text_upper_limit(Config) when is_list(Config) ->
{skip,"Consumes too much resources" }.
-%% Ref = ?config(connection_ref, Config),
-%% Table = ?config(tableName, Config),
+%% Ref = proplists:get_value(connection_ref, Config),
+%% Table = proplists:get_value(tableName, Config),
%% {updated, _} = % Value == 0 || -1 driver dependent!
%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -534,13 +515,11 @@ text_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_char_fixed_lower_limit(doc) ->
- ["Tests fixed length char data type lower boundaries."];
-binary_char_fixed_lower_limit(suite) ->
- [];
+binary_char_fixed_lower_limit() ->
+ [{doc,"Tests fixed length char data type lower boundaries."}].
binary_char_fixed_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Below limit
{error, _} =
@@ -579,18 +558,16 @@ binary_char_fixed_lower_limit(Config) when is_list(Config) ->
++ "')").
%%-------------------------------------------------------------------------
-binary_char_fixed_upper_limit(doc) ->
- ["Tests fixed length char data type upper boundaries."];
-binary_char_fixed_upper_limit(suite) ->
- [];
+binary_char_fixed_upper_limit() ->
+ [{doc,"Tests fixed length char data type upper boundaries."}].
binary_char_fixed_upper_limit(Config) when is_list(Config) ->
case ?RDBMS of
postgres ->
{skip, "Limit unknown"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Upper limit
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -630,14 +607,12 @@ binary_char_fixed_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_char_fixed_padding(doc) ->
- ["Tests that data that is shorter than the given size is padded "
- "with blanks."];
-binary_char_fixed_padding(suite) ->
- [];
+binary_char_fixed_padding() ->
+ [{doc, "Tests that data that is shorter than the given size is padded "
+ "with blanks."}].
binary_char_fixed_padding(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Data should be padded with blanks
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -658,13 +633,11 @@ binary_char_fixed_padding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_varchar_lower_limit(doc) ->
- ["Tests variable length char data type lower boundaries."];
-binary_varchar_lower_limit(suite) ->
- [];
+binary_varchar_lower_limit() ->
+ [{doc,"Tests variable length char data type lower boundaries."}].
binary_varchar_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Below limit
{error, _} =
@@ -701,13 +674,11 @@ binary_varchar_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_varchar_upper_limit(doc) ->
- ["Tests variable length char data type upper boundaries."];
-binary_varchar_upper_limit(suite) ->
- [];
+binary_varchar_upper_limit() ->
+ [{doc,"Tests variable length char data type upper boundaries."}].
binary_varchar_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
case ?RDBMS of
oracle ->
@@ -750,14 +721,12 @@ binary_varchar_upper_limit(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-binary_varchar_no_padding(doc) ->
- ["Tests that data that is shorter than the given max size is not padded "
- "with blanks."];
-binary_varchar_no_padding(suite) ->
- [];
+binary_varchar_no_padding() ->
+ [{doc,"Tests that data that is shorter than the given max size is not padded "
+ "with blanks."}].
binary_varchar_no_padding(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Data should NOT be padded with blanks
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -776,13 +745,11 @@ binary_varchar_no_padding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_text_lower_limit(doc) ->
- ["Tests 'long' char data type lower boundaries."];
-binary_text_lower_limit(suite) ->
- [];
+binary_text_lower_limit() ->
+ [{doc,"Tests 'long' char data type lower boundaries."}].
binary_text_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -799,15 +766,13 @@ binary_text_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_text_upper_limit(doc) ->
- [];
-binary_text_upper_limit(suite) ->
- [];
+binary_text_upper_limit() ->
+ [{doc,"Tests text char data type upper boundaries."}].
binary_text_upper_limit(Config) when is_list(Config) ->
{skip,"Consumes too much resources" }.
-%% Ref = ?config(connection_ref, Config),
-%% Table = ?config(tableName, Config),
+%% Ref = proplists:get_value(connection_ref, Config),
+%% Table = proplists:get_value(tableName, Config),
%% {updated, _} = % Value == 0 || -1 driver dependent!
%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -830,17 +795,15 @@ binary_text_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-tiny_int_lower_limit(doc) ->
- ["Tests integer of type tinyint."];
-tiny_int_lower_limit(suite) ->
- [];
+tiny_int_lower_limit() ->
+ [{doc,"Tests integer of type tinyint."}].
tiny_int_lower_limit(Config) when is_list(Config) ->
case ?RDBMS of
postgres ->
{skip, "Type tiniyint not supported"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -864,17 +827,15 @@ tiny_int_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-tiny_int_upper_limit(doc) ->
- ["Tests integer of type tinyint."];
-tiny_int_upper_limit(suite) ->
- [];
+tiny_int_upper_limit() ->
+ [{doc,"Tests integer of type tinyint."}].
tiny_int_upper_limit(Config) when is_list(Config) ->
case ?RDBMS of
postgres ->
{skip, "Type tiniyint not supported"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -898,13 +859,11 @@ tiny_int_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-small_int_lower_limit(doc) ->
- ["Tests integer of type smallint."];
-small_int_lower_limit(suite) ->
- [];
+small_int_lower_limit() ->
+ [{doc,"Tests integer of type smallint."}].
small_int_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -927,13 +886,11 @@ small_int_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-small_int_upper_limit(doc) ->
- ["Tests integer of type smallint."];
-small_int_upper_limit(suite) ->
- [];
+small_int_upper_limit() ->
+ [{doc,"Tests integer of type smallint."}].
small_int_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -955,13 +912,11 @@ small_int_upper_limit(Config) when is_list(Config) ->
++ "')").
%%-------------------------------------------------------------------------
-int_lower_limit(doc) ->
- ["Tests integer of type int."];
-int_lower_limit(suite) ->
- [];
+int_lower_limit() ->
+ [{doc,"Tests integer of type int."}].
int_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -983,13 +938,11 @@ int_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-int_upper_limit(doc) ->
- ["Tests integer of type int."];
-int_upper_limit(suite) ->
- [];
+int_upper_limit() ->
+ [{doc,"Tests integer of type int."}].
int_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1011,13 +964,11 @@ int_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-big_int_lower_limit(doc) ->
- ["Tests integer of type bigint"];
-big_int_lower_limit(suite) ->
- [];
+big_int_lower_limit() ->
+ [{doc,"Tests integer of type bigint"}].
big_int_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1040,13 +991,11 @@ big_int_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-big_int_upper_limit(doc) ->
- ["Tests integer of type bigint."];
-big_int_upper_limit(suite) ->
- [];
+big_int_upper_limit() ->
+ [{doc,"Tests integer of type bigint."}].
big_int_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1068,17 +1017,13 @@ big_int_upper_limit(Config) when is_list(Config) ->
++ "')").
%%-------------------------------------------------------------------------
-bit_false(doc) ->
- [""];
-bit_false(suite) ->
- [];
bit_false(Config) when is_list(Config) ->
case ?RDBMS of
oracle ->
{skip, "Not supported by driver"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1102,17 +1047,13 @@ bit_false(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-bit_true(doc) ->
- [""];
-bit_true(suite) ->
- [];
bit_true(Config) when is_list(Config) ->
case ?RDBMS of
oracle ->
{skip, "Not supported by driver"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -1136,14 +1077,11 @@ bit_true(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-float_lower_limit(doc) ->
- [""];
-float_lower_limit(suite) ->
- [];
+
float_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
case ?RDBMS of
mysql ->
@@ -1186,13 +1124,10 @@ float_lower_limit(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-float_upper_limit(doc) ->
- [""];
-float_upper_limit(suite) ->
- [];
+
float_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
case ?RDBMS of
mysql ->
@@ -1218,13 +1153,11 @@ float_upper_limit(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-float_zero(doc) ->
- ["Test the float value zero."];
-float_zero(suite) ->
- [];
+float_zero() ->
+ [{doc,"Test the float value zero."}].
float_zero(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1237,13 +1170,11 @@ float_zero(Config) when is_list(Config) ->
SelectResult =
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table).
%%-------------------------------------------------------------------------
-real_zero(doc) ->
- ["Test the real value zero."];
-real_zero(suite) ->
- [];
+real_zero() ->
+ [{doc,"Test the real value zero."}].
real_zero(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
case ?RDBMS of
oracle ->
@@ -1262,13 +1193,11 @@ real_zero(Config) when is_list(Config) ->
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table)
end.
%%------------------------------------------------------------------------
-dec_long(doc) ->
- [""];
dec_long(suit) ->
[];
dec_long(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1281,13 +1210,11 @@ dec_long(Config) when is_list(Config) ->
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
["FIELD"] = odbc_test_lib:to_upper(Fields).
%%------------------------------------------------------------------------
-dec_double(doc) ->
- [""];
dec_double(suit) ->
[];
dec_double(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1329,13 +1256,11 @@ dec_double(Config) when is_list(Config) ->
["FIELD"] = odbc_test_lib:to_upper(Fields2).
%%------------------------------------------------------------------------
-dec_bignum(doc) ->
- [""];
dec_bignum(suit) ->
[];
dec_bignum(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1361,13 +1286,11 @@ dec_bignum(Config) when is_list(Config) ->
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
["FIELD"] = odbc_test_lib:to_upper(Fields1).
%%------------------------------------------------------------------------
-num_long(doc) ->
- [""];
num_long(suit) ->
[];
num_long(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1380,13 +1303,11 @@ num_long(Config) when is_list(Config) ->
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
["FIELD"] = odbc_test_lib:to_upper(Fields).
%%------------------------------------------------------------------------
-num_double(doc) ->
- [""];
num_double(suit) ->
[];
num_double(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1426,13 +1347,11 @@ num_double(Config) when is_list(Config) ->
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
["FIELD"] = odbc_test_lib:to_upper(Fields2).
%%------------------------------------------------------------------------
-num_bignum(doc) ->
- [""];
num_bignum(suit) ->
[];
num_bignum(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1459,13 +1378,13 @@ num_bignum(Config) when is_list(Config) ->
["FIELD"] = odbc_test_lib:to_upper(Fields1).
%%------------------------------------------------------------------------
-utf8(doc) ->
- ["Test unicode support"];
+utf8() ->
+ [{doc,"Test unicode support"}].
utf8(suit) ->
[];
utf8(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ "(FIELD text)"),
@@ -1487,30 +1406,30 @@ utf8(Config) when is_list(Config) ->
end,
Latin1Data),
- test_server:format("UnicodeIn: ~p ~n",[UnicodeIn]),
+ ct:pal("UnicodeIn: ~p ~n",[UnicodeIn]),
{updated, _} = odbc:param_query(Ref,"INSERT INTO " ++ Table ++ "(FIELD) values(?)",
[{{sql_varchar,50}, UnicodeIn}]),
{selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table),
- test_server:format("UnicodeOut: ~p~n", [UnicodeOut]),
+ ct:pal("UnicodeOut: ~p~n", [UnicodeOut]),
Result = lists:map(fun({Char}) ->
unicode:characters_to_list(Char,utf8)
end, UnicodeOut),
- test_server:format("Result: ~p ~n", [Result]),
+ ct:pal("Result: ~p ~n", [Result]),
Latin1Data = Result.
%%------------------------------------------------------------------------
-nchar(doc) ->
- ["Test unicode nchar support in sqlserver"];
+nchar() ->
+ [{doc,"Test unicode nchar support in sqlserver"}].
nchar(suit) ->
[];
nchar(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1520,13 +1439,13 @@ nchar(Config) when is_list(Config) ->
%%------------------------------------------------------------------------
-nvarchar(doc) ->
- ["Test 'unicode' nvarchar support"];
+nvarchar() ->
+ [{doc,"Test 'unicode' nvarchar support"}].
nvarchar(suit) ->
[];
nvarchar(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1535,13 +1454,11 @@ nvarchar(Config) when is_list(Config) ->
w_char_support(Ref, Table, sql_wlongvarchar, 50).
%%------------------------------------------------------------------------
-timestamp(doc) ->
- [""];
timestamp(suit) ->
[];
timestamp(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1582,21 +1499,21 @@ w_char_support(Ref, Table, CharType, Size) ->
end,
Latin1Data),
- test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]),
+ ct:pal("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]),
{updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)",
[{{CharType, Size},UnicodeIn}]),
{selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table),
- test_server:format("UnicodeOut: ~p~n", [UnicodeOut]),
+ ct:pal("UnicodeOut: ~p~n", [UnicodeOut]),
PadResult = lists:map(fun({Unicode}) ->
unicode:characters_to_list(Unicode,{utf16,little})
end,
UnicodeOut),
- test_server:format("Result: ~p~n", [PadResult]),
+ ct:pal("Result: ~p~n", [PadResult]),
Result = lists:map(fun(Str) -> string:strip(Str) end, PadResult),
diff --git a/lib/odbc/test/odbc_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl
index 5f719b7287..c283872965 100644
--- a/lib/odbc/test/odbc_query_SUITE.erl
+++ b/lib/odbc/test/odbc_query_SUITE.erl
@@ -113,6 +113,7 @@ init_per_suite(Config) when is_list(Config) ->
false ->
case (catch odbc:start()) of
ok ->
+ ct:timetrap(?default_timeout),
[{tableName, odbc_test_lib:unique_table_name()}| Config];
_ ->
{skip, "ODBC not startable"}
@@ -144,10 +145,10 @@ end_per_suite(_Config) ->
init_per_testcase(_Case, Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
odbc_test_lib:strict(Ref, ?RDBMS),
- Dog = test_server:timetrap(?default_timeout),
- Temp = lists:keydelete(connection_ref, 1, Config),
- NewConfig = lists:keydelete(watchdog, 1, Temp),
- [{watchdog, Dog}, {connection_ref, Ref} | NewConfig].
+
+ NewConfig = lists:keydelete(connection_ref, 1, Config),
+
+ [{connection_ref, Ref} | NewConfig].
%%--------------------------------------------------------------------
%% Function: end_per_testcase(Case, Config) -> _
@@ -158,27 +159,23 @@ init_per_testcase(_Case, Config) ->
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
end_per_testcase(_Case, Config) ->
- Ref = ?config(connection_ref, Config),
+ Ref = proplists:get_value(connection_ref, Config),
ok = odbc:disconnect(Ref),
%% Clean up if needed
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
odbc:sql_query(NewRef, "DROP TABLE " ++ Table),
- odbc:disconnect(NewRef),
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
+ odbc:disconnect(NewRef).
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-stored_proc(doc)->
- ["Test stored proc with OUT param"];
-stored_proc(suite) -> [];
+stored_proc()->
+ [{doc, "Test stored proc with OUT param"}].
stored_proc(Config) when is_list(Config) ->
case ?RDBMS of
X when X == oracle; X == postgres->
- Ref = ?config(connection_ref, Config),
+ Ref = proplists:get_value(connection_ref, Config),
{updated, _} =
odbc:sql_query(Ref,
?RDBMS:stored_proc_integer_out()),
@@ -192,12 +189,11 @@ stored_proc(Config) when is_list(Config) ->
{skip, "stored proc not yet supported"}
end.
-sql_query(doc)->
- ["Test the common cases"];
-sql_query(suite) -> [];
+sql_query()->
+ [{doc, "Test the common cases"}].
sql_query(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -235,14 +231,14 @@ sql_query(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-select_count(doc) ->
- ["Tests select_count/[2,3]'s timeout, "
- " select_count's functionality will be better tested by other tests "
- " such as first."];
+select_count() ->
+ [{doc, "Tests select_count/[2,3]'s timeout, "
+ " select_count's functionality will be better tested by other tests "
+ " such as first."}].
select_count(sute) -> [];
select_count(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -257,12 +253,11 @@ select_count(Config) when is_list(Config) ->
(catch odbc:select_count(Ref, "SELECT * FROM ", -1)),
ok.
%%-------------------------------------------------------------------------
-first(doc) ->
- ["Tests first/[1,2]"];
-first(suite) -> [];
+first() ->
+ [doc, {"Tests first/[1,2]"}].
first(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -284,12 +279,11 @@ first(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-last(doc) ->
- ["Tests last/[1,2]"];
-last(suite) -> [];
+last() ->
+ [{doc, "Tests last/[1,2]"}].
last(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -311,12 +305,11 @@ last(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-next(doc) ->
- ["Tests next/[1,2]"];
-next(suite) -> [];
+next() ->
+ [{doc, "Tests next/[1,2]"}].
next(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -337,12 +330,11 @@ next(Config) when is_list(Config) ->
{'EXIT', {function_clause, _}} = (catch odbc:next(Ref, -1)),
ok.
%%-------------------------------------------------------------------------
-prev(doc) ->
- ["Tests prev/[1,2]"];
-prev(suite) -> [];
+prev() ->
+ [{doc, "Tests prev/[1,2]"}].
prev(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -366,12 +358,12 @@ prev(Config) when is_list(Config) ->
{'EXIT', {function_clause, _}} = (catch odbc:prev(Ref, -1)),
ok.
%%-------------------------------------------------------------------------
-select_next(doc) ->
- ["Tests select/[4,5] with CursorRelation = next "];
+select_next() ->
+ [{doc, "Tests select/[4,5] with CursorRelation = next "}].
select_next(suit) -> [];
select_next(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -407,12 +399,12 @@ select_next(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-select_relative(doc) ->
- ["Tests select/[4,5] with CursorRelation = relative "];
+select_relative() ->
+ [{doc, "Tests select/[4,5] with CursorRelation = relative "}].
select_relative(suit) -> [];
select_relative(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -448,12 +440,12 @@ select_relative(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-select_absolute(doc) ->
- ["Tests select/[4,5] with CursorRelation = absolute "];
+select_absolute() ->
+ [{doc, "Tests select/[4,5] with CursorRelation = absolute "}].
select_absolute(suit) -> [];
select_absolute(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -482,12 +474,11 @@ select_absolute(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-create_table_twice(doc) ->
- ["Test what happens if you try to create the same table twice."];
-create_table_twice(suite) -> [];
+create_table_twice() ->
+ [{doc, "Test what happens if you try to create the same table twice."}].
create_table_twice(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -501,12 +492,11 @@ create_table_twice(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-delete_table_twice(doc) ->
- ["Test what happens if you try to delete the same table twice."];
-delete_table_twice(suite) -> [];
+delete_table_twice() ->
+ [{doc, "Test what happens if you try to delete the same table twice."}].
delete_table_twice(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -518,12 +508,12 @@ delete_table_twice(Config) when is_list(Config) ->
ok.
%-------------------------------------------------------------------------
-duplicate_key(doc) ->
- ["Test what happens if you try to use the same key twice"];
+duplicate_key() ->
+ [{doc, "Test what happens if you try to use the same key twice"}].
duplicate_key(suit) -> [];
duplicate_key(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -539,13 +529,12 @@ duplicate_key(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-not_connection_owner(doc) ->
- ["Test what happens if a process that did not start the connection"
- " tries to acess it."];
-not_connection_owner(suite) -> [];
+not_connection_owner() ->
+ [{doc, "Test what happens if a process that did not start the connection"
+ " tries to acess it."}].
not_connection_owner(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
spawn_link(?MODULE, not_owner, [self(), Ref, Table]),
@@ -564,12 +553,11 @@ not_owner(Pid, Ref, Table) ->
Pid ! continue.
%%-------------------------------------------------------------------------
-no_result_set(doc) ->
- ["Tests what happens if you try to use a function that needs an "
- "associated result set when there is none."];
-no_result_set(suite) -> [];
+no_result_set() ->
+ [{doc, "Tests what happens if you try to use a function that needs an "
+ "associated result set when there is none."}].
no_result_set(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
+ Ref = proplists:get_value(connection_ref, Config),
{error, result_set_does_not_exist} = odbc:first(Ref),
{error, result_set_does_not_exist} = odbc:last(Ref),
@@ -582,13 +570,11 @@ no_result_set(Config) when is_list(Config) ->
odbc:select(Ref, {relative, 2}, 1),
ok.
%%-------------------------------------------------------------------------
-query_error(doc) ->
- ["Test what happens if there is an error in the query."];
-query_error(suite) ->
- [];
+query_error() ->
+ [{doc, "Test what happens if there is an error in the query."}].
query_error(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -605,15 +591,13 @@ query_error(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-multiple_select_result_sets(doc) ->
- ["Test what happens if you have a batch of select queries."];
-multiple_select_result_sets(suite) ->
- [];
+multiple_select_result_sets() ->
+ [{doc, "Test what happens if you have a batch of select queries."}].
multiple_select_result_sets(Config) when is_list(Config) ->
case ?RDBMS of
sqlserver ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -640,16 +624,14 @@ multiple_select_result_sets(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-multiple_mix_result_sets(doc) ->
- ["Test what happens if you have a batch of select and other type of"
- " queries."];
-multiple_mix_result_sets(suite) ->
- [];
+multiple_mix_result_sets() ->
+ [{doc, "Test what happens if you have a batch of select and other type of"
+ " queries."}].
multiple_mix_result_sets(Config) when is_list(Config) ->
case ?RDBMS of
sqlserver ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -674,15 +656,13 @@ multiple_mix_result_sets(Config) when is_list(Config) ->
{skip, "multiple result_set not supported"}
end.
%%-------------------------------------------------------------------------
-multiple_result_sets_error(doc) ->
- ["Test what happens if one of the batched queries fails."];
-multiple_result_sets_error(suite) ->
- [];
+multiple_result_sets_error() ->
+ [{doc, "Test what happens if one of the batched queries fails."}].
multiple_result_sets_error(Config) when is_list(Config) ->
case ?RDBMS of
sqlserver ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -709,15 +689,13 @@ multiple_result_sets_error(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-param_insert_tiny_int(doc)->
- ["Test insertion of tiny ints by parameterized queries."];
-param_insert_tiny_int(suite) ->
- [];
+param_insert_tiny_int()->
+ [{doc,"Test insertion of tiny ints by parameterized queries."}].
param_insert_tiny_int(Config) when is_list(Config) ->
case ?RDBMS of
sqlserver ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -746,13 +724,11 @@ param_insert_tiny_int(Config) when is_list(Config) ->
{skip, "Type tiniyint not supported"}
end.
%%-------------------------------------------------------------------------
-param_insert_small_int(doc)->
- ["Test insertion of small ints by parameterized queries."];
-param_insert_small_int(suite) ->
- [];
+param_insert_small_int()->
+ [{doc,"Test insertion of small ints by parameterized queries."}].
param_insert_small_int(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -778,13 +754,11 @@ param_insert_small_int(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_int(doc)->
- ["Test insertion of ints by parameterized queries."];
-param_insert_int(suite) ->
- [];
+param_insert_int()->
+ [{doc,"Test insertion of ints by parameterized queries."}].
param_insert_int(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -810,13 +784,11 @@ param_insert_int(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_integer(doc)->
- ["Test insertion of integers by parameterized queries."];
-param_insert_integer(suite) ->
- [];
+param_insert_integer()->
+ [{doc,"Test insertion of integers by parameterized queries."}].
param_insert_integer(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -842,13 +814,11 @@ param_insert_integer(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_decimal(doc)->
- ["Test insertion of decimal numbers by parameterized queries."];
-param_insert_decimal(suite) ->
- [];
+param_insert_decimal()->
+ [{doc,"Test insertion of decimal numbers by parameterized queries."}].
param_insert_decimal(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -893,13 +863,11 @@ param_insert_decimal(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_numeric(doc)->
- ["Test insertion of numeric numbers by parameterized queries."];
-param_insert_numeric(suite) ->
- [];
+param_insert_numeric()->
+ [{doc,"Test insertion of numeric numbers by parameterized queries."}].
param_insert_numeric(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -944,13 +912,11 @@ param_insert_numeric(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_char(doc)->
- ["Test insertion of fixed length string by parameterized queries."];
-param_insert_char(suite) ->
- [];
+param_insert_char()->
+ [{doc,"Test insertion of fixed length string by parameterized queries."}].
param_insert_char(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -980,13 +946,11 @@ param_insert_char(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_character(doc)->
- ["Test insertion of fixed length string by parameterized queries."];
-param_insert_character(suite) ->
- [];
+param_insert_character()->
+ [{doc,"Test insertion of fixed length string by parameterized queries."}].
param_insert_character(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1017,13 +981,11 @@ param_insert_character(Config) when is_list(Config) ->
ok.
%%------------------------------------------------------------------------
-param_insert_char_varying(doc)->
- ["Test insertion of variable length strings by parameterized queries."];
-param_insert_char_varying(suite) ->
- [];
+param_insert_char_varying()->
+ [{doc,"Test insertion of variable length strings by parameterized queries."}].
param_insert_char_varying(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1054,13 +1016,11 @@ param_insert_char_varying(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_character_varying(doc)->
- ["Test insertion of variable length strings by parameterized queries."];
-param_insert_character_varying(suite) ->
- [];
+param_insert_character_varying()->
+ [{doc,"Test insertion of variable length strings by parameterized queries."}].
param_insert_character_varying(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1091,13 +1051,11 @@ param_insert_character_varying(Config) when is_list(Config) ->
[{{sql_varchar, 10}, ["1", 2]}])),
ok.
%%-------------------------------------------------------------------------
-param_insert_float(doc)->
- ["Test insertion of floats by parameterized queries."];
-param_insert_float(suite) ->
- [];
+param_insert_float()->
+ [{doc,"Test insertion of floats by parameterized queries."}].
param_insert_float(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1120,7 +1078,7 @@ param_insert_float(Config) when is_list(Config) ->
true ->
ok;
false ->
- test_server:fail(float_numbers_do_not_match)
+ ct:fail(float_numbers_do_not_match)
end,
{'EXIT',{badarg,odbc,param_query,'Params'}} =
@@ -1130,13 +1088,11 @@ param_insert_float(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_real(doc)->
- ["Test insertion of real numbers by parameterized queries."];
-param_insert_real(suite) ->
- [];
+param_insert_real()->
+ [{doc,"Test insertion of real numbers by parameterized queries."}].
param_insert_real(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1161,7 +1117,7 @@ param_insert_real(Config) when is_list(Config) ->
true ->
ok;
false ->
- test_server:fail(real_numbers_do_not_match)
+ ct:fail(real_numbers_do_not_match)
end,
{'EXIT',{badarg,odbc,param_query,'Params'}} =
@@ -1171,13 +1127,11 @@ param_insert_real(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_double(doc)->
- ["Test insertion of doubles by parameterized queries."];
-param_insert_double(suite) ->
- [];
+param_insert_double()->
+ [{doc,"Test insertion of doubles by parameterized queries."}].
param_insert_double(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1200,7 +1154,7 @@ param_insert_double(Config) when is_list(Config) ->
true ->
ok;
false ->
- test_server:fail(double_numbers_do_not_match)
+ ct:fail(double_numbers_do_not_match)
end,
{'EXIT',{badarg,odbc,param_query,'Params'}} =
@@ -1210,13 +1164,11 @@ param_insert_double(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_mix(doc)->
- ["Test insertion of a mixture of datatypes by parameterized queries."];
-param_insert_mix(suite) ->
- [];
+param_insert_mix()->
+ [{doc,"Test insertion of a mixture of datatypes by parameterized queries."}].
param_insert_mix(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1237,13 +1189,11 @@ param_insert_mix(Config) when is_list(Config) ->
odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
ok.
%%-------------------------------------------------------------------------
-param_update(doc)->
- ["Test parameterized update query."];
-param_update(suite) ->
- [];
+param_update()->
+ [{doc,"Test parameterized update query."}].
param_update(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1272,12 +1222,12 @@ param_update(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-delete_nonexisting_row(doc) -> % OTP-5759
- ["Make a delete...where with false conditions (0 rows deleted). ",
- "This used to give an error message (see ticket OTP-5759)."];
+delete_nonexisting_row() -> % OTP-5759
+ [{doc, "Make a delete...where with false conditions (0 rows deleted). ",
+ "This used to give an error message (see ticket OTP-5759)."}].
delete_nonexisting_row(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref, "CREATE TABLE " ++ Table
@@ -1301,13 +1251,11 @@ delete_nonexisting_row(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_delete(doc) ->
- ["Test parameterized delete query."];
-param_delete(suite) ->
- [];
+param_delete() ->
+ [{doc,"Test parameterized delete query."}].
param_delete(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1336,13 +1284,11 @@ param_delete(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-param_select(doc) ->
- ["Test parameterized select query."];
-param_select(suite) ->
- [];
+param_select() ->
+ [{doc,"Test parameterized select query."}].
param_select(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1366,13 +1312,11 @@ param_select(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_select_empty_params(doc) ->
- ["Test parameterized select query with no parameters."];
-param_select_empty_params(suite) ->
- [];
+param_select_empty_params() ->
+ [{doc,"Test parameterized select query with no parameters."}].
param_select_empty_params(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1396,13 +1340,11 @@ param_select_empty_params(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_delete_empty_params(doc) ->
- ["Test parameterized delete query with no parameters."];
-param_delete_empty_params(suite) ->
- [];
+param_delete_empty_params() ->
+ [{doc,"Test parameterized delete query with no parameters."}].
param_delete_empty_params(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1430,13 +1372,11 @@ param_delete_empty_params(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-describe_integer(doc) ->
- ["Test describe_table/[2,3] for integer columns."];
-describe_integer(suite) ->
- [];
+describe_integer() ->
+ [{doc,"Test describe_table/[2,3] for integer columns."}].
describe_integer(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1449,13 +1389,11 @@ describe_integer(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-describe_string(doc) ->
- ["Test describe_table/[2,3] for string columns."];
-describe_string(suite) ->
- [];
+describe_string() ->
+ [{doc,"Test describe_table/[2,3] for string columns."}].
describe_string(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1470,13 +1408,11 @@ describe_string(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-describe_floating(doc) ->
- ["Test describe_table/[2,3] for floting columns."];
-describe_floating(suite) ->
- [];
+describe_floating() ->
+ [{doc,"Test describe_table/[2,3] for floting columns."}].
describe_floating(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1490,14 +1426,12 @@ describe_floating(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-describe_dec_num(doc) ->
- ["Test describe_table/[2,3] for decimal and numerical columns"];
-describe_dec_num(suite) ->
- [];
+describe_dec_num() ->
+ [{doc,"Test describe_table/[2,3] for decimal and numerical columns"}].
describe_dec_num(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1511,14 +1445,12 @@ describe_dec_num(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-describe_timestamp(doc) ->
- ["Test describe_table/[2,3] for tinmestap columns"];
-describe_timestamp(suite) ->
- [];
+describe_timestamp() ->
+ [{doc,"Test describe_table/[2,3] for tinmestap columns"}].
describe_timestamp(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1530,14 +1462,12 @@ describe_timestamp(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-describe_no_such_table(doc) ->
- ["Test what happens if you try to describe a table that does not exist."];
-describe_no_such_table(suite) ->
- [];
+describe_no_such_table() ->
+ [{doc,"Test what happens if you try to describe a table that does not exist."}].
describe_no_such_table(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{error, _ } = odbc:describe_table(Ref, Table),
ok.
@@ -1549,10 +1479,10 @@ describe_no_such_table(Config) when is_list(Config) ->
is_driver_error(Error) ->
case is_list(Error) of
true ->
- test_server:format("Driver error ~p~n", [Error]),
+ ct:pal("Driver error ~p~n", [Error]),
ok;
false ->
- test_server:fail(Error)
+ ct:fail(Error)
end.
is_supported_multiple_resultsets(sqlserver) ->
true;
diff --git a/lib/odbc/test/odbc_start_SUITE.erl b/lib/odbc/test/odbc_start_SUITE.erl
index f055eeb60e..310b92ca29 100644
--- a/lib/odbc/test/odbc_start_SUITE.erl
+++ b/lib/odbc/test/odbc_start_SUITE.erl
@@ -49,6 +49,7 @@ init_per_suite(Config) ->
_ ->
%% Make sure odbc is not already started
odbc:stop(),
+ ct:timetrap(?TIMEOUT),
[{tableName, odbc_test_lib:unique_table_name()} | Config]
end
end.
@@ -74,11 +75,9 @@ end_per_suite(_Config) ->
%% variable, but should NOT alter/remove any existing entries.
%% Description: Initialization before each test case
%%--------------------------------------------------------------------
-init_per_testcase(_TestCase, Config0) ->
- test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]),
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = test_server:timetrap(?TIMEOUT),
- [{watchdog, Dog} | Config].
+init_per_testcase(_TestCase, Config) ->
+ ct:pal("ODBCINI = ~p~n", [os:getenv("ODBCINI")]),
+ Config.
%%--------------------------------------------------------------------
%% Function: end_per_testcase(TestCase, Config) -> _
@@ -88,15 +87,8 @@ init_per_testcase(_TestCase, Config0) ->
%% A list of key/value pairs, holding the test case configuration.
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
-end_per_testcase(_TestCase, Config) ->
- Dog = ?config(watchdog, Config),
- case Dog of
- undefined ->
- ok;
- _ ->
- test_server:timetrap_cancel(Dog)
- end.
-
+end_per_testcase(_TestCase, _Config) ->
+ ok.
%%--------------------------------------------------------------------
%% Function: all(Clause) -> TestCases
%% Clause - atom() - suite | doc
@@ -135,10 +127,8 @@ app(Config) when is_list(Config) ->
appup(Config) when is_list(Config) ->
ok = ?t:appup_test(odbc).
-start(doc) ->
- ["Test start/stop of odbc"];
-start(suite) ->
- [];
+start() ->
+ [{doc,"Test start/stop of odbc"}].
start(Config) when is_list(Config) ->
PlatformOptions = odbc_test_lib:platform_options(),
{error,odbc_not_started} = odbc:connect(?RDBMS:connection_string(),
@@ -153,9 +143,9 @@ start(Config) when is_list(Config) ->
start_odbc(transient),
start_odbc(permanent);
{error, odbc_not_started} ->
- test_server:fail(start_failed);
+ ct:fail(start_failed);
Error ->
- test_server:format("Connection failed: ~p~n", [Error]),
+ ct:pal("Connection failed: ~p~n", [Error]),
{skip, "ODBC is not properly setup"}
end.
@@ -166,13 +156,12 @@ start_odbc(Type) ->
ok = odbc:disconnect(Ref),
odbc:stop();
{error, odbc_not_started} ->
- test_server:fail(start_failed)
+ ct:fail(start_failed)
end.
-long_connection_line(doc)->
- ["Test a connection line longer than 127 characters"];
-long_connection_line(suite) -> [];
+long_connection_line()->
+ [{doc,"Test a connection line longer than 127 characters"}].
long_connection_line(_Config) ->
odbc:start(),
String133 = "unknown_odbc_parameter=01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789",
diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl
index 37c2249303..cf82d4d32a 100644
--- a/lib/odbc/test/odbc_test_lib.erl
+++ b/lib/odbc/test/odbc_test_lib.erl
@@ -61,13 +61,13 @@ odbc_check() ->
end.
check_row_count(Count, Count) ->
- test_server:format("Correct row count Count: ~p~n", [Count]),
+ ct:pal("Correct row count Count: ~p~n", [Count]),
true;
check_row_count(_, undefined) ->
- test_server:format("Undefined row count ~n", []),
+ ct:pal("Undefined row count ~n", []),
true;
check_row_count(Expected, Count) ->
- test_server:format("Incorrect row count Expected ~p Got ~p~n",
+ ct:pal("Incorrect row count Expected ~p Got ~p~n",
[Expected, Count]),
false.
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index 957c6b42eb..2e313570e1 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.11.2
+ODBC_VSN = 2.12
diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml
index e6e80b046d..df4151147c 100644
--- a/lib/os_mon/doc/src/notes.xml
+++ b/lib/os_mon/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the OS_Mon application.</p>
+<section><title>Os_Mon 2.4.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Support s390x in os_mon.</p>
+ <p>
+ Own Id: OTP-14161 Aux Id: PR-1309 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Os_Mon 2.4.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/os_mon/src/memsup.erl b/lib/os_mon/src/memsup.erl
index 4729d090f8..428e5587ba 100644
--- a/lib/os_mon/src/memsup.erl
+++ b/lib/os_mon/src/memsup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -701,6 +701,7 @@ get_os_wordsize_with_uname() ->
"sparc64" -> 64;
"amd64" -> 64;
"ppc64" -> 64;
+ "s390x" -> 64;
_ -> 32
end.
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index 1ac0fb1d27..59a3d9dee4 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.4.1
+OS_MON_VSN = 2.4.2
diff --git a/lib/parsetools/doc/src/notes.xml b/lib/parsetools/doc/src/notes.xml
index b826b4d03a..5a16445577 100644
--- a/lib/parsetools/doc/src/notes.xml
+++ b/lib/parsetools/doc/src/notes.xml
@@ -31,6 +31,42 @@
</header>
<p>This document describes the changes made to the Parsetools application.</p>
+<section><title>Parsetools 2.1.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct counting of newlines when rules with newlines
+ are used in Leex. </p>
+ <p>
+ Own Id: OTP-13916 Aux Id: ERL-263 </p>
+ </item>
+ <item>
+ <p> Correct handling of Unicode in Leex. </p>
+ <p>
+ Own Id: OTP-13919</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Parsetools 2.1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Yecc generates Dialyzer suppressions to avoid
+ warnings when operator precedence declarations are used.
+ </p>
+ <p>
+ Own Id: OTP-13681</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Parsetools 2.1.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/parsetools/doc/src/yecc.xml b/lib/parsetools/doc/src/yecc.xml
index 6ec9fb5f0e..9188bd2a22 100644
--- a/lib/parsetools/doc/src/yecc.xml
+++ b/lib/parsetools/doc/src/yecc.xml
@@ -201,7 +201,7 @@
<p>The grammar starts with an optional <c>header</c> section. The
header is put first in the generated file, before the module
declaration. The purpose of the header is to provide a means to
- make the documentation generated by <c>EDoc</c> look nicer. Each
+ make the documentation generated by EDoc look nicer. Each
header line should be enclosed in double quotes, and newlines
will be inserted between the lines. For example:</p>
<code>
diff --git a/lib/parsetools/include/leexinc.hrl b/lib/parsetools/include/leexinc.hrl
index 2657fdcfaa..b4449607cb 100644
--- a/lib/parsetools/include/leexinc.hrl
+++ b/lib/parsetools/include/leexinc.hrl
@@ -36,8 +36,10 @@ string(Ics0, L0, Tcs, Ts) ->
string_cont(Ics1, L1, yyaction(A, Alen, Tcs, L0), Ts);
{reject,_Alen,Tlen,_Ics1,L1,_S1} -> % After a non-accepting state
{error,{L0,?MODULE,{illegal,yypre(Tcs, Tlen+1)}},L1};
- {A,Alen,_Tlen,_Ics1,_L1,_S1} ->
- string_cont(yysuf(Tcs, Alen), L0, yyaction(A, Alen, Tcs, L0), Ts)
+ {A,Alen,Tlen,_Ics1,L1,_S1} ->
+ Tcs1 = yysuf(Tcs, Alen),
+ L2 = adjust_line(Tlen, Alen, Tcs1, L1),
+ string_cont(Tcs1, L2, yyaction(A, Alen, Tcs, L0), Ts)
end.
%% string_cont(RestChars, Line, Token, Tokens)
@@ -107,8 +109,10 @@ token(S0, Ics0, L0, Tcs, Tlen0, Tline, A0, Alen0) ->
{reject,_Alen1,Tlen1,Ics1,L1,_S1} -> % No token match
Error = {Tline,?MODULE,{illegal,yypre(Tcs, Tlen1+1)}},
{done,{error,Error,L1},Ics1};
- {A1,Alen1,_Tlen1,_Ics1,_L1,_S1} -> % Use last accept match
- token_cont(yysuf(Tcs, Alen1), L0, yyaction(A1, Alen1, Tcs, Tline))
+ {A1,Alen1,Tlen1,_Ics1,L1,_S1} -> % Use last accept match
+ Tcs1 = yysuf(Tcs, Alen1),
+ L2 = adjust_line(Tlen1, Alen1, Tcs1, L1),
+ token_cont(Tcs1, L2, yyaction(A1, Alen1, Tcs, Tline))
end.
%% token_cont(RestChars, Line, Token)
@@ -181,9 +185,11 @@ tokens(S0, Ics0, L0, Tcs, Tlen0, Tline, Ts, A0, Alen0) ->
%% Skip rest of tokens.
Error = {L1,?MODULE,{illegal,yypre(Tcs, Tlen1+1)}},
skip_tokens(yysuf(Tcs, Tlen1+1), L1, Error);
- {A1,Alen1,_Tlen1,_Ics1,_L1,_S1} ->
+ {A1,Alen1,Tlen1,_Ics1,L1,_S1} ->
Token = yyaction(A1, Alen1, Tcs, Tline),
- tokens_cont(yysuf(Tcs, Alen1), L0, Token, Ts)
+ Tcs1 = yysuf(Tcs, Alen1),
+ L2 = adjust_line(Tlen1, Alen1, Tcs1, L1),
+ tokens_cont(Tcs1, L2, Token, Ts)
end.
%% tokens_cont(RestChars, Line, Token, Tokens)
@@ -235,9 +241,11 @@ skip_tokens(S0, Ics0, L0, Tcs, Tlen0, Tline, Error, A0, Alen0) ->
{done,{error,Error,L1},eof};
{reject,_Alen1,Tlen1,_Ics1,L1,_S1} ->
skip_tokens(yysuf(Tcs, Tlen1+1), L1, Error);
- {A1,Alen1,_Tlen1,_Ics1,L1,_S1} ->
+ {A1,Alen1,Tlen1,_Ics1,L1,_S1} ->
Token = yyaction(A1, Alen1, Tcs, Tline),
- skip_cont(yysuf(Tcs, Alen1), L1, Token, Error)
+ Tcs1 = yysuf(Tcs, Alen1),
+ L2 = adjust_line(Tlen1, Alen1, Tcs1, L1),
+ skip_cont(Tcs1, L2, Token, Error)
end.
%% skip_cont(RestChars, Line, Token, Error)
@@ -269,6 +277,17 @@ yyrev(List, Tail) -> lists:reverse(List, Tail).
yypre(List, N) -> lists:sublist(List, N).
yysuf(List, N) -> lists:nthtail(N, List).
+%% adjust_line(TokenLength, AcceptLength, Chars, Line) -> NewLine
+%% Make sure that newlines in Chars are not counted twice.
+%% Line has been updated with respect to newlines in the prefix of
+%% Chars consisting of (TokenLength - AcceptLength) characters.
+
+adjust_line(N, N, _Cs, L) -> L;
+adjust_line(T, A, [$\n|Cs], L) ->
+ adjust_line(T-1, A, Cs, L-1);
+adjust_line(T, A, [_|Cs], L) ->
+ adjust_line(T-1, A, Cs, L).
+
%% yystate() -> InitialState.
%% yystate(State, InChars, Line, CurrTokLen, AcceptAction, AcceptLen) ->
%% {Action, AcceptLen, RestChars, Line} |
diff --git a/lib/parsetools/src/leex.erl b/lib/parsetools/src/leex.erl
index 15d42a4d9c..602e47404d 100644
--- a/lib/parsetools/src/leex.erl
+++ b/lib/parsetools/src/leex.erl
@@ -1586,6 +1586,8 @@ out_dfa_graph(St, DFA, DF) ->
case file:open(St#leex.gfile, [write]) of
{ok,Gfile} ->
try
+ %% Set the same encoding as infile:
+ set_encoding(St, Gfile),
io:fwrite(Gfile, "digraph DFA {~n", []),
out_dfa_states(Gfile, DFA, DF),
out_dfa_edges(Gfile, DFA),
@@ -1621,7 +1623,7 @@ out_dfa_edges(File, DFA) ->
foreach(fun (T) ->
Crs = orddict:fetch(T, Tdict),
Edgelab = dfa_edgelabel(Crs),
- io:fwrite(File, " ~b -> ~b [label=\"~s\"];~n",
+ io:fwrite(File, " ~b -> ~b [label=\"~ts\"];~n",
[S,T,Edgelab])
end, sort(orddict:fetch_keys(Tdict)))
end, DFA).
diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl
index 1b426141a1..f6b80eb1b4 100644
--- a/lib/parsetools/src/yecc.erl
+++ b/lib/parsetools/src/yecc.erl
@@ -1978,7 +1978,8 @@ output_goto(St, [{_Nonterminal, []} | Go], StateInfo) ->
output_goto(St, Go, StateInfo);
output_goto(St0, [{Nonterminal, List} | Go], StateInfo) ->
F = function_name(yeccgoto, Nonterminal),
- St10 = output_goto1(St0, List, F, StateInfo, true),
+ St05 = fwrite(St0, <<"-dialyzer({nowarn_function, ~w/7}).\n">>, [F]),
+ St10 = output_goto1(St05, List, F, StateInfo, true),
St = output_goto_fini(F, Nonterminal, St10),
output_goto(St, Go, StateInfo);
output_goto(St, [], _StateInfo) ->
diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl
index 949ef3c36e..54602848ec 100644
--- a/lib/parsetools/test/leex_SUITE.erl
+++ b/lib/parsetools/test/leex_SUITE.erl
@@ -45,7 +45,7 @@
pt/1, man/1, ex/1, ex2/1, not_yet/1,
line_wrap/1,
- otp_10302/1, otp_11286/1, unicode/1]).
+ otp_10302/1, otp_11286/1, unicode/1, otp_13916/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -62,12 +62,12 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [{group, checks}, {group, examples}, {group, bugs}].
+ [{group, checks}, {group, examples}, {group, tickets}, {group, bugs}].
groups() ->
[{checks, [], [file, compile, syntax]},
{examples, [], [pt, man, ex, ex2, not_yet, unicode]},
- {tickets, [], [otp_10302, otp_11286]},
+ {tickets, [], [otp_10302, otp_11286, otp_13916]},
{bugs, [], [line_wrap]}].
init_per_suite(Config) ->
@@ -408,12 +408,12 @@ unicode(Config) when is_list(Config) ->
Ts = [{unicode_1,
<<"%% -*- coding: utf-8 -*-\n"
"Definitions.\n"
- "RTLarrow = (←)\n"
+ "RTLarrow = (←)\n"
"Rules.\n"
- "{RTLarrow} : {token,{'<-',TokenLine}}.\n"
+ "{RTLarrow} : {token,{\"←\",TokenLine}}.\n"
"Erlang code.\n"
"-export([t/0]).\n"
- "t() -> {ok, [{'<-', 1}], 1} = string(\"←\"), ok.">>,
+ "t() -> {ok, [{\"←\", 1}], 1} = string(\"←\"), ok.">>,
default,
ok}],
@@ -1052,7 +1052,7 @@ otp_11286(Config) when is_list(Config) ->
Dir = ?privdir,
UName = [1024] ++ "u",
UDir = filename:join(Dir, UName),
- ok = rpc:call(Node, file, make_dir, [UDir]),
+ _ = rpc:call(Node, file, make_dir, [UDir]),
%% Note: Cannot use UName as filename since the filename is used
%% as module name. To be fixed in R18.
@@ -1095,6 +1095,42 @@ otp_11286(Config) when is_list(Config) ->
true = test_server:stop_node(Node),
ok.
+otp_13916(doc) ->
+ "OTP-13916. Leex rules with newlines result in bad line numbers";
+otp_13916(suite) -> [];
+otp_13916(Config) when is_list(Config) ->
+ Ts = [{otp_13916_1,
+ <<"Definitions.\n"
+ "W = [a-zA-Z0-9]\n"
+ "S = [\\s\\t]\n"
+ "B = [\\n\\r]\n"
+ "Rules.\n"
+ "%% mark line break(s) and empty lines by token 'break'\n"
+ "%% in order to use as delimiters\n"
+ "{B}({S}*{B})+ : {token, {break, TokenLine}}.\n"
+ "{B} : {token, {break, TokenLine}}.\n"
+ "{S}+ : {token, {blank, TokenLine, TokenChars}}.\n"
+ "{W}+ : {token, {word, TokenLine, TokenChars}}.\n"
+ "Erlang code.\n"
+ "-export([t/0]).\n"
+ "t() ->\n"
+ " {ok,[{break,1},{blank,4,\" \"},{word,4,\"breaks\"}],4} =\n"
+ " string(\"\\n\\n \\n breaks\"),\n"
+ " {ok,[{break,1},{word,4,\"works\"}],4} =\n"
+ " string(\"\\n\\n \\nworks\"),\n"
+ " {ok,[{break,1},{word,4,\"L4\"},{break,4},\n"
+ " {word,5,\"L5\"},{break,5},{word,7,\"L7\"}], 7} =\n"
+ " string(\"\\n\\n \\nL4\\nL5\\n\\nL7\"),\n"
+ " {ok,[{break,1},{blank,4,\" \"},{word,4,\"L4\"},\n"
+ " {break,4},{blank,5,\" \"},{word,5,\"L5\"},\n"
+ " {break,5},{blank,7,\" \"},{word,7,\"L7\"}], 7} =\n"
+ " string(\"\\n\\n \\n L4\\n L5\\n\\n L7\"),\n"
+ " ok.\n">>,
+ default,
+ ok}],
+ ?line run(Config, Ts),
+ ok.
+
start_node(Name, Args) ->
[_,Host] = string:tokens(atom_to_list(node()), "@"),
ct:log("Trying to start ~w@~s~n", [Name,Host]),
@@ -1137,7 +1173,7 @@ run_test(Config, Def, Pre) ->
XrlFile = filename:join(DataDir, DefFile),
ErlFile = filename:join(DataDir, Filename),
Opts = [return, warn_unused_vars,{outdir,DataDir}],
- ok = file:write_file(XrlFile, Def, [{encoding, unicode}]),
+ ok = file:write_file(XrlFile, Def),
LOpts = [return, {report, false} |
case Pre of
default ->
diff --git a/lib/parsetools/test/yecc_SUITE.erl b/lib/parsetools/test/yecc_SUITE.erl
index 3710569aba..5bd71d5d19 100644
--- a/lib/parsetools/test/yecc_SUITE.erl
+++ b/lib/parsetools/test/yecc_SUITE.erl
@@ -342,7 +342,7 @@ syntax(Config) when is_list(Config) ->
{L2,_,{bad_inline,{yeccpars2_2_,1}}}]}],
[]} = compile:file(Parserfile1, [basic_validation,return]),
?line L1 = 31 + SzYeccPre,
- ?line L2 = 38 + SzYeccPre
+ ?line L2 = 39 + SzYeccPre
end(),
%% Bad macro in action. OTP-7224.
@@ -360,7 +360,7 @@ syntax(Config) when is_list(Config) ->
{L2,_,{bad_inline,{yeccpars2_2_,1}}}]}],
[]} = compile:file(Parserfile1, [basic_validation,return]),
?line L1 = 31 + SzYeccPre,
- ?line L2 = 38 + SzYeccPre
+ ?line L2 = 39 + SzYeccPre
end(),
%% Check line numbers. OTP-7224.
@@ -1623,7 +1623,7 @@ otp_7292(Config) when is_list(Config) ->
[{_,[{16,_,{unused_function,{foo,0}}}]}]} =
compile:file(Parserfile1, [basic_validation, return]),
L1 = 41 + SzYeccPre,
- L2 = 48 + SzYeccPre
+ L2 = 49 + SzYeccPre
end(),
YeccPre = filename:join(Dir, "yeccpre.hrl"),
@@ -1641,7 +1641,7 @@ otp_7292(Config) when is_list(Config) ->
[{_,[{16,_,{unused_function,{foo,0}}}]}]} =
compile:file(Parserfile1, [basic_validation, return]),
?line L1 = 40 + SzYeccPre,
- ?line L2 = 47 + SzYeccPre
+ ?line L2 = 48 + SzYeccPre
end(),
file:delete(YeccPre),
@@ -2009,7 +2009,7 @@ otp_11286(Config) when is_list(Config) ->
Dir = ?privdir,
UName = [1024] ++ "u",
UDir = filename:join(Dir, UName),
- ok = rpc:call(Node, file, make_dir, [UDir]),
+ _ = rpc:call(Node, file, make_dir, [UDir]),
%% Note: Cannot use UName as filename since the filename is used
%% as module name. To be fixed in R18.
diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk
index befdd82d6e..d102c63730 100644
--- a/lib/parsetools/vsn.mk
+++ b/lib/parsetools/vsn.mk
@@ -1 +1 @@
-PARSETOOLS_VSN = 2.1.2
+PARSETOOLS_VSN = 2.1.4
diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index c4d930c01f..92e314186e 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -35,6 +35,51 @@
<file>notes.xml</file>
</header>
+<section><title>Public_Key 1.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ New function <c>pkix_verify_hostname/2,3</c> Implements
+ certificate hostname checking. See the manual and RFC
+ 6125.</p>
+ <p>
+ Own Id: OTP-13009</p>
+ </item>
+ <item>
+ <p>
+ The ssh host key fingerprint generation now also takes a
+ list of algorithms and returns a list of corresponding
+ fingerprints. See
+ <c>public_key:ssh_hostkey_fingerprint/2</c> and the
+ option <c>silently_accept_hosts</c> in
+ <c>ssh:connect</c>.</p>
+ <p>
+ Own Id: OTP-14223</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Public_Key 1.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ New function
+ <c>public_key:ssh_hostkey_fingerprint/1,2</c> to
+ calculate the SSH host key fingerprint string.</p>
+ <p>
+ Own Id: OTP-13888 Aux Id: OTP-13887 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 1.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 385604677c..a34c9de76b 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2015</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -757,6 +757,39 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
</func>
<func>
+ <name>pkix_verify_hostname(Cert, ReferenceIDs) -> boolean()</name>
+ <name>pkix_verify_hostname(Cert, ReferenceIDs, Opts) -> boolean()</name>
+ <fsummary>Verifies that a PKIX x.509 certificate <i>presented identifier</i> (e.g hostname) is
+ an expected one.</fsummary>
+ <type>
+ <v>Cert = der_encoded() | #'OTPCertificate'{} </v>
+ <v>ReferenceIDs = [ RefID ]</v>
+ <v>RefID = {IdType,string()}</v>
+ <v>IdType = dns_id | srv_id | uri_id</v>
+ <v>Opts = [ PvhOpt() ]</v>
+ <v>PvhOpt = [MatchOpt | FailCallBackOpt | FqdnExtractOpt]</v>
+ <v>MatchOpt = {fun(RefId | FQDN::string(), PresentedID) -> boolean() | default}</v>
+ <v>PresentedID = {dNSName,string()} | {uniformResourceIdentifier,string()}</v>
+ <v>FailCallBackOpt = {fail_callback, fun(#'OTPCertificate'{}) -> boolean()}</v>
+ <v>FqdnExtractOpt = {fqdn_fun, fun(RefID) -> FQDN::string() | default | undefined}</v>
+ </type>
+ <desc>
+ <p>This function checks that the <i>Presented Identifier</i> (e.g hostname) in a peer certificate
+ conforms with the Expected Identifier that the client wants to connect to.
+ This functions is intended to be added as an extra client check to the peer certificate when performing
+ <seealso marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso>
+ </p>
+ <p>See <url href="https://tools.ietf.org/html/rfc6125">RFC 6125</url>
+ for detailed information about hostname verification.
+ The <seealso marker="using_public_key#verify_hostname">User's Manual</seealso>
+ and
+ <seealso marker="using_public_key#verify_hostname_examples">code examples</seealso>
+ describes this function more detailed.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name>sign(Msg, DigestType, Key) -> binary()</name>
<fsummary>Creates a digital signature.</fsummary>
<type>
@@ -822,6 +855,41 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
</func>
<func>
+ <name>ssh_hostkey_fingerprint(HostKey) -> string()</name>
+ <name>ssh_hostkey_fingerprint(DigestType, HostKey) -> string()</name>
+ <name>ssh_hostkey_fingerprint([DigestType], HostKey) -> [string()]</name>
+ <fsummary>Calculates a ssh fingerprint for a hostkey.</fsummary>
+ <type>
+ <v>Key = public_key()</v>
+ <v>DigestType = digest_type()</v>
+ </type>
+ <desc>
+ <p>Calculates a ssh fingerprint from a public host key as openssh does.</p>
+ <p>The algorithm in <c>ssh_hostkey_fingerprint/1</c> is md5 to be compatible with older
+ ssh-keygen commands. The string from the second variant is prepended by the algorithm name
+ in uppercase as in newer ssh-keygen commands.</p>
+ <p>Examples:</p>
+ <code>
+ 2> public_key:ssh_hostkey_fingerprint(Key).
+ "f5:64:a6:c1:5a:cb:9f:0a:10:46:a2:5c:3e:2f:57:84"
+
+ 3> public_key:ssh_hostkey_fingerprint(md5,Key).
+ "MD5:f5:64:a6:c1:5a:cb:9f:0a:10:46:a2:5c:3e:2f:57:84"
+
+ 4> public_key:ssh_hostkey_fingerprint(sha,Key).
+ "SHA1:bSLY/C4QXLDL/Iwmhyg0PGW9UbY"
+
+ 5> public_key:ssh_hostkey_fingerprint(sha256,Key).
+ "SHA256:aZGXhabfbf4oxglxltItWeHU7ub3Dc31NcNw2cMJePQ"
+
+ 6> public_key:ssh_hostkey_fingerprint([sha,sha256],Key).
+ ["SHA1:bSLY/C4QXLDL/Iwmhyg0PGW9UbY",
+ "SHA256:aZGXhabfbf4oxglxltItWeHU7ub3Dc31NcNw2cMJePQ"]
+ </code>
+ </desc>
+ </func>
+
+ <func>
<name>verify(Msg, DigestType, Signature, Key) -> boolean()</name>
<fsummary>Verifies a digital signature.</fsummary>
<type>
diff --git a/lib/public_key/doc/src/public_key_app.xml b/lib/public_key/doc/src/public_key_app.xml
index 1f87932b6c..923a9f1dfb 100644
--- a/lib/public_key/doc/src/public_key_app.xml
+++ b/lib/public_key/doc/src/public_key_app.xml
@@ -61,7 +61,7 @@
<section>
<title>DEPENDENCIES</title>
<p>The <c>public_key</c> application uses the
- Crypto application to preform cryptographic operations and the
+ Crypto application to perform cryptographic operations and the
ASN-1 application to handle PKIX-ASN-1 specifications, hence
these applications must be loaded for the <c>public_key</c> application to work.
In an embedded environment this means they must be started with
@@ -72,7 +72,7 @@
<section>
<title>ERROR LOGGER AND EVENT HANDLERS</title>
<p> The <c>public_key</c> application is a library application
- and does not use the error logger. The functions will either sucssed
+ and does not use the error logger. The functions will either succeed
or fail with a runtime error.
</p>
</section>
diff --git a/lib/public_key/doc/src/using_public_key.xml b/lib/public_key/doc/src/using_public_key.xml
index e3a1eed4be..417d479da3 100644
--- a/lib/public_key/doc/src/using_public_key.xml
+++ b/lib/public_key/doc/src/using_public_key.xml
@@ -417,6 +417,259 @@ true = public_key:verify(Digest, none, Signature, PublicKey),</code>
</section>
+ <section>
+ <marker id="verify_hostname"></marker>
+ <title>Verifying a certificate hostname</title>
+ <section>
+ <title>Background</title>
+ <p>When a client checks a server certificate there are a number of checks available like
+ checks that the certificate is not revoked, not forged or not out-of-date.
+ </p>
+ <p>There are however attacks that are not detected by those checks. Suppose a bad guy has
+ succeded with a DNS infection. Then the client could belive it is connecting to one host but
+ ends up at another but evil one. Though it is evil, it could have a perfectly legal
+ certificate! The certificate has a valid signature, it is not revoked, the certificate chain
+ is not faked and has a trusted root and so on.
+ </p>
+ <p>To detect that the server is not the intended one, the client must additionaly perform
+ a <i>hostname verification</i>. This procedure is described in
+ <url href="https://tools.ietf.org/html/rfc6125">RFC 6125</url>. The idea is that the certificate
+ lists the hostnames it could be fetched from. This is checked by the certificate issuer when
+ the certificate is signed. So if the certificate is issued by a trusted root the client
+ could trust the host names signed in it.
+ </p>
+ <p>There is a default hostname matching procedure defined in
+ <url href="https://tools.ietf.org/html/rfc6125#section-6">RFC 6125, section 6</url>
+ as well as protocol dependent variations defined in
+ <url href="https://tools.ietf.org/html/rfc6125#appendix-B">RFC 6125 appendix B</url>.
+ The default procedure is implemented in
+ <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2,3</seealso>.
+ It is possible for a client to hook in modified rules using the options list.
+ </p>
+ <p>Some terminology is needed: the certificate presents hostname(s) on which it is valid.
+ Those are called <i>Presented IDs</i>. The hostname(s) the client belives it connects to
+ are called <i>Reference IDs</i>. The matching rules aims to verify that there is at least
+ one of the Reference IDs that matches one of the Presented IDs. If not, the verification fails.
+ </p>
+ <p>The IDs contains normal fully qualified domain names like e.g <c>foo.example.com</c>,
+ but IP addresses are not recommended. The rfc describes why this is not recommended as well
+ as security considerations about how to aquire the Reference IDs.
+ </p>
+ <p>Internationalized domain names are not supported.
+ </p>
+ </section>
+ <section>
+ <title>The verification process</title>
+ <p>Traditionally the Presented IDs were found in the <c>Subject</c> certificate field as <c>CN</c>
+ names. This is still quite common. When printing a certificate they show up as:
+ </p>
+ <code>
+ $ openssl x509 -text &lt; cert.pem
+ ...
+ Subject: C=SE, CN=example.com, CN=*.example.com, O=erlang.org
+ ...
+ </code>
+ <p>The example <c>Subject</c> field has one C, two CN and one O part. It is only the
+ CN (Common Name) that is used by hostname verification. The two other (C and O) is not used
+ here even when they contain a domain name like the O part. The C and O parts are defined
+ elsewhere and meaningful only for other functions.
+ </p>
+ <p>In the example the Presented IDs are <c>example.com</c> as well as hostnames matching
+ <c>*.example.com</c>. For example <c>foo.example.com</c> and <c>bar.example.com</c> both
+ matches but not <c>foo.bar.example.com</c>. The name <c>erlang.org</c> matches neither
+ since it is not a CN.
+ </p>
+ <p>In case where the Presented IDs are fetched from the <c>Subject</c> certificate field, the
+ names may contain wildcard characters. The function handles this as defined in
+ <url href="https://tools.ietf.org/html/rfc6125#section-6.4.3">chapter 6.4.3 in RFC 6125</url>.
+ </p>
+ <p>There may only be one wildcard character and that is in the first label, for example:
+ <c>*.example.com</c>. This matches <c>foo.example.com</c> but neither <c>example.com</c> nor
+ <c>foo.bar.example.com</c>.
+ </p>
+ <p>There may be label characters before or/and after the wildcard. For example:
+ <c>a*d.example.com</c> matches <c>abcd.example.com</c> and <c>ad.example.com</c>,
+ but not <c>ab.cd.example.com</c>.
+ </p>
+ <p>In the previous example there is no indication of which protocols are expected. So a client
+ has no indication of whether it is a web server, an ldap server or maybe a sip server it is
+ connected to.
+ There are fields in the certificate that can indicate this. To be more exact, the rfc
+ introduces the usage of the <c>X509v3 Subject Alternative Name</c> in the <c>X509v3 extensions</c>
+ field:
+ </p>
+ <code>
+ $ openssl x509 -text &lt; cert.pem
+ ...
+ X509v3 extensions:
+ X509v3 Subject Alternative Name:
+ DNS:kb.example.org, URI:https://www.example.org
+ ...
+ </code>
+ <p>Here <c>kb.example.org</c> serves any protocol while <c>www.example.org</c> presents a secure
+ web server.
+ </p>
+
+ <p>The next example has both <c>Subject</c> and <c>Subject Alternate Name</c> present:</p>
+ <code>
+ $ openssl x509 -text &lt; cert.pem
+ ...
+ Subject: C=SE, CN=example.com, CN=*.example.com, O=erlang.org
+ ...
+ X509v3 extensions:
+ X509v3 Subject Alternative Name:
+ DNS:kb.example.org, URI:https://www.example.org
+ ...
+ </code>
+ <p>The RFC states that if a certificate defines Reference IDs in a <c>Subject Alternate Name</c>
+ field, the <c>Subject</c> field MUST NOT be used for host name checking, even if it contains
+ valid CN names.
+ Therefore only <c>kb.example.org</c> and <c>https://www.example.org</c> matches. The match fails
+ both for <c>example.com</c> and <c>foo.example.com</c> becuase they are in the <c>Subject</c>
+ field which is not checked because the <c>Subject Alternate Name</c> field is present.
+ </p>
+ </section>
+
+ <section>
+ <marker id="verify_hostname_examples"></marker>
+ <title>Function call examples</title>
+ <note>
+ <p>Other applications like ssl/tls or https might have options that are passed
+ down to the <c>public_key:pkix_verify_hostname</c>. You will probably not
+ have to call it directly</p>
+ </note>
+ <p>Suppose our client expects to connect to the web server https://www.example.net. This
+ URI is therefore the Reference IDs of the client.
+ The call will be:
+ </p>
+ <code>
+ public_key:pkix_verify_hostname(CertFromHost,
+ [{uri_id, "https://www.example.net"}
+ ]).
+ </code>
+ <p>The call will return <c>true</c> or <c>false</c> depending on the check. The caller
+ do not need to handle the matching rules in the rfc. The matching will proceed as:
+ </p>
+ <list>
+ <item>If there is a <c>Subject Alternate Name</c> field, the <c>{uri_id,string()}</c> in the
+ function call will be compared to any
+ <c>{uniformResourceIdentifier,string()}</c> in the Certificate field.
+ If the two <c>strings()</c> are equal (case insensitive), there is a match.
+ The same applies for any <c>{dns_id,string()}</c> in the call which is compared
+ with all <c>{dNSName,string()}</c> in the Certificate field.
+ </item>
+ <item>If there is NO <c>Subject Alternate Name</c> field, the <c>Subject</c> field will be
+ checked. All <c>CN</c> names will be compared to all hostnames <i>extracted</i> from
+ <c>{uri_id,string()}</c> and from <c>{dns_id,string()}</c>.
+ </item>
+ </list>
+ </section>
+ <section>
+ <title>Extending the search mechanism</title>
+ <p>The caller can use own extraction and matching rules. This is done with the two options
+ <c>fqdn_fun</c> and <c>match_fun</c>.
+ </p>
+ <section>
+ <title>Hostname extraction</title>
+ <p>The <c>fqdn_fun</c> extracts hostnames (Fully Qualified Domain Names) from uri_id
+ or other ReferenceIDs that are not pre-defined in the public_key function.
+ Suppose you have some URI with a very special protocol-part:
+ <c>myspecial://example.com"</c>. Since this a non-standard URI there will be no hostname
+ extracted for matching CN-names in the <c>Subject</c>.</p>
+ <p>To "teach" the function how to extract, you can give a fun which replaces the default
+ extraction function.
+ The <c>fqdn_fun</c> takes one argument and returns
+ either a <c>string()</c> to be matched to each CN-name or the atom <c>default</c> which will invoke
+ the default fqdn extraction function. The return value <c>undefined</c> removes the current
+ URI from the fqdn extraction.
+ </p>
+ <code>
+ ...
+ Extract = fun({uri_id, "myspecial://"++HostName}) -> HostName;
+ (_Else) -> default
+ end,
+ ...
+ public_key:pkix_verify_hostname(CertFromHost, RefIDs,
+ [{fqdn_fun, Extract}])
+ ...
+ </code>
+ </section>
+ <section>
+ <title>Re-defining the match operations</title>
+ <p>The default matching handles dns_id and uri_id. In an uri_id the value is tested for
+ equality with a value from the <c>Subject Alternate Name</c>. If som other kind of matching
+ is needed, use the <c>match_fun</c> option.
+ </p>
+ <p>The <c>match_fun</c> takes two arguments and returns either <c>true</c>,
+ <c>false</c> or <c>default</c>. The value <c>default</c> will invoke the default
+ match function.
+ </p>
+ <code>
+ ...
+ Match = fun({uri_id,"myspecial://"++A},
+ {uniformResourceIdentifier,"myspecial://"++B}) ->
+ my_match(A,B);
+ (_RefID, _PresentedID) ->
+ default
+ end,
+ ...
+ public_key:pkix_verify_hostname(CertFromHost, RefIDs,
+ [{match_fun, Match}]),
+ ...
+ </code>
+ <p>In case of a match operation between a ReferenceID and a CN value from the <c>Subject</c>
+ field, the first argument to the fun is the extracted hostname from the ReferenceID, and the
+ second argument is the tuple <c>{cn, string()}</c> taken from the <c>Subject</c> field. That
+ makes it possible to have separate matching rules for Presented IDs from the <c>Subject</c>
+ field and from the <c>Subject Alternate Name</c> field.
+ </p>
+ <p>The default matching transformes the ascii values in strings to lowercase before comparing.
+ The <c>match_fun</c> is however called without any transfomation applied to the strings. The
+ reason is to enable the user to do unforseen handling of the strings where the original format
+ is needed.
+ </p>
+ </section>
+ </section>
+ <section>
+ <title>"Pinning" a Certificate</title>
+ <p>The <url href="https://tools.ietf.org/html/rfc6125">RFC 6125</url> defines <i>pinning</i>
+ as:</p>
+ <quote>
+ <p>"The act of establishing a cached name association between
+ the application service's certificate and one of the client's
+ reference identifiers, despite the fact that none of the presented
+ identifiers matches the given reference identifier. ..."
+ </p>
+ </quote>
+ <p>The purpose is to have a mechanism for a human to accept an otherwise faulty Certificate.
+ In for example a web browser, you could get a question like </p>
+ <quote>
+ <p>Warning: you wanted to visit the site www.example.com,
+ but the certificate is for shop.example.com. Accept anyway (yes/no)?"
+ </p>
+ </quote>
+ <p>This could be accomplished with the option <c>fail_callback</c> which will
+ be called if the hostname verification fails:
+ </p>
+ <code>
+ -include_lib("public_key/include/public_key.hrl"). % Record def
+ ...
+ Fail = fun(#'OTPCertificate'{}=C) ->
+ case in_my_cache(C) orelse my_accept(C) of
+ true ->
+ enter_my_cache(C),
+ true;
+ false ->
+ false
+ end,
+ ...
+ public_key:pkix_verify_hostname(CertFromHost, RefIDs,
+ [{fail_callback, Fail}]),
+ ...
+ </code>
+ </section>
+ </section>
+
<section>
<title>SSH Files</title>
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index d23abfe256..730f90e745 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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,7 +48,9 @@
pkix_issuer_id/2,
pkix_normalize_name/1,
pkix_path_validation/3,
+ pkix_verify_hostname/2, pkix_verify_hostname/3,
ssh_decode/2, ssh_encode/2,
+ ssh_hostkey_fingerprint/1, ssh_hostkey_fingerprint/2,
ssh_curvename2oid/1, oid2ssh_curvename/1,
pkix_crls_validate/3,
pkix_dist_point/1,
@@ -91,7 +93,8 @@
-type public_crypt_options() :: [{rsa_pad, rsa_padding()}].
-type rsa_digest_type() :: 'md5' | 'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512'.
-type dss_digest_type() :: 'none' | 'sha'. %% None is for backwards compatibility
--type ecdsa_digest_type() :: 'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512'.
+-type ecdsa_digest_type() :: 'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512'.
+-type digest_type() :: rsa_digest_type() | dss_digest_type() | ecdsa_digest_type().
-type crl_reason() :: unspecified | keyCompromise | cACompromise | affiliationChanged | superseded
| cessationOfOperation | certificateHold | privilegeWithdrawn | aACompromise.
-type oid() :: tuple().
@@ -761,6 +764,76 @@ pkix_crls_validate(OtpCert, DPAndCRLs0, Options) ->
pkix_crls_validate(OtpCert, DPAndCRLs, DPAndCRLs,
Options, pubkey_crl:init_revokation_state()).
+%--------------------------------------------------------------------
+-spec pkix_verify_hostname(Cert :: #'OTPCertificate'{} | binary(),
+ ReferenceIDs :: [{uri_id | dns_id | oid(), string()}]) -> boolean().
+
+-spec pkix_verify_hostname(Cert :: #'OTPCertificate'{} | binary(),
+ ReferenceIDs :: [{uri_id | dns_id | oid(), string()}],
+ Options :: proplists:proplist()) -> boolean().
+
+%% Description: Validates a hostname to RFC 6125
+%%--------------------------------------------------------------------
+pkix_verify_hostname(Cert, ReferenceIDs) ->
+ pkix_verify_hostname(Cert, ReferenceIDs, []).
+
+pkix_verify_hostname(BinCert, ReferenceIDs, Options) when is_binary(BinCert) ->
+ pkix_verify_hostname(pkix_decode_cert(BinCert,otp), ReferenceIDs, Options);
+
+pkix_verify_hostname(Cert = #'OTPCertificate'{tbsCertificate = TbsCert}, ReferenceIDs0, Opts) ->
+ MatchFun = proplists:get_value(match_fun, Opts, undefined),
+ FailCB = proplists:get_value(fail_callback, Opts, fun(_Cert) -> false end),
+ FqdnFun = proplists:get_value(fqdn_fun, Opts, fun verify_hostname_extract_fqdn_default/1),
+
+ ReferenceIDs = [{T,to_string(V)} || {T,V} <- ReferenceIDs0],
+ PresentedIDs =
+ try lists:keyfind(?'id-ce-subjectAltName',
+ #'Extension'.extnID,
+ TbsCert#'OTPTBSCertificate'.extensions)
+ of
+ #'Extension'{extnValue = ExtVals} ->
+ [{T,to_string(V)} || {T,V} <- ExtVals];
+ false ->
+ []
+ catch
+ _:_ -> []
+ end,
+ %% PresentedIDs example: [{dNSName,"ewstest.ericsson.com"}, {dNSName,"www.ericsson.com"}]}
+ case PresentedIDs of
+ [] ->
+ %% Fallback to CN-ids [rfc6125, ch6]
+ case TbsCert#'OTPTBSCertificate'.subject of
+ {rdnSequence,RDNseq} ->
+ PresentedCNs =
+ [{cn, to_string(V)}
+ || ATVs <- RDNseq, % RDNseq is list-of-lists
+ #'AttributeTypeAndValue'{type = ?'id-at-commonName',
+ value = {_T,V}} <- ATVs
+ % _T = kind of string (teletexString etc)
+ ],
+ %% Example of PresentedCNs: [{cn,"www.ericsson.se"}]
+ %% match ReferenceIDs to PresentedCNs
+ verify_hostname_match_loop(verify_hostname_fqnds(ReferenceIDs, FqdnFun),
+ PresentedCNs,
+ MatchFun, FailCB, Cert);
+
+ _ ->
+ false
+ end;
+ _ ->
+ %% match ReferenceIDs to PresentedIDs
+ case verify_hostname_match_loop(ReferenceIDs, PresentedIDs,
+ MatchFun, FailCB, Cert) of
+ false ->
+ %% Try to extract DNS-IDs from URIs etc
+ DNS_ReferenceIDs =
+ [{dns_is,X} || X <- verify_hostname_fqnds(ReferenceIDs, FqdnFun)],
+ verify_hostname_match_loop(DNS_ReferenceIDs, PresentedIDs,
+ MatchFun, FailCB, Cert);
+ true ->
+ true
+ end
+ end.
%%--------------------------------------------------------------------
-spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}]
@@ -819,6 +892,51 @@ oid2ssh_curvename(?'secp384r1') -> <<"nistp384">>;
oid2ssh_curvename(?'secp521r1') -> <<"nistp521">>.
%%--------------------------------------------------------------------
+-spec ssh_hostkey_fingerprint(public_key()) -> string().
+-spec ssh_hostkey_fingerprint( digest_type(), public_key()) -> string()
+ ; ([digest_type()], public_key()) -> [string()]
+ .
+
+ssh_hostkey_fingerprint(Key) ->
+ sshfp_string(md5, public_key:ssh_encode(Key,ssh2_pubkey) ).
+
+ssh_hostkey_fingerprint(HashAlgs, Key) when is_list(HashAlgs) ->
+ EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ [sshfp_full_string(HashAlg,EncKey) || HashAlg <- HashAlgs];
+ssh_hostkey_fingerprint(HashAlg, Key) when is_atom(HashAlg) ->
+ EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ sshfp_full_string(HashAlg, EncKey).
+
+
+sshfp_string(HashAlg, EncodedKey) ->
+ %% Other HashAlgs than md5 will be printed with
+ %% other formats than hextstr by
+ %% ssh-keygen -E <alg> -lf <file>
+ fp_fmt(sshfp_fmt(HashAlg), crypto:hash(HashAlg, EncodedKey)).
+
+sshfp_full_string(HashAlg, EncKey) ->
+ lists:concat([sshfp_alg_name(HashAlg),
+ [$: | sshfp_string(HashAlg, EncKey)]
+ ]).
+
+sshfp_alg_name(sha) -> "SHA1";
+sshfp_alg_name(Alg) -> string:to_upper(atom_to_list(Alg)).
+
+sshfp_fmt(md5) -> hexstr;
+sshfp_fmt(_) -> b64.
+
+fp_fmt(hexstr, Bin) ->
+ lists:flatten(string:join([io_lib:format("~2.16.0b",[C1]) || <<C1>> <= Bin], ":"));
+fp_fmt(b64, Bin) ->
+ %% This function clause *seems* to be
+ %% [C || C<-base64:encode_to_string(Bin), C =/= $=]
+ %% but I am not sure. Must be checked.
+ B64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
+ BitsInLast = 8*size(Bin) rem 6,
+ Padding = (6-BitsInLast) rem 6, % Want BitsInLast = [1:5] to map to padding [5:1] and 0 -> 0
+ [lists:nth(C+1,B64Chars) || <<C:6>> <= <<Bin/binary,0:Padding>> ].
+
+%%--------------------------------------------------------------------
-spec short_name_hash({rdnSequence, [#'AttributeTypeAndValue'{}]}) ->
string().
@@ -1163,3 +1281,96 @@ ascii_to_lower(String) ->
end)>>
||
<<C>> <= iolist_to_binary(String) >>.
+
+%%%----------------------------------------------------------------
+%%% pkix_verify_hostname help functions
+verify_hostname_extract_fqdn_default({dns_id,S}) ->
+ S;
+verify_hostname_extract_fqdn_default({uri_id,URI}) ->
+ {ok,{https,_,Host,_,_,_}} = http_uri:parse(URI),
+ Host.
+
+
+verify_hostname_fqnds(L, FqdnFun) ->
+ [E || E0 <- L,
+ E <- [try case FqdnFun(E0) of
+ default -> verify_hostname_extract_fqdn_default(E0);
+ undefined -> undefined; % will make the "is_list(E)" test fail
+ Other -> Other
+ end
+ catch _:_-> undefined % will make the "is_list(E)" test fail
+ end],
+ is_list(E),
+ E =/= "",
+ {error,einval} == inet:parse_address(E)
+ ].
+
+
+-define(srvName_OID, {1,3,6,1,4,1,434,2,2,1,37,0}).
+
+verify_hostname_match_default(Ref, Pres) ->
+ verify_hostname_match_default0(to_lower_ascii(Ref), to_lower_ascii(Pres)).
+
+verify_hostname_match_default0(FQDN=[_|_], {cn,FQDN}) ->
+ not lists:member($*, FQDN);
+verify_hostname_match_default0(FQDN=[_|_], {cn,Name=[_|_]}) ->
+ [F1|Fs] = string:tokens(FQDN, "."),
+ [N1|Ns] = string:tokens(Name, "."),
+ match_wild(F1,N1) andalso Fs==Ns;
+verify_hostname_match_default0({dns_id,R}, {dNSName,P}) ->
+ R==P;
+verify_hostname_match_default0({uri_id,R}, {uniformResourceIdentifier,P}) ->
+ R==P;
+verify_hostname_match_default0({srv_id,R}, {T,P}) when T == srvName ;
+ T == ?srvName_OID ->
+ R==P;
+verify_hostname_match_default0(_, _) ->
+ false.
+
+
+match_wild(A, [$*|B]) -> match_wild_suffixes(A, B);
+match_wild([C|A], [ C|B]) -> match_wild(A, B);
+match_wild([], []) -> true;
+match_wild(_, _) -> false.
+
+%% Match the parts after the only wildcard by comparing them from the end
+match_wild_suffixes(A, B) -> match_wild_sfx(lists:reverse(A), lists:reverse(B)).
+
+match_wild_sfx([$*|_], _) -> false; % Bad name (no wildcards alowed)
+match_wild_sfx(_, [$*|_]) -> false; % Bad pattern (no more wildcards alowed)
+match_wild_sfx([A|Ar], [A|Br]) -> match_wild_sfx(Ar, Br);
+match_wild_sfx(Ar, []) -> not lists:member($*, Ar); % Chk for bad name (= wildcards)
+match_wild_sfx(_, _) -> false.
+
+
+verify_hostname_match_loop(Refs0, Pres0, undefined, FailCB, Cert) ->
+ Pres = lists:map(fun to_lower_ascii/1, Pres0),
+ Refs = lists:map(fun to_lower_ascii/1, Refs0),
+ lists:any(
+ fun(R) ->
+ lists:any(fun(P) ->
+ verify_hostname_match_default(R,P) orelse FailCB(Cert)
+ end, Pres)
+ end, Refs);
+verify_hostname_match_loop(Refs, Pres, MatchFun, FailCB, Cert) ->
+ lists:any(
+ fun(R) ->
+ lists:any(fun(P) ->
+ (case MatchFun(R,P) of
+ default -> verify_hostname_match_default(R,P);
+ Bool -> Bool
+ end) orelse FailCB(Cert)
+ end,
+ Pres)
+ end,
+ Refs).
+
+
+to_lower_ascii(S) when is_list(S) -> lists:map(fun to_lower_ascii/1, S);
+to_lower_ascii({T,S}) -> {T, to_lower_ascii(S)};
+to_lower_ascii(C) when $A =< C,C =< $Z -> C + ($a-$A);
+to_lower_ascii(C) -> C.
+
+to_string(S) when is_list(S) -> S;
+to_string(B) when is_binary(B) -> binary_to_list(B).
+
diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl
index 004eaefc27..44caf479e5 100644
--- a/lib/public_key/test/pbe_SUITE.erl
+++ b/lib/public_key/test/pbe_SUITE.erl
@@ -219,7 +219,12 @@ pbes2() ->
pbes2(Config) when is_list(Config) ->
decode_encode_key_file("pbes2_des_cbc_enc_key.pem", "password", "DES-CBC", Config),
decode_encode_key_file("pbes2_des_ede3_cbc_enc_key.pem", "password", "DES-EDE3-CBC", Config),
- decode_encode_key_file("pbes2_rc2_cbc_enc_key.pem", "password", "RC2-CBC", Config).
+ case lists:member(rc2_cbc, proplists:get_value(ciphers, crypto:supports())) of
+ true ->
+ decode_encode_key_file("pbes2_rc2_cbc_enc_key.pem", "password", "RC2-CBC", Config);
+ false ->
+ ok
+ end.
check_key_info(#'PrivateKeyInfo'{privateKeyAlgorithm =
#'PrivateKeyInfo_privateKeyAlgorithm'{algorithm = ?rsaEncryption},
diff --git a/lib/public_key/test/public_key.cover b/lib/public_key/test/public_key.cover
index ec00814578..6c46492bec 100644
--- a/lib/public_key/test/public_key.cover
+++ b/lib/public_key/test/public_key.cover
@@ -1,4 +1,4 @@
{incl_app,public_key,details}.
-{excl_mods, public_key, ['OTP-PUB-KEY']}.
+{excl_mods, public_key, ['OTP-PUB-KEY', 'PKCS-FRAME']}.
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index b22b69a0f2..80895ce97c 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -45,7 +45,18 @@ all() ->
{group, sign_verify},
pkix, pkix_countryname, pkix_emailaddress, pkix_path_validation,
pkix_iso_rsa_oid, pkix_iso_dsa_oid, pkix_crl, general_name,
- short_cert_issuer_hash, short_crl_issuer_hash].
+ pkix_verify_hostname_cn,
+ pkix_verify_hostname_subjAltName,
+ pkix_verify_hostname_options,
+ short_cert_issuer_hash, short_crl_issuer_hash,
+ ssh_hostkey_fingerprint_md5_implicit,
+ ssh_hostkey_fingerprint_md5,
+ ssh_hostkey_fingerprint_sha,
+ ssh_hostkey_fingerprint_sha256,
+ ssh_hostkey_fingerprint_sha384,
+ ssh_hostkey_fingerprint_sha512,
+ ssh_hostkey_fingerprint_list
+ ].
groups() ->
[{pem_decode_encode, [], [dsa_pem, rsa_pem, ec_pem, encrypted_pem,
@@ -81,7 +92,26 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
%%-------------------------------------------------------------------
-init_per_testcase(_TestCase, Config0) ->
+init_per_testcase(TestCase, Config) ->
+ case TestCase of
+ ssh_hostkey_fingerprint_md5_implicit -> init_fingerprint_testcase([md5], Config);
+ ssh_hostkey_fingerprint_md5 -> init_fingerprint_testcase([md5], Config);
+ ssh_hostkey_fingerprint_sha -> init_fingerprint_testcase([sha], Config);
+ ssh_hostkey_fingerprint_sha256 -> init_fingerprint_testcase([sha256], Config);
+ ssh_hostkey_fingerprint_sha384 -> init_fingerprint_testcase([sha384], Config);
+ ssh_hostkey_fingerprint_sha512 -> init_fingerprint_testcase([sha512], Config);
+ ssh_hostkey_fingerprint_list -> init_fingerprint_testcase([sha,md5], Config);
+ _ -> init_common_per_testcase(Config)
+ end.
+
+init_fingerprint_testcase(Algs, Config) ->
+ Hashs = proplists:get_value(hashs, crypto:supports(), []),
+ case Algs -- Hashs of
+ [] -> init_common_per_testcase(Config);
+ UnsupportedAlgs -> {skip,{UnsupportedAlgs,not_supported}}
+ end.
+
+init_common_per_testcase(Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
Dog = ct:timetrap(?TIMEOUT),
[{watchdog, Dog} | Config].
@@ -89,6 +119,7 @@ init_per_testcase(_TestCase, Config0) ->
end_per_testcase(_TestCase, _Config) ->
ok.
+
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -529,6 +560,56 @@ ssh_openssh_public_key_long_header(Config) when is_list(Config) ->
Decoded = public_key:ssh_decode(Encoded, rfc4716_public_key).
%%--------------------------------------------------------------------
+%% Check of different host keys left to later
+ssh_hostkey_fingerprint_md5_implicit(_Config) ->
+ Expected = "4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a",
+ Expected = public_key:ssh_hostkey_fingerprint(ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Check of different host keys left to later
+ssh_hostkey_fingerprint_md5(_Config) ->
+ Expected = "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a",
+ Expected = public_key:ssh_hostkey_fingerprint(md5, ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead. The Expected is generated with:
+%% $ openssh-7.3p1/ssh-keygen -E sha1 -lf <file>
+%% 2048 SHA1:Soammnaqg06jrm2jivMSnzQGlmk [email protected] (RSA)
+ssh_hostkey_fingerprint_sha(_Config) ->
+ Expected = "SHA1:Soammnaqg06jrm2jivMSnzQGlmk",
+ Expected = public_key:ssh_hostkey_fingerprint(sha, ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead.
+ssh_hostkey_fingerprint_sha256(_Config) ->
+ Expected = "SHA256:T7F1BahkJWR7iJO8+rpzWOPbp7LZP4MlNrDExdNYOvY",
+ Expected = public_key:ssh_hostkey_fingerprint(sha256, ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead.
+ssh_hostkey_fingerprint_sha384(_Config) ->
+ Expected = "SHA384:QhkLoGNI4KXdPvC//HxxSCP3uTQVADqxdajbgm+Gkx9zqz8N94HyP1JmH8C4/aEl",
+ Expected = public_key:ssh_hostkey_fingerprint(sha384, ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead.
+ssh_hostkey_fingerprint_sha512(_Config) ->
+ Expected = "SHA512:ezUismvm3ADQQb6Nm0c1DwQ6ydInlJNfsnSQejFkXNmABg1Aenk9oi45CXeBOoTnlfTsGG8nFDm0smP10PBEeA",
+ Expected = public_key:ssh_hostkey_fingerprint(sha512, ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead.
+ssh_hostkey_fingerprint_list(_Config) ->
+ Expected = ["SHA1:Soammnaqg06jrm2jivMSnzQGlmk",
+ "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a"],
+ Expected = public_key:ssh_hostkey_fingerprint([sha,md5], ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
encrypt_decrypt() ->
[{doc, "Test public_key:encrypt_private and public_key:decrypt_public"}].
encrypt_decrypt(Config) when is_list(Config) ->
@@ -746,6 +827,114 @@ pkix_path_validation(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
+%% To generate the PEM file contents:
+%%
+%% openssl req -x509 -nodes -newkey rsa:1024 -keyout /dev/null -subj '/C=SE/CN=example.com/CN=*.foo.example.com/CN=a*b.bar.example.com/O=erlang.org' > public_key_SUITE_data/pkix_verify_hostname_cn.pem
+%%
+%% Note that the same pem-file is used in pkix_verify_hostname_options/1
+%%
+%% Subject: C=SE, CN=example.com, CN=*.foo.example.com, CN=a*b.bar.example.com, O=erlang.org
+%% extensions = no subjAltName
+
+pkix_verify_hostname_cn(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ {ok,Bin} = file:read_file(filename:join(DataDir,"pkix_verify_hostname_cn.pem")),
+ Cert = public_key:pkix_decode_cert(element(2,hd(public_key:pem_decode(Bin))), otp),
+
+ %% Check that 1) only CNs are checked,
+ %% 2) an empty label does not match a wildcard and
+ %% 3) a wildcard does not match more than one label
+ false = public_key:pkix_verify_hostname(Cert, [{dns_id,"erlang.org"},
+ {dns_id,"foo.EXAMPLE.com"},
+ {dns_id,"b.a.foo.EXAMPLE.com"}]),
+
+ %% Check that a hostname is extracted from a https-uri and used for checking:
+ true = public_key:pkix_verify_hostname(Cert, [{uri_id,"HTTPS://EXAMPLE.com"}]),
+
+ %% Check wildcard matching one label:
+ true = public_key:pkix_verify_hostname(Cert, [{dns_id,"a.foo.EXAMPLE.com"}]),
+
+ %% Check wildcard with surrounding chars matches one label:
+ true = public_key:pkix_verify_hostname(Cert, [{dns_id,"accb.bar.EXAMPLE.com"}]),
+
+ %% Check that a wildcard with surrounding chars matches an empty string:
+ true = public_key:pkix_verify_hostname(Cert, [{uri_id,"https://ab.bar.EXAMPLE.com"}]).
+
+%%--------------------------------------------------------------------
+%% To generate the PEM file contents:
+%%
+%% openssl req -x509 -nodes -newkey rsa:1024 -keyout /dev/null -extensions SAN -config public_key_SUITE_data/verify_hostname.conf 2>/dev/null > public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem
+%%
+%% Subject: C=SE, CN=example.com
+%% Subject Alternative Name: DNS:kb.example.org, URI:http://www.example.org, URI:https://wws.example.org
+
+pkix_verify_hostname_subjAltName(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ {ok,Bin} = file:read_file(filename:join(DataDir,"pkix_verify_hostname_subjAltName.pem")),
+ Cert = public_key:pkix_decode_cert(element(2,hd(public_key:pem_decode(Bin))), otp),
+
+ %% Check that neither a uri nor dns hostname matches a CN if subjAltName is present:
+ false = public_key:pkix_verify_hostname(Cert, [{uri_id,"https://example.com"},
+ {dns_id,"example.com"}]),
+
+ %% Check that a uri_id matches a URI subjAltName:
+ true = public_key:pkix_verify_hostname(Cert, [{uri_id,"https://wws.example.org"}]),
+
+ %% Check that a dns_id does not match a URI subjAltName:
+ false = public_key:pkix_verify_hostname(Cert, [{dns_id,"www.example.org"},
+ {dns_id,"wws.example.org"}]),
+
+ %% Check that a dns_id matches a DNS subjAltName:
+ true = public_key:pkix_verify_hostname(Cert, [{dns_id,"kb.example.org"}]).
+
+%%--------------------------------------------------------------------
+%% Uses the pem-file for pkix_verify_hostname_cn
+%% Subject: C=SE, CN=example.com, CN=*.foo.example.com, CN=a*b.bar.example.com, O=erlang.org
+pkix_verify_hostname_options(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ {ok,Bin} = file:read_file(filename:join(DataDir,"pkix_verify_hostname_cn.pem")),
+ Cert = public_key:pkix_decode_cert(element(2,hd(public_key:pem_decode(Bin))), otp),
+
+ %% Check that the fail_callback is called and is presented the correct certificate:
+ true = public_key:pkix_verify_hostname(Cert, [{dns_id,"erlang.org"}],
+ [{fail_callback,
+ fun(#'OTPCertificate'{}=C) when C==Cert ->
+ true; % To test the return value matters
+ (#'OTPCertificate'{}=C) ->
+ ct:log("~p:~p: Wrong cert:~n~p~nExpect~n~p",
+ [?MODULE, ?LINE, C, Cert]),
+ ct:fail("Wrong cert, see log");
+ (C) ->
+ ct:log("~p:~p: Bad cert: ~p",[?MODULE,?LINE,C]),
+ ct:fail("Bad cert, see log")
+ end}]),
+
+ %% Check the callback for user-provided match functions:
+ true = public_key:pkix_verify_hostname(Cert, [{dns_id,"very.wrong.domain"}],
+ [{match_fun,
+ fun("very.wrong.domain", {cn,"example.com"}) ->
+ true;
+ (_, _) ->
+ false
+ end}]),
+ false = public_key:pkix_verify_hostname(Cert, [{dns_id,"not.example.com"}],
+ [{match_fun, fun(_, _) -> default end}]),
+ true = public_key:pkix_verify_hostname(Cert, [{dns_id,"example.com"}],
+ [{match_fun, fun(_, _) -> default end}]),
+
+ %% Check the callback for user-provided fqdn extraction:
+ true = public_key:pkix_verify_hostname(Cert, [{uri_id,"some://very.wrong.domain"}],
+ [{fqdn_fun,
+ fun({uri_id, "some://very.wrong.domain"}) ->
+ "example.com";
+ (_) ->
+ ""
+ end}]),
+ true = public_key:pkix_verify_hostname(Cert, [{uri_id,"https://example.com"}],
+ [{fqdn_fun, fun(_) -> default end}]),
+ false = public_key:pkix_verify_hostname(Cert, [{uri_id,"some://very.wrong.domain"}]).
+
+%%--------------------------------------------------------------------
pkix_iso_rsa_oid() ->
[{doc, "Test workaround for supporting certs that use ISO oids"
" 1.3.14.3.2.29 instead of PKIX/PKCS oid"}].
@@ -929,3 +1118,13 @@ incorrect_countryname_pkix_cert() ->
incorrect_emailaddress_pkix_cert() ->
<<48,130,3,74,48,130,2,50,2,9,0,133,49,203,25,198,156,252,230,48,13,6,9,42,134, 72,134,247,13,1,1,5,5,0,48,103,49,11,48,9,6,3,85,4,6,19,2,65,85,49,19,48,17, 6,3,85,4,8,12,10,83,111,109,101,45,83,116,97,116,101,49,33,48,31,6,3,85,4,10, 12,24,73,110,116,101,114,110,101,116,32,87,105,100,103,105,116,115,32,80,116, 121,32,76,116,100,49,32,48,30,6,9,42,134,72,134,247,13,1,9,1,12,17,105,110, 118,97,108,105,100,64,101,109,97,105,108,46,99,111,109,48,30,23,13,49,51,49, 49,48,55,50,48,53,54,49,56,90,23,13,49,52,49,49,48,55,50,48,53,54,49,56,90, 48,103,49,11,48,9,6,3,85,4,6,19,2,65,85,49,19,48,17,6,3,85,4,8,12,10,83,111, 109,101,45,83,116,97,116,101,49,33,48,31,6,3,85,4,10,12,24,73,110,116,101, 114,110,101,116,32,87,105,100,103,105,116,115,32,80,116,121,32,76,116,100,49, 32,48,30,6,9,42,134,72,134,247,13,1,9,1,12,17,105,110,118,97,108,105,100,64, 101,109,97,105,108,46,99,111,109,48,130,1,34,48,13,6,9,42,134,72,134,247,13, 1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,190,243,49,213,219,60,232,105, 1,127,126,9,130,15,60,190,78,100,148,235,246,223,21,91,238,200,251,84,55,212, 78,32,120,61,85,172,0,144,248,5,165,29,143,79,64,178,51,153,203,76,115,238, 192,49,173,37,121,203,89,62,157,13,181,166,30,112,154,40,202,140,104,211,157, 73,244,9,78,236,70,153,195,158,233,141,42,238,2,143,160,225,249,27,30,140, 151,176,43,211,87,114,164,108,69,47,39,195,123,185,179,219,28,218,122,53,83, 77,48,81,184,14,91,243,12,62,146,86,210,248,228,171,146,225,87,51,146,155, 116,112,238,212,36,111,58,41,67,27,6,61,61,3,84,150,126,214,121,57,38,12,87, 121,67,244,37,45,145,234,131,115,134,58,194,5,36,166,52,59,229,32,47,152,80, 237,190,58,182,248,98,7,165,198,211,5,31,231,152,116,31,108,71,218,64,188, 178,143,27,167,79,15,112,196,103,116,212,65,197,94,37,4,132,103,91,217,73, 223,207,185,7,153,221,240,232,31,44,102,108,82,83,56,242,210,214,74,71,246, 177,217,148,227,220,230,4,176,226,74,194,37,2,3,1,0,1,48,13,6,9,42,134,72, 134,247,13,1,1,5,5,0,3,130,1,1,0,89,247,141,154,173,123,123,203,143,85,28,79, 73,37,164,6,17,89,171,224,149,22,134,17,198,146,158,192,241,41,253,58,230, 133,71,189,43,66,123,88,15,242,119,227,249,99,137,61,200,54,161,0,177,167, 169,114,80,148,90,22,97,78,162,181,75,93,209,116,245,46,81,232,64,157,93,136, 52,57,229,113,197,218,113,93,42,161,213,104,205,137,30,144,183,58,10,98,47, 227,177,96,40,233,98,150,209,217,68,22,221,133,27,161,152,237,46,36,179,59, 172,97,134,194,205,101,137,71,192,57,153,20,114,27,173,233,166,45,56,0,61, 205,45,202,139,7,132,103,248,193,157,184,123,43,62,172,236,110,49,62,209,78, 249,83,219,133,1,213,143,73,174,16,113,143,189,41,84,60,128,222,30,177,104, 134,220,52,239,171,76,59,176,36,113,176,214,118,16,44,235,21,167,199,216,200, 76,219,142,248,13,70,145,205,216,230,226,148,97,223,216,179,68,209,222,63, 140,137,24,164,192,149,194,79,119,247,75,159,49,116,70,241,70,116,11,40,119, 176,157,36,160,102,140,255,34,248,25,231,136,59>>.
+
+
+
+ssh_hostkey(rsa) ->
+ [{PKdecoded,_}] =
+ public_key:ssh_decode(
+ <<"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDYXcYmsyJBstl4EfFYzfQJmSiUE162zvSGSoMYybShYOI6rnnyvvihfw8Aml+2gZ716F2tqG48FQ/yPZEGWNPMrCejPpJctaPWhpNdNMJ8KFXSEgr5bY2mEpa19DHmuDeXKzeJJ+X7s3fVdYc4FMk5731KIW6Huf019ZnTxbx0VKG6b1KAJBg3vpNsDxEMwQ4LFMB0JHVklOTzbxmpaeULuIxvl65A+eGeFVeo2Q+YI9UnwY1vSgmc9Azwy8Ie9Z0HpQBN5I7Uc5xnknT8V6xDhgNfXEfzsgsRdDfZLECt1WO/1gP9wkosvAGZWt5oG8pbNQWiQdFq536ck8WQD9WD [email protected]">>,
+ public_key),
+ PKdecoded.
+
diff --git a/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_cn.pem b/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_cn.pem
new file mode 100644
index 0000000000..9f7b428f9a
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_cn.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICsjCCAhugAwIBAgIJAMCGx1ezaJFRMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNV
+BAYTAlNFMRQwEgYDVQQDDAtleGFtcGxlLmNvbTEaMBgGA1UEAwwRKi5mb28uZXhh
+bXBsZS5jb20xHDAaBgNVBAMME2EqYi5iYXIuZXhhbXBsZS5jb20xEzARBgNVBAoM
+CmVybGFuZy5vcmcwHhcNMTYxMjIwMTUwNDUyWhcNMTcwMTE5MTUwNDUyWjByMQsw
+CQYDVQQGEwJTRTEUMBIGA1UEAwwLZXhhbXBsZS5jb20xGjAYBgNVBAMMESouZm9v
+LmV4YW1wbGUuY29tMRwwGgYDVQQDDBNhKmIuYmFyLmV4YW1wbGUuY29tMRMwEQYD
+VQQKDAplcmxhbmcub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVGJgZ
+defGucvMXf0RrEm6Hb18IfVUo9IV6swSP/kwAu/608ZIZdzlfp2pxC0e72a4E3WN
+4vrGxAr2wMMQOiyoy4qlAeLX27THJ6Q4Vl82fc6QuOJbScKIydSZ4KoB+luGlBu5
+b6xYh2pBbneKFpsecmK5rsWtTactjD4n1tKjUwIDAQABo1AwTjAdBgNVHQ4EFgQU
+OCtzidUeaDva7qp12T0CQrgfLW4wHwYDVR0jBBgwFoAUOCtzidUeaDva7qp12T0C
+QrgfLW4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQCAz+ComCMo9Qbu
+PHxG7pv3mQvoxrMFva/Asg4o9mW2mDyrk0DwI4zU8vMHbSRKSBYGm4TATXsQkDQT
+gJw/bxhISnhZZtPC7Yup8kJCkJ6S6EDLYrlzgsRqfeU6jWim3nbfaLyMi9dHFDMk
+HULnyNNW3qxTEKi8Wo2sCMej4l7KFg==
+-----END CERTIFICATE-----
diff --git a/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem b/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem
new file mode 100644
index 0000000000..83e1ad37b3
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICEjCCAXugAwIBAgIJANwliLph5EiAMA0GCSqGSIb3DQEBCwUAMCMxCzAJBgNV
+BAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNvbTAeFw0xNjEyMjAxNTEyMjRaFw0x
+NzAxMTkxNTEyMjRaMCMxCzAJBgNVBAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNv
+bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAydstIN157w8QxkVaOl3wm81j
+fgZ8gqO3BXkECPF6bw5ewLlmePL6Qs4RypsaRe7cKJ9rHFlwhpdcYkxWSWEt2N7Z
+Ry3N4SjuU04ohWbYgy3ijTt7bJg7jOV1Dh56BnI4hwhQj0oNFizNZOeRRfEzdMnS
++uk03t/Qre2NS7KbwnUCAwEAAaNOMEwwSgYDVR0RBEMwQYIOa2IuZXhhbXBsZS5v
+cmeGFmh0dHA6Ly93d3cuZXhhbXBsZS5vcmeGF2h0dHBzOi8vd3dzLmV4YW1wbGUu
+b3JnMA0GCSqGSIb3DQEBCwUAA4GBAKqFqW5gCso422bXriCBJoygokOTTOw1Rzpq
+K8Mm0B8W9rrW9OTkoLEcjekllZcUCZFin2HovHC5HlHZz+mQvBI1M6sN2HVQbSzS
+EgL66U9gwJVnn9/U1hXhJ0LO28aGbyE29DxnewNR741dWN3oFxCdlNaO6eMWaEsO
+gduJ5sDl
+-----END CERTIFICATE-----
diff --git a/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf b/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf
new file mode 100644
index 0000000000..a28864dc78
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf
@@ -0,0 +1,16 @@
+[req]
+prompt = no
+distinguished_name = DN
+
+[DN]
+C=SE
+CN=example.com
+
+[SAN]
+subjectAltName = @alt_names
+
+[alt_names]
+DNS = kb.example.org
+URI.1 = http://www.example.org
+URI.2 = https://wws.example.org
+
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index 84f6a659b5..b94768ae77 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 1.2
+PUBLIC_KEY_VSN = 1.4
diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml
index 6df4924d0a..b47d451055 100644
--- a/lib/reltool/doc/src/notes.xml
+++ b/lib/reltool/doc/src/notes.xml
@@ -38,7 +38,53 @@
thus constitutes one section in this document. The title of each
section is the version number of Reltool.</p>
- <section><title>Reltool 0.7.1</title>
+ <section><title>Reltool 0.7.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed xml issues in old release notes</p>
+ <p>
+ Own Id: OTP-14269</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Reltool 0.7.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Dependencies specified in .app files would earlier only
+ be followed for applications that are included in a 'rel'
+ spec in the reltool config. For other applications, only
+ xref would decide the dependencies.</p>
+ <p>
+ Some dependency chains would even be missed for
+ applications that are included in a 'rel' spec in the
+ reltool config. E.g.</p>
+
+ <list> <item>Application x has y as included application,
+ and y in turn has z as included application. Then z is
+ not included. </item> <item>Application x has y in its
+ 'applications' tag in the .app file, and y in turn has z
+ as included application. Then z is not included.</item>
+ </list>
+ <p>
+ These bugs are now corrected.</p>
+ <p>
+ Own Id: OTP-11993</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Reltool 0.7.1</title>
<section><title>Improvements and New Features</title>
<list>
@@ -546,8 +592,8 @@
<p>The handling of applications included in releases has
been improved. Applications that are required to be
started before other applications in a release are now
- automatically included in the release. The <c>kernel</c>
- and <c>stdlib</c> applications are always included as
+ automatically included in the release. The Kernel
+ and STDLIB applications are always included as
they are mandatory.</p>
<p>Applications that are (explicitly or implicitly)
included in a release are now automatically included as
diff --git a/lib/reltool/doc/src/part.xml b/lib/reltool/doc/src/part.xml
index 5904084879..e608d548ea 100644
--- a/lib/reltool/doc/src/part.xml
+++ b/lib/reltool/doc/src/part.xml
@@ -31,7 +31,7 @@
<rev>%VSN%</rev>
</header>
<description>
- <p><c>Reltool</c> is a release management tool. It analyses a given
+ <p>Reltool is a release management tool. It analyses a given
Erlang/OTP installation and determines various dependencies
between applications. The <c>graphical</c> frontend depicts the
dependencies and enables interactive customization of a
diff --git a/lib/reltool/doc/src/ref_man.xml b/lib/reltool/doc/src/ref_man.xml
index 38f270b79a..1684f075ff 100644
--- a/lib/reltool/doc/src/ref_man.xml
+++ b/lib/reltool/doc/src/ref_man.xml
@@ -31,7 +31,7 @@
<rev>%VSN%</rev>
</header>
<description>
- <p><c>Reltool</c> is a release management tool. It analyses a given
+ <p>Reltool is a release management tool. It analyses a given
Erlang/OTP installation and determines various dependencies
between applications. The <c>graphical</c> frontend depicts the
dependencies and enables interactive customization of a
diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml
index 38448e7961..5bfbee966b 100644
--- a/lib/reltool/doc/src/reltool.xml
+++ b/lib/reltool/doc/src/reltool.xml
@@ -47,7 +47,7 @@
<p>The tool uses an installed Erlang/OTP system as input.
<c>root_dir</c> is the root directory of the analysed system and
- it defaults to the system executing <c>reltool</c>. Applications
+ it defaults to the system executing Reltool. Applications
may also be located outside <c>root_dir</c>. <c>lib_dirs</c>
defines library directories where additional applications
may reside and it defaults to the directories
@@ -56,7 +56,7 @@
<p>An application directory <c>AppDir</c> under a library
directory is recognized by the existence of an <c>AppDir/ebin</c>
- directory. If this does not exist, <c>reltool</c> will not
+ directory. If this does not exist, Reltool will not
consider <c>AppDir</c> at all when looking for applications.</p>
<p>It is recommended that application directories are named as the
@@ -81,14 +81,14 @@
<tag><c>config</c></tag>
<item>
<p>This is the main option and it controls the configuration
- of <c>reltool</c>. It can either be a <c>sys</c> tuple or
+ of Reltool. It can either be a <c>sys</c> tuple or
a name of a <c>file</c> containing a sys tuple.</p>
</item>
<tag><c>trap_exit</c></tag>
<item>
<p>This option controls the error handling behavior of
- <c>reltool</c>. By default the window processes traps
+ Reltool. By default the window processes traps
exit, but this behavior can altered by setting
<c>trap_exit</c> to <c>false</c>.</p>
</item>
diff --git a/lib/reltool/doc/src/reltool_intro.xml b/lib/reltool/doc/src/reltool_intro.xml
index 8e232b8838..2980ad7977 100644
--- a/lib/reltool/doc/src/reltool_intro.xml
+++ b/lib/reltool/doc/src/reltool_intro.xml
@@ -34,7 +34,7 @@
<rev>%VSN%</rev>
<file>reltool_intro.xml</file>
</header>
- <p><c>Reltool</c> is a release management tool. It analyses a given
+ <p>Reltool is a release management tool. It analyses a given
Erlang/OTP installation and determines various dependencies between
applications. The <c>graphical</c> frontend depicts the dependencies and
enables interactive customization of a target system. The backend provides a
@@ -82,7 +82,7 @@
and about the Erlang/OTP development system:</p>
<list type="bulleted">
<item>
- <p>the Reference Manual of <c>Reltool</c></p>
+ <p>the Reference Manual of Reltool</p>
</item>
<item>
<p>the Erlang/OTP <c>System Principles</c></p>
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index 9ac22b9450..3b1e868757 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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.
@@ -154,19 +154,19 @@
-record(app_info,
{
- description = "" :: string(),
- id = "" :: string(),
- vsn = "" :: app_vsn(),
- modules = [] :: [mod_name()],
- maxP = infinity :: integer() | infinity,
- maxT = infinity :: integer() | infinity,
- registered = [] :: [atom()],
- incl_apps = [] :: [app_name()],
- applications = [] :: [app_name()],
- env = [] :: [{atom(), term()}],
- mod = undefined :: {mod_name(), [term()]} | undefined,
- start_phases = undefined :: [{atom(), term()}] | undefined,
- runtime_dependencies = [] :: [string()]
+ description = "" :: '_' | string(),
+ id = "" :: '_' | string(),
+ vsn = "" :: '_' | app_vsn(),
+ modules = [] :: '_' | [mod_name()],
+ maxP = infinity :: '_' | integer() | infinity,
+ maxT = infinity :: '_' | integer() | infinity,
+ registered = [] :: '_' | [atom()],
+ incl_apps = [] :: '_' | '$3' | [app_name()],
+ applications = [] :: '_' | '$2' | [app_name()],
+ env = [] :: '_' | [{atom(), term()}],
+ mod = undefined :: '_' | {mod_name(), [term()]} | undefined,
+ start_phases = undefined :: '_' | [{atom(), term()}] | undefined,
+ runtime_dependencies = [] :: '_' | [string()]
}).
-record(regexp, {source, compiled}).
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 21a4485f94..89e90670cf 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -530,9 +530,14 @@ analyse(#state{sys=Sys} = S, Apps, Status) ->
%% Write all #app to app_tab and all #mod to mod_tab.
Status2 = apps_init_is_included(S, Apps, RelApps, Status),
+ %% For each application that is not (directly or indirectly) part
+ %% of a release, but still has #app.is_included==true, propagate
+ %% is_included to the dependencies specified in the .app files.
+ app_propagate_is_included(S),
+
%% For each module that has #mod.is_included==true, propagate
%% is_included to the modules it uses.
- propagate_is_included(S),
+ mod_propagate_is_included(S),
%% Insert reverse dependencies - i.e. for each
%% #mod{name=Mod, uses_mods=[UsedMod]},
@@ -565,31 +570,34 @@ apps_in_rels(Rels, Apps) ->
apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps) ->
Mandatory = [{RelName, kernel}, {RelName, stdlib}],
- Other =
+ Explicit0 = [{RelName, AppName} || #rel_app{name=AppName} <- RelApps],
+ Explicit = Mandatory ++ Explicit0,
+ Deps =
[{RelName, AppName} ||
RA <- RelApps,
- AppName <- [RA#rel_app.name |
+ AppName <-
+ case lists:keyfind(RA#rel_app.name,
+ #app.name,
+ Apps) of
+ App=#app{info = #app_info{applications = AA}} ->
%% Included applications in rel shall overwrite included
%% applications in .app. I.e. included applications in
%% .app shall only be used if it is not defined in rel.
- case RA#rel_app.incl_apps of
- undefined ->
- case lists:keyfind(RA#rel_app.name,
- #app.name,
- Apps) of
- #app{info = #app_info{incl_apps = IA}} ->
- IA;
- false ->
- reltool_utils:throw_error(
- "Release ~tp uses non existing "
- "application ~w",
- [RelName,RA#rel_app.name])
- end;
- IA ->
- IA
- end],
- not lists:keymember(AppName, 2, Mandatory)],
- more_apps_in_rels(Mandatory ++ Other, Apps, []).
+ IA = case RA#rel_app.incl_apps of
+ undefined ->
+ (App#app.info)#app_info.incl_apps;
+ RelIA ->
+ RelIA
+ end,
+ AA ++ IA;
+ false ->
+ reltool_utils:throw_error(
+ "Release ~tp uses non existing "
+ "application ~w",
+ [RelName,RA#rel_app.name])
+ end,
+ not lists:keymember(AppName, 2, Explicit)],
+ more_apps_in_rels(Deps, Apps, Explicit).
more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) ->
case lists:member(RA, Acc) of
@@ -597,8 +605,8 @@ more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) ->
more_apps_in_rels(RelApps, Apps, Acc);
false ->
case lists:keyfind(AppName, #app.name, Apps) of
- #app{info = #app_info{applications = InfoApps}} ->
- Extra = [{RelName, N} || N <- InfoApps],
+ #app{info = #app_info{applications = AA, incl_apps=IA}} ->
+ Extra = [{RelName, N} || N <- AA++IA],
Acc2 = more_apps_in_rels(Extra, Apps, [RA | Acc]),
more_apps_in_rels(RelApps, Apps, Acc2);
false ->
@@ -610,7 +618,6 @@ more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) ->
more_apps_in_rels([], _Apps, Acc) ->
Acc.
-
apps_init_is_included(S, Apps, RelApps, Status) ->
lists:foldl(fun(App, AccStatus) ->
app_init_is_included(S, App, RelApps, AccStatus)
@@ -745,6 +752,100 @@ false_to_undefined(Bool) ->
_ -> Bool
end.
+get_no_rel_apps_and_dependencies(S) ->
+ ets:select(S#state.app_tab, [{#app{name='$1',
+ is_included=true,
+ info=#app_info{applications='$2',
+ incl_apps='$3',
+ _='_'},
+ rels=[],
+ _='_'},
+ [],
+ [{{'$1','$2','$3'}}]}]).
+
+app_propagate_is_included(S) ->
+ lists:foreach(
+ fun({AppName,DepNames1,DepNames2}) ->
+ app_mark_is_included(S,AppName,DepNames1++DepNames2)
+ end,
+ get_no_rel_apps_and_dependencies(S)).
+
+app_mark_is_included(#state{app_tab=AppTab, mod_tab=ModTab, sys=Sys}=S,UsedByName,[AppName|AppNames]) ->
+ case ets:lookup(AppTab, AppName) of
+ [A] ->
+ case A#app.is_included of
+ undefined ->
+ %% Not yet marked => mark and propagate
+ A2 =
+ case A#app.incl_cond of
+ include ->
+ A#app{is_pre_included = true,
+ is_included = true};
+ exclude ->
+ A#app{is_pre_included = false,
+ is_included = false};
+ AppInclCond when AppInclCond==undefined;
+ AppInclCond==derived ->
+ A#app{is_included = true}
+ end,
+ ets:insert(AppTab, A2),
+
+ ModCond =
+ case A#app.mod_cond of
+ undefined -> Sys#sys.mod_cond;
+ _ -> A#app.mod_cond
+ end,
+ Filter =
+ fun(M) ->
+ case ModCond of
+ all -> true;
+ app -> M#mod.is_app_mod;
+ ebin -> M#mod.is_ebin_mod;
+ derived -> false;
+ none -> false
+ end
+ end,
+ Mods = lists:filter(Filter, A#app.mods),
+ %% Mark the modules of this app, but no need to go
+ %% recursive on modules since this is done in
+ %% mod_mark_is_included.
+ [case M#mod.is_included of
+ undefined ->
+ M2 =
+ case M#mod.incl_cond of
+ include ->
+ M#mod{is_pre_included = true,
+ is_included = true};
+ exclude ->
+ M#mod{is_pre_included = false,
+ is_included = false};
+ ModInclCond when ModInclCond==undefined;
+ ModInclCond==derived ->
+ M#mod{is_included = true}
+ end,
+ ets:insert(ModTab, M2);
+ _ ->
+ ok
+ end || M <- Mods],
+
+ %% Go recursive on dependencies
+ #app{info=#app_info{applications=DepNames1,
+ incl_apps=DepNames2}} = A,
+ app_mark_is_included(S,AppName,DepNames1++DepNames2);
+ _ ->
+ %% Already marked
+ ok
+ end;
+ [] ->
+ %% Missing app
+ reltool_utils:throw_error(
+ "Application ~tp uses non existing application ~w",
+ [UsedByName,AppName])
+ end,
+ app_mark_is_included(S, UsedByName, AppNames);
+app_mark_is_included(_S, _UsedByName, []) ->
+ ok.
+
%% Return the list for {ModName, UsesModNames} for all modules where
%% #mod.is_included==true.
get_all_mods_and_dependencies(S) ->
@@ -755,7 +856,7 @@ get_all_mods_and_dependencies(S) ->
[],
[{{'$1','$2'}}]}]).
-propagate_is_included(S) ->
+mod_propagate_is_included(S) ->
case lists:flatmap(
fun({ModName,UsesModNames}) ->
mod_mark_is_included(S,ModName,UsesModNames,[])
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index e9bd46fb27..e8dfea94da 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -142,6 +142,7 @@ all() ->
save_config,
dependencies,
mod_incl_cond_derived,
+ dep_in_app_not_xref,
use_selected_vsn,
use_selected_vsn_relative_path,
non_standard_vsn_id,
@@ -408,7 +409,6 @@ create_release_sort(Config) ->
{app,tools,[{mod_cond,app},{incl_cond,include}]}
]},
%% Generate release
-
?msym({ok, {release, {RelName1, RelVsn},
{erts, _},
[{kernel, _},
@@ -2304,6 +2304,7 @@ dependencies(Config) ->
ok.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test that incl_cond on mod level overwrites mod_cond on app level
%% Uses same test applications as dependencies/1 above
mod_incl_cond_derived(Config) ->
@@ -2346,6 +2347,40 @@ mod_incl_cond_derived(Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% ERL-167, OTP-11993: For applications that are not included in a
+%% release spec ('rel'), dependencies in the .app files are not
+%% considered - only those found with xref.
+dep_in_app_not_xref(Config) ->
+ RelName = "Just testing...",
+ RelVsn = "1.0",
+ Sys =
+ {sys,
+ [
+ {lib_dirs,[filename:join(datadir(Config),"dep_in_app_not_xref")]},
+ {incl_cond,exclude},
+ {incl_archive_filters,[]},
+ {erts,[{incl_cond,exclude}]},
+ {boot_rel, RelName},
+ {rel, RelName, RelVsn, [kernel, stdlib]},
+ {app,kernel,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,x,[{incl_cond,include}]},
+ {app,y,[{incl_cond,derived}]},
+ {app,z,[{incl_cond,derived}]}
+ ]},
+
+ TargetDir = filename:join([?WORK_DIR, "target_dep_in_app_not_xref"]),
+ ?m(ok, reltool_utils:recursive_delete(TargetDir)),
+ ?m(ok, file:make_dir(TargetDir)),
+ ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]),
+ ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)),
+ ?log("~p~n",[file:list_dir(filename:join([TargetDir,"lib"]))]),
+
+ ?m(true, filelib:is_dir(filename:join([TargetDir,"lib","y-1.0"]))),
+ ?m(true, filelib:is_dir(filename:join([TargetDir,"lib","z-1.0"]))),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
use_selected_vsn(Config) ->
LibDir1 = filename:join(datadir(Config),"use_selected_vsn"),
B1Dir = filename:join(LibDir1,"b-1.0"),
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app
new file mode 100644
index 0000000000..fe922e3a41
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app
@@ -0,0 +1,7 @@
+% -*-erlang-*-
+{application, x,
+ [{description, "Main application in reltool dependency test"},
+ {vsn, "1.0"},
+ {modules, [x1]},
+ {registered, []},
+ {applications, [kernel, stdlib, y]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl
new file mode 100644
index 0000000000..bf1e7f9279
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl
@@ -0,0 +1,5 @@
+-module(x1).
+-compile(export_all).
+
+f() ->
+ ok.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app
new file mode 100644
index 0000000000..a21cfe6c21
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app
@@ -0,0 +1,8 @@
+% -*-erlang-*-
+{application, y,
+ [{description, "Library application in reltool dependency test"},
+ {vsn, "1.0"},
+ {modules, [y1]},
+ {registered, []},
+ {applications, [kernel, stdlib]},
+ {included_applications, [z]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl
new file mode 100644
index 0000000000..902a7e21f3
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl
@@ -0,0 +1,5 @@
+-module(y1).
+-compile(export_all).
+
+f() ->
+ ok.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app
new file mode 100644
index 0000000000..437a0968e9
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app
@@ -0,0 +1,7 @@
+% -*-erlang-*-
+{application, z,
+ [{description, "Library application in reltool dependency test"},
+ {vsn, "1.0"},
+ {modules, [z1]},
+ {registered, []},
+ {applications, [kernel, stdlib]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl
new file mode 100644
index 0000000000..97ef90b87f
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl
@@ -0,0 +1,5 @@
+-module(z1).
+-compile(export_all).
+
+f() ->
+ ok.
diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk
index 76f69fd294..2d07eeb8f0 100644
--- a/lib/reltool/vsn.mk
+++ b/lib/reltool/vsn.mk
@@ -1 +1 @@
-RELTOOL_VSN = 0.7.1
+RELTOOL_VSN = 0.7.3
diff --git a/lib/runtime_tools/doc/src/LTTng.xml b/lib/runtime_tools/doc/src/LTTng.xml
index 82a4c79379..392d54857c 100644
--- a/lib/runtime_tools/doc/src/LTTng.xml
+++ b/lib/runtime_tools/doc/src/LTTng.xml
@@ -1,9 +1,9 @@
-<?xml version="1.0" encoding="utf8" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2016</year><year>2016</year>
+ <year>2016</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml
index 14a81b2293..95f74d4607 100644
--- a/lib/runtime_tools/doc/src/dbg.xml
+++ b/lib/runtime_tools/doc/src/dbg.xml
@@ -78,7 +78,7 @@
<p>Pseudo function that by means of a <c>parse_transform</c>
translates the <em>literal</em><c>fun()</c> typed as parameter in
the function call to a match specification as described in
- the <c>match_spec</c> manual of <c>ERTS</c> users guide.
+ the <c>match_spec</c> manual of ERTS users guide.
(with literal I mean that the <c>fun()</c> needs to
textually be written as the parameter of the function, it
cannot be held in a variable which in turn is passed to the
@@ -954,7 +954,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
as the tuple <c>{drop, N}</c> where <c>N</c> is the number of consecutive messages
dropped. In case of heavy tracing, drop's are likely to occur,
and they surely occur if no client is reading the trace
- messages.</p>
+ messages. The default value of <c>QueSize</c> is 200.</p>
</desc>
</func>
<func>
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 6bec7cb9ca..0eafc437cc 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -32,6 +32,65 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.11.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ etop erroneously reported the average scheduler
+ utilization since the tool was first started instead of
+ the scheduler utilization since last update. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-14090 Aux Id: seq13232 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Runtime_Tools 1.11</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add option <c>queue_size</c> to ttb:tracer/2. This sets
+ the maximum queue size for the IP trace driver which is
+ used when tracing to shell and/or <c>{local,File}</c>.</p>
+ <p>
+ The default value for <c>queue_size</c> is specified by
+ <c>dbg</c>, and it is now changed from 50 to 200.</p>
+ <p>
+ Own Id: OTP-13829 Aux Id: seq13171 </p>
+ </item>
+ <item>
+ <p>
+ The port information page is updated to show more
+ information per port.</p>
+ <p>
+ Own Id: OTP-13948 Aux Id: ERL-272 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Runtime_Tools 1.10.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Correct some minor documentation issues. </p>
+ <p>
+ Own Id: OTP-13891</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.10</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -668,7 +727,7 @@
also been extended. </item><item> The <c>configure</c>
scripts of <c>erl_interface</c> and <c>odbc</c> now
search for thread libraries and thread library quirks the
- same way as <c>erts</c> do. </item><item> The
+ same way as ERTS do. </item><item> The
<c>configure</c> script of the <c>odbc</c> application
now also looks for odbc libraries in <c>lib64</c> and
<c>lib/64</c> directories when building on a 64-bit
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index c0d4665bda..f17aa528ed 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -427,7 +427,7 @@ trace_port(file, Filename) ->
trace_port1(file, Filename, nowrap);
trace_port(ip, Portno) when is_integer(Portno) ->
- trace_port(ip,{Portno,50});
+ trace_port(ip,{Portno,200});
trace_port(ip, {Portno, Qsiz}) when is_integer(Portno), is_integer(Qsiz) ->
fun() ->
diff --git a/lib/runtime_tools/src/msacc.erl b/lib/runtime_tools/src/msacc.erl
index 4db5dbec91..0d9b2690e5 100644
--- a/lib/runtime_tools/src/msacc.erl
+++ b/lib/runtime_tools/src/msacc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2014-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.
diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index cedb677178..b27bc63d15 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -141,14 +141,59 @@ get_mnesia_loop(Parent, {Match, Cont}) ->
get_mnesia_loop(Parent, mnesia:select(Cont)).
get_port_list() ->
+ ExtraItems = [monitors,monitored_by,parallelism,locking,queue_size,memory],
[begin
[{port_id,P}|erlang:port_info(P)] ++
- case erlang:port_info(P,monitors) of
- undefined -> [];
- Monitors -> [Monitors]
- end
+ port_info(P,ExtraItems) ++
+ inet_port_extra(erlang:port_info(P, name), P)
end || P <- erlang:ports()].
+port_info(P,[Item|Items]) ->
+ case erlang:port_info(P,Item) of
+ undefined -> port_info(P,Items);
+ Value -> [Value|port_info(P,Items)]
+ end;
+port_info(_,[]) ->
+ [].
+
+inet_port_extra({_,Type},Port) when Type =:= "udp_inet";
+ Type =:= "tcp_inet";
+ Type =:= "sctp_inet" ->
+ Data =
+ case inet:getstat(Port) of
+ {ok, Stats} -> [{statistics, Stats}];
+ _ -> []
+ end ++
+ case inet:peername(Port) of
+ {ok, {RAddr,RPort}} when is_tuple(RAddr), is_integer(RPort) ->
+ [{remote_address,RAddr},{remote_port,RPort}];
+ {ok, RAddr} ->
+ [{remote_address,RAddr}];
+ {error, _} -> []
+ end ++
+ case inet:sockname(Port) of
+ {ok, {LAddr,LPort}} when is_tuple(LAddr), is_integer(LPort) ->
+ [{local_address,LAddr},{local_port,LPort}];
+ {ok, LAddr} ->
+ [{local_address,LAddr}];
+ {error, _} -> []
+ end ++
+ case inet:getopts(Port,
+ [active, broadcast, buffer, delay_send,
+ deliver, dontroute, exit_on_close,
+ header, high_msgq_watermark, high_watermark,
+ ipv6_v6only, keepalive, linger, low_msgq_watermark,
+ low_watermark, mode, netns, nodelay, packet,
+ packet_size, priority, read_packets, recbuf,
+ reuseaddr, send_timeout, send_timeout_close,
+ show_econnreset, sndbuf, tos, tclass]) of
+ {ok, Opts} -> [{options, Opts}];
+ {error, _} -> []
+ end,
+ [{inet,Data}];
+inet_port_extra(_,_) ->
+ [].
+
get_table_list(ets, Opts) ->
HideUnread = proplists:get_value(unread_hidden, Opts, true),
HideSys = proplists:get_value(sys_hidden, Opts, true),
@@ -269,13 +314,12 @@ etop_collect(Collector) ->
case SchedulerWallTime of
undefined ->
- spawn(fun() -> flag_holder_proc(Collector) end),
+ erlang:system_flag(scheduler_wall_time,true),
+ spawn(fun() -> flag_holder_proc(Collector) end),
ok;
_ ->
ok
- end,
-
- erlang:system_flag(scheduler_wall_time,true).
+ end.
flag_holder_proc(Collector) ->
Ref = erlang:monitor(process,Collector),
diff --git a/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c b/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c
index ecdee7e3a2..ec00c68049 100644
--- a/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c
+++ b/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2014. All Rights Reserved.
+ * Copyright Ericsson AB 2009-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.
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index b33f6f4721..8ec532de76 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.10
+RUNTIME_TOOLS_VSN = 1.11.1
diff --git a/lib/sasl/doc/src/alarm_handler.xml b/lib/sasl/doc/src/alarm_handler.xml
index 8550a88b28..4160757164 100644
--- a/lib/sasl/doc/src/alarm_handler.xml
+++ b/lib/sasl/doc/src/alarm_handler.xml
@@ -54,7 +54,7 @@
format is defined by the user. For example, an event handler
for SNMP can be defined, together with an alarm Management
Information Base (MIB).</p>
- <p>The alarm handler is part of the <c>SASL</c> application.</p>
+ <p>The alarm handler is part of the SASL application.</p>
<p>When writing new event handlers for the alarm handler, the
following events must be handled:</p>
<taglist>
@@ -76,7 +76,7 @@
{NewHandler, Args})</c>. <c>NewHandler:init({Args, {alarm_handler,
Alarms}})</c> is called. For more details, see
<seealso marker="stdlib:gen_event"><c>gen_event(3)</c></seealso>
- in <c>STDLIB</c>.</p>
+ in STDLIB.</p>
</description>
<funcs>
diff --git a/lib/sasl/doc/src/appup.xml b/lib/sasl/doc/src/appup.xml
index 6fbdcb9f5b..a43a966dcb 100644
--- a/lib/sasl/doc/src/appup.xml
+++ b/lib/sasl/doc/src/appup.xml
@@ -362,12 +362,12 @@ point_of_no_return</pre>
system is restarted.</p>
<pre>
restart_new_emulator</pre>
- <p>This instruction is used when the application <c>ERTS</c>,
- <c>Kernel</c>, <c>STDLIB</c>, or <c>SASL</c> is
+ <p>This instruction is used when the application ERTS,
+ Kernel, STDLIB, or SASL is
upgraded. It shuts down the current emulator and starts a new
one. All processes are terminated gracefully, and the new
- version of <c>ERTS</c>, <c>Kernel</c>, <c>STDLIB</c>, and
- <c>SASL</c> are used when the emulator restarts.
+ version of ERTS, Kernel, STDLIB, and
+ SASL are used when the emulator restarts.
Only one <c>restart_new_emulator</c> instruction is allowed
in the <c>relup</c> file, and it must be placed first.
<seealso marker="systools#make_relup/3"><c>systools:make_relup/3,4</c></seealso>
@@ -385,7 +385,7 @@ restart_new_emulator</pre>
<warning>
<p>As stated earlier, instruction <c>restart_new_emulator</c>
causes the emulator to be restarted with new versions of
- <c>ERTS</c>, <c>Kernel</c>, <c>STDLIB</c>, and <c>SASL</c>.
+ ERTS>, Kernel, STDLIB, and SASL.
However, all other applications do at startup run their old
versions in this new emulator. This is usually no problem,
but every now and then incompatible changes occur to the
diff --git a/lib/sasl/doc/src/error_logging.xml b/lib/sasl/doc/src/error_logging.xml
index 8464a41ff9..4b2c960bbb 100644
--- a/lib/sasl/doc/src/error_logging.xml
+++ b/lib/sasl/doc/src/error_logging.xml
@@ -32,20 +32,20 @@
<rev>B</rev>
<file>error_logging.xml</file>
</header>
- <p>The <c>SASL</c> application introduces three types of reports:</p>
+ <p>The SASL application introduces three types of reports:</p>
<list type="bulleted">
<item>Supervisor report</item>
<item>Progress report</item>
<item>Crash report</item>
</list>
- <p>When the <c>SASL</c> application is started, it adds a handler that
+ <p>When the SASL application is started, it adds a handler that
formats and writes these reports, as specified in the configuration
- parameters for <c>SASL</c>, that is, the environment variables
- in the <c>SASL</c> application specification, which is found in the
- <c>.app</c> file of <c>SASL</c>. For details, see the
+ parameters for SASL, that is, the environment variables
+ in the SASL application specification, which is found in the
+ <c>.app</c> file of SASL. For details, see the
<seealso marker="sasl_app"><c>sasl(6)</c></seealso> application in the
Reference Manual and the <seealso marker="kernel:app"><c>app(4)</c></seealso>
- file in the <c>Kernel</c> Reference Manual.</p>
+ file in the Kernel Reference Manual.</p>
<section>
<title>Supervisor Report</title>
@@ -180,14 +180,14 @@
<p>The report browser is used to browse and format error reports
written by the error logger handler
<seealso marker="stdlib:log_mf_h"><c>log_mf_h</c></seealso>
- defined in <c>STDLIB</c>.</p>
+ defined in STDLIB.</p>
<p>The <c>log_mf_h</c> handler writes all reports to a
report logging directory, which is specified when
- configuring the <c>SASL</c> application.</p>
+ configuring the SASL application.</p>
<p>If the report browser is
used offline, the reports can be copied to another directory
specified when starting the browser. If no such directory
- is specified, the browser reads reports from the <c>SASL</c>
+ is specified, the browser reads reports from the SASL
<c>error_logger_mf_dir</c>.</p>
<section>
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index dae73f8b23..cd3f0e1864 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -31,6 +31,99 @@
</header>
<p>This document describes the changes made to the SASL application.</p>
+<section><title>SASL 3.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When both options 'warnings_as_errors' and 'silent' were
+ given to systools:make_script or systools:make_relup, no
+ error reason would be returned if warnings occurred.
+ Instead only the atom 'error' was returned. This is now
+ corrected.</p>
+ <p>
+ Options 'warnings_as_errors' and 'no_warn_sasl' are now
+ also allowed for systools:make_tar.</p>
+ <p>
+ Own Id: OTP-14170</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SASL 3.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>code:add_pathsa/1</c> and command line option
+ <c>-pa</c> both revert the given list of directories when
+ adding it at the beginning of the code path. This is now
+ documented.</p>
+ <p>
+ Own Id: OTP-13920 Aux Id: ERL-267 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SASL 3.0.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved dirty scheduler support. A purge of a module can
+ now be performed without having to wait for completion of
+ all ongoing dirty NIF calls.</p>
+ <p>
+ Note that when enabling support for dirty schedulers, a
+ new purge strategy will as of ERTS version 8.1 be
+ enabled. This new strategy is not fully backwards
+ compatible with the strategy used by default. For more
+ information see the documentation of <seealso
+ marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>.</p>
+ <p>
+ Own Id: OTP-13808 Aux Id: OTP-13833 </p>
+ </item>
+ <item>
+ <p>
+ A new purge strategy has been introduced. The new
+ strategy will by default be disabled during the OTP 19
+ release, but will be the only strategy available as of
+ the OTP 20 release.</p>
+ <p>
+ The new strategy is slightly incompatible with the
+ strategy being used by default in OTP 19. Using the
+ default strategy, processes holding <c>fun</c>s that
+ refer to the module being purged either fail a soft
+ purge, or will be killed during a hard purge. The new
+ strategy completely ignores <c>fun</c>s. If <c>fun</c>s
+ referring to the code being purged exist, and are used
+ after a purge, an exception will be raised upon usage.
+ That is, the behavior will be exactly the same as the
+ case when a <c>fun</c> is received by a process after the
+ purge.</p>
+ <p>
+ The new strategy can optionally be enabled when building
+ OTP during OTP 19, and will automatically be enabled if
+ the runtime system is built with support for dirty
+ schedulers.</p>
+ <p>
+ For more information see the documentation of <seealso
+ marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>.</p>
+ <p>
+ Own Id: OTP-13833</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SASL 3.0</title>
<section><title>Improvements and New Features</title>
@@ -824,7 +917,7 @@
<list>
<item>
<p>Use an infinity timeout in all calls to
- <c>gen_server:call()</c> in the <c>sasl</c>
+ <c>gen_server:call()</c> in the SASL
application.</p>
<p>
Own Id: OTP-8506 Aux Id: seq11509 </p>
diff --git a/lib/sasl/doc/src/part.xml b/lib/sasl/doc/src/part.xml
index f531ed2dea..659710487e 100644
--- a/lib/sasl/doc/src/part.xml
+++ b/lib/sasl/doc/src/part.xml
@@ -30,7 +30,7 @@
<file>part.xml</file>
</header>
<description>
- <p>The System Architecture Support Libraries <c>SASL</c> application
+ <p>The System Architecture Support Libraries SASL application
provides support for alarm handling, release handling, and
related functions.</p>
</description>
diff --git a/lib/sasl/doc/src/rb.xml b/lib/sasl/doc/src/rb.xml
index 1ce19046eb..d5df4fd345 100644
--- a/lib/sasl/doc/src/rb.xml
+++ b/lib/sasl/doc/src/rb.xml
@@ -38,7 +38,7 @@
<p>The Report Browser (RB) tool is used to browse and
format error reports written by the error logger handler
<seealso marker="stdlib:log_mf_h"><c>log_mf_h</c></seealso>
- in <c>STDLIB</c>.</p>
+ in STDLIB.</p>
</description>
<funcs>
@@ -62,7 +62,7 @@
reports that match that filter.</p>
<p>The reports are matched using the
<seealso marker="stdlib:proplists"><c>proplists</c></seealso>
- module in <c>STDLIB</c>. The report must be a proplist
+ module in STDLIB. The report must be a proplist
to be matched against any of the filters.</p>
<p>If the filter has the form <c>{Key, RegExp, re}</c>, the
report must contain an element with key equal to <c>Key</c> and
@@ -102,7 +102,7 @@
</list>
<p>For a definition of valid regular expressions and options, see
the <seealso marker="stdlib:re"><c>re</c></seealso> module in
- <c>STDLIB</c> and in particular function <c>re:run/3</c>.</p>
+ STDLIB and in particular function <c>re:run/3</c>.</p>
<p>For details about data type <c>mp()</c>, see
<seealso marker="stdlib:re#type-mp"><c>re:mp()</c></seealso>.</p>
</desc>
diff --git a/lib/sasl/doc/src/ref_man.xml b/lib/sasl/doc/src/ref_man.xml
index 42045df5ec..78cf1eb26e 100644
--- a/lib/sasl/doc/src/ref_man.xml
+++ b/lib/sasl/doc/src/ref_man.xml
@@ -30,7 +30,7 @@
<file>application.xml</file>
</header>
<description>
- <p>The <c>SASL</c> application provides support for alarm handling,
+ <p>The SASL application provides support for alarm handling,
release handling, and related functions.</p>
</description>
<xi:include href="sasl_app.xml"/>
diff --git a/lib/sasl/doc/src/rel.xml b/lib/sasl/doc/src/rel.xml
index d6558c06b4..9356b2cd47 100644
--- a/lib/sasl/doc/src/rel.xml
+++ b/lib/sasl/doc/src/rel.xml
@@ -59,7 +59,7 @@
<tag><c>Vsn = string()</c></tag>
<item><p>Release version.</p></item>
<tag><c>EVsn = string()</c></tag>
- <item><p><c>ERTS</c> version the release is intended for.</p></item>
+ <item><p>ERTS version the release is intended for.</p></item>
<tag><c>Application = atom()</c></tag>
<item><p>Name of an application included in the release.</p></item>
<tag><c>AppVsn = string()</c></tag>
@@ -82,8 +82,8 @@
to the same value as in the application resource file.</p></item>
</taglist>
<note>
- <p>The list of applications must contain the <c>Kernel</c> and
- <c>STDLIB</c> applications.</p>
+ <p>The list of applications must contain the Kernel and
+ STDLIB applications.</p>
</note>
</section>
diff --git a/lib/sasl/doc/src/release_handler.xml b/lib/sasl/doc/src/release_handler.xml
index bcbc5f5339..8f073807fb 100644
--- a/lib/sasl/doc/src/release_handler.xml
+++ b/lib/sasl/doc/src/release_handler.xml
@@ -31,7 +31,7 @@
<module>release_handler</module>
<modulesummary>Unpacking and Installation of Release Packages</modulesummary>
<description>
- <p>The <em>release handler</em> process belongs to the <c>SASL</c>
+ <p>The <em>release handler</em> process belongs to the SASL
application, which is responsible for <em>release handling</em>,
that is, unpacking, installation, and removal of release packages.</p>
<p>An introduction to release handling and an example is provided in
@@ -44,7 +44,7 @@
directory of the previous version of the release, where
<c>$ROOT</c> is the installation root directory,
<seealso marker="kernel:code#root_dir/0"><c>code:root_dir()</c></seealso>.
- Another <c>releases</c> directory can be specified using the <c>SASL</c>
+ Another <c>releases</c> directory can be specified using the SASL
configuration parameter <c>releases_dir</c> or the OS environment
variable <c>RELDIR</c>. The release handler must have write access
to this directory to install the new release.
@@ -56,7 +56,7 @@
<item>A boot script, <c>Name.boot</c></item>
</list>
<p>The <c>.rel</c> file contains information about the release: its name,
- version, and which <c>ERTS</c> and application versions it uses.</p>
+ version, and which ERTS and application versions it uses.</p>
<p>A release package can also contain:</p>
<list type="bulleted">
<item>A release upgrade file, <c>relup</c></item>
@@ -115,7 +115,7 @@
<em>System Documentation</em>. In this case, the system
configuration file <c>sys.config</c> is mandatory.</p>
<p>The installation of a new release can restart the system. Which
- program to use is specified by the <c>SASL</c> configuration
+ program to use is specified by the SASL configuration
parameter <c>start_prg</c>, which defaults
to <c>$ROOT/bin/start</c>.</p>
<p>The emulator restart on Windows NT expects that the system is
@@ -132,7 +132,7 @@
is made permanent.</p>
<p>The release handler at a node running on a diskless machine,
or with a read-only file system, must be configured accordingly
- using the following <c>SASL</c> configuration parameters (for
+ using the following SASL configuration parameters (for
details, see <seealso marker="sasl_app">sasl(6)</seealso>):</p>
<taglist>
<tag><c>masters</c></tag>
@@ -287,7 +287,7 @@
returned, the emulator is restarted
before the upgrade instructions are executed. This
occurs if the emulator or any of the applications
- <c>Kernel</c>, <c>STDLIB</c>, or <c>SASL</c>
+ Kernel, STDLIB, or SASL
are updated. The new emulator version
and these core applications execute after the restart.
For all other applications the old versions are
@@ -310,13 +310,13 @@
<tag><c>code_change_timeout</c></tag>
<item><p>Defines the time-out
for all calls to
- <seealso marker="stdlib:sys#change_code/4"><c>stdlib:sys:change_code</c></seealso>.
+ <seealso marker="stdlib:sys#change_code/4"><c>sys:change_code</c></seealso>.
If no value is specified or <c>default</c> is specified, the
default value defined in <c>sys</c> is used.</p></item>
<tag><c>suspend_timeout</c></tag>
<item><p>Defines the time-out for
all calls to
- <seealso marker="stdlib:sys#suspend/1"><c>stdlib:sys:suspend</c></seealso>.
+ <seealso marker="stdlib:sys#suspend/1"><c>sys:suspend</c></seealso>.
If no value is specified, the values defined by the <c>Timeout</c>
parameter of the <c>upgrade</c> or <c>suspend</c> instructions are used.
If <c>default</c> is specified, the default value defined in
@@ -342,7 +342,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]).
=> {ok,NewVsn}</code>
<p>If <c>NewVsn</c> is installed with option
<c>{update_paths,true}</c>, then
- <seealso marker="kernel:code#lib_dir/1"><c>kernel:code:lib_dir(myapp)</c></seealso>
+ <seealso marker="kernel:code#lib_dir/1"><c>code:lib_dir(myapp)</c></seealso>
returns <c>/home/user/myapp-1.0</c>.</p></item>
</taglist>
<note>
@@ -801,7 +801,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]).
is an atom named from the Posix error codes, such as
<c>enoent</c>, <c>eacces</c>, or <c>eisdir</c>. See
<seealso marker="kernel:file"><c>file(3)</c></seealso>
- in <c>Kernel</c>.</p></item>
+ in Kernel.</p></item>
<tag><c>Posix</c></tag>
<item><p>Some file operation failed, as for the previous item in
the list.</p></item>
diff --git a/lib/sasl/doc/src/sasl_app.xml b/lib/sasl/doc/src/sasl_app.xml
index 52f449c2a8..0576397f9b 100644
--- a/lib/sasl/doc/src/sasl_app.xml
+++ b/lib/sasl/doc/src/sasl_app.xml
@@ -31,17 +31,17 @@
<app>sasl</app>
<appsummary>The SASL application</appsummary>
<description>
- <p>The <c>SASL</c> application provides the following services:</p>
+ <p>The SASL application provides the following services:</p>
<list type="bulleted">
<item><c>alarm_handler</c></item>
<item><c>rb</c></item>
<item><c>release_handler</c></item>
<item><c>systools</c></item>
</list>
- <p>The <c>SASL</c> application also includes <c>error_logger</c> event
- handlers for formatting <c>SASL</c> error and crash reports.</p>
+ <p>The SASL application also includes <c>error_logger</c> event
+ handlers for formatting SASL error and crash reports.</p>
<note>
- <p>The <c>SASL</c> application in OTP has nothing to do with
+ <p>The SASL application in OTP has nothing to do with
"Simple Authentication and Security Layer" (RFC 4422).</p>
</note>
</description>
@@ -49,7 +49,7 @@
<section>
<title>Error Logger Event Handlers</title>
<p>The following error logger event handlers are used by
- the <c>SASL</c> application.</p>
+ the SASL application.</p>
<taglist>
<tag><c>sasl_report_tty_h</c></tag>
<item>
@@ -57,7 +57,7 @@
reports</em>, and <em>progress reports</em> to <c>stdio</c>.
This error logger event handler uses
<seealso marker="kernel:kernel_app#error_logger_format_depth">error_logger_format_depth</seealso>
- in the <c>Kernel</c> application to limit how much detail is
+ in the Kernel application to limit how much detail is
printed in crash and supervisor reports.</p>
</item>
<tag><c>sasl_report_file_h</c></tag>
@@ -66,7 +66,7 @@
report</em>, and <em>progress report</em> to a single file.
This error logger event handler uses
<seealso marker="kernel:kernel_app#error_logger_format_depth">error_logger_format_depth</seealso>
- in the <c>Kernel</c> application to limit the details
+ in the Kernel application to limit the details
printed in crash and supervisor reports.</p>
</item>
<tag><c>log_mf_h</c></tag>
@@ -75,9 +75,9 @@
error logger to disk. Multiple files and log rotation are
used. For efficiency reasons, each event is written as a
binary. For more information about this handler,
- see <seealso marker="stdlib:log_mf_h">the <c>STDLIB</c> Reference
+ see <seealso marker="stdlib:log_mf_h">the STDLIB Reference
Manual</seealso>.</p>
- <p>To activate this event handler, three <c>SASL</c>
+ <p>To activate this event handler, three SASL
configuration parameters must be set,
<c>error_logger_mf_dir</c>, <c>error_logger_mf_maxbytes</c>,
and <c>error_logger_mf_maxfiles</c>. The next section provides
@@ -88,9 +88,9 @@
<section>
<title>Configuration</title>
- <p>The following configuration parameters are defined for the <c>SASL</c>
+ <p>The following configuration parameters are defined for the SASL
application. For more information about configuration parameters, see
- <seealso marker="kernel:app"><c>app(4)</c></seealso> in <c>Kernel</c>.</p>
+ <seealso marker="kernel:app"><c>app(4)</c></seealso> in Kernel.</p>
<p>All configuration parameters are optional.</p>
<taglist>
<tag><c><![CDATA[sasl_error_logger = Value ]]></c></tag>
@@ -112,7 +112,7 @@
Use <c>[append]</c> to have the <c>FileName</c> open in append mode.
<c>FileName</c> is a string.</p></item>
<tag><c>false</c></tag>
- <item><p>No <c>SASL</c> error logger handler is installed.</p></item>
+ <item><p>No SASL error logger handler is installed.</p></item>
</taglist>
</item>
<tag><c><![CDATA[errlog_type = error | progress | all ]]></c></tag>
diff --git a/lib/sasl/doc/src/sasl_intro.xml b/lib/sasl/doc/src/sasl_intro.xml
index b71dafb192..f74a7c1db8 100644
--- a/lib/sasl/doc/src/sasl_intro.xml
+++ b/lib/sasl/doc/src/sasl_intro.xml
@@ -32,7 +32,7 @@
<section>
<title>Scope</title>
- <p>The <c>SASL</c> application provides support for:</p>
+ <p>The SASL application provides support for:</p>
<list type="bulleted">
<item>Error logging</item>
<item>Alarm handling</item>
diff --git a/lib/sasl/doc/src/script.xml b/lib/sasl/doc/src/script.xml
index 8ed132354d..b40ff28179 100644
--- a/lib/sasl/doc/src/script.xml
+++ b/lib/sasl/doc/src/script.xml
@@ -88,7 +88,7 @@
follows:</p>
<list type="bulleted">
<item><c>-pa Dir1 Dir2 ... DirN</c> adds the directories
- <c>Dir1, Dir2, ..., DirN</c> to the front of the initial
+ <c>DirN, DirN-1, ..., Dir2, Dir1</c> to the front of the initial
load path.</item>
<item><c>-pz Dir1 Dir2 ... DirN</c> adds the directories
<c>Dir1, Dir2, ..., DirN</c> to the end of the initial
diff --git a/lib/sasl/doc/src/systools.xml b/lib/sasl/doc/src/systools.xml
index 7446762de4..e7c3c499da 100644
--- a/lib/sasl/doc/src/systools.xml
+++ b/lib/sasl/doc/src/systools.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2016</year>
+ <year>2017</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -85,7 +85,7 @@
</item>
<item>
<p>If the emulator needs to be restarted after upgrading or
- downgrading, that is, if the <c>ERTS</c> version differs
+ downgrading, that is, if the ERTS version differs
between <c>Name.rel</c> and <c>Name2.rel</c></p>
</item>
</list>
@@ -201,10 +201,10 @@
between the applications. Where there are no dependencies,
the order in the <c>.rel</c> file is kept.</p>
<p>The function fails if the mandatory
- applications <c>Kernel</c> and <c>STDLIB</c> are not
+ applications Kernel and STDLIB are not
included in the <c>.rel</c> file and have start
type <c>permanent</c> (which is default).</p>
- <p>If <c>SASL</c> is not included as an application in
+ <p>If SASL is not included as an application in
the <c>.rel</c> file, a warning is issued because such a
release cannot be used in an upgrade. To turn off this
warning, add option <c>no_warn_sasl</c>.</p>
@@ -268,7 +268,7 @@
<fsummary>Creates a release package.</fsummary>
<type>
<v>Name = string()</v>
- <v>Opt = {dirs,[IncDir]} | {path,[Dir]} | {variables,[Var]} | {var_tar,VarTar} | {erts,Dir} | src_tests | exref | {exref,[App]} | silent | {outdir,Dir}</v>
+ <v>Opt = {dirs,[IncDir]} | {path,[Dir]} | {variables,[Var]} | {var_tar,VarTar} | {erts,Dir} | src_tests | exref | {exref,[App]} | silent | {outdir,Dir} | | no_warn_sasl | warnings_as_errors</v>
<v>&nbsp;Dir = string()</v>
<v>&nbsp;IncDir = src | include | atom()</v>
<v>&nbsp;Var = {VarName,PreFix}</v>
@@ -297,6 +297,10 @@
directory unless <c>Name</c> contains a path. If option
<c>{outdir,Dir}</c> is specified, it is located in <c>Dir</c>
instead.</p>
+ <p>If SASL is not included as an application in
+ the <c>.rel</c> file, a warning is issued because such a
+ release cannot be used in an upgrade. To turn off this
+ warning, add option <c>no_warn_sasl</c>.</p>
<p>By default, the release package contains the directories
<c>lib/App-Vsn/ebin</c> and <c>lib/App-Vsn/priv</c> for each
included application. If more directories are to be included,
diff --git a/lib/sasl/src/sasl.app.src b/lib/sasl/src/sasl.app.src
index 4ee8a7d6c8..633cdfa070 100644
--- a/lib/sasl/src/sasl.app.src
+++ b/lib/sasl/src/sasl.app.src
@@ -46,5 +46,5 @@
{errlog_type, all}]},
{mod, {sasl, []}},
{runtime_dependencies, ["tools-2.6.14","stdlib-3.0","kernel-5.0",
- "erts-8.0"]}]}.
+ "erts-8.1"]}]}.
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 352e4984df..3f8c8bc6d8 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -94,7 +94,11 @@ make_script(RelName, Output, Flags) when is_list(RelName),
Warnings = wsasl(Flags, Warnings0),
case systools_lib:werror(Flags, Warnings) of
true ->
- return(ok,Warnings,Flags);
+ Warnings1 = [W || {warning,W}<-Warnings],
+ return({error,?MODULE,
+ {warnings_treated_as_errors,Warnings1}},
+ Warnings,
+ Flags);
false ->
case generate_script(Output,Release,Appls,Flags) of
ok ->
@@ -115,7 +119,6 @@ make_script(RelName, _Output, Flags) when is_list(Flags) ->
make_script(RelName, _Output, Flags) ->
badarg(Flags,[RelName, Flags]).
-
wsasl(Options, Warnings) ->
case lists:member(no_warn_sasl,Options) of
true -> lists:delete({warning,missing_sasl},Warnings);
@@ -148,21 +151,10 @@ get_outdir(Flags) ->
return(ok,Warnings,Flags) ->
case member(silent,Flags) of
true ->
- case systools_lib:werror(Flags, Warnings) of
- true ->
- error;
- false ->
- {ok,?MODULE,Warnings}
- end;
+ {ok,?MODULE,Warnings};
_ ->
- case member(warnings_as_errors,Flags) of
- true ->
- io:format("~ts",[format_warning(Warnings, true)]),
- error;
- false ->
- io:format("~ts",[format_warning(Warnings)]),
- ok
- end
+ io:format("~ts",[format_warning(Warnings)]),
+ ok
end;
return({error,Mod,Error},_,Flags) ->
case member(silent,Flags) of
@@ -300,6 +292,8 @@ add_apply_upgrade(Script,Args) ->
%% {variables,[{Name,AbsString}]}
%% {machine, jam | beam | vee}
%% {var_tar, include | ownfile | omit}
+%% no_warn_sasl
+%% warnings_as_errors
%%
%% The tar file contains:
%% lib/App-Vsn/ebin
@@ -332,13 +326,23 @@ make_tar(RelName, Flags) when is_list(RelName), is_list(Flags) ->
Path = make_set(Path1 ++ code:get_path()),
ModTestP = {member(src_tests, Flags),xref_p(Flags)},
case get_release(RelName, Path, ModTestP, machine(Flags)) of
- {ok, Release, Appls, Warnings} ->
- case catch mk_tar(RelName, Release, Appls, Flags, Path1) of
- ok ->
- return(ok,Warnings,Flags);
- Error ->
- return(Error,Warnings,Flags)
- end;
+ {ok, Release, Appls, Warnings0} ->
+ Warnings = wsasl(Flags, Warnings0),
+ case systools_lib:werror(Flags, Warnings) of
+ true ->
+ Warnings1 = [W || {warning,W}<-Warnings],
+ return({error,?MODULE,
+ {warnings_treated_as_errors,Warnings1}},
+ Warnings,
+ Flags);
+ false ->
+ case catch mk_tar(RelName, Release, Appls, Flags, Path1) of
+ ok ->
+ return(ok,Warnings,Flags);
+ Error ->
+ return(Error,Warnings,Flags)
+ end
+ end;
Error ->
return(Error,[],Flags)
end;
@@ -1491,8 +1495,9 @@ mandatory_modules() ->
preloaded() ->
%% Sorted
[erl_prim_loader,erl_tracer,erlang,
- erts_code_purger,
- erts_internal,init,otp_ring0,prim_eval,prim_file,
+ erts_code_purger,erts_dirty_process_code_checker,
+ erts_internal,erts_literal_area_collector,
+ init,otp_ring0,prim_eval,prim_file,
prim_inet,prim_zip,zlib].
%%______________________________________________________________________
@@ -2108,90 +2113,80 @@ cas([Y | Args], X) ->
%% Check Options for make_tar
check_args_tar(Args) ->
- cat(Args, {undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, []}).
+ cat(Args, []).
-cat([], {_Path,_Sil,_Dirs,_Erts,_Test,_Var,_VarTar,_Mach,_Xref,_XrefApps, X}) ->
+cat([], X) ->
X;
%%% path ---------------------------------------------------------------
-cat([{path, P} | Args], {Path, Sil, Dirs, Erts, Test,
- Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(P) ->
+cat([{path, P} | Args], X) when is_list(P) ->
case check_path(P) of
ok ->
- cat(Args, {P, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+ cat(Args, X);
error ->
- cat(Args, {Path, Sil, Dirs, Erts, Test,
- Var, VarTar, Mach, Xref, XrefApps, X++[{path,P}]})
+ cat(Args, X++[{path,P}])
end;
%%% silent -------------------------------------------------------------
-cat([silent | Args], {Path, _Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
- cat(Args, {Path, silent, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+cat([silent | Args], X) ->
+ cat(Args, X);
%%% dirs ---------------------------------------------------------------
-cat([{dirs, D} | Args], {Path, Sil, Dirs, Erts, Test,
- Var, VarTar, Mach, Xref, XrefApps, X}) ->
+cat([{dirs, D} | Args], X) ->
case check_dirs(D) of
ok ->
- cat(Args, {Path, Sil, D, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+ cat(Args, X);
error ->
- cat(Args, {Path, Sil, Dirs, Erts, Test,
- Var, VarTar, Mach, Xref, XrefApps, X++[{dirs, D}]})
+ cat(Args, X++[{dirs, D}])
end;
%%% erts ---------------------------------------------------------------
-cat([{erts, E} | Args], {Path, Sil, Dirs, _Erts, Test,
- Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(E)->
- cat(Args, {Path, Sil, Dirs, E, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+cat([{erts, E} | Args], X) when is_list(E)->
+ cat(Args, X);
%%% src_tests ----------------------------------------------------
-cat([src_tests | Args], {Path, Sil, Dirs, Erts, _Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
- cat(Args, {Path, Sil, Dirs, Erts, src_tests, Var, VarTar, Mach,
- Xref, XrefApps, X});
+cat([src_tests | Args], X) ->
+ cat(Args, X);
%%% variables ----------------------------------------------------------
-cat([{variables, V} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(V) ->
+cat([{variables, V} | Args], X) when is_list(V) ->
case check_vars(V) of
ok ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, V, VarTar, Mach, Xref, XrefApps, X});
+ cat(Args, X);
error ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
- Xref, XrefApps, X++[{variables, V}]})
+ cat(Args, X++[{variables, V}])
end;
%%% var_tar ------------------------------------------------------------
-cat([{var_tar, VT} | Args], {Path, Sil, Dirs, Erts, Test,
- Var, _VarTar, Mach, Xref, XrefApps, X}) when VT == include ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, include, Mach, Xref, XrefApps, X});
-cat([{var_tar, VT} | Args], {Path, Sil, Dirs, Erts, Test,
- Var, _VarTar, Mach, Xref, XrefApps, X}) when VT == ownfile ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, ownfile, Mach, Xref, XrefApps, X});
-cat([{var_tar, VT} | Args], {Path, Sil, Dirs, Erts, Test,
- Var, _VarTar, Mach, Xref, XrefApps, X}) when VT == omit ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, omit, Mach, Xref, XrefApps, X});
+cat([{var_tar, VT} | Args], X) when VT == include;
+ VT == ownfile;
+ VT == omit ->
+ cat(Args, X);
%%% machine ------------------------------------------------------------
-cat([{machine, M} | Args], {Path, Sil, Dirs, Erts, Test,
- Var, VarTar, Mach, Xref, XrefApps, X}) when is_atom(M) ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+cat([{machine, M} | Args], X) when is_atom(M) ->
+ cat(Args, X);
%%% exref --------------------------------------------------------------
-cat([exref | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, _Xref, XrefApps, X}) ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, exref, XrefApps, X});
+cat([exref | Args], X) ->
+ cat(Args, X);
%%% exref Apps ---------------------------------------------------------
-cat([{exref, Apps} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(Apps) ->
+cat([{exref, Apps} | Args], X) when is_list(Apps) ->
case check_apps(Apps) of
ok ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
- Xref, Apps, X});
+ cat(Args, X);
error ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
- Xref, XrefApps, X++[{exref, Apps}]})
+ cat(Args, X++[{exref, Apps}])
end;
%%% outdir Dir ---------------------------------------------------------
-cat([{outdir, Dir} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(Dir) ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
- Xref, XrefApps, X});
+cat([{outdir, Dir} | Args], X) when is_list(Dir) ->
+ cat(Args, X);
%%% otp_build (secret, not documented) ---------------------------------
-cat([otp_build | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+cat([otp_build | Args], X) ->
+ cat(Args, X);
+%%% warnings_as_errors ----
+cat([warnings_as_errors | Args], X) ->
+ cat(Args, X);
+%%% no_warn_sasl ----
+cat([no_warn_sasl | Args], X) ->
+ cat(Args, X);
%%% no_module_tests (kept for backwards compatibility, but ignored) ----
-cat([no_module_tests | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+cat([no_module_tests | Args], X) ->
+ cat(Args, X);
%%% ERROR --------------------------------------------------------------
-cat([Y | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
- cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X++[Y]}).
+cat([Y | Args], X) ->
+ cat(Args, X++[Y]).
check_path([]) ->
ok;
@@ -2291,6 +2286,9 @@ format_error({delete,File,Error}) ->
[File,file:format_error(Error)]);
format_error({tar_error,What}) ->
form_tar_err(What);
+format_error({warnings_treated_as_errors,Warnings}) ->
+ io_lib:format("Warnings being treated as errors:~n~ts",
+ [map(fun(W) -> form_warn("",W) end, Warnings)]);
format_error(ListOfErrors) when is_list(ListOfErrors) ->
format_errors(ListOfErrors);
format_error(E) -> io_lib:format("~p~n",[E]).
@@ -2347,24 +2345,15 @@ form_tar_err({add, File, Error}) ->
%% Format warning
format_warning(Warnings) ->
- format_warning(Warnings, false).
-
-format_warning(Warnings, Werror) ->
- Prefix = case Werror of
- true ->
- "";
- false ->
- "*WARNING* "
- end,
- map(fun({warning,W}) -> form_warn(Prefix, W) end, Warnings).
-
-form_warn(Prefix, {source_not_found,{Mod,_,App,_,_}}) ->
+ map(fun({warning,W}) -> form_warn("*WARNING* ", W) end, Warnings).
+
+form_warn(Prefix, {source_not_found,{Mod,App,_}}) ->
io_lib:format("~ts~w: Source code not found: ~w.erl~n",
[Prefix,App,Mod]);
form_warn(Prefix, {{parse_error, File},{_,_,App,_,_}}) ->
io_lib:format("~ts~w: Parse error: ~p~n",
[Prefix,App,File]);
-form_warn(Prefix, {obj_out_of_date,{Mod,_,App,_,_}}) ->
+form_warn(Prefix, {obj_out_of_date,{Mod,App,_}}) ->
io_lib:format("~ts~w: Object code (~w) out of date~n",
[Prefix,App,Mod]);
form_warn(Prefix, {exref_undef, Undef}) ->
@@ -2374,8 +2363,8 @@ form_warn(Prefix, {exref_undef, Undef}) ->
end,
map(F, Undef);
form_warn(Prefix, missing_sasl) ->
- io_lib:format("~ts: Missing application sasl. "
+ io_lib:format("~tsMissing application sasl. "
"Can not upgrade with this release~n",
[Prefix]);
form_warn(Prefix, What) ->
- io_lib:format("~ts ~p~n", [Prefix,What]).
+ io_lib:format("~ts~p~n", [Prefix,What]).
diff --git a/lib/sasl/src/systools_relup.erl b/lib/sasl/src/systools_relup.erl
index 28534dc0c8..6bc26c8cc9 100644
--- a/lib/sasl/src/systools_relup.erl
+++ b/lib/sasl/src/systools_relup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -155,36 +155,12 @@ mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs) ->
mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, Opts) ->
case check_opts(Opts) of
[] ->
- R = (catch do_mk_relup(TopRelFile,BaseUpRelDcs,BaseDnRelDcs,
- add_code_path(Opts), Opts)),
- case {get_opt(silent, Opts), get_opt(noexec, Opts)} of
- {false, false} ->
- case R of
- {ok, _Res, _Mod, Ws} ->
- print_warnings(Ws, Opts),
- case systools_lib:werror(Opts, Ws) of
- true ->
- error;
- false ->
- ok
- end;
- Other ->
- print_error(Other),
- error
- end;
- _ ->
- case R of
- {ok, _Res, _Mod, Ws} ->
- case systools_lib:werror(Opts, Ws) of
- true ->
- error;
- false ->
- R
- end;
- R ->
- R
- end
- end;
+ R = try do_mk_relup(TopRelFile,BaseUpRelDcs,BaseDnRelDcs,
+ add_code_path(Opts), Opts)
+ catch throw:Error ->
+ Error
+ end,
+ done_mk_relup(Opts, R);
BadArg ->
erlang:error({badarg, BadArg})
end.
@@ -224,17 +200,45 @@ do_mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, Path, Opts) ->
{Dn, Ws2} = foreach_baserel_dn(TopRel, TopApps, BaseDnRelDcs,
Path, Opts, Ws1),
Relup = {TopRel#release.vsn, Up, Dn},
- case systools_lib:werror(Opts, Ws2) of
- true ->
- ok;
- false ->
- write_relup_file(Relup, Opts)
- end,
- {ok, Relup, ?MODULE, Ws2};
+
+ {ok, Relup, Ws2};
Other ->
- throw(Other)
+ Other
end.
+done_mk_relup(Opts, {ok,Relup,Ws}) ->
+ WAE = get_opt(warnings_as_errors,Opts),
+ Silent = get_opt(silent,Opts),
+ Noexec = get_opt(noexec,Opts),
+
+ if WAE andalso Ws=/=[] ->
+ return_error(Silent,
+ {error,?MODULE,{warnings_treated_as_errors, Ws}});
+ not Noexec ->
+ case write_relup_file(Relup,Opts) of
+ ok ->
+ return_ok(Silent,Relup,Ws);
+ Error ->
+ return_error(Silent,Error)
+ end;
+ true -> % noexec
+ return_ok(true,Relup,Ws)
+ end;
+done_mk_relup(Opts, Error) ->
+ return_error(get_opt(silent,Opts) orelse get_opt(noexec,Opts), Error).
+
+return_error(true, Error) ->
+ Error;
+return_error(false, Error) ->
+ print_error(Error),
+ error.
+
+return_ok(true,Relup,Ws) ->
+ {ok,Relup,?MODULE,Ws};
+return_ok(false,_Relup,Ws) ->
+ print_warnings(Ws),
+ ok.
+
%%-----------------------------------------------------------------
%% foreach_baserel_up(Rel, TopApps, BaseRelDcs, Path, Opts, Ws) -> Ret
%% foreach_baserel_dn(Rel, TopApps, BaseRelDcs, Path, Opts, Ws) -> Ret
@@ -529,33 +533,18 @@ to_list(X) when is_list(X) -> X.
%% Writes a relup file.
%%
write_relup_file(Relup, Opts) ->
- case get_opt(noexec, Opts) of
- true ->
- ok;
- _ ->
- Filename = case get_opt(outdir, Opts) of
- OutDir when is_list(OutDir) ->
- filename:join(filename:absname(OutDir),
- "relup");
- false ->
- "relup";
- Badarg ->
- throw({error, ?MODULE, {badarg, {outdir,Badarg}}})
- end,
-
- case file:open(Filename, [write]) of
- {ok, Fd} ->
- io:format(Fd, "~p.~n", [Relup]),
- case file:close(Fd) of
- ok -> ok;
- {error,Reason} ->
- throw({error, ?MODULE,
- {file_problem, {"relup", {close,Reason}}}})
- end;
- {error, Reason} ->
- throw({error, ?MODULE,
- {file_problem, {"relup", {open, Reason}}}})
- end
+ Filename = filename:join(filename:absname(get_opt(outdir,Opts)),
+ "relup"),
+ case file:open(Filename, [write]) of
+ {ok, Fd} ->
+ io:format(Fd, "~p.~n", [Relup]),
+ case file:close(Fd) of
+ ok -> ok;
+ {error,Reason} ->
+ {error, ?MODULE, {file_problem, {"relup", {close,Reason}}}}
+ end;
+ {error, Reason} ->
+ {error, ?MODULE, {file_problem, {"relup", {open, Reason}}}}
end.
add_code_path(Opts) ->
@@ -593,10 +582,9 @@ default(path) -> false;
default(noexec) -> false;
default(silent) -> false;
default(restart_emulator) -> false;
-default(outdir) -> false.
+default(outdir) -> ".";
+default(warnings_as_errors) -> false.
-print_error({'EXIT', Err}) ->
- print_error(Err);
print_error({error, Mod, Error}) ->
S = apply(Mod, format_error, [Error]),
io:format(S, []);
@@ -614,24 +602,20 @@ format_error({missing_sasl,Release}) ->
io_lib:format("No sasl application in release ~ts, ~ts. "
"Can not be upgraded.",
[Release#release.name, Release#release.vsn]);
+format_error({warnings_treated_as_errors, Warnings}) ->
+ io_lib:format("Warnings being treated as errors:~n~ts",
+ [[format_warning("",W) || W <- Warnings]]);
format_error(Error) ->
- io:format("~p~n", [Error]).
+ io_lib:format("~p~n", [Error]).
-print_warnings(Ws, Opts) when is_list(Ws) ->
- lists:foreach(fun(W) -> print_warning(W, Opts) end, Ws);
-print_warnings(W, Opts) ->
- print_warning(W, Opts).
+print_warnings(Ws) when is_list(Ws) ->
+ lists:foreach(fun(W) -> print_warning(W) end, Ws);
+print_warnings(W) ->
+ print_warning(W).
-print_warning(W, Opts) ->
- Prefix = case lists:member(warnings_as_errors, Opts) of
- true ->
- "";
- false ->
- "*WARNING* "
- end,
- S = format_warning(Prefix, W),
- io:format("~ts", [S]).
+print_warning(W) ->
+ io:format("~ts", [format_warning(W)]).
format_warning(W) ->
format_warning("*WARNING* ", W).
@@ -639,6 +623,8 @@ format_warning(W) ->
format_warning(Prefix, {erts_vsn_changed, {Rel1, Rel2}}) ->
io_lib:format("~tsThe ERTS version changed between ~p and ~p~n",
[Prefix, Rel1, Rel2]);
+format_warning(Prefix, pre_R15_emulator_upgrade) ->
+ io_lib:format("~tsUpgrade from an OTP version earlier than R15. New code should be compiled with the old emulator.~n",[Prefix]);
format_warning(Prefix, What) ->
io_lib:format("~ts~p~n",[Prefix, What]).
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index ad58bb6b7b..7093158502 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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.
@@ -1063,6 +1063,12 @@ otp_9395_check_and_purge(cleanup,_Conf) ->
%% OTP-9395 - performance problems when there are MANY processes
%% Upgrade which updates many modules (brutal_purge)
otp_9395_update_many_mods(Conf) when is_list(Conf) ->
+
+ %% "nain" is very slow - it fails this test quite often due to a
+ %% long sys call
+ %% /proc/cpuinfo: "clock: 1249MHz"
+ inet:gethostname() == {ok,"nain"} andalso throw({skip,"slow test host"}),
+
%% Set some paths
PrivDir = priv_dir(Conf),
Dir = filename:join(PrivDir,"otp_9395_update_many_mods"),
@@ -1162,6 +1168,12 @@ otp_9395_update_many_mods(cleanup,_Conf) ->
%% OTP-9395 - performance problems when there are MANY processes
%% Upgrade which removes many modules (brutal_purge)
otp_9395_rm_many_mods(Conf) when is_list(Conf) ->
+
+ %% "nain" is very slow - it fails this test quite often due to a
+ %% long sys call
+ %% /proc/cpuinfo: "clock: 1249MHz"
+ inet:gethostname() == {ok,"nain"} andalso throw({skip,"slow test host"}),
+
%% Set some paths
PrivDir = priv_dir(Conf),
Dir = filename:join(PrivDir,"otp_9395_rm_many_mods"),
@@ -1920,7 +1932,7 @@ wait_nodes_up(Nodes, Tag) ->
wait_nodes_up(Nodes0, Tag, Apps) ->
?t:format("wait_nodes_up(~p, ~p, ~p):",[Nodes0, Tag, Apps]),
Nodes = fix_nodes(Nodes0),
- wait_nodes_up(Nodes, Tag, lists:umerge(Apps,[kernel,stdlib,sasl]), 30).
+ wait_nodes_up(Nodes, Tag, lists:umerge(Apps,[kernel,stdlib,sasl]), 60).
fix_nodes([{Node,InitPid}|Nodes]) ->
[{Node,InitPid} | fix_nodes(Nodes)];
@@ -1962,7 +1974,7 @@ wait_nodes_up(Nodes, Tag, Apps, N) ->
?t:format("",[]),
ok;
_ ->
- timer:sleep(1000),
+ timer:sleep(2000),
wait_nodes_up(Pang, Tag, Apps, N-1)
end.
diff --git a/lib/sasl/test/release_handler_SUITE_data/start b/lib/sasl/test/release_handler_SUITE_data/start
index 87275045b1..eab2b77aed 100755
--- a/lib/sasl/test/release_handler_SUITE_data/start
+++ b/lib/sasl/test/release_handler_SUITE_data/start
@@ -21,8 +21,7 @@ then
fi
HEART_COMMAND=$ROOTDIR/bin/start
-HW_WD_DISABLE=true
-export HW_WD_DISABLE HEART_COMMAND
+export HEART_COMMAND
START_ERL_DATA=${1:-$RELDIR/start_erl.data}
diff --git a/lib/sasl/test/release_handler_SUITE_data/start_client b/lib/sasl/test/release_handler_SUITE_data/start_client
index 5ea94d6f7c..05d744f06e 100755
--- a/lib/sasl/test/release_handler_SUITE_data/start_client
+++ b/lib/sasl/test/release_handler_SUITE_data/start_client
@@ -24,8 +24,7 @@ RELDIR=$CLIENTDIR/releases
# Note that this scripts is modified an copied to $CLIENTDIR/bin/start
# in release_handler_SUITE:copy_client - therefore HEART_COMMAND is as follows:
HEART_COMMAND=$CLIENTDIR/bin/start
-HW_WD_DISABLE=true
-export HW_WD_DISABLE HEART_COMMAND
+export HEART_COMMAND
START_ERL_DATA=${1:-$RELDIR/start_erl.data}
diff --git a/lib/sasl/test/rh_test_lib.erl b/lib/sasl/test/rh_test_lib.erl
index 11935496d8..dacd8b6b9f 100644
--- a/lib/sasl/test/rh_test_lib.erl
+++ b/lib/sasl/test/rh_test_lib.erl
@@ -18,7 +18,7 @@ cmd(Cmd,Args,Env) ->
case open_port({spawn_executable, Cmd}, [{args,Args},{env,Env}]) of
Port when is_port(Port) ->
unlink(Port),
- erlang:port_close(Port),
+ catch erlang:port_close(Port), % migth already be closed, so catching
ok;
Error ->
Error
diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl
index bf95ceb70c..5e546d16a8 100644
--- a/lib/sasl/test/systools_SUITE.erl
+++ b/lib/sasl/test/systools_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2017. 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.
@@ -29,6 +29,8 @@
-module(systools_SUITE).
+-compile(export_all).
+
%%-define(debug, true).
-include_lib("common_test/include/ct.hrl").
@@ -39,31 +41,6 @@
-include_lib("kernel/include/file.hrl").
--export([all/0,suite/0,groups/0,init_per_group/2,end_per_group/2]).
-
--export([script_options/1, normal_script/1, unicode_script/1,
- unicode_script/2, no_mod_vsn_script/1,
- wildcard_script/1, variable_script/1, no_sasl_script/1,
- no_dot_erlang_script/1,
- abnormal_script/1, src_tests_script/1, crazy_script/1,
- included_script/1, included_override_script/1,
- included_fail_script/1, included_bug_script/1, exref_script/1,
- duplicate_modules_script/1,
- otp_3065_circular_dependenies/1, included_and_used_sort_script/1]).
--export([tar_options/1, normal_tar/1, no_mod_vsn_tar/1, system_files_tar/1,
- system_files_tar/2, invalid_system_files_tar/1,
- invalid_system_files_tar/2, variable_tar/1,
- src_tests_tar/1, var_tar/1, exref_tar/1, link_tar/1,
- otp_9507_path_ebin/1]).
--export([normal_relup/1, restart_relup/1, abnormal_relup/1, no_sasl_relup/1,
- no_appup_relup/1, bad_appup_relup/1, app_start_type_relup/1,
- regexp_relup/1]).
--export([normal_hybrid/1,hybrid_no_old_sasl/1,hybrid_no_new_sasl/1]).
--export([otp_6226_outdir/1]).
--export([init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, end_per_testcase/2]).
--export([delete_tree/1]).
-
-import(lists, [foldl/3]).
-define(default_timeout, ?t:minutes(20)).
@@ -91,7 +68,8 @@ groups() ->
{tar, [],
[tar_options, normal_tar, no_mod_vsn_tar, system_files_tar,
invalid_system_files_tar, variable_tar,
- src_tests_tar, var_tar, exref_tar, link_tar, otp_9507_path_ebin]},
+ src_tests_tar, var_tar, exref_tar, link_tar, no_sasl_tar,
+ otp_9507_path_ebin]},
{relup, [],
[normal_relup, restart_relup, abnormal_relup, no_sasl_relup,
no_appup_relup, bad_appup_relup, app_start_type_relup, regexp_relup
@@ -238,6 +216,7 @@ normal_script(Config) when is_list(Config) ->
%% Check the same but w. silent flag
{ok, _, []} = systools:make_script(LatestName, [silent]),
+ {ok, _, []} = systools:make_script(LatestName, [silent,warnings_as_errors]),
%% Use the local option
ok = systools:make_script(LatestName, [local]),
@@ -456,9 +435,16 @@ no_sasl_script(Config) when is_list(Config) ->
{ok, _ , [{warning,missing_sasl}]} =
systools:make_script(LatestName,[{path, P},silent]),
+ {error, systools_make, {warnings_treated_as_errors,[missing_sasl]}} =
+ systools:make_script(LatestName,[{path, P},silent,warnings_as_errors]),
+
{ok, _ , []} =
systools:make_script(LatestName,[{path, P},silent, no_warn_sasl]),
+ {ok, _ , []} =
+ systools:make_script(LatestName,[{path, P},silent, no_warn_sasl,
+ warnings_as_errors]),
+
ok = file:set_cwd(OldDir),
ok.
@@ -525,7 +511,9 @@ src_tests_script(Config) when is_list(Config) ->
ok = file:delete(BootFile),
false = filelib:is_regular(BootFile),
%% With warnings_as_errors and src_tests option, an error should be issued
- error =
+ {error, systools_make,
+ {warnings_treated_as_errors, [{obj_out_of_date,_},
+ {source_not_found,_}]}} =
systools:make_script(LatestName, [silent, {path, N}, src_tests,
warnings_as_errors]),
error =
@@ -745,7 +733,7 @@ exref_script(Config) when is_list(Config) ->
ok = file:set_cwd(LatestDir),
- {ok, _, _} = systools:make_script(LatestName, [{path,P}, silent]),
+ {ok, _, []} = systools:make_script(LatestName, [{path,P}, silent]),
%% Complete exref
{ok, _, W1} =
@@ -894,10 +882,10 @@ normal_tar(Config) when is_list(Config) ->
ok = file:set_cwd(LatestDir),
- {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]),
+ {ok, _, []} = systools:make_script(LatestName, [silent, {path, P}]),
ok = systools:make_tar(LatestName, [{path, P}]),
ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName),
- {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]),
+ {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent]),
ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName),
ok = file:set_cwd(OldDir),
@@ -918,10 +906,10 @@ no_mod_vsn_tar(Config) when is_list(Config) ->
ok = file:set_cwd(LatestDir),
- {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]),
+ {ok, _, []} = systools:make_script(LatestName, [silent, {path, P}]),
ok = systools:make_tar(LatestName, [{path, P}]),
ok = check_tar(fname([lib,'db-3.1',ebin,'db.app']), LatestName),
- {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]),
+ {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent]),
ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName),
ok = file:set_cwd(OldDir),
@@ -945,11 +933,11 @@ system_files_tar(Config) ->
ok = file:write_file("sys.config","[].\n"),
ok = file:write_file("relup","{\"LATEST\",[],[]}.\n"),
- {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]),
+ {ok, _, []} = systools:make_script(LatestName, [silent, {path, P}]),
ok = systools:make_tar(LatestName, [{path, P}]),
ok = check_tar(fname(["releases","LATEST","sys.config"]), LatestName),
ok = check_tar(fname(["releases","LATEST","relup"]), LatestName),
- {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]),
+ {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent]),
ok = check_tar(fname(["releases","LATEST","sys.config"]), LatestName),
ok = check_tar(fname(["releases","LATEST","relup"]), LatestName),
@@ -978,7 +966,7 @@ invalid_system_files_tar(Config) ->
ok = file:set_cwd(LatestDir),
- {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]),
+ {ok, _, []} = systools:make_script(LatestName, [silent, {path, P}]),
%% Add dummy relup and sys.config - faulty sys.config
ok = file:write_file("sys.config","[]\n"), %!!! syntax error - missing '.'
@@ -1036,7 +1024,7 @@ variable_tar(Config) when is_list(Config) ->
ok = file:set_cwd(LatestDir),
- {ok, _, _} = systools:make_script(LatestName,
+ {ok, _, []} = systools:make_script(LatestName,
[silent,
{path, P},
{variables,[{"TEST", LibDir}]}]),
@@ -1045,7 +1033,7 @@ variable_tar(Config) when is_list(Config) ->
{variables,[{"TEST", LibDir}]}]),
ok = check_var_tar("TEST", LatestName),
- {ok, _, _} = systools:make_tar(LatestName,
+ {ok, _, []} = systools:make_tar(LatestName,
[{path, P}, silent,
{variables,[{"TEST", LibDir}]}]),
ok = check_var_tar("TEST", LatestName),
@@ -1174,7 +1162,7 @@ var_tar(Config) when is_list(Config) ->
ok = file:set_cwd(LatestDir),
- {ok, _, _} = systools:make_script(LatestName,
+ {ok, _, []} = systools:make_script(LatestName,
[silent,
{path, P},
{variables,[{"TEST", LibDir}]}]),
@@ -1218,7 +1206,7 @@ exref_tar(Config) when is_list(Config) ->
ok = file:set_cwd(LatestDir),
- {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]),
+ {ok, _, []} = systools:make_script(LatestName, [silent, {path, P}]),
%% Complete exref
{ok, _, W1} =
@@ -1248,7 +1236,41 @@ exref_tar(Config) when is_list(Config) ->
ok = file:set_cwd(OldDir),
ok.
+%% make_tar: Create tar without sasl appl. Check warning.
+no_sasl_tar(Config) when is_list(Config) ->
+ {ok, OldDir} = file:get_cwd(),
+ {LatestDir, LatestName} = create_script(latest1_no_sasl,Config),
+
+ DataDir = filename:absname(?copydir),
+ LibDir = fname([DataDir, d_normal, lib]),
+ P = [fname([LibDir, '*', ebin]),
+ fname([DataDir, lib, kernel, ebin]),
+ fname([DataDir, lib, stdlib, ebin]),
+ fname([DataDir, lib, sasl, ebin])],
+
+ ok = file:set_cwd(LatestDir),
+
+ {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]),
+ ok = systools:make_tar(LatestName, [{path, P}]),
+ {ok, _, [{warning,missing_sasl}]} =
+ systools:make_tar(LatestName, [{path, P}, silent]),
+ {ok, _, []} =
+ systools:make_tar(LatestName, [{path, P}, silent, no_warn_sasl]),
+ {ok, _, []} =
+ systools:make_tar(LatestName, [{path, P}, silent, no_warn_sasl,
+ warnings_as_errors]),
+ TarFile = LatestName ++ ".tar.gz",
+ true = filelib:is_regular(TarFile),
+ ok = file:delete(TarFile),
+ {error, systools_make, {warnings_treated_as_errors,[missing_sasl]}} =
+ systools:make_tar(LatestName, [{path, P}, silent, warnings_as_errors]),
+ error =
+ systools:make_tar(LatestName, [{path, P}, warnings_as_errors]),
+ false = filelib:is_regular(TarFile),
+
+ ok = file:set_cwd(OldDir),
+ ok.
%% make_tar: OTP-9507 - make_tar failed when path given as just 'ebin'.
otp_9507_path_ebin(Config) when is_list(Config) ->
@@ -1268,7 +1290,7 @@ otp_9507_path_ebin(Config) when is_list(Config) ->
fname([DataDir, lib, kernel, ebin]),
fname([DataDir, lib, stdlib, ebin]),
fname([DataDir, lib, sasl, ebin])],
- {ok, _, _} = systools:make_script(RelName, [silent, {path, P1}]),
+ {ok, _, []} = systools:make_script(RelName, [silent, {path, P1}]),
ok = systools:make_tar(RelName, [{path, P1}]),
Content1 = tar_contents(RelName),
@@ -1309,7 +1331,7 @@ normal_relup(Config) when is_list(Config) ->
ok = systools:make_relup(LatestName, [LatestName1], [LatestName1],
[{path, P}]),
ok = check_relup([{db, "2.1"}], [{db, "1.0"}]),
- {ok, _, _, []} =
+ {ok, Relup, _, []} =
systools:make_relup(LatestName, [LatestName1], [LatestName1],
[{path, P}, silent]),
ok = check_relup([{db, "2.1"}], [{db, "1.0"}]),
@@ -1322,7 +1344,9 @@ normal_relup(Config) when is_list(Config) ->
error =
systools:make_relup(LatestName, [LatestName2], [LatestName1],
[{path, P}, warnings_as_errors]),
- error =
+ {error, systools_relup,
+ {warnings_treated_as_errors,[pre_R15_emulator_upgrade,
+ {erts_vsn_changed, _}]}} =
systools:make_relup(LatestName, [LatestName2], [LatestName1],
[{path, P}, silent, warnings_as_errors]),
@@ -1341,6 +1365,14 @@ normal_relup(Config) when is_list(Config) ->
%% relup file should exist now
true = filelib:is_regular("relup"),
+ %% file should not be written if noexec option is used.
+ %% delete before running tests.
+ ok = file:delete("relup"),
+ {ok,Relup,_,[]} =
+ systools:make_relup(LatestName, [LatestName1], [LatestName1],
+ [{path, P}, noexec]),
+ false = filelib:is_regular("relup"),
+
ok = file:set_cwd(OldDir),
ok.
diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
index fd0fc9b8b5..6aa662a743 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 3.0
+SASL_VSN = 3.0.3
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 0f5c35b300..f1919a6bb1 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -34,7 +34,55 @@
</header>
- <section><title>SNMP 5.2.3</title>
+ <section><title>SNMP 5.2.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The SNMP MIB compiler has been fixed to compile MIBS with
+ refinements on user types such as in RFC 4669
+ RADIUS-AUTH-SERVER-MIB.mib. Problem reported and
+ researched by Kenneth Lakin and Daniel Goertzen.</p>
+ <p>
+ See also: https://bugs.erlang.org/browse/ERL-325</p>
+ <p>
+ Own Id: OTP-14145 Aux Id: ERL-325 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SNMP 5.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct bugs when path to mib or idl spec files
+ contains UTF-8 characters. </p>
+ <p>
+ Own Id: OTP-13718 Aux Id: ERL-179 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Solves snmp config string handling as reported by ERL-164
+ and solved by PR-1100</p>
+ <p>
+ Own Id: OTP-13706</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SNMP 5.2.3</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl
index 94325a8ed6..fc5116dac9 100644
--- a/lib/snmp/src/agent/snmpa_conf.erl
+++ b/lib/snmp/src/agent/snmpa_conf.erl
@@ -154,7 +154,7 @@ do_write_agent_conf(Fd, {intAgentMaxPacketSize = Tag, Val} ) ->
do_write_agent_conf(Fd, {snmpEngineMaxMessageSize = Tag, Val} ) ->
io:format(Fd, "{~w, ~w}.~n", [Tag, Val]);
do_write_agent_conf(Fd, {snmpEngineID = Tag, Val} ) ->
- io:format(Fd, "{~w, \"~s\"}.~n", [Tag, Val]);
+ io:format(Fd, "{~w, ~p}.~n", [Tag, Val]);
do_write_agent_conf(_Fd, Crap) ->
error({bad_agent_config, Crap}).
@@ -758,9 +758,9 @@ do_write_usm_conf(
PrivP, PrivKeyC, OwnPrivKeyC,
Public, AuthKey, PrivKey}) ->
io:format(Fd, "{", []),
- io:format(Fd, "\"~s\", ", [EngineID]),
- io:format(Fd, "\"~s\", ", [UserName]),
- io:format(Fd, "\"~s\", ", [SecName]),
+ io:format(Fd, "~p, ", [EngineID]),
+ io:format(Fd, "~p, ", [UserName]),
+ io:format(Fd, "~p, ", [SecName]),
io:format(Fd, "~w, ", [Clone]),
io:format(Fd, "~w, ", [AuthP]),
do_write_usm2(Fd, AuthKeyC, ", "),
@@ -859,15 +859,15 @@ do_write_vacm_conf(
{vacmSecurityToGroup,
SecModel, SecName, GroupName}) ->
io:format(
- Fd, "{vacmSecurityToGroup, ~w, \"~s\", \"~s\"}.~n",
+ Fd, "{vacmSecurityToGroup, ~w, ~p, ~p}.~n",
[SecModel, SecName, GroupName]);
do_write_vacm_conf(
Fd,
{vacmAccess,
GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV}) ->
io:format(
- Fd, "{vacmAccess, \"~s\", \"~s\", ~w, ~w, ~w, "
- "\"~s\", \"~s\", \"~s\"}.~n",
+ Fd, "{vacmAccess, ~p, ~p, ~w, ~w, ~w, "
+ "~p, ~p, ~p}.~n",
[GroupName, Prefix, SecModel, SecLevel,
Match, RV, WV, NV]);
do_write_vacm_conf(
@@ -875,7 +875,7 @@ do_write_vacm_conf(
{vacmViewTreeFamily,
ViewIndex, ViewSubtree, ViewStatus, ViewMask}) ->
io:format(
- Fd, "{vacmViewTreeFamily, \"~s\", ~w, ~w, ~w}.~n",
+ Fd, "{vacmViewTreeFamily, ~p, ~w, ~w, ~w}.~n",
[ViewIndex, ViewSubtree, ViewStatus, ViewMask]);
do_write_vacm_conf(_Fd, Crap) ->
error({bad_vacm_config, Crap}).
diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src
index b593e9ea84..d4bf0de61a 100644
--- a/lib/snmp/src/app/snmp.app.src
+++ b/lib/snmp/src/app/snmp.app.src
@@ -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.
@@ -23,12 +23,12 @@
{vsn, "%VSN%"},
{modules, [
%% Compiler modules (not in the runtime part of the app)
-% snmpc,
-% snmpc_lib,
-% snmpc_mib_gram,
-% snmpc_mib_to_hrl,
-% snmpc_misc,
-% snmpc_tok,
+ snmpc,
+ snmpc_lib,
+ snmpc_mib_gram,
+ snmpc_mib_to_hrl,
+ snmpc_misc,
+ snmpc_tok,
%% Application modules
snmp,
diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src
index ca61782639..db09ec3dc5 100644
--- a/lib/snmp/src/app/snmp.appup.src
+++ b/lib/snmp/src/app/snmp.appup.src
@@ -8,6 +8,10 @@
%% {update, snmpa_local_db, soft, soft_purge, soft_purge, []}
%% {add_module, snmpm_net_if_mt}
[
+ {<<"5\\.2\\.4">>,
+ [{load_module, snmp, soft_purge, soft_purge, []},
+ {load_module, snmpc_lib, soft_purge, soft_purge, []},
+ {load_module, snmpc_mib_gram, soft_purge, soft_purge, []}]},
{<<"5\\..*">>, [{restart_application, snmp}]},
{<<"4\\..*">>, [{restart_application, snmp}]}
],
@@ -17,6 +21,10 @@
%% {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
[
+ {<<"5\\.2\\.4">>,
+ [{load_module, snmp, soft_purge, soft_purge, []},
+ {load_module, snmpc_lib, soft_purge, soft_purge, []},
+ {load_module, snmpc_mib_gram, soft_purge, soft_purge, []}]},
{<<"5\\..*">>, [{restart_application, snmp}]},
{<<"4\\..*">>, [{restart_application, snmp}]}
]
diff --git a/lib/snmp/src/app/snmp.erl b/lib/snmp/src/app/snmp.erl
index df3933ea01..8a736f688b 100644
--- a/lib/snmp/src/app/snmp.erl
+++ b/lib/snmp/src/app/snmp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -573,9 +573,16 @@ print_mod_info(Prefix, {Module, Info}) ->
CompDate =
case key1search(compile_time, Info) of
{value, {Year, Month, Day, Hour, Min, Sec}} ->
- lists:flatten(
- io_lib:format("~w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",
- [Year, Month, Day, Hour, Min, Sec]));
+ io_lib:format(
+ "~w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",
+ [Year, Month, Day, Hour, Min, Sec]);
+ _ ->
+ "Not found"
+ end,
+ Digest =
+ case key1search(md5, Info) of
+ {value, MD5} when is_binary(MD5) ->
+ [io_lib:format("~2.16.0b", [Byte]) || <<Byte>> <= MD5];
_ ->
"Not found"
end,
@@ -583,12 +590,14 @@ print_mod_info(Prefix, {Module, Info}) ->
"~s Vsn: ~s~n"
"~s App vsn: ~s~n"
"~s Compiler ver: ~s~n"
- "~s Compile time: ~s~n",
+ "~s Compile time: ~s~n"
+ "~s MD5 digest: ~s~n",
[Prefix, Module,
Prefix, Vsn,
Prefix, AppVsn,
- Prefix, CompVer,
- Prefix, CompDate]),
+ Prefix, CompVer,
+ Prefix, CompDate,
+ Prefix, Digest]),
ok.
key1search(Key, Vals) ->
@@ -617,7 +626,7 @@ versions1() ->
Error ->
Error
end.
-
+
versions2() ->
case ms2() of
{ok, Mods} ->
@@ -625,25 +634,56 @@ versions2() ->
Error ->
Error
end.
-
+
version_info(Mods) ->
SysInfo = sys_info(),
OsInfo = os_info(),
ModInfo = [mod_version_info(Mod) || Mod <- Mods],
[{sys_info, SysInfo}, {os_info, OsInfo}, {mod_info, ModInfo}].
-
+
mod_version_info(Mod) ->
Info = Mod:module_info(),
- {value, {attributes, Attr}} = lists:keysearch(attributes, 1, Info),
- {value, {vsn, [Vsn]}} = lists:keysearch(vsn, 1, Attr),
- {value, {app_vsn, AppVsn}} = lists:keysearch(app_vsn, 1, Attr),
- {value, {compile, Comp}} = lists:keysearch(compile, 1, Info),
- {value, {version, Ver}} = lists:keysearch(version, 1, Comp),
- {value, {time, Time}} = lists:keysearch(time, 1, Comp),
- {Mod, [{vsn, Vsn},
- {app_vsn, AppVsn},
- {compiler_version, Ver},
- {compile_time, Time}]}.
+ {Mod,
+ case key1search(attributes, Info) of
+ {value, Attr} ->
+ case key1search(vsn, Attr) of
+ {value, [Vsn]} ->
+ [{vsn, Vsn}];
+ not_found ->
+ []
+ end ++
+ case key1search(app_vsn, Attr) of
+ {value, AppVsn} ->
+ [{app_vsn, AppVsn}];
+ not_found ->
+ []
+ end;
+ not_found ->
+ []
+ end ++
+ case key1search(compile, Info) of
+ {value, Comp} ->
+ case key1search(version, Comp) of
+ {value, Ver} ->
+ [{compiler_version, Ver}];
+ not_found ->
+ []
+ end ++
+ case key1search(time, Comp) of
+ {value, Ver} ->
+ [{compile_time, Ver}];
+ not_found ->
+ []
+ end;
+ not_found ->
+ []
+ end ++
+ case key1search(md5, Info) of
+ {value, Bin} ->
+ [{md5, Bin}];
+ not_found ->
+ []
+ end}.
sys_info() ->
SysArch = string:strip(erlang:system_info(system_architecture),right,$\n),
diff --git a/lib/snmp/src/compile/snmpc.erl b/lib/snmp/src/compile/snmpc.erl
index db1f9ee61b..416353508e 100644
--- a/lib/snmp/src/compile/snmpc.erl
+++ b/lib/snmp/src/compile/snmpc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -64,7 +64,7 @@ compile(Input, _Output, Options) ->
{ok, _} ->
ok;
{error, Reason} ->
- io:format("~p", [Reason]),
+ io:format("~tp", [Reason]),
error
end.
@@ -126,7 +126,14 @@ compile(FileName) ->
%%----------------------------------------------------------------------
compile(FileName, Options) when is_list(FileName) ->
- true = snmpc_misc:is_string(FileName),
+ case snmpc_misc:check_file(FileName) of
+ true ->
+ compile_1(FileName, Options);
+ false ->
+ {error, {invalid_file, FileName}}
+ end.
+
+compile_1(FileName, Options) ->
DefOpts = [{deprecated, true},
{group_check, true},
{i, ["./"]},
diff --git a/lib/snmp/src/compile/snmpc_lib.erl b/lib/snmp/src/compile/snmpc_lib.erl
index 51690b6e7e..33ddd78308 100644
--- a/lib/snmp/src/compile/snmpc_lib.erl
+++ b/lib/snmp/src/compile/snmpc_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. 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.
@@ -99,7 +99,7 @@ make_ASN1type({{type_with_size,Type,{range,Lo,Hi}},Line}) ->
print_error("Undefined type '~w'",[Type],Line),
guess_string_type()
end;
-make_ASN1type({{integer_with_enum,Type,Enums},Line}) ->
+make_ASN1type({{type_with_enum,Type,Enums},Line}) ->
case lookup_vartype(Type) of
{value,ASN1type} -> ASN1type#asn1_type{assocList = [{enums, Enums}]};
false ->
diff --git a/lib/snmp/src/compile/snmpc_mib_gram.yrl b/lib/snmp/src/compile/snmpc_mib_gram.yrl
index 743c3a6550..14a668127e 100644
--- a/lib/snmp/src/compile/snmpc_mib_gram.yrl
+++ b/lib/snmp/src/compile/snmpc_mib_gram.yrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -387,10 +387,12 @@ syntax -> type : {{type, cat('$1')},line_of('$1')}.
syntax -> type size : {{type_with_size, cat('$1'), '$2'},line_of('$1')}.
syntax -> usertype size : {{type_with_size,val('$1'), '$2'},line_of('$1')}.
syntax -> 'INTEGER' '{' namedbits '}' :
- {{integer_with_enum, 'INTEGER', '$3'}, line_of('$1')}.
+ {{type_with_enum, 'INTEGER', '$3'}, line_of('$1')}.
syntax -> 'BITS' '{' namedbits '}' :
ensure_ver(2,'$1'),
{{bits, '$3'}, line_of('$1')}.
+syntax -> usertype '{' namedbits '}' :
+ {{type_with_enum, 'INTEGER', '$3'}, line_of('$1')}.
syntax -> 'SEQUENCE' 'OF' usertype :
{{sequence_of,val('$3')},line_of('$1')}.
diff --git a/lib/snmp/src/compile/snmpc_misc.erl b/lib/snmp/src/compile/snmpc_misc.erl
index 933d629746..312074f2e7 100644
--- a/lib/snmp/src/compile/snmpc_misc.erl
+++ b/lib/snmp/src/compile/snmpc_misc.erl
@@ -29,7 +29,7 @@
bits_to_int/2,
ensure_trailing_dir_delimiter/1,
foreach/3,
- is_string/1,
+ check_file/1,
read_mib/1,
read_noexit/2,
strip_extension_from_filename/2,
@@ -86,21 +86,21 @@ to_upper([C|Cs]) -> [C|to_upper(Cs)];
to_upper([]) -> [].
-is_string([]) -> true;
-is_string([Tkn | Str])
- when is_integer(Tkn) andalso (Tkn >= 0) andalso (Tkn =< 255) ->
- is_string(Str);
-is_string(_) -> false.
-
-
+check_file(FileName) ->
+ case filename:extension(FileName) of
+ ".mib" ->
+ filelib:is_regular(FileName);
+ _ ->
+ filelib:is_regular(FileName ++ ".mib")
+ end.
+
+
foreach(Function, ExtraArgs, [H | T]) ->
apply(Function, [H | ExtraArgs]),
foreach(Function, ExtraArgs, T);
foreach(_Function, _ExtraArgs, []) ->
true.
-
-
%%----------------------------------------------------------------------
%% Returns: {ok, Mib}|{error, Reason}
%% The reason for having the function if this module is:
diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk
index 87539f88f7..0f54e67c65 100644
--- a/lib/snmp/test/modules.mk
+++ b/lib/snmp/test/modules.mk
@@ -31,6 +31,7 @@ SUITE_MODULES = \
snmp_agent_mibs_test \
snmp_agent_nfilter_test \
snmp_agent_test \
+ snmp_agent_conf_test \
snmp_agent_test_lib \
snmp_manager_config_test \
snmp_manager_user \
diff --git a/lib/snmp/test/snmp_SUITE.erl b/lib/snmp/test/snmp_SUITE.erl
index 3b9219739b..05bd86253b 100644
--- a/lib/snmp/test/snmp_SUITE.erl
+++ b/lib/snmp/test/snmp_SUITE.erl
@@ -81,7 +81,8 @@ groups() ->
{group, note_store_test}]},
{agent, [], [{group, mibs_test},
{group, nfilter_test},
- {group, agent_test},
+ {group, agent_test},
+ {group, agent_conf_test},
{group, snmpnet_test}]},
{manager, [], [{group, manager_config_test},
{group, manager_user_test},
@@ -97,6 +98,7 @@ groups() ->
{mibs_test, [], [{snmp_agent_mibs_test, all}]},
{nfilter_test, [], [{snmp_agent_nfilter_test, all}]},
{agent_test, [], [{snmp_agent_test, all}]},
+ {agent_conf_test, [], [{snmp_agent_conf_test, all}]},
{snmpnet_test, [], [{snmp_to_snmpnet_SUITE, all}]},
{manager_config_test, [], [{snmp_manager_config_test, all}]},
{manager_user_test, [], [{snmp_manager_user_test, all}]},
diff --git a/lib/snmp/test/snmp_agent_conf_test.erl b/lib/snmp/test/snmp_agent_conf_test.erl
new file mode 100644
index 0000000000..0a22bd47d1
--- /dev/null
+++ b/lib/snmp/test/snmp_agent_conf_test.erl
@@ -0,0 +1,210 @@
+%%
+%% %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(snmp_agent_conf_test).
+
+%%----------------------------------------------------------------------
+%% Include files
+%%----------------------------------------------------------------------
+
+%-include_lib("test_server/include/test_server.hrl").
+%-include("snmp_test_lib.hrl").
+-include_lib("common_test/include/ct.hrl").
+
+-export([
+ all/0,
+ groups/0,
+ init_per_suite/1,
+ end_per_suite/1,
+
+ check_agent/1,
+ check_usm/1,
+ check_vacm/1
+ ]).
+
+
+all() -> [
+ check_agent,
+ check_usm,
+ check_vacm
+ ].
+
+
+groups() ->
+ [].
+
+
+init_per_suite(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ PrivSubdir = filename:join(PrivDir, "snmp_agent_conf_test"),
+ ok = filelib:ensure_dir(filename:join(PrivSubdir, "dummy")),
+ [{priv_subdir, PrivSubdir} | Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+%%======================================================================
+%% Test data
+%%======================================================================
+
+engine_ids() -> [
+ "plain eid",
+ "here\"eid",
+ "comes\neid",
+ "trouble\0eid",
+ binary_to_list(<<"中国引擎标识符"/utf8>>)
+].
+
+snmp_admin_strings() -> [
+ "plain string",
+ "heres\"eid",
+ "trouble\neid",
+ binary_to_list(<<"中国引擎标识符"/utf8>>)
+].
+
+
+%%======================================================================
+%% Test functions
+%%======================================================================
+
+
+check_agent(Config) ->
+ Dir = ?config(priv_subdir, Config),
+ lists:foreach(
+ fun(EngineId) -> check_agent_by_engineid(Dir, EngineId) end,
+ engine_ids()
+ ),
+ ok.
+
+check_agent_by_engineid(Dir, EngineId) ->
+ WEntries = [
+ snmpa_conf:agent_entry(intAgentIpAddress, {0,0,0,0}),
+ snmpa_conf:agent_entry(intAgentUDPPort, 161),
+ snmpa_conf:agent_entry(snmpEngineMaxMessageSize, 484),
+ snmpa_conf:agent_entry(snmpEngineID, EngineId)
+ ],
+
+ ok = snmpa_conf:write_agent_config(Dir, WEntries),
+ {ok, REntries} = snmpa_conf:read_agent_config(Dir),
+
+ true = is_subset(WEntries, REntries),
+ ok.
+
+%%======================================================================
+
+check_usm(Config) ->
+ Dir = ?config(priv_subdir, Config),
+ EngineId = hd(engine_ids()),
+ UserName = hd(snmp_admin_strings()),
+ SecName = hd(snmp_admin_strings()),
+
+ %% vary engine id
+ lists:foreach(
+ fun(EngineId_) -> check_usm_by_params(Dir, EngineId_, UserName, SecName) end,
+ engine_ids()
+ ),
+
+ %% vary user name
+ lists:foreach(
+ fun(UserName_) -> check_usm_by_params(Dir, EngineId, UserName_, SecName) end,
+ snmp_admin_strings()
+ ),
+
+ %% vary sec name
+ lists:foreach(
+ fun(SecName_) -> check_usm_by_params(Dir, EngineId, UserName, SecName_) end,
+ snmp_admin_strings()
+ ),
+
+ ok.
+
+check_usm_by_params(Dir, EngineId, UserName, SecName) ->
+ WEntries = [
+ snmpa_conf:usm_entry(
+ EngineId,
+ UserName,
+ SecName,
+ zeroDotZero,
+ usmNoAuthProtocol, % authproto
+ "", "",
+ usmNoPrivProtocol, % privproto
+ "", "", "",
+ [], %AuthKey
+ []) %PrivKey
+ ],
+
+ ok = snmpa_conf:write_usm_config(Dir, WEntries),
+ {ok, REntries} = snmpa_conf:read_usm_config(Dir),
+
+ true = is_subset(WEntries, REntries),
+ ok.
+
+%%======================================================================
+
+check_vacm(Config) ->
+ Dir = ?config(priv_subdir, Config),
+
+ %% vary sec name
+ lists:foreach(
+ fun(SecName_) -> check_vacm_by_params(Dir, SecName_) end,
+ snmp_admin_strings()
+ ),
+
+ ok.
+
+
+check_vacm_by_params(Dir, SecName) ->
+ WEntries = [
+ %% SecModel, SecName, GroupName
+ snmpa_conf:vacm_s2g_entry(usm, SecName, SecName),
+ %% GroupName,Prefix,SecModel,SecLevel,Match,ReadView,WriteView,NotifyView
+ snmpa_conf:vacm_acc_entry(SecName, "", any, noAuthNoPriv, exact, "all", "all", "all")
+ ],
+
+ ok = snmpa_conf:write_vacm_config(Dir, WEntries),
+ {ok, REntries} = snmpa_conf:read_vacm_config(Dir),
+
+ true = is_subset(WEntries, REntries),
+ ok.
+
+
+
+%%======================================================================
+
+
+%% additional tests needed:
+% check_context()
+% check_community()
+% check_standard()
+% check_target_addr()
+% check_target_params()
+% check_notify()
+
+
+%%======================================================================
+%% Local utility functions
+%%======================================================================
+
+is_subset(List1, List2) ->
+ io:format("Check ~p is subset of ~p\n", [List1, List2]),
+ sets:is_subset(
+ sets:from_list(List1),
+ sets:from_list(List2)
+ ).
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index 8ae495bb1b..3c1a6f2afd 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -647,22 +647,22 @@ init_per_group(GroupName, Config) ->
snmp_test_lib:init_group_top_dir(GroupName, Config).
init_per_group_ipv6(GroupName, Config, Init) ->
+ {ok, Hostname0} = inet:gethostname(),
case ct:require(ipv6_hosts) of
ok ->
- case gen_udp:open(0, [inet6]) of
- {ok, S} ->
- ok = gen_udp:close(S),
- Init(
- snmp_test_lib:init_group_top_dir(
- GroupName,
- [{ipfamily, inet6},
- {ip, ?LOCALHOST(inet6)}
- | lists:keydelete(ip, 1, Config)]));
- {error, _} ->
- {skip, "Host seems to not support IPv6"}
- end;
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
+ Init(
+ snmp_test_lib:init_group_top_dir(
+ GroupName,
+ [{ipfamily, inet6},
+ {ip, ?LOCALHOST(inet6)}
+ | lists:keydelete(ip, 1, Config)]));
+ false ->
+ {skip, "Host does not support IPV6"}
+ end;
_ ->
- {skip, "Host does not support IPV6"}
+ {skip, "Test config ipv6_hosts is missing"}
end.
end_per_group(all_tcs, Config) ->
diff --git a/lib/snmp/test/snmp_app_test.erl b/lib/snmp/test/snmp_app_test.erl
index 6e7e85d3b4..5e69866f9a 100644
--- a/lib/snmp/test/snmp_app_test.erl
+++ b/lib/snmp/test/snmp_app_test.erl
@@ -23,366 +23,29 @@
%%----------------------------------------------------------------------
-module(snmp_app_test).
--export([
- all/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,
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
- fields/1,
- modules/1,
- exportall/1,
- app_depend/1,
-
- start_and_stop_empty/1,
- start_and_stop_with_agent/1,
- start_and_stop_with_manager/1,
- start_and_stop_with_agent_and_manager/1,
- start_epmty_and_then_agent_and_manager_and_stop/1,
- start_with_agent_and_then_manager_and_stop/1,
- start_with_manager_and_then_agent_and_stop/1
- ]).
-
-
--include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
--include("snmp_test_lib.hrl").
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
all() ->
- Cases =
- [
- fields,
- modules,
- exportall,
- app_depend,
- {group, start_and_stop}
- ],
- Cases.
-
-groups() ->
- [{start_and_stop, [],
- [start_and_stop_empty,
- start_and_stop_with_agent,
- start_and_stop_with_manager,
- start_and_stop_with_agent_and_manager,
- start_epmty_and_then_agent_and_manager_and_stop,
- start_with_agent_and_then_manager_and_stop,
- start_with_manager_and_then_agent_and_stop]}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_suite(Config) when is_list(Config) ->
- ?DISPLAY_SUITE_INFO(),
-
- %% Note that part of this stuff (the suite top dir creation)
- %% may already be done (if we run the entire snmp suite).
-
- PrivDir = ?config(priv_dir, Config),
- TopDir = filename:join(PrivDir, app),
- case file:make_dir(TopDir) of
- ok ->
- ok;
- {error, eexist} ->
- ok;
- Error ->
- fail({failed_creating_subsuite_top_dir, Error})
- end,
- AppFile =
- case is_app() of
- {ok, File} ->
- io:format("File: ~n~p~n", [File]),
- snmp:print_version_info(),
- File;
- {error, Reason} ->
- fail(Reason)
- end,
- [{app_topdir, TopDir}, {app_file, AppFile} | Config].
-
-
-is_app() ->
- is_app(?APPLICATION).
-
-is_app(App) ->
- LibDir = code:lib_dir(App),
- File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]),
- case file:consult(File) of
- {ok, [{application, App, AppFile}]} ->
- {ok, AppFile};
- Error ->
- {error, {invalid_format, Error}}
- end.
-
-end_per_suite(suite) -> [];
-end_per_suite(doc) -> [];
-end_per_suite(Config) when is_list(Config) ->
- Config.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% Test server callbacks
-init_per_testcase(_Case, Config) ->
- Config.
-
-end_per_testcase(_Case, Config) ->
- Config.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-fields(suite) ->
- [];
-fields(doc) ->
- [];
-fields(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Fields = [vsn, description, modules, registered, applications],
- case check_fields(Fields, AppFile, []) of
- [] ->
- ok;
- Missing ->
- fail({missing_fields, Missing})
- end.
-
-check_fields([], _AppFile, Missing) ->
- Missing;
-check_fields([Field|Fields], AppFile, Missing) ->
- check_fields(Fields, AppFile, check_field(Field, AppFile, Missing)).
-
-check_field(Name, AppFile, Missing) ->
- io:format("checking field: ~p~n", [Name]),
- case lists:keymember(Name, 1, AppFile) of
- true ->
- Missing;
- false ->
- [Name|Missing]
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-modules(suite) ->
- [];
-modules(doc) ->
- [];
-modules(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Mods = key1search(modules, AppFile),
- EbinList = get_ebin_mods(snmp),
- case missing_modules(Mods, EbinList, []) of
- [] ->
- ok;
- Missing ->
- fail({missing_modules, Missing})
- end,
- Allowed = [snmpc,
- snmpc_lib,
- snmpc_misc,
- snmpc_mib_gram,
- snmpc_mib_to_hrl,
- snmpc_tok],
- case extra_modules(Mods, EbinList, Allowed, []) of
- [] ->
- ok;
- Extra ->
- fail({extra_modules, Extra})
- end,
- {ok, Mods}.
-
-get_ebin_mods(App) ->
- LibDir = code:lib_dir(App),
- EbinDir = filename:join([LibDir,"ebin"]),
- {ok, Files0} = file:list_dir(EbinDir),
- Files1 = [lists:reverse(File) || File <- Files0],
- [list_to_atom(lists:reverse(Name)) || [$m,$a,$e,$b,$.|Name] <- Files1].
-
-
-missing_modules([], _Ebins, Missing) ->
- Missing;
-missing_modules([Mod|Mods], Ebins, Missing) ->
- case lists:member(Mod, Ebins) of
- true ->
- missing_modules(Mods, Ebins, Missing);
- false ->
- io:format("missing module: ~p~n", [Mod]),
- missing_modules(Mods, Ebins, [Mod|Missing])
- end.
-
-
-extra_modules(_Mods, [], Allowed, Extra) ->
- Extra--Allowed;
-extra_modules(Mods, [Mod|Ebins], Allowed, Extra) ->
- case lists:member(Mod, Mods) of
- true ->
- extra_modules(Mods, Ebins, Allowed, Extra);
- false ->
- io:format("superfluous module: ~p~n", [Mod]),
- extra_modules(Mods, Ebins, Allowed, [Mod|Extra])
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-exportall(suite) ->
- [];
-exportall(doc) ->
- [];
-exportall(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Mods = key1search(modules, AppFile),
- check_export_all(Mods).
-
-
-check_export_all([]) ->
- ok;
-check_export_all([Mod|Mods]) ->
- case (catch apply(Mod, module_info, [compile])) of
- {'EXIT', {undef, _}} ->
- check_export_all(Mods);
- O ->
- case lists:keysearch(options, 1, O) of
- false ->
- check_export_all(Mods);
- {value, {options, List}} ->
- case lists:member(export_all, List) of
- true ->
- fail({export_all, Mod});
- false ->
- check_export_all(Mods)
- end
- end
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-app_depend(suite) ->
- [];
-app_depend(doc) ->
- [];
-app_depend(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Apps = key1search(applications, AppFile),
- check_apps(Apps).
-
-
-check_apps([]) ->
- ok;
-check_apps([App|Apps]) ->
- case is_app(App) of
- {ok, _} ->
- check_apps(Apps);
- Error ->
- throw({error, {missing_app, {App, Error}}})
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_and_stop_empty(suite) ->
- [];
-start_and_stop_empty(doc) ->
- ["Start and stop the application empty (no configured components)"];
-start_and_stop_empty(Config) when is_list(Config) ->
- ?line false = ?IS_SNMP_RUNNING(),
-
- ?line ok = snmp:start(),
-
- ?line true = ?IS_SNMP_RUNNING(),
-
- ?line ok = snmp:stop(),
-
- ?line false = ?IS_SNMP_RUNNING(),
-
- ok.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_and_stop_with_agent(suite) ->
- [];
-start_and_stop_with_agent(doc) ->
- ["Start and stop the application with the agent pre-configured"];
-start_and_stop_with_agent(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_and_stop_with_manager(suite) ->
- [];
-start_and_stop_with_manager(doc) ->
- ["Start and stop the application with the manager pre-configured"];
-start_and_stop_with_manager(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_and_stop_with_agent_and_manager(suite) ->
- [];
-start_and_stop_with_agent_and_manager(doc) ->
- ["Start and stop the application with both the agent "
- "and the manager pre-configured"];
-start_and_stop_with_agent_and_manager(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_epmty_and_then_agent_and_manager_and_stop(suite) ->
- [];
-start_epmty_and_then_agent_and_manager_and_stop(doc) ->
- ["Start the application empty, then start the agent and then "
- "the manager and then stop the application"];
-start_epmty_and_then_agent_and_manager_and_stop(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_with_agent_and_then_manager_and_stop(suite) ->
- [];
-start_with_agent_and_then_manager_and_stop(doc) ->
- ["Start the application with the agent pre-configured, "
- "then start the manager and then stop the application"];
-start_with_agent_and_then_manager_and_stop(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-start_with_manager_and_then_agent_and_stop(suite) ->
- [];
-start_with_manager_and_then_agent_and_stop(doc) ->
- ["Start the application with the manager pre-configured, "
- "then start the agent and then stop the application"];
-start_with_manager_and_then_agent_and_stop(Config) when is_list(Config) ->
- ?SKIP(not_implemented_yet).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-fail(Reason) ->
- exit({suite_failed, Reason}).
-
-key1search(Key, L) ->
- case lists:keysearch(Key, 1, L) of
- undefined ->
- fail({not_found, Key, L});
- {value, {Key, Value}} ->
- Value
- end.
+ [
+ app,
+ appup
+ ].
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+app() ->
+ [{doc, "Test that the snmp app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = test_server:app_test(snmp).
+%%--------------------------------------------------------------------
+appup() ->
+ [{doc, "Test that the snmp appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = test_server:appup_test(snmp).
diff --git a/lib/snmp/test/snmp_compiler_test.erl b/lib/snmp/test/snmp_compiler_test.erl
index 2c8851c2a7..9b3c2bfd2c 100644
--- a/lib/snmp/test/snmp_compiler_test.erl
+++ b/lib/snmp/test/snmp_compiler_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2017. 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.
@@ -56,7 +56,8 @@
otp_8574/1,
otp_8595/1,
otp_10799/1,
- otp_10808/1
+ otp_10808/1,
+ otp_14145/1
]).
@@ -135,7 +136,8 @@ all() ->
].
groups() ->
- [{tickets, [], [otp_6150, otp_8574, otp_8595, otp_10799, otp_10808]}].
+ [{tickets, [],
+ [otp_6150, otp_8574, otp_8595, otp_10799, otp_10808, otp_14145]}].
init_per_group(_GroupName, Config) ->
Config.
@@ -431,6 +433,30 @@ otp_10808(Config) when is_list(Config) ->
%%======================================================================
+otp_14145(suite) ->
+ [];
+otp_14145(Config) when is_list(Config) ->
+ put(tname, otp10808),
+ p("starting with Config: ~p~n", [Config]),
+
+ Dir = ?config(case_top_dir, Config),
+ MibDir = ?config(mib_dir, Config),
+ MibName = "OTP14145-MIB",
+ MibFile = join(MibDir, MibName++".mib"),
+ ?line {ok, MibBin} =
+ snmpc:compile(MibFile, [{outdir, Dir},
+ {verbosity, trace},
+ {group_check, false},
+ module_compliance]),
+ p("Mib: ~n~p~n", [MibBin]),
+ MIB = read_mib(MibBin),
+ Oid = [1,3,6,1,2,1,67,4],
+ check_mib(MIB#mib.mes, Oid, undefined),
+ ok.
+
+
+%%======================================================================
+
augments_extra_info(suite) ->
[];
augments_extra_info(Config) when is_list(Config) ->
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index d17882e765..71f4017d8b 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -583,38 +583,38 @@ init_per_group(event_tests_mt = GroupName, Config) ->
GroupName,
[{manager_net_if_module, snmpm_net_if_mt} | Config]);
init_per_group(ipv6_mt = GroupName, Config) ->
+ {ok, Hostname0} = inet:gethostname(),
case ct:require(ipv6_hosts) of
ok ->
- case gen_udp:open(0, [inet6]) of
- {ok, S} ->
- ok = gen_udp:close(S),
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
ipv6_init(
snmp_test_lib:init_group_top_dir(
GroupName,
[{manager_net_if_module, snmpm_net_if_mt}
| Config]));
- {error, _} ->
- {skip, "Host seems to not support IPv6"}
+ false ->
+ {skip, "Host does not support IPv6"}
end;
_ ->
- {skip, "Host does not support IPV6"}
+ {skip, "Test config ipv6_hosts is missing"}
end;
init_per_group(ipv6 = GroupName, Config) ->
+ {ok, Hostname0} = inet:gethostname(),
case ct:require(ipv6_hosts) of
ok ->
- case gen_udp:open(0, [inet6]) of
- {ok, S} ->
- ok = gen_udp:close(S),
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config));
- {error, _} ->
- {skip, "Host seems to not support IPv6"}
+ false ->
+ {skip, "Host does not support IPv6"}
end;
_ ->
- {skip, "Host does not support IPV6"}
+ {skip, "Test config ipv6_hosts is missing"}
end;
init_per_group(GroupName, Config) ->
snmp_test_lib:init_group_top_dir(GroupName, Config).
-
+
end_per_group(_GroupName, Config) ->
%% Do we really need to do this?
lists:keydelete(snmp_group_top_dir, 1, Config).
diff --git a/lib/snmp/test/snmp_test_data/OTP14145-MIB.mib b/lib/snmp/test/snmp_test_data/OTP14145-MIB.mib
new file mode 100644
index 0000000000..f29c65c4c2
--- /dev/null
+++ b/lib/snmp/test/snmp_test_data/OTP14145-MIB.mib
@@ -0,0 +1,44 @@
+OTP14145-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY, OBJECT-TYPE,
+ mib-2 FROM SNMPv2-SMI
+ InetAddressType, InetAddress FROM INET-ADDRESS-MIB
+ MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF;
+
+testMibId MODULE-IDENTITY
+ LAST-UPDATED "200608210000Z" -- 21 August 2006
+ ORGANIZATION "a"
+ CONTACT-INFO "a"
+ DESCRIPTION "a"
+ REVISION "200608210000Z" -- 21 August 2006
+ DESCRIPTION "a"
+ ::= { mib-2 67 }
+
+testObj OBJECT-TYPE
+ SYNTAX InetAddressType
+ -- SYNTAX InetAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION "a"
+ ::= { testMibId 2 }
+
+testObjId OBJECT IDENTIFIER ::= { testMibId 3 }
+
+testMibCompliance MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION "a"
+ MODULE
+ OBJECT testObj
+ SYNTAX InetAddressType { ipv4(1), ipv6(2) }
+ -- SYNTAX InetAddress ( SIZE(4|16) )
+ DESCRIPTION "a"
+ ::= { testMibId 4 }
+
+testObjGroup OBJECT-GROUP
+ OBJECTS { testObj }
+ STATUS current
+ DESCRIPTION "a"
+ ::= { testObjId 1 }
+
+END
diff --git a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
index ac9e37bc8b..24c14d86ea 100644
--- a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
+++ b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
@@ -121,14 +121,14 @@ init_per_group(_, Config) ->
Config.
init_per_group_ipv6(Families, Config) ->
+ {ok, Hostname0} = inet:gethostname(),
case ct:require(ipv6_hosts) of
ok ->
- case gen_udp:open(0, [inet6]) of
- {ok, S} ->
- ok = gen_udp:close(S),
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
init_per_group_ip(Families, Config);
- {error, _} ->
- {skip, "Host seems to not support IPv6"}
+ false ->
+ {skip, "Host does not support IPv6"}
end;
_ ->
{skip, "Test config ipv6_hosts is missing"}
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index f95b428290..30b8ee1124 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2016. All Rights Reserved.
+# Copyright Ericsson AB 1997-2017. 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,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 5.2.3
+SNMP_VSN = 5.2.5
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/introduction.xml b/lib/ssh/doc/src/introduction.xml
index ca84528f3d..b7a73e2597 100644
--- a/lib/ssh/doc/src/introduction.xml
+++ b/lib/ssh/doc/src/introduction.xml
@@ -195,8 +195,6 @@
Transport Layer Protocol</item>
<item><url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> -
Connection Protocol</item>
- <item><url href="http://www.ietf.org/rfc/rfc4255.txt">RFC 4255</url> -
- Key Fingerprints</item>
<item><url href="http://www.ietf.org/rfc/rfc4344.txt">RFC 4344</url> -
Transport Layer Encryption Modes</item>
<item><url href="http://www.ietf.org/rfc/rfc4716.txt">RFC 4716</url> -
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index fd15c334a3..6e98cab129 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,377 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.4.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix rare spurios shutdowns of ssh servers when receiveing
+ <c>{'EXIT',_,normal}</c> messages.</p>
+ <p>
+ Own Id: OTP-15018</p>
+ </item>
+ <item>
+ <p>
+ Host key hash erroneously calculated for clients
+ following draft-00 of RFC 4419, for example PuTTY</p>
+ <p>
+ Own Id: OTP-15064</p>
+ </item>
+ <item>
+ <p>
+ Renegotiation could fail in some states</p>
+ <p>
+ Own Id: OTP-15066</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.4.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ An ssh_sftp server (running version 6) could fail if it
+ is told to remove a file which in fact is a directory.</p>
+ <p>
+ Own Id: OTP-15004</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.4.2.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Default exec is disabled when a user-defined shell is
+ enabled.</p>
+ <p>
+ Own Id: OTP-14881</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.4.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Trailing white space was removed at end of the
+ hello-string. This caused interoperability problems with
+ some other ssh-implementations (e.g OpenSSH 7.3p1 on
+ Solaris 11)</p>
+ <p>
+ Own Id: OTP-14763 Aux Id: ERIERL-74 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ ssh:daemon_info/1 crashed if the listening IP was not
+ 'any'</p>
+ <p>
+ Own Id: OTP-14298 Aux Id: seq13294 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug when opening connections. If the tcp setup
+ failed, that would in some cases not result in an error
+ return value.</p>
+ <p>
+ Own Id: OTP-14108</p>
+ </item>
+ <item>
+ <p>
+ Reduce information leakage in case of decryption errors.</p>
+ <p>
+ Own Id: OTP-14109</p>
+ </item>
+ <item>
+ <p>
+ The key exchange algorithm
+ diffie-hellman-group-exchange-sha* has a server-option
+ <c>{dh_gex_limits,{Min,Max}}</c>. There was a hostkey
+ signature validation error on the client side if the
+ option was used and the <c>Min</c> or the <c>Max</c>
+ differed from the corresponding values obtained from the
+ client.</p>
+ <p>
+ This bug is now corrected.</p>
+ <p>
+ Own Id: OTP-14166</p>
+ </item>
+ <item>
+ <p>
+ The sftpd server now correctly uses <c>root_dir</c> and
+ <c>cwd</c> when resolving file paths if both are
+ provided. The <c>cwd</c> handling is also corrected.</p>
+ <p>
+ Thanks to kape1395!</p>
+ <p>
+ Own Id: OTP-14225 Aux Id: PR-1331, PR-1335 </p>
+ </item>
+ <item>
+ <p>
+ Ssh_cli used a function that does not handle non-utf8
+ unicode correctly.</p>
+ <p>
+ Own Id: OTP-14230 Aux Id: ERL-364 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The implementation of the key exchange algorithms
+ diffie-hellman-group-exchange-sha* are optimized, up to a
+ factor of 11 for the slowest ( = biggest and safest)
+ group size.</p>
+ <p>
+ Own Id: OTP-14169 Aux Id: seq-13261 </p>
+ </item>
+ <item>
+ <p>
+ The ssh host key fingerprint generation now also takes a
+ list of algorithms and returns a list of corresponding
+ fingerprints. See
+ <c>public_key:ssh_hostkey_fingerprint/2</c> and the
+ option <c>silently_accept_hosts</c> in
+ <c>ssh:connect</c>.</p>
+ <p>
+ Own Id: OTP-14223</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A file read with an sftp client could loose data if the
+ packet_size is set to larger than 64k. This is corrected
+ now in such a way that the packet_size is silently
+ lowered if there is a risk for data loss.</p>
+ <p>
+ Own Id: OTP-13857 Aux Id: ERL-238, OTP-13858 </p>
+ </item>
+ <item>
+ <p>
+ When user defined SSH shell REPL process exits with
+ reason normal, the SSH channel callback module should
+ report successful exit status to the SSH client. This
+ provides simple way for SSH clients to check for
+ successful completion of executed commands. (Thanks to
+ isvilen)</p>
+ <p>
+ Own Id: OTP-13905 Aux Id: PR-1173 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Extended the option <c>silently_accept_hosts</c> for
+ <c>ssh:connect</c> to make it possible for the client to
+ check the SSH host key fingerprint string. Se the
+ reference manual for SSH.</p>
+ <p>
+ Own Id: OTP-13887 Aux Id: OTP-13888 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.3.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Re-negotiation problems with OpenSSH client solved.</p>
+ <p>
+ Own Id: OTP-13972</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If a client illegaly sends an info-line and then
+ immediatly closes the TCP-connection, a badmatch
+ exception was raised.</p>
+ <p>
+ Own Id: OTP-13966</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Intermittent ssh ERROR REPORT mentioning
+ nonblocking_sender</p>
+ <p>
+ Own Id: OTP-13953 Aux Id: seq13199 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Handle all possible exit values that should be
+ interpreted as {error, closed}. Failing to do so could
+ lead to unexpected crashes for users of the ssh
+ application.</p>
+ <p>
+ Own Id: OTP-13932 Aux Id: seq13189 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Upgrade of an established client connection could crash
+ because the ssh client supervisors children had wrong
+ type. This is fixed now.</p>
+ <p>
+ Own Id: OTP-13782 Aux Id: seq13158 </p>
+ </item>
+ <item>
+ <p>
+ Partly checks the public key early in public key
+ authorization</p>
+ <p>
+ Own Id: OTP-13847 Aux Id:
+ defensics-ssh3.1.0-190243,205277,219318 </p>
+ </item>
+ <item>
+ <p>
+ Corrected handling of SHA for ECDSA (Elliptic curve
+ public keys)</p>
+ <p>
+ Own Id: OTP-13850 Aux Id: defensics-ssh3.1.0-214168 </p>
+ </item>
+ <item>
+ <p>
+ Problems found by test suites as well as by
+ Codenomicon/Defensics fixed: - reduce max random padding
+ to 15 bytes (Codenomicon/Defensics) - inclomplete pdu
+ handling (Codenomicon/Defensics) - badmatch in test suite
+ - non-blocking send fixes deadlock in
+ ssh_connection_SUITE:interrupted_send</p>
+ <p>
+ Own Id: OTP-13854</p>
+ </item>
+ <item>
+ <p>
+ Caller is now notified when a tcp close is received.</p>
+ <p>
+ Own Id: OTP-13859 Aux Id: seq13177 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Use application:ensure_all_started/2 instead of
+ hard-coding deps</p>
+ <p>
+ Own Id: OTP-13843 Aux Id: PR-1147 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ SSH client does not any longer retry a bad password given
+ as option to ssh:connect et al.</p>
+ <p>
+ Own Id: OTP-13674 Aux Id: TR-HU92273 </p>
+ </item>
+ <item>
+ <p>
+ Removed possible hanging risk for a certain timing
+ sequence when communicating client and server executes on
+ the same node.</p>
+ <p>
+ Own Id: OTP-13715</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.3</title>
<section><title>Improvements and New Features</title>
@@ -108,6 +479,22 @@
</section>
+<section><title>Ssh 4.2.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ SSH client does not any longer retry a bad password given
+ as option to ssh:connect et al.</p>
+ <p>
+ Own Id: OTP-13674 Aux Id: TR-HU92273 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index e6c54d27bf..604b9f5bbb 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2015</year>
+ <year>2004</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -153,7 +153,7 @@
<item>
<p>IP version to use.</p>
</item>
- <tag><c><![CDATA[{user_dir, string()}]]></c></tag>
+ <tag><marker id="opt_user_dir"></marker><c><![CDATA[{user_dir, string()}]]></c></tag>
<item>
<p>Sets the user directory, that is, the directory containing
<c>ssh</c> configuration files for the user, such as
@@ -175,12 +175,48 @@
supplied with this option.
</p>
</item>
- <tag><c><![CDATA[{silently_accept_hosts, boolean()}]]></c></tag>
+ <tag>
+ <c><![CDATA[{silently_accept_hosts, boolean()}]]></c> <br/>
+ <c><![CDATA[{silently_accept_hosts, CallbackFun}]]></c> <br/>
+ <c><![CDATA[{silently_accept_hosts, {HashAlgoSpec, CallbackFun} }]]></c> <br/>
+ <br/>
+ <c><![CDATA[HashAlgoSpec = crypto:digest_type() | [ crypto:digest_type() ] ]]></c><br/>
+ <c><![CDATA[CallbackFun = fun(PeerName, FingerPrint) -> boolean()]]></c><br/>
+ <c><![CDATA[PeerName = string()]]></c><br/>
+ <c><![CDATA[FingerPrint = string() | [ string() ] ]]></c>
+ </tag>
<item>
- <p>When <c>true</c>, hosts are added to the
- file <c><![CDATA[known_hosts]]></c> without asking the user.
- Defaults to <c>false</c>.
- </p>
+ <p>This option guides the <c>connect</c> function how to act when the connected server presents a Host
+ Key that the client has not seen before. The default is to ask the user with a question on stdio of whether to
+ accept or reject the new Host Key.
+ See also the option <seealso marker="#opt_user_dir"><c>user_dir</c></seealso>
+ for the path to the file <c>known_hosts</c> where previously accepted Host Keys are recorded.
+ </p>
+ <p>The option can be given in three different forms as seen above:</p>
+ <list>
+ <item>The value is a <c>boolean()</c>. The value <c>true</c> will make the client accept any unknown
+ Host Key without any user interaction. The value <c>false</c> keeps the default behaviour of asking the
+ the user on stdio.
+ </item>
+ <item>A <c>CallbackFun</c> will be called and the boolean return value <c>true</c> will make the client
+ accept the Host Key. A return value of <c>false</c> will make the client to reject the Host Key and therefore
+ also the connection will be closed. The arguments to the fun are:
+ <list type="bulleted">
+ <item><c>PeerName</c> - a string with the name or address of the remote host.</item>
+ <item><c>FingerPrint</c> - the fingerprint of the Host Key as
+ <seealso marker="public_key:public_key#ssh_hostkey_fingerprint-1">public_key:ssh_hostkey_fingerprint/1</seealso>
+ calculates it.
+ </item>
+ </list>
+ </item>
+ <item>A tuple <c>{HashAlgoSpec, CallbackFun}</c>. The <c>HashAlgoSpec</c> specifies which hash algorithm
+ shall be used to calculate the fingerprint used in the call of the <c>CallbackFun</c>. The <c>HashALgoSpec</c>
+ is either an atom or a list of atoms as the first argument in
+ <seealso marker="public_key:public_key#ssh_hostkey_fingerprint-2">public_key:ssh_hostkey_fingerprint/2</seealso>.
+ If it is a list of hash algorithm names, the <c>FingerPrint</c> argument in the <c>CallbackFun</c> will be
+ a list of fingerprints in the same order as the corresponding name in the <c>HashAlgoSpec</c> list.
+ </item>
+ </list>
</item>
<tag><c><![CDATA[{user_interaction, boolean()}]]></c></tag>
<item>
@@ -190,7 +226,7 @@
supplying a password. Defaults to <c>true</c>.
Even if user interaction is allowed it can be
suppressed by other options, such as <c>silently_accept_hosts</c>
- and <c>password</c>. However, those optins are not always desirable
+ and <c>password</c>. However, those options are not always desirable
to use from a security point of view.</p>
</item>
@@ -756,7 +792,7 @@
<p>Utility function that starts the applications <c>crypto</c>, <c>public_key</c>,
and <c>ssh</c>. Default type is <c>temporary</c>.
For more information, see the <seealso marker="kernel:application">application(3)</seealso>
- manual page in <c>kernel</c>.</p>
+ manual page in Kernel.</p>
</desc>
</func>
@@ -769,7 +805,7 @@
<desc>
<p>Stops the <c>ssh</c> application.
For more information, see the <seealso marker="kernel:application">application(3)</seealso>
- manual page in <c>kernel</c>.</p>
+ manual page in Kernel.</p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_app.xml b/lib/ssh/doc/src/ssh_app.xml
index f6ce44c015..5cc4c24889 100644
--- a/lib/ssh/doc/src/ssh_app.xml
+++ b/lib/ssh/doc/src/ssh_app.xml
@@ -4,7 +4,7 @@
<appref>
<header>
<copyright>
- <year>2012</year><year>2015</year>
+ <year>2012</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -203,7 +203,7 @@
<section>
<title>Unicode support</title>
<p>Unicode filenames are supported if the emulator and the underlaying OS support it. See section DESCRIPTION in the
- <seealso marker="kernel:file">file</seealso> manual page in <c>kernel</c> for information about this subject.
+ <seealso marker="kernel:file">file</seealso> manual page in Kernel for information about this subject.
</p>
<p>The shell and the cli both support unicode.
</p>
diff --git a/lib/ssh/doc/src/ssh_channel.xml b/lib/ssh/doc/src/ssh_channel.xml
index 907b0b3bec..7b598494f7 100644
--- a/lib/ssh/doc/src/ssh_channel.xml
+++ b/lib/ssh/doc/src/ssh_channel.xml
@@ -139,7 +139,7 @@
enters the <c>ssh_channel</c> process receive loop and become an
<c>ssh_channel process</c>. The process must have been started using
one of the start functions in <c>proc_lib</c>, see the <seealso
- marker="stdlib:proc_lib">proc_lib(3)</seealso> manual page in <c>stdlib</c>.
+ marker="stdlib:proc_lib">proc_lib(3)</seealso> manual page in STDLIB.
The user is responsible for any initialization of the process
and must call <seealso marker = "#init-1">ssh_channel:init/1</seealso>.
</p>
diff --git a/lib/ssh/doc/src/ssh_protocol.xml b/lib/ssh/doc/src/ssh_protocol.xml
index 7288266cf7..a0032ab449 100644
--- a/lib/ssh/doc/src/ssh_protocol.xml
+++ b/lib/ssh/doc/src/ssh_protocol.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2013</year><year>2013</year>
+ <year>2013</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -138,8 +138,6 @@
Transport Layer Protocol.</item>
<item><url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> -
Connection Protocol.</item>
- <item><url href="http://www.ietf.org/rfc/rfc4255.txt">RFC 4255</url> -
- Key Fingerprints.</item>
<item><url href="http://www.ietf.org/rfc/rfc4344.txt">RFC 4344</url> -
Transport Layer Encryption Modes.</item>
<item><url href="http://www.ietf.org/rfc/rfc4716.txt">RFC 4716</url> -
diff --git a/lib/ssh/doc/src/using_ssh.xml b/lib/ssh/doc/src/using_ssh.xml
index 0861c641c7..ab307624e6 100644
--- a/lib/ssh/doc/src/using_ssh.xml
+++ b/lib/ssh/doc/src/using_ssh.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2012</year>
- <year>2016</year>
+ <year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -305,7 +305,7 @@ ok = erl_tar:close(HandleRead),
<code type="erl" >
-module(ssh_echo_server).
--behaviour(ssh_subsystem).
+-behaviour(ssh_daemon_channel).
-record(state, {
n,
id,
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 69d5a47f83..7ab6f22424 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -96,7 +96,7 @@ APP_TARGET= $(EBIN)/$(APP_FILE)
APPUP_SRC= $(APPUP_FILE).src
APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
-INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_userauth.hrl ssh_xfer.hrl
+INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_userauth.hrl ssh_xfer.hrl ssh_dbg.hrl
# ----------------------------------------------------
# FLAGS
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 3245ba5197..7859ab4064 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -41,11 +41,10 @@
{env, []},
{mod, {ssh_app, []}},
{runtime_dependencies, [
- "crypto-3.3",
+ "crypto-3.7.3",
"erts-6.0",
"kernel-3.0",
- "public_key-1.1",
- "stdlib-3.0"
+ "public_key-1.4",
+ "stdlib-3.3"
]}]}.
-
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index e38cecf226..2540720c41 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
+%% 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.
@@ -20,9 +20,13 @@
{"%VSN%",
[
+ {<<"4.3.2">>, [{load_module, ssh_channel, soft_purge, soft_purge, []}
+ ]},
{<<".*">>, [{restart_application, ssh}]}
],
[
+ {<<"4.3.2">>, [{load_module, ssh_channel, soft_purge, soft_purge, []}
+ ]},
{<<".*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 0570853a9b..ecdb184db3 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -1,7 +1,7 @@
%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -52,16 +52,15 @@
%% is temporary. see application(3)
%%--------------------------------------------------------------------
start() ->
- application:start(crypto),
- application:start(asn1),
- application:start(public_key),
- application:start(ssh).
+ start(temporary).
start(Type) ->
- application:start(crypto, Type),
- application:start(asn1),
- application:start(public_key, Type),
- application:start(ssh, Type).
+ case application:ensure_all_started(ssh, Type) of
+ {ok, _} ->
+ ok;
+ Other ->
+ Other
+ end.
%%--------------------------------------------------------------------
-spec stop() -> ok | {error, term()}.
@@ -90,7 +89,7 @@ connect(Socket, Options, Timeout) when is_port(Socket) ->
{error, Error};
{_SocketOptions, SshOptions} ->
case valid_socket_to_use(Socket, Options) of
- ok ->
+ ok ->
{ok, {Host,_Port}} = inet:sockname(Socket),
Opts = [{user_pid,self()}, {host,fmt_host(Host)} | SshOptions],
ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
@@ -128,23 +127,23 @@ connect(Host, Port, Options, Timeout) ->
-spec close(pid()) -> ok.
%%
%% Description: Closes an ssh connection.
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
close(ConnectionRef) ->
ssh_connection_handler:stop(ConnectionRef).
%%--------------------------------------------------------------------
-spec connection_info(pid(), [atom()]) -> [{atom(), term()}].
%%
-%% Description: Retrieves information about a connection.
-%%--------------------------------------------------------------------
+%% Description: Retrieves information about a connection.
+%%--------------------------------------------------------------------
connection_info(ConnectionRef, Options) ->
ssh_connection_handler:connection_info(ConnectionRef, Options).
%%--------------------------------------------------------------------
-spec channel_info(pid(), channel_id(), [atom()]) -> [{atom(), term()}].
%%
-%% Description: Retrieves information about a connection.
-%%--------------------------------------------------------------------
+%% Description: Retrieves information about a connection.
+%%--------------------------------------------------------------------
channel_info(ConnectionRef, ChannelId, Options) ->
ssh_connection_handler:channel_info(ConnectionRef, ChannelId, Options).
@@ -153,9 +152,9 @@ channel_info(ConnectionRef, ChannelId, Options) ->
-spec daemon(integer()|port(), proplists:proplist()) -> {ok, pid()} | {error, term()}.
-spec daemon(any | inet:ip_address(), integer(), proplists:proplist()) -> {ok, pid()} | {error, term()}.
-%% Description: Starts a server listening for SSH connections
+%% Description: Starts a server listening for SSH connections
%% on the given port.
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
daemon(Port) ->
daemon(Port, []).
@@ -176,7 +175,7 @@ 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},
+ [Prt || {{ssh_acceptor_sup,_,Prt,_},
_WorkerPid,worker,[ssh_acceptor]} <- supervisor:which_children(AsupPid)],
{ok, [{port,Port}]};
@@ -188,9 +187,9 @@ daemon_info(Pid) ->
-spec stop_listener(pid()) -> ok.
-spec stop_listener(inet:ip_address(), integer()) -> ok.
%%
-%% Description: Stops the listener, but leaves
+%% Description: Stops the listener, but leaves
%% existing connections started by the listener up and running.
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
stop_listener(SysSup) ->
ssh_system_sup:stop_listener(SysSup).
stop_listener(Address, Port) ->
@@ -202,9 +201,9 @@ stop_listener(Address, Port, Profile) ->
-spec stop_daemon(pid()) -> ok.
-spec stop_daemon(inet:ip_address(), integer()) -> ok.
%%
-%% Description: Stops the listener and all connections started by
+%% Description: Stops the listener and all connections started by
%% the listener.
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
stop_daemon(SysSup) ->
ssh_system_sup:stop_system(SysSup).
stop_daemon(Address, Port) ->
@@ -243,7 +242,7 @@ start_shell({ok, ConnectionRef}) ->
case ssh_connection:session_channel(ConnectionRef, infinity) of
{ok,ChannelId} ->
success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []),
- Args = [{channel_cb, ssh_shell},
+ Args = [{channel_cb, ssh_shell},
{init_args,[ConnectionRef, ChannelId]},
{cm, ConnectionRef}, {channel_id, ChannelId}],
{ok, State} = ssh_channel:init([Args]),
@@ -256,7 +255,7 @@ start_shell(Error) ->
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
-default_algorithms() ->
+default_algorithms() ->
ssh_transport:default_algorithms().
%%--------------------------------------------------------------------
@@ -281,14 +280,16 @@ valid_socket_to_use(Socket, Options) ->
{error, {unsupported,L4}}
end.
-is_tcp_socket(Socket) -> {ok,[]} =/= inet:getopts(Socket, [delay_send]).
-
-
+is_tcp_socket(Socket) ->
+ case inet:getopts(Socket, [delay_send]) of
+ {ok,[_]} -> true;
+ _ -> false
+ end.
daemon_shell_opt(Options) ->
case proplists:get_value(shell, Options) of
undefined ->
- [{shell, {shell, start, []}} | Options];
+ [{shell, ?DEFAULT_SHELL} | Options];
_ ->
Options
end.
@@ -296,13 +297,13 @@ daemon_shell_opt(Options) ->
daemon_host_inet_opt(HostAddr, Options1) ->
case HostAddr of
any ->
- {ok, Host0} = inet:gethostname(),
+ {ok, Host0} = inet:gethostname(),
{Host0, proplists:get_value(inet, Options1, inet), Options1};
{_,_,_,_} ->
- {HostAddr, inet,
+ {HostAddr, inet,
[{ip, HostAddr} | Options1]};
{_,_,_,_,_,_,_,_} ->
- {HostAddr, inet6,
+ {HostAddr, inet6,
[{ip, HostAddr} | Options1]}
end.
@@ -313,11 +314,12 @@ start_daemon(Socket, Options) ->
{error, Error};
{SocketOptions, SshOptions} ->
case valid_socket_to_use(Socket, Options) of
- ok ->
- try
+ ok ->
+ try
do_start_daemon(Socket, [{role,server}|SshOptions], SocketOptions)
catch
throw:bad_fd -> {error,bad_fd};
+ throw:bad_socket -> {error,bad_socket};
_C:_E -> {error,{cannot_start_daemon,_C,_E}}
end;
{error,SockError} ->
@@ -330,16 +332,17 @@ start_daemon(Host, Port, Options, Inet) ->
{error, _Reason} = Error ->
Error;
{SocketOptions, SshOptions}->
- try
+ try
do_start_daemon(Host, Port, [{role,server}|SshOptions] , [Inet|SocketOptions])
catch
throw:bad_fd -> {error,bad_fd};
+ throw:bad_socket -> {error,bad_socket};
_C:_E -> {error,{cannot_start_daemon,_C,_E}}
end
end.
-
+
do_start_daemon(Socket, SshOptions, SocketOptions) ->
- {ok, {IP,Port}} =
+ {ok, {IP,Port}} =
try {ok,_} = inet:sockname(Socket)
catch
_:_ -> throw(bad_socket)
@@ -351,7 +354,7 @@ do_start_daemon(Socket, SshOptions, SocketOptions) ->
{address, Host},
{port, Port},
{role, server},
- {socket_opts, SocketOptions},
+ {socket_opts, SocketOptions},
{ssh_opts, SshOptions}],
{_, Callback, _} = proplists:get_value(transport, SshOptions, {tcp, gen_tcp, tcp_closed}),
case ssh_system_sup:system_supervisor(Host, Port, Profile) of
@@ -363,8 +366,7 @@ do_start_daemon(Socket, SshOptions, SocketOptions) ->
{error, {already_started, _}} ->
{error, eaddrinuse};
Result = {ok,_} ->
- ssh_acceptor:handle_connection(Callback, Host, Port, Opts, Socket),
- Result;
+ call_ssh_acceptor_handle_connection(Callback, Host, Port, Opts, Socket, Result);
Result = {error, _} ->
Result
catch
@@ -377,15 +379,14 @@ do_start_daemon(Socket, SshOptions, SocketOptions) ->
{error, {already_started, _}} ->
{error, eaddrinuse};
{ok, _} ->
- ssh_acceptor:handle_connection(Callback, Host, Port, Opts, Socket),
- {ok, Sup};
+ call_ssh_acceptor_handle_connection(Callback, Host, Port, Opts, Socket, {ok, Sup});
Other ->
Other
end
end.
do_start_daemon(Host0, Port0, SshOptions, SocketOptions) ->
- {Host,Port1} =
+ {Host,Port1} =
try
case proplists:get_value(fd, SocketOptions) of
undefined ->
@@ -402,21 +403,21 @@ do_start_daemon(Host0, Port0, SshOptions, SocketOptions) ->
{Port, WaitRequestControl, Opts0} =
case Port1 of
0 -> %% Allocate the socket here to get the port number...
- {_, Callback, _} =
+ {_, 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},
+ {LPort,
+ {LSock,Callback},
[{lsocket,LSock},{lsock_owner,self()}]
};
_ ->
{Port1, false, []}
end,
- Opts = [{address, Host},
+ Opts = [{address, Host},
{port, Port},
{role, server},
- {socket_opts, SocketOptions},
+ {socket_opts, SocketOptions},
{ssh_opts, SshOptions} | Opts0],
case ssh_system_sup:system_supervisor(Host, Port, Profile) of
undefined ->
@@ -448,6 +449,16 @@ do_start_daemon(Host0, Port0, SshOptions, SocketOptions) ->
end
end.
+call_ssh_acceptor_handle_connection(Callback, Host, Port, Opts, Socket, DefaultResult) ->
+ try ssh_acceptor:handle_connection(Callback, Host, Port, Opts, Socket)
+ of
+ {error,Error} -> {error,Error};
+ _ -> DefaultResult
+ catch
+ C:R -> {error,{could_not_start_connection,{C,R}}}
+ end.
+
+
sync_request_control(false) ->
ok;
sync_request_control({LSock,Callback}) ->
@@ -465,7 +476,7 @@ find_hostport(Fd) ->
{ok, HostPort} = inet:sockname(S),
ok = inet:close(S),
HostPort.
-
+
handle_options(Opts) ->
try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of
@@ -480,9 +491,9 @@ handle_options(Opts) ->
algs_compatibility(Os0) ->
%% Take care of old options 'public_key_alg' and 'pref_public_key_algs'
case proplists:get_value(public_key_alg, Os0) of
- undefined ->
+ undefined ->
Os0;
- A when is_atom(A) ->
+ A when is_atom(A) ->
%% Skip public_key_alg if pref_public_key_algs is defined:
Os = lists:keydelete(public_key_alg, 1, Os0),
case proplists:get_value(pref_public_key_algs,Os) of
@@ -492,7 +503,7 @@ algs_compatibility(Os0) ->
[{pref_public_key_algs,['ssh-dss','ssh-rsa']} | Os];
undefined ->
throw({error, {eoptions, {public_key_alg,A} }});
- _ ->
+ _ ->
Os
end;
V ->
@@ -618,9 +629,29 @@ handle_ssh_option({user_dir_fun, Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_boolean(Value) ->
Opt;
+handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_function(Value,2) ->
+ Opt;
+handle_ssh_option({silently_accept_hosts, {DigestAlg,Value}} = Opt) when is_function(Value,2) ->
+ Algs = if is_atom(DigestAlg) -> [DigestAlg];
+ is_list(DigestAlg) -> DigestAlg;
+ true -> throw({error, {eoptions, Opt}})
+ end,
+ case [A || A <- Algs,
+ not lists:member(A, [md5, sha, sha224, sha256, sha384, sha512])] of
+ [_|_] = UnSup1 ->
+ throw({error, {{eoptions, Opt}, {not_fingerprint_algos,UnSup1}}});
+ [] ->
+ CryptoHashAlgs = proplists:get_value(hashs, crypto:supports(), []),
+ case [A || A <- Algs,
+ not lists:member(A, CryptoHashAlgs)] of
+ [_|_] = UnSup2 ->
+ throw({error, {{eoptions, Opt}, {unsupported_algo,UnSup2}}});
+ [] -> Opt
+ end
+ end;
handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) ->
Opt;
-handle_ssh_option({preferred_algorithms,[_|_]} = Opt) ->
+handle_ssh_option({preferred_algorithms,[_|_]} = Opt) ->
handle_pref_algs(Opt);
handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) ->
@@ -629,7 +660,7 @@ handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) ->
lists:foldl(
fun({N,G,P}, Acc) when is_integer(N),N>0,
is_integer(G),G>0,
- is_integer(P),P>0 ->
+ is_integer(P),P>0 ->
[{N,{G,P}} | Acc];
({N,{G,P}}, Acc) when is_integer(N),N>0,
is_integer(G),G>0,
@@ -637,7 +668,7 @@ handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) ->
[{N,{G,P}} | Acc];
({N,GPs}, Acc) when is_list(GPs) ->
lists:foldr(fun({Gi,Pi}, Acci) when is_integer(Gi),Gi>0,
- is_integer(Pi),Pi>0 ->
+ is_integer(Pi),Pi>0 ->
[{N,{Gi,Pi}} | Acci]
end, Acc, GPs)
end, [], L0))};
@@ -647,7 +678,7 @@ handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0,
Tag == ssh_moduli_file ->
{ok,GroupDefs} =
case Tag of
- file ->
+ file ->
file:consult(File);
ssh_moduli_file ->
case file:open(File,[read]) of
@@ -672,14 +703,14 @@ handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0,
catch
_:_ ->
throw({error, {{eoptions, Opt}, "Bad format in file: "++File}})
- end;
-
+ end;
+
-handle_ssh_option({dh_gex_limits,{Min,Max}} = Opt) when is_integer(Min), Min>0,
+handle_ssh_option({dh_gex_limits,{Min,Max}} = Opt) when is_integer(Min), Min>0,
is_integer(Max), Max>=Min ->
%% Server
Opt;
-handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0,
+handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0,
is_integer(I), I>=Min,
is_integer(Max), Max>=I ->
%% Client
@@ -724,7 +755,7 @@ handle_ssh_option({keyboard_interact_fun, Value} = Opt) when is_function(Value,3
Opt;
handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
Opt;
-handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
+handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
is_atom(Function) ->
Opt;
handle_ssh_option({exec, Function} = Opt) when is_function(Function) ->
@@ -772,7 +803,7 @@ handle_ssh_option({quiet_mode, Value} = Opt) when is_boolean(Value) ->
Opt;
handle_ssh_option({idle_time, Value} = Opt) when is_integer(Value), Value > 0 ->
Opt;
-handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) ->
+handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) ->
Opt;
handle_ssh_option({id_string, random}) ->
{id_string, {random,2,5}}; %% 2 - 5 random characters
@@ -814,11 +845,11 @@ handle_pref_algs({preferred_algorithms,Algs}) ->
of
DefAlgs -> handle_pref_alg(Key,Vals,DefAlgs)
catch
- _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}},
+ _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}},
"Bad preferred_algorithms key"}})
end || {Key,Vals} <- Algs]
};
-
+
Dups ->
throw({error, {{eoptions, {preferred_algorithms,Dups}}, "Duplicates found"}})
catch
@@ -857,13 +888,13 @@ handle_pref_alg(Key,
) ->
handle_pref_alg(Key, lists:reverse(Vs), Sup);
-handle_pref_alg(Key,
+handle_pref_alg(Key,
Vs=[V|_],
Sup=[{client2server,_},{server2client,_}]
) when is_atom(V) ->
handle_pref_alg(Key, [{client2server,Vs},{server2client,Vs}], Sup);
-handle_pref_alg(Key,
+handle_pref_alg(Key,
Vs=[V|_],
Sup=[S|_]
) when is_atom(V), is_atom(S) ->
@@ -878,14 +909,14 @@ chk_alg_vs(OptKey, Values, SupportedValues) ->
[] -> Values;
Bad -> throw({error, {{eoptions, {OptKey,Bad}}, "Unsupported value(s) found"}})
end.
-
+
handle_ip(Inet) -> %% Default to ipv4
case lists:member(inet, Inet) of
true ->
Inet;
false ->
case lists:member(inet6, Inet) of
- true ->
+ true ->
Inet;
false ->
[inet | Inet]
@@ -916,8 +947,8 @@ directory_exist_readable(Dir) ->
{error, Error} ->
{error, Error}
end.
-
-
+
+
collect_per_size(L) ->
lists:foldr(
@@ -948,7 +979,7 @@ read_moduli_file(D, I, Acc) ->
read_moduli_file(D, I+1, Acc)
end
end.
-
+
handle_user_pref_pubkey_algs([], Acc) ->
{true, lists:reverse(Acc)};
handle_user_pref_pubkey_algs([H|T], Acc) ->
@@ -963,7 +994,7 @@ handle_user_pref_pubkey_algs([H|T], Acc) ->
false
end.
-fmt_host({A,B,C,D}) ->
+fmt_host({A,B,C,D}) ->
lists:concat([A,".",B,".",C,".",D]);
-fmt_host(T={_,_,_,_,_,_,_,_}) ->
+fmt_host(T={_,_,_,_,_,_,_,_}) ->
lists:flatten(string:join([io_lib:format("~.16B",[A]) || A <- tuple_to_list(T)], ":")).
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 868f3a9181..9d119f4f42 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -33,6 +33,12 @@
-define(REKEY_DATA_TIMOUT, 60000).
-define(DEFAULT_PROFILE, default).
+-define(DEFAULT_TRANSPORT, {tcp, gen_tcp, tcp_closed} ).
+
+-define(DEFAULT_SHELL, {shell, start, []} ).
+
+-define(MAX_RND_PADDING_LEN, 15).
+
-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
-define(SUPPORTED_USER_KEYS, ['ssh-rsa','ssh-dss','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521']).
@@ -127,7 +133,7 @@
recv_sequence = 0,
keyex_key,
keyex_info,
- random_length_padding = 255, % From RFC 4253 section 6.
+ random_length_padding = 15, % From RFC 4253 section 6.
%% User auth
user,
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index 9f3e60bd62..13c9d9af4a 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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.
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 49eec8072f..9b54ecb2dd 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -31,24 +31,123 @@
-export([publickey_msg/1, password_msg/1, keyboard_interactive_msg/1,
service_request_msg/1, init_userauth_request_msg/1,
userauth_request_msg/1, handle_userauth_request/3,
- handle_userauth_info_request/3, handle_userauth_info_response/2
+ handle_userauth_info_request/2, handle_userauth_info_response/2
]).
%%--------------------------------------------------------------------
%%% Internal application API
%%--------------------------------------------------------------------
+%%%----------------------------------------------------------------
+userauth_request_msg(#ssh{userauth_methods = ServerMethods,
+ userauth_supported_methods = UserPrefMethods, % Note: this is not documented as supported for clients
+ userauth_preference = ClientMethods0
+ } = Ssh0) ->
+ case sort_select_mthds(ClientMethods0, UserPrefMethods, ServerMethods) of
+ [] ->
+ Msg = #ssh_msg_disconnect{code = ?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+ description = "Unable to connect using the available authentication methods",
+ language = "en"},
+ {disconnect, Msg, ssh_transport:ssh_packet(Msg, Ssh0)};
+
+ [{Pref,Module,Function,Args} | Prefs] ->
+ Ssh = case Pref of
+ "keyboard-interactive" -> Ssh0;
+ _ -> Ssh0#ssh{userauth_preference = Prefs}
+ end,
+ case Module:Function(Args ++ [Ssh]) of
+ {not_ok, Ssh1} ->
+ userauth_request_msg(Ssh1#ssh{userauth_preference = Prefs});
+ Result ->
+ {Pref,Result}
+ end
+ end.
+
+
+
+sort_select_mthds(Clients, undefined, Servers) ->
+ %% User has not expressed an opinion via option "auth_methods", use the server's prefs
+ sort_select_mthds1(Clients, Servers, string:tokens(?SUPPORTED_AUTH_METHODS,","));
+
+sort_select_mthds(Clients, Users0, Servers0) ->
+ %% The User has an opinion, use the intersection of that and the Servers whishes but
+ %% in the Users order
+ sort_select_mthds1(Clients, string:tokens(Users0,","), Servers0).
+
+
+sort_select_mthds1(Clients, Users0, Servers0) ->
+ Servers = unique(Servers0),
+ Users = unique(Users0),
+ [C || Key <- Users,
+ lists:member(Key, Servers),
+ C <- Clients,
+ element(1,C) == Key].
+
+unique(L) ->
+ lists:reverse(
+ lists:foldl(fun(E,Acc) ->
+ case lists:member(E,Acc) of
+ true -> Acc;
+ false -> [E|Acc]
+ end
+ end, [], L)).
+
+
+%%%---- userauth_request_msg "callbacks"
+password_msg([#ssh{opts = Opts, io_cb = IoCb,
+ user = User, service = Service} = Ssh0]) ->
+ {Password,Ssh} =
+ case proplists:get_value(password, Opts) of
+ undefined when IoCb == ssh_no_io ->
+ {not_ok, Ssh0};
+ undefined ->
+ {IoCb:read_password("ssh password: ",Ssh0), Ssh0};
+ PW ->
+ %% If "password" option is given it should not be tried again
+ {PW, Ssh0#ssh{opts = lists:keyreplace(password,1,Opts,{password,not_ok})}}
+ end,
+ case Password of
+ not_ok ->
+ {not_ok, Ssh};
+ _ ->
+ ssh_transport:ssh_packet(
+ #ssh_msg_userauth_request{user = User,
+ service = Service,
+ method = "password",
+ data =
+ <<?BOOLEAN(?FALSE),
+ ?STRING(unicode:characters_to_binary(Password))>>},
+ Ssh)
+ end.
+
+%% See RFC 4256 for info on keyboard-interactive
+keyboard_interactive_msg([#ssh{user = User,
+ opts = Opts,
+ service = Service} = Ssh]) ->
+ case proplists:get_value(password, Opts) of
+ not_ok ->
+ {not_ok,Ssh}; % No need to use a failed pwd once more
+ _ ->
+ ssh_transport:ssh_packet(
+ #ssh_msg_userauth_request{user = User,
+ service = Service,
+ method = "keyboard-interactive",
+ data = << ?STRING(<<"">>),
+ ?STRING(<<>>) >> },
+ Ssh)
+ end.
+
publickey_msg([Alg, #ssh{user = User,
session_id = SessionId,
service = Service,
opts = Opts} = Ssh]) ->
- Hash = sha, %% Maybe option?!
+ Hash = ssh_transport:sha(Alg),
KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
case KeyCb:user_key(Alg, Opts) of
{ok, PrivKey} ->
StrAlgo = atom_to_list(Alg),
case encode_public_key(StrAlgo, ssh_transport:extract_public_key(PrivKey)) of
not_ok ->
- not_ok;
+ {not_ok, Ssh};
PubKeyBlob ->
SigData = build_sig_data(SessionId,
User, Service, PubKeyBlob, StrAlgo),
@@ -65,52 +164,15 @@ publickey_msg([Alg, #ssh{user = User,
Ssh)
end;
_Error ->
- not_ok
- end.
-
-password_msg([#ssh{opts = Opts, io_cb = IoCb,
- user = User, service = Service} = Ssh]) ->
- Password = case proplists:get_value(password, Opts) of
- undefined ->
- user_interaction(IoCb, Ssh);
- PW ->
- PW
- end,
- case Password of
- not_ok ->
- not_ok;
- _ ->
- ssh_transport:ssh_packet(
- #ssh_msg_userauth_request{user = User,
- service = Service,
- method = "password",
- data =
- <<?BOOLEAN(?FALSE),
- ?STRING(unicode:characters_to_binary(Password))>>},
- Ssh)
+ {not_ok, Ssh}
end.
-user_interaction(ssh_no_io, _) ->
- not_ok;
-user_interaction(IoCb, Ssh) ->
- IoCb:read_password("ssh password: ", Ssh).
-
-
-%% See RFC 4256 for info on keyboard-interactive
-keyboard_interactive_msg([#ssh{user = User,
- service = Service} = Ssh]) ->
- ssh_transport:ssh_packet(
- #ssh_msg_userauth_request{user = User,
- service = Service,
- method = "keyboard-interactive",
- data = << ?STRING(<<"">>),
- ?STRING(<<>>) >> },
- Ssh).
-
+%%%----------------------------------------------------------------
service_request_msg(Ssh) ->
ssh_transport:ssh_packet(#ssh_msg_service_request{name = "ssh-userauth"},
Ssh#ssh{service = "ssh-userauth"}).
+%%%----------------------------------------------------------------
init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
case user_name(Opts) of
{ok, User} ->
@@ -140,34 +202,9 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
description = ErrStr})
end.
-userauth_request_msg(#ssh{userauth_preference = []} = Ssh) ->
- Msg = #ssh_msg_disconnect{code =
- ?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
- description = "Unable to connect using the available"
- " authentication methods",
- language = "en"},
- {disconnect, Msg, ssh_transport:ssh_packet(Msg, Ssh)};
-
-userauth_request_msg(#ssh{userauth_methods = Methods,
- userauth_preference = [{Pref, Module,
- Function, Args} | Prefs]}
- = Ssh0) ->
- Ssh = Ssh0#ssh{userauth_preference = Prefs},
- case lists:member(Pref, Methods) of
- true ->
- case Module:Function(Args ++ [Ssh]) of
- not_ok ->
- userauth_request_msg(Ssh);
- Result ->
- {Pref,Result}
- end;
- false ->
- userauth_request_msg(Ssh)
- end.
-
-
-handle_userauth_request(#ssh_msg_service_request{name =
- Name = "ssh-userauth"},
+%%%----------------------------------------------------------------
+%%% called by server
+handle_userauth_request(#ssh_msg_service_request{name = Name = "ssh-userauth"},
_, Ssh) ->
{ok, ssh_transport:ssh_packet(#ssh_msg_service_accept{name = Name},
Ssh#ssh{service = "ssh-connection"})};
@@ -223,32 +260,54 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
handle_userauth_request(#ssh_msg_userauth_request{user = User,
service = "ssh-connection",
method = "publickey",
- data = Data},
- SessionId,
+ data = <<?BYTE(?FALSE),
+ ?UINT32(ALen), BAlg:ALen/binary,
+ ?UINT32(KLen), KeyBlob:KLen/binary,
+ _/binary
+ >>
+ },
+ _SessionId,
#ssh{opts = Opts,
userauth_supported_methods = Methods} = Ssh) ->
- <<?BYTE(HaveSig), ?UINT32(ALen), BAlg:ALen/binary,
- ?UINT32(KLen), KeyBlob:KLen/binary, SigWLen/binary>> = Data,
- Alg = binary_to_list(BAlg),
- case HaveSig of
- ?TRUE ->
- case verify_sig(SessionId, User, "ssh-connection", Alg,
- KeyBlob, SigWLen, Opts) of
- true ->
- {authorized, User,
- ssh_transport:ssh_packet(
- #ssh_msg_userauth_success{}, Ssh)};
- false ->
- {not_authorized, {User, undefined},
- ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
- authentications = Methods,
- partial_success = false}, Ssh)}
- end;
- ?FALSE ->
+
+ case pre_verify_sig(User, binary_to_list(BAlg),
+ KeyBlob, Opts) of
+ true ->
{not_authorized, {User, undefined},
ssh_transport:ssh_packet(
- #ssh_msg_userauth_pk_ok{algorithm_name = Alg,
- key_blob = KeyBlob}, Ssh)}
+ #ssh_msg_userauth_pk_ok{algorithm_name = binary_to_list(BAlg),
+ key_blob = KeyBlob}, Ssh)};
+ false ->
+ {not_authorized, {User, undefined},
+ ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
+ authentications = Methods,
+ partial_success = false}, Ssh)}
+ end;
+
+handle_userauth_request(#ssh_msg_userauth_request{user = User,
+ service = "ssh-connection",
+ method = "publickey",
+ data = <<?BYTE(?TRUE),
+ ?UINT32(ALen), BAlg:ALen/binary,
+ ?UINT32(KLen), KeyBlob:KLen/binary,
+ SigWLen/binary>>
+ },
+ SessionId,
+ #ssh{opts = Opts,
+ userauth_supported_methods = Methods} = Ssh) ->
+
+ case verify_sig(SessionId, User, "ssh-connection",
+ binary_to_list(BAlg),
+ KeyBlob, SigWLen, Opts) of
+ true ->
+ {authorized, User,
+ ssh_transport:ssh_packet(
+ #ssh_msg_userauth_success{}, Ssh)};
+ false ->
+ {not_authorized, {User, undefined},
+ ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
+ authentications = Methods,
+ partial_success = false}, Ssh)}
end;
handle_userauth_request(#ssh_msg_userauth_request{user = User,
@@ -319,31 +378,54 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
partial_success = false}, Ssh)}.
-
-handle_userauth_info_request(
- #ssh_msg_userauth_info_request{name = Name,
- instruction = Instr,
- num_prompts = NumPrompts,
- data = Data}, IoCb,
- #ssh{opts = Opts} = Ssh) ->
+%%%----------------------------------------------------------------
+%%% keyboard-interactive client
+handle_userauth_info_request(#ssh_msg_userauth_info_request{name = Name,
+ instruction = Instr,
+ num_prompts = NumPrompts,
+ data = Data},
+ #ssh{opts = Opts,
+ io_cb = IoCb
+ } = Ssh) ->
PromptInfos = decode_keyboard_interactive_prompts(NumPrompts,Data),
- Responses = keyboard_interact_get_responses(IoCb, Opts,
- Name, Instr, PromptInfos),
- {ok,
- ssh_transport:ssh_packet(
- #ssh_msg_userauth_info_response{num_responses = NumPrompts,
- data = Responses}, Ssh)}.
+ case keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) of
+ not_ok ->
+ not_ok;
+ Responses ->
+ {ok,
+ ssh_transport:ssh_packet(
+ #ssh_msg_userauth_info_response{num_responses = NumPrompts,
+ data = Responses}, Ssh)}
+ end.
+%%%----------------------------------------------------------------
+%%% keyboard-interactive server
handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1,
data = <<?UINT32(Sz), Password:Sz/binary>>},
#ssh{opts = Opts,
kb_tries_left = KbTriesLeft,
user = User,
userauth_supported_methods = Methods} = Ssh) ->
+ SendOneEmpty =
+ (proplists:get_value(tstflg,Opts) == one_empty)
+ orelse
+ proplists:get_value(one_empty, proplists:get_value(tstflg,Opts,[]), false),
+
case check_password(User, unicode:characters_to_list(Password), Opts, Ssh) of
+ {true,Ssh1} when SendOneEmpty==true ->
+ Msg = #ssh_msg_userauth_info_request{name = "",
+ instruction = "",
+ language_tag = "",
+ num_prompts = 0,
+ data = <<?BOOLEAN(?FALSE)>>
+ },
+ {authorized_but_one_more, User,
+ ssh_transport:ssh_packet(Msg, Ssh1)};
+
{true,Ssh1} ->
{authorized, User,
ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh1)};
+
{false,Ssh1} ->
{not_authorized, {User, {error,"Bad user or password"}},
ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
@@ -353,6 +435,11 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1,
)}
end;
+handle_userauth_info_response({extra,#ssh_msg_userauth_info_response{}},
+ #ssh{user = User} = Ssh) ->
+ {authorized, User,
+ ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)};
+
handle_userauth_info_response(#ssh_msg_userauth_info_response{},
_Auth) ->
ssh_connection_handler:disconnect(
@@ -369,11 +456,6 @@ method_preference(Algs) ->
[{"publickey", ?MODULE, publickey_msg, [A]} | Acc]
end,
[{"password", ?MODULE, password_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []},
{"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
],
Algs).
@@ -434,19 +516,34 @@ get_password_option(Opts, User) ->
false -> proplists:get_value(password, Opts, false)
end.
-verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) ->
- {ok, Key} = decode_public_key_v2(KeyBlob, Alg),
- KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
+pre_verify_sig(User, Alg, KeyBlob, Opts) ->
+ try
+ {ok, Key} = decode_public_key_v2(KeyBlob, Alg),
+ KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
+ KeyCb:is_auth_key(Key, User, Opts)
+ catch
+ _:_ ->
+ false
+ end.
- case KeyCb:is_auth_key(Key, User, Opts) of
- true ->
- PlainText = build_sig_data(SessionId, User,
- Service, KeyBlob, Alg),
- <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen,
- <<?UINT32(AlgLen), _Alg:AlgLen/binary,
- ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig,
- ssh_transport:verify(PlainText, sha, Sig, Key);
- false ->
+verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) ->
+ try
+ {ok, Key} = decode_public_key_v2(KeyBlob, Alg),
+ KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
+
+ case KeyCb:is_auth_key(Key, User, Opts) of
+ true ->
+ PlainText = build_sig_data(SessionId, User,
+ Service, KeyBlob, Alg),
+ <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen,
+ <<?UINT32(AlgLen), _Alg:AlgLen/binary,
+ ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig,
+ ssh_transport:verify(PlainText, ssh_transport:sha(list_to_atom(Alg)), Sig, Key);
+ false ->
+ false
+ end
+ catch
+ _:_ ->
false
end.
@@ -473,6 +570,9 @@ keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) ->
proplists:get_value(password, Opts, undefined), IoCb, Name,
Instr, PromptInfos, Opts, NumPrompts).
+
+keyboard_interact_get_responses(_, _, not_ok, _, _, _, _, _, _) ->
+ not_ok;
keyboard_interact_get_responses(_, undefined, Password, _, _, _, _, _,
1) when Password =/= undefined ->
[Password]; %% Password auth implemented with keyboard-interaction and passwd is known
@@ -486,17 +586,18 @@ keyboard_interact_get_responses(true, Fun, _Pwd, _IoCb, Name, Instr, PromptInfos
keyboard_interact_fun(Fun, Name, Instr, PromptInfos, NumPrompts).
keyboard_interact(IoCb, Name, Instr, Prompts, Opts) ->
- if Name /= "" -> IoCb:format("~s~n", [Name]);
- true -> ok
- end,
- if Instr /= "" -> IoCb:format("~s~n", [Instr]);
- true -> ok
- end,
+ write_if_nonempty(IoCb, Name),
+ write_if_nonempty(IoCb, Instr),
lists:map(fun({Prompt, true}) -> IoCb:read_line(Prompt, Opts);
({Prompt, false}) -> IoCb:read_password(Prompt, Opts)
end,
Prompts).
+write_if_nonempty(_, "") -> ok;
+write_if_nonempty(_, <<>>) -> ok;
+write_if_nonempty(IoCb, Text) -> IoCb:format("~s~n",[Text]).
+
+
keyboard_interact_fun(KbdInteractFun, Name, Instr, PromptInfos, NumPrompts) ->
Prompts = lists:map(fun({Prompt, _Echo}) -> Prompt end,
PromptInfos),
diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl
index a8e6ebde16..85b31f3669 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.erl
@@ -93,11 +93,16 @@ call(ChannelPid, Msg, TimeOute) ->
catch
exit:{noproc, _} ->
{error, closed};
+ exit:{normal, _} ->
+ {error, closed};
+ exit:{shutdown, _} ->
+ {error, closed};
+ exit:{{shutdown, _}, _} ->
+ {error, closed};
exit:{timeout, _} ->
{error, timeout}
end.
-
cast(ChannelPid, Msg) ->
gen_server:cast(ChannelPid, Msg).
@@ -256,7 +261,7 @@ handle_info({ssh_cm, _, _} = Msg, #state{cm = ConnectionManager,
adjust_window(Msg),
{noreply, State#state{channel_state = ChannelState}, Timeout};
{stop, ChannelId, ChannelState} ->
- ssh_connection:close(ConnectionManager, ChannelId),
+ catch ssh_connection:close(ConnectionManager, ChannelId),
{stop, normal, State#state{close_sent = true,
channel_state = ChannelState}}
end;
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 74cd2e081a..94d1275e7b 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2017. 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.
@@ -127,7 +127,8 @@ handle_ssh_msg({ssh_cm, ConnectionHandler,
cm = ConnectionHandler}};
handle_ssh_msg({ssh_cm, ConnectionHandler,
- {exec, ChannelId, WantReply, Cmd}}, #state{exec=undefined} = State) ->
+ {exec, ChannelId, WantReply, Cmd}}, #state{exec=undefined,
+ shell=?DEFAULT_SHELL} = State) ->
{Reply, Status} = exec(Cmd),
write_chars(ConnectionHandler,
ChannelId, io_lib:format("~p\n", [Reply])),
@@ -136,6 +137,15 @@ handle_ssh_msg({ssh_cm, ConnectionHandler,
ssh_connection:exit_status(ConnectionHandler, ChannelId, Status),
ssh_connection:send_eof(ConnectionHandler, ChannelId),
{stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionHandler}};
+
+handle_ssh_msg({ssh_cm, ConnectionHandler,
+ {exec, ChannelId, WantReply, _Cmd}}, #state{exec = undefined} = State) ->
+ write_chars(ConnectionHandler, ChannelId, 1, "Prohibited.\n"),
+ ssh_connection:reply_request(ConnectionHandler, WantReply, success, ChannelId),
+ ssh_connection:exit_status(ConnectionHandler, ChannelId, 255),
+ ssh_connection:send_eof(ConnectionHandler, ChannelId),
+ {stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionHandler}};
+
handle_ssh_msg({ssh_cm, ConnectionHandler,
{exec, ChannelId, WantReply, Cmd}}, State) ->
NewState = start_shell(ConnectionHandler, Cmd, State),
@@ -208,8 +218,15 @@ handle_msg({Group, Req}, #state{group = Group, buf = Buf, pty = Pty,
write_chars(ConnectionHandler, ChannelId, Chars),
{ok, State#state{buf = NewBuf}};
-handle_msg({'EXIT', Group, _Reason}, #state{group = Group,
- channel = ChannelId} = State) ->
+handle_msg({'EXIT', Group, Reason}, #state{group = Group,
+ cm = ConnectionHandler,
+ channel = ChannelId} = State) ->
+ Status = case Reason of
+ normal -> 0;
+ _ -> -1
+ end,
+ ssh_connection:exit_status(ConnectionHandler, ChannelId, Status),
+ ssh_connection:send_eof(ConnectionHandler, ChannelId),
{stop, ChannelId, State};
handle_msg(_, State) ->
@@ -446,14 +463,23 @@ move_cursor(From, To, #ssh_pty{width=Width, term=Type}) ->
%% %%% make sure that there is data to send
%% %%% before calling ssh_connection:send
write_chars(ConnectionHandler, ChannelId, Chars) ->
- case erlang:iolist_size(Chars) of
- 0 ->
- ok;
- _ ->
- ssh_connection:send(ConnectionHandler, ChannelId,
- ?SSH_EXTENDED_DATA_DEFAULT, Chars)
+ write_chars(ConnectionHandler, ChannelId, ?SSH_EXTENDED_DATA_DEFAULT, Chars).
+
+write_chars(ConnectionHandler, ChannelId, Type, Chars) ->
+ case has_chars(Chars) of
+ false -> ok;
+ true -> ssh_connection:send(ConnectionHandler,
+ ChannelId,
+ Type,
+ Chars)
end.
+has_chars([C|_]) when is_integer(C) -> true;
+has_chars([H|T]) when is_list(H) ; is_binary(H) -> has_chars(H) orelse has_chars(T);
+has_chars(<<_:8,_/binary>>) -> true;
+has_chars(_) -> false.
+
+
%%% tail, works with empty lists
tl1([_|A]) -> A;
tl1(_) -> [].
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index d0f2d54c06..c7a2c92670 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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.
@@ -287,6 +287,9 @@ handle_msg(#ssh_msg_channel_open_confirmation{recipient_channel = ChannelId,
ssh_channel:cache_update(Cache, Channel#channel{
remote_id = RemoteId,
+ recv_packet_size = max(32768, % rfc4254/5.2
+ min(PacketSz, Channel#channel.recv_packet_size)
+ ),
send_window_size = WindowSz,
send_packet_size = PacketSz}),
{Reply, Connection} = reply_msg(Channel, Connection0, {open, ChannelId}),
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index e952a333ff..7d055f0579 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -60,7 +60,8 @@
]).
%%% Behaviour callbacks
--export([handle_event/4, terminate/3, format_status/2, code_change/4]).
+-export([callback_mode/0, 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
@@ -374,14 +375,12 @@ init_connection_handler(Role, Socket, Opts) ->
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.
@@ -428,7 +427,12 @@ init_connection(server, C = #connection{}, Opts) ->
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),
+ AuthMethods = proplists:get_value(auth_methods,
+ Opts,
+ case Role of
+ server -> ?SUPPORTED_AUTH_METHODS;
+ client -> undefined
+ end),
S0 = #ssh{role = Role,
key_cb = KeyCb,
opts = Opts,
@@ -499,6 +503,9 @@ init_ssh_record(Role, Socket, Opts) ->
%%% ######## Error in the initialisation ####
+callback_mode() ->
+ handle_event_function.
+
handle_event(_, _Event, {init_error,Error}, _) ->
case Error of
{badmatch,{error,enotconn}} ->
@@ -518,7 +525,7 @@ handle_event(_, _Event, {init_error,Error}, _) ->
%% 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),
+ 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:
@@ -538,12 +545,13 @@ 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
+ %% RFC4253/4.2
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),
+ send_bytes("Protocol mismatch.", D),
{stop, {shutdown,"Protocol mismatch in version exchange. Client sent info lines."}}
end;
@@ -558,7 +566,7 @@ handle_event(_, {version_exchange,Version}, {hello,Role}, D) ->
{active, once},
{recbuf, D#data.inet_initial_recbuf_size}]),
{KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1),
- ok = send_bytes(SshPacket, D),
+ send_bytes(SshPacket, D),
{next_state, {kexinit,Role,init}, D#data{ssh_params = Ssh,
key_exchange_init_msg = KeyInitMsg}};
not_supported ->
@@ -576,7 +584,7 @@ handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg},
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),
+ send_bytes(NextKexMsg, D),
Ssh2;
{ok, Ssh2} when Role==server ->
Ssh2
@@ -589,43 +597,45 @@ handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg},
%%%---- 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),
+ send_bytes(KexdhReply, D),
{ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- ok = send_bytes(NewKeys, D),
+ 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),
+ 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),
+ {ok, GexGroup, Ssh1} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
+ send_bytes(GexGroup, D),
+ Ssh = ssh_transport:parallell_gen_key(Ssh1),
{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),
+ {ok, GexGroup, Ssh1} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
+ send_bytes(GexGroup, D),
+ Ssh = ssh_transport:parallell_gen_key(Ssh1),
{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),
+ 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),
+ send_bytes(KexEcdhReply, D),
{ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- ok = send_bytes(NewKeys, D),
+ send_bytes(NewKeys, D),
{next_state, {new_keys,server,ReNeg}, D#data{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),
+ send_bytes(NewKeys, D),
{next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
@@ -633,9 +643,9 @@ handle_event(_, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D)
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),
+ send_bytes(KexGexReply, D),
{ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- ok = send_bytes(NewKeys, D),
+ send_bytes(NewKeys, D),
{next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
@@ -643,7 +653,7 @@ handle_event(_, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,serv
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),
+ send_bytes(NewKeys, D),
{next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh1}};
@@ -655,7 +665,7 @@ handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,Role,init}, D) ->
Ssh = case Role of
client ->
{MsgReq, Ssh2} = ssh_auth:service_request_msg(Ssh1),
- ok = send_bytes(MsgReq, D),
+ send_bytes(MsgReq, D),
Ssh2;
server ->
Ssh1
@@ -663,8 +673,9 @@ handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,Role,init}, D) ->
{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};
+handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,Role,renegotiate}, D) ->
+ {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
+ {next_state, {connected,Role}, D#data{ssh_params=Ssh}};
%%% ######## {service_request, client|server}
@@ -673,7 +684,7 @@ handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {s
"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),
+ send_bytes(Reply, D),
{next_state, {userauth,server}, D#data{ssh_params = Ssh}};
_ ->
@@ -685,7 +696,7 @@ handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {s
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),
- ok = send_bytes(Msg, State),
+ send_bytes(Msg, State),
{next_state, {userauth,client}, State#data{auth_user = Ssh#ssh.user, ssh_params = Ssh}};
@@ -702,7 +713,7 @@ handle_event(_,
%% 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),
+ send_bytes(Reply, D),
{keep_state, D#data{ssh_params = Ssh}};
{"ssh-connection", "ssh-connection", Method} ->
@@ -712,7 +723,7 @@ handle_event(_,
%% 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),
+ send_bytes(Reply, D),
D#data.starter ! ssh_connected,
connected_fun(User, Method, D),
{next_state, {connected,server},
@@ -720,11 +731,11 @@ handle_event(_,
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),
+ 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),
+ send_bytes(Reply, D),
{keep_state, D#data{ssh_params = Ssh}}
end;
false ->
@@ -794,9 +805,13 @@ handle_event(_, #ssh_msg_userauth_banner{message = Msg}, {userauth,client}, D) -
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}};
+ case ssh_auth:handle_userauth_info_request(Msg, Ssh0) of
+ {ok, {Reply, Ssh}} ->
+ send_bytes(Reply, D),
+ {next_state, {userauth_keyboard_interactive_info_response,client}, D#data{ssh_params = Ssh}};
+ not_ok ->
+ {next_state, {userauth,client}, D, [{next_event, internal, Msg}]}
+ end;
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
@@ -809,9 +824,21 @@ handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_inte
{not_authorized, {User, Reason}, {Reply, Ssh}} ->
retry_fun(User, Reason, D),
send_bytes(Reply, D),
- {next_state, {userauth,server}, D#data{ssh_params = Ssh}}
+ {next_state, {userauth,server}, D#data{ssh_params = Ssh}};
+
+ {authorized_but_one_more, _User, {Reply, Ssh}} ->
+ send_bytes(Reply, D),
+ {next_state, {userauth_keyboard_interactive_extra,server}, D#data{ssh_params = Ssh}}
end;
+handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive_extra, server}, D) ->
+ {authorized, User, {Reply, Ssh}} = ssh_auth:handle_userauth_info_response({extra,Msg}, D#data.ssh_params),
+ 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}}};
+
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,
@@ -819,7 +846,18 @@ handle_event(_, Msg = #ssh_msg_userauth_failure{}, {userauth_keyboard_interactiv
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) ->
+handle_event(_, Msg=#ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client},
+ #data{ssh_params = Ssh0} = D0) ->
+ Opts = Ssh0#ssh.opts,
+ D = case proplists:get_value(password, Opts) of
+ undefined ->
+ D0;
+ _ ->
+ D0#data{ssh_params =
+ Ssh0#ssh{opts =
+ lists:keyreplace(password,1,Opts,
+ {password,not_ok})}} % FIXME:intermodule dependency
+ end,
{next_state, {userauth,client}, D, [{next_event, internal, Msg}]};
handle_event(_, Msg=#ssh_msg_userauth_success{}, {userauth_keyboard_interactive_info_response, client}, D) ->
@@ -892,6 +930,7 @@ 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) ->
+ update_inet_buffers(D#data.socket),
handle_connection_msg(Msg, StateName, D);
handle_event(internal, Msg=#ssh_msg_channel_failure{}, StateName, D) ->
@@ -971,6 +1010,7 @@ 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),
+ update_inet_buffers(D#data.socket),
{keep_state, send_msg(Msg,D)};
undefined ->
@@ -1006,13 +1046,13 @@ handle_event({call,From}, get_print_info, StateName, D) ->
{keep_state_and_data, [{reply,From,Reply}]};
handle_event({call,From}, {connection_info, Options}, _, D) ->
- Info = ssh_info(Options, D, []),
+ Info = fold_keys(Options, fun conn_info/2, D),
{keep_state_and_data, [{reply,From,Info}]};
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, []),
+ Info = fold_keys(Options, fun chann_info/2, Channel),
{keep_state_and_data, [{reply,From,Info}]};
undefined ->
{keep_state_and_data, [{reply,From,[]}]}
@@ -1158,17 +1198,17 @@ handle_event(info, {Proto, Sock, NewData}, StateName, D0 = #data{socket = Sock,
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}
+ {keep_state, D, [{next_event, internal, prepare_next_packet},
+ {next_event, internal, {Msg,DecryptedBytes}}
]};
Msg ->
- {keep_state, D, [{next_event, internal, Msg},
- {next_event, internal, prepare_next_packet}
+ {keep_state, D, [{next_event, internal, prepare_next_packet},
+ {next_event, internal, Msg}
]}
catch
_C:_E ->
disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Encountered unexpected input"},
+ description = "Bad packet"},
StateName, D)
end;
@@ -1183,13 +1223,12 @@ handle_event(info, {Proto, Sock, NewData}, StateName, D0 = #data{socket = Sock,
{bad_mac, Ssh1} ->
disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad mac"},
+ description = "Bad packet"},
StateName, D0#data{ssh_params=Ssh1});
- {error, {exceeds_max_size,PacketLen}} ->
+ {error, {exceeds_max_size,_PacketLen}} ->
disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad packet length "
- ++ integer_to_list(PacketLen)},
+ description = "Bad packet"},
StateName, D0)
catch
_C:_E ->
@@ -1206,16 +1245,20 @@ handle_event(internal, prepare_next_packet, _, D) ->
Sz when Sz >= Enough ->
self() ! {D#data.transport_protocol, D#data.socket, <<>>};
_ ->
- inet:setopts(D#data.socket, [{active, once}])
+ ok
end,
+ inet:setopts(D#data.socket, [{active, once}]),
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);
+ %% Simulate a disconnect from the peer
+ handle_event(info,
+ #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) ->
@@ -1237,8 +1280,21 @@ handle_event(info, {'DOWN', _Ref, process, ChannelPid, _Reason}, _, D0) ->
{keep_state, D, Repls};
%%% So that terminate will be run when supervisor is shutdown
-handle_event(info, {'EXIT', _Sup, Reason}, _, _) ->
- {stop, {shutdown, Reason}};
+handle_event(info, {'EXIT', _Sup, Reason}, StateName, _) ->
+ Role = role(StateName),
+ if
+ Role == client ->
+ %% OTP-8111 tells this function clause fixes a problem in
+ %% clients, but there were no check for that role.
+ {stop, {shutdown, Reason}};
+
+ Reason == normal ->
+ %% An exit normal should not cause a server to crash. This has happend...
+ keep_state_and_data;
+
+ true ->
+ {stop, {shutdown, Reason}}
+ end;
handle_event(info, check_cache, _, D) ->
{keep_state, cache_check_set_idle_timer(D)};
@@ -1315,12 +1371,10 @@ terminate(shutdown, StateName, State0) ->
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) ->
@@ -1382,12 +1436,12 @@ fmt_stat_rec(FieldNames, Rec, Exclude) ->
state_name(),
#data{},
term()
- ) -> {gen_statem:callback_mode(), state_name(), #data{}}.
+ ) -> {ok, state_name(), #data{}}.
%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
code_change(_OldVsn, StateName, State, _Extra) ->
- {handle_event_function, StateName, State}.
+ {ok, StateName, State}.
%%====================================================================
@@ -1434,7 +1488,7 @@ role({_,Role,_}) -> Role.
%%--------------------------------------------------------------------
%% Check the StateName to see if we are in the renegotiation phase
-renegotiation({_,_,ReNeg}) -> ReNeg == renegotiation;
+renegotiation({_,_,ReNeg}) -> ReNeg == renegotiate;
renegotiation(_) -> false.
%%--------------------------------------------------------------------
@@ -1476,7 +1530,8 @@ send_msg(Msg, State=#data{ssh_params=Ssh0}) when is_tuple(Msg) ->
State#data{ssh_params=Ssh}.
send_bytes(Bytes, #data{socket = Socket, transport_cb = Transport}) ->
- Transport:send(Socket, Bytes).
+ _ = Transport:send(Socket, Bytes),
+ ok.
handle_version({2, 0} = NumVsn, StrVsn, Ssh0) ->
Ssh = counterpart_versions(NumVsn, StrVsn, Ssh0),
@@ -1635,7 +1690,6 @@ new_channel_id(#data{connection_state = #connection{channel_id_seed = Id} =
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}.
%%%----------------------------------------------------------------
@@ -1644,43 +1698,43 @@ counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) ->
counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) ->
Ssh#ssh{s_vsn = NumVsn , s_version = StrVsn}.
-ssh_info([], _State, Acc) ->
- Acc;
-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], #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], #data{ssh_params = #ssh{peer = Peer}} = State, Acc) ->
- ssh_info(Rest, State, [{peer, Peer} | 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], #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;
+%%%----------------------------------------------------------------
+conn_info(client_version, #data{ssh_params=S}) -> {S#ssh.c_vsn, S#ssh.c_version};
+conn_info(server_version, #data{ssh_params=S}) -> {S#ssh.s_vsn, S#ssh.s_version};
+conn_info(peer, #data{ssh_params=S}) -> S#ssh.peer;
+conn_info(user, D) -> D#data.auth_user;
+conn_info(sockname, D) -> {ok, SockName} = inet:sockname(D#data.socket),
+ SockName;
+%% dbg options ( = not documented):
+conn_info(socket, D) -> D#data.socket;
+conn_info(chan_ids, D) ->
+ ssh_channel:cache_foldl(fun(#channel{local_id=Id}, Acc) ->
+ [Id | Acc]
+ end, [], cache(D)).
-ssh_channel_info([recv_window | Rest], #channel{recv_window_size = WinSize,
- recv_packet_size = Packsize
- } = Channel, Acc) ->
- ssh_channel_info(Rest, Channel, [{recv_window, {{win_size, WinSize},
- {packet_size, Packsize}}} | Acc]);
-ssh_channel_info([send_window | Rest], #channel{send_window_size = WinSize,
- send_packet_size = Packsize
- } = Channel, Acc) ->
- ssh_channel_info(Rest, Channel, [{send_window, {{win_size, WinSize},
- {packet_size, Packsize}}} | Acc]);
-ssh_channel_info([ _ | Rest], Channel, Acc) ->
- ssh_channel_info(Rest, Channel, Acc).
+%%%----------------------------------------------------------------
+chann_info(recv_window, C) ->
+ {{win_size, C#channel.recv_window_size},
+ {packet_size, C#channel.recv_packet_size}};
+chann_info(send_window, C) ->
+ {{win_size, C#channel.send_window_size},
+ {packet_size, C#channel.send_packet_size}};
+%% dbg options ( = not documented):
+chann_info(pid, C) ->
+ C#channel.user.
+%%%----------------------------------------------------------------
+%% Assisting meta function for the *_info functions
+fold_keys(Keys, Fun, Extra) ->
+ lists:foldr(fun(Key, Acc) ->
+ try Fun(Key, Extra) of
+ Value -> [{Key,Value}|Acc]
+ catch
+ _:_ -> Acc
+ end
+ end, [], Keys).
+%%%----------------------------------------------------------------
log_error(Reason) ->
Report = io_lib:format("Erlang ssh connection handler failed with reason:~n"
" ~p~n"
@@ -1689,7 +1743,6 @@ log_error(Reason) ->
[Reason, erlang:get_stacktrace()]),
error_logger:error_report(Report).
-
%%%----------------------------------------------------------------
not_connected_filter({connection_reply, _Data}) -> true;
not_connected_filter(_) -> false.
@@ -1701,6 +1754,11 @@ send_replies(Repls, State) ->
Repls).
get_repl({connection_reply,Msg}, {CallRepls,S}) ->
+ if is_record(Msg, ssh_msg_channel_success) ->
+ update_inet_buffers(S#data.socket);
+ true ->
+ ok
+ end,
{CallRepls, send_msg(Msg,S)};
get_repl({channel_data,undefined,_Data}, Acc) ->
Acc;
@@ -1889,3 +1947,13 @@ handshake(Pid, Ref, Timeout) ->
{error, timeout}
end.
+update_inet_buffers(Socket) ->
+ {ok, BufSzs0} = inet:getopts(Socket, [sndbuf,recbuf]),
+ MinVal = 655360,
+ case
+ [{Tag,MinVal} || {Tag,Val} <- BufSzs0,
+ Val < MinVal]
+ of
+ [] -> ok;
+ NewOpts -> inet:setopts(Socket, NewOpts)
+ end.
diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl
index 480795cfc7..251741da7e 100644
--- a/lib/ssh/src/ssh_dbg.erl
+++ b/lib/ssh/src/ssh_dbg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -24,9 +24,13 @@
-export([messages/0,
messages/1,
+ messages/2,
stop/0
]).
+-export([shrink_bin/1,
+ wr_record/3]).
+
-include("ssh.hrl").
-include("ssh_transport.hrl").
-include("ssh_connect.hrl").
@@ -36,45 +40,72 @@
writer,
acc = []}).
%%%================================================================
-messages() -> messages(fun(String,_D) -> io:format(String) end).
-%% messages() -> messages(fun(String,Acc) -> [String|Acc] end)
+messages() ->
+ messages(fun(String,_D) -> io:format(String) end).
messages(Write) when is_function(Write,2) ->
+ messages(Write, fun(X) -> X end).
+
+messages(Write, MangleArg) when is_function(Write,2),
+ is_function(MangleArg,1) ->
catch dbg:start(),
- setup_tracer(Write),
- dbg:p(new,c),
+ setup_tracer(Write, MangleArg),
+ dbg:p(new,[c,timestamp]),
dbg_ssh_messages().
dbg_ssh_messages() ->
dbg:tp(ssh_message,encode,1, x),
dbg:tp(ssh_message,decode,1, x),
- dbg:tpl(ssh_transport,select_algorithm,3, x).
-
+ dbg:tpl(ssh_transport,select_algorithm,3, x),
+ dbg:tp(ssh_transport,hello_version_msg,1, x),
+ dbg:tp(ssh_transport,handle_hello_version,1, x).
+
%%%----------------------------------------------------------------
stop() ->
dbg:stop().
%%%================================================================
-msg_formater({trace,Pid,call,{ssh_message,encode,[Msg]}}, D) ->
- fmt("~nSEND ~p ~s~n", [Pid,wr_record(shrink_bin(Msg))], D);
-msg_formater({trace,_Pid,return_from,{ssh_message,encode,1},_Res}, D) ->
+msg_formater({trace_ts,Pid,call,{ssh_message,encode,[Msg]},TS}, D) ->
+ fmt("~n~s SEND ~p ~s~n", [ts(TS),Pid,wr_record(shrink_bin(Msg))], D);
+msg_formater({trace_ts,_Pid,return_from,{ssh_message,encode,1},_Res,_TS}, D) ->
D;
-msg_formater({trace,_Pid,call,{ssh_message,decode,_}}, D) ->
+msg_formater({trace_ts,_Pid,call,{ssh_message,decode,_},_TS}, D) ->
D;
-msg_formater({trace,Pid,return_from,{ssh_message,decode,1},Msg}, D) ->
- fmt("~nRECV ~p ~s~n", [Pid,wr_record(shrink_bin(Msg))], D);
+msg_formater({trace_ts,Pid,return_from,{ssh_message,decode,1},Msg,TS}, D) ->
+ fmt("~n~s ~p RECV ~s~n", [ts(TS),Pid,wr_record(shrink_bin(Msg))], D);
-msg_formater({trace,_Pid,call,{ssh_transport,select_algorithm,_}}, D) ->
+msg_formater({trace_ts,_Pid,call,{ssh_transport,select_algorithm,_},_TS}, D) ->
+ D;
+msg_formater({trace_ts,Pid,return_from,{ssh_transport,select_algorithm,3},{ok,Alg},TS}, D) ->
+ fmt("~n~s ~p ALGORITHMS~n~s~n", [ts(TS),Pid, wr_record(Alg)], D);
+
+msg_formater({trace_ts,_Pid,call,{ssh_transport,hello_version_msg,_},_TS}, D) ->
+ D;
+msg_formater({trace_ts,Pid,return_from,{ssh_transport,hello_version_msg,1},Hello,TS}, D) ->
+ fmt("~n~s ~p TCP SEND HELLO~n ~p~n", [ts(TS),Pid,lists:flatten(Hello)], D);
+
+msg_formater({trace_ts,Pid,call,{ssh_transport,handle_hello_version,[Hello]},TS}, D) ->
+ fmt("~n~s ~p RECV HELLO~n ~p~n", [ts(TS),Pid,lists:flatten(Hello)], D);
+msg_formater({trace_ts,_Pid,return_from,{ssh_transport,handle_hello_version,1},_,_TS}, D) ->
D;
-msg_formater({trace,Pid,return_from,{ssh_transport,select_algorithm,3},{ok,Alg}}, D) ->
- fmt("~nALGORITHMS ~p~n~s~n", [Pid, wr_record(Alg)], D);
-msg_formater({trace,Pid,send,ErlangMsg,Dest}, D) ->
- fmt("~nERL MSG ~p SEND TO ~p~n ~p~n", [Pid,Dest, shrink_bin(ErlangMsg)], D);
+msg_formater({trace_ts,Pid,send,{tcp,Sock,Bytes},Pid,TS}, D) ->
+ fmt("~n~s ~p TCP SEND on ~p~n ~p~n", [ts(TS),Pid,Sock, shrink_bin(Bytes)], D);
+
+msg_formater({trace_ts,Pid,send,{tcp,Sock,Bytes},Dest,TS}, D) ->
+ fmt("~n~s ~p TCP SEND from ~p TO ~p~n ~p~n", [ts(TS),Pid,Sock,Dest, shrink_bin(Bytes)], D);
+
+msg_formater({trace_ts,Pid,send,ErlangMsg,Dest,TS}, D) ->
+ fmt("~n~s ~p ERL MSG SEND TO ~p~n ~p~n", [ts(TS),Pid,Dest, shrink_bin(ErlangMsg)], D);
+
+
+msg_formater({trace_ts,Pid,'receive',{tcp,Sock,Bytes},TS}, D) ->
+ fmt("~n~s ~p TCP RECEIVE on ~p~n ~p~n", [ts(TS),Pid,Sock,shrink_bin(Bytes)], D);
+
+msg_formater({trace_ts,Pid,'receive',ErlangMsg,TS}, D) ->
+ fmt("~n~s ~p ERL MSG RECEIVE~n ~p~n", [ts(TS),Pid,shrink_bin(ErlangMsg)], D);
-msg_formater({trace,Pid,'receive',ErlangMsg}, D) ->
- fmt("~nERL MSG ~p RECIEVE~n ~p~n", [Pid,shrink_bin(ErlangMsg)], D);
msg_formater(M, D) ->
fmt("~nDBG ~n~p~n", [shrink_bin(M)], D).
@@ -86,15 +117,27 @@ msg_formater(M, D) ->
fmt(Fmt, Args, D=#data{writer=Write,acc=Acc}) ->
D#data{acc = Write(io_lib:format(Fmt, Args), Acc)}.
+ts({_,_,Usec}=Now) ->
+ {_Date,{HH,MM,SS}} = calendar:now_to_local_time(Now),
+ io_lib:format("~.2.0w:~.2.0w:~.2.0w.~.6.0w",[HH,MM,SS,Usec]);
+ts(_) ->
+ "-".
%%%----------------------------------------------------------------
-setup_tracer(Write) ->
- Handler = fun msg_formater/2,
+setup_tracer(Write, MangleArg) ->
+ Handler = fun(Arg, D) ->
+ msg_formater(MangleArg(Arg), D)
+ end,
InitialData = #data{writer = Write},
{ok,_} = dbg:tracer(process, {Handler, InitialData}),
ok.
%%%----------------------------------------------------------------
-shrink_bin(B) when is_binary(B), size(B)>100 -> {'*** SHRINKED BIN',size(B),element(1,split_binary(B,20)),'***'};
+shrink_bin(B) when is_binary(B), size(B)>256 -> {'*** SHRINKED BIN',
+ size(B),
+ element(1,split_binary(B,64)),
+ '...',
+ element(2,split_binary(B,size(B)-64))
+ };
shrink_bin(L) when is_list(L) -> lists:map(fun shrink_bin/1, L);
shrink_bin(T) when is_tuple(T) -> list_to_tuple(shrink_bin(tuple_to_list(T)));
shrink_bin(X) -> X.
diff --git a/lib/ssh/src/ssh_dbg.hrl b/lib/ssh/src/ssh_dbg.hrl
new file mode 100644
index 0000000000..e94664737b
--- /dev/null
+++ b/lib/ssh/src/ssh_dbg.hrl
@@ -0,0 +1,27 @@
+%%
+%% %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%
+%%
+
+-ifndef(SSH_DBG_HRL).
+-define(SSH_DBG_HRL, 1).
+
+-define(formatrec(RecName,R),
+ ssh_dbg:wr_record(R, record_info(fields,RecName), [])).
+
+-endif. % SSH_DBG_HRL defined
diff --git a/lib/ssh/src/ssh_info.erl b/lib/ssh/src/ssh_info.erl
index 0c24c09887..d464def6fa 100644
--- a/lib/ssh/src/ssh_info.erl
+++ b/lib/ssh/src/ssh_info.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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.
diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl
index 026d0f6151..1d8f370884 100644
--- a/lib/ssh/src/ssh_io.erl
+++ b/lib/ssh/src/ssh_io.erl
@@ -31,56 +31,55 @@ read_line(Prompt, Ssh) ->
format("~s", [listify(Prompt)]),
proplists:get_value(user_pid, Ssh) ! {self(), question},
receive
- Answer ->
+ Answer when is_list(Answer) ->
Answer
end.
yes_no(Prompt, Ssh) ->
- io:format("~s [y/n]?", [Prompt]),
+ format("~s [y/n]?", [Prompt]),
proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), question},
receive
- Answer ->
+ %% I can't see that the atoms y and n are ever received, but it must
+ %% be investigated before removing
+ y -> yes;
+ n -> no;
+
+ Answer when is_list(Answer) ->
case trim(Answer) of
"y" -> yes;
"n" -> no;
"Y" -> yes;
"N" -> no;
- y -> yes;
- n -> no;
_ ->
- io:format("please answer y or n\n"),
+ format("please answer y or n\n",[]),
yes_no(Prompt, Ssh)
end
end.
-read_password(Prompt, Ssh) ->
+read_password(Prompt, #ssh{opts=Opts}) -> read_password(Prompt, Opts);
+read_password(Prompt, Opts) when is_list(Opts) ->
format("~s", [listify(Prompt)]),
- case is_list(Ssh) of
- false ->
- proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), user_password};
- _ ->
- proplists:get_value(user_pid, Ssh) ! {self(), user_password}
- end,
+ proplists:get_value(user_pid, Opts) ! {self(), user_password},
receive
- Answer ->
- case Answer of
- "" ->
- read_password(Prompt, Ssh);
- Pass -> Pass
- end
+ Answer when is_list(Answer) ->
+ case trim(Answer) of
+ "" ->
+ read_password(Prompt, Opts);
+ Pwd ->
+ Pwd
+ end
end.
-listify(A) when is_atom(A) ->
- atom_to_list(A);
-listify(L) when is_list(L) ->
- L;
-listify(B) when is_binary(B) ->
- binary_to_list(B).
format(Fmt, Args) ->
io:format(Fmt, Args).
+%%%================================================================
+listify(A) when is_atom(A) -> atom_to_list(A);
+listify(L) when is_list(L) -> L;
+listify(B) when is_binary(B) -> binary_to_list(B).
+
trim(Line) when is_list(Line) ->
lists:reverse(trim1(lists:reverse(trim1(Line))));
@@ -93,6 +92,3 @@ trim1([$\r|Cs]) -> trim(Cs);
trim1([$\n|Cs]) -> trim(Cs);
trim1([$\t|Cs]) -> trim(Cs);
trim1(Cs) -> Cs.
-
-
-
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index dca018f20f..8589730438 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2017. 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.
@@ -384,10 +384,12 @@ handle_op(?SSH_FXP_REMOVE, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>,
case IsDir of %% This version 6 we still have ver 5
true when Vsn > 5 ->
ssh_xfer:xf_send_status(State0#state.xf, ReqId,
- ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory");
+ ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory"),
+ State0;
true ->
ssh_xfer:xf_send_status(State0#state.xf, ReqId,
- ?SSH_FX_FAILURE, "File is a directory");
+ ?SSH_FX_FAILURE, "File is a directory"),
+ State0;
false ->
{Status, FS1} = FileMod:delete(Path, FS0),
State1 = State0#state{file_state = FS1},
@@ -664,29 +666,25 @@ open(Vsn, ReqId, Data, State) when Vsn >= 4 ->
do_open(ReqId, State, Path, Flags).
do_open(ReqId, State0, Path, Flags) ->
- #state{file_handler = FileMod, file_state = FS0, root = Root, xf = #ssh_xfer{vsn = Vsn}} = State0,
- XF = State0#state.xf,
- F = [binary | Flags],
- {IsDir, _FS1} = FileMod:is_dir(Path, FS0),
+ #state{file_handler = FileMod, file_state = FS0, xf = #ssh_xfer{vsn = Vsn}} = State0,
+ AbsPath = relate_file_name(Path, State0),
+ {IsDir, _FS1} = FileMod:is_dir(AbsPath, FS0),
case IsDir of
true when Vsn > 5 ->
ssh_xfer:xf_send_status(State0#state.xf, ReqId,
- ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory");
+ ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory"),
+ State0;
true ->
ssh_xfer:xf_send_status(State0#state.xf, ReqId,
- ?SSH_FX_FAILURE, "File is a directory");
+ ?SSH_FX_FAILURE, "File is a directory"),
+ State0;
false ->
- AbsPath = case Root of
- "" ->
- Path;
- _ ->
- relate_file_name(Path, State0)
- end,
- {Res, FS1} = FileMod:open(AbsPath, F, FS0),
+ OpenFlags = [binary | Flags],
+ {Res, FS1} = FileMod:open(AbsPath, OpenFlags, FS0),
State1 = State0#state{file_state = FS1},
case Res of
{ok, IoDevice} ->
- add_handle(State1, XF, ReqId, file, {Path,IoDevice});
+ add_handle(State1, State0#state.xf, ReqId, file, {Path,IoDevice});
{error, Error} ->
ssh_xfer:xf_send_status(State1#state.xf, ReqId,
ssh_xfer:encode_erlang_status(Error)),
@@ -742,6 +740,10 @@ resolve_symlinks_2([], State, _LinkCnt, AccPath) ->
{{ok, AccPath}, State}.
+%% The File argument is always in a user visible file system, i.e.
+%% is under Root and is relative to CWD or Root, if starts with "/".
+%% The result of the function is always an absolute path in a
+%% "backend" file system.
relate_file_name(File, State) ->
relate_file_name(File, State, _Canonicalize=true).
@@ -749,19 +751,20 @@ relate_file_name(File, State, Canonicalize) when is_binary(File) ->
relate_file_name(unicode:characters_to_list(File), State, Canonicalize);
relate_file_name(File, #state{cwd = CWD, root = ""}, Canonicalize) ->
relate_filename_to_path(File, CWD, Canonicalize);
-relate_file_name(File, #state{root = Root}, Canonicalize) ->
- case is_within_root(Root, File) of
- true ->
- File;
- false ->
- RelFile = make_relative_filename(File),
- NewFile = relate_filename_to_path(RelFile, Root, Canonicalize),
- case is_within_root(Root, NewFile) of
- true ->
- NewFile;
- false ->
- Root
- end
+relate_file_name(File, #state{cwd = CWD, root = Root}, Canonicalize) ->
+ CWD1 = case is_within_root(Root, CWD) of
+ true -> CWD;
+ false -> Root
+ end,
+ AbsFile = case make_relative_filename(File) of
+ File ->
+ relate_filename_to_path(File, CWD1, Canonicalize);
+ RelFile ->
+ relate_filename_to_path(RelFile, Root, Canonicalize)
+ end,
+ case is_within_root(Root, AbsFile) of
+ true -> AbsFile;
+ false -> Root
end.
is_within_root(Root, File) ->
diff --git a/lib/ssh/src/ssh_sftpd_file_api.erl b/lib/ssh/src/ssh_sftpd_file_api.erl
index 78f452df67..81f181f1fc 100644
--- a/lib/ssh/src/ssh_sftpd_file_api.erl
+++ b/lib/ssh/src/ssh_sftpd_file_api.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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,7 +36,7 @@
-callback list_dir(file:name(), State::term()) ->
{{ok, Filenames::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
-callback make_dir(Dir::term(), State::term()) ->
- {{ok, State::term()},State::term()} | {{error, Reason::term()}, State::term()}.
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
-callback make_symlink(Path2::term(), Path::term(), State::term()) ->
{ok, State::term()} | {{error, Reason::term()}, State::term()}.
-callback open(Path::term(), Flags::term(), State::term()) ->
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 7cb3b75ac0..a160309b95 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -44,9 +44,10 @@
handle_kexdh_reply/2,
handle_kex_ecdh_init/2,
handle_kex_ecdh_reply/2,
+ parallell_gen_key/1,
extract_public_key/1,
ssh_packet/2, pack/2,
- sign/3, verify/4]).
+ sha/1, sign/3, verify/4]).
%%% For test suites
-export([pack/3]).
@@ -296,9 +297,6 @@ handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
end.
-%% TODO: diffie-hellman-group14-sha1 should also be supported.
-%% Maybe check more things ...
-
verify_algorithm(#alg{kex = undefined}) -> false;
verify_algorithm(#alg{hkey = undefined}) -> false;
verify_algorithm(#alg{send_mac = undefined}) -> false;
@@ -316,17 +314,29 @@ verify_algorithm(#alg{kex = Kex}) -> lists:member(Kex, supported_algorithms(kex)
key_exchange_first_msg(Kex, Ssh0) when Kex == 'diffie-hellman-group1-sha1' ;
Kex == 'diffie-hellman-group14-sha1' ->
{G, P} = dh_group(Kex),
- {Public, Private} = generate_key(dh, [P,G]),
+ Sz = dh_bits(Ssh0#ssh.algorithms),
+ {Public, Private} = generate_key(dh, [P,G,2*Sz]),
{SshPacket, Ssh1} = ssh_packet(#ssh_msg_kexdh_init{e = Public}, Ssh0),
{ok, SshPacket,
Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}}};
key_exchange_first_msg(Kex, Ssh0=#ssh{opts=Opts}) when Kex == 'diffie-hellman-group-exchange-sha1' ;
Kex == 'diffie-hellman-group-exchange-sha256' ->
- {Min,NBits,Max} =
+ {Min,NBits0,Max} =
proplists:get_value(dh_gex_limits, Opts, {?DEFAULT_DH_GROUP_MIN,
?DEFAULT_DH_GROUP_NBITS,
?DEFAULT_DH_GROUP_MAX}),
+ DhBits = dh_bits(Ssh0#ssh.algorithms),
+ NBits1 =
+ %% NIST Special Publication 800-57 Part 1 Revision 4: Recommendation for Key Management
+ if
+ DhBits =< 112 -> 2048;
+ DhBits =< 128 -> 3072;
+ DhBits =< 192 -> 7680;
+ true -> 8192
+ end,
+ NBits = min(max(max(NBits0,NBits1),Min), Max),
+
{SshPacket, Ssh1} =
ssh_packet(#ssh_msg_kex_dh_gex_request{min = Min,
n = NBits,
@@ -350,12 +360,13 @@ key_exchange_first_msg(Kex, Ssh0) when Kex == 'ecdh-sha2-nistp256' ;
%%% diffie-hellman-group14-sha1
%%%
handle_kexdh_init(#ssh_msg_kexdh_init{e = E},
- Ssh0 = #ssh{algorithms = #alg{kex=Kex}}) ->
+ Ssh0 = #ssh{algorithms = #alg{kex=Kex} = Algs}) ->
%% server
{G, P} = dh_group(Kex),
if
1=<E, E=<(P-1) ->
- {Public, Private} = generate_key(dh, [P,G]),
+ Sz = dh_bits(Algs),
+ {Public, Private} = generate_key(dh, [P,G,2*Sz]),
K = compute_key(dh, E, Private, [P,G]),
MyPrivHostKey = get_host_key(Ssh0),
MyPubHostKey = extract_public_key(MyPrivHostKey),
@@ -426,13 +437,12 @@ handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request{min = Min0,
{Min, Max} = adjust_gex_min_max(Min0, Max0, Opts),
case public_key:dh_gex_group(Min, NBits, Max,
proplists:get_value(dh_gex_groups,Opts)) of
- {ok, {_Sz, {G,P}}} ->
- {Public, Private} = generate_key(dh, [P,G]),
+ {ok, {_, {G,P}}} ->
{SshPacket, Ssh} =
ssh_packet(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0),
{ok, SshPacket,
- Ssh#ssh{keyex_key = {{Private, Public}, {G, P}},
- keyex_info = {Min, Max, NBits}
+ Ssh#ssh{keyex_key = {x, {G, P}},
+ keyex_info = {Min0, Max0, NBits}
}};
{error,_} ->
ssh_connection_handler:disconnect(
@@ -461,12 +471,11 @@ handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request_old{n = NBits},
{Min, Max} = adjust_gex_min_max(Min0, Max0, Opts),
case public_key:dh_gex_group(Min, NBits, Max,
proplists:get_value(dh_gex_groups,Opts)) of
- {ok, {_Sz, {G,P}}} ->
- {Public, Private} = generate_key(dh, [P,G]),
+ {ok, {_, {G,P}}} ->
{SshPacket, Ssh} =
ssh_packet(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0),
{ok, SshPacket,
- Ssh#ssh{keyex_key = {{Private, Public}, {G, P}},
+ Ssh#ssh{keyex_key = {x, {G, P}},
keyex_info = {-1, -1, NBits} % flag for kex_h hash calc
}};
{error,_} ->
@@ -507,7 +516,8 @@ adjust_gex_min_max(Min0, Max0, Opts) ->
handle_kex_dh_gex_group(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0) ->
%% client
- {Public, Private} = generate_key(dh, [P,G]),
+ Sz = dh_bits(Ssh0#ssh.algorithms),
+ {Public, Private} = generate_key(dh, [P,G,2*Sz]),
{SshPacket, Ssh1} =
ssh_packet(#ssh_msg_kex_dh_gex_init{e = Public}, Ssh0), % Pub = G^Priv mod P (def)
@@ -734,12 +744,16 @@ public_algo({#'ECPoint'{},{namedCurve,OID}}) ->
list_to_atom("ecdsa-sha2-" ++ binary_to_list(Curve)).
-accepted_host(Ssh, PeerName, Opts) ->
+accepted_host(Ssh, PeerName, Public, Opts) ->
case proplists:get_value(silently_accept_hosts, Opts, false) of
+ F when is_function(F,2) ->
+ true == (catch F(PeerName, public_key:ssh_hostkey_fingerprint(Public)));
+ {DigestAlg,F} when is_function(F,2) ->
+ true == (catch F(PeerName, public_key:ssh_hostkey_fingerprint(DigestAlg,Public)));
true ->
- yes;
+ true;
false ->
- yes_no(Ssh, "New host " ++ PeerName ++ " accept")
+ yes == yes_no(Ssh, "New host " ++ PeerName ++ " accept")
end.
known_host_key(#ssh{opts = Opts, key_cb = Mod, peer = Peer} = Ssh,
@@ -749,10 +763,10 @@ known_host_key(#ssh{opts = Opts, key_cb = Mod, peer = Peer} = Ssh,
true ->
ok;
false ->
- case accepted_host(Ssh, PeerName, Opts) of
- yes ->
+ case accepted_host(Ssh, PeerName, Public, Opts) of
+ true ->
Mod:add_host_key(PeerName, Public, Opts);
- no ->
+ false ->
{error, rejected}
end
end.
@@ -1113,6 +1127,51 @@ verify(PlainText, Hash, Sig, Key) ->
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Unit: bytes
+
+-record(cipher_data, {
+ key_bytes,
+ iv_bytes,
+ block_bytes
+ }).
+
+%%% Start of a more parameterized crypto handling.
+cipher('AEAD_AES_128_GCM') ->
+ #cipher_data{key_bytes = 16,
+ iv_bytes = 12,
+ block_bytes = 16};
+
+cipher('AEAD_AES_256_GCM') ->
+ #cipher_data{key_bytes = 32,
+ iv_bytes = 12,
+ block_bytes = 16};
+
+cipher('3des-cbc') ->
+ #cipher_data{key_bytes = 24,
+ iv_bytes = 8,
+ block_bytes = 8};
+
+cipher('aes128-cbc') ->
+ #cipher_data{key_bytes = 16,
+ iv_bytes = 16,
+ block_bytes = 16};
+
+cipher('aes128-ctr') ->
+ #cipher_data{key_bytes = 16,
+ iv_bytes = 16,
+ block_bytes = 16};
+
+cipher('aes192-ctr') ->
+ #cipher_data{key_bytes = 24,
+ iv_bytes = 16,
+ block_bytes = 16};
+
+cipher('aes256-ctr') ->
+ #cipher_data{key_bytes = 32,
+ iv_bytes = 16,
+ block_bytes = 16}.
+
+
encrypt_init(#ssh{encrypt = none} = Ssh) ->
{ok, Ssh};
encrypt_init(#ssh{encrypt = 'AEAD_AES_128_GCM', role = client} = Ssh) ->
@@ -1493,11 +1552,11 @@ send_mac_init(SSH) ->
common ->
case SSH#ssh.role of
client ->
- KeySize = mac_key_size(SSH#ssh.send_mac),
+ KeySize = 8*mac_key_bytes(SSH#ssh.send_mac),
Key = hash(SSH, "E", KeySize),
{ok, SSH#ssh { send_mac_key = Key }};
server ->
- KeySize = mac_key_size(SSH#ssh.send_mac),
+ KeySize = 8*mac_key_bytes(SSH#ssh.send_mac),
Key = hash(SSH, "F", KeySize),
{ok, SSH#ssh { send_mac_key = Key }}
end;
@@ -1516,10 +1575,10 @@ recv_mac_init(SSH) ->
common ->
case SSH#ssh.role of
client ->
- Key = hash(SSH, "F", mac_key_size(SSH#ssh.recv_mac)),
+ Key = hash(SSH, "F", 8*mac_key_bytes(SSH#ssh.recv_mac)),
{ok, SSH#ssh { recv_mac_key = Key }};
server ->
- Key = hash(SSH, "E", mac_key_size(SSH#ssh.recv_mac)),
+ Key = hash(SSH, "E", 8*mac_key_bytes(SSH#ssh.recv_mac)),
{ok, SSH#ssh { recv_mac_key = Key }}
end;
aead ->
@@ -1604,13 +1663,13 @@ kex_h(SSH, Curve, Key, Q_c, Q_s, K) ->
kex_h(SSH, Key, Min, NBits, Max, Prime, Gen, E, F, K) ->
KeyBin = public_key:ssh_encode(Key, ssh2_pubkey),
L = if Min==-1; Max==-1 ->
- %% flag from 'ssh_msg_kex_dh_gex_request_old'
- %% It was like this before that message was supported,
- %% why?
+ %% ssh_msg_kex_dh_gex_request_old
<<?Estring(SSH#ssh.c_version), ?Estring(SSH#ssh.s_version),
?Ebinary(SSH#ssh.c_keyinit), ?Ebinary(SSH#ssh.s_keyinit), ?Ebinary(KeyBin),
- ?Empint(E), ?Empint(F), ?Empint(K)>>;
+ ?Euint32(NBits),
+ ?Empint(Prime), ?Empint(Gen), ?Empint(E), ?Empint(F), ?Empint(K)>>;
true ->
+ %% ssh_msg_kex_dh_gex_request
<<?Estring(SSH#ssh.c_version), ?Estring(SSH#ssh.s_version),
?Ebinary(SSH#ssh.c_keyinit), ?Ebinary(SSH#ssh.s_keyinit), ?Ebinary(KeyBin),
?Euint32(Min), ?Euint32(NBits), ?Euint32(Max),
@@ -1619,6 +1678,11 @@ kex_h(SSH, Key, Min, NBits, Max, Prime, Gen, E, F, K) ->
crypto:hash(sha((SSH#ssh.algorithms)#alg.kex), L).
+sha('ssh-rsa') -> sha;
+sha('ssh-dss') -> sha;
+sha('ecdsa-sha2-nistp256') -> sha(secp256r1);
+sha('ecdsa-sha2-nistp384') -> sha(secp384r1);
+sha('ecdsa-sha2-nistp521') -> sha(secp521r1);
sha(secp256r1) -> sha256;
sha(secp384r1) -> sha384;
sha(secp521r1) -> sha512;
@@ -1631,13 +1695,15 @@ sha(?'secp384r1') -> sha(secp384r1);
sha(?'secp521r1') -> sha(secp521r1).
-mac_key_size('hmac-sha1') -> 20*8;
-mac_key_size('hmac-sha1-96') -> 20*8;
-mac_key_size('hmac-md5') -> 16*8;
-mac_key_size('hmac-md5-96') -> 16*8;
-mac_key_size('hmac-sha2-256')-> 32*8;
-mac_key_size('hmac-sha2-512')-> 512;
-mac_key_size(none) -> 0.
+mac_key_bytes('hmac-sha1') -> 20;
+mac_key_bytes('hmac-sha1-96') -> 20;
+mac_key_bytes('hmac-md5') -> 16;
+mac_key_bytes('hmac-md5-96') -> 16;
+mac_key_bytes('hmac-sha2-256')-> 32;
+mac_key_bytes('hmac-sha2-512')-> 64;
+mac_key_bytes('AEAD_AES_128_GCM') -> 0;
+mac_key_bytes('AEAD_AES_256_GCM') -> 0;
+mac_key_bytes(none) -> 0.
mac_digest_size('hmac-sha1') -> 20;
mac_digest_size('hmac-sha1-96') -> 12;
@@ -1662,6 +1728,13 @@ dh_group('diffie-hellman-group1-sha1') -> ?dh_group1;
dh_group('diffie-hellman-group14-sha1') -> ?dh_group14.
%%%----------------------------------------------------------------
+parallell_gen_key(Ssh = #ssh{keyex_key = {x, {G, P}},
+ algorithms = Algs}) ->
+ Sz = dh_bits(Algs),
+ {Public, Private} = generate_key(dh, [P,G,2*Sz]),
+ Ssh#ssh{keyex_key = {{Private, Public}, {G, P}}}.
+
+
generate_key(Algorithm, Args) ->
{Public,Private} = crypto:generate_key(Algorithm, Args),
{crypto:bytes_to_integer(Public), crypto:bytes_to_integer(Private)}.
@@ -1672,6 +1745,15 @@ compute_key(Algorithm, OthersPublic, MyPrivate, Args) ->
crypto:bytes_to_integer(Shared).
+dh_bits(#alg{encrypt = Encrypt,
+ send_mac = SendMac}) ->
+ C = cipher(Encrypt),
+ 8 * lists:max([C#cipher_data.key_bytes,
+ C#cipher_data.block_bytes,
+ C#cipher_data.iv_bytes,
+ mac_key_bytes(SendMac)
+ ]).
+
ecdh_curve('ecdh-sha2-nistp256') -> secp256r1;
ecdh_curve('ecdh-sha2-nistp384') -> secp384r1;
ecdh_curve('ecdh-sha2-nistp521') -> secp521r1.
@@ -1745,12 +1827,6 @@ same(Algs) -> [{client2server,Algs}, {server2client,Algs}].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
trim_tail(Str) ->
- lists:reverse(trim_head(lists:reverse(Str))).
-
-trim_head([$\s|Cs]) -> trim_head(Cs);
-trim_head([$\t|Cs]) -> trim_head(Cs);
-trim_head([$\n|Cs]) -> trim_head(Cs);
-trim_head([$\r|Cs]) -> trim_head(Cs);
-trim_head(Cs) -> Cs.
-
-
+ lists:takewhile(fun(C) ->
+ C=/=$\r andalso C=/=$\n
+ end, Str).
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 6ce6d6f537..3fca78237c 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -52,7 +52,8 @@ MODULES= \
ssh_echo_server \
ssh_peername_sockname_server \
ssh_test_cli \
- ssh_relay
+ ssh_relay \
+ ssh_eqc_event_handler
HRL_FILES_NEEDED_IN_TEST= \
$(ERL_TOP)/lib/ssh/test/ssh_test_lib.hrl \
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl
new file mode 100644
index 0000000000..c07140dc43
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl
@@ -0,0 +1,92 @@
+%%
+%% %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(ssh_eqc_client_info_timing).
+
+-compile(export_all).
+
+-proptest(eqc).
+-proptest([triq,proper]).
+
+-ifndef(EQC).
+-ifndef(PROPER).
+-ifndef(TRIQ).
+-define(EQC,true).
+%%-define(PROPER,true).
+%%-define(TRIQ,true).
+-endif.
+-endif.
+-endif.
+
+-ifdef(EQC).
+-include_lib("eqc/include/eqc.hrl").
+-define(MOD_eqc,eqc).
+
+-else.
+-ifdef(PROPER).
+-include_lib("proper/include/proper.hrl").
+-define(MOD_eqc,proper).
+
+-else.
+-ifdef(TRIQ).
+-define(MOD_eqc,triq).
+-include_lib("triq/include/triq.hrl").
+
+-endif.
+-endif.
+-endif.
+
+
+%%% Properties:
+
+prop_seq(_Config) ->
+ {ok,Pid} = ssh_eqc_event_handler:add_report_handler(),
+ {_, _, Port} = init_daemon(),
+ numtests(1000,
+ ?FORALL(Delay, choose(0,100),%% Micro seconds
+ try
+ send_bad_sequence(Port, Delay, Pid),
+ not any_relevant_error_report(Pid)
+ catch
+ C:E -> io:format('~p:~p~n',[C,E]),
+ false
+ end
+ )).
+
+send_bad_sequence(Port, Delay, Pid) ->
+ {ok,S} = gen_tcp:connect("localhost",Port,[]),
+ gen_tcp:send(S,"Illegal info-string\r\n"),
+ ssh_test_lib:sleep_microsec(Delay),
+ gen_tcp:close(S).
+
+any_relevant_error_report(Pid) ->
+ {ok, Reports} = ssh_eqc_event_handler:get_reports(Pid),
+ lists:any(fun({error_report,_,{_,supervisor_report,L}}) when is_list(L) ->
+ lists:member({reason,{badmatch,{error,closed}}}, L);
+ (_) ->
+ false
+ end, Reports).
+
+%%%================================================================
+init_daemon() ->
+ ok = begin ssh:stop(), ssh:start() end,
+ ssh_test_lib:daemon([]).
+
diff --git a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
index dc3b7dc7e6..410a9ea983 100644
--- a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
+++ b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -54,15 +54,18 @@
-endif.
-endif.
+%% Public key records:
+-include_lib("public_key/include/public_key.hrl").
%%% Properties:
prop_ssh_decode() ->
- ?FORALL(Msg, ssh_msg(),
- try ssh_message:decode(Msg)
+ ?FORALL({Msg,KexFam}, ?LET(KF, kex_family(), {ssh_msg(KF),KF} ),
+ try ssh_message:decode(decode_state(Msg,KexFam))
of
_ -> true
catch
+
C:E -> io:format('~p:~p~n',[C,E]),
false
end
@@ -71,122 +74,101 @@ prop_ssh_decode() ->
%%% This fails because ssh_message is not symmetric in encode and decode regarding data types
prop_ssh_decode_encode() ->
- ?FORALL(Msg, ssh_msg(),
- Msg == ssh_message:encode(ssh_message:decode(Msg))
+ ?FORALL({Msg,KexFam}, ?LET(KF, kex_family(), {ssh_msg(KF),KF} ),
+ Msg == ssh_message:encode(
+ fix_asym(
+ ssh_message:decode(decode_state(Msg,KexFam))))
).
%%%================================================================
%%%
-%%% Scripts to generate message generators
-%%%
-
-%% awk '/^( |\t)+byte( |\t)+SSH/,/^( |\t)*$/{print}' rfc425?.txt | sed 's/^\( \|\\t\)*//' > msgs.txt
-
-%% awk '/^byte( |\t)+SSH/{print $2","}' < msgs.txt
-
-%% awk 'BEGIN{print "%%%---- BEGIN GENERATED";prev=0} END{print " >>.\n%%%---- END GENERATED"} /^byte( |\t)+SSH/{if (prev==1) print " >>.\n"; prev=1; printf "%c%s%c",39,$2,39; print "()->\n <<?"$2;next} /^string( |\t)+\"/{print " ,"$2;next} /^string( |\t)+.*address/{print " ,(ssh_string_address())/binary %%",$2,$3,$4,$5,$6;next}/^string( |\t)+.*US-ASCII/{print " ,(ssh_string_US_ASCII())/binary %%",$2,$3,$4,$5,$6;next} /^string( |\t)+.*UTF-8/{print " ,(ssh_string_UTF_8())/binary %% ",$2,$3,$4,$5,$6;next} /^[a-z0-9]+( |\t)/{print " ,(ssh_"$1"())/binary %%",$2,$3,$4,$5,$6;next} /^byte\[16\]( |\t)+/{print" ,(ssh_byte_16())/binary %%",$2,$3,$4,$5,$6;next} /^name-list( |\t)+/{print" ,(ssh_name_list())/binary %%",$2,$3,$4,$5,$6;next} /./{print "?? %%",$0}' < msgs.txt > gen.txt
-
-%%%================================================================
-%%%
%%% Generators
%%%
-ssh_msg() -> ?LET(M,oneof(
-[[msg_code('SSH_MSG_CHANNEL_CLOSE'),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_DATA'),gen_uint32(),gen_string( )],
- [msg_code('SSH_MSG_CHANNEL_EOF'),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_EXTENDED_DATA'),gen_uint32(),gen_uint32(),gen_string( )],
- [msg_code('SSH_MSG_CHANNEL_FAILURE'),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("direct-tcpip"),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( ),gen_uint32(),gen_string( ),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("forwarded-tcpip"),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( ),gen_uint32(),gen_string( ),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("session"),gen_uint32(),gen_uint32(),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("x11"),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( ),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string( ),gen_uint32(),gen_uint32(),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_OPEN_CONFIRMATION'),gen_uint32(),gen_uint32(),gen_uint32(),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_OPEN_FAILURE'),gen_uint32(),gen_uint32(),gen_string( ),gen_string( )],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("env"),gen_boolean(),gen_string( ),gen_string( )],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("exec"),gen_boolean(),gen_string( )],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("exit-signal"),0,gen_string( ),gen_boolean(),gen_string( ),gen_string( )],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("exit-status"),0,gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("pty-req"),gen_boolean(),gen_string( ),gen_uint32(),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( )],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("shell"),gen_boolean()],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("signal"),0,gen_string( )],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("subsystem"),gen_boolean(),gen_string( )],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("window-change"),0,gen_uint32(),gen_uint32(),gen_uint32(),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("x11-req"),gen_boolean(),gen_boolean(),gen_string( ),gen_string( ),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("xon-xoff"),0,gen_boolean()],
- [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string( ),gen_boolean()],
- [msg_code('SSH_MSG_CHANNEL_SUCCESS'),gen_uint32()],
- [msg_code('SSH_MSG_CHANNEL_WINDOW_ADJUST'),gen_uint32(),gen_uint32()],
-%%Assym [msg_code('SSH_MSG_DEBUG'),gen_boolean(),gen_string( ),gen_string( )],
- [msg_code('SSH_MSG_DISCONNECT'),gen_uint32(),gen_string( ),gen_string( )],
-%%Assym [msg_code('SSH_MSG_GLOBAL_REQUEST'),gen_string("cancel-tcpip-forward"),gen_boolean(),gen_string( ),gen_uint32()],
-%%Assym [msg_code('SSH_MSG_GLOBAL_REQUEST'),gen_string("tcpip-forward"),gen_boolean(),gen_string( ),gen_uint32()],
-%%Assym [msg_code('SSH_MSG_GLOBAL_REQUEST'),gen_string( ),gen_boolean()],
- [msg_code('SSH_MSG_IGNORE'),gen_string( )],
- %% [msg_code('SSH_MSG_KEXDH_INIT'),gen_mpint()],
- %% [msg_code('SSH_MSG_KEXDH_REPLY'),gen_string( ),gen_mpint(),gen_string( )],
- %% [msg_code('SSH_MSG_KEXINIT'),gen_byte(16),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_boolean(),gen_uint32()],
- [msg_code('SSH_MSG_KEX_DH_GEX_GROUP'),gen_mpint(),gen_mpint()],
- [msg_code('SSH_MSG_NEWKEYS')],
- [msg_code('SSH_MSG_REQUEST_FAILURE')],
- [msg_code('SSH_MSG_REQUEST_SUCCESS')],
- [msg_code('SSH_MSG_REQUEST_SUCCESS'),gen_uint32()],
- [msg_code('SSH_MSG_SERVICE_ACCEPT'),gen_string( )],
- [msg_code('SSH_MSG_SERVICE_REQUEST'),gen_string( )],
- [msg_code('SSH_MSG_UNIMPLEMENTED'),gen_uint32()],
- [msg_code('SSH_MSG_USERAUTH_BANNER'),gen_string( ),gen_string( )],
- [msg_code('SSH_MSG_USERAUTH_FAILURE'),gen_name_list(),gen_boolean()],
- [msg_code('SSH_MSG_USERAUTH_PASSWD_CHANGEREQ'),gen_string( ),gen_string( )],
- [msg_code('SSH_MSG_USERAUTH_PK_OK'),gen_string( ),gen_string( )],
- [msg_code('SSH_MSG_USERAUTH_SUCCESS')]
-]
-
-), list_to_binary(M)).
-
-
-%%%================================================================
-%%%
-%%% Generator
-%%%
-
-do() ->
- io_lib:format('[~s~n]',
- [write_gen(
- files(["rfc4254.txt",
- "rfc4253.txt",
- "rfc4419.txt",
- "rfc4252.txt",
- "rfc4256.txt"]))]).
-
-
-write_gen(L) when is_list(L) ->
- string:join(lists:map(fun write_gen/1, L), ",\n ");
-write_gen({MsgName,Args}) ->
- lists:flatten(["[",generate_args([MsgName|Args]),"]"]).
-
-generate_args(As) -> string:join([generate_arg(A) || A <- As], ",").
-
-generate_arg({<<"string">>, <<"\"",B/binary>>}) ->
- S = get_string($",B),
- ["gen_string(\"",S,"\")"];
-generate_arg({<<"string">>, _}) -> "gen_string( )";
-generate_arg({<<"byte[",B/binary>>, _}) ->
- io_lib:format("gen_byte(~p)",[list_to_integer(get_string($],B))]);
-generate_arg({<<"byte">> ,_}) -> "gen_byte()";
-generate_arg({<<"uint16">>,_}) -> "gen_uint16()";
-generate_arg({<<"uint32">>,_}) -> "gen_uint32()";
-generate_arg({<<"uint64">>,_}) -> "gen_uint64()";
-generate_arg({<<"mpint">>,_}) -> "gen_mpint()";
-generate_arg({<<"name-list">>,_}) -> "gen_name_list()";
-generate_arg({<<"boolean">>,<<"FALSE">>}) -> "0";
-generate_arg({<<"boolean">>,<<"TRUE">>}) -> "1";
-generate_arg({<<"boolean">>,_}) -> "gen_boolean()";
-generate_arg({<<"....">>,_}) -> ""; %% FIXME
-generate_arg(Name) when is_binary(Name) ->
- lists:flatten(["msg_code('",binary_to_list(Name),"')"]).
-
+ssh_msg(<<"dh">>) ->
+ ?LET(M,oneof(
+ [
+ [msg_code('SSH_MSG_KEXDH_INIT'),gen_mpint()], % 30
+ [msg_code('SSH_MSG_KEXDH_REPLY'),gen_pubkey_string(rsa),gen_mpint(),gen_signature_string(rsa)] % 31
+ | rest_ssh_msgs()
+ ]),
+ list_to_binary(M));
+
+ssh_msg(<<"dh_gex">>) ->
+ ?LET(M,oneof(
+ [
+ [msg_code('SSH_MSG_KEX_DH_GEX_REQUEST_OLD'),gen_uint32()], % 30
+ [msg_code('SSH_MSG_KEX_DH_GEX_GROUP'),gen_mpint(),gen_mpint()] % 31
+ | rest_ssh_msgs()
+ ]),
+ list_to_binary(M));
+
+ ssh_msg(<<"ecdh">>) ->
+ ?LET(M,oneof(
+ [
+ [msg_code('SSH_MSG_KEX_ECDH_INIT'),gen_mpint()], % 30
+ [msg_code('SSH_MSG_KEX_ECDH_REPLY'),gen_pubkey_string(ecdsa),gen_mpint(),gen_signature_string(ecdsa)] % 31
+ | rest_ssh_msgs()
+ ]),
+ list_to_binary(M)).
+
+
+rest_ssh_msgs() ->
+ [%% SSH_MSG_USERAUTH_INFO_RESPONSE
+ %% hard args SSH_MSG_USERAUTH_INFO_REQUEST
+ %% rfc4252 p12 error SSH_MSG_USERAUTH_REQUEST
+ [msg_code('SSH_MSG_KEX_DH_GEX_REQUEST'),gen_uint32(),gen_uint32(),gen_uint32()],
+ [msg_code('SSH_MSG_KEX_DH_GEX_INIT'),gen_mpint()],
+ [msg_code('SSH_MSG_KEX_DH_GEX_REPLY'),gen_pubkey_string(rsa),gen_mpint(),gen_signature_string(rsa)],
+ [msg_code('SSH_MSG_CHANNEL_CLOSE'),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_DATA'),gen_uint32(),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_EOF'),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_EXTENDED_DATA'),gen_uint32(),gen_uint32(),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_FAILURE'),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("direct-tcpip"),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( ),gen_uint32(),gen_string( ),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("forwarded-tcpip"),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( ),gen_uint32(),gen_string( ),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("session"),gen_uint32(),gen_uint32(),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("x11"),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( ),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string( ),gen_uint32(),gen_uint32(),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN_CONFIRMATION'),gen_uint32(),gen_uint32(),gen_uint32(),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN_FAILURE'),gen_uint32(),gen_uint32(),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("env"),gen_boolean(),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("exec"),gen_boolean(),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("exit-signal"),0,gen_string( ),gen_boolean(),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("exit-status"),0,gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("pty-req"),gen_boolean(),gen_string( ),gen_uint32(),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("shell"),gen_boolean()],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("signal"),0,gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("subsystem"),gen_boolean(),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("window-change"),0,gen_uint32(),gen_uint32(),gen_uint32(),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("x11-req"),gen_boolean(),gen_boolean(),gen_string( ),gen_string( ),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("xon-xoff"),0,gen_boolean()],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string( ),gen_boolean()],
+ [msg_code('SSH_MSG_CHANNEL_SUCCESS'),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_WINDOW_ADJUST'),gen_uint32(),gen_uint32()],
+ [msg_code('SSH_MSG_DEBUG'),gen_boolean(),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_DISCONNECT'),gen_uint32(),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_GLOBAL_REQUEST'),gen_string("cancel-tcpip-forward"),gen_boolean(),gen_string( ),gen_uint32()],
+ [msg_code('SSH_MSG_GLOBAL_REQUEST'),gen_string("tcpip-forward"),gen_boolean(),gen_string( ),gen_uint32()],
+ [msg_code('SSH_MSG_GLOBAL_REQUEST'),gen_string( ),gen_boolean()],
+ [msg_code('SSH_MSG_IGNORE'),gen_string( )],
+ [msg_code('SSH_MSG_KEXINIT'),gen_byte(16),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_boolean(),gen_uint32()],
+ [msg_code('SSH_MSG_NEWKEYS')],
+ [msg_code('SSH_MSG_REQUEST_FAILURE')],
+ [msg_code('SSH_MSG_REQUEST_SUCCESS')],
+ [msg_code('SSH_MSG_REQUEST_SUCCESS'),gen_uint32()],
+ [msg_code('SSH_MSG_SERVICE_ACCEPT'),gen_string( )],
+ [msg_code('SSH_MSG_SERVICE_REQUEST'),gen_string( )],
+ [msg_code('SSH_MSG_UNIMPLEMENTED'),gen_uint32()],
+ [msg_code('SSH_MSG_USERAUTH_BANNER'),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_USERAUTH_FAILURE'),gen_name_list(),gen_boolean()],
+ [msg_code('SSH_MSG_USERAUTH_PASSWD_CHANGEREQ'),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_USERAUTH_PK_OK'),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_USERAUTH_SUCCESS')]
+ ].
+
+kex_family() -> oneof([<<"dh">>, <<"dh_gex">>, <<"ecdh">>]).
gen_boolean() -> choose(0,1).
@@ -202,10 +184,7 @@ gen_byte(N) when N>0 -> [gen_byte() || _ <- lists:seq(1,N)].
gen_char() -> choose($a,$z).
-gen_mpint() -> ?LET(Size, choose(1,20),
- ?LET(Str, vector(Size, gen_byte()),
- gen_string( strip_0s(Str) )
- )).
+gen_mpint() -> ?LET(I, largeint(), ssh_bits:mpint(I)).
strip_0s([0|T]) -> strip_0s(T);
strip_0s(X) -> X.
@@ -230,13 +209,22 @@ gen_name() -> gen_string().
uint32_to_list(I) -> binary_to_list(<<I:32/unsigned-big-integer>>).
-%%%----
-get_string(Delim, B) ->
- binary_to_list( element(1, split_binary(B, count_string_chars(Delim,B,0))) ).
-
-count_string_chars(Delim, <<Delim,_/binary>>, Acc) -> Acc;
-count_string_chars(Delim, <<_,B/binary>>, Acc) -> count_string_chars(Delim, B, Acc+1).
+gen_pubkey_string(Type) ->
+ PubKey = case Type of
+ rsa -> #'RSAPublicKey'{modulus = 12345,publicExponent = 2};
+ ecdsa -> {#'ECPoint'{point=[1,2,3,4,5]},
+ {namedCurve,{1,2,840,10045,3,1,7}}} % 'secp256r1' nistp256
+ end,
+ gen_string(public_key:ssh_encode(PubKey, ssh2_pubkey)).
+
+gen_signature_string(Type) ->
+ Signature = <<"hejhopp">>,
+ Id = case Type of
+ rsa -> "ssh-rsa";
+ ecdsa -> "ecdsa-sha2-nistp256"
+ end,
+ gen_string(gen_string(Id) ++ gen_string(Signature)).
-define(MSG_CODE(Name,Num),
msg_code(Name) -> Num;
@@ -273,124 +261,34 @@ msg_code(Num) -> Name
?MSG_CODE('SSH_MSG_CHANNEL_FAILURE', 100);
?MSG_CODE('SSH_MSG_USERAUTH_INFO_REQUEST', 60);
?MSG_CODE('SSH_MSG_USERAUTH_INFO_RESPONSE', 61);
+?MSG_CODE('SSH_MSG_KEXDH_INIT', 30);
+?MSG_CODE('SSH_MSG_KEXDH_REPLY', 31);
?MSG_CODE('SSH_MSG_KEX_DH_GEX_REQUEST_OLD', 30);
?MSG_CODE('SSH_MSG_KEX_DH_GEX_REQUEST', 34);
?MSG_CODE('SSH_MSG_KEX_DH_GEX_GROUP', 31);
?MSG_CODE('SSH_MSG_KEX_DH_GEX_INIT', 32);
-?MSG_CODE('SSH_MSG_KEX_DH_GEX_REPLY', 33).
-
-%%%=============================================================================
-%%%=============================================================================
-%%%=============================================================================
-
-files(Fs) ->
- Defs = lists:usort(lists:flatten(lists:map(fun file/1, Fs))),
- DefinedIDs = lists:usort([binary_to_list(element(1,D)) || D <- Defs]),
- WantedIDs = lists:usort(wanted_messages()),
- Missing = WantedIDs -- DefinedIDs,
- case Missing of
- [] -> ok;
- _ -> io:format('%% Warning: missing ~p~n', [Missing])
- end,
- Defs.
-
-
-file(F) ->
- {ok,B} = file:read_file(F),
- hunt_msg_def(B).
-
-
-hunt_msg_def(<<"\n",B/binary>>) -> some_hope(skip_blanks(B));
-hunt_msg_def(<<_, B/binary>>) -> hunt_msg_def(B);
-hunt_msg_def(<<>>) -> [].
-
-some_hope(<<"byte ", B/binary>>) -> try_message(skip_blanks(B));
-some_hope(B) -> hunt_msg_def(B).
-
-try_message(B = <<"SSH_MSG_",_/binary>>) ->
- {ID,Rest} = get_id(B),
- case lists:member(binary_to_list(ID), wanted_messages()) of
- true ->
- {Lines,More} = get_def_lines(skip_blanks(Rest), []),
- [{ID,lists:reverse(Lines)} | hunt_msg_def(More)];
- false ->
- hunt_msg_def(Rest)
- end;
-try_message(B) -> hunt_msg_def(B).
-
-
-skip_blanks(<<32, B/binary>>) -> skip_blanks(B);
-skip_blanks(<< 9, B/binary>>) -> skip_blanks(B);
-skip_blanks(B) -> B.
-
-get_def_lines(B0 = <<"\n",B/binary>>, Acc) ->
- {ID,Rest} = get_id(skip_blanks(B)),
- case {size(ID), skip_blanks(Rest)} of
- {0,<<"....",More/binary>>} ->
- {Text,LineEnd} = get_to_eol(skip_blanks(More)),
- get_def_lines(LineEnd, [{<<"....">>,Text}|Acc]);
- {0,_} ->
- {Acc,B0};
- {_,Rest1} ->
- {Text,LineEnd} = get_to_eol(Rest1),
- get_def_lines(LineEnd, [{ID,Text}|Acc])
- end;
-get_def_lines(B, Acc) ->
- {Acc,B}.
-
-
-get_to_eol(B) -> split_binary(B, count_to_eol(B,0)).
-
-count_to_eol(<<"\n",_/binary>>, Acc) -> Acc;
-count_to_eol(<<>>, Acc) -> Acc;
-count_to_eol(<<_,B/binary>>, Acc) -> count_to_eol(B,Acc+1).
-
-
-get_id(B) -> split_binary(B, count_id_chars(B,0)).
-
-count_id_chars(<<C,B/binary>>, Acc) when $A=<C,C=<$Z -> count_id_chars(B,Acc+1);
-count_id_chars(<<C,B/binary>>, Acc) when $a=<C,C=<$z -> count_id_chars(B,Acc+1);
-count_id_chars(<<C,B/binary>>, Acc) when $0=<C,C=<$9 -> count_id_chars(B,Acc+1);
-count_id_chars(<<"_",B/binary>>, Acc) -> count_id_chars(B,Acc+1);
-count_id_chars(<<"-",B/binary>>, Acc) -> count_id_chars(B,Acc+1); %% e.g name-list
-count_id_chars(<<"[",B/binary>>, Acc) -> count_id_chars(B,Acc+1); %% e.g byte[16]
-count_id_chars(<<"]",B/binary>>, Acc) -> count_id_chars(B,Acc+1); %% e.g byte[16]
-count_id_chars(_, Acc) -> Acc.
-
-wanted_messages() ->
- ["SSH_MSG_CHANNEL_CLOSE",
- "SSH_MSG_CHANNEL_DATA",
- "SSH_MSG_CHANNEL_EOF",
- "SSH_MSG_CHANNEL_EXTENDED_DATA",
- "SSH_MSG_CHANNEL_FAILURE",
- "SSH_MSG_CHANNEL_OPEN",
- "SSH_MSG_CHANNEL_OPEN_CONFIRMATION",
- "SSH_MSG_CHANNEL_OPEN_FAILURE",
- "SSH_MSG_CHANNEL_REQUEST",
- "SSH_MSG_CHANNEL_SUCCESS",
- "SSH_MSG_CHANNEL_WINDOW_ADJUST",
- "SSH_MSG_DEBUG",
- "SSH_MSG_DISCONNECT",
- "SSH_MSG_GLOBAL_REQUEST",
- "SSH_MSG_IGNORE",
- "SSH_MSG_KEXDH_INIT",
- "SSH_MSG_KEXDH_REPLY",
- "SSH_MSG_KEXINIT",
- "SSH_MSG_KEX_DH_GEX_GROUP",
- "SSH_MSG_KEX_DH_GEX_REQUEST",
- "SSH_MSG_KEX_DH_GEX_REQUEST_OLD",
- "SSH_MSG_NEWKEYS",
- "SSH_MSG_REQUEST_FAILURE",
- "SSH_MSG_REQUEST_SUCCESS",
- "SSH_MSG_SERVICE_ACCEPT",
- "SSH_MSG_SERVICE_REQUEST",
- "SSH_MSG_UNIMPLEMENTED",
- "SSH_MSG_USERAUTH_BANNER",
- "SSH_MSG_USERAUTH_FAILURE",
-%% hard args "SSH_MSG_USERAUTH_INFO_REQUEST",
-%% "SSH_MSG_USERAUTH_INFO_RESPONSE",
- "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ",
- "SSH_MSG_USERAUTH_PK_OK",
-%%rfc4252 p12 error "SSH_MSG_USERAUTH_REQUEST",
- "SSH_MSG_USERAUTH_SUCCESS"].
+?MSG_CODE('SSH_MSG_KEX_DH_GEX_REPLY', 33);
+?MSG_CODE('SSH_MSG_KEX_ECDH_INIT', 30);
+?MSG_CODE('SSH_MSG_KEX_ECDH_REPLY', 31).
+
+%%%====================================================
+%%%=== WARNING: Knowledge of the test object ahead! ===
+%%%====================================================
+
+%% SSH message records:
+-include_lib("ssh/src/ssh_connect.hrl").
+-include_lib("ssh/src/ssh_transport.hrl").
+
+%%% Encoding and decodeing is asymetric so out=binary in=string. Sometimes. :(
+fix_asym(#ssh_msg_global_request{name=N} = M) -> M#ssh_msg_global_request{name = binary_to_list(N)};
+fix_asym(#ssh_msg_debug{message=D,language=L} = M) -> M#ssh_msg_debug{message = binary_to_list(D),
+ language = binary_to_list(L)};
+fix_asym(#ssh_msg_kexinit{cookie=C} = M) -> M#ssh_msg_kexinit{cookie = <<C:128>>};
+fix_asym(M) -> M.
+
+%%% Message codes 30 and 31 are overloaded depending on kex family so arrange the decoder
+%%% input as the test object does
+decode_state(<<30,_/binary>>=Msg, KexFam) -> <<KexFam/binary, Msg/binary>>;
+decode_state(<<31,_/binary>>=Msg, KexFam) -> <<KexFam/binary, Msg/binary>>;
+decode_state(Msg, _) -> Msg.
diff --git a/lib/ssh/test/ssh.cover b/lib/ssh/test/ssh.cover
index a4221fbbbe..69d2a1c4f8 100644
--- a/lib/ssh/test/ssh.cover
+++ b/lib/ssh/test/ssh.cover
@@ -1,2 +1,3 @@
{incl_app,ssh,details}.
+{excl_mods, ssh, [ssh_dbg, ssh_info, ssh_server_key_api, ssh_sftpd_file_api]}. \ No newline at end of file
diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl
index 0f68130a05..28a013ea33 100644
--- a/lib/ssh/test/ssh_algorithms_SUITE.erl
+++ b/lib/ssh/test/ssh_algorithms_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -58,9 +58,11 @@ groups() ->
|| {Tag,Algs} <- ErlAlgos,
lists:member(Tag,tags())
],
+
+ TypeSSH = ssh_test_lib:ssh_type(),
AlgoTcSet =
- [{Alg, [parallel], specific_test_cases(Tag,Alg,SshcAlgos,SshdAlgos)}
+ [{Alg, [parallel], specific_test_cases(Tag,Alg,SshcAlgos,SshdAlgos,TypeSSH)}
|| {Tag,Algs} <- ErlAlgos ++ DoubleAlgos,
Alg <- Algs],
@@ -180,38 +182,35 @@ simple_exec(Config) ->
%%--------------------------------------------------------------------
%% Testing if no group matches
simple_exec_groups_no_match_too_small(Config) ->
- try simple_exec_group({400,500,600}, Config)
- of
- _ -> ct:fail("Exec though no group available")
- catch
- error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} ->
- ok
- end.
+ try_exec_simple_group({400,500,600}, Config).
simple_exec_groups_no_match_too_large(Config) ->
- try simple_exec_group({9200,9500,9700}, Config)
+ try_exec_simple_group({9200,9500,9700}, Config).
+
+
+try_exec_simple_group(Group, Config) ->
+ try simple_exec_group(Group, Config)
of
_ -> ct:fail("Exec though no group available")
catch
- error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} ->
- ok
+ error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} -> ok;
+ error:{badmatch,{error,"Connection closed"}} -> ok
end.
%%--------------------------------------------------------------------
%% Testing all default groups
-simple_exec_groups() -> [{timetrap,{minutes,5}}].
-
+simple_exec_groups() ->
+ [{timetrap,{seconds,120}}].
+
simple_exec_groups(Config) ->
Sizes = interpolate( public_key:dh_gex_group_sizes() ),
lists:foreach(
fun(Sz) ->
ct:log("Try size ~p",[Sz]),
ct:comment(Sz),
- case simple_exec_group(Sz, Config) of
- expected -> ct:log("Size ~p ok",[Sz]);
- _ -> ct:log("Size ~p not ok",[Sz])
- end
+ simple_exec_group(Sz, Config),
+ ct:log("Size ~p ok",[Sz])
end, Sizes),
ct:comment("~p",[lists:map(fun({_,I,_}) -> I;
(I) -> I
@@ -319,18 +318,13 @@ concat(A1, A2) -> list_to_atom(lists:concat([A1," + ",A2])).
split(Alg) -> ssh_test_lib:to_atoms(string:tokens(atom_to_list(Alg), " + ")).
-specific_test_cases(Tag, Alg, SshcAlgos, SshdAlgos) ->
+specific_test_cases(Tag, Alg, SshcAlgos, SshdAlgos, TypeSSH) ->
[simple_exec, simple_sftp] ++
case supports(Tag, Alg, SshcAlgos) of
- true ->
- case ssh_test_lib:ssh_type() of
- openSSH ->
- [sshc_simple_exec_os_cmd];
- _ ->
- []
- end;
- false ->
- []
+ true when TypeSSH == openSSH ->
+ [sshc_simple_exec_os_cmd];
+ _ ->
+ []
end ++
case supports(Tag, Alg, SshdAlgos) of
true ->
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 733414e23a..0a0ab5cdf7 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -50,7 +50,12 @@
inet6_option/1,
inet_option/1,
internal_error/1,
- known_hosts/1,
+ known_hosts/1,
+ login_bad_pwd_no_retry1/1,
+ login_bad_pwd_no_retry2/1,
+ login_bad_pwd_no_retry3/1,
+ login_bad_pwd_no_retry4/1,
+ login_bad_pwd_no_retry5/1,
misc_ssh_options/1,
openssh_zlib_basic_test/1,
packet_size_zero/1,
@@ -62,7 +67,8 @@
shell_unicode_string/1,
ssh_info_print/1,
key_callback/1,
- key_callback_options/1
+ key_callback_options/1,
+ shell_exit_status/1
]).
%%% Common test callbacks
@@ -100,7 +106,9 @@ all() ->
daemon_opt_fd,
multi_daemon_opt_fd,
packet_size_zero,
- ssh_info_print
+ ssh_info_print,
+ {group, login_bad_pwd_no_retry},
+ shell_exit_status
].
groups() ->
@@ -116,7 +124,13 @@ groups() ->
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
{key_cb, [], [key_callback, key_callback_options]},
- {internal_error, [], [internal_error]}
+ {internal_error, [], [internal_error]},
+ {login_bad_pwd_no_retry, [], [login_bad_pwd_no_retry1,
+ login_bad_pwd_no_retry2,
+ login_bad_pwd_no_retry3,
+ login_bad_pwd_no_retry4,
+ login_bad_pwd_no_retry5
+ ]}
].
@@ -301,9 +315,9 @@ init_per_testcase(TC, Config) when TC==shell_no_unicode ;
{user_passwords, [{"foo", "bar"}]}]),
ct:sleep(500),
IO = ssh_test_lib:start_io_server(),
- Shell = ssh_test_lib:start_shell(Port, IO, UserDir,
- [{silently_accept_hosts, true},
- {user,"foo"},{password,"bar"}]),
+ Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir},
+ {silently_accept_hosts, true},
+ {user,"foo"},{password,"bar"}]),
ct:log("IO=~p, Shell=~p, self()=~p",[IO,Shell,self()]),
ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p",
[file:native_name_encoding(),io:getopts()]),
@@ -329,14 +343,15 @@ end_per_testcase(TC, Config) when TC==shell_no_unicode ;
TC==shell_unicode_string ->
case proplists:get_value(sftpd, Config) of
{Pid, _, _} ->
- ssh:stop_daemon(Pid),
- ssh:stop();
+ catch ssh:stop_daemon(Pid);
_ ->
- ssh:stop()
- end;
+ ok
+ end,
+ end_per_testcase(Config);
end_per_testcase(_TestCase, Config) ->
end_per_testcase(Config).
-end_per_testcase(_Config) ->
+
+end_per_testcase(_Config) ->
ssh:stop(),
ok.
@@ -510,7 +525,7 @@ shell(Config) when is_list(Config) ->
ct:sleep(500),
IO = ssh_test_lib:start_io_server(),
- Shell = ssh_test_lib:start_shell(Port, IO, UserDir),
+ Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}]),
receive
{'EXIT', _, _} ->
ct:fail(no_ssh_connection);
@@ -548,10 +563,10 @@ exec_key_differs(Config, UserPKAlgs) ->
ct:sleep(500),
IO = ssh_test_lib:start_io_server(),
- Shell = ssh_test_lib:start_shell(Port, IO, UserDir,
- [{preferred_algorithms,[{public_key,['ssh-rsa']}]},
- {pref_public_key_algs,UserPKAlgs}
- ]),
+ Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir},
+ {preferred_algorithms,[{public_key,['ssh-rsa']}]},
+ {pref_public_key_algs,UserPKAlgs}
+ ]),
receive
@@ -582,9 +597,9 @@ exec_key_differs_fail(Config) when is_list(Config) ->
ct:sleep(500),
IO = ssh_test_lib:start_io_server(),
- ssh_test_lib:start_shell(Port, IO, UserDir,
- [{preferred_algorithms,[{public_key,['ssh-rsa']}]},
- {pref_public_key_algs,['ssh-dss']}]),
+ ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir},
+ {preferred_algorithms,[{public_key,['ssh-rsa']}]},
+ {pref_public_key_algs,['ssh-dss']}]),
receive
{'EXIT', _, _} ->
ok;
@@ -1090,6 +1105,99 @@ ssh_info_print(Config) ->
%%--------------------------------------------------------------------
+%% Check that a basd pwd is not tried more times. Could cause lock-out
+%% on server
+
+login_bad_pwd_no_retry1(Config) ->
+ login_bad_pwd_no_retry(Config, "keyboard-interactive,password").
+
+login_bad_pwd_no_retry2(Config) ->
+ login_bad_pwd_no_retry(Config, "password,keyboard-interactive").
+
+login_bad_pwd_no_retry3(Config) ->
+ login_bad_pwd_no_retry(Config, "password,publickey,keyboard-interactive").
+
+login_bad_pwd_no_retry4(Config) ->
+ login_bad_pwd_no_retry(Config, "password,other,keyboard-interactive").
+
+login_bad_pwd_no_retry5(Config) ->
+ login_bad_pwd_no_retry(Config, "password,other,keyboard-interactive,password,password").
+
+
+
+
+
+login_bad_pwd_no_retry(Config, AuthMethods) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+
+ Parent = self(),
+ PwdFun = fun(_, _, _, undefined) -> {false, 1};
+ (_, _, _, _) -> Parent ! retry_bad_pwd,
+ false
+ end,
+
+ {DaemonRef, _Host, Port} =
+ ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {auth_methods, AuthMethods},
+ {user_passwords, [{"foo","somepwd"}]},
+ {pwdfun, PwdFun}
+ ]),
+
+ ConnRes = ssh:connect("localhost", Port,
+ [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "badpwd"},
+ {user_dir, UserDir},
+ {user_interaction, false}]),
+
+ receive
+ retry_bad_pwd ->
+ ssh:stop_daemon(DaemonRef),
+ {fail, "Retry bad password"}
+ after 0 ->
+ case ConnRes of
+ {error,"Unable to connect using the available authentication methods"} ->
+ ssh:stop_daemon(DaemonRef),
+ ok;
+ {ok,Conn} ->
+ ssh:close(Conn),
+ ssh:stop_daemon(DaemonRef),
+ {fail, "Connect erroneosly succeded"}
+ end
+ end.
+
+
+%%----------------------------------------------------------------------------
+%%% Test that when shell REPL exit with reason normal client receives status 0
+shell_exit_status(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = proplists:get_value(data_dir, Config),
+ UserDir = proplists:get_value(priv_dir, Config),
+
+ ShellFun = fun (_User) -> spawn(fun() -> ok end) end,
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"vego", "morot"}]},
+ {shell, ShellFun},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false}]),
+
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:shell(ConnectionRef, ChannelId),
+ ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
+ ssh:stop_daemon(Pid).
+
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
%% Due to timing the error message may or may not be delivered to
diff --git a/lib/ssh/test/ssh_benchmark_SUITE.erl b/lib/ssh/test/ssh_benchmark_SUITE.erl
index c2bfc48449..f3ed6a5b9b 100644
--- a/lib/ssh/test/ssh_benchmark_SUITE.erl
+++ b/lib/ssh/test/ssh_benchmark_SUITE.erl
@@ -1,7 +1,7 @@
%%%-------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2017. 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.
@@ -30,7 +30,7 @@
suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]},
- {timetrap,{minutes,3}}
+ {timetrap,{minutes,6}}
].
%%suite() -> [{ct_hooks,[ts_install_cth]}].
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index bcf3b01824..952fb8da99 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -45,6 +45,8 @@ all() ->
{group, openssh},
small_interrupted_send,
interrupted_send,
+ exec_erlang_term,
+ exec_erlang_term_non_default_shell,
start_shell,
start_shell_exec,
start_shell_exec_fun,
@@ -381,13 +383,13 @@ do_interrupted_send(Config, SendSize, EchoSize) ->
{password, "morot"},
{subsystems, [{"echo_n",EchoSS_spec}]}]),
- ct:log("connect", []),
+ ct:log("~p:~p connect", [?MODULE,?LINE]),
ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user, "foo"},
{password, "morot"},
{user_interaction, false},
{user_dir, UserDir}]),
- ct:log("connected", []),
+ ct:log("~p:~p connected", [?MODULE,?LINE]),
%% build big binary
Data = << <<X:32>> || X <- lists:seq(1,SendSize div 4)>>,
@@ -399,58 +401,80 @@ do_interrupted_send(Config, SendSize, EchoSize) ->
Parent = self(),
ResultPid = spawn(
fun() ->
- ct:log("open channel",[]),
+ ct:log("~p:~p open channel",[?MODULE,?LINE]),
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
- ct:log("start subsystem", []),
+ ct:log("~p:~p start subsystem", [?MODULE,?LINE]),
case ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity) of
success ->
Parent ! {self(), channelId, ChannelId},
Result =
- try collect_data(ConnectionRef, ChannelId)
+ try collect_data(ConnectionRef, ChannelId, EchoSize)
of
ExpectedData ->
+ ct:log("~p:~p got expected data",[?MODULE,?LINE]),
ok;
- _ ->
- {fail,"unexpected result"}
+ Other ->
+ ct:log("~p:~p unexpect: ~p", [?MODULE,?LINE,Other]),
+ {fail,"unexpected result in listener"}
catch
Class:Exception ->
- {fail, io_lib:format("Exception ~p:~p",[Class,Exception])}
+ {fail, io_lib:format("Listener exception ~p:~p",[Class,Exception])}
end,
- Parent ! {self(), Result};
+ Parent ! {self(), result, Result};
Other ->
Parent ! {self(), channelId, error, Other}
end
end),
receive
+ {ResultPid, channelId, error, Other} ->
+ ct:log("~p:~p channelId error ~p", [?MODULE,?LINE,Other]),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ {fail, "ssh_connection:subsystem"};
+
{ResultPid, channelId, ChannelId} ->
- %% pre-adjust receive window so the other end doesn't block
- ct:log("adjust window", []),
- ssh_connection:adjust_window(ConnectionRef, ChannelId, size(ExpectedData) + 1),
-
- ct:log("going to send ~p bytes", [size(Data)]),
- case ssh_connection:send(ConnectionRef, ChannelId, Data, 30000) of
- {error, closed} ->
- ct:log("{error,closed} - That's what we expect :)", []),
- ok;
- Msg ->
- ct:log("Got ~p - that's bad, very bad indeed",[Msg]),
- ct:fail({expected,{error,closed}, got, Msg})
- end,
- ct:log("going to check the result (if it is available)", []),
+ ct:log("~p:~p ~p going to send ~p bytes", [?MODULE,?LINE,self(),size(Data)]),
+ SenderPid = spawn(fun() ->
+ Parent ! {self(), ssh_connection:send(ConnectionRef, ChannelId, Data, 30000)}
+ end),
receive
- {ResultPid, Result} ->
- ct:log("Got result: ~p", [Result]),
+ {ResultPid, result, {fail, Fail}} ->
+ ct:log("~p:~p Listener failed: ~p", [?MODULE,?LINE,Fail]),
+ {fail, Fail};
+
+ {ResultPid, result, Result} ->
+ ct:log("~p:~p Got result: ~p", [?MODULE,?LINE,Result]),
ssh:close(ConnectionRef),
ssh:stop_daemon(Pid),
- Result
- end;
+ ct:log("~p:~p Check sender", [?MODULE,?LINE]),
+ receive
+ {SenderPid, {error, closed}} ->
+ ct:log("~p:~p {error,closed} - That's what we expect :)",[?MODULE,?LINE]),
+ ok;
+ Msg ->
+ ct:log("~p:~p Not expected send result: ~p",[?MODULE,?LINE,Msg]),
+ {fail, "Not expected msg"}
+ end;
+
+ {SenderPid, {error, closed}} ->
+ ct:log("~p:~p {error,closed} - That's what we expect, but client channel handler has not reported yet",[?MODULE,?LINE]),
+ receive
+ {ResultPid, result, Result} ->
+ ct:log("~p:~p Now got the result: ~p", [?MODULE,?LINE,Result]),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ ok;
+ Msg ->
+ ct:log("~p:~p Got an unexpected msg ~p",[?MODULE,?LINE,Msg]),
+ {fail, "Un-expected msg"}
+ end;
- {ResultPid, channelId, error, Other} ->
- ssh:close(ConnectionRef),
- ssh:stop_daemon(Pid),
- {fail, io_lib:format("ssh_connection:subsystem: ~p",[Other])}
+ Msg ->
+ ct:log("~p:~p Got unexpected ~p",[?MODULE,?LINE,Msg]),
+ {fail, "Unexpected msg"}
+ end
end.
%%--------------------------------------------------------------------
@@ -520,6 +544,79 @@ start_shell_exec(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+exec_erlang_term(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"}
+ ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got expected ~p",[R]);
+ Other ->
+ ct:log("Got unexpected ~p",[Other])
+ after 5000 ->
+ {fail,"Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
+
+%%--------------------------------------------------------------------
+exec_erlang_term_non_default_shell(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {shell, fun(U, H) -> start_our_shell(U, H) end}
+ ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}
+ ]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got unexpected ~p",[R]),
+ {fail,"Could exec erlang term although non-erlang shell"};
+ Other ->
+ ct:log("Got expected ~p",[Other])
+ after 5000 ->
+ {fail, "Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
+
+%%--------------------------------------------------------------------
start_shell_exec_fun() ->
[{doc, "start shell to exec command"}].
@@ -909,36 +1006,46 @@ big_cat_rx(ConnectionRef, ChannelId, Acc) ->
timeout
end.
-collect_data(ConnectionRef, ChannelId) ->
- ct:log("Listener ~p running! ConnectionRef=~p, ChannelId=~p",[self(),ConnectionRef,ChannelId]),
- collect_data(ConnectionRef, ChannelId, [], 0).
+collect_data(ConnectionRef, ChannelId, EchoSize) ->
+ ct:log("~p:~p Listener ~p running! ConnectionRef=~p, ChannelId=~p",[?MODULE,?LINE,self(),ConnectionRef,ChannelId]),
+ collect_data(ConnectionRef, ChannelId, EchoSize, [], 0).
-collect_data(ConnectionRef, ChannelId, Acc, Sum) ->
+collect_data(ConnectionRef, ChannelId, EchoSize, Acc, Sum) ->
TO = 5000,
receive
{ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} when is_binary(Data) ->
- ct:log("collect_data: received ~p bytes. total ~p bytes",[size(Data),Sum+size(Data)]),
- collect_data(ConnectionRef, ChannelId, [Data | Acc], Sum+size(Data));
- {ssh_cm, ConnectionRef, {eof, ChannelId}} ->
- try
- iolist_to_binary(lists:reverse(Acc))
- of
- Bin ->
- ct:log("collect_data: received eof.~nGot in total ~p bytes",[size(Bin)]),
- Bin
- catch
- C:E ->
- ct:log("collect_data: received eof.~nAcc is strange...~nException=~p:~p~nAcc=~p",
- [C,E,Acc]),
- {error,{C,E}}
- end;
+ ct:log("~p:~p collect_data: received ~p bytes. total ~p bytes, want ~p more",
+ [?MODULE,?LINE,size(Data),Sum+size(Data),EchoSize-Sum]),
+ ssh_connection:adjust_window(ConnectionRef, ChannelId, size(Data)),
+ collect_data(ConnectionRef, ChannelId, EchoSize, [Data | Acc], Sum+size(Data));
+ {ssh_cm, ConnectionRef, Msg={eof, ChannelId}} ->
+ collect_data_report_end(Acc, Msg, EchoSize);
+
+ {ssh_cm, ConnectionRef, Msg={closed,ChannelId}} ->
+ collect_data_report_end(Acc, Msg, EchoSize);
+
Msg ->
- ct:log("collect_data: ***** unexpected message *****~n~p",[Msg]),
- collect_data(ConnectionRef, ChannelId, Acc, Sum)
+ ct:log("~p:~p collect_data: ***** unexpected message *****~n~p",[?MODULE,?LINE,Msg]),
+ collect_data(ConnectionRef, ChannelId, EchoSize, Acc, Sum)
after TO ->
- ct:log("collect_data: ----- Nothing received for ~p seconds -----~n",[]),
- collect_data(ConnectionRef, ChannelId, Acc, Sum)
+ ct:log("~p:~p collect_data: ----- Nothing received for ~p seconds -----~n",[?MODULE,?LINE,TO]),
+ collect_data(ConnectionRef, ChannelId, EchoSize, Acc, Sum)
+ end.
+
+collect_data_report_end(Acc, Msg, EchoSize) ->
+ try
+ iolist_to_binary(lists:reverse(Acc))
+ of
+ Bin ->
+ ct:log("~p:~p collect_data: received ~p.~nGot in total ~p bytes, want ~p more",
+ [?MODULE,?LINE,Msg,size(Bin),EchoSize,size(Bin)]),
+ Bin
+ catch
+ C:E ->
+ ct:log("~p:~p collect_data: received ~p.~nAcc is strange...~nException=~p:~p~nAcc=~p",
+ [?MODULE,?LINE,Msg,C,E,Acc]),
+ {error,{C,E}}
end.
%%%-------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_eqc_event_handler.erl b/lib/ssh/test/ssh_eqc_event_handler.erl
new file mode 100644
index 0000000000..233965012a
--- /dev/null
+++ b/lib/ssh/test/ssh_eqc_event_handler.erl
@@ -0,0 +1,43 @@
+-module(ssh_eqc_event_handler).
+
+-compile(export_all).
+
+-behaviour(gen_event).
+
+add_report_handler() ->
+ error_logger:add_report_handler(?MODULE, [self(),Ref=make_ref()]),
+ receive
+ {event_handler_started,HandlerPid,Ref} ->
+ {ok,HandlerPid}
+ end.
+
+get_reports(Pid) ->
+ Pid ! {get_reports,self(),Ref=make_ref()},
+ receive
+ {reports,Reports,Ref} ->
+ {ok,Reports}
+ end.
+
+%%%================================================================
+
+-record(state, {
+ reports = []
+ }).
+
+%% error_logger:add_report_handler(ssh_eqc_event_handler, [self()]).
+
+init([CallerPid,Ref]) ->
+ CallerPid ! {event_handler_started,self(),Ref},
+ {ok, #state{}}.
+
+handle_event(Event, State) ->
+ {ok, State#state{reports = [Event|State#state.reports]}}.
+
+handle_info({get_reports,From,Ref}, State) ->
+ From ! {reports, lists:reverse(State#state.reports), Ref},
+ {ok, State#state{reports=[]}}.
+
+handle_call(_Request, State) -> {ok,reply,State}.
+terminate(_Arg, _State) -> stop.
+
+code_change(_OldVsn, State, _Extra) -> {ok, State}.
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index 61883c0647..aefd31834a 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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,7 +36,9 @@
id_string_no_opt_client/1,
id_string_no_opt_server/1,
id_string_own_string_client/1,
+ id_string_own_string_client_trail_space/1,
id_string_own_string_server/1,
+ id_string_own_string_server_trail_space/1,
id_string_random_client/1,
id_string_random_server/1,
max_sessions_sftp_start_channel_parallel/1,
@@ -61,7 +63,14 @@
unexpectedfun_option_client/1,
unexpectedfun_option_server/1,
user_dir_option/1,
- connectfun_disconnectfun_server/1
+ connectfun_disconnectfun_server/1,
+ hostkey_fingerprint_check/1,
+ hostkey_fingerprint_check_md5/1,
+ hostkey_fingerprint_check_sha/1,
+ hostkey_fingerprint_check_sha256/1,
+ hostkey_fingerprint_check_sha384/1,
+ hostkey_fingerprint_check_sha512/1,
+ hostkey_fingerprint_check_list/1
]).
%%% Common test callbacks
@@ -100,11 +109,20 @@ all() ->
disconnectfun_option_client,
unexpectedfun_option_server,
unexpectedfun_option_client,
+ hostkey_fingerprint_check,
+ hostkey_fingerprint_check_md5,
+ hostkey_fingerprint_check_sha,
+ hostkey_fingerprint_check_sha256,
+ hostkey_fingerprint_check_sha384,
+ hostkey_fingerprint_check_sha512,
+ hostkey_fingerprint_check_list,
id_string_no_opt_client,
id_string_own_string_client,
+ id_string_own_string_client_trail_space,
id_string_random_client,
id_string_no_opt_server,
id_string_own_string_server,
+ id_string_own_string_server_trail_space,
id_string_random_server,
{group, hardening_tests}
].
@@ -540,10 +558,18 @@ connectfun_disconnectfun_server(Config) ->
{disconnect,Ref,R} ->
ct:log("Disconnect result: ~p",[R]),
ssh:stop_daemon(Pid)
- after 2000 ->
+ after 5000 ->
+ receive
+ X -> ct:log("received ~p",[X])
+ after 0 -> ok
+ end,
{fail, "No disconnectfun action"}
end
- after 2000 ->
+ after 5000 ->
+ receive
+ X -> ct:log("received ~p",[X])
+ after 0 -> ok
+ end,
{fail, "No connectfun action"}
end.
@@ -649,7 +675,7 @@ disconnectfun_option_server(Config) ->
ct:log("Server detected disconnect: ~p",[Reason]),
ssh:stop_daemon(Pid),
ok
- after 3000 ->
+ after 5000 ->
receive
X -> ct:log("received ~p",[X])
after 0 -> ok
@@ -774,6 +800,106 @@ unexpectedfun_option_client(Config) ->
end.
%%--------------------------------------------------------------------
+hostkey_fingerprint_check(Config) ->
+ do_hostkey_fingerprint_check(Config, old).
+
+hostkey_fingerprint_check_md5(Config) ->
+ do_hostkey_fingerprint_check(Config, md5).
+
+hostkey_fingerprint_check_sha(Config) ->
+ do_hostkey_fingerprint_check(Config, sha).
+
+hostkey_fingerprint_check_sha256(Config) ->
+ do_hostkey_fingerprint_check(Config, sha256).
+
+hostkey_fingerprint_check_sha384(Config) ->
+ do_hostkey_fingerprint_check(Config, sha384).
+
+hostkey_fingerprint_check_sha512(Config) ->
+ do_hostkey_fingerprint_check(Config, sha512).
+
+hostkey_fingerprint_check_list(Config) ->
+ do_hostkey_fingerprint_check(Config, [sha,md5,sha256]).
+
+%%%----
+do_hostkey_fingerprint_check(Config, HashAlg) ->
+ case supported_hash(HashAlg) of
+ true ->
+ really_do_hostkey_fingerprint_check(Config, HashAlg);
+ false ->
+ {skip,{unsupported_hash,HashAlg}}
+ end.
+
+supported_hash(old) -> true;
+supported_hash(HashAlg) ->
+ Hs = if is_atom(HashAlg) -> [HashAlg];
+ is_list(HashAlg) -> HashAlg
+ end,
+ [] == (Hs -- proplists:get_value(hashs, crypto:supports(), [])).
+
+
+really_do_hostkey_fingerprint_check(Config, HashAlg) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDirServer = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDirServer),
+ SysDir = proplists:get_value(data_dir, Config),
+
+ UserDirClient =
+ ssh_test_lib:create_random_dir(Config), % Ensure no 'known_hosts' disturbs
+
+ %% All host key fingerprints. Trust that public_key has checked the ssh_hostkey_fingerprint
+ %% function since that function is used by the ssh client...
+ FPs0 = [case HashAlg of
+ old -> public_key:ssh_hostkey_fingerprint(Key);
+ _ -> public_key:ssh_hostkey_fingerprint(HashAlg, Key)
+ end
+ || FileCandidate <- begin
+ {ok,KeyFileCands} = file:list_dir(SysDir),
+ KeyFileCands
+ end,
+ nomatch =/= re:run(FileCandidate, ".*\\.pub", []),
+ {Key,_Cmnts} <- begin
+ {ok,Bin} = file:read_file(filename:join(SysDir, FileCandidate)),
+ try public_key:ssh_decode(Bin, public_key)
+ catch
+ _:_ -> []
+ end
+ end],
+ FPs = if is_atom(HashAlg) -> FPs0;
+ is_list(HashAlg) -> lists:concat(FPs0)
+ end,
+ ct:log("Fingerprints(~p) = ~p",[HashAlg,FPs]),
+
+ %% Start daemon with the public keys that we got fingerprints from
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDirServer},
+ {password, "morot"}]),
+
+ FP_check_fun = fun(PeerName, FP) ->
+ ct:pal("PeerName = ~p, FP = ~p",[PeerName,FP]),
+ HostCheck = (Host == PeerName),
+ FPCheck =
+ if is_atom(HashAlg) -> lists:member(FP, FPs);
+ is_list(HashAlg) -> lists:all(fun(FP1) -> lists:member(FP1,FPs) end,
+ FP)
+ end,
+ ct:log("check ~p == ~p (~p) and ~n~p~n in ~p (~p)~n",
+ [PeerName,Host,HostCheck,FP,FPs,FPCheck]),
+ HostCheck and FPCheck
+ end,
+
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts,
+ case HashAlg of
+ old -> FP_check_fun;
+ _ -> {HashAlg, FP_check_fun}
+ end},
+ {user, "foo"},
+ {password, "morot"},
+ {user_dir, UserDirClient},
+ {user_interaction, false}]),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
%%% Test connect_timeout option in ssh:connect/4
ssh_connect_timeout(_Config) ->
ConnTimeout = 2000,
@@ -912,6 +1038,19 @@ id_string_own_string_client(Config) ->
end.
%%--------------------------------------------------------------------
+id_string_own_string_client_trail_space(Config) ->
+ {Server, _Host, Port} = fake_daemon(Config),
+ {error,_} = ssh:connect("localhost", Port, [{id_string,"Pelle "}], 1000),
+ receive
+ {id,Server,"SSH-2.0-Pelle \r\n"} ->
+ ok;
+ {id,Server,Other} ->
+ ct:fail("Unexpected id: ~s.",[Other])
+ after 5000 ->
+ {fail,timeout}
+ end.
+
+%%--------------------------------------------------------------------
id_string_random_client(Config) ->
{Server, _Host, Port} = fake_daemon(Config),
{error,_} = ssh:connect("localhost", Port, [{id_string,random}], 1000),
@@ -940,6 +1079,12 @@ id_string_own_string_server(Config) ->
{ok,"SSH-2.0-Olle\r\n"} = gen_tcp:recv(S1, 0, 2000).
%%--------------------------------------------------------------------
+id_string_own_string_server_trail_space(Config) ->
+ {_Server, Host, Port} = ssh_test_lib:std_daemon(Config, [{id_string,"Olle "}]),
+ {ok,S1}=gen_tcp:connect(Host,Port,[{active,false},{packet,line}]),
+ {ok,"SSH-2.0-Olle \r\n"} = gen_tcp:recv(S1, 0, 2000).
+
+%%--------------------------------------------------------------------
id_string_random_server(Config) ->
{_Server, Host, Port} = ssh_test_lib:std_daemon(Config, [{id_string,random}]),
{ok,S1}=gen_tcp:connect(Host,Port,[{active,false},{packet,line}]),
@@ -974,7 +1119,14 @@ ssh_connect_negtimeout(Config, Parallel) ->
ct:sleep(round(Factor * NegTimeOut)),
case inet:sockname(Socket) of
- {ok,_} -> ct:fail("Socket not closed");
+ {ok,_} ->
+ %% Give it another chance...
+ ct:log("Sleep more...",[]),
+ ct:sleep(round(Factor * NegTimeOut)),
+ case inet:sockname(Socket) of
+ {ok,_} -> ct:fail("Socket not closed");
+ {error,_} -> ok
+ end;
{error,_} -> ok
end.
@@ -1003,7 +1155,7 @@ ssh_connect_nonegtimeout_connected(Config, Parallel) ->
ct:sleep(500),
IO = ssh_test_lib:start_io_server(),
- Shell = ssh_test_lib:start_shell(Port, IO, UserDir),
+ Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}]),
receive
Error = {'EXIT', _, _} ->
ct:log("~p",[Error]),
diff --git a/lib/ssh/test/ssh_property_test_SUITE.erl b/lib/ssh/test/ssh_property_test_SUITE.erl
index c8aabcedb7..9b2a84d8e4 100644
--- a/lib/ssh/test/ssh_property_test_SUITE.erl
+++ b/lib/ssh/test/ssh_property_test_SUITE.erl
@@ -38,6 +38,7 @@
-include_lib("common_test/include/ct.hrl").
all() -> [{group, messages},
+ client_sends_info_timing,
{group, client_server}
].
@@ -67,9 +68,6 @@ init_per_group(_, Config) ->
end_per_group(_, Config) ->
Config.
-%%% Always skip the testcase that is not quite in phase with the
-%%% ssh_message.erl code
-init_per_testcase(decode_encode, _) -> {skip, "Fails - testcase is not ok"};
init_per_testcase(_TestCase, Config) -> Config.
end_per_testcase(_TestCase, Config) -> Config.
@@ -106,3 +104,9 @@ client_server_parallel_multi(Config) ->
ssh_eqc_client_server:prop_parallel_multi(Config),
Config
).
+
+client_sends_info_timing(Config) ->
+ ct_property_test:quickcheck(
+ ssh_eqc_client_info_timing:prop_seq(Config),
+ Config
+ ).
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index 4fac1f718a..619ff5e27a 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -48,6 +48,7 @@ suite() ->
all() ->
[{group,tool_tests},
+ client_info_line,
{group,kex},
{group,service_requests},
{group,authentication},
@@ -106,7 +107,10 @@ init_per_testcase(TC, Config) when TC == gex_client_init_option_groups ;
TC == gex_client_old_request_noexact ->
Opts = case TC of
gex_client_init_option_groups ->
- [{dh_gex_groups, [{2345, 3, 41}]}];
+ [{dh_gex_groups,
+ [{1023, 5,
+ 16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A770E2EC9F
+ }]}];
gex_client_init_option_groups_file ->
DataDir = proplists:get_value(data_dir, Config),
F = filename:join(DataDir, "dh_group_test"),
@@ -118,10 +122,12 @@ init_per_testcase(TC, Config) when TC == gex_client_init_option_groups ;
_ when TC == gex_server_gex_limit ;
TC == gex_client_old_request_exact ;
TC == gex_client_old_request_noexact ->
- [{dh_gex_groups, [{ 500, 3, 17},
- {1000, 7, 91},
- {3000, 5, 61}]},
- {dh_gex_limits,{500,1500}}
+ [{dh_gex_groups,
+ [{1023, 2, 16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A771225323},
+ {1535, 5, 16#D1391174233D315398FE2830AC6B2B66BCCD01B0A634899F339B7879F1DB85712E9DC4E4B1C6C8355570C1D2DCB53493DF18175A9C53D1128B592B4C72D97136F5542FEB981CBFE8012FDD30361F288A42BD5EBB08BAB0A5640E1AC48763B2ABD1945FEE36B2D55E1D50A1C86CED9DD141C4E7BE2D32D9B562A0F8E2E927020E91F58B57EB9ACDDA106A59302D7E92AD5F6E851A45FA1CFE86029A0F727F65A8F475F33572E2FDAB6073F0C21B8B54C3823DB2EF068927E5D747498F96E1E827},
+ {3071, 2, 16#DFAA35D35531E0F524F0099877A482D2AC8D589F374394A262A8E81A8A4FB2F65FADBAB395E05D147B29D486DFAA41F41597A256DA82A8B6F76401AED53D0253F956CEC610D417E42E3B287F7938FC24D8821B40BFA218A956EB7401BED6C96C68C7FD64F8170A8A76B953DD2F05420118F6B144D8FE48060A2BCB85056B478EDEF96DBC70427053ECD2958C074169E9550DD877779A3CF17C5AC850598C7586BEEA9DCFE9DD2A5FB62DF5F33EA7BC00CDA31B9D2DD721F979EA85B6E63F0C4E30BDDCD3A335522F9004C4ED50B15DC537F55324DD4FA119FB3F101467C6D7E1699DE4B3E3C478A8679B8EB3FA5C9B826B44530FD3BE9AD3063B240B0C853EBDDBD68DD940332D98F148D5D9E1DC977D60A0D23D0CA1198637FEAE4E7FAAC173AF2B84313A666CFB4EE6972811921D0AD867CE57F3BBC8D6CB057E3B66757BB46C9F72662624D44E14528327E3A7100E81A12C43C4E236118318CD90C8AA185BBB0C764826DAEAEE8DD245C5B451B4944E6122CC522D1C335C2EEF9429825A2B}
+ ]},
+ {dh_gex_limits, {1023,2000}}
];
_ ->
[]
@@ -350,20 +356,25 @@ no_common_alg_client_disconnects(Config) ->
%%%--------------------------------------------------------------------
gex_client_init_option_groups(Config) ->
- do_gex_client_init(Config, {2000, 2048, 4000},
- {3,41}).
+ do_gex_client_init(Config, {512, 2048, 4000},
+ {5,16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A770E2EC9F}
+ ).
gex_client_init_option_groups_file(Config) ->
do_gex_client_init(Config, {2000, 2048, 4000},
- {5,61}).
+ {5, 16#DFAA35D35531E0F524F0099877A482D2AC8D589F374394A262A8E81A8A4FB2F65FADBAB395E05D147B29D486DFAA41F41597A256DA82A8B6F76401AED53D0253F956CEC610D417E42E3B287F7938FC24D8821B40BFA218A956EB7401BED6C96C68C7FD64F8170A8A76B953DD2F05420118F6B144D8FE48060A2BCB85056B478EDEF96DBC70427053ECD2958C074169E9550DD877779A3CF17C5AC850598C7586BEEA9DCFE9DD2A5FB62DF5F33EA7BC00CDA31B9D2DD721F979EA85B6E63F0C4E30BDDCD3A335522F9004C4ED50B15DC537F55324DD4FA119FB3F101467C6D7E1699DE4B3E3C478A8679B8EB3FA5C9B826B44530FD3BE9AD3063B240B0C853EBDDBD68DD940332D98F148D5D9E1DC977D60A0D23D0CA1198637FEAE4E7FAAC173AF2B84313A666CFB4EE6972811921D0AD867CE57F3BBC8D6CB057E3B66757BB46C9F72662624D44E14528327E3A7100E81A12C43C4E236118318CD90C8AA185BBB0C764826DAEAEE8DD245C5B451B4944E6122CC522D1C335C2EEF9424273F1F}
+ ).
gex_client_init_option_groups_moduli_file(Config) ->
do_gex_client_init(Config, {2000, 2048, 4000},
- {5,16#B7}).
+ {5, 16#DD2047CBDBB6F8E919BC63DE885B34D0FD6E3DB2887D8B46FE249886ACED6B46DFCD5553168185FD376122171CD8927E60120FA8D01F01D03E58281FEA9A1ABE97631C828E41815F34FDCDF787419FE13A3137649AA93D2584230DF5F24B5C00C88B7D7DE4367693428C730376F218A53E853B0851BAB7C53C15DA7839CBE1285DB63F6FA45C1BB59FE1C5BB918F0F8459D7EF60ACFF5C0FA0F3FCAD1C5F4CE4416D4F4B36B05CDCEBE4FB879E95847EFBC6449CD190248843BC7EDB145FBFC4EDBB1A3C959298F08F3BA2CFBE231BBE204BE6F906209D28BD4820AB3E7BE96C26AE8A809ADD8D1A5A0B008E9570FA4C4697E116B8119892C604293683A9635F}
+ ).
gex_server_gex_limit(Config) ->
do_gex_client_init(Config, {1000, 3000, 4000},
- {7,91}).
+ %% {7,91}).
+ {5, 16#D1391174233D315398FE2830AC6B2B66BCCD01B0A634899F339B7879F1DB85712E9DC4E4B1C6C8355570C1D2DCB53493DF18175A9C53D1128B592B4C72D97136F5542FEB981CBFE8012FDD30361F288A42BD5EBB08BAB0A5640E1AC48763B2ABD1945FEE36B2D55E1D50A1C86CED9DD141C4E7BE2D32D9B562A0F8E2E927020E91F58B57EB9ACDDA106A59302D7E92AD5F6E851A45FA1CFE86029A0F727F65A8F475F33572E2FDAB6073F0C21B8B54C3823DB2EF068927E5D747498F96E1E827}
+ ).
do_gex_client_init(Config, {Min,N,Max}, {G,P}) ->
@@ -389,8 +400,15 @@ do_gex_client_init(Config, {Min,N,Max}, {G,P}) ->
).
%%%--------------------------------------------------------------------
-gex_client_old_request_exact(Config) -> do_gex_client_init_old(Config, 500, {3,17}).
-gex_client_old_request_noexact(Config) -> do_gex_client_init_old(Config, 800, {7,91}).
+gex_client_old_request_exact(Config) ->
+ do_gex_client_init_old(Config, 1023,
+ {2, 16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A771225323}
+ ).
+
+gex_client_old_request_noexact(Config) ->
+ do_gex_client_init_old(Config, 1400,
+ {5, 16#D1391174233D315398FE2830AC6B2B66BCCD01B0A634899F339B7879F1DB85712E9DC4E4B1C6C8355570C1D2DCB53493DF18175A9C53D1128B592B4C72D97136F5542FEB981CBFE8012FDD30361F288A42BD5EBB08BAB0A5640E1AC48763B2ABD1945FEE36B2D55E1D50A1C86CED9DD141C4E7BE2D32D9B562A0F8E2E927020E91F58B57EB9ACDDA106A59302D7E92AD5F6E851A45FA1CFE86029A0F727F65A8F475F33572E2FDAB6073F0C21B8B54C3823DB2EF068927E5D747498F96E1E827}
+ ).
do_gex_client_init_old(Config, N, {G,P}) ->
{ok,_} =
@@ -575,6 +593,36 @@ client_handles_keyboard_interactive_0_pwds(Config) ->
).
+
+%%%--------------------------------------------------------------------
+client_info_line(_Config) ->
+ %% A client must not send an info-line. If it does, the server should handle
+ %% handle this gracefully
+ {ok,Pid} = ssh_eqc_event_handler:add_report_handler(),
+ {_, _, Port} = ssh_test_lib:daemon([]),
+
+ %% Fake client:
+ {ok,S} = gen_tcp:connect("localhost",Port,[]),
+ gen_tcp:send(S,"An illegal info-string\r\n"),
+ gen_tcp:close(S),
+
+ %% wait for server to react:
+ timer:sleep(1000),
+
+ %% check if a badmatch was received:
+ {ok, Reports} = ssh_eqc_event_handler:get_reports(Pid),
+ case lists:any(fun({error_report,_,{_,supervisor_report,L}}) when is_list(L) ->
+ lists:member({reason,{badmatch,{error,closed}}}, L);
+ (_) ->
+ false
+ end, Reports) of
+ true ->
+ ct:fail("Bad error report on info_line from client");
+ false ->
+ ok
+ end.
+
+
%%%================================================================
%%%==== Internal functions ========================================
%%%================================================================
diff --git a/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test b/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test
index 2887bb4b60..87c4b4afc8 100644
--- a/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test
+++ b/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test
@@ -1,3 +1,3 @@
-{2222, 5, 61}.
-{1111, 7, 91}.
+{1023, 5, 16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A770E2EC9F}.
+{3071, 5, 16#DFAA35D35531E0F524F0099877A482D2AC8D589F374394A262A8E81A8A4FB2F65FADBAB395E05D147B29D486DFAA41F41597A256DA82A8B6F76401AED53D0253F956CEC610D417E42E3B287F7938FC24D8821B40BFA218A956EB7401BED6C96C68C7FD64F8170A8A76B953DD2F05420118F6B144D8FE48060A2BCB85056B478EDEF96DBC70427053ECD2958C074169E9550DD877779A3CF17C5AC850598C7586BEEA9DCFE9DD2A5FB62DF5F33EA7BC00CDA31B9D2DD721F979EA85B6E63F0C4E30BDDCD3A335522F9004C4ED50B15DC537F55324DD4FA119FB3F101467C6D7E1699DE4B3E3C478A8679B8EB3FA5C9B826B44530FD3BE9AD3063B240B0C853EBDDBD68DD940332D98F148D5D9E1DC977D60A0D23D0CA1198637FEAE4E7FAAC173AF2B84313A666CFB4EE6972811921D0AD867CE57F3BBC8D6CB057E3B66757BB46C9F72662624D44E14528327E3A7100E81A12C43C4E236118318CD90C8AA185BBB0C764826DAEAEE8DD245C5B451B4944E6122CC522D1C335C2EEF9424273F1F}.
diff --git a/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test.moduli b/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test.moduli
index f6995ba4c9..6d2b4bcb59 100644
--- a/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test.moduli
+++ b/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test.moduli
@@ -1,3 +1,2 @@
-20151021104105 2 6 100 2222 5 B7
-20151021104106 2 6 100 1111 5 4F
-
+20120821044046 2 6 100 1023 2 D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A7711F2C6B
+20120821050554 2 6 100 2047 5 DD2047CBDBB6F8E919BC63DE885B34D0FD6E3DB2887D8B46FE249886ACED6B46DFCD5553168185FD376122171CD8927E60120FA8D01F01D03E58281FEA9A1ABE97631C828E41815F34FDCDF787419FE13A3137649AA93D2584230DF5F24B5C00C88B7D7DE4367693428C730376F218A53E853B0851BAB7C53C15DA7839CBE1285DB63F6FA45C1BB59FE1C5BB918F0F8459D7EF60ACFF5C0FA0F3FCAD1C5F4CE4416D4F4B36B05CDCEBE4FB879E95847EFBC6449CD190248843BC7EDB145FBFC4EDBB1A3C959298F08F3BA2CFBE231BBE204BE6F906209D28BD4820AB3E7BE96C26AE8A809ADD8D1A5A0B008E9570FA4C4697E116B8119892C604293683A9635F
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl
index b10ec3707f..74bbc291b2 100644
--- a/lib/ssh/test/ssh_renegotiate_SUITE.erl
+++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl
@@ -92,11 +92,11 @@ rekey(Config) ->
ConnectionRef =
ssh_test_lib:std_connect(Config, Host, Port,
[{rekey_limit, 0}]),
- Kex1 = get_kex_init(ConnectionRef),
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
receive
after ?REKEY_DATA_TMO ->
%%By this time rekeying would have been done
- Kex2 = get_kex_init(ConnectionRef),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
false = (Kex2 == Kex1),
ssh:close(ConnectionRef),
ssh:stop_daemon(Pid)
@@ -120,31 +120,31 @@ rekey_limit(Config) ->
{max_random_length_padding,0}]),
{ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
- Kex1 = get_kex_init(ConnectionRef),
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
timer:sleep(?REKEY_DATA_TMO),
- Kex1 = get_kex_init(ConnectionRef),
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
Data = lists:duplicate(159000,1),
ok = ssh_sftp:write_file(SftpPid, DataFile, Data),
timer:sleep(?REKEY_DATA_TMO),
- Kex2 = get_kex_init(ConnectionRef),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
false = (Kex2 == Kex1),
timer:sleep(?REKEY_DATA_TMO),
- Kex2 = get_kex_init(ConnectionRef),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
ok = ssh_sftp:write_file(SftpPid, DataFile, "hi\n"),
timer:sleep(?REKEY_DATA_TMO),
- Kex2 = get_kex_init(ConnectionRef),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
false = (Kex2 == Kex1),
timer:sleep(?REKEY_DATA_TMO),
- Kex2 = get_kex_init(ConnectionRef),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
ssh_sftp:stop_channel(SftpPid),
ssh:close(ConnectionRef),
@@ -169,7 +169,7 @@ renegotiate1(Config) ->
ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]),
{ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
- Kex1 = get_kex_init(ConnectionRef),
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
{ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]),
@@ -181,7 +181,7 @@ renegotiate1(Config) ->
timer:sleep(2000),
- Kex2 = get_kex_init(ConnectionRef),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
false = (Kex2 == Kex1),
@@ -208,7 +208,7 @@ renegotiate2(Config) ->
ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]),
{ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
- Kex1 = get_kex_init(ConnectionRef),
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
{ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]),
@@ -223,7 +223,7 @@ renegotiate2(Config) ->
timer:sleep(2000),
- Kex2 = get_kex_init(ConnectionRef),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
false = (Kex2 == Kex1),
@@ -235,19 +235,3 @@ renegotiate2(Config) ->
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
-%% 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),
- %% 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
- %% unique to each invocation of the key exchange procedure (RFC4253)
- SL = tuple_to_list(S),
- case lists:keyfind(ssh_msg_kexinit, 1, SL) of
- false ->
- throw(not_found);
- KexInit ->
- KexInit
- end.
-
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index 19ad81e7da..acf76157a2 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -60,12 +60,16 @@ end_per_suite(_onfig) ->
groups() ->
[{not_unicode, [], [{group,erlang_server},
{group,openssh_server},
+ {group,big_recvpkt_size},
sftp_nonexistent_subsystem]},
{unicode, [], [{group,erlang_server},
{group,openssh_server},
sftp_nonexistent_subsystem]},
+ {big_recvpkt_size, [], [{group,erlang_server},
+ {group,openssh_server}]},
+
{erlang_server, [], [{group,write_read_tests},
version_option,
{group,remote_tar}]},
@@ -149,6 +153,9 @@ init_per_group(unicode, Config) ->
{skip, "Not unicode file encoding"}
end;
+init_per_group(big_recvpkt_size, Config) ->
+ [{pkt_sz,123456} | Config];
+
init_per_group(erlang_server, Config) ->
ct:comment("Begin ~p",[grps(Config)]),
PrivDir = proplists:get_value(priv_dir, Config),
@@ -257,7 +264,10 @@ init_per_testcase(Case, Config00) ->
Dog = ct:timetrap(2 * ?default_timeout),
User = proplists:get_value(user, Config0),
Passwd = proplists:get_value(passwd, Config0),
-
+ PktSzOpt = case proplists:get_value(pkt_sz, Config0) of
+ undefined -> [];
+ Sz -> [{packet_size,Sz}]
+ end,
Config =
case proplists:get_value(group,Config2) of
erlang_server ->
@@ -267,7 +277,9 @@ init_per_testcase(Case, Config00) ->
[{user, User},
{password, Passwd},
{user_interaction, false},
- {silently_accept_hosts, true}]
+ {silently_accept_hosts, true}
+ | PktSzOpt
+ ]
),
Sftp = {ChannelPid, Connection},
[{sftp, Sftp}, {watchdog, Dog} | Config2];
@@ -278,7 +290,9 @@ init_per_testcase(Case, Config00) ->
{ok, ChannelPid, Connection} =
ssh_sftp:start_channel(Host,
[{user_interaction, false},
- {silently_accept_hosts, true}]),
+ {silently_accept_hosts, true}
+ | PktSzOpt
+ ]),
Sftp = {ChannelPid, Connection},
[{sftp, Sftp}, {watchdog, Dog} | Config2]
end,
@@ -1024,7 +1038,7 @@ oldprep(Config) ->
prepare(Config0) ->
PrivDir = proplists:get_value(priv_dir, Config0),
- Dir = filename:join(PrivDir, random_chars(10)),
+ Dir = filename:join(PrivDir, ssh_test_lib:random_chars(10)),
file:make_dir(Dir),
Keys = [filename,
testfile,
@@ -1044,8 +1058,6 @@ prepare(Config0) ->
[{sftp_priv_dir,Dir} | Config2].
-random_chars(N) -> [crypto:rand_uniform($a,$z) || _<-lists:duplicate(N,x)].
-
foldl_keydelete(Keys, L) ->
lists:foldl(fun(K,E) -> lists:keydelete(K,1,E) end,
L,
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
index 52a26110c4..2c6eaeee0d 100644
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -65,7 +65,12 @@ all() ->
ver3_open_flags,
relpath,
sshd_read_file,
- ver6_basic].
+ ver6_basic,
+ access_outside_root,
+ root_with_cwd,
+ relative_path,
+ open_file_dir_v5,
+ open_file_dir_v6].
groups() ->
[].
@@ -117,6 +122,31 @@ init_per_testcase(TestCase, Config) ->
ver6_basic ->
SubSystems = [ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}])],
ssh:daemon(0, [{subsystems, SubSystems}|Options]);
+ access_outside_root ->
+ %% Build RootDir/access_outside_root/a/b and set Root and CWD
+ BaseDir = filename:join(PrivDir, access_outside_root),
+ RootDir = filename:join(BaseDir, a),
+ CWD = filename:join(RootDir, b),
+ %% Make the directory chain:
+ ok = filelib:ensure_dir(filename:join(CWD, tmp)),
+ SubSystems = [ssh_sftpd:subsystem_spec([{root, RootDir},
+ {cwd, CWD}])],
+ ssh:daemon(0, [{subsystems, SubSystems}|Options]);
+ root_with_cwd ->
+ RootDir = filename:join(PrivDir, root_with_cwd),
+ CWD = filename:join(RootDir, home),
+ SubSystems = [ssh_sftpd:subsystem_spec([{root, RootDir}, {cwd, CWD}])],
+ ssh:daemon(0, [{subsystems, SubSystems}|Options]);
+ relative_path ->
+ SubSystems = [ssh_sftpd:subsystem_spec([{cwd, PrivDir}])],
+ ssh:daemon(0, [{subsystems, SubSystems}|Options]);
+ open_file_dir_v5 ->
+ SubSystems = [ssh_sftpd:subsystem_spec([{cwd, PrivDir}])],
+ ssh:daemon(0, [{subsystems, SubSystems}|Options]);
+ open_file_dir_v6 ->
+ SubSystems = [ssh_sftpd:subsystem_spec([{cwd, PrivDir},
+ {sftpd_vsn, 6}])],
+ ssh:daemon(0, [{subsystems, SubSystems}|Options]);
_ ->
SubSystems = [ssh_sftpd:subsystem_spec([])],
ssh:daemon(0, [{subsystems, SubSystems}|Options])
@@ -646,6 +676,133 @@ ver6_basic(Config) when is_list(Config) ->
open_file(PrivDir, Cm, Channel, ReqId,
?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
?SSH_FXF_OPEN_EXISTING).
+
+%%--------------------------------------------------------------------
+access_outside_root() ->
+ [{doc, "Try access files outside the tree below RootDir"}].
+access_outside_root(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ BaseDir = filename:join(PrivDir, access_outside_root),
+ %% A file outside the tree below RootDir which is BaseDir/a
+ %% Make the file BaseDir/bad :
+ BadFilePath = filename:join([BaseDir, bad]),
+ ok = file:write_file(BadFilePath, <<>>),
+ {Cm, Channel} = proplists:get_value(sftp, Config),
+ %% Try to access a file parallell to the RootDir:
+ try_access("/../bad", Cm, Channel, 0),
+ %% Try to access the same file via the CWD which is /b relative to the RootDir:
+ try_access("../../bad", Cm, Channel, 1).
+
+
+try_access(Path, Cm, Channel, ReqId) ->
+ Return =
+ open_file(Path, Cm, Channel, ReqId,
+ ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+ ?SSH_FXF_OPEN_EXISTING),
+ ct:log("Try open ~p -> ~p",[Path,Return]),
+ case Return of
+ {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), _Handle0/binary>>, _} ->
+ ct:fail("Could open a file outside the root tree!");
+ {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(Code), Rest/binary>>, <<>>} ->
+ case Code of
+ ?SSH_FX_FILE_IS_A_DIRECTORY ->
+ ct:pal("Got the expected SSH_FX_FILE_IS_A_DIRECTORY status",[]),
+ ok;
+ ?SSH_FX_FAILURE ->
+ ct:pal("Got the expected SSH_FX_FAILURE status",[]),
+ ok;
+ _ ->
+ case Rest of
+ <<?UINT32(Len), Txt:Len/binary, _/binary>> ->
+ ct:fail("Got unexpected SSH_FX_code: ~p (~p)",[Code,Txt]);
+ _ ->
+ ct:fail("Got unexpected SSH_FX_code: ~p",[Code])
+ end
+ end;
+ _ ->
+ ct:fail("Completly unexpected return: ~p", [Return])
+ end.
+
+%%--------------------------------------------------------------------
+root_with_cwd() ->
+ [{doc, "Check if files are found, if the CWD and Root are specified"}].
+root_with_cwd(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ RootDir = filename:join(PrivDir, root_with_cwd),
+ CWD = filename:join(RootDir, home),
+ FileName = "root_with_cwd.txt",
+ FilePath = filename:join(CWD, FileName),
+ ok = filelib:ensure_dir(FilePath),
+ ok = file:write_file(FilePath ++ "0", <<>>),
+ ok = file:write_file(FilePath ++ "1", <<>>),
+ ok = file:write_file(FilePath ++ "2", <<>>),
+ {Cm, Channel} = proplists:get_value(sftp, Config),
+ ReqId0 = 0,
+ {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId0), _Handle0/binary>>, _} =
+ open_file(FileName ++ "0", Cm, Channel, ReqId0,
+ ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+ ?SSH_FXF_OPEN_EXISTING),
+ ReqId1 = 1,
+ {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId1), _Handle1/binary>>, _} =
+ open_file("./" ++ FileName ++ "1", Cm, Channel, ReqId1,
+ ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+ ?SSH_FXF_OPEN_EXISTING),
+ ReqId2 = 2,
+ {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId2), _Handle2/binary>>, _} =
+ open_file("/home/" ++ FileName ++ "2", Cm, Channel, ReqId2,
+ ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+ ?SSH_FXF_OPEN_EXISTING).
+
+%%--------------------------------------------------------------------
+relative_path() ->
+ [{doc, "Test paths relative to CWD when opening a file handle."}].
+relative_path(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ FileName = "test_relative_path.txt",
+ FilePath = filename:join(PrivDir, FileName),
+ ok = filelib:ensure_dir(FilePath),
+ ok = file:write_file(FilePath, <<>>),
+ {Cm, Channel} = proplists:get_value(sftp, Config),
+ ReqId = 0,
+ {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), _Handle/binary>>, _} =
+ open_file(FileName, Cm, Channel, ReqId,
+ ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+ ?SSH_FXF_OPEN_EXISTING).
+
+%%--------------------------------------------------------------------
+open_file_dir_v5() ->
+ [{doc, "Test if open_file fails when opening existing directory."}].
+open_file_dir_v5(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ FileName = "open_file_dir_v5",
+ FilePath = filename:join(PrivDir, FileName),
+ ok = filelib:ensure_dir(FilePath),
+ ok = file:make_dir(FilePath),
+ {Cm, Channel} = proplists:get_value(sftp, Config),
+ ReqId = 0,
+ {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId),
+ ?UINT32(?SSH_FX_FAILURE), _/binary>>, _} =
+ open_file(FileName, Cm, Channel, ReqId,
+ ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+ ?SSH_FXF_OPEN_EXISTING).
+
+%%--------------------------------------------------------------------
+open_file_dir_v6() ->
+ [{doc, "Test if open_file fails when opening existing directory."}].
+open_file_dir_v6(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ FileName = "open_file_dir_v6",
+ FilePath = filename:join(PrivDir, FileName),
+ ok = filelib:ensure_dir(FilePath),
+ ok = file:make_dir(FilePath),
+ {Cm, Channel} = proplists:get_value(sftp, Config),
+ ReqId = 0,
+ {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId),
+ ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY), _/binary>>, _} =
+ open_file(FileName, Cm, Channel, ReqId,
+ ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+ ?SSH_FXF_OPEN_EXISTING).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -688,9 +845,7 @@ reply(Cm, Channel, RBuf) ->
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
end.
-
open_file(File, Cm, Channel, ReqId, Access, Flags) ->
-
Data = list_to_binary([?uint32(ReqId),
?binary(list_to_binary(File)),
?uint32(Access),
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 6233680dce..643ff8e41d 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -113,38 +113,41 @@ std_simple_exec(Host, Port, Config) ->
std_simple_exec(Host, Port, Config, []).
std_simple_exec(Host, Port, Config, Opts) ->
+ ct:log("~p:~p std_simple_exec",[?MODULE,?LINE]),
ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, Opts),
+ ct:log("~p:~p connected! ~p",[?MODULE,?LINE,ConnectionRef]),
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId, "23+21-2.", infinity),
- Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"42\n">>}},
- case ssh_test_lib:receive_exec_result(Data) of
- expected ->
- ok;
- Other ->
- ct:fail(Other)
- end,
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
- ssh:close(ConnectionRef).
-
-
-start_shell(Port, IOServer, UserDir) ->
- start_shell(Port, IOServer, UserDir, []).
-
-start_shell(Port, IOServer, UserDir, Options) ->
- spawn_link(?MODULE, init_shell, [Port, IOServer, [{user_dir, UserDir}|Options]]).
+ ct:log("~p:~p session_channel ok ~p",[?MODULE,?LINE,ChannelId]),
+ ExecResult = ssh_connection:exec(ConnectionRef, ChannelId, "23+21-2.", infinity),
+ ct:log("~p:~p exec ~p",[?MODULE,?LINE,ExecResult]),
+ case ExecResult of
+ success ->
+ Expected = {ssh_cm, ConnectionRef, {data,ChannelId,0,<<"42\n">>}},
+ case receive_exec_result(Expected) of
+ expected ->
+ ok;
+ Other ->
+ ct:fail(Other)
+ end,
+ receive_exec_end(ConnectionRef, ChannelId),
+ ssh:close(ConnectionRef);
+ _ ->
+ ct:fail(ExecResult)
+ end.
start_shell(Port, IOServer) ->
- spawn_link(?MODULE, init_shell, [Port, IOServer, []]).
+ start_shell(Port, IOServer, []).
-init_shell(Port, IOServer, UserDir) ->
- Host = hostname(),
- Options = [{user_interaction, false}, {silently_accept_hosts,
- true}] ++ UserDir,
- group_leader(IOServer, self()),
- loop_shell(Host, Port, Options).
+start_shell(Port, IOServer, ExtraOptions) ->
+ spawn_link(
+ fun() ->
+ Host = hostname(),
+ Options = [{user_interaction, false},
+ {silently_accept_hosts,true} | ExtraOptions],
+ group_leader(IOServer, self()),
+ ssh:shell(Host, Port, Options)
+ end).
-loop_shell(Host, Port, Options) ->
- ssh:shell(Host, Port, Options).
start_io_server() ->
spawn_link(?MODULE, init_io_server, [self()]).
@@ -208,6 +211,16 @@ reply(TestCase, Result) ->
rcv_expected(Expect, SshPort, Timeout) ->
receive
+ {SshPort, Recvd} when is_function(Expect) ->
+ case Expect(Recvd) of
+ true ->
+ ct:log("Got expected ~p from ~p",[Recvd,SshPort]),
+ catch port_close(SshPort),
+ rcv_lingering(50);
+ false ->
+ ct:log("Got UNEXPECTED ~p~n",[Recvd]),
+ rcv_expected(Expect, SshPort, Timeout)
+ end;
{SshPort, Expect} ->
ct:log("Got expected ~p from ~p",[Expect,SshPort]),
catch port_close(SshPort),
@@ -677,13 +690,16 @@ ssh_type() ->
ssh_type1() ->
try
+ ct:log("~p:~p os:find_executable(\"ssh\")",[?MODULE,?LINE]),
case os:find_executable("ssh") of
false ->
ct:log("~p:~p Executable \"ssh\" not found",[?MODULE,?LINE]),
not_found;
- _ ->
+ Path ->
+ ct:log("~p:~p Found \"ssh\" at ~p",[?MODULE,?LINE,Path]),
case os:cmd("ssh -V") of
- "OpenSSH" ++ _ ->
+ Version = "OpenSSH" ++ _ ->
+ ct:log("~p:~p Found OpenSSH ~p",[?MODULE,?LINE,Version]),
openSSH;
Str ->
ct:log("ssh client ~p is unknown",[Str]),
@@ -767,3 +783,82 @@ open_port(Arg1, ExtraOpts) ->
use_stdio,
overlapped_io, hide %only affects windows
| ExtraOpts]).
+
+%%%----------------------------------------------------------------
+%%% Sleeping
+
+%%% Milli sec
+sleep_millisec(Nms) -> receive after Nms -> ok end.
+
+%%% Micro sec
+sleep_microsec(Nus) ->
+ busy_wait(Nus, erlang:system_time(microsecond)).
+
+busy_wait(Nus, T0) ->
+ T = erlang:system_time(microsecond) - T0,
+ Tleft = Nus - T,
+ if
+ Tleft > 2000 ->
+ sleep_millisec((Tleft-1500) div 1000), % μs -> ms
+ busy_wait(Nus,T0);
+ Tleft > 1 ->
+ busy_wait(Nus, T0);
+ true ->
+ T
+ end.
+
+%%%----------------------------------------------------------------
+%% get_kex_init - helper function to get key_exchange_init_msg
+
+get_kex_init(Conn) ->
+ Ref = make_ref(),
+ {ok,TRef} = timer:send_after(15000, {reneg_timeout,Ref}),
+ get_kex_init(Conn, Ref, TRef).
+
+get_kex_init(Conn, Ref, TRef) ->
+ %% First, validate the key exchange is complete (StateName == connected)
+ case sys:get_state(Conn) of
+ {{connected,_}, S} ->
+ timer:cancel(TRef),
+ %% 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
+ %% unique to each invocation of the key exchange procedure (RFC4253)
+ SL = tuple_to_list(S),
+ case lists:keyfind(ssh_msg_kexinit, 1, SL) of
+ false ->
+ throw(not_found);
+ KexInit ->
+ KexInit
+ end;
+
+ {OtherState, S} ->
+ ct:log("Not in 'connected' state: ~p",[OtherState]),
+ receive
+ {reneg_timeout,Ref} ->
+ ct:log("S = ~p", [S]),
+ ct:fail(reneg_timeout)
+ after 0 ->
+ timer:sleep(100), % If renegotiation is complete we do not
+ % want to exit on the reneg_timeout
+ get_kex_init(Conn, Ref, TRef)
+ end
+ end.
+
+%%%----------------------------------------------------------------
+%%% Return a string with N random characters
+%%%
+random_chars(N) -> [crypto:rand_uniform($a,$z) || _<-lists:duplicate(N,x)].
+
+
+create_random_dir(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Name = filename:join(PrivDir, random_chars(15)),
+ case file:make_dir(Name) of
+ ok ->
+ Name;
+ {error,eexist} ->
+ %% The Name already denotes an existing file system object, try again.
+ %% The likelyhood of always generating an existing file name is low
+ create_random_dir(Config)
+ end.
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index a914938c41..547f759a00 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -29,13 +29,14 @@
-define(TIMEOUT, 50000).
-define(SSH_DEFAULT_PORT, 22).
+-define(REKEY_DATA_TMO, 65000).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() ->
- [{timetrap,{seconds,20}}].
+ [{timetrap,{seconds,60}}].
all() ->
case os:find_executable("ssh") of
@@ -55,10 +56,12 @@ groups() ->
erlang_client_openssh_server_publickey_rsa,
erlang_client_openssh_server_password,
erlang_client_openssh_server_kexs,
- erlang_client_openssh_server_nonexistent_subsystem
+ erlang_client_openssh_server_nonexistent_subsystem,
+ erlang_client_openssh_server_renegotiate
]},
{erlang_server, [], [erlang_server_openssh_client_public_key_dsa,
- erlang_server_openssh_client_public_key_rsa
+ erlang_server_openssh_client_public_key_rsa,
+ erlang_server_openssh_client_renegotiate
]}
].
@@ -104,6 +107,11 @@ 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(erlang_server_openssh_client_renegotiate, Config) ->
+ case os:type() of
+ {unix,_} -> ssh:start(), Config;
+ Type -> {skip, io_lib:format("Unsupported test on ~p",[Type])}
+ end;
init_per_testcase(_TestCase, Config) ->
ssh:start(),
Config.
@@ -145,7 +153,7 @@ erlang_shell_client_openssh_server(Config) when is_list(Config) ->
IO = ssh_test_lib:start_io_server(),
Shell = ssh_test_lib:start_shell(?SSH_DEFAULT_PORT, IO),
IO ! {input, self(), "echo Hej\n"},
- receive_hej(),
+ receive_data("Hej"),
IO ! {input, self(), "exit\n"},
receive_logout(),
receive_normal_exit(Shell).
@@ -386,6 +394,112 @@ erlang_server_openssh_client_public_key_X(Config, PubKeyAlg) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+%% Test that the Erlang/OTP server can renegotiate with openSSH
+erlang_server_openssh_client_renegotiate(Config) ->
+ PubKeyAlg = ssh_rsa,
+ SystemDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ KnownHosts = filename:join(PrivDir, "known_hosts"),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {public_key_alg, PubKeyAlg},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ct:sleep(500),
+
+ RenegLimitK = 3,
+ DataFile = filename:join(PrivDir, "renegotiate_openssh_client.data"),
+ Data = lists:duplicate(trunc(1.1*RenegLimitK*1024), $a),
+ ok = file:write_file(DataFile, Data),
+
+ Cmd = "ssh -p " ++ integer_to_list(Port) ++
+ " -o UserKnownHostsFile=" ++ KnownHosts ++
+ " -o RekeyLimit=" ++ integer_to_list(RenegLimitK) ++"K" ++
+ " " ++ Host ++ " < " ++ DataFile,
+ OpenSsh = ssh_test_lib:open_port({spawn, Cmd}),
+
+ Expect = fun({data,R}) ->
+ try
+ NonAlphaChars = [C || C<-lists:seq(1,255),
+ not lists:member(C,lists:seq($a,$z)),
+ not lists:member(C,lists:seq($A,$Z))
+ ],
+ Lines = string:tokens(binary_to_list(R), NonAlphaChars),
+ lists:any(fun(L) -> length(L)>1 andalso lists:prefix(L, Data) end,
+ Lines)
+ catch
+ _:_ -> false
+ end;
+
+ ({exit_status,E}) when E=/=0 ->
+ ct:log("exit_status ~p",[E]),
+ throw({skip,"exit status"});
+
+ (_) ->
+ false
+ end,
+
+ try
+ ssh_test_lib:rcv_expected(Expect, OpenSsh, ?TIMEOUT)
+ of
+ _ ->
+ %% Unfortunatly we can't check that there has been a renegotiation, just trust OpenSSH.
+ ssh:stop_daemon(Pid)
+ catch
+ throw:{skip,R} -> {skip,R}
+ end.
+
+%%--------------------------------------------------------------------
+erlang_client_openssh_server_renegotiate(_Config) ->
+ process_flag(trap_exit, true),
+
+ IO = ssh_test_lib:start_io_server(),
+ Ref = make_ref(),
+ Parent = self(),
+
+ Shell =
+ spawn_link(
+ fun() ->
+ Host = ssh_test_lib:hostname(),
+ Options = [{user_interaction, false},
+ {silently_accept_hosts,true}],
+ group_leader(IO, self()),
+ {ok, ConnRef} = ssh:connect(Host, ?SSH_DEFAULT_PORT, Options),
+ ct:pal("Parent = ~p, IO = ~p, Shell = ~p, ConnRef = ~p~n",[Parent, IO, self(), ConnRef]),
+ case ssh_connection:session_channel(ConnRef, infinity) of
+ {ok,ChannelId} ->
+ success = ssh_connection:ptty_alloc(ConnRef, ChannelId, []),
+ Args = [{channel_cb, ssh_shell},
+ {init_args,[ConnRef, ChannelId]},
+ {cm, ConnRef}, {channel_id, ChannelId}],
+ {ok, State} = ssh_channel:init([Args]),
+ Parent ! {ok, Ref, ConnRef},
+ ssh_channel:enter_loop(State);
+ Error ->
+ Parent ! {error, Ref, Error}
+ end,
+ receive
+ nothing -> ok
+ end
+ end),
+
+ receive
+ {error, Ref, Error} ->
+ ct:fail("Error=~p",[Error]);
+ {ok, Ref, ConnectionRef} ->
+ IO ! {input, self(), "echo Hej1\n"},
+ receive_data("Hej1"),
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+ ssh_connection_handler:renegotiate(ConnectionRef),
+ IO ! {input, self(), "echo Hej2\n"},
+ receive_data("Hej2"),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+ IO ! {input, self(), "exit\n"},
+ receive_logout(),
+ receive_normal_exit(Shell),
+ true = (Kex1 =/= Kex2)
+ end.
+
+%%--------------------------------------------------------------------
erlang_client_openssh_server_password() ->
[{doc, "Test client password option"}].
erlang_client_openssh_server_password(Config) when is_list(Config) ->
@@ -440,27 +554,24 @@ erlang_client_openssh_server_nonexistent_subsystem(Config) when is_list(Config)
%%--------------------------------------------------------------------
%%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
-receive_hej() ->
+receive_data(Data) ->
receive
- <<"Hej", _binary>> = Hej ->
- ct:log("Expected result: ~p~n", [Hej]);
- <<"Hej\n", _binary>> = Hej ->
- ct:log("Expected result: ~p~n", [Hej]);
- <<"Hej\r\n", _/binary>> = Hej ->
- ct:log("Expected result: ~p~n", [Hej]);
- Info ->
- Lines = binary:split(Info, [<<"\r\n">>], [global]),
- case lists:member(<<"Hej">>, Lines) of
+ Info when is_binary(Info) ->
+ Lines = string:tokens(binary_to_list(Info), "\r\n "),
+ case lists:member(Data, Lines) of
true ->
ct:log("Expected result found in lines: ~p~n", [Lines]),
ok;
false ->
ct:log("Extra info: ~p~n", [Info]),
- receive_hej()
- end
+ receive_data(Data)
+ end;
+ Other ->
+ ct:log("Unexpected: ~p",[Other]),
+ receive_data(Data)
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
- end.
+ end.
receive_logout() ->
receive
diff --git a/lib/ssh/test/ssh_trpt_test_lib.erl b/lib/ssh/test/ssh_trpt_test_lib.erl
index e34071af99..bc86000d81 100644
--- a/lib/ssh/test/ssh_trpt_test_lib.erl
+++ b/lib/ssh/test/ssh_trpt_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/ssh/test/ssh_upgrade_SUITE.erl b/lib/ssh/test/ssh_upgrade_SUITE.erl
index b5b27c369a..7b9b109fa1 100644
--- a/lib/ssh/test/ssh_upgrade_SUITE.erl
+++ b/lib/ssh/test/ssh_upgrade_SUITE.erl
@@ -199,6 +199,4 @@ close(#state{server = Server,
connection = undefined}.
-random_contents() -> list_to_binary( random_chars(3) ).
-
-random_chars(N) -> [crypto:rand_uniform($a,$z) || _<-lists:duplicate(N,x)].
+random_contents() -> list_to_binary( ssh_test_lib:random_chars(3) ).
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index b165928877..9135d8f954 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.3
+SSH_VSN = 4.4.2.4
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 3b6f988a2d..a8b284304e 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -28,6 +28,293 @@
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 8.1.3.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix alert handling so that unexpected messages are logged
+ and alerted correctly</p>
+ <p>
+ Own Id: OTP-14929</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.1.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> An erlang TLS server configured with cipher suites
+ using rsa key exchange, may be vulnerable to an Adaptive
+ Chosen Ciphertext attack (AKA Bleichenbacher attack)
+ against RSA, which when exploited, may result in
+ plaintext recovery of encrypted messages and/or a
+ Man-in-the-middle (MiTM) attack, despite the attacker not
+ having gained access to the server’s private key
+ itself. <url
+ href="https://nvd.nist.gov/vuln/detail/CVE-2017-1000385">CVE-2017-1000385</url>
+ </p> <p> Exploiting this vulnerability to perform
+ plaintext recovery of encrypted messages will, in most
+ practical cases, allow an attacker to read the plaintext
+ only after the session has completed. Only TLS sessions
+ established using RSA key exchange are vulnerable to this
+ attack. </p> <p> Exploiting this vulnerability to conduct
+ a MiTM attack requires the attacker to complete the
+ initial attack, which may require thousands of server
+ requests, during the handshake phase of the targeted
+ session within the window of the configured handshake
+ timeout. This attack may be conducted against any TLS
+ session using RSA signatures, but only if cipher suites
+ using RSA key exchange are also enabled on the server.
+ The limited window of opportunity, limitations in
+ bandwidth, and latency make this attack significantly
+ more difficult to execute. </p> <p> RSA key exchange is
+ enabled by default although least prioritized if server
+ order is honored. For such a cipher suite to be chosen it
+ must also be supported by the client and probably the
+ only shared cipher suite. </p> <p> Captured TLS sessions
+ encrypted with ephemeral cipher suites (DHE or ECDHE) are
+ not at risk for subsequent decryption due to this
+ vulnerability. </p> <p> As a workaround if default cipher
+ suite configuration was used you can configure the server
+ to not use vulnerable suites with the ciphers option like
+ this: </p> <c> {ciphers, [Suite || Suite &lt;-
+ ssl:cipher_suites(), element(1,Suite) =/= rsa]} </c> <p>
+ that is your code will look somethingh like this: </p>
+ <c> ssl:listen(Port, [{ciphers, [Suite || Suite &lt;-
+ ssl:cipher_suites(), element(1,S) =/= rsa]} | Options]).
+ </c> <p> Thanks to Hanno Böck, Juraj Somorovsky and
+ Craig Young for reporting this vulnerability. </p>
+ <p>
+ Own Id: OTP-14748</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Remove debug printout</p>
+ <p>
+ Own Id: OTP-14396</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correct active once emulation, for TLS. Now all data
+ received by the connection process will be delivered
+ through active once, even when the active once arrives
+ after that the gen_tcp socket is closed by the peer.</p>
+ <p>
+ Own Id: OTP-14300</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Corrected termination behavior, that caused a PEM cache
+ bug and sometimes resulted in connection failures.</p>
+ <p>
+ Own Id: OTP-14100</p>
+ </item>
+ <item>
+ <p>
+ Fix bug that could hang ssl connection processes when
+ failing to require more data for very large handshake
+ packages. Add option max_handshake_size to mitigate DoS
+ attacks.</p>
+ <p>
+ Own Id: OTP-14138</p>
+ </item>
+ <item>
+ <p>
+ Improved support for CRL handling that could fail to work
+ as intended when an id-ce-extKeyUsage was present in the
+ certificate. Also improvements where needed to
+ distributionpoint handling so that all revocations
+ actually are found and not deemed to be not determinable.</p>
+ <p>
+ Own Id: OTP-14141</p>
+ </item>
+ <item>
+ <p>
+ A TLS handshake might accidentally match old sslv2 format
+ and ssl application would incorrectly aborted TLS
+ handshake with ssl_v2_client_hello_no_supported. Parsing
+ was altered to avoid this problem.</p>
+ <p>
+ Own Id: OTP-14222</p>
+ </item>
+ <item>
+ <p>
+ Correct default cipher list to prefer AES 128 before 3DES</p>
+ <p>
+ Own Id: OTP-14235</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Move PEM cache to a dedicated process, to avoid making
+ the SSL manager process a bottleneck. This improves
+ scalability of TLS connections.</p>
+ <p>
+ Own Id: OTP-13874</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ List of possible anonymous suites, never supported by
+ default, where incorrect for some TLS versions.</p>
+ <p>
+ Own Id: OTP-13926</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Experimental version of DTLS. It is runnable but not
+ complete and cannot be considered reliable for production
+ usage.</p>
+ <p>
+ Own Id: OTP-12982</p>
+ </item>
+ <item>
+ <p>
+ Add API options to handle ECC curve selection.</p>
+ <p>
+ Own Id: OTP-13959</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A timing related bug in event handling could cause
+ interoperability problems between an erlang TLS server
+ and some TLS clients, especially noticed with Firefox as
+ TLS client.</p>
+ <p>
+ Own Id: OTP-13917</p>
+ </item>
+ <item>
+ <p>
+ Correct ECC curve selection, the error could cause the
+ default to always be selected.</p>
+ <p>
+ Own Id: OTP-13918</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correctly formed handshake messages received out of order
+ will now correctly fail the connection with unexpected
+ message.</p>
+ <p>
+ Own Id: OTP-13853</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ ssl application now behaves gracefully also on partially
+ incorrect input from peer.</p>
+ <p>
+ Own Id: OTP-13834</p>
+ </item>
+ <item>
+ <p>
+ Add application environment configuration
+ bypass_pem_cache. This can be used as a workaround for
+ the current implementation of the PEM-cache that has
+ proven to be a bottleneck.</p>
+ <p>
+ Own Id: OTP-13883</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 8.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The TLS/SSL protocol version selection for the SSL server
+ has been corrected to follow RFC 5246 Appendix E.1
+ especially in case where the list of supported versions
+ has gaps. Now the server selects the highest protocol
+ version it supports that is not higher than what the
+ client supports.</p>
+ <p>
+ Own Id: OTP-13753 Aux Id: seq13150 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 8.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index abba5aaf59..916b41742e 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -155,7 +155,7 @@
<tag><c>cipher() =</c></tag>
<item><p><c>rc4_128 | des_cbc | '3des_ede_cbc'
- | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm</c></p></item>
+ | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305</c></p></item>
<tag><c>hash() =</c></tag>
<item><p><c>md5 | sha | sha224 | sha256 | sha348 | sha512</c></p></item>
@@ -170,6 +170,14 @@
<tag><c>SNIfun::fun()</c></tag>
<item><p><c>= fun(ServerName :: string()) -> [ssl_option()]</c></p></item>
+ <tag><c>named_curve() =</c></tag>
+ <item><p><c>sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1
+ | sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1
+ | sect283k1 | sect283r1 | brainpoolP256r1 | secp256k1 | secp256r1
+ | sect239k1 | sect233k1 | sect233r1 | secp224k1 | secp224r1
+ | sect193r1 | sect193r2 | secp192k1 | secp192r1 | sect163k1
+ | sect163r1 | sect163r2 | secp160k1 | secp160r1 | secp160r2</c></p></item>
+
</taglist>
</section>
@@ -217,6 +225,11 @@
Anonymous cipher suites are supported for testing purposes
only and are not be used when security matters.</p></item>
+ <tag><c>{eccs, [named_curve()]}</c></tag>
+ <item><p> Allows to specify the order of preference for named curves
+ and to restrict their usage when using a cipher suite supporting them.
+ </p></item>
+
<tag><c>{secure_renegotiate, boolean()}</c></tag>
<item><p>Specifies if to reject renegotiation attempt that does
not live up to
@@ -411,6 +424,14 @@ marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_valid
</taglist>
</item>
+
+ <tag><c>max_handshake_size</c></tag>
+ <item>
+ <p>Integer (24 bits unsigned). Used to limit the size of
+ valid TLS handshake packets to avoid DoS attacks.
+ Defaults to 256*1024.</p>
+ </item>
+
</taglist>
</item>
@@ -751,6 +772,11 @@ fun(srp, Username :: string(), UserState :: term()) ->
(the default), use the client's preference.
</item>
+ <tag><c>{honor_ecc_order, boolean()}</c></tag>
+ <item>If true, use the server's preference for ECC curve selection. If false
+ (the default), use the client's preference.
+ </item>
+
<tag><c>{signature_algs, [{hash(), ecdsa | rsa | dsa}]}</c></tag>
<item><p> The algorithms specified by
this option will be the ones accepted by the server in a signature algorithm
@@ -804,6 +830,17 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
+ <name>eccs() -></name>
+ <name>eccs(protocol()) -> [named_curve()]</name>
+ <fsummary>Returns a list of supported ECCs.</fsummary>
+
+ <desc><p>Returns a list of supported ECCs. <c>eccs()</c>
+ is equivalent to calling <c>eccs(Protocol)</c> with all
+ supported protocols and then deduplicating the output.</p>
+ </desc>
+ </func>
+
+ <func>
<name>clear_pem_cache() -> ok </name>
<fsummary> Clears the pem cache</fsummary>
@@ -898,7 +935,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<fsummary>Returns all the connection information.
</fsummary>
<type>
- <v>Item = protocol | cipher_suite | sni_hostname | atom()</v>
+ <v>Item = protocol | cipher_suite | sni_hostname | ecc | atom()</v>
<d>Meaningful atoms, not specified above, are the ssl option names.</d>
<v>Result = [{Item::atom(), Value::term()}]</v>
<v>Reason = term()</v>
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
index a66e947bc1..f317dfded4 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -141,6 +141,16 @@
marker="ssl#clear_pem_cache-0">ssl:clear_pem_cache/0</seealso>
</item>
+
+ <tag><c><![CDATA[bypass_pem_cache = boolean() <optional>]]></c></tag>
+ <item>
+ <p>Introduced in ssl-8.0.2. Disables the PEM-cache.
+ The PEM cache has proven to be a bottleneck, until the
+ implementation has been improved this can be used as
+ a workaround. Defaults to false.
+ </p>
+ </item>
+
<tag><c><![CDATA[alert_timeout = integer() <optional>]]></c></tag>
<item>
<p>
diff --git a/lib/ssl/doc/src/ssl_crl_cache_api.xml b/lib/ssl/doc/src/ssl_crl_cache_api.xml
index 7440b6ef04..c6774b4df6 100644
--- a/lib/ssl/doc/src/ssl_crl_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_crl_cache_api.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2015</year><year>2015</year>
+ <year>2015</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml
index 4bd5f67202..61f88e3860 100644
--- a/lib/ssl/doc/src/ssl_distribution.xml
+++ b/lib/ssl/doc/src/ssl_distribution.xml
@@ -43,7 +43,7 @@
Erlang node distributed, <c>net_kernel</c> uses this module to
set up listen ports and connections.</p>
- <p>In the SSL application, an exra distribution
+ <p>In the SSL application, an extra distribution
module, <c>inet_tls_dist</c>, can be used as an
alternative. All distribution connections will use SSL and
all participating Erlang nodes in a distributed system must use
@@ -71,8 +71,8 @@
<section>
<title>Building Boot Scripts Including the ssl Application</title>
<p>Boot scripts are built using the <c>systools</c> utility in the
- <c>sasl</c> application. For more information on <c>systools</c>,
- see the <c>sasl</c> documentation. This is only an example of
+ SASL application. For more information on <c>systools</c>,
+ see the SASL documentation. This is only an example of
what can be done.</p>
<p>The simplest boot script possible includes only the Kernel
diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml
index b85d8fb284..a84a3dfce9 100644
--- a/lib/ssl/doc/src/ssl_session_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_session_cache_api.xml
@@ -4,14 +4,14 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2015</year>
+ <year>1999</year><year>2017</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
@@ -62,8 +62,8 @@
</taglist>
</section>
-
- <funcs>
+
+ <funcs>
<func>
<name>delete(Cache, Key) -> _</name>
@@ -134,7 +134,7 @@
</p>
</desc>
</func>
-
+
<func>
<name>select_session(Cache, PartialKey) -> [session()]</name>
<fsummary>Selects sessions that can be reused.</fsummary>
@@ -151,6 +151,21 @@
</func>
<func>
+ <name>size(Cache) -> integer()</name>
+ <fsummary>Returns the number of sessions in the cache.</fsummary>
+ <type>
+ <v>Cache = cache_ref()</v>
+ </type>
+ <desc>
+ <p>Returns the number of sessions in the cache. If size
+ exceeds the maximum number of sessions, the current cache
+ entries will be invalidated regardless of their remaining
+ lifetime. Is to be callable from any process.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name>terminate(Cache) -> _</name>
<fsummary>Called by the process that handles the cache when it
is about to terminate.</fsummary>
@@ -178,7 +193,7 @@
</p>
</desc>
</func>
-
- </funcs>
-
+
+ </funcs>
+
</erlref>
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index b625db0656..2e7df9792e 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -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.
@@ -48,8 +48,17 @@ MODULES= \
dtls \
ssl_alert \
ssl_app \
- ssl_dist_sup\
ssl_sup \
+ ssl_admin_sup\
+ tls_connection_sup \
+ ssl_connection_sup \
+ ssl_listen_tracker_sup\
+ dtls_connection_sup \
+ dtls_udp_listener\
+ dtls_udp_sup \
+ ssl_dist_sup\
+ ssl_dist_admin_sup\
+ ssl_dist_connection_sup\
inet_tls_dist \
inet6_tls_dist \
ssl_certificate\
@@ -60,19 +69,18 @@ MODULES= \
dtls_connection \
ssl_config \
ssl_connection \
- tls_connection_sup \
- dtls_connection_sup \
tls_handshake \
dtls_handshake\
ssl_handshake\
ssl_manager \
ssl_session \
ssl_session_cache \
+ ssl_pem_cache \
ssl_crl\
ssl_crl_cache \
ssl_crl_hash_dir \
- ssl_socket \
- ssl_listen_tracker_sup \
+ tls_socket \
+ dtls_socket \
tls_record \
dtls_record \
ssl_record \
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index b8be686b99..130a8bb1c5 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. 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.
@@ -39,35 +39,28 @@
-export([start_fsm/8, start_link/7, init/1]).
%% State transition handling
--export([next_record/1, next_event/3]).
+-export([next_record/1, next_event/3, next_event/4]).
%% Handshake handling
--export([%%renegotiate/2,
- send_handshake/2, queue_handshake/2, queue_change_cipher/2]).
+-export([renegotiate/2,
+ reinit_handshake_data/1,
+ send_handshake/2, queue_handshake/2, queue_change_cipher/2,
+ select_sni_extension/1]).
%% Alert and close handling
--export([%%send_alert/2, handle_own_alert/4, handle_close_alert/3,
- handle_normal_shutdown/3 %%, close/5
- %%alert_user/6, alert_user/9
- ]).
+-export([encode_alert/3,send_alert/2, close/5]).
%% Data handling
--export([%%write_application_data/3,
- read_application_data/2,
- passive_receive/2, next_record_if_active/1%,
- %%handle_common_event/4,
- %handle_packet/3
- ]).
+-export([encode_data/3, passive_receive/2, next_record_if_active/1, handle_common_event/4,
+ send/3, socket/5]).
%% 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).
+-export([callback_mode/0, terminate/3, code_change/4, format_status/2]).
%%====================================================================
%% Internal application API
@@ -84,70 +77,139 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker}
catch
error:{badmatch, {error, _} = Error} ->
Error
- end;
-
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} = Opts,
- User, {CbModule, _,_, _} = CbInfo,
- Timeout) ->
- try
- {ok, Pid} = dtls_connection_sup:start_child_dist([Role, Host, Port, Socket,
- Opts, User, CbInfo]),
- {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
- ok = ssl_connection:handshake(SslSocket, Timeout),
- {ok, SslSocket}
- catch
- error:{badmatch, {error, _} = Error} ->
- Error
end.
-send_handshake(Handshake, State) ->
- send_handshake_flight(queue_handshake(Handshake, State)).
-
-queue_flight_buffer(Msg, #state{negotiated_version = Version,
- connection_states = #connection_states{
- current_write =
- #connection_state{epoch = Epoch}},
- flight_buffer = Flight} = State) ->
- State#state{flight_buffer = Flight ++ [{Version, Epoch, Msg}]}.
+send_handshake(Handshake, #state{connection_states = ConnectionStates} = States) ->
+ #{epoch := Epoch} = ssl_record:current_connection_state(ConnectionStates, write),
+ send_handshake_flight(queue_handshake(Handshake, States), Epoch).
+
+queue_handshake(Handshake0, #state{tls_handshake_history = Hist0,
+ negotiated_version = Version,
+ flight_buffer = #{handshakes := HsBuffer0,
+ change_cipher_spec := undefined,
+ next_sequence := Seq} = Flight0} = State) ->
+ Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq),
+ Hist = update_handshake_history(Handshake0, Handshake, Hist0),
+ State#state{flight_buffer = Flight0#{handshakes => [Handshake | HsBuffer0],
+ next_sequence => Seq +1},
+ tls_handshake_history = Hist};
+
+queue_handshake(Handshake0, #state{tls_handshake_history = Hist0,
+ negotiated_version = Version,
+ flight_buffer = #{handshakes_after_change_cipher_spec := Buffer0,
+ next_sequence := Seq} = Flight0} = State) ->
+ Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq),
+ Hist = update_handshake_history(Handshake0, Handshake, Hist0),
+ State#state{flight_buffer = Flight0#{handshakes_after_change_cipher_spec => [Handshake | Buffer0],
+ next_sequence => Seq +1},
+ tls_handshake_history = Hist}.
-queue_handshake(Handshake, #state{negotiated_version = Version,
- tls_handshake_history = Hist0,
- connection_states = ConnectionStates0} = State0) ->
- {Frag, ConnectionStates, Hist} =
- encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
- queue_flight_buffer(Frag, State0#state{connection_states = ConnectionStates,
- tls_handshake_history = Hist}).
send_handshake_flight(#state{socket = Socket,
transport_cb = Transport,
- flight_buffer = Flight,
- connection_states = ConnectionStates0} = State0) ->
-
+ flight_buffer = #{handshakes := Flight,
+ change_cipher_spec := undefined},
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State0, Epoch) ->
+ %% TODO remove hardcoded Max size
{Encoded, ConnectionStates} =
- encode_handshake_flight(Flight, ConnectionStates0),
+ encode_handshake_flight(lists:reverse(Flight), Version, 1400, Epoch, ConnectionStates0),
+ send(Transport, Socket, Encoded),
+ start_flight(State0#state{connection_states = ConnectionStates});
- Transport:send(Socket, Encoded),
- State0#state{flight_buffer = [], connection_states = ConnectionStates}.
+send_handshake_flight(#state{socket = Socket,
+ transport_cb = Transport,
+ flight_buffer = #{handshakes := [_|_] = Flight0,
+ change_cipher_spec := ChangeCipher,
+ handshakes_after_change_cipher_spec := []},
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State0, Epoch) ->
+ {HsBefore, ConnectionStates1} =
+ encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch, ConnectionStates0),
+ {EncChangeCipher, ConnectionStates} = encode_change_cipher(ChangeCipher, Version, Epoch, ConnectionStates1),
+
+ send(Transport, Socket, [HsBefore, EncChangeCipher]),
+ start_flight(State0#state{connection_states = ConnectionStates});
-queue_change_cipher(Msg, State) ->
- queue_flight_buffer(Msg, State).
+send_handshake_flight(#state{socket = Socket,
+ transport_cb = Transport,
+ flight_buffer = #{handshakes := [_|_] = Flight0,
+ change_cipher_spec := ChangeCipher,
+ handshakes_after_change_cipher_spec := Flight1},
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State0, Epoch) ->
+ {HsBefore, ConnectionStates1} =
+ encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch-1, ConnectionStates0),
+ {EncChangeCipher, ConnectionStates2} =
+ encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates1),
+ {HsAfter, ConnectionStates} =
+ encode_handshake_flight(lists:reverse(Flight1), Version, 1400, Epoch, ConnectionStates2),
+ send(Transport, Socket, [HsBefore, EncChangeCipher, HsAfter]),
+ start_flight(State0#state{connection_states = ConnectionStates});
+
+send_handshake_flight(#state{socket = Socket,
+ transport_cb = Transport,
+ flight_buffer = #{handshakes := [],
+ change_cipher_spec := ChangeCipher,
+ handshakes_after_change_cipher_spec := Flight1},
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State0, Epoch) ->
+ {EncChangeCipher, ConnectionStates1} =
+ encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0),
+ {HsAfter, ConnectionStates} =
+ encode_handshake_flight(lists:reverse(Flight1), Version, 1400, Epoch, ConnectionStates1),
+ send(Transport, Socket, [EncChangeCipher, HsAfter]),
+ start_flight(State0#state{connection_states = ConnectionStates}).
+
+queue_change_cipher(ChangeCipher, #state{flight_buffer = Flight,
+ connection_states = ConnectionStates0} = State) ->
+ ConnectionStates =
+ dtls_record:next_epoch(ConnectionStates0, write),
+ State#state{flight_buffer = Flight#{change_cipher_spec => ChangeCipher},
+ connection_states = ConnectionStates}.
send_alert(Alert, #state{negotiated_version = Version,
socket = Socket,
transport_cb = Transport,
connection_states = ConnectionStates0} = State0) ->
{BinMsg, ConnectionStates} =
- ssl_alert:encode(Alert, Version, ConnectionStates0),
- Transport:send(Socket, BinMsg),
+ encode_alert(Alert, Version, ConnectionStates0),
+ send(Transport, Socket, BinMsg),
State0#state{connection_states = ConnectionStates}.
+close(downgrade, _,_,_,_) ->
+ ok;
+%% Other
+close(_, Socket, Transport, _,_) ->
+ dtls_socket:close(Transport,Socket).
+
+reinit_handshake_data(#state{protocol_buffers = Buffers} = State) ->
+ State#state{premaster_secret = undefined,
+ public_key_info = undefined,
+ tls_handshake_history = ssl_handshake:init_handshake_history(),
+ flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
+ protocol_buffers =
+ Buffers#protocol_buffers{
+ dtls_handshake_next_seq = 0,
+ dtls_handshake_next_fragments = [],
+ dtls_handshake_later_fragments = []
+ }}.
+
+select_sni_extension(#client_hello{extensions = HelloExtensions}) ->
+ HelloExtensions#hello_extensions.sni;
+select_sni_extension(_) ->
+ undefined.
+
+socket(Pid, Transport, Socket, Connection, _) ->
+ dtls_socket:socket(Pid, Transport, Socket, Connection).
+
%%====================================================================
%% tls_connection_sup API
%%====================================================================
%%--------------------------------------------------------------------
-spec start_link(atom(), host(), inet:port_number(), port(), list(), pid(), tuple()) ->
- {ok, pid()} | ignore | {error, reason()}.
+ {ok, pid()} | ignore | {error, reason()}.
%%
%% Description: Creates a gen_fsm process which calls Module:init/1 to
%% initialize. To ensure a synchronized start-up procedure, this function
@@ -161,21 +223,23 @@ init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
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)
+ gen_statem:enter_loop(?MODULE, [], init, State)
catch
throw:Error ->
- gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, error, {Error,State0})
+ gen_statem:enter_loop(?MODULE, [], error, {Error,State0})
end.
+callback_mode() ->
+ state_functions.
+
%%--------------------------------------------------------------------
-%% State functionsconnection/2
+%% State functions
%%--------------------------------------------------------------------
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,
@@ -183,23 +247,28 @@ init({call, From}, {start, Timeout},
} = 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, HelloVersion, ConnectionStates0, Handshake0),
- Transport:send(Socket, BinMsg),
- State1 = State0#state{connection_states = ConnectionStates,
- negotiated_version = Version, %% Requested version
+ State1 = prepare_flight(State0#state{negotiated_version = Version}),
+ {State2, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}),
+ State3 = State2#state{negotiated_version = Version, %% Requested version
session =
Session0#session{session_id = Hello#client_hello.session_id},
- tls_handshake_history = Handshake,
start_or_recv_from = From,
- timer = Timer},
- {Record, State} = next_record(State1),
- next_event(hello, Record, State);
+ timer = Timer,
+ flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}
+ },
+ {Record, State} = next_record(State3),
+ next_event(hello, Record, State, Actions);
+init({call, _} = Type, Event, #state{role = server, transport_cb = gen_udp} = State) ->
+ ssl_connection:init(Type, Event,
+ State#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}},
+ ?MODULE);
+init({call, _} = Type, Event, #state{role = server} = State) ->
+ %% I.E. DTLS over sctp
+ ssl_connection:init(Type, Event, State#state{flight_state = reliable}, ?MODULE);
init(Type, Event, State) ->
ssl_connection:init(Type, Event, State, ?MODULE).
@@ -216,37 +285,53 @@ error(_, _, _) ->
#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, _},
- session_cache = Cache,
- session_cache_cb = CacheCb,
- negotiated_protocol = CurrentProtocol,
- key_algorithm = KeyExAlg,
- ssl_options = SslOpts}) ->
-
- case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
- ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
- #alert{} = Alert ->
- handle_own_alert(Alert, ClientVersion, hello, State);
- {Version, {Type, Session},
- ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
- Protocol = case Protocol0 of
- undefined -> CurrentProtocol;
- _ -> Protocol0
- end,
-
- ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt},
- State#state{connection_states = ConnectionStates,
- negotiated_version = Version,
- hashsign_algorithm = HashSign,
- session = Session,
- client_ecc = {EllipticCurves, EcPointFormats},
- negotiated_protocol = Protocol}, ?MODULE)
+hello(internal, #client_hello{cookie = <<>>,
+ client_version = Version} = Hello, #state{role = server,
+ transport_cb = Transport,
+ socket = Socket} = State0) ->
+ %% TODO: not hard code key
+ {ok, {IP, Port}} = dtls_socket:peername(Transport, Socket),
+ Cookie = dtls_handshake:cookie(<<"secret">>, IP, Port, Hello),
+ VerifyRequest = dtls_handshake:hello_verify_request(Cookie, Version),
+ State1 = prepare_flight(State0#state{negotiated_version = Version}),
+ {State2, Actions} = send_handshake(VerifyRequest, State1),
+ {Record, State} = next_record(State2),
+ next_event(hello, Record, State#state{tls_handshake_history = ssl_handshake:init_handshake_history()}, Actions);
+hello(internal, #client_hello{cookie = Cookie} = Hello, #state{role = server,
+ transport_cb = Transport,
+ socket = Socket} = State0) ->
+ {ok, {IP, Port}} = dtls_socket:peername(Transport, Socket),
+ %% TODO: not hard code key
+ case dtls_handshake:cookie(<<"secret">>, IP, Port, Hello) of
+ Cookie ->
+ handle_client_hello(Hello, State0);
+ _ ->
+ %% Handle bad cookie as new cookie request RFC 6347 4.1.2
+ hello(internal, Hello#client_hello{cookie = <<>>}, State0)
end;
+hello(internal, #hello_verify_request{cookie = Cookie}, #state{role = client,
+ host = Host, port = Port,
+ ssl_options = SslOpts,
+ session = #session{own_certificate = OwnCert}
+ = Session0,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _},
+ session_cache = Cache,
+ session_cache_cb = CacheCb
+ } = State0) ->
+ State1 = prepare_flight(State0#state{tls_handshake_history = ssl_handshake:init_handshake_history()}),
+ Hello = dtls_handshake:client_hello(Host, Port, Cookie, ConnectionStates0,
+ SslOpts,
+ Cache, CacheCb, Renegotiation, OwnCert),
+ Version = Hello#client_hello.client_version,
+ HelloVersion = dtls_record:lowest_protocol_version(SslOpts#ssl_options.versions),
+ {State2, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}),
+ State3 = State2#state{negotiated_version = Version, %% Requested version
+ session =
+ Session0#session{session_id =
+ Hello#client_hello.session_id}},
+ {Record, State} = next_record(State3),
+ next_event(hello, Record, State, Actions);
hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
negotiated_version = ReqVersion,
@@ -255,29 +340,64 @@ hello(internal, #server_hello{} = Hello,
ssl_options = SslOptions} = State) ->
case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
- handle_own_alert(Alert, ReqVersion, hello, State);
+ ssl_connection:handle_own_alert(Alert, ReqVersion, hello, State);
{Version, NewId, ConnectionStates, ProtoExt, Protocol} ->
ssl_connection:handle_session(Hello,
Version, NewId, ConnectionStates, ProtoExt, Protocol, State)
end;
+hello(internal, {handshake, {#client_hello{cookie = <<>>} = Handshake, _}}, State) ->
+ %% Initial hello should not be in handshake history
+ {next_state, hello, State, [{next_event, internal, Handshake}]};
+hello(internal, {handshake, {#hello_verify_request{} = Handshake, _}}, State) ->
+ %% hello_verify should not be in handshake history
+ {next_state, hello, State, [{next_event, internal, Handshake}]};
hello(info, Event, State) ->
handle_info(Event, hello, State);
-
+hello(state_timeout, Event, State) ->
+ handle_state_timeout(Event, hello, State);
hello(Type, Event, State) ->
ssl_connection:hello(Type, Event, State, ?MODULE).
abbreviated(info, Event, State) ->
handle_info(Event, abbreviated, State);
+abbreviated(internal = Type,
+ #change_cipher_spec{type = <<1>>} = Event,
+ #state{connection_states = ConnectionStates0} = State) ->
+ ConnectionStates1 = dtls_record:save_current_connection_state(ConnectionStates0, read),
+ ConnectionStates = dtls_record:next_epoch(ConnectionStates1, read),
+ ssl_connection:abbreviated(Type, Event, State#state{connection_states = ConnectionStates}, ?MODULE);
+abbreviated(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates} = State) ->
+ ssl_connection:abbreviated(Type, Event,
+ prepare_flight(State#state{connection_states = ConnectionStates,
+ flight_state = connection}), ?MODULE);
+abbreviated(state_timeout, Event, State) ->
+ handle_state_timeout(Event, abbreviated, State);
abbreviated(Type, Event, State) ->
ssl_connection:abbreviated(Type, Event, State, ?MODULE).
certify(info, Event, State) ->
handle_info(Event, certify, State);
+certify(internal = Type, #server_hello_done{} = Event, State) ->
+ ssl_connection:certify(Type, Event, prepare_flight(State), ?MODULE);
+certify(state_timeout, Event, State) ->
+ handle_state_timeout(Event, certify, State);
certify(Type, Event, State) ->
ssl_connection:certify(Type, Event, State, ?MODULE).
cipher(info, Event, State) ->
handle_info(Event, cipher, State);
+cipher(internal = Type, #change_cipher_spec{type = <<1>>} = Event,
+ #state{connection_states = ConnectionStates0} = State) ->
+ ConnectionStates1 = dtls_record:save_current_connection_state(ConnectionStates0, read),
+ ConnectionStates = dtls_record:next_epoch(ConnectionStates1, read),
+ ssl_connection:cipher(Type, Event, State#state{connection_states = ConnectionStates}, ?MODULE);
+cipher(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates} = State) ->
+ ssl_connection:cipher(Type, Event,
+ prepare_flight(State#state{connection_states = ConnectionStates,
+ flight_state = connection}),
+ ?MODULE);
+cipher(state_timeout, Event, State) ->
+ handle_state_timeout(Event, cipher, State);
cipher(Type, Event, State) ->
ssl_connection:cipher(Type, Event, State, ?MODULE).
@@ -291,13 +411,12 @@ connection(internal, #hello_request{}, #state{host = Host, port = Port,
renegotiation = {Renegotiation, _}} = State0) ->
Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
- State1 = send_handshake(Hello, State0),
+ {State1, Actions} = send_handshake(Hello, State0),
{Record, State} =
next_record(
State1#state{session = Session0#session{session_id
= Hello#client_hello.session_id}}),
- next_event(hello, Record, State);
-
+ next_event(hello, Record, State, Actions);
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
@@ -306,21 +425,17 @@ connection(internal, #client_hello{} = Hello, #state{role = server, allow_renego
%% renegotiations immediately after each other.
erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate),
{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),
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).
downgrade(Type, Event, State) ->
ssl_connection:downgrade(Type, Event, State, ?MODULE).
-
%%--------------------------------------------------------------------
%% Description: This function is called by a gen_fsm when it receives any
%% other message than a synchronous or asynchronous event
@@ -328,15 +443,15 @@ downgrade(Type, Event, State) ->
%%--------------------------------------------------------------------
%% raw data from socket, unpack records
-handle_info({Protocol, _, Data}, StateName,
+handle_info({Protocol, _, _, _, Data}, StateName,
#state{data_tag = Protocol} = State0) ->
- case next_tls_record(Data, State0) of
+ case next_dtls_record(Data, State0) of
{Record, State} ->
next_event(StateName, Record, State);
#alert{} = Alert ->
- handle_normal_shutdown(Alert, StateName, State0),
+ ssl_connection:handle_normal_shutdown(Alert, StateName, State0),
{stop, {shutdown, own_alert}}
- end;
+ end;
handle_info({CloseTag, Socket}, StateName,
#state{socket = Socket, close_tag = CloseTag,
negotiated_version = Version} = State) ->
@@ -354,7 +469,7 @@ handle_info({CloseTag, Socket}, StateName,
%%invalidate_session(Role, Host, Port, Session)
ok
end,
- handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
+ ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
{stop, {shutdown, transport_closed}};
handle_info(Msg, StateName, State) ->
ssl_connection:handle_info(Msg, StateName, State).
@@ -362,6 +477,59 @@ handle_info(Msg, StateName, State) ->
handle_call(Event, From, StateName, State) ->
ssl_connection:handle_call(Event, From, StateName, State, ?MODULE).
+handle_common_event(internal, #alert{} = Alert, StateName,
+ #state{negotiated_version = Version} = State) ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State);
+%%% DTLS record protocol level handshake messages
+handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE,
+ fragment = Data},
+ StateName,
+ #state{protocol_buffers = Buffers0,
+ negotiated_version = Version} = State0) ->
+ try
+ case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0) of
+ {[], Buffers} ->
+ {Record, State} = next_record(State0#state{protocol_buffers = Buffers}),
+ next_event(StateName, Record, State);
+ {Packets, Buffers} ->
+ State = State0#state{protocol_buffers = Buffers},
+ Events = dtls_handshake_events(Packets),
+ {next_state, StateName,
+ State#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events}
+ end
+ catch throw:#alert{} = Alert ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State0)
+ end;
+%%% DTLS 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}}]};
+%%% DTLS 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}}]};
+%%% DTLS 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 ->
+ ssl_connection: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}.
+
+handle_state_timeout(flight_retransmission_timeout, StateName,
+ #state{flight_state = {retransmit, NextTimeout}} = State0) ->
+ {State1, Actions} = send_handshake_flight(State0#state{flight_state = {retransmit, NextTimeout}},
+ retransmit_epoch(StateName, State0)),
+ {Record, State} = next_record(State1),
+ next_event(StateName, Record, State, Actions).
+
+send(Transport, {_, {{_,_}, _} = Socket}, Data) ->
+ send(Transport, Socket, Data);
+send(Transport, Socket, Data) ->
+ dtls_socket:send(Transport, Socket, Data).
%%--------------------------------------------------------------------
%% 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
@@ -376,7 +544,7 @@ terminate(Reason, StateName, State) ->
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, StateName, State, _Extra) ->
- {?GEN_STATEM_CB_MODE, StateName, State}.
+ {ok, StateName, State}.
format_status(Type, Data) ->
ssl_connection:format_status(Type, Data).
@@ -384,88 +552,60 @@ format_status(Type, Data) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-encode_handshake(Handshake, Version, ConnectionStates0, Hist0) ->
- {Seq, ConnectionStates} = sequence(ConnectionStates0),
- {EncHandshake, Frag} = dtls_handshake:encode_handshake(Handshake, Version, Seq),
- Hist = ssl_handshake:update_handshake_history(Hist0, EncHandshake),
- {Frag, ConnectionStates, Hist}.
+handle_client_hello(#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,
+ negotiated_protocol = CurrentProtocol,
+ key_algorithm = KeyExAlg,
+ ssl_options = SslOpts} = State0) ->
+
+ case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
+ ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
+ #alert{} = Alert ->
+ ssl_connection:handle_own_alert(Alert, ClientVersion, hello, State0);
+ {Version, {Type, Session},
+ ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
+ Protocol = case Protocol0 of
+ undefined -> CurrentProtocol;
+ _ -> Protocol0
+ end,
-encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
- dtls_record:encode_change_cipher_spec(Version, ConnectionStates).
+ State = prepare_flight(State0#state{connection_states = ConnectionStates,
+ negotiated_version = Version,
+ client_hello_version = ClientVersion,
+ hashsign_algorithm = HashSign,
+ session = Session,
+ negotiated_protocol = Protocol}),
+
+ ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt},
+ State, ?MODULE)
+ end.
-encode_handshake_flight(Flight, ConnectionStates) ->
- MSS = 1400,
- encode_handshake_records(Flight, ConnectionStates, MSS, init_pack_records()).
+encode_handshake_flight(Flight, Version, MaxFragmentSize, Epoch, ConnectionStates) ->
+ Fragments = lists:map(fun(Handshake) ->
+ dtls_handshake:fragment_handshake(Handshake, MaxFragmentSize)
+ end, Flight),
+ dtls_record:encode_handshake(Fragments, Version, Epoch, ConnectionStates).
-encode_handshake_records([], CS, _MSS, Recs) ->
- {finish_pack_records(Recs), CS};
+encode_change_cipher(#change_cipher_spec{}, Version, Epoch, ConnectionStates) ->
+ dtls_record:encode_change_cipher_spec(Version, Epoch, ConnectionStates).
-encode_handshake_records([{Version, _Epoch, Frag = #change_cipher_spec{}}|Tail], ConnectionStates0, MSS, Recs0) ->
- {Encoded, ConnectionStates} =
- encode_change_cipher(Frag, Version, ConnectionStates0),
- Recs = append_pack_records([Encoded], MSS, Recs0),
- encode_handshake_records(Tail, ConnectionStates, MSS, Recs);
-
-encode_handshake_records([{Version, Epoch, {MsgType, MsgSeq, Bin}}|Tail], CS0, MSS, Recs0 = {Buf0, _}) ->
- Space = MSS - iolist_size(Buf0),
- Len = byte_size(Bin),
- {Encoded, CS} =
- encode_handshake_record(Version, Epoch, Space, MsgType, MsgSeq, Len, Bin, 0, MSS, [], CS0),
- Recs = append_pack_records(Encoded, MSS, Recs0),
- encode_handshake_records(Tail, CS, MSS, Recs).
-
-%% TODO: move to dtls_handshake????
-encode_handshake_record(_Version, _Epoch, _Space, _MsgType, _MsgSeq, _Len, <<>>, _Offset, _MRS, Encoded, CS)
- when length(Encoded) > 0 ->
- %% make sure we encode at least one segment (for empty messages like Server Hello Done
- {lists:reverse(Encoded), CS};
-
-encode_handshake_record(Version, Epoch, Space, MsgType, MsgSeq, Len, Bin,
- Offset, MRS, Encoded0, CS0) ->
- MaxFragmentLen = Space - 25,
- case Bin of
- <<BinFragment:MaxFragmentLen/bytes, Rest/binary>> ->
- ok;
- _ ->
- BinFragment = Bin,
- Rest = <<>>
- end,
- FragLength = byte_size(BinFragment),
- Frag = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragLength), BinFragment],
- %% TODO Real solution, now avoid dialyzer error {Encoded, CS} = ssl_record:encode_handshake({Epoch, Frag}, Version, CS0),
- {Encoded, CS} = ssl_record:encode_handshake(Frag, Version, CS0),
- encode_handshake_record(Version, Epoch, MRS, MsgType, MsgSeq, Len, Rest, Offset + FragLength, MRS, [Encoded|Encoded0], CS).
-
-init_pack_records() ->
- {[], []}.
-
-append_pack_records([], MSS, Recs = {Buf0, Acc0}) ->
- Remaining = MSS - iolist_size(Buf0),
- if Remaining < 12 ->
- {[], [lists:reverse(Buf0)|Acc0]};
- true ->
- Recs
- end;
-append_pack_records([Head|Tail], MSS, {Buf0, Acc0}) ->
- TotLen = iolist_size(Buf0) + iolist_size(Head),
- if TotLen > MSS ->
- append_pack_records(Tail, MSS, {[Head], [lists:reverse(Buf0)|Acc0]});
- true ->
- append_pack_records(Tail, MSS, {[Head|Buf0], Acc0})
- end.
+encode_data(Data, Version, ConnectionStates0)->
+ dtls_record:encode_data(Data, Version, ConnectionStates0).
-finish_pack_records({[], Acc}) ->
- lists:reverse(Acc);
-finish_pack_records({Buf, Acc}) ->
- lists:reverse([lists:reverse(Buf)|Acc]).
+encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
+ dtls_record:encode_alert_record(Alert, Version, ConnectionStates).
-%% decode_alerts(Bin) ->
-%% ssl_alert:decode(Bin).
+decode_alerts(Bin) ->
+ ssl_alert:decode(Bin).
-initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
+initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
#ssl_options{beast_mitigation = BeastMitigation} = SSLOptions,
- ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation),
+ ConnectionStates = dtls_record:init_connection_states(Role, BeastMitigation),
SessionCacheCb = case application:get_env(ssl, session_cb) of
{ok, Cb} when is_atom(Cb) ->
@@ -497,10 +637,12 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
renegotiation = {false, first},
allow_renegotiate = SSLOptions#ssl_options.client_renegotiation,
start_or_recv_from = undefined,
- protocol_cb = ?MODULE
+ protocol_cb = ?MODULE,
+ flight_buffer = new_flight(),
+ flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}
}.
-next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{
+next_dtls_record(Data, #state{protocol_buffers = #protocol_buffers{
dtls_record_buffer = Buf0,
dtls_cipher_texts = CT0} = Buffers} = State0) ->
case dtls_record:get_dtls_records(Data, Buf0) of
@@ -509,14 +651,15 @@ next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{
next_record(State0#state{protocol_buffers =
Buffers#protocol_buffers{dtls_record_buffer = Buf1,
dtls_cipher_texts = CT1}});
-
#alert{} = Alert ->
Alert
- end.
+ end.
-next_record(#state{%%flight = #flight{state = finished},
- protocol_buffers =
- #protocol_buffers{dtls_packets = [], dtls_cipher_texts = [CT | Rest]}
+next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
+ {no_record, State#state{unprocessed_handshake_events = N-1}};
+
+next_record(#state{protocol_buffers =
+ #protocol_buffers{dtls_cipher_texts = [CT | Rest]}
= Buffers,
connection_states = ConnStates0} = State) ->
case dtls_record:decode_cipher_text(CT, ConnStates0) of
@@ -527,9 +670,15 @@ next_record(#state{%%flight = #flight{state = finished},
#alert{} = Alert ->
{Alert, State}
end;
-next_record(#state{socket = Socket,
- transport_cb = Transport} = State) -> %% when FlightState =/= finished
- ssl_socket:setopts(Transport, Socket, [{active,once}]),
+next_record(#state{role = server,
+ socket = {Listener, {Client, _}},
+ transport_cb = gen_udp} = State) ->
+ dtls_udp_listener:active_once(Listener, Client, self()),
+ {no_record, State};
+next_record(#state{role = client,
+ socket = {_Server, Socket},
+ transport_cb = Transport} = State) ->
+ dtls_socket:setopts(Transport, Socket, [{active,once}]),
{no_record, State};
next_record(State) ->
{no_record, State}.
@@ -548,88 +697,146 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
{Record, State} = next_record(State0),
next_event(StateName, Record, State);
_ ->
- {Record, State} = read_application_data(<<>>, State0),
+ {Record, State} = ssl_connection: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) ->
+next_event(connection = StateName, no_record,
+ #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = 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]};
+ ssl_connection:hibernate_after(StateName, State, Actions);
+ {#ssl_tls{epoch = CurrentEpoch} = Record, State} ->
+ {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
+ {#ssl_tls{epoch = Epoch,
+ type = ?HANDSHAKE,
+ version = _Version}, State1} = _Record when Epoch == CurrentEpoch-1 ->
+ {State, MoreActions} = send_handshake_flight(State1, Epoch),
+ {next_state, StateName, State, Actions ++ MoreActions};
+ {#ssl_tls{epoch = _Epoch,
+ version = _Version}, State} ->
+ %% TODO maybe buffer later epoch
+ {next_state, StateName, State, Actions};
{#alert{} = Alert, State} ->
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end;
-next_event(StateName, Record, State, Actions) ->
+next_event(StateName, Record,
+ #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = 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]};
+ #ssl_tls{epoch = CurrentEpoch,
+ version = Version} = Record ->
+ {next_state, StateName,
+ dtls_version(StateName, Version, State),
+ [{next_event, internal, {protocol_record, Record}} | Actions]};
+ #ssl_tls{epoch = _Epoch,
+ version = _Version} = _Record ->
+ %% TODO maybe buffer later epoch
+ {next_state, StateName, State, Actions};
#alert{} = Alert ->
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end.
-read_application_data(_,State) ->
- {#ssl_tls{fragment = <<"place holder">>}, State}.
-
-handle_own_alert(_,_,_, State) -> %% Place holder
- {stop, {shutdown, own_alert}, State}.
-
-handle_normal_shutdown(_, _, _State) -> %% Place holder
- ok.
-
-%% TODO This generates dialyzer warnings, has to be handled differently.
-%% handle_packet(Address, Port, Packet) ->
-%% try dtls_record:get_dtls_records(Packet, <<>>) of
-%% %% expect client hello
-%% {[#ssl_tls{type = ?HANDSHAKE, version = {254, _}} = Record], <<>>} ->
-%% handle_dtls_client_hello(Address, Port, Record);
-%% _Other ->
-%% {error, not_dtls}
-%% catch
-%% _Class:_Error ->
-%% {error, not_dtls}
-%% end.
-
-%% handle_dtls_client_hello(Address, Port,
-%% #ssl_tls{epoch = Epoch, sequence_number = Seq,
-%% version = Version} = Record) ->
-%% {[{Hello, _}], _} =
-%% dtls_handshake:get_dtls_handshake(Record,
-%% dtls_handshake:dtls_handshake_new_flight(undefined)),
-%% #client_hello{client_version = {Major, Minor},
-%% random = Random,
-%% session_id = SessionId,
-%% cipher_suites = CipherSuites,
-%% compression_methods = CompressionMethods} = Hello,
-%% CookieData = [address_to_bin(Address, Port),
-%% <<?BYTE(Major), ?BYTE(Minor)>>,
-%% Random, SessionId, CipherSuites, CompressionMethods],
-%% Cookie = crypto:hmac(sha, <<"secret">>, CookieData),
-
-%% case Hello of
-%% #client_hello{cookie = Cookie} ->
-%% accept;
-
-%% _ ->
-%% %% generate HelloVerifyRequest
-%% {RequestFragment, _} = dtls_handshake:encode_handshake(
-%% dtls_handshake:hello_verify_request(Cookie),
-%% Version, 0),
-%% HelloVerifyRequest =
-%% dtls_record:encode_tls_cipher_text(?HANDSHAKE, Version, Epoch, Seq, RequestFragment),
-%% {reply, HelloVerifyRequest}
-%% end.
-
-%% address_to_bin({A,B,C,D}, Port) ->
-%% <<0:80,16#ffff:16,A,B,C,D,Port:16>>;
-%% address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
-%% <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
-
-sequence(#connection_states{dtls_write_msg_seq = Seq} = CS) ->
- {Seq, CS#connection_states{dtls_write_msg_seq = Seq + 1}}.
+dtls_version(hello, Version, #state{role = server} = State) ->
+ State#state{negotiated_version = Version}; %%Inital version
+dtls_version(_,_, State) ->
+ State.
+
+prepare_flight(#state{flight_buffer = Flight,
+ connection_states = ConnectionStates0,
+ protocol_buffers =
+ #protocol_buffers{} = Buffers} = State) ->
+ ConnectionStates = dtls_record:save_current_connection_state(ConnectionStates0, write),
+ State#state{flight_buffer = next_flight(Flight),
+ connection_states = ConnectionStates,
+ protocol_buffers = Buffers#protocol_buffers{
+ dtls_handshake_next_fragments = [],
+ dtls_handshake_later_fragments = []}}.
+new_flight() ->
+ #{next_sequence => 0,
+ handshakes => [],
+ change_cipher_spec => undefined,
+ handshakes_after_change_cipher_spec => []}.
+
+next_flight(Flight) ->
+ Flight#{handshakes => [],
+ change_cipher_spec => undefined,
+ handshakes_after_change_cipher_spec => []}.
+
+start_flight(#state{transport_cb = gen_udp,
+ flight_state = {retransmit, Timeout}} = State) ->
+ start_retransmision_timer(Timeout, State);
+start_flight(#state{transport_cb = gen_udp,
+ flight_state = connection} = State) ->
+ {State, []};
+start_flight(State) ->
+ %% No retransmision needed i.e DTLS over SCTP
+ {State#state{flight_state = reliable}, []}.
+
+start_retransmision_timer(Timeout, State) ->
+ {State#state{flight_state = {retransmit, new_timeout(Timeout)}},
+ [{state_timeout, Timeout, flight_retransmission_timeout}]}.
+
+new_timeout(N) when N =< 30 ->
+ N * 2;
+new_timeout(_) ->
+ 60.
+
+dtls_handshake_events(Packets) ->
+ lists:map(fun(Packet) ->
+ {next_event, internal, {handshake, Packet}}
+ end, Packets).
+
+renegotiate(#state{role = client} = State, Actions) ->
+ %% Handle same way as if server requested
+ %% the renegotiation
+ Hs0 = ssl_handshake:init_handshake_history(),
+ {next_state, connection, State#state{tls_handshake_history = Hs0,
+ protocol_buffers = #protocol_buffers{}},
+ [{next_event, internal, #hello_request{}} | Actions]};
+
+renegotiate(#state{role = server,
+ connection_states = CS0} = State0, Actions) ->
+ HelloRequest = ssl_handshake:hello_request(),
+ CS = CS0#{write_msg_seq => 0},
+ {State1, MoreActions} = send_handshake(HelloRequest,
+ State0#state{connection_states =
+ CS}),
+ Hs0 = ssl_handshake:init_handshake_history(),
+ {Record, State} = next_record(State1#state{tls_handshake_history = Hs0,
+ protocol_buffers = #protocol_buffers{}}),
+ next_event(hello, Record, State, Actions ++ MoreActions).
+
+handle_alerts([], Result) ->
+ Result;
+handle_alerts(_, {stop,_} = Stop) ->
+ Stop;
+handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
+ handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State));
+handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
+ handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)).
+
+retransmit_epoch(_StateName, #state{connection_states = ConnectionStates}) ->
+ #{epoch := Epoch} =
+ ssl_record:current_connection_state(ConnectionStates, write),
+ Epoch.
+
+
+update_handshake_history(#hello_verify_request{}, _, Hist) ->
+ Hist;
+update_handshake_history(_, Handshake, Hist) ->
+ %% DTLS never needs option "v2_hello_compatible" to be true
+ ssl_handshake:update_handshake_history(Hist, iolist_to_binary(Handshake), false).
+
+unprocessed_events(Events) ->
+ %% The first handshake event will be processed immediately
+ %% as it is entered first in the event queue and
+ %% when it is processed there will be length(Events)-1
+ %% handshake events left to process before we should
+ %% process more TLS-records received on the socket.
+ erlang:length(Events)-1.
+
diff --git a/lib/ssl/src/dtls_connection.hrl b/lib/ssl/src/dtls_connection.hrl
index ee3daa3c14..3dd78235d0 100644
--- a/lib/ssl/src/dtls_connection.hrl
+++ b/lib/ssl/src/dtls_connection.hrl
@@ -29,20 +29,14 @@
-include("ssl_connection.hrl").
-record(protocol_buffers, {
- dtls_packets = [], %%::[binary()], % Not yet handled decode ssl/tls packets.
- dtls_record_buffer = <<>>, %%:: binary(), % Buffer of incomplete records
- dtls_fragment_state, %%:: [], % DTLS fragments
- dtls_handshake_buffer = <<>>, %%:: binary(), % Buffer of incomplete handshakes
- dtls_cipher_texts = [], %%:: [binary()],
- dtls_cipher_texts_next %%:: [binary()] % Received for Epoch not yet active
+ dtls_record_buffer = <<>>, %% Buffer of incomplete records
+ dtls_handshake_next_seq = 0,
+ dtls_flight_last,
+ dtls_handshake_next_fragments = [], %% Fragments of the next handshake message
+ dtls_handshake_later_fragments = [], %% Fragments of handsake messages come after the one in next buffer
+ dtls_cipher_texts = [] %%:: [binary()],
}).
--record(flight, {
- last_retransmit,
- last_read_seq,
- msl_timer,
- state,
- buffer % buffer of not yet ACKed TLS records
- }).
+-define(INITIAL_RETRANSMIT_TIMEOUT, 1000). %1 sec
-endif. % -ifdef(dtls_connection).
diff --git a/lib/ssl/src/dtls_connection_sup.erl b/lib/ssl/src/dtls_connection_sup.erl
index dc7601a684..7d7be5743d 100644
--- a/lib/ssl/src/dtls_connection_sup.erl
+++ b/lib/ssl/src/dtls_connection_sup.erl
@@ -60,7 +60,7 @@ init(_O) ->
StartFunc = {dtls_connection, start_link, []},
Restart = temporary, % E.g. should not be restarted
Shutdown = 4000,
- Modules = [dtls_connection],
+ Modules = [dtls_connection, ssl_connection],
Type = worker,
ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 5a799cf441..a94954d8f2 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. 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.
@@ -18,15 +18,15 @@
%% %CopyrightEnd%
-module(dtls_handshake).
+-include("dtls_connection.hrl").
-include("dtls_handshake.hrl").
-include("dtls_record.hrl").
-include("ssl_internal.hrl").
-include("ssl_alert.hrl").
--export([client_hello/8, client_hello/9, hello/4,
- hello_verify_request/1, get_dtls_handshake/2,
- dtls_handshake_new_flight/1, dtls_handshake_new_epoch/1,
- encode_handshake/3]).
+-export([client_hello/8, client_hello/9, cookie/4, hello/4,
+ hello_verify_request/2, get_dtls_handshake/3, fragment_handshake/2,
+ handshake_bin/2, encode_handshake/3]).
-type dtls_handshake() :: #client_hello{} | #hello_verify_request{} |
ssl_handshake:ssl_handshake().
@@ -35,7 +35,7 @@
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
--spec client_hello(host(), inet:port_number(), #connection_states{},
+-spec client_hello(host(), inet:port_number(), ssl_record:connection_states(),
#ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
@@ -48,7 +48,7 @@ client_hello(Host, Port, ConnectionStates, SslOpts,
Cache, CacheCb, Renegotiation, OwnCert).
%%--------------------------------------------------------------------
--spec client_hello(host(), inet:port_number(), term(), #connection_states{},
+-spec client_hello(host(), inet:port_number(), term(), ssl_record:connection_states(),
#ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
@@ -61,10 +61,11 @@ client_hello(Host, Port, Cookie, ConnectionStates,
Cache, CacheCb, Renegotiation, OwnCert) ->
Version = dtls_record:highest_protocol_version(Versions),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = Pending#connection_state.security_parameters,
- CipherSuites = ssl_handshake:available_suites(UserSuites, Version),
+ SecParams = maps:get(security_parameters, Pending),
+ TLSVersion = dtls_v1:corresponding_tls_version(Version),
+ CipherSuites = ssl_handshake:available_suites(UserSuites, TLSVersion),
- Extensions = ssl_handshake:client_hello_extensions(Host, dtls_v1:corresponding_tls_version(Version), CipherSuites,
+ Extensions = ssl_handshake:client_hello_extensions(Host, TLSVersion, CipherSuites,
SslOpts, ConnectionStates, Renegotiation),
Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
@@ -96,72 +97,53 @@ hello(#client_hello{client_version = ClientVersion} = Hello,
#ssl_options{versions = Versions} = SslOpts,
Info, Renegotiation) ->
Version = ssl_handshake:select_version(dtls_record, ClientVersion, Versions),
- %%
- %% TODO: handle Cipher Fallback
- %%
handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation).
--spec hello_verify_request(binary()) -> #hello_verify_request{}.
+cookie(Key, Address, Port, #client_hello{client_version = {Major, Minor},
+ random = Random,
+ session_id = SessionId,
+ cipher_suites = CipherSuites,
+ compression_methods = CompressionMethods}) ->
+ CookieData = [address_to_bin(Address, Port),
+ <<?BYTE(Major), ?BYTE(Minor)>>,
+ Random, SessionId, CipherSuites, CompressionMethods],
+ crypto:hmac(sha, Key, CookieData).
+
+-spec hello_verify_request(binary(), dtls_record:dtls_version()) -> #hello_verify_request{}.
%%
%% Description: Creates a hello verify request message sent by server to
%% verify client
%%--------------------------------------------------------------------
-hello_verify_request(Cookie) ->
- %% TODO: DTLS Versions?????
- #hello_verify_request{protocol_version = {254, 255}, cookie = Cookie}.
+hello_verify_request(Cookie, Version) ->
+ #hello_verify_request{protocol_version = Version, cookie = Cookie}.
%%--------------------------------------------------------------------
-%% %%--------------------------------------------------------------------
-encode_handshake(Handshake, Version, MsgSeq) ->
+encode_handshake(Handshake, Version, Seq) ->
{MsgType, Bin} = enc_handshake(Handshake, Version),
Len = byte_size(Bin),
- Enc = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(0), ?uint24(Len), Bin],
- Frag = {MsgType, MsgSeq, Bin},
- {Enc, Frag}.
+ [MsgType, ?uint24(Len), ?uint16(Seq), ?uint24(0), ?uint24(Len), Bin].
-%%--------------------------------------------------------------------
--spec get_dtls_handshake(#ssl_tls{}, #dtls_hs_state{} | undefined) ->
- {[dtls_handshake()], #dtls_hs_state{}} | {retransmit, #dtls_hs_state{}}.
-%%
-%% Description: Given a DTLS state and new data from ssl_record, collects
-%% and returns it as a list of handshake messages, also returns a new
-%% DTLS state
-%%--------------------------------------------------------------------
-get_dtls_handshake(Records, undefined) ->
- HsState = #dtls_hs_state{highest_record_seq = 0,
- starting_read_seq = 0,
- fragments = gb_trees:empty(),
- completed = []},
- get_dtls_handshake(Records, HsState);
-get_dtls_handshake(Records, HsState0) when is_list(Records) ->
- HsState1 = lists:foldr(fun get_dtls_handshake_aux/2, HsState0, Records),
- get_dtls_handshake_completed(HsState1);
-get_dtls_handshake(Record, HsState0) when is_record(Record, ssl_tls) ->
- HsState1 = get_dtls_handshake_aux(Record, HsState0),
- get_dtls_handshake_completed(HsState1).
+fragment_handshake(Bin, _) when is_binary(Bin)->
+ %% This is the change_cipher_spec not a "real handshake" but part of the flight
+ Bin;
+fragment_handshake([MsgType, Len, Seq, _, Len, Bin], Size) ->
+ Bins = bin_fragments(Bin, Size),
+ handshake_fragments(MsgType, Seq, Len, Bins, []).
+handshake_bin([Type, Length, Data], Seq) ->
+ handshake_bin(Type, Length, Seq, Data).
+
%%--------------------------------------------------------------------
--spec dtls_handshake_new_epoch(#dtls_hs_state{}) -> #dtls_hs_state{}.
+-spec get_dtls_handshake(dtls_record:dtls_version(), binary(), #protocol_buffers{}) ->
+ {[dtls_handshake()], #protocol_buffers{}}.
%%
-%% Description: Reset the DTLS decoder state for a new Epoch
+%% Description: Given buffered and new data from dtls_record, collects
+%% and returns it as a list of handshake messages, also returns
+%% possible leftover data in the new "protocol_buffers".
%%--------------------------------------------------------------------
-%% dtls_handshake_new_epoch(<<>>) ->
-%% dtls_hs_state_init();
-dtls_handshake_new_epoch(HsState) ->
- HsState#dtls_hs_state{highest_record_seq = 0,
- starting_read_seq = HsState#dtls_hs_state.current_read_seq,
- fragments = gb_trees:empty(), completed = []}.
-
-%--------------------------------------------------------------------
--spec dtls_handshake_new_flight(integer() | undefined) -> #dtls_hs_state{}.
-%
-% Description: Init the DTLS decoder state for a new Flight
-dtls_handshake_new_flight(ExpectedReadReq) ->
- #dtls_hs_state{current_read_seq = ExpectedReadReq,
- highest_record_seq = 0,
- starting_read_seq = 0,
- fragments = gb_trees:empty(), completed = []}.
+get_dtls_handshake(Version, Fragment, ProtocolBuffers) ->
+ handle_fragments(Version, Fragment, ProtocolBuffers, []).
%%--------------------------------------------------------------------
%%% Internal functions
@@ -170,27 +152,29 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId,
cipher_suites = CipherSuites,
compression_methods = Compressions,
random = Random,
- extensions = #hello_extensions{elliptic_curves = Curves,
- signature_algs = ClientHashSigns} = HelloExt},
+ extensions =
+ #hello_extensions{elliptic_curves = Curves,
+ signature_algs = ClientHashSigns} = HelloExt},
#ssl_options{versions = Versions,
signature_algs = SupportedHashSigns} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) ->
case dtls_record:is_acceptable_version(Version, Versions) of
true ->
+ TLSVersion = dtls_v1:corresponding_tls_version(Version),
AvailableHashSigns = ssl_handshake:available_signature_algs(
- ClientHashSigns, SupportedHashSigns, Cert,
- dtls_v1:corresponding_tls_version(Version)),
- ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(Version)),
+ ClientHashSigns, SupportedHashSigns, Cert,TLSVersion),
+ ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(TLSVersion)),
{Type, #session{cipher_suite = CipherSuite} = Session1}
= ssl_handshake:select_session(SugesstedId, CipherSuites, AvailableHashSigns, Compressions,
- Port, Session0#session{ecc = ECCCurve}, Version,
+ Port, Session0#session{ecc = ECCCurve}, TLSVersion,
SslOpts, Cache, CacheCb, Cert),
case CipherSuite of
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
_ ->
{KeyExAlg,_,_,_} = ssl_cipher:suite_definition(CipherSuite),
- case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of
+ case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg,
+ SupportedHashSigns, TLSVersion) of
#alert{} = Alert ->
Alert;
HashSign ->
@@ -228,214 +212,15 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
{Version, SessionId, ConnectionStates, ProtoExt, Protocol}
end.
-get_dtls_handshake_completed(HsState = #dtls_hs_state{completed = Completed}) ->
- {lists:reverse(Completed), HsState#dtls_hs_state{completed = []}}.
-
-get_dtls_handshake_aux(#ssl_tls{version = Version,
- sequence_number = SeqNo,
- fragment = Data}, HsState) ->
- get_dtls_handshake_aux(Version, SeqNo, Data, HsState).
-
-get_dtls_handshake_aux(Version, SeqNo,
- <<?BYTE(Type), ?UINT24(Length),
- ?UINT16(MessageSeq),
- ?UINT24(FragmentOffset), ?UINT24(FragmentLength),
- Body:FragmentLength/binary, Rest/binary>>,
- HsState0) ->
- case reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, HsState0) of
- {HsState1, HighestSeqNo, MsgBody} ->
- HsState2 = dec_dtls_fragment(Version, HighestSeqNo, Type, Length, MessageSeq, MsgBody, HsState1),
- HsState3 = process_dtls_fragments(Version, HsState2),
- get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3);
-
- HsState2 ->
- HsState3 = process_dtls_fragments(Version, HsState2),
- get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3)
- end;
-
-get_dtls_handshake_aux(_Version, _SeqNo, <<>>, HsState) ->
- HsState.
-
-dec_dtls_fragment(Version, SeqNo, Type, Length, MessageSeq, MsgBody,
- HsState = #dtls_hs_state{highest_record_seq = HighestSeqNo, completed = Acc}) ->
- Raw = <<?BYTE(Type), ?UINT24(Length), ?UINT16(MessageSeq), ?UINT24(0), ?UINT24(Length), MsgBody/binary>>,
- H = decode_handshake(Version, Type, MsgBody),
- HsState#dtls_hs_state{completed = [{H,Raw}|Acc], highest_record_seq = erlang:max(HighestSeqNo, SeqNo)}.
-
-process_dtls_fragments(Version,
- HsState0 = #dtls_hs_state{current_read_seq = CurrentReadSeq,
- fragments = Fragments0}) ->
- case gb_trees:is_empty(Fragments0) of
- true ->
- HsState0;
- _ ->
- case gb_trees:smallest(Fragments0) of
- {CurrentReadSeq, {SeqNo, Type, Length, CurrentReadSeq, {Length, [{0, Length}], MsgBody}}} ->
- HsState1 = dtls_hs_state_process_seq(HsState0),
- HsState2 = dec_dtls_fragment(Version, SeqNo, Type, Length, CurrentReadSeq, MsgBody, HsState1),
- process_dtls_fragments(Version, HsState2);
- _ ->
- HsState0
- end
- end.
-
-dtls_hs_state_process_seq(HsState0 = #dtls_hs_state{current_read_seq = CurrentReadSeq,
- fragments = Fragments0}) ->
- Fragments1 = gb_trees:delete_any(CurrentReadSeq, Fragments0),
- HsState0#dtls_hs_state{current_read_seq = CurrentReadSeq + 1,
- fragments = Fragments1}.
-
-dtls_hs_state_add_fragment(MessageSeq, Fragment, HsState0 = #dtls_hs_state{fragments = Fragments0}) ->
- Fragments1 = gb_trees:enter(MessageSeq, Fragment, Fragments0),
- HsState0#dtls_hs_state{fragments = Fragments1}.
-
-reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq, 0, Length,
- Body, HsState0 = #dtls_hs_state{current_read_seq = undefined})
- when Type == ?CLIENT_HELLO;
- Type == ?SERVER_HELLO;
- Type == ?HELLO_VERIFY_REQUEST ->
- %% First message, should be client hello
- %% return the current message and set the next expected Sequence
- %%
- %% Note: this could (should?) be restricted further, ClientHello and
- %% HelloVerifyRequest have to have message_seq = 0, ServerHello
- %% can have a message_seq of 0 or 1
- %%
- {HsState0#dtls_hs_state{current_read_seq = MessageSeq + 1}, SeqNo, Body};
-
-reassemble_dtls_fragment(_SeqNo, _Type, Length, _MessageSeq, _, Length,
- _Body, HsState = #dtls_hs_state{current_read_seq = undefined}) ->
- %% not what we expected, drop it
- HsState;
-
-reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
- Body, HsState0 =
- #dtls_hs_state{starting_read_seq = StartingReadSeq})
- when MessageSeq < StartingReadSeq ->
- %% this has to be the start of a new flight, let it through
- %%
- %% Note: this could (should?) be restricted further, the first message of a
- %% new flight has to have message_seq = 0
- %%
- HsState = dtls_hs_state_process_seq(HsState0),
- {HsState, SeqNo, Body};
-
-reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, 0, Length,
- _Body, HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
- when MessageSeq < CurrentReadSeq ->
- HsState;
-
-reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
- Body, HsState0 = #dtls_hs_state{current_read_seq = MessageSeq}) ->
- %% Message fully contained and it's the current seq
- HsState1 = dtls_hs_state_process_seq(HsState0),
- {HsState1, SeqNo, Body};
-
-reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq, 0, Length,
- Body, HsState) ->
- %% Message fully contained and it's the NOT the current seq -> buffer
- Fragment = {SeqNo, Type, Length, MessageSeq,
- dtls_fragment_init(Length, 0, Length, Body)},
- dtls_hs_state_add_fragment(MessageSeq, Fragment, HsState);
-
-reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, FragmentOffset, FragmentLength,
- _Body,
- HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
- when FragmentOffset + FragmentLength == Length andalso MessageSeq == (CurrentReadSeq - 1) ->
- {retransmit, HsState};
-
-reassemble_dtls_fragment(_SeqNo, _Type, _Length, MessageSeq, _FragmentOffset, _FragmentLength,
- _Body,
- HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
- when MessageSeq < CurrentReadSeq ->
- HsState;
-
-reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body,
- HsState = #dtls_hs_state{fragments = Fragments0}) ->
- case gb_trees:lookup(MessageSeq, Fragments0) of
- {value, Fragment} ->
- dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, Fragment, HsState);
- none ->
- dtls_fragment_start(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, HsState)
- end.
-dtls_fragment_start(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, HsState = #dtls_hs_state{fragments = Fragments0}) ->
- Fragment = {SeqNo, Type, Length, MessageSeq,
- dtls_fragment_init(Length, FragmentOffset, FragmentLength, Body)},
- Fragments1 = gb_trees:insert(MessageSeq, Fragment, Fragments0),
- HsState#dtls_hs_state{fragments = Fragments1}.
-
-dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body,
- {LastSeqNo, Type, Length, MessageSeq, FragBuffer0},
- HsState = #dtls_hs_state{fragments = Fragments0}) ->
- FragBuffer1 = dtls_fragment_add(FragBuffer0, FragmentOffset, FragmentLength, Body),
- Fragment = {erlang:max(SeqNo, LastSeqNo), Type, Length, MessageSeq, FragBuffer1},
- Fragments1 = gb_trees:enter(MessageSeq, Fragment, Fragments0),
- HsState#dtls_hs_state{fragments = Fragments1};
-
-%% Type, Length or Seq mismatch, drop everything...
-%% Note: the RFC is not clear on how to handle this...
-dtls_fragment_reassemble(_SeqNo, _Type, _Length, MessageSeq,
- _FragmentOffset, _FragmentLength, _Body, _Fragment,
- HsState = #dtls_hs_state{fragments = Fragments0}) ->
- Fragments1 = gb_trees:delete_any(MessageSeq, Fragments0),
- HsState#dtls_hs_state{fragments = Fragments1}.
-
-dtls_fragment_add({Length, FragmentList0, Bin0}, FragmentOffset, FragmentLength, Body) ->
- Bin1 = dtls_fragment_bin_add(FragmentOffset, FragmentLength, Body, Bin0),
- FragmentList1 = add_fragment(FragmentList0, {FragmentOffset, FragmentLength}),
- {Length, FragmentList1, Bin1}.
-
-dtls_fragment_init(Length, 0, Length, Body) ->
- {Length, [{0, Length}], Body};
-dtls_fragment_init(Length, FragmentOffset, FragmentLength, Body) ->
- Bin = dtls_fragment_bin_add(FragmentOffset, FragmentLength, Body, <<0:(Length*8)>>),
- {Length, [{FragmentOffset, FragmentOffset + FragmentLength}], Bin}.
-
-dtls_fragment_bin_add(FragmentOffset, FragmentLength, Add, Buffer) ->
- <<First:FragmentOffset/bytes, _:FragmentLength/bytes, Rest/binary>> = Buffer,
- <<First/binary, Add/binary, Rest/binary>>.
-
-merge_fragment_list([], Fragment, Acc) ->
- lists:reverse([Fragment|Acc]);
-
-merge_fragment_list([H = {_, HEnd}|Rest], Frag = {FStart, _}, Acc)
- when FStart > HEnd ->
- merge_fragment_list(Rest, Frag, [H|Acc]);
-
-merge_fragment_list(Rest = [{HStart, _HEnd}|_], Frag = {_FStart, FEnd}, Acc)
- when FEnd < HStart ->
- lists:reverse(Acc) ++ [Frag|Rest];
-
-merge_fragment_list([{HStart, HEnd}|Rest], _Frag = {FStart, FEnd}, Acc)
- when
- FStart =< HEnd orelse FEnd >= HStart ->
- Start = erlang:min(HStart, FStart),
- End = erlang:max(HEnd, FEnd),
- NewFrag = {Start, End},
- merge_fragment_list(Rest, NewFrag, Acc).
-
-add_fragment(List, {FragmentOffset, FragmentLength}) ->
- merge_fragment_list(List, {FragmentOffset, FragmentOffset + FragmentLength}, []).
+%%%%%%% Encodeing %%%%%%%%%%%%%
enc_handshake(#hello_verify_request{protocol_version = {Major, Minor},
cookie = Cookie}, _Version) ->
- CookieLength = byte_size(Cookie),
+ CookieLength = byte_size(Cookie),
{?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
?BYTE(CookieLength),
- Cookie/binary>>};
+ Cookie:CookieLength/binary>>};
enc_handshake(#hello_request{}, _Version) ->
{?HELLO_REQUEST, <<>>};
@@ -445,58 +230,258 @@ enc_handshake(#client_hello{client_version = {Major, Minor},
cookie = Cookie,
cipher_suites = CipherSuites,
compression_methods = CompMethods,
- extensions = HelloExtensions}, Version) ->
+ extensions = HelloExtensions}, _Version) ->
SIDLength = byte_size(SessionID),
- BinCookie = enc_client_hello_cookie(Version, Cookie),
+ CookieLength = byte_size(Cookie),
BinCompMethods = list_to_binary(CompMethods),
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
-
+
{?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SIDLength), SessionID/binary,
- BinCookie/binary,
+ ?BYTE(CookieLength), Cookie/binary,
?UINT16(CsLength), BinCipherSuites/binary,
?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
+
+enc_handshake(#server_hello{} = HandshakeMsg, Version) ->
+ {Type, <<?BYTE(Major), ?BYTE(Minor), Rest/binary>>} =
+ ssl_handshake:encode_handshake(HandshakeMsg, Version),
+ {DTLSMajor, DTLSMinor} = dtls_v1:corresponding_dtls_version({Major, Minor}),
+ {Type, <<?BYTE(DTLSMajor), ?BYTE(DTLSMinor), Rest/binary>>};
+
enc_handshake(HandshakeMsg, Version) ->
ssl_handshake:encode_handshake(HandshakeMsg, Version).
-enc_client_hello_cookie(_, <<>>) ->
- <<>>;
-enc_client_hello_cookie(_, Cookie) ->
- CookieLength = byte_size(Cookie),
- <<?BYTE(CookieLength), Cookie/binary>>.
+bin_fragments(Bin, Size) ->
+ bin_fragments(Bin, size(Bin), Size, 0, []).
-decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+bin_fragments(Bin, BinSize, FragSize, Offset, Fragments) ->
+ case (BinSize - Offset - FragSize) > 0 of
+ true ->
+ Frag = binary:part(Bin, {Offset, FragSize}),
+ bin_fragments(Bin, BinSize, FragSize, Offset + FragSize, [{Frag, Offset} | Fragments]);
+ false ->
+ Frag = binary:part(Bin, {Offset, BinSize-Offset}),
+ lists:reverse([{Frag, Offset} | Fragments])
+ end.
+
+handshake_fragments(_, _, _, [], Acc) ->
+ lists:reverse(Acc);
+handshake_fragments(MsgType, Seq, Len, [{Bin, Offset} | Bins], Acc) ->
+ FragLen = size(Bin),
+ handshake_fragments(MsgType, Seq, Len, Bins,
+ [<<?BYTE(MsgType), Len/binary, Seq/binary, ?UINT24(Offset),
+ ?UINT24(FragLen), Bin/binary>> | Acc]).
+
+address_to_bin({A,B,C,D}, Port) ->
+ <<0:80,16#ffff:16,A,B,C,D,Port:16>>;
+address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
+ <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
+
+%%%%%%% Decodeing %%%%%%%%%%%%%
+
+handle_fragments(Version, FragmentData, Buffers0, Acc) ->
+ Fragments = decode_handshake_fragments(FragmentData),
+ do_handle_fragments(Version, Fragments, Buffers0, Acc).
+
+do_handle_fragments(_, [], Buffers, Acc) ->
+ {lists:reverse(Acc), Buffers};
+do_handle_fragments(Version, [Fragment | Fragments], Buffers0, Acc) ->
+ case reassemble(Version, Fragment, Buffers0) of
+ {more_data, Buffers} when Fragments == [] ->
+ {lists:reverse(Acc), Buffers};
+ {more_data, Buffers} ->
+ do_handle_fragments(Version, Fragments, Buffers, Acc);
+ {HsPacket, Buffers} ->
+ do_handle_fragments(Version, Fragments, Buffers, [HsPacket | Acc])
+ end.
+
+decode_handshake(Version, <<?BYTE(Type), Bin/binary>>) ->
+ decode_handshake(Version, Type, Bin).
+
+decode_handshake(_, ?HELLO_REQUEST, <<>>) ->
+ #hello_request{};
+decode_handshake(_Version, ?CLIENT_HELLO, <<?UINT24(_), ?UINT16(_),
+ ?UINT24(_), ?UINT24(_),
+ ?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
- ?BYTE(Cookie_length), Cookie:Cookie_length/binary,
+ ?BYTE(CookieLength), Cookie:CookieLength/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
?BYTE(Cm_length), Comp_methods:Cm_length/binary,
Extensions/binary>>) ->
- DecodedExtensions = ssl_handshake:decode_hello_extensions(Extensions),
-
+ DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}),
+
#client_hello{
client_version = {Major,Minor},
random = Random,
- session_id = Session_ID,
cookie = Cookie,
+ session_id = Session_ID,
cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites),
compression_methods = Comp_methods,
extensions = DecodedExtensions
- };
+ };
+
+decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?UINT24(_), ?UINT16(_),
+ ?UINT24(_), ?UINT24(_),
+ ?BYTE(Major), ?BYTE(Minor),
+ ?BYTE(CookieLength),
+ Cookie:CookieLength/binary>>) ->
+ #hello_verify_request{protocol_version = {Major, Minor},
+ cookie = Cookie};
+
+decode_handshake(Version, Tag, <<?UINT24(_), ?UINT16(_),
+ ?UINT24(_), ?UINT24(_), Msg/binary>>) ->
+ %% DTLS specifics stripped
+ decode_tls_thandshake(Version, Tag, Msg).
+
+decode_tls_thandshake(Version, Tag, Msg) ->
+ TLSVersion = dtls_v1:corresponding_tls_version(Version),
+ ssl_handshake:decode_handshake(TLSVersion, Tag, Msg).
+
+decode_handshake_fragments(<<>>) ->
+ [<<>>];
+decode_handshake_fragments(<<?BYTE(Type), ?UINT24(Length),
+ ?UINT16(MessageSeq),
+ ?UINT24(FragmentOffset), ?UINT24(FragmentLength),
+ Fragment:FragmentLength/binary, Rest/binary>>) ->
+ [#handshake_fragment{type = Type,
+ length = Length,
+ message_seq = MessageSeq,
+ fragment_offset = FragmentOffset,
+ fragment_length = FragmentLength,
+ fragment = Fragment} | decode_handshake_fragments(Rest)].
+
+reassemble(Version, #handshake_fragment{message_seq = Seq} = Fragment,
+ #protocol_buffers{dtls_handshake_next_seq = Seq,
+ dtls_handshake_next_fragments = Fragments0,
+ dtls_handshake_later_fragments = LaterFragments0} =
+ Buffers0)->
+ case reassemble_fragments(Fragment, Fragments0) of
+ {more_data, Fragments} ->
+ {more_data, Buffers0#protocol_buffers{dtls_handshake_next_fragments = Fragments}};
+ {raw, RawHandshake} ->
+ Handshake = decode_handshake(Version, RawHandshake),
+ {NextFragments, LaterFragments} = next_fragments(LaterFragments0),
+ {{Handshake, RawHandshake}, Buffers0#protocol_buffers{dtls_handshake_next_seq = Seq + 1,
+ dtls_handshake_next_fragments = NextFragments,
+ dtls_handshake_later_fragments = LaterFragments}}
+ end;
+reassemble(_, #handshake_fragment{message_seq = FragSeq} = Fragment,
+ #protocol_buffers{dtls_handshake_next_seq = Seq,
+ dtls_handshake_later_fragments = LaterFragments} = Buffers0) when FragSeq > Seq->
+ {more_data,
+ Buffers0#protocol_buffers{dtls_handshake_later_fragments = [Fragment | LaterFragments]}};
+reassemble(_, _, Buffers) ->
+ %% Disregard fragments FragSeq < Seq
+ {more_data, Buffers}.
+
+reassemble_fragments(Current, Fragments0) ->
+ [Frag1 | Frags] = lists:keysort(#handshake_fragment.fragment_offset, [Current | Fragments0]),
+ [Fragment | _] = Fragments = merge_fragment(Frag1, Frags),
+ case is_complete_handshake(Fragment) of
+ true ->
+ {raw, handshake_bin(Fragment)};
+ false ->
+ {more_data, Fragments}
+ end.
-decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
- ?BYTE(CookieLength), Cookie:CookieLength/binary>>) ->
+merge_fragment(Frag0, []) ->
+ [Frag0];
+merge_fragment(Frag0, [Frag1 | Rest]) ->
+ case merge_fragments(Frag0, Frag1) of
+ [_|_] = Frags ->
+ Frags ++ Rest;
+ Frag ->
+ merge_fragment(Frag, Rest)
+ end.
- #hello_verify_request{
- protocol_version = {Major,Minor},
- cookie = Cookie};
-decode_handshake(Version, Tag, Msg) ->
- ssl_handshake:decode_handshake(Version, Tag, Msg).
+is_complete_handshake(#handshake_fragment{length = Length, fragment_length = Length}) ->
+ true;
+is_complete_handshake(_) ->
+ false.
+
+next_fragments(LaterFragments) ->
+ case lists:keysort(#handshake_fragment.message_seq, LaterFragments) of
+ [] ->
+ {[], []};
+ [#handshake_fragment{message_seq = Seq} | _] = Fragments ->
+ split_frags(Fragments, Seq, [])
+ end.
-%% address_to_bin({A,B,C,D}, Port) ->
-%% <<0:80,16#ffff:16,A,B,C,D,Port:16>>;
-%% address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
-%% <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
+split_frags([#handshake_fragment{message_seq = Seq} = Frag | Rest], Seq, Acc) ->
+ split_frags(Rest, Seq, [Frag | Acc]);
+split_frags(Frags, _, Acc) ->
+ {lists:reverse(Acc), Frags}.
+
+
+%% Duplicate
+merge_fragments(#handshake_fragment{
+ fragment_offset = PreviousOffSet,
+ fragment_length = PreviousLen,
+ fragment = PreviousData
+ } = Previous,
+ #handshake_fragment{
+ fragment_offset = PreviousOffSet,
+ fragment_length = PreviousLen,
+ fragment = PreviousData}) ->
+ Previous;
+
+%% Lager fragment save new data
+merge_fragments(#handshake_fragment{
+ fragment_offset = PreviousOffSet,
+ fragment_length = PreviousLen,
+ fragment = PreviousData
+ } = Previous,
+ #handshake_fragment{
+ fragment_offset = PreviousOffSet,
+ fragment_length = CurrentLen,
+ fragment = CurrentData}) when CurrentLen > PreviousLen ->
+ NewLength = CurrentLen - PreviousLen,
+ <<_:PreviousLen/binary, NewData/binary>> = CurrentData,
+ Previous#handshake_fragment{
+ fragment_length = PreviousLen + NewLength,
+ fragment = <<PreviousData/binary, NewData/binary>>
+ };
+
+%% Smaller fragment
+merge_fragments(#handshake_fragment{
+ fragment_offset = PreviousOffSet,
+ fragment_length = PreviousLen
+ } = Previous,
+ #handshake_fragment{
+ fragment_offset = PreviousOffSet,
+ fragment_length = CurrentLen}) when CurrentLen < PreviousLen ->
+ Previous;
+%% Next fragment
+merge_fragments(#handshake_fragment{
+ fragment_offset = PreviousOffSet,
+ fragment_length = PreviousLen,
+ fragment = PreviousData
+ } = Previous,
+ #handshake_fragment{
+ fragment_offset = CurrentOffSet,
+ fragment_length = CurrentLen,
+ fragment = CurrentData}) when PreviousOffSet + PreviousLen == CurrentOffSet->
+ Previous#handshake_fragment{
+ fragment_length = PreviousLen + CurrentLen,
+ fragment = <<PreviousData/binary, CurrentData/binary>>};
+%% No merge there is a gap
+merge_fragments(Previous, Current) ->
+ [Previous, Current].
+
+handshake_bin(#handshake_fragment{
+ type = Type,
+ length = Len,
+ message_seq = Seq,
+ fragment_length = Len,
+ fragment_offset = 0,
+ fragment = Fragment}) ->
+ handshake_bin(Type, Len, Seq, Fragment).
+
+handshake_bin(Type, Length, Seq, FragmentData) ->
+ <<?BYTE(Type), ?UINT24(Length),
+ ?UINT16(Seq), ?UINT24(0), ?UINT24(Length),
+ FragmentData:Length/binary>>.
diff --git a/lib/ssl/src/dtls_handshake.hrl b/lib/ssl/src/dtls_handshake.hrl
index 0298fd3105..0a980c5f31 100644
--- a/lib/ssl/src/dtls_handshake.hrl
+++ b/lib/ssl/src/dtls_handshake.hrl
@@ -46,12 +46,13 @@
cookie
}).
--record(dtls_hs_state,
- {current_read_seq,
- starting_read_seq,
- highest_record_seq,
- fragments,
- completed
- }).
+-record(handshake_fragment, {
+ type,
+ length,
+ message_seq,
+ fragment_offset,
+ fragment_length,
+ fragment
+ }).
-endif. % -ifdef(dtls_handshake).
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index 5387fcafa8..f447897d59 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2013-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.
@@ -30,13 +30,15 @@
-include("ssl_cipher.hrl").
%% Handling of incoming data
--export([get_dtls_records/2]).
+-export([get_dtls_records/2, init_connection_states/2]).
%% Decoding
-export([decode_cipher_text/2]).
%% Encoding
--export([encode_plain_text/4, encode_tls_cipher_text/5, encode_change_cipher_spec/2]).
+-export([encode_handshake/4, encode_alert_record/3,
+ encode_change_cipher_spec/3, encode_data/3]).
+-export([encode_plain_text/5]).
%% Protocol version handling
-export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2,
@@ -44,9 +46,9 @@
is_higher/2, supported_protocol_versions/0,
is_acceptable_version/2]).
-%% DTLS Epoch handling
--export([init_connection_state_seq/2, current_connection_state_epoch/2,
- set_connection_state_by_epoch/3, connection_state_by_epoch/3]).
+-export([save_current_connection_state/2, next_epoch/2]).
+
+-export([init_connection_state_seq/2, current_connection_state_epoch/2]).
-export_type([dtls_version/0, dtls_atom_version/0]).
@@ -58,6 +60,73 @@
%%====================================================================
%% Internal application API
%%====================================================================
+%%--------------------------------------------------------------------
+-spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled) ->
+ ssl_record:connection_states().
+%% %
+ %
+%% Description: Creates a connection_states record with appropriate
+%% values for the initial SSL connection setup.
+%%--------------------------------------------------------------------
+init_connection_states(Role, BeastMitigation) ->
+ ConnectionEnd = ssl_record:record_protocol_role(Role),
+ Initial = initial_connection_state(ConnectionEnd, BeastMitigation),
+ Current = Initial#{epoch := 0},
+ InitialPending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation),
+ Pending = InitialPending#{epoch => undefined},
+ #{saved_read => Current,
+ current_read => Current,
+ pending_read => Pending,
+ saved_write => Current,
+ current_write => Current,
+ pending_write => Pending}.
+
+%%--------------------------------------------------------------------
+-spec save_current_connection_state(ssl_record:connection_states(), read | write) ->
+ ssl_record:connection_states().
+%%
+%% Description: Returns the instance of the connection_state map
+%% where the current read|write state has been copied to the save state.
+%%--------------------------------------------------------------------
+save_current_connection_state(#{current_read := Current} = States, read) ->
+ States#{saved_read := Current};
+
+save_current_connection_state(#{current_write := Current} = States, write) ->
+ States#{saved_write := Current}.
+
+next_epoch(#{pending_read := Pending,
+ current_read := #{epoch := Epoch}} = States, read) ->
+ States#{pending_read := Pending#{epoch := Epoch + 1}};
+
+next_epoch(#{pending_write := Pending,
+ current_write := #{epoch := Epoch}} = States, write) ->
+ States#{pending_write := Pending#{epoch := Epoch + 1}}.
+
+get_connection_state_by_epoch(Epoch, #{current_write := #{epoch := Epoch} = Current},
+ write) ->
+ Current;
+get_connection_state_by_epoch(Epoch, #{saved_write := #{epoch := Epoch} = Saved},
+ write) ->
+ Saved;
+get_connection_state_by_epoch(Epoch, #{current_read := #{epoch := Epoch} = Current},
+ read) ->
+ Current;
+get_connection_state_by_epoch(Epoch, #{saved_read := #{epoch := Epoch} = Saved},
+ read) ->
+ Saved.
+
+set_connection_state_by_epoch(WriteState, Epoch, #{current_write := #{epoch := Epoch}} = States,
+ write) ->
+ States#{current_write := WriteState};
+set_connection_state_by_epoch(WriteState, Epoch, #{saved_write := #{epoch := Epoch}} = States,
+ write) ->
+ States#{saved_write := WriteState};
+set_connection_state_by_epoch(ReadState, Epoch, #{current_read := #{epoch := Epoch}} = States,
+ read) ->
+ States#{current_read := ReadState};
+set_connection_state_by_epoch(ReadState, Epoch, #{saved_read := #{epoch := Epoch}} = States,
+ read) ->
+ States#{saved_read := ReadState}.
%%--------------------------------------------------------------------
-spec get_dtls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}.
@@ -121,103 +190,57 @@ get_dtls_records_aux(Data, Acc) ->
?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
end.
-encode_plain_text(Type, Version, Data,
- #connection_states{current_write =
- #connection_state{
- epoch = Epoch,
- sequence_number = Seq,
- compression_state=CompS0,
- security_parameters=
- #security_parameters{
- cipher_type = ?AEAD,
- compression_algorithm=CompAlg}
- }= WriteState0} = ConnectionStates) ->
- {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#connection_state{compression_state = CompS1},
- AAD = calc_aad(Type, Version, Epoch, Seq),
- {CipherFragment, WriteState} = ssl_record:cipher_aead(dtls_v1:corresponding_tls_version(Version),
- Comp, WriteState1, AAD),
- CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment),
- {CipherText, ConnectionStates#connection_states{current_write =
- WriteState#connection_state{sequence_number = Seq +1}}};
-
-encode_plain_text(Type, Version, Data,
- #connection_states{current_write=#connection_state{
- epoch = Epoch,
- sequence_number = Seq,
- compression_state=CompS0,
- security_parameters=
- #security_parameters{compression_algorithm=CompAlg}
- }= WriteState0} = ConnectionStates) ->
- {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#connection_state{compression_state = CompS1},
- MacHash = calc_mac_hash(WriteState1, Type, Version, Epoch, Seq, Comp),
- {CipherFragment, WriteState} = ssl_record:cipher(dtls_v1:corresponding_tls_version(Version),
- Comp, WriteState1, MacHash),
- CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment),
- {CipherText, ConnectionStates#connection_states{current_write =
- WriteState#connection_state{sequence_number = Seq +1}}}.
+%%--------------------------------------------------------------------
+-spec encode_handshake(iolist(), dtls_version(), integer(), ssl_record:connection_states()) ->
+ {iolist(), ssl_record:connection_states()}.
+%
+%% Description: Encodes a handshake message to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_handshake(Frag, Version, Epoch, ConnectionStates) ->
+ encode_plain_text(?HANDSHAKE, Version, Epoch, Frag, ConnectionStates).
-decode_cipher_text(#ssl_tls{type = Type, version = Version,
- epoch = Epoch,
- sequence_number = Seq,
- fragment = CipherFragment} = CipherText,
- #connection_states{current_read =
- #connection_state{
- compression_state = CompressionS0,
- security_parameters=
- #security_parameters{
- cipher_type = ?AEAD,
- compression_algorithm=CompAlg}
- } = ReadState0}= ConnnectionStates0) ->
- AAD = calc_aad(Type, Version, Epoch, Seq),
- case ssl_record:decipher_aead(dtls_v1:corresponding_tls_version(Version),
- CipherFragment, ReadState0, AAD) of
- {PlainFragment, ReadState1} ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
- PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- compression_state = CompressionS1}},
- {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
- #alert{} = Alert ->
- Alert
- end;
-decode_cipher_text(#ssl_tls{type = Type, version = Version,
- epoch = Epoch,
- sequence_number = Seq,
- fragment = CipherFragment} = CipherText,
- #connection_states{current_read =
- #connection_state{
- compression_state = CompressionS0,
- security_parameters=
- #security_parameters{
- compression_algorithm=CompAlg}
- } = ReadState0}= ConnnectionStates0) ->
- {PlainFragment, Mac, ReadState1} = ssl_record:decipher(dtls_v1:corresponding_tls_version(Version),
- CipherFragment, ReadState0, true),
- MacHash = calc_mac_hash(ReadState1, Type, Version, Epoch, Seq, PlainFragment),
- case ssl_record:is_correct_mac(Mac, MacHash) of
- true ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
- PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- compression_state = CompressionS1}},
- {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
- false ->
- ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
- end.
+%%--------------------------------------------------------------------
+-spec encode_alert_record(#alert{}, dtls_version(), ssl_record:connection_states()) ->
+ {iolist(), ssl_record:connection_states()}.
+%%
+%% Description: Encodes an alert message to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_alert_record(#alert{level = Level, description = Description},
+ Version, ConnectionStates) ->
+ #{epoch := Epoch} = ssl_record:current_connection_state(ConnectionStates, write),
+ encode_plain_text(?ALERT, Version, Epoch, <<?BYTE(Level), ?BYTE(Description)>>,
+ ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_change_cipher_spec(dtls_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
+-spec encode_change_cipher_spec(dtls_version(), integer(), ssl_record:connection_states()) ->
+ {iolist(), ssl_record:connection_states()}.
%%
%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
%%--------------------------------------------------------------------
-encode_change_cipher_spec(Version, ConnectionStates) ->
- encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
+encode_change_cipher_spec(Version, Epoch, ConnectionStates) ->
+ encode_plain_text(?CHANGE_CIPHER_SPEC, Version, Epoch, ?byte(?CHANGE_CIPHER_SPEC_PROTO), ConnectionStates).
+
+%%--------------------------------------------------------------------
+-spec encode_data(binary(), dtls_version(), ssl_record:connection_states()) ->
+ {iolist(),ssl_record:connection_states()}.
+%%
+%% Description: Encodes data to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_data(Data, Version, ConnectionStates) ->
+ #{epoch := Epoch} = ssl_record:current_connection_state(ConnectionStates, write),
+ encode_plain_text(?APPLICATION_DATA, Version, Epoch, Data, ConnectionStates).
+
+encode_plain_text(Type, Version, Epoch, Data, ConnectionStates) ->
+ Write0 = get_connection_state_by_epoch(Epoch, ConnectionStates, write),
+ {CipherFragment, Write1} = encode_plain_text(Type, Version, Data, Write0),
+ {CipherText, Write} = encode_dtls_cipher_text(Type, Version, CipherFragment, Write1),
+ {CipherText, set_connection_state_by_epoch(Write, Epoch, ConnectionStates, write)}.
+
+
+decode_cipher_text(#ssl_tls{epoch = Epoch} = CipherText, ConnnectionStates0) ->
+ ReadState = get_connection_state_by_epoch(Epoch, ConnnectionStates0, read),
+ decode_cipher_text(CipherText, ReadState, ConnnectionStates0).
%%--------------------------------------------------------------------
-spec protocol_version(dtls_atom_version() | dtls_version()) ->
@@ -352,92 +375,49 @@ is_acceptable_version(Version, Versions) ->
%%--------------------------------------------------------------------
--spec init_connection_state_seq(dtls_version(), #connection_states{}) ->
- #connection_state{}.
+-spec init_connection_state_seq(dtls_version(), ssl_record:connection_states()) ->
+ ssl_record:connection_state().
%%
%% Description: Copy the read sequence number to the write sequence number
%% This is only valid for DTLS in the first client_hello
%%--------------------------------------------------------------------
init_connection_state_seq({254, _},
- #connection_states{
- current_read = Read = #connection_state{epoch = 0},
- current_write = Write = #connection_state{epoch = 0}} = CS0) ->
- CS0#connection_states{current_write =
- Write#connection_state{
- sequence_number = Read#connection_state.sequence_number}};
-init_connection_state_seq(_, CS) ->
- CS.
+ #{current_read := #{epoch := 0, sequence_number := Seq},
+ current_write := #{epoch := 0} = Write} = ConnnectionStates0) ->
+ ConnnectionStates0#{current_write => Write#{sequence_number => Seq}};
+init_connection_state_seq(_, ConnnectionStates) ->
+ ConnnectionStates.
%%--------------------------------------------------------
--spec current_connection_state_epoch(#connection_states{}, read | write) ->
+-spec current_connection_state_epoch(ssl_record:connection_states(), read | write) ->
integer().
%%
%% Description: Returns the epoch the connection_state record
%% that is currently defined as the current conection state.
%%--------------------------------------------------------------------
-current_connection_state_epoch(#connection_states{current_read = Current},
+current_connection_state_epoch(#{current_read := #{epoch := Epoch}},
read) ->
- Current#connection_state.epoch;
-current_connection_state_epoch(#connection_states{current_write = Current},
+ Epoch;
+current_connection_state_epoch(#{current_write := #{epoch := Epoch}},
write) ->
- Current#connection_state.epoch.
-
-%%--------------------------------------------------------------------
-
--spec connection_state_by_epoch(#connection_states{}, integer(), read | write) ->
- #connection_state{}.
-%%
-%% Description: Returns the instance of the connection_state record
-%% that is defined by the Epoch.
-%%--------------------------------------------------------------------
-connection_state_by_epoch(#connection_states{current_read = CS}, Epoch, read)
- when CS#connection_state.epoch == Epoch ->
- CS;
-connection_state_by_epoch(#connection_states{pending_read = CS}, Epoch, read)
- when CS#connection_state.epoch == Epoch ->
- CS;
-connection_state_by_epoch(#connection_states{current_write = CS}, Epoch, write)
- when CS#connection_state.epoch == Epoch ->
- CS;
-connection_state_by_epoch(#connection_states{pending_write = CS}, Epoch, write)
- when CS#connection_state.epoch == Epoch ->
- CS.
-%%--------------------------------------------------------------------
--spec set_connection_state_by_epoch(#connection_states{},
- #connection_state{}, read | write)
- -> #connection_states{}.
-%%
-%% Description: Returns the instance of the connection_state record
-%% that is defined by the Epoch.
-%%--------------------------------------------------------------------
-set_connection_state_by_epoch(ConnectionStates0 =
- #connection_states{current_read = CS},
- NewCS = #connection_state{epoch = Epoch}, read)
- when CS#connection_state.epoch == Epoch ->
- ConnectionStates0#connection_states{current_read = NewCS};
-
-set_connection_state_by_epoch(ConnectionStates0 =
- #connection_states{pending_read = CS},
- NewCS = #connection_state{epoch = Epoch}, read)
- when CS#connection_state.epoch == Epoch ->
- ConnectionStates0#connection_states{pending_read = NewCS};
-
-set_connection_state_by_epoch(ConnectionStates0 =
- #connection_states{current_write = CS},
- NewCS = #connection_state{epoch = Epoch}, write)
- when CS#connection_state.epoch == Epoch ->
- ConnectionStates0#connection_states{current_write = NewCS};
-
-set_connection_state_by_epoch(ConnectionStates0 =
- #connection_states{pending_write = CS},
- NewCS = #connection_state{epoch = Epoch}, write)
- when CS#connection_state.epoch == Epoch ->
- ConnectionStates0#connection_states{pending_write = NewCS}.
+ Epoch.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-
+initial_connection_state(ConnectionEnd, BeastMitigation) ->
+ #{security_parameters =>
+ ssl_record:initial_security_params(ConnectionEnd),
+ epoch => undefined,
+ sequence_number => 0,
+ beast_mitigation => BeastMitigation,
+ compression_state => undefined,
+ cipher_state => undefined,
+ mac_secret => undefined,
+ secure_renegotiation => undefined,
+ client_verify_data => undefined,
+ server_verify_data => undefined
+ }.
lowest_list_protocol_version(Ver, []) ->
Ver;
@@ -449,14 +429,85 @@ highest_list_protocol_version(Ver, []) ->
highest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
highest_list_protocol_version(highest_protocol_version(Ver1, Ver2), Rest).
-encode_tls_cipher_text(Type, {MajVer, MinVer}, Epoch, Seq, Fragment) ->
+encode_dtls_cipher_text(Type, {MajVer, MinVer}, Fragment,
+ #{epoch := Epoch, sequence_number := Seq} = WriteState) ->
Length = erlang:iolist_size(Fragment),
- [<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Epoch),
- ?UINT48(Seq), ?UINT16(Length)>>, Fragment].
+ {[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Epoch),
+ ?UINT48(Seq), ?UINT16(Length)>>, Fragment],
+ WriteState#{sequence_number => Seq + 1}}.
+
+encode_plain_text(Type, Version, Data, #{compression_state := CompS0,
+ epoch := Epoch,
+ sequence_number := Seq,
+ security_parameters :=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm = CompAlg}
+ } = WriteState0) ->
+ {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
+ WriteState1 = WriteState0#{compression_state => CompS1},
+ AAD = calc_aad(Type, Version, Epoch, Seq),
+ ssl_record:cipher_aead(dtls_v1:corresponding_tls_version(Version), Comp, WriteState1, AAD);
+encode_plain_text(Type, Version, Data, #{compression_state := CompS0,
+ epoch := Epoch,
+ sequence_number := Seq,
+ security_parameters :=
+ #security_parameters{compression_algorithm = CompAlg}
+ }= WriteState0) ->
+ {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
+ WriteState1 = WriteState0#{compression_state => CompS1},
+ MacHash = calc_mac_hash(Type, Version, WriteState1, Epoch, Seq, Comp),
+ ssl_record:cipher(dtls_v1:corresponding_tls_version(Version), Comp, WriteState1, MacHash).
+
+decode_cipher_text(#ssl_tls{type = Type, version = Version,
+ epoch = Epoch,
+ sequence_number = Seq,
+ fragment = CipherFragment} = CipherText,
+ #{compression_state := CompressionS0,
+ security_parameters :=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm = CompAlg}} = ReadState0,
+ ConnnectionStates0) ->
+ AAD = calc_aad(Type, Version, Epoch, Seq),
+ case ssl_record:decipher_aead(dtls_v1:corresponding_tls_version(Version),
+ CipherFragment, ReadState0, AAD) of
+ {PlainFragment, ReadState1} ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
+ PlainFragment, CompressionS0),
+ ReadState = ReadState1#{compression_state => CompressionS1},
+ ConnnectionStates = set_connection_state_by_epoch(ReadState, Epoch, ConnnectionStates0, read),
+ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end;
+decode_cipher_text(#ssl_tls{type = Type, version = Version,
+ epoch = Epoch,
+ sequence_number = Seq,
+ fragment = CipherFragment} = CipherText,
+ #{compression_state := CompressionS0,
+ security_parameters :=
+ #security_parameters{
+ compression_algorithm = CompAlg}} = ReadState0,
+ ConnnectionStates0) ->
+ {PlainFragment, Mac, ReadState1} = ssl_record:decipher(dtls_v1:corresponding_tls_version(Version),
+ CipherFragment, ReadState0, true),
+ MacHash = calc_mac_hash(Type, Version, ReadState1, Epoch, Seq, PlainFragment),
+ case ssl_record:is_correct_mac(Mac, MacHash) of
+ true ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
+ PlainFragment, CompressionS0),
+
+ ReadState = ReadState1#{compression_state => CompressionS1},
+ ConnnectionStates = set_connection_state_by_epoch(ReadState, Epoch, ConnnectionStates0, read),
+ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end.
-calc_mac_hash(#connection_state{mac_secret = MacSecret,
- security_parameters = #security_parameters{mac_algorithm = MacAlg}},
- Type, Version, Epoch, SeqNo, Fragment) ->
+calc_mac_hash(Type, Version, #{mac_secret := MacSecret,
+ security_parameters := #security_parameters{mac_algorithm = MacAlg}},
+ Epoch, SeqNo, Fragment) ->
Length = erlang:iolist_size(Fragment),
NewSeq = (Epoch bsl 48) + SeqNo,
mac_hash(Version, MacAlg, MacSecret, NewSeq, Type,
diff --git a/lib/ssl/src/dtls_record.hrl b/lib/ssl/src/dtls_record.hrl
index b9f84cbe7f..373481c3f8 100644
--- a/lib/ssl/src/dtls_record.hrl
+++ b/lib/ssl/src/dtls_record.hrl
@@ -34,11 +34,10 @@
-record(ssl_tls, {
type,
version,
- epoch,
- sequence_number,
- offset,
- length,
- fragment
+ %%length,
+ fragment,
+ epoch,
+ sequence_number
}).
-endif. % -ifdef(dtls_record).
diff --git a/lib/ssl/src/dtls_socket.erl b/lib/ssl/src/dtls_socket.erl
new file mode 100644
index 0000000000..2a746d97f0
--- /dev/null
+++ b/lib/ssl/src/dtls_socket.erl
@@ -0,0 +1,155 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016-2017. 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(dtls_socket).
+
+-include("ssl_internal.hrl").
+-include("ssl_api.hrl").
+
+-export([send/3, listen/3, accept/3, connect/4, socket/4, setopts/3, getopts/3, getstat/3,
+ peername/2, sockname/2, port/2, close/2]).
+-export([emulated_options/0, internal_inet_values/0, default_inet_values/0, default_cb_info/0]).
+
+send(Transport, {{IP,Port},Socket}, Data) ->
+ Transport:send(Socket, IP, Port, Data).
+
+listen(gen_udp = Transport, Port, #config{transport_info = {Transport, _, _, _},
+ ssl = SslOpts,
+ emulated = EmOpts,
+ inet_user = Options} = Config) ->
+
+
+ case dtls_udp_sup:start_child([Port, emulated_socket_options(EmOpts, #socket_options{}),
+ Options ++ internal_inet_values(), SslOpts]) of
+ {ok, Pid} ->
+ {ok, #sslsocket{pid = {udp, Config#config{udp_handler = {Pid, Port}}}}};
+ Err = {error, _} ->
+ Err
+ end.
+
+accept(udp, #config{transport_info = {Transport = gen_udp,_,_,_},
+ connection_cb = ConnectionCb,
+ udp_handler = {Listner, _}}, _Timeout) ->
+ case dtls_udp_listener:accept(Listner, self()) of
+ {ok, Pid, Socket} ->
+ {ok, socket(Pid, Transport, {Listner, Socket}, ConnectionCb)};
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+connect(Address, Port, #config{transport_info = {Transport, _, _, _} = CbInfo,
+ connection_cb = ConnectionCb,
+ ssl = SslOpts,
+ emulated = EmOpts,
+ inet_ssl = SocketOpts}, Timeout) ->
+ case Transport:open(0, SocketOpts ++ internal_inet_values()) of
+ {ok, Socket} ->
+ ssl_connection:connect(ConnectionCb, Address, Port, {{Address, Port},Socket},
+ {SslOpts,
+ emulated_socket_options(EmOpts, #socket_options{}), undefined},
+ self(), CbInfo, Timeout);
+ {error, _} = Error->
+ Error
+ end.
+
+close(gen_udp, {_Client, _Socket}) ->
+ ok.
+
+socket(Pid, gen_udp = Transport, {{_, _}, Socket}, ConnectionCb) ->
+ #sslsocket{pid = Pid,
+ %% "The name "fd" is keept for backwards compatibility
+ fd = {Transport, Socket, ConnectionCb}};
+socket(Pid, Transport, Socket, ConnectionCb) ->
+ #sslsocket{pid = Pid,
+ %% "The name "fd" is keept for backwards compatibility
+ fd = {Transport, Socket, ConnectionCb}}.
+%% Vad göra med emulerade
+setopts(gen_udp, #sslsocket{pid = {Socket, _}}, Options) ->
+ {SockOpts, _} = tls_socket:split_options(Options),
+ inet:setopts(Socket, SockOpts);
+setopts(_, #sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}}}}, Options) ->
+ {SockOpts, _} = tls_socket:split_options(Options),
+ Transport:setopts(ListenSocket, SockOpts);
+%%% Following clauses will not be called for emulated options, they are handled in the connection process
+setopts(gen_udp, Socket, Options) ->
+ inet:setopts(Socket, Options);
+setopts(Transport, Socket, Options) ->
+ Transport:setopts(Socket, Options).
+
+getopts(gen_udp, #sslsocket{pid = {Socket, #config{emulated = EmOpts}}}, Options) ->
+ {SockOptNames, EmulatedOptNames} = tls_socket:split_options(Options),
+ EmulatedOpts = get_emulated_opts(EmOpts, EmulatedOptNames),
+ SocketOpts = tls_socket:get_socket_opts(Socket, SockOptNames, inet),
+ {ok, EmulatedOpts ++ SocketOpts};
+getopts(Transport, #sslsocket{pid = {ListenSocket, #config{emulated = EmOpts}}}, Options) ->
+ {SockOptNames, EmulatedOptNames} = tls_socket:split_options(Options),
+ EmulatedOpts = get_emulated_opts(EmOpts, EmulatedOptNames),
+ SocketOpts = tls_socket:get_socket_opts(ListenSocket, SockOptNames, Transport),
+ {ok, EmulatedOpts ++ SocketOpts};
+%%% Following clauses will not be called for emulated options, they are handled in the connection process
+getopts(gen_udp, {_,Socket}, Options) ->
+ inet:getopts(Socket, Options);
+getopts(Transport, Socket, Options) ->
+ Transport:getopts(Socket, Options).
+getstat(gen_udp, {_,Socket}, Options) ->
+ inet:getstat(Socket, Options);
+getstat(Transport, Socket, Options) ->
+ Transport:getstat(Socket, Options).
+peername(udp, _) ->
+ {error, enotconn};
+peername(gen_udp, {_, {Client, _Socket}}) ->
+ {ok, Client};
+peername(Transport, Socket) ->
+ Transport:peername(Socket).
+sockname(gen_udp, {_, {_,Socket}}) ->
+ inet:sockname(Socket);
+sockname(gen_udp, Socket) ->
+ inet:sockname(Socket);
+sockname(Transport, Socket) ->
+ Transport:sockname(Socket).
+
+port(gen_udp, {_,Socket}) ->
+ inet:port(Socket);
+port(Transport, Socket) ->
+ Transport:port(Socket).
+
+emulated_options() ->
+ [mode, active, packet, packet_size].
+
+internal_inet_values() ->
+ [{active, false}, {mode,binary}].
+
+default_inet_values() ->
+ [{active, true}, {mode, list}].
+
+default_cb_info() ->
+ {gen_udp, udp, udp_closed, udp_error}.
+
+get_emulated_opts(EmOpts, EmOptNames) ->
+ lists:map(fun(Name) -> {value, Value} = lists:keysearch(Name, 1, EmOpts),
+ Value end,
+ EmOptNames).
+
+emulated_socket_options(InetValues, #socket_options{
+ mode = Mode,
+ active = Active}) ->
+ #socket_options{
+ mode = proplists:get_value(mode, InetValues, Mode),
+ active = proplists:get_value(active, InetValues, Active)
+ }.
diff --git a/lib/ssl/src/dtls_udp_listener.erl b/lib/ssl/src/dtls_udp_listener.erl
new file mode 100644
index 0000000000..f0ace2d887
--- /dev/null
+++ b/lib/ssl/src/dtls_udp_listener.erl
@@ -0,0 +1,249 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016-2017. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(dtls_udp_listener).
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/4, active_once/3, accept/2, sockname/1, close/1,
+ get_all_opts/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state,
+ {port,
+ listner,
+ dtls_options,
+ emulated_options,
+ dtls_msq_queues = kv_new(),
+ clients = set_new(),
+ dtls_processes = kv_new(),
+ accepters = queue:new(),
+ first,
+ close
+ }).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+start_link(Port, EmOpts, InetOptions, DTLSOptions) ->
+ gen_server:start_link(?MODULE, [Port, EmOpts, InetOptions, DTLSOptions], []).
+
+active_once(UDPConnection, Client, Pid) ->
+ gen_server:cast(UDPConnection, {active_once, Client, Pid}).
+
+accept(UDPConnection, Accepter) ->
+ call(UDPConnection, {accept, Accepter}).
+
+sockname(UDPConnection) ->
+ call(UDPConnection, sockname).
+close(UDPConnection) ->
+ call(UDPConnection, close).
+get_all_opts(UDPConnection) ->
+ call(UDPConnection, get_all_opts).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+init([Port, EmOpts, InetOptions, DTLSOptions]) ->
+ try
+ {ok, Socket} = gen_udp:open(Port, InetOptions),
+ {ok, #state{port = Port,
+ first = true,
+ dtls_options = DTLSOptions,
+ emulated_options = EmOpts,
+ listner = Socket,
+ close = false}}
+ catch _:_ ->
+ {error, closed}
+ end.
+handle_call({accept, _}, _, #state{close = true} = State) ->
+ {reply, {error, closed}, State};
+
+handle_call({accept, Accepter}, From, #state{first = true,
+ accepters = Accepters,
+ listner = Socket} = State0) ->
+ next_datagram(Socket),
+ State = State0#state{first = false,
+ accepters = queue:in({Accepter, From}, Accepters)},
+ {noreply, State};
+
+handle_call({accept, Accepter}, From, #state{accepters = Accepters} = State0) ->
+ State = State0#state{accepters = queue:in({Accepter, From}, Accepters)},
+ {noreply, State};
+handle_call(sockname, _, #state{listner = Socket} = State) ->
+ Reply = inet:sockname(Socket),
+ {reply, Reply, State};
+handle_call(close, _, #state{dtls_processes = Processes,
+ accepters = Accepters} = State) ->
+ case kv_empty(Processes) of
+ true ->
+ {stop, normal, ok, State#state{close=true}};
+ false ->
+ lists:foreach(fun({_, From}) ->
+ gen_server:reply(From, {error, closed})
+ end, queue:to_list(Accepters)),
+ {reply, ok, State#state{close = true, accepters = queue:new()}}
+ end;
+handle_call(get_all_opts, _, #state{dtls_options = DTLSOptions,
+ emulated_options = EmOpts} = State) ->
+ {reply, {ok, EmOpts, DTLSOptions}, State}.
+
+handle_cast({active_once, Client, Pid}, State0) ->
+ State = handle_active_once(Client, Pid, State0),
+ {noreply, State}.
+
+handle_info({udp, Socket, IP, InPortNo, _} = Msg, #state{listner = Socket} = State0) ->
+ State = handle_datagram({IP, InPortNo}, Msg, State0),
+ next_datagram(Socket),
+ {noreply, State};
+
+handle_info({'DOWN', _, process, Pid, _}, #state{clients = Clients,
+ dtls_processes = Processes0,
+ close = ListenClosed} = State) ->
+ Client = kv_get(Pid, Processes0),
+ Processes = kv_delete(Pid, Processes0),
+ case ListenClosed andalso kv_empty(Processes) of
+ true ->
+ {stop, normal, State};
+ false ->
+ {noreply, State#state{clients = set_delete(Client, Clients),
+ dtls_processes = Processes}}
+ end.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+handle_datagram(Client, Msg, #state{clients = Clients,
+ accepters = AcceptorsQueue0} = State) ->
+ case set_is_member(Client, Clients) of
+ false ->
+ case queue:out(AcceptorsQueue0) of
+ {{value, {UserPid, From}}, AcceptorsQueue} ->
+ setup_new_connection(UserPid, From, Client, Msg,
+ State#state{accepters = AcceptorsQueue});
+ {empty, _} ->
+ %% Drop packet client will resend
+ State
+ end;
+ true ->
+ dispatch(Client, Msg, State)
+ end.
+
+dispatch(Client, Msg, #state{dtls_msq_queues = MsgQueues} = State) ->
+ case kv_lookup(Client, MsgQueues) of
+ {value, Queue0} ->
+ case queue:out(Queue0) of
+ {{value, Pid}, Queue} when is_pid(Pid) ->
+ Pid ! Msg,
+ State#state{dtls_msq_queues =
+ kv_update(Client, Queue, MsgQueues)};
+ {{value, _}, Queue} ->
+ State#state{dtls_msq_queues =
+ kv_update(Client, queue:in(Msg, Queue), MsgQueues)};
+ {empty, Queue} ->
+ State#state{dtls_msq_queues =
+ kv_update(Client, queue:in(Msg, Queue), MsgQueues)}
+ end
+ end.
+next_datagram(Socket) ->
+ inet:setopts(Socket, [{active, once}]).
+
+handle_active_once(Client, Pid, #state{dtls_msq_queues = MsgQueues} = State0) ->
+ Queue0 = kv_get(Client, MsgQueues),
+ case queue:out(Queue0) of
+ {{value, Pid}, _} when is_pid(Pid) ->
+ State0;
+ {{value, Msg}, Queue} ->
+ Pid ! Msg,
+ State0#state{dtls_msq_queues = kv_update(Client, Queue, MsgQueues)};
+ {empty, Queue0} ->
+ State0#state{dtls_msq_queues = kv_update(Client, queue:in(Pid, Queue0), MsgQueues)}
+ end.
+
+setup_new_connection(User, From, Client, Msg, #state{dtls_processes = Processes,
+ clients = Clients,
+ dtls_msq_queues = MsgQueues,
+ dtls_options = DTLSOpts,
+ port = Port,
+ listner = Socket,
+ emulated_options = EmOpts} = State) ->
+ ConnArgs = [server, "localhost", Port, {self(), {Client, Socket}},
+ {DTLSOpts, EmOpts, udp_listner}, User, dtls_socket:default_cb_info()],
+ case dtls_connection_sup:start_child(ConnArgs) of
+ {ok, Pid} ->
+ erlang:monitor(process, Pid),
+ gen_server:reply(From, {ok, Pid, {Client, Socket}}),
+ Pid ! Msg,
+ State#state{clients = set_insert(Client, Clients),
+ dtls_msq_queues = kv_insert(Client, queue:new(), MsgQueues),
+ dtls_processes = kv_insert(Pid, Client, Processes)};
+ {error, Reason} ->
+ gen_server:reply(From, {error, Reason}),
+ State
+ end.
+
+kv_update(Key, Value, Store) ->
+ gb_trees:update(Key, Value, Store).
+kv_lookup(Key, Store) ->
+ gb_trees:lookup(Key, Store).
+kv_insert(Key, Value, Store) ->
+ gb_trees:insert(Key, Value, Store).
+kv_get(Key, Store) ->
+ gb_trees:get(Key, Store).
+kv_delete(Key, Store) ->
+ gb_trees:delete(Key, Store).
+kv_new() ->
+ gb_trees:empty().
+kv_empty(Store) ->
+ gb_trees:is_empty(Store).
+
+set_new() ->
+ gb_sets:empty().
+set_insert(Item, Set) ->
+ gb_sets:insert(Item, Set).
+set_delete(Item, Set) ->
+ gb_sets:delete(Item, Set).
+set_is_member(Item, Set) ->
+ gb_sets:is_member(Item, Set).
+
+call(Server, Msg) ->
+ try
+ gen_server:call(Server, Msg, infinity)
+ catch
+ exit:{noproc, _} ->
+ {error, closed};
+ exit:{normal, _} ->
+ {error, closed};
+ exit:{{shutdown, _},_} ->
+ {error, closed}
+ end.
diff --git a/lib/ssl/src/dtls_udp_sup.erl b/lib/ssl/src/dtls_udp_sup.erl
new file mode 100644
index 0000000000..197882e92f
--- /dev/null
+++ b/lib/ssl/src/dtls_udp_sup.erl
@@ -0,0 +1,62 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016-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%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Supervisor for a procsses dispatching upd datagrams to
+%% correct DTLS handler
+%%----------------------------------------------------------------------
+-module(dtls_udp_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+-export([start_child/1]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+start_child(Args) ->
+ supervisor:start_child(?MODULE, Args).
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+init(_O) ->
+ RestartStrategy = simple_one_for_one,
+ MaxR = 0,
+ MaxT = 3600,
+
+ Name = undefined, % As simple_one_for_one is used.
+ StartFunc = {dtls_udp_listener, start_link, []},
+ Restart = temporary, % E.g. should not be restarted
+ Shutdown = 4000,
+ Modules = [dtls_udp_listener],
+ Type = worker,
+
+ ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
+ {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}.
diff --git a/lib/ssl/src/dtls_v1.erl b/lib/ssl/src/dtls_v1.erl
index 8c03bda513..7169477a82 100644
--- a/lib/ssl/src/dtls_v1.erl
+++ b/lib/ssl/src/dtls_v1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. 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.
@@ -21,15 +21,24 @@
-include("ssl_cipher.hrl").
--export([suites/1, mac_hash/7, ecc_curves/1, corresponding_tls_version/1]).
+-export([suites/1, all_suites/1, mac_hash/7, ecc_curves/1,
+ corresponding_tls_version/1, corresponding_dtls_version/1]).
-spec suites(Minor:: 253|255) -> [ssl_cipher:cipher_suite()].
suites(Minor) ->
- tls_v1:suites(corresponding_minor_tls_version(Minor)).
+ lists:filter(fun(Cipher) ->
+ is_acceptable_cipher(ssl_cipher:suite_definition(Cipher))
+ end,
+ tls_v1:suites(corresponding_minor_tls_version(Minor))).
+all_suites(Version) ->
+ lists:filter(fun(Cipher) ->
+ is_acceptable_cipher(ssl_cipher:suite_definition(Cipher))
+ end,
+ ssl_cipher:all_suites(corresponding_tls_version(Version))).
mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
- tls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, corresponding_tls_version(Version),
+ tls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
Length, Fragment).
ecc_curves({_Major, Minor}) ->
@@ -42,3 +51,13 @@ corresponding_minor_tls_version(255) ->
2;
corresponding_minor_tls_version(253) ->
3.
+
+corresponding_dtls_version({3, Minor}) ->
+ {254, corresponding_minor_dtls_version(Minor)}.
+
+corresponding_minor_dtls_version(2) ->
+ 255;
+corresponding_minor_dtls_version(3) ->
+ 253.
+is_acceptable_cipher(Suite) ->
+ not ssl_cipher:is_stream_ciphersuite(Suite).
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index b26efbd88f..064dcd6892 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -6,14 +6,20 @@
tls_connection,
tls_handshake,
tls_record,
+ tls_socket,
tls_v1,
ssl_v3,
ssl_v2,
+ tls_connection_sup,
%% DTLS
dtls_connection,
dtls_handshake,
dtls_record,
+ dtls_socket,
dtls_v1,
+ dtls_connection_sup,
+ dtls_udp_listener,
+ dtls_udp_sup,
%% API
ssl, %% Main API
tls, %% TLS specific
@@ -27,17 +33,19 @@
ssl_cipher,
ssl_srp_primes,
ssl_alert,
- ssl_socket,
- ssl_listen_tracker_sup,
+ ssl_listen_tracker_sup, %% may be used by DTLS over SCTP
%% Erlang Distribution over SSL/TLS
inet_tls_dist,
inet6_tls_dist,
ssl_tls_dist_proxy,
ssl_dist_sup,
- %% SSL/TLS session handling
+ ssl_dist_connection_sup,
+ ssl_dist_admin_sup,
+ %% SSL/TLS session and cert handling
ssl_session,
ssl_session_cache,
ssl_manager,
+ ssl_pem_cache,
ssl_pkix_db,
ssl_certificate,
%% CRL handling
@@ -48,14 +56,14 @@
%% App structure
ssl_app,
ssl_sup,
- tls_connection_sup,
- dtls_connection_sup
+ ssl_admin_sup,
+ ssl_connection_sup
]},
{registered, [ssl_sup, ssl_manager]},
{applications, [crypto, public_key, kernel, stdlib]},
{env, []},
{mod, {ssl_app, []}},
- {runtime_dependencies, ["stdlib-3.0","public_key-1.2","kernel-3.0",
+ {runtime_dependencies, ["stdlib-3.2","public_key-1.2","kernel-3.0",
"erts-7.0","crypto-3.3", "inets-5.10.7"]}]}.
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 11728128c4..bfdd0c205b 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,6 +1,7 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {<<"8\\..*">>, [{restart_application, ssl}]},
{<<"7\\..*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
@@ -8,6 +9,7 @@
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
+ {<<"8\\..*">>, [{restart_application, ssl}]},
{<<"7\\..*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
@@ -15,4 +17,3 @@
{<<"3\\..*">>, [{restart_application, ssl}]}
]
}.
-
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index d2aeb3258f..9a106f9742 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2017. 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.
@@ -38,12 +38,12 @@
getopts/2, setopts/2, getstat/1, getstat/2
]).
%% SSL/TLS protocol handling
--export([cipher_suites/0, cipher_suites/1,
+-export([cipher_suites/0, cipher_suites/1, eccs/0, eccs/1,
connection_info/1, versions/0, session_info/1, format_error/1,
renegotiate/1, prf/5, negotiated_protocol/1, negotiated_next_protocol/1,
connection_information/1, connection_information/2]).
%% Misc
--export([handle_options/2]).
+-export([handle_options/2, tls_version/1]).
-deprecated({negotiated_next_protocol, 1, next_major_release}).
-deprecated({connection_info, 1, next_major_release}).
@@ -101,33 +101,27 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
{Transport,_,_,_} = proplists:get_value(cb_info, SslOptions0,
{gen_tcp, tcp, tcp_closed, tcp_error}),
- EmulatedOptions = ssl_socket:emulated_options(),
- {ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
+ EmulatedOptions = tls_socket:emulated_options(),
+ {ok, SocketValues} = tls_socket:getopts(Transport, Socket, EmulatedOptions),
try handle_options(SslOptions0 ++ SocketValues, client) of
- {ok, #config{transport_info = CbInfo, ssl = SslOptions, emulated = EmOpts,
- connection_cb = ConnectionCb}} ->
-
- ok = ssl_socket:setopts(Transport, Socket, ssl_socket:internal_inet_values()),
- case ssl_socket:peername(Transport, Socket) of
- {ok, {Address, Port}} ->
- ssl_connection:connect(ConnectionCb, Address, Port, Socket,
- {SslOptions, emulated_socket_options(EmOpts, #socket_options{}), undefined},
- self(), CbInfo, Timeout);
- {error, Error} ->
- {error, Error}
- end
+ {ok, Config} ->
+ tls_socket:upgrade(Socket, Config, Timeout)
catch
_:{error, Reason} ->
{error, Reason}
end;
-
connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
- try handle_options(Options, client) of
- {ok, Config} ->
- do_connect(Host,Port,Config,Timeout)
+ try
+ {ok, Config} = handle_options(Options, client),
+ case Config#config.connection_cb of
+ tls_connection ->
+ tls_socket:connect(Host,Port,Config,Timeout);
+ dtls_connection ->
+ dtls_socket:connect(Host,Port,Config,Timeout)
+ end
catch
throw:Error ->
Error
@@ -144,17 +138,7 @@ listen(_Port, []) ->
listen(Port, Options0) ->
try
{ok, Config} = handle_options(Options0, server),
- ConnectionCb = connection_cb(Options0),
- #config{transport_info = {Transport, _, _, _}, inet_user = Options, connection_cb = ConnectionCb,
- ssl = SslOpts, emulated = EmOpts} = Config,
- case Transport:listen(Port, Options) of
- {ok, ListenSocket} ->
- ok = ssl_socket:setopts(Transport, ListenSocket, ssl_socket:internal_inet_values()),
- {ok, Tracker} = ssl_socket:inherit_tracker(ListenSocket, EmOpts, SslOpts),
- {ok, #sslsocket{pid = {ListenSocket, Config#config{emulated = Tracker}}}};
- Err = {error, _} ->
- Err
- end
+ do_listen(Port, Config, connection_cb(Options0))
catch
Error = {error, _} ->
Error
@@ -171,27 +155,15 @@ transport_accept(ListenSocket) ->
transport_accept(ListenSocket, infinity).
transport_accept(#sslsocket{pid = {ListenSocket,
- #config{transport_info = {Transport,_,_, _} =CbInfo,
- connection_cb = ConnectionCb,
- ssl = SslOpts,
- emulated = Tracker}}}, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
- case Transport:accept(ListenSocket, Timeout) of
- {ok, Socket} ->
- {ok, EmOpts} = ssl_socket:get_emulated_opts(Tracker),
- {ok, Port} = ssl_socket:port(Transport, Socket),
- ConnArgs = [server, "localhost", Port, Socket,
- {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), Tracker}, self(), CbInfo],
- ConnectionSup = connection_sup(ConnectionCb),
- case ConnectionSup:start_child(ConnArgs) of
- {ok, Pid} ->
- ssl_connection:socket_control(ConnectionCb, Socket, Pid, Transport, Tracker);
- {error, Reason} ->
- {error, Reason}
- end;
- {error, Reason} ->
- {error, Reason}
+ #config{connection_cb = ConnectionCb} = Config}}, Timeout)
+ when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+ case ConnectionCb of
+ tls_connection ->
+ tls_socket:accept(ListenSocket, Config, Timeout);
+ dtls_connection ->
+ dtls_socket:accept(ListenSocket, Config, Timeout)
end.
-
+
%%--------------------------------------------------------------------
-spec ssl_accept(#sslsocket{}) -> ok | {error, reason()}.
-spec ssl_accept(#sslsocket{} | port(), timeout()| [ssl_option()
@@ -214,13 +186,22 @@ ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
ssl_accept(ListenSocket, SslOptions, infinity).
ssl_accept(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
- ssl_accept(#sslsocket{} = Socket, Timeout);
-ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) when
+ ssl_accept(Socket, Timeout);
+ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts, Timeout) when
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
try
- {ok, EmOpts, InheritedSslOpts} = ssl_socket:get_all_opts(Tracker),
- SslOpts = handle_options(SslOpts0, InheritedSslOpts),
- ssl_connection:handshake(Socket, {SslOpts, emulated_socket_options(EmOpts, #socket_options{})}, Timeout)
+ {ok, EmOpts, _} = tls_socket:get_all_opts(Tracker),
+ ssl_connection:handshake(Socket, {SslOpts,
+ tls_socket:emulated_socket_options(EmOpts, #socket_options{})}, Timeout)
+ catch
+ Error = {error, _Reason} -> Error
+ end;
+ssl_accept(#sslsocket{pid = Pid, fd = {_, _, _}} = Socket, SslOpts, Timeout) when
+ (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
+ try
+ {ok, EmOpts, _} = dtls_udp_listener:get_all_opts(Pid),
+ ssl_connection:handshake(Socket, {SslOpts,
+ tls_socket:emulated_socket_options(EmOpts, #socket_options{})}, Timeout)
catch
Error = {error, _Reason} -> Error
end;
@@ -228,20 +209,20 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
{Transport,_,_,_} =
proplists:get_value(cb_info, SslOptions, {gen_tcp, tcp, tcp_closed, tcp_error}),
- EmulatedOptions = ssl_socket:emulated_options(),
- {ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
+ EmulatedOptions = tls_socket:emulated_options(),
+ {ok, SocketValues} = tls_socket:getopts(Transport, Socket, EmulatedOptions),
ConnetionCb = connection_cb(SslOptions),
try handle_options(SslOptions ++ SocketValues, server) of
{ok, #config{transport_info = CbInfo, ssl = SslOpts, emulated = EmOpts}} ->
- ok = ssl_socket:setopts(Transport, Socket, ssl_socket:internal_inet_values()),
- {ok, Port} = ssl_socket:port(Transport, Socket),
+ ok = tls_socket:setopts(Transport, Socket, tls_socket:internal_inet_values()),
+ {ok, Port} = tls_socket:port(Transport, Socket),
ssl_connection:ssl_accept(ConnetionCb, Port, Socket,
- {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), undefined},
+ {SslOpts,
+ tls_socket:emulated_socket_options(EmOpts, #socket_options{}), undefined},
self(), CbInfo, Timeout)
catch
Error = {error, _Reason} -> Error
end.
-
%%--------------------------------------------------------------------
-spec close(#sslsocket{}) -> term().
%%
@@ -249,6 +230,8 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket),
%%--------------------------------------------------------------------
close(#sslsocket{pid = Pid}) when is_pid(Pid) ->
ssl_connection:close(Pid, {close, ?DEFAULT_TIMEOUT});
+close(#sslsocket{pid = {udp, #config{udp_handler = {Pid, _}}}}) ->
+ dtls_udp_listener:close(Pid);
close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _}}}}) ->
Transport:close(ListenSocket).
@@ -275,6 +258,10 @@ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _}
%%--------------------------------------------------------------------
send(#sslsocket{pid = Pid}, Data) when is_pid(Pid) ->
ssl_connection:send(Pid, Data);
+send(#sslsocket{pid = {_, #config{transport_info={gen_udp, _, _, _}}}}, _) ->
+ {error,enotconn}; %% Emulate connection behaviour
+send(#sslsocket{pid = {udp,_}}, _) ->
+ {error,enotconn};
send(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport, _, _, _}}}}, Data) ->
Transport:send(ListenSocket, Data). %% {error,enotconn}
@@ -289,6 +276,8 @@ recv(Socket, Length) ->
recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
ssl_connection:recv(Pid, Length, Timeout);
+recv(#sslsocket{pid = {udp,_}}, _, _) ->
+ {error,enotconn};
recv(#sslsocket{pid = {Listen,
#config{transport_info = {Transport, _, _, _}}}}, _,_) when is_port(Listen)->
Transport:recv(Listen, 0). %% {error,enotconn}
@@ -301,10 +290,14 @@ recv(#sslsocket{pid = {Listen,
%%--------------------------------------------------------------------
controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid), is_pid(NewOwner) ->
ssl_connection:new_user(Pid, NewOwner);
+controlling_process(#sslsocket{pid = {udp, _}},
+ NewOwner) when is_pid(NewOwner) ->
+ ok; %% Meaningless but let it be allowed to conform with TLS
controlling_process(#sslsocket{pid = {Listen,
#config{transport_info = {Transport, _, _, _}}}},
NewOwner) when is_port(Listen),
is_pid(NewOwner) ->
+ %% Meaningless but let it be allowed to conform with normal sockets
Transport:controlling_process(Listen, NewOwner).
@@ -321,7 +314,9 @@ connection_information(#sslsocket{pid = Pid}) when is_pid(Pid) ->
Error
end;
connection_information(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
- {error, enotconn}.
+ {error, enotconn};
+connection_information(#sslsocket{pid = {udp,_}}) ->
+ {error,enotconn}.
%%--------------------------------------------------------------------
-spec connection_information(#sslsocket{}, [atom()]) -> {ok, list()} | {error, reason()}.
@@ -357,10 +352,18 @@ connection_info(#sslsocket{} = SSLSocket) ->
%%
%% Description: same as inet:peername/1.
%%--------------------------------------------------------------------
+peername(#sslsocket{pid = Pid, fd = {Transport, Socket, _}}) when is_pid(Pid)->
+ dtls_socket:peername(Transport, Socket);
peername(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid)->
- ssl_socket:peername(Transport, Socket);
+ tls_socket:peername(Transport, Socket);
+peername(#sslsocket{pid = {udp = Transport, #config{udp_handler = {_Pid, _}}}}) ->
+ dtls_socket:peername(Transport, undefined);
+peername(#sslsocket{pid = Pid, fd = {gen_udp= Transport, Socket, _, _}}) when is_pid(Pid) ->
+ dtls_socket:peername(Transport, Socket);
peername(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}}}}) ->
- ssl_socket:peername(Transport, ListenSocket). %% Will return {error, enotconn}
+ tls_socket:peername(Transport, ListenSocket); %% Will return {error, enotconn}
+peername(#sslsocket{pid = {udp,_}}) ->
+ {error,enotconn}.
%%--------------------------------------------------------------------
-spec peercert(#sslsocket{}) ->{ok, DerCert::binary()} | {error, reason()}.
@@ -374,6 +377,8 @@ peercert(#sslsocket{pid = Pid}) when is_pid(Pid) ->
Result ->
Result
end;
+peercert(#sslsocket{pid = {udp, _}}) ->
+ {error, enotconn};
peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
{error, enotconn}.
@@ -420,6 +425,33 @@ cipher_suites(all) ->
[ssl_cipher:erl_suite_definition(Suite) || Suite <- available_suites(all)].
%%--------------------------------------------------------------------
+-spec eccs() -> tls_v1:curves().
+%% Description: returns all supported curves across all versions
+%%--------------------------------------------------------------------
+eccs() ->
+ Curves = tls_v1:ecc_curves(all), % only tls_v1 has named curves right now
+ eccs_filter_supported(Curves).
+
+%%--------------------------------------------------------------------
+-spec eccs(tls_record:tls_version() | tls_record:tls_atom_version()) ->
+ tls_v1:curves().
+%% Description: returns the curves supported for a given version of
+%% ssl/tls.
+%%--------------------------------------------------------------------
+eccs({3,0}) ->
+ [];
+eccs({3,_}) ->
+ Curves = tls_v1:ecc_curves(all),
+ eccs_filter_supported(Curves);
+eccs(AtomVersion) when is_atom(AtomVersion) ->
+ eccs(tls_record:protocol_version(AtomVersion)).
+
+eccs_filter_supported(Curves) ->
+ CryptoCurves = crypto:ec_curves(),
+ lists:filter(fun(Curve) -> proplists:get_bool(Curve, CryptoCurves) end,
+ Curves).
+
+%%--------------------------------------------------------------------
-spec getopts(#sslsocket{}, [gen_tcp:option_name()]) ->
{ok, [gen_tcp:option()]} | {error, reason()}.
%%
@@ -429,7 +461,7 @@ getopts(#sslsocket{pid = Pid}, OptionTags) when is_pid(Pid), is_list(OptionTags)
ssl_connection:get_opts(Pid, OptionTags);
getopts(#sslsocket{pid = {_, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket,
OptionTags) when is_list(OptionTags) ->
- try ssl_socket:getopts(Transport, ListenSocket, OptionTags) of
+ try tls_socket:getopts(Transport, ListenSocket, OptionTags) of
{ok, _} = Result ->
Result;
{error, InetError} ->
@@ -457,7 +489,7 @@ setopts(#sslsocket{pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) ->
end;
setopts(#sslsocket{pid = {_, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, Options) when is_list(Options) ->
- try ssl_socket:setopts(Transport, ListenSocket, Options) of
+ try tls_socket:setopts(Transport, ListenSocket, Options) of
ok ->
ok;
{error, InetError} ->
@@ -490,10 +522,10 @@ getstat(Socket) ->
%% Description: Get one or more statistic options for a socket.
%%--------------------------------------------------------------------
getstat(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}, Options) when is_port(Listen), is_list(Options) ->
- ssl_socket:getstat(Transport, Listen, Options);
+ tls_socket:getstat(Transport, Listen, Options);
getstat(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}, Options) when is_pid(Pid), is_list(Options) ->
- ssl_socket:getstat(Transport, Socket, Options).
+ tls_socket:getstat(Transport, Socket, Options).
%%---------------------------------------------------------------
-spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}.
@@ -503,6 +535,8 @@ getstat(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}, Options) when is_
shutdown(#sslsocket{pid = {Listen, #config{transport_info = {Transport,_, _, _}}}},
How) when is_port(Listen) ->
Transport:shutdown(Listen, How);
+shutdown(#sslsocket{pid = {udp,_}},_) ->
+ {error, enotconn};
shutdown(#sslsocket{pid = Pid}, How) ->
ssl_connection:shutdown(Pid, How).
@@ -512,10 +546,13 @@ shutdown(#sslsocket{pid = Pid}, How) ->
%% Description: Same as inet:sockname/1
%%--------------------------------------------------------------------
sockname(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}) when is_port(Listen) ->
- ssl_socket:sockname(Transport, Listen);
-
+ tls_socket:sockname(Transport, Listen);
+sockname(#sslsocket{pid = {udp, #config{udp_handler = {Pid, _}}}}) ->
+ dtls_udp_listener:sockname(Pid);
+sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _}}) when is_pid(Pid) ->
+ dtls_socket:sockname(Transport, Socket);
sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid) ->
- ssl_socket:sockname(Transport, Socket).
+ tls_socket:sockname(Transport, Socket).
%%---------------------------------------------------------------
-spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}.
@@ -525,6 +562,8 @@ sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid)
%%--------------------------------------------------------------------
session_info(#sslsocket{pid = Pid}) when is_pid(Pid) ->
ssl_connection:session_info(Pid);
+session_info(#sslsocket{pid = {udp,_}}) ->
+ {error, enotconn};
session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
{error, enotconn}.
@@ -549,6 +588,8 @@ versions() ->
%%--------------------------------------------------------------------
renegotiate(#sslsocket{pid = Pid}) when is_pid(Pid) ->
ssl_connection:renegotiation(Pid);
+renegotiate(#sslsocket{pid = {udp,_}}) ->
+ {error, enotconn};
renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
{error, enotconn}.
@@ -562,6 +603,8 @@ renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
prf(#sslsocket{pid = Pid},
Secret, Label, Seed, WantedLength) when is_pid(Pid) ->
ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength);
+prf(#sslsocket{pid = {udp,_}}, _,_,_,_) ->
+ {error, enotconn};
prf(#sslsocket{pid = {Listen,_}}, _,_,_,_) when is_port(Listen) ->
{error, enotconn}.
@@ -571,7 +614,7 @@ prf(#sslsocket{pid = {Listen,_}}, _,_,_,_) when is_port(Listen) ->
%% Description: Clear the PEM cache
%%--------------------------------------------------------------------
clear_pem_cache() ->
- ssl_manager:clear_pem_cache().
+ ssl_pem_cache:clear().
%%---------------------------------------------------------------
-spec format_error({error, term()}) -> list().
@@ -607,6 +650,11 @@ format_error(Error) ->
Other
end.
+tls_version({3, _} = Version) ->
+ Version;
+tls_version({254, _} = Version) ->
+ dtls_v1:corresponding_tls_version(Version).
+
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -620,28 +668,15 @@ available_suites(all) ->
Version = tls_record:highest_protocol_version([]),
ssl_cipher:filter_suites(ssl_cipher:all_suites(Version)).
-do_connect(Address, Port,
- #config{transport_info = CbInfo, inet_user = UserOpts, ssl = SslOpts,
- emulated = EmOpts, inet_ssl = SocketOpts, connection_cb = ConnetionCb},
- Timeout) ->
- {Transport, _, _, _} = CbInfo,
- try Transport:connect(Address, Port, SocketOpts, Timeout) of
- {ok, Socket} ->
- ssl_connection:connect(ConnetionCb, Address, Port, Socket,
- {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), undefined},
- self(), CbInfo, Timeout);
- {error, Reason} ->
- {error, Reason}
- catch
- exit:{function_clause, _} ->
- {error, {options, {cb_info, CbInfo}}};
- exit:badarg ->
- {error, {options, {socket_options, UserOpts}}};
- exit:{badarg, _} ->
- {error, {options, {socket_options, UserOpts}}}
- end.
+do_listen(Port, #config{transport_info = {Transport, _, _, _}} = Config, tls_connection) ->
+ tls_socket:listen(Transport, Port, Config);
+do_listen(Port, #config{transport_info = {Transport, _, _, _}} = Config, dtls_connection) ->
+ dtls_socket:listen(Transport, Port, Config).
+
%% Handle extra ssl options given to ssl_accept
+-spec handle_options([any()], #ssl_options{}) -> #ssl_options{}
+ ; ([any()], client | server) -> {ok, #config{}}.
handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
cacertfile = CaCertFile0} = InheritedSslOpts) ->
RecordCB = record_cb(Protocol),
@@ -698,6 +733,8 @@ handle_options(Opts0, Role) ->
[RecordCb:protocol_version(Vsn) || Vsn <- Vsns]
end,
+ Protocol = handle_option(protocol, Opts, tls),
+
SSLOptions = #ssl_options{
versions = Versions,
verify = validate_option(verify, Verify),
@@ -720,10 +757,12 @@ handle_options(Opts0, Role) ->
srp_identity = handle_option(srp_identity, Opts, undefined),
ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []),
RecordCb:highest_protocol_version(Versions)),
+ eccs = handle_eccs_option(proplists:get_value(eccs, Opts, eccs()),
+ RecordCb:highest_protocol_version(Versions)),
signature_algs = handle_hashsigns_option(proplists:get_value(signature_algs, Opts,
default_option_role(server,
tls_v1:default_signature_algs(Versions), Role)),
- RecordCb:highest_protocol_version(Versions)),
+ tls_version(RecordCb:highest_protocol_version(Versions))),
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
@@ -750,7 +789,10 @@ handle_options(Opts0, Role) ->
honor_cipher_order = handle_option(honor_cipher_order, Opts,
default_option_role(server, false, Role),
server, Role),
- protocol = proplists:get_value(protocol, Opts, tls),
+ honor_ecc_order = handle_option(honor_ecc_order, Opts,
+ default_option_role(server, false, Role),
+ server, Role),
+ protocol = Protocol,
padding_check = proplists:get_value(padding_check, Opts, true),
beast_mitigation = handle_option(beast_mitigation, Opts, one_n_minus_one),
fallback = handle_option(fallback, Opts,
@@ -760,10 +802,11 @@ handle_options(Opts0, Role) ->
client, Role),
crl_check = handle_option(crl_check, Opts, false),
crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}),
- v2_hello_compatible = handle_option(v2_hello_compatible, Opts, false)
+ v2_hello_compatible = handle_option(v2_hello_compatible, Opts, false),
+ max_handshake_size = handle_option(max_handshake_size, Opts, ?DEFAULT_MAX_HANDSHAKE_SIZE)
},
- CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
+ CbInfo = proplists:get_value(cb_info, Opts, default_cb_info(Protocol)),
SslOptions = [protocol, versions, verify, verify_fun, partial_chain,
fail_if_no_peer_cert, verify_client_once,
depth, cert, certfile, key, keyfile,
@@ -775,13 +818,14 @@ handle_options(Opts0, Role) ->
alpn_preferred_protocols, next_protocols_advertised,
client_preferred_next_protocols, log_alert,
server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
- fallback, signature_algs, beast_mitigation, v2_hello_compatible],
+ fallback, signature_algs, eccs, honor_ecc_order, beast_mitigation, v2_hello_compatible,
+ max_handshake_size],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
end, Opts, SslOptions),
- {Sock, Emulated} = emulated_options(SockOpts),
+ {Sock, Emulated} = emulated_options(Protocol, SockOpts),
ConnetionCb = connection_cb(Opts),
{ok, #config{ssl = SSLOptions, emulated = Emulated, inet_ssl = Sock,
@@ -1005,6 +1049,8 @@ validate_option(sni_fun, Fun) when is_function(Fun) ->
Fun;
validate_option(honor_cipher_order, Value) when is_boolean(Value) ->
Value;
+validate_option(honor_ecc_order, Value) when is_boolean(Value) ->
+ Value;
validate_option(padding_check, Value) when is_boolean(Value) ->
Value;
validate_option(fallback, Value) when is_boolean(Value) ->
@@ -1021,6 +1067,12 @@ validate_option(beast_mitigation, Value) when Value == one_n_minus_one orelse
Value;
validate_option(v2_hello_compatible, Value) when is_boolean(Value) ->
Value;
+validate_option(max_handshake_size, Value) when is_integer(Value) andalso Value =< ?MAX_UNIT24 ->
+ Value;
+validate_option(protocol, Value = tls) ->
+ Value;
+validate_option(protocol, Value = dtls) ->
+ Value;
validate_option(Opt, Value) ->
throw({error, {options, {Opt, Value}}}).
@@ -1058,17 +1110,37 @@ validate_binary_list(Opt, List) ->
(Bin) ->
throw({error, {options, {Opt, {invalid_protocol, Bin}}}})
end, List).
-
validate_versions([], Versions) ->
Versions;
validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2';
Version == 'tlsv1.1';
Version == tlsv1;
Version == sslv3 ->
- validate_versions(Rest, Versions);
+ tls_validate_versions(Rest, Versions);
+validate_versions([Version | Rest], Versions) when Version == 'dtlsv1';
+ Version == 'dtlsv2'->
+ dtls_validate_versions(Rest, Versions);
validate_versions([Ver| _], Versions) ->
throw({error, {options, {Ver, {versions, Versions}}}}).
+tls_validate_versions([], Versions) ->
+ Versions;
+tls_validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2';
+ Version == 'tlsv1.1';
+ Version == tlsv1;
+ Version == sslv3 ->
+ tls_validate_versions(Rest, Versions);
+tls_validate_versions([Ver| _], Versions) ->
+ throw({error, {options, {Ver, {versions, Versions}}}}).
+
+dtls_validate_versions([], Versions) ->
+ Versions;
+dtls_validate_versions([Version | Rest], Versions) when Version == 'dtlsv1';
+ Version == 'dtlsv2'->
+ dtls_validate_versions(Rest, Versions);
+dtls_validate_versions([Ver| _], Versions) ->
+ throw({error, {options, {Ver, {versions, Versions}}}}).
+
validate_inet_option(mode, Value)
when Value =/= list, Value =/= binary ->
throw({error, {options, {mode,Value}}});
@@ -1098,8 +1170,13 @@ ca_cert_default(verify_peer, {Fun,_}, _) when is_function(Fun) ->
%% some trusted certs.
ca_cert_default(verify_peer, undefined, _) ->
"".
-emulated_options(Opts) ->
- emulated_options(Opts, ssl_socket:internal_inet_values(), ssl_socket:default_inet_values()).
+emulated_options(Protocol, Opts) ->
+ case Protocol of
+ tls ->
+ emulated_options(Opts, tls_socket:internal_inet_values(), tls_socket:default_inet_values());
+ dtls ->
+ emulated_options(Opts, dtls_socket:internal_inet_values(), dtls_socket:default_inet_values())
+ end.
emulated_options([{mode, Value} = Opt |Opts], Inet, Emulated) ->
validate_inet_option(mode, Value),
@@ -1135,18 +1212,18 @@ handle_cipher_option(Value, Version) when is_list(Value) ->
binary_cipher_suites(Version, []) ->
%% Defaults to all supported suites that does
%% not require explicit configuration
- ssl_cipher:filter_suites(ssl_cipher:suites(Version));
+ ssl_cipher:filter_suites(ssl_cipher:suites(tls_version(Version)));
binary_cipher_suites(Version, [Tuple|_] = Ciphers0) when is_tuple(Tuple) ->
Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0],
binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
- All = ssl_cipher:all_suites(Version),
+ All = ssl_cipher:all_suites(tls_version(Version)),
case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, All)] of
[] ->
%% Defaults to all supported suites that does
%% not require explicit configuration
- ssl_cipher:filter_suites(ssl_cipher:suites(Version));
+ ssl_cipher:filter_suites(ssl_cipher:suites(tls_version(Version)));
Ciphers ->
Ciphers
end;
@@ -1159,6 +1236,15 @@ binary_cipher_suites(Version, Ciphers0) ->
Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")],
binary_cipher_suites(Version, Ciphers).
+handle_eccs_option(Value, Version) when is_list(Value) ->
+ {_Major, Minor} = tls_version(Version),
+ try tls_v1:ecc_curves(Minor, Value) of
+ Curves -> #elliptic_curves{elliptic_curve_list = Curves}
+ catch
+ exit:_ -> throw({error, {options, {eccs, Value}}});
+ error:_ -> throw({error, {options, {eccs, Value}}})
+ end.
+
unexpected_format(Error) ->
lists:flatten(io_lib:format("Unexpected error: ~p", [Error])).
@@ -1232,11 +1318,6 @@ record_cb(dtls) ->
record_cb(Opts) ->
record_cb(proplists:get_value(protocol, Opts, tls)).
-connection_sup(tls_connection) ->
- tls_connection_sup;
-connection_sup(dtls_connection) ->
- dtls_connection_sup.
-
binary_filename(FileName) ->
Enc = file:native_name_encoding(),
unicode:characters_to_binary(FileName, unicode, Enc).
@@ -1255,20 +1336,6 @@ assert_proplist([inet6 | Rest]) ->
assert_proplist([Value | _]) ->
throw({option_not_a_key_value_tuple, Value}).
-emulated_socket_options(InetValues, #socket_options{
- mode = Mode,
- header = Header,
- active = Active,
- packet = Packet,
- packet_size = Size}) ->
- #socket_options{
- mode = proplists:get_value(mode, InetValues, Mode),
- header = proplists:get_value(header, InetValues, Header),
- active = proplists:get_value(active, InetValues, Active),
- packet = proplists:get_value(packet, InetValues, Packet),
- packet_size = proplists:get_value(packet_size, InetValues, Size)
- }.
-
new_ssl_options([], #ssl_options{} = Opts, _) ->
Opts;
new_ssl_options([{verify_client_once, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
@@ -1329,13 +1396,24 @@ new_ssl_options([{server_name_indication, Value} | Rest], #ssl_options{} = Opts,
new_ssl_options(Rest, Opts#ssl_options{server_name_indication = validate_option(server_name_indication, Value)}, RecordCB);
new_ssl_options([{honor_cipher_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
new_ssl_options(Rest, Opts#ssl_options{honor_cipher_order = validate_option(honor_cipher_order, Value)}, RecordCB);
+new_ssl_options([{honor_ecc_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{honor_ecc_order = validate_option(honor_ecc_order, Value)}, RecordCB);
+new_ssl_options([{eccs, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest,
+ Opts#ssl_options{eccs =
+ handle_eccs_option(Value, RecordCB:highest_protocol_version())
+ },
+ RecordCB);
new_ssl_options([{signature_algs, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
new_ssl_options(Rest,
Opts#ssl_options{signature_algs =
handle_hashsigns_option(Value,
- RecordCB:highest_protocol_version())},
+ tls_version(RecordCB:highest_protocol_version()))},
RecordCB);
-
+new_ssl_options([{protocol, dtls = Value} | Rest], #ssl_options{} = Opts, dtls_record = RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{protocol = Value}, RecordCB);
+new_ssl_options([{protocol, tls = Value} | Rest], #ssl_options{} = Opts, tls_record = RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{protocol = Value}, RecordCB);
new_ssl_options([{Key, Value} | _Rest], #ssl_options{}, _) ->
throw({error, {options, {Key, Value}}}).
@@ -1397,3 +1475,8 @@ default_option_role(Role, Value, Role) ->
Value;
default_option_role(_,_,_) ->
undefined.
+
+default_cb_info(tls) ->
+ {gen_tcp, tcp, tcp_closed, tcp_error};
+default_cb_info(dtls) ->
+ {gen_udp, udp, udp_closed, udp_error}.
diff --git a/lib/ssl/src/ssl_admin_sup.erl b/lib/ssl/src/ssl_admin_sup.erl
new file mode 100644
index 0000000000..9c96435753
--- /dev/null
+++ b/lib/ssl/src/ssl_admin_sup.erl
@@ -0,0 +1,95 @@
+%%
+%% %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(ssl_admin_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0, manager_opts/0]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+
+-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+
+init([]) ->
+ PEMCache = pem_cache_child_spec(),
+ SessionCertManager = session_and_cert_manager_child_spec(),
+ {ok, {{rest_for_one, 10, 3600}, [PEMCache, SessionCertManager]}}.
+
+manager_opts() ->
+ CbOpts = case application:get_env(ssl, session_cb) of
+ {ok, Cb} when is_atom(Cb) ->
+ InitArgs = session_cb_init_args(),
+ [{session_cb, Cb}, {session_cb_init_args, InitArgs}];
+ _ ->
+ []
+ end,
+ case application:get_env(ssl, session_lifetime) of
+ {ok, Time} when is_integer(Time) ->
+ [{session_lifetime, Time}| CbOpts];
+ _ ->
+ CbOpts
+ end.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+pem_cache_child_spec() ->
+ Name = ssl_pem_cache,
+ StartFunc = {ssl_pem_cache, start_link, [[]]},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [ssl_pem_cache],
+ Type = worker,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+session_and_cert_manager_child_spec() ->
+ Opts = manager_opts(),
+ Name = ssl_manager,
+ StartFunc = {ssl_manager, start_link, [Opts]},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [ssl_manager],
+ Type = worker,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+session_cb_init_args() ->
+ case application:get_env(ssl, session_cb_init_args) of
+ {ok, Args} when is_list(Args) ->
+ Args;
+ _ ->
+ []
+ end.
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index db71b16d80..696a55e4b9 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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,22 +32,13 @@
-include("ssl_record.hrl").
-include("ssl_internal.hrl").
--export([encode/3, decode/1, alert_txt/1, reason_code/2]).
+-export([decode/1, alert_txt/1, reason_code/2]).
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
--spec encode(#alert{}, ssl_record:ssl_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
-%%
-%% Description: Encodes an alert
-%%--------------------------------------------------------------------
-encode(#alert{} = Alert, Version, ConnectionStates) ->
- ssl_record:encode_alert_record(Alert, Version, ConnectionStates).
-
-%%--------------------------------------------------------------------
-spec decode(binary()) -> [#alert{}] | #alert{}.
%%
%% Description: Decode alert(s), will return a singel own alert if peer
diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl
index 38facb964f..f3743ba0f0 100644
--- a/lib/ssl/src/ssl_alert.hrl
+++ b/lib/ssl/src/ssl_alert.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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.
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 3ec3f50e05..2046ec75b3 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016 All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017 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.
@@ -64,7 +64,7 @@ trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) -
{ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self),
{self, IssuerId};
false ->
- other_issuer(OtpCert, BinCert, CertDbHandle)
+ other_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef)
end,
case SignedAndIssuerID of
@@ -125,21 +125,21 @@ file_to_crls(File, DbHandle) ->
%% Description: Validates ssl/tls specific extensions
%%--------------------------------------------------------------------
validate(_,{extension, #'Extension'{extnID = ?'id-ce-extKeyUsage',
- extnValue = KeyUse}}, {Role, _,_, _, _}) ->
+ extnValue = KeyUse}}, UserState = {Role, _,_, _, _}) ->
case is_valid_extkey_usage(KeyUse, Role) of
true ->
- {valid, Role};
+ {valid, UserState};
false ->
{fail, {bad_cert, invalid_ext_key_usage}}
end;
-validate(_, {extension, _}, Role) ->
- {unknown, Role};
+validate(_, {extension, _}, UserState) ->
+ {unknown, UserState};
validate(_, {bad_cert, _} = Reason, _) ->
{fail, Reason};
-validate(_, valid, Role) ->
- {valid, Role};
-validate(_, valid_peer, Role) ->
- {valid, Role}.
+validate(_, valid, UserState) ->
+ {valid, UserState};
+validate(_, valid_peer, UserState) ->
+ {valid, UserState}.
%%--------------------------------------------------------------------
-spec is_valid_key_usage(list(), term()) -> boolean().
@@ -200,7 +200,7 @@ certificate_chain(OtpCert, BinCert, CertDbHandle, CertsDbRef, Chain) ->
{_, true = SelfSigned} ->
certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned);
{{error, issuer_not_found}, SelfSigned} ->
- case find_issuer(OtpCert, BinCert, CertDbHandle) of
+ case find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) of
{ok, {SerialNr, Issuer}} ->
certificate_chain(CertDbHandle, CertsDbRef, Chain,
SerialNr, Issuer, SelfSigned);
@@ -232,7 +232,7 @@ certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned
{ok, undefined, lists:reverse(Chain)}
end.
-find_issuer(OtpCert, BinCert, CertDbHandle) ->
+find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) ->
IsIssuerFun =
fun({_Key, {_Der, #'OTPCertificate'{} = ErlCertCandidate}}, Acc) ->
case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of
@@ -250,12 +250,24 @@ find_issuer(OtpCert, BinCert, CertDbHandle) ->
Acc
end,
- try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of
- issuer_not_found ->
- {error, issuer_not_found}
- catch
- {ok, _IssuerId} = Return ->
- Return
+ if is_reference(CertsDbRef) -> % actual DB exists
+ try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of
+ issuer_not_found ->
+ {error, issuer_not_found}
+ catch
+ {ok, _IssuerId} = Return ->
+ Return
+ end;
+ is_tuple(CertsDbRef), element(1,CertsDbRef) =:= extracted -> % cache bypass byproduct
+ {extracted, CertsData} = CertsDbRef,
+ DB = [Entry || {decoded, Entry} <- CertsData],
+ try lists:foldl(IsIssuerFun, issuer_not_found, DB) of
+ issuer_not_found ->
+ {error, issuer_not_found}
+ catch
+ {ok, _IssuerId} = Return ->
+ Return
+ end
end.
is_valid_extkey_usage(KeyUse, client) ->
@@ -281,12 +293,12 @@ public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorith
subjectPublicKey = Key}) ->
{Key, Params}.
-other_issuer(OtpCert, BinCert, CertDbHandle) ->
+other_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) ->
case public_key:pkix_issuer_id(OtpCert, other) of
{ok, IssuerId} ->
{other, IssuerId};
{error, issuer_not_found} ->
- case find_issuer(OtpCert, BinCert, CertDbHandle) of
+ case find_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) of
{ok, IssuerId} ->
{other, IssuerId};
Other ->
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index e935c033c7..32f05628bb 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,7 +1,7 @@
%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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.
@@ -40,15 +40,16 @@
ec_keyed_suites/0, anonymous_suites/1, psk_suites/1, srp_suites/0,
rc4_suites/1, des_suites/1, openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1,
- random_bytes/1]).
+ random_bytes/1, calc_aad/3, calc_mac_hash/4,
+ is_stream_ciphersuite/1]).
-export_type([cipher_suite/0,
erl_cipher_suite/0, openssl_cipher_suite/0,
hash/0, key_algo/0, sign_algo/0]).
--type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
+-type cipher() :: null |rc4_128 | des_cbc | '3des_ede_cbc'
| aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305.
--type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512.
+-type hash() :: null | md5 | sha | sha224 | sha256 | sha384 | sha512.
-type sign_algo() :: rsa | dsa | ecdsa.
-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss |
psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
@@ -310,16 +311,21 @@ aead_decipher(Type, #cipher_state{key = Key, iv = IV} = CipherState,
%%--------------------------------------------------------------------
suites({3, 0}) ->
ssl_v3:suites();
-suites({3, N}) ->
- tls_v1:suites(N).
+suites({3, Minor}) ->
+ tls_v1:suites(Minor);
+suites({_, Minor}) ->
+ dtls_v1:suites(Minor).
-all_suites(Version) ->
+all_suites({3, _} = Version) ->
suites(Version)
++ anonymous_suites(Version)
++ psk_suites(Version)
++ srp_suites()
++ rc4_suites(Version)
- ++ des_suites(Version).
+ ++ des_suites(Version);
+all_suites(Version) ->
+ dtls_v1:all_suites(Version).
+
%%--------------------------------------------------------------------
-spec anonymous_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
%%
@@ -333,21 +339,27 @@ anonymous_suites({3, N}) ->
anonymous_suites(N)
when N >= 3 ->
[?TLS_DH_anon_WITH_AES_128_GCM_SHA256,
- ?TLS_DH_anon_WITH_AES_256_GCM_SHA384
- ] ++ anonymous_suites(0);
-
-anonymous_suites(_) ->
- [?TLS_DH_anon_WITH_RC4_128_MD5,
- ?TLS_DH_anon_WITH_DES_CBC_SHA,
- ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
- ?TLS_DH_anon_WITH_AES_128_CBC_SHA,
- ?TLS_DH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_DH_anon_WITH_AES_256_GCM_SHA384,
?TLS_DH_anon_WITH_AES_128_CBC_SHA256,
?TLS_DH_anon_WITH_AES_256_CBC_SHA256,
- ?TLS_ECDH_anon_WITH_RC4_128_SHA,
- ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
?TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
- ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA].
+ ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_RC4_128_MD5];
+
+anonymous_suites(2) ->
+ [?TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
+ ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
+ ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_DES_CBC_SHA,
+ ?TLS_DH_anon_WITH_RC4_128_MD5];
+
+anonymous_suites(N) when N == 0;
+ N == 1 ->
+ [?TLS_DH_anon_WITH_RC4_128_MD5,
+ ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_DES_CBC_SHA
+ ].
%%--------------------------------------------------------------------
-spec psk_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
@@ -1441,25 +1453,60 @@ filter_suites(Suites) ->
is_acceptable_prf(Prf, Hashs)
end, Suites).
-is_acceptable_keyexchange(KeyExchange, Algos)
- when KeyExchange == ecdh_ecdsa;
- KeyExchange == ecdhe_ecdsa;
- KeyExchange == ecdh_rsa;
- KeyExchange == ecdhe_rsa;
- KeyExchange == ecdh_anon ->
+is_acceptable_keyexchange(KeyExchange, _Algos) when KeyExchange == psk;
+ KeyExchange == null ->
+ true;
+is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == dh_anon;
+ KeyExchange == dhe_psk ->
+ proplists:get_bool(dh, Algos);
+is_acceptable_keyexchange(dhe_dss, Algos) ->
+ proplists:get_bool(dh, Algos) andalso
+ proplists:get_bool(dss, Algos);
+is_acceptable_keyexchange(dhe_rsa, Algos) ->
+ proplists:get_bool(dh, Algos) andalso
+ proplists:get_bool(rsa, Algos);
+is_acceptable_keyexchange(ecdh_anon, Algos) ->
proplists:get_bool(ecdh, Algos);
-is_acceptable_keyexchange(_, _) ->
- true.
-
+is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == ecdh_ecdsa;
+ KeyExchange == ecdhe_ecdsa ->
+ proplists:get_bool(ecdh, Algos) andalso
+ proplists:get_bool(ecdsa, Algos);
+is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == ecdh_rsa;
+ KeyExchange == ecdhe_rsa ->
+ proplists:get_bool(ecdh, Algos) andalso
+ proplists:get_bool(rsa, Algos);
+is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == rsa;
+ KeyExchange == rsa_psk ->
+ proplists:get_bool(rsa, Algos);
+is_acceptable_keyexchange(srp_anon, Algos) ->
+ proplists:get_bool(srp, Algos);
+is_acceptable_keyexchange(srp_dss, Algos) ->
+ proplists:get_bool(srp, Algos) andalso
+ proplists:get_bool(dss, Algos);
+is_acceptable_keyexchange(srp_rsa, Algos) ->
+ proplists:get_bool(srp, Algos) andalso
+ proplists:get_bool(rsa, Algos);
+is_acceptable_keyexchange(_KeyExchange, _Algos) ->
+ false.
+
+is_acceptable_cipher(null, _Algos) ->
+ true;
+is_acceptable_cipher(rc4_128, Algos) ->
+ proplists:get_bool(rc4, Algos);
+is_acceptable_cipher(des_cbc, Algos) ->
+ proplists:get_bool(des_cbc, Algos);
+is_acceptable_cipher('3des_ede_cbc', Algos) ->
+ proplists:get_bool(des3_cbc, Algos);
+is_acceptable_cipher(aes_128_cbc, Algos) ->
+ proplists:get_bool(aes_cbc128, Algos);
+is_acceptable_cipher(aes_256_cbc, Algos) ->
+ proplists:get_bool(aes_cbc256, Algos);
is_acceptable_cipher(Cipher, Algos)
when Cipher == aes_128_gcm;
Cipher == aes_256_gcm ->
proplists:get_bool(aes_gcm, Algos);
-is_acceptable_cipher(Cipher, Algos)
- when Cipher == chacha20_poly1305 ->
- proplists:get_bool(Cipher, Algos);
-is_acceptable_cipher(_, _) ->
- true.
+is_acceptable_cipher(Cipher, Algos) ->
+ proplists:get_bool(Cipher, Algos).
is_acceptable_hash(null, _Algos) ->
true;
@@ -1484,9 +1531,36 @@ is_fallback(CipherSuites)->
random_bytes(N) ->
crypto:strong_rand_bytes(N).
+calc_aad(Type, {MajVer, MinVer},
+ #{sequence_number := SeqNo}) ->
+ <<SeqNo:64/integer, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
+
+calc_mac_hash(Type, Version,
+ PlainFragment, #{sequence_number := SeqNo,
+ mac_secret := MacSecret,
+ security_parameters:=
+ SecPars}) ->
+ Length = erlang:iolist_size(PlainFragment),
+ mac_hash(Version, SecPars#security_parameters.mac_algorithm,
+ MacSecret, SeqNo, Type,
+ Length, PlainFragment).
+
+is_stream_ciphersuite({_, rc4_128, _, _}) ->
+ true;
+is_stream_ciphersuite(_) ->
+ false.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
+ _Length, _Fragment) ->
+ <<>>;
+mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
+ ssl_v3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment);
+mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
+ when N =:= 1; N =:= 2; N =:= 3 ->
+ tls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
+ Length, Fragment).
bulk_cipher_algorithm(null) ->
?NULL;
diff --git a/lib/ssl/src/ssl_config.erl b/lib/ssl/src/ssl_config.erl
index 0652d029c3..e4611995ec 100644
--- a/lib/ssl/src/ssl_config.erl
+++ b/lib/ssl/src/ssl_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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,18 +32,20 @@ init(SslOpts, Role) ->
init_manager_name(SslOpts#ssl_options.erl_dist),
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CRLDbHandle, OwnCert}
+ {ok, #{pem_cache := PemCache} = Config}
= init_certificates(SslOpts, Role),
PrivateKey =
- init_private_key(PemCacheHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
+ init_private_key(PemCache, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
SslOpts#ssl_options.password, Role),
- DHParams = init_diffie_hellman(PemCacheHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role),
- {ok, CertDbRef, CertDbHandle, FileRefHandle, CacheHandle, CRLDbHandle, OwnCert, PrivateKey, DHParams}.
+ DHParams = init_diffie_hellman(PemCache, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role),
+ {ok, Config#{private_key => PrivateKey, dh_params => DHParams}}.
init_manager_name(false) ->
- put(ssl_manager, ssl_manager:manager_name(normal));
+ put(ssl_manager, ssl_manager:name(normal)),
+ put(ssl_pem_cache, ssl_pem_cache:name(normal));
init_manager_name(true) ->
- put(ssl_manager, ssl_manager:manager_name(dist)).
+ put(ssl_manager, ssl_manager:name(dist)),
+ put(ssl_pem_cache, ssl_pem_cache:name(dist)).
init_certificates(#ssl_options{cacerts = CaCerts,
cacertfile = CACertFile,
@@ -51,7 +53,7 @@ init_certificates(#ssl_options{cacerts = CaCerts,
cert = Cert,
crl_cache = CRLCache
}, Role) ->
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CRLDbInfo} =
+ {ok, Config} =
try
Certs = case CaCerts of
undefined ->
@@ -59,41 +61,37 @@ init_certificates(#ssl_options{cacerts = CaCerts,
_ ->
{der, CaCerts}
end,
- {ok, _, _, _, _, _, _} = ssl_manager:connection_init(Certs, Role, CRLCache)
+ {ok,_} = ssl_manager:connection_init(Certs, Role, CRLCache)
catch
_:Reason ->
file_error(CACertFile, {cacertfile, Reason})
end,
- init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle,
- CacheHandle, CRLDbInfo, CertFile, Role).
+ init_certificates(Cert, Config, CertFile, Role).
-init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle,
- CRLDbInfo, <<>>, _) ->
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CRLDbInfo, undefined};
+init_certificates(undefined, Config, <<>>, _) ->
+ {ok, Config#{own_certificate => undefined}};
-init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle,
- CacheHandle, CRLDbInfo, CertFile, client) ->
+init_certificates(undefined, #{pem_cache := PemCache} = Config, CertFile, client) ->
try
%% Ignoring potential proxy-certificates see:
%% http://dev.globus.org/wiki/Security/ProxyFileFormat
- [OwnCert|_] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CRLDbInfo, OwnCert}
+ [OwnCert|_] = ssl_certificate:file_to_certificats(CertFile, PemCache),
+ {ok, Config#{own_certificate => OwnCert}}
catch _Error:_Reason ->
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CRLDbInfo, undefined}
- end;
+ {ok, Config#{own_certificate => undefined}}
+ end;
-init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle,
- PemCacheHandle, CacheRef, CRLDbInfo, CertFile, server) ->
+init_certificates(undefined, #{pem_cache := PemCache} = Config, CertFile, server) ->
try
- [OwnCert|_] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CRLDbInfo, OwnCert}
+ [OwnCert|_] = ssl_certificate:file_to_certificats(CertFile, PemCache),
+ {ok, Config#{own_certificate => OwnCert}}
catch
_:Reason ->
file_error(CertFile, {certfile, Reason})
end;
-init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CRLDbInfo, _, _) ->
- {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CRLDbInfo, Cert}.
-
+init_certificates(Cert, Config, _, _) ->
+ {ok, Config#{own_certificate => Cert}}.
+
init_private_key(_, undefined, <<>>, _Password, _Client) ->
undefined;
init_private_key(DbHandle, undefined, KeyFile, Password, _) ->
@@ -135,6 +133,8 @@ file_error(File, Throw) ->
case Throw of
{Opt,{badmatch, {error, {badmatch, Error}}}} ->
throw({options, {Opt, binary_to_list(File), Error}});
+ {Opt, {badmatch, Error}} ->
+ throw({options, {Opt, binary_to_list(File), Error}});
_ ->
throw(Throw)
end.
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 53282998d0..2db9172d50 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. 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.
@@ -44,7 +44,7 @@
-export([send/2, recv/3, close/2, shutdown/2,
new_user/2, get_opts/2, set_opts/2, session_info/1,
peer_certificate/1, renegotiation/1, negotiated_protocol/1, prf/5,
- connection_information/1
+ connection_information/1, handle_common_event/5
]).
%% General gen_statem state functions with extra callback argument
@@ -58,13 +58,21 @@
-export([handle_info/3, handle_call/5, handle_session/7, ssl_config/3,
prepare_connection/2, hibernate_after/3]).
+%% Alert and close handling
+-export([handle_own_alert/4,handle_alert/3,
+ handle_normal_shutdown/3
+ ]).
+
+%% Data handling
+-export([write_application_data/3, read_application_data/2]).
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
-spec connect(tls_connection | dtls_connection,
- host(), inet:port_number(), port(),
+ host(), inet:port_number(),
+ port() | {tuple(), port()}, %% TLS | DTLS
{#ssl_options{}, #socket_options{},
%% Tracker only needed on server side
undefined},
@@ -138,14 +146,24 @@ socket_control(Connection, Socket, Pid, Transport) ->
-spec socket_control(tls_connection | dtls_connection, port(), pid(), atom(), pid()| undefined) ->
{ok, #sslsocket{}} | {error, reason()}.
%%--------------------------------------------------------------------
-socket_control(Connection, Socket, Pid, Transport, ListenTracker) ->
+socket_control(Connection, Socket, Pid, Transport, udp_listner) ->
+ %% dtls listner process must have the socket control
+ {ok, Connection:socket(Pid, Transport, Socket, Connection, undefined)};
+
+socket_control(tls_connection = Connection, Socket, Pid, Transport, ListenTracker) ->
+ case Transport:controlling_process(Socket, Pid) of
+ ok ->
+ {ok, Connection:socket(Pid, Transport, Socket, Connection, ListenTracker)};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+socket_control(dtls_connection = Connection, {_, Socket}, Pid, Transport, ListenTracker) ->
case Transport:controlling_process(Socket, Pid) of
ok ->
- {ok, ssl_socket:socket(Pid, Transport, Socket, Connection, ListenTracker)};
+ {ok, Connection:socket(Pid, Transport, Socket, Connection, ListenTracker)};
{error, Reason} ->
{error, Reason}
end.
-
%%--------------------------------------------------------------------
-spec send(pid(), iodata()) -> ok | {error, reason()}.
%%
@@ -264,7 +282,7 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) ->
%%--------------------------------------------------------------------
-spec handle_session(#server_hello{}, ssl_record:ssl_version(),
- binary(), #connection_states{}, _,_, #state{}) ->
+ binary(), ssl_record:connection_states(), _,_, #state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
handle_session(#server_hello{cipher_suite = CipherSuite,
@@ -272,19 +290,21 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
Version, NewId, ConnectionStates, ProtoExt, Protocol0,
#state{session = #session{session_id = OldId},
negotiated_version = ReqVersion,
- negotiated_protocol = CurrentProtocol} = State0) ->
+ negotiated_protocol = CurrentProtocol} = State0) ->
{KeyAlgorithm, _, _, _} =
ssl_cipher:suite_definition(CipherSuite),
PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
- {ExpectNPN, Protocol} = case Protocol0 of
- undefined -> {false, CurrentProtocol};
- _ -> {ProtoExt =:= npn, Protocol0}
- end,
+ {ExpectNPN, Protocol} = case Protocol0 of
+ undefined ->
+ {false, CurrentProtocol};
+ _ ->
+ {ProtoExt =:= npn, Protocol0}
+ end,
State = State0#state{key_algorithm = KeyAlgorithm,
- negotiated_version = Version,
+ negotiated_version = Version,
connection_states = ConnectionStates,
premaster_secret = PremasterSecret,
expecting_next_protocol_negotiation = ExpectNPN,
@@ -303,8 +323,14 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
-spec ssl_config(#ssl_options{}, client | server, #state{}) -> #state{}.
%%--------------------------------------------------------------------
ssl_config(Opts, Role, State) ->
- {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo,
- OwnCert, Key, DHParams} =
+ {ok, #{cert_db_ref := Ref,
+ cert_db_handle := CertDbHandle,
+ fileref_db_handle := FileRefHandle,
+ session_cache := CacheHandle,
+ crl_db_info := CRLDbHandle,
+ private_key := Key,
+ dh_params := DHParams,
+ own_certificate := OwnCert}} =
ssl_config:init(Opts, Role),
Handshake = ssl_handshake:init_handshake_history(),
TimeStamp = erlang:monotonic_time(),
@@ -315,7 +341,7 @@ ssl_config(Opts, Role, State) ->
file_ref_db = FileRefHandle,
cert_db_ref = Ref,
cert_db = CertDbHandle,
- crl_db = CRLDbInfo,
+ crl_db = CRLDbHandle,
session_cache = CacheHandle,
private_key = Key,
diffie_hellman_params = DHParams,
@@ -337,11 +363,13 @@ init({call, From}, {start, Timeout}, State0, Connection) ->
timer = Timer}),
Connection:next_event(hello, Record, State);
init({call, From}, {start, {Opts, EmOpts}, Timeout},
- #state{role = Role} = State0, Connection) ->
+ #state{role = Role, ssl_options = OrigSSLOptions,
+ socket_options = SockOpts} = State0, Connection) ->
try
- State = ssl_config(Opts, Role, State0),
+ SslOpts = ssl:handle_options(Opts, OrigSSLOptions),
+ State = ssl_config(SslOpts, Role, State0),
init({call, From}, {start, Timeout},
- State#state{ssl_options = Opts, socket_options = EmOpts}, Connection)
+ State#state{ssl_options = SslOpts, socket_options = new_emulated(EmOpts, SockOpts)}, Connection)
catch throw:Error ->
{stop_and_reply, normal, {reply, From, {error, Error}}}
end;
@@ -382,7 +410,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
session = #session{master_secret = MasterSecret},
connection_states = ConnectionStates0} =
State0, Connection) ->
- case ssl_handshake:verify_connection(Version, Finished, client,
+ case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, client,
get_current_prf(ConnectionStates0, write),
MasterSecret, Handshake) of
verified ->
@@ -392,7 +420,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
expecting_finished = false}, Connection),
Connection:next_event(connection, Record, State);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, abbreviated, State0)
+ handle_own_alert(Alert, Version, abbreviated, State0)
end;
abbreviated(internal, #finished{verify_data = Data} = Finished,
@@ -400,19 +428,19 @@ abbreviated(internal, #finished{verify_data = Data} = Finished,
session = #session{master_secret = MasterSecret},
negotiated_version = Version,
connection_states = ConnectionStates0} = State0, Connection) ->
- case ssl_handshake:verify_connection(Version, Finished, server,
+ case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, server,
get_pending_prf(ConnectionStates0, write),
MasterSecret, Handshake0) of
verified ->
ConnectionStates1 =
ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0),
- State1 =
+ {State1, Actions} =
finalize_handshake(State0#state{connection_states = ConnectionStates1},
abbreviated, Connection),
{Record, State} = prepare_connection(State1#state{expecting_finished = false}, Connection),
- Connection:next_event(connection, Record, State);
+ Connection:next_event(connection, Record, State, Actions);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, abbreviated, State0)
+ handle_own_alert(Alert, Version, abbreviated, State0)
end;
%% only allowed to send next_protocol message after change cipher spec
@@ -452,9 +480,9 @@ certify(internal, #certificate{asn1_certificates = []},
#state{role = server, negotiated_version = Version,
ssl_options = #ssl_options{verify = verify_peer,
fail_if_no_peer_cert = true}} =
- State, Connection) ->
+ State, _) ->
Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE),
- Connection:handle_own_alert(Alert, Version, certify, State);
+ handle_own_alert(Alert, Version, certify, State);
certify(internal, #certificate{asn1_certificates = []},
#state{role = server,
@@ -469,9 +497,9 @@ certify(internal, #certificate{},
#state{role = server,
negotiated_version = Version,
ssl_options = #ssl_options{verify = verify_none}} =
- State, Connection) ->
+ State, _) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate),
- Connection:handle_own_alert(Alert, Version, certify, State);
+ handle_own_alert(Alert, Version, certify, State);
certify(internal, #certificate{} = Cert,
#state{negotiated_version = Version,
@@ -492,7 +520,7 @@ certify(internal, #certificate{} = Cert,
handle_peer_cert(Role, PeerCert, PublicKeyInfo,
State#state{client_certificate_requested = false}, Connection);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State)
+ handle_own_alert(Alert, Version, certify, State)
end;
certify(internal, #server_key_exchange{exchange_keys = Keys},
@@ -506,10 +534,10 @@ certify(internal, #server_key_exchange{exchange_keys = Keys},
Alg == psk; Alg == dhe_psk; Alg == rsa_psk;
Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon ->
- Params = ssl_handshake:decode_server_key(Keys, Alg, Version),
+ Params = ssl_handshake:decode_server_key(Keys, Alg, ssl:tls_version(Version)),
%% Use negotiated value if TLS-1.2 otherwhise return default
- HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, Version),
+ HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, ssl:tls_version(Version)),
case is_anonymous(Alg) of
true ->
@@ -517,26 +545,25 @@ certify(internal, #server_key_exchange{exchange_keys = Keys},
State#state{hashsign_algorithm = HashSign}, Connection);
false ->
case ssl_handshake:verify_server_key(Params, HashSign,
- ConnectionStates, Version, PubKeyInfo) of
+ ConnectionStates, ssl:tls_version(Version), PubKeyInfo) of
true ->
calculate_secret(Params#server_key_params.params,
State#state{hashsign_algorithm = HashSign},
Connection);
false ->
- Connection:handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR),
+ handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR),
Version, certify, State)
end
end;
-certify(internal, #certificate_request{hashsign_algorithms = HashSigns},
+certify(internal, #certificate_request{} = CertRequest,
#state{session = #session{own_certificate = Cert},
- key_algorithm = KeyExAlg,
+ role = client,
ssl_options = #ssl_options{signature_algs = SupportedHashSigns},
negotiated_version = Version} = State0, Connection) ->
-
- case ssl_handshake:select_hashsign(HashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of
+ case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, ssl:tls_version(Version)) of
#alert {} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0);
+ handle_own_alert(Alert, Version, certify, State0);
NegotiatedHashSign ->
{Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}),
Connection:next_event(certify, Record,
@@ -555,7 +582,7 @@ certify(internal, #server_hello_done{},
when Alg == psk ->
case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup) of
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0);
+ handle_own_alert(Alert, Version, certify, State0);
PremasterSecret ->
State = master_secret(PremasterSecret,
State0#state{premaster_secret = PremasterSecret}),
@@ -576,7 +603,7 @@ certify(internal, #server_hello_done{},
case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup,
RSAPremasterSecret) of
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0);
+ handle_own_alert(Alert, Version, certify, State0);
PremasterSecret ->
State = master_secret(PremasterSecret,
State0#state{premaster_secret = RSAPremasterSecret}),
@@ -590,13 +617,13 @@ certify(internal, #server_hello_done{},
negotiated_version = Version,
premaster_secret = undefined,
role = client} = State0, Connection) ->
- case ssl_handshake:master_secret(record_cb(Connection), Version, Session,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), Session,
ConnectionStates0, client) of
{MasterSecret, ConnectionStates} ->
State = State0#state{connection_states = ConnectionStates},
client_certify_and_key_exchange(State, Connection);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0)
+ handle_own_alert(Alert, Version, certify, State0)
end;
%% Master secret is calculated from premaster_secret
@@ -606,7 +633,7 @@ certify(internal, #server_hello_done{},
negotiated_version = Version,
premaster_secret = PremasterSecret,
role = client} = State0, Connection) ->
- case ssl_handshake:master_secret(record_cb(Connection), Version, PremasterSecret,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret,
ConnectionStates0, client) of
{MasterSecret, ConnectionStates} ->
Session = Session0#session{master_secret = MasterSecret},
@@ -614,7 +641,7 @@ certify(internal, #server_hello_done{},
session = Session},
client_certify_and_key_exchange(State, Connection);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0)
+ handle_own_alert(Alert, Version, certify, State0)
end;
certify(internal = Type, #client_key_exchange{} = Msg,
@@ -628,11 +655,11 @@ certify(internal = Type, #client_key_exchange{} = Msg,
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),
+ certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, ssl:tls_version(Version)),
State, Connection)
catch
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State)
+ handle_own_alert(Alert, Version, certify, State)
end;
certify(Type, Msg, State, Connection) ->
@@ -663,21 +690,21 @@ cipher(internal, #certificate_verify{signature = Signature,
%% Use negotiated value if TLS-1.2 otherwhise return default
HashSign = negotiated_hashsign(CertHashSign, KexAlg, PublicKeyInfo, Version),
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
- Version, HashSign, MasterSecret, Handshake) of
+ ssl:tls_version(Version), HashSign, MasterSecret, Handshake) of
valid ->
{Record, State} = Connection:next_record(State0),
Connection:next_event(cipher, Record,
State#state{cert_hashsign_algorithm = HashSign});
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, cipher, State0)
+ handle_own_alert(Alert, Version, cipher, State0)
end;
%% client must send a next protocol message if we are expecting it
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);
+ _Connection) ->
+ handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0);
cipher(internal, #finished{verify_data = Data} = Finished,
#state{negotiated_version = Version,
@@ -689,7 +716,7 @@ cipher(internal, #finished{verify_data = Data} = Finished,
= Session0,
connection_states = ConnectionStates0,
tls_handshake_history = Handshake0} = State, Connection) ->
- case ssl_handshake:verify_connection(Version, Finished,
+ case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished,
opposite_role(Role),
get_current_prf(ConnectionStates0, read),
MasterSecret, Handshake0) of
@@ -698,7 +725,7 @@ cipher(internal, #finished{verify_data = Data} = Finished,
cipher_role(Role, Data, Session,
State#state{expecting_finished = false}, Connection);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, cipher, State)
+ handle_own_alert(Alert, Version, cipher, State)
end;
%% only allowed to send next_protocol message after change cipher spec
@@ -731,7 +758,7 @@ connection({call, From}, {application_data, Data},
%% parallize send and receive decoding and not block the receiver
%% if sending is overloading the socket.
try
- Connection:write_application_data(Data, From, State)
+ write_application_data(Data, From, State)
catch throw:Error ->
hibernate_after(connection, State, [{reply, From, Error}])
end;
@@ -780,7 +807,7 @@ connection(Type, Msg, State, Connection) ->
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}]),
+ tls_socket:setopts(Transport, Socket, [{active, false}, {packet, 0}, {mode, binary}]),
Transport:controlling_process(Socket, Pid),
gen_statem:reply(From, {ok, Socket}),
{stop, normal, State};
@@ -802,35 +829,37 @@ handle_common_event(internal, {handshake, {#hello_request{}, _}}, StateName, #st
when StateName =/= connection ->
{keep_state_and_data};
handle_common_event(internal, {handshake, {Handshake, Raw}}, StateName,
- #state{tls_handshake_history = Hs0} = State0, Connection) ->
+ #state{tls_handshake_history = Hs0,
+ ssl_options = #ssl_options{v2_hello_compatible = V2HComp}} = State0,
+ Connection) ->
+
+ PossibleSNI = Connection:select_sni_extension(Handshake),
%% This function handles client SNI hello extension when Handshake is
%% a client_hello, which needs to be determined by the connection callback.
%% In other cases this is a noop
- State = Connection:handle_sni_extension(Handshake, State0),
- HsHist = ssl_handshake:update_handshake_history(Hs0, Raw),
+ State = handle_sni_extension(PossibleSNI, State0),
+ HsHist = ssl_handshake:update_handshake_history(Hs0, iolist_to_binary(Raw), V2HComp),
{next_state, StateName, State#state{tls_handshake_history = HsHist},
[{next_event, internal, Handshake}]};
-handle_common_event(internal, {tls_record, TLSRecord}, StateName, State, Connection) ->
- Connection:handle_common_event(internal, TLSRecord, StateName, State);
+handle_common_event(internal, {protocol_record, TLSorDTLSRecord}, StateName, State, Connection) ->
+ Connection:handle_common_event(internal, TLSorDTLSRecord, StateName, 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
+ case read_application_data(Data, State0) of
{stop, Reason, State} ->
{stop, Reason, State};
{Record, State} ->
Connection:next_event(StateName, Record, State)
end;
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,
+ #state{negotiated_version = Version} = State, _) ->
+ 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).
+ _) ->
+ Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, Msg}),
+ handle_own_alert(Alert, Version, StateName, State).
handle_call({application_data, _Data}, _, _, _, _) ->
%% In renegotiation priorities handshake, send data when handshake is finished
@@ -843,24 +872,24 @@ handle_call({close, {Pid, Timeout}}, From, StateName, State0, Connection) when i
%% 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}]};
+ {next_state, downgrade, State#state{terminated = true}, [{timeout, Timeout, downgrade}]};
handle_call({close, _} = Close, From, StateName, State, Connection) ->
%% Run terminate before returning so that the reuseaddr
- %% inet-option
- Result = Connection:terminate(Close, StateName, State),
+ %% inet-option works properly
+ Result = Connection:terminate(Close, StateName, State#state{terminated = true}),
{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}, _) ->
+ socket = Socket}, Connection) ->
case How0 of
How when How == write; How == both ->
Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
{BinMsg, _} =
- ssl_alert:encode(Alert, Version, ConnectionStates),
- Transport:send(Socket, BinMsg);
+ Connection:encode_alert(Alert, Version, ConnectionStates),
+ Connection:send(Transport, Socket, BinMsg);
_ ->
ok
end,
@@ -908,9 +937,8 @@ handle_call(renegotiate, From, StateName, _, _) when StateName =/= connection ->
handle_call({prf, Secret, Label, Seed, WantedLength}, From, _,
#state{connection_states = ConnectionStates,
negotiated_version = Version}, _) ->
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:current_connection_state(ConnectionStates, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{master_secret = MasterSecret,
client_random = ClientRandom,
server_random = ServerRandom,
@@ -925,7 +953,7 @@ handle_call({prf, Secret, Label, Seed, WantedLength}, From, _,
(client_random, Acc) -> [ClientRandom|Acc];
(server_random, Acc) -> [ServerRandom|Acc]
end, [], Seed)),
- ssl_handshake:prf(Version, PRFAlgorithm, SecretToUse, Label, SeedToUse, WantedLength)
+ ssl_handshake:prf(ssl:tls_version(Version), PRFAlgorithm, SecretToUse, Label, SeedToUse, WantedLength)
catch
exit:_ -> {error, badarg};
error:Reason -> {error, Reason}
@@ -936,20 +964,19 @@ handle_call(_,_,_,_,_) ->
handle_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, transport_cb = Transport,
- start_or_recv_from = StartFrom, role = Role,
protocol_cb = Connection,
+ start_or_recv_from = StartFrom, role = Role,
error_tag = ErrorTag,
tracker = Tracker} = State) when StateName =/= connection ->
- Connection:alert_user(Transport, Tracker,Socket,
- StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role),
+ alert_user(Transport, Tracker,Socket,
+ StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, Connection),
{stop, normal, State};
handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,
- protocol_cb = Connection,
error_tag = ErrorTag} = State) ->
Report = io_lib:format("SSL: Socket error: ~p ~n", [Reason]),
error_logger:info_report(Report),
- Connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
+ handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
{stop, normal, State};
handle_info({'DOWN', MonitorRef, _, _, _}, _,
@@ -991,7 +1018,10 @@ handle_info(Msg, StateName, #state{socket = Socket, error_tag = Tag} = State) ->
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.
+ %% when ssl:close/1 returns unless it is a downgrade where
+ %% we want to guarantee that close alert is recived before
+ %% returning. In both cases terminate has been run manually
+ %% before run by gen_statem which will end up here
ok;
terminate({shutdown, transport_closed} = Reason,
@@ -1017,8 +1047,8 @@ terminate(Reason, connection, #state{negotiated_version = Version,
transport_cb = Transport, socket = Socket
} = State) ->
handle_trusted_certs_db(State),
- {BinAlert, ConnectionStates} = terminate_alert(Reason, Version, ConnectionStates0),
- Transport:send(Socket, BinAlert),
+ {BinAlert, ConnectionStates} = terminate_alert(Reason, Version, ConnectionStates0, Connection),
+ Connection:send(Transport, Socket, BinAlert),
Connection:close(Reason, Socket, Transport, ConnectionStates, Check);
terminate(Reason, _StateName, #state{transport_cb = Transport, protocol_cb = Connection,
@@ -1052,15 +1082,135 @@ format_status(terminate, [_, StateName, State]) ->
ssl_options = NewOptions,
flight_buffer = ?SECRET_PRINTOUT}
}}]}].
+
+%%--------------------------------------------------------------------
+%%%
+%%--------------------------------------------------------------------
+write_application_data(Data0, From,
+ #state{socket = Socket,
+ negotiated_version = Version,
+ protocol_cb = Connection,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ 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 ->
+ Connection:renegotiate(State#state{renegotiation = {true, internal}},
+ [{next_event, {call, From}, {application_data, Data0}}]);
+ false ->
+ {Msgs, ConnectionStates} = Connection:encode_data(Data, Version, ConnectionStates0),
+ Result = Connection:send(Transport, Socket, Msgs),
+ ssl_connection:hibernate_after(connection, State#state{connection_states = ConnectionStates},
+ [{reply, From, Result}])
+ end.
+
+read_application_data(Data, #state{user_application = {_Mon, Pid},
+ socket = Socket,
+ protocol_cb = Connection,
+ transport_cb = Transport,
+ socket_options = SOpts,
+ bytes_to_read = BytesToRead,
+ start_or_recv_from = RecvFrom,
+ timer = Timer,
+ user_data_buffer = Buffer0,
+ tracker = Tracker} = State0) ->
+ Buffer1 = if
+ Buffer0 =:= <<>> -> Data;
+ Data =:= <<>> -> Buffer0;
+ true -> <<Buffer0/binary, Data/binary>>
+ end,
+ case get_data(SOpts, BytesToRead, Buffer1) of
+ {ok, ClientData, Buffer} -> % Send data
+ SocketOpt = deliver_app_data(Transport, Socket, SOpts,
+ ClientData, Pid, RecvFrom, Tracker, Connection),
+ cancel_timer(Timer),
+ State = State0#state{user_data_buffer = Buffer,
+ start_or_recv_from = undefined,
+ timer = undefined,
+ bytes_to_read = undefined,
+ socket_options = SocketOpt
+ },
+ if
+ SocketOpt#socket_options.active =:= false; Buffer =:= <<>> ->
+ %% Passive mode, wait for active once or recv
+ %% Active and empty, get more data
+ Connection:next_record_if_active(State);
+ true -> %% We have more data
+ read_application_data(<<>>, State)
+ end;
+ {more, Buffer} -> % no reply, we need more data
+ Connection:next_record(State0#state{user_data_buffer = Buffer});
+ {passive, Buffer} ->
+ Connection:next_record_if_active(State0#state{user_data_buffer = Buffer});
+ {error,_Reason} -> %% Invalid packet in packet mode
+ deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker, Connection),
+ {stop, normal, State0}
+ end.
+%%--------------------------------------------------------------------
+%%%
+%%--------------------------------------------------------------------
+handle_alert(#alert{level = ?FATAL} = Alert, StateName,
+ #state{socket = Socket, transport_cb = Transport,
+ protocol_cb = Connection,
+ 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}) ->
+ 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, Connection),
+ {stop, normal};
+
+handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
+ StateName, State) ->
+ handle_normal_shutdown(Alert, StateName, 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}};
+
+handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
+ #state{ssl_options = SslOpts, renegotiation = {true, From},
+ protocol_cb = Connection} = State0) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
+ gen_statem:reply(From, {error, renegotiation_rejected}),
+ {Record, State} = Connection:next_record(State0),
+ %% Go back to connection!
+ 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, protocol_cb = Connection} = State0) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
+ {Record, State} = Connection:next_record(State0),
+ Connection:next_event(StateName, Record, State).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
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).
+ session = #session{cipher_suite = CipherSuite, ecc = ECCCurve},
+ protocol_cb = Connection,
+ negotiated_version = {_,_} = Version,
+ ssl_options = Opts}) ->
+ RecordCB = record_cb(Connection),
+ CipherSuiteDef = ssl_cipher:erl_suite_definition(CipherSuite),
+ IsNamedCurveSuite = lists:member(element(1,CipherSuiteDef),
+ [ecdh_ecdsa, ecdhe_ecdsa, ecdh_anon]),
+ CurveInfo = case ECCCurve of
+ {namedCurve, Curve} when IsNamedCurveSuite ->
+ [{ecc, {named_curve, pubkey_cert_records:namedCurves(Curve)}}];
+ _ ->
+ []
+ end,
+ [{protocol, RecordCB:protocol_version(Version)},
+ {cipher_suite, CipherSuiteDef},
+ {sni_hostname, SNIHostname} | CurveInfo] ++ ssl_options_list(Opts).
do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocols} =
ServerHelloExt,
@@ -1070,7 +1220,7 @@ do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocol
= State0, Connection) when is_atom(Type) ->
ServerHello =
- ssl_handshake:server_hello(SessId, Version, ConnectionStates0, ServerHelloExt),
+ ssl_handshake:server_hello(SessId, ssl:tls_version(Version), ConnectionStates0, ServerHelloExt),
State = server_hello(ServerHello,
State0#state{expecting_next_protocol_negotiation =
NextProtocols =/= undefined}, Connection),
@@ -1088,33 +1238,33 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite,
negotiated_version = Version} = State0, Connection) ->
try server_certify_and_key_exchange(State0, Connection) of
#state{} = State1 ->
- State2 = server_hello_done(State1, Connection),
+ {State2, Actions} = server_hello_done(State1, Connection),
Session =
Session0#session{session_id = SessionId,
cipher_suite = CipherSuite,
compression_method = Compression},
{Record, State} = Connection:next_record(State2#state{session = Session}),
- Connection:next_event(certify, Record, State)
+ Connection:next_event(certify, Record, State, Actions)
catch
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, hello, State0)
+ handle_own_alert(Alert, Version, hello, State0)
end.
resumed_server_hello(#state{session = Session,
connection_states = ConnectionStates0,
negotiated_version = Version} = State0, Connection) ->
- case ssl_handshake:master_secret(record_cb(Connection), Version, Session,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), Session,
ConnectionStates0, server) of
{_, ConnectionStates1} ->
State1 = State0#state{connection_states = ConnectionStates1,
session = Session},
- State2 =
+ {State2, Actions} =
finalize_handshake(State1, abbreviated, Connection),
{Record, State} = Connection:next_record(State2),
- Connection:next_event(abbreviated, Record, State);
+ Connection:next_event(abbreviated, Record, State, Actions);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, hello, State0)
+ handle_own_alert(Alert, Version, hello, State0)
end.
server_hello(ServerHello, State0, Connection) ->
@@ -1180,7 +1330,7 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
tls_handshake_history = Handshake0} = State, Connection) ->
case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret,
- Version, HashSign, PrivateKey, Handshake0) of
+ ssl:tls_version(Version), HashSign, PrivateKey, Handshake0) of
#certificate_verify{} = Verified ->
Connection:queue_handshake(Verified, State);
ignore ->
@@ -1195,15 +1345,15 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} =
State0, Connection) ->
try do_client_certify_and_key_exchange(State0, Connection) of
State1 = #state{} ->
- State2 = finalize_handshake(State1, certify, Connection),
+ {State2, Actions} = finalize_handshake(State1, certify, Connection),
State3 = State2#state{
%% Reinitialize
client_certificate_requested = false},
{Record, State} = Connection:next_record(State3),
- Connection:next_event(cipher, Record, State)
+ Connection:next_event(cipher, Record, State, Actions)
catch
throw:#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0)
+ handle_own_alert(Alert, Version, certify, State0)
end.
do_client_certify_and_key_exchange(State0, Connection) ->
@@ -1217,8 +1367,25 @@ server_certify_and_key_exchange(State0, Connection) ->
request_client_cert(State2, Connection).
certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS},
- #state{private_key = Key} = State, Connection) ->
- PremasterSecret = ssl_handshake:premaster_secret(EncPMS, Key),
+ #state{private_key = Key, client_hello_version = {Major, Minor} = Version} = State, Connection) ->
+
+ %% Countermeasure for Bleichenbacher attack always provide some kind of premaster secret
+ %% and fail handshake later.RFC 5246 section 7.4.7.1.
+ PremasterSecret =
+ try ssl_handshake:premaster_secret(EncPMS, Key) of
+ Secret when erlang:byte_size(Secret) == ?NUM_OF_PREMASTERSECRET_BYTES ->
+ case Secret of
+ <<?BYTE(Major), ?BYTE(Minor), _/binary>> -> %% Correct
+ Secret;
+ <<?BYTE(_), ?BYTE(_), Rest/binary>> -> %% Version mismatch
+ <<?BYTE(Major), ?BYTE(Minor), Rest/binary>>
+ end;
+ _ -> %% erlang:byte_size(Secret) =/= ?NUM_OF_PREMASTERSECRET_BYTES
+ make_premaster_secret(Version, rsa)
+ catch
+ #alert{description = ?DECRYPT_ERROR} ->
+ make_premaster_secret(Version, rsa)
+ end,
calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey},
@@ -1294,12 +1461,11 @@ key_exchange(#state{role = server, key_algorithm = Algo,
Algo == dhe_rsa;
Algo == dh_anon ->
DHKeys = public_key:generate_key(Params),
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {dh, DHKeys, Params,
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version), {dh, DHKeys, Params,
HashSignAlgo, ClientRandom,
ServerRandom,
PrivateKey}),
@@ -1312,22 +1478,23 @@ key_exchange(#state{role = server, private_key = Key, key_algorithm = Algo} = St
key_exchange(#state{role = server, key_algorithm = Algo,
hashsign_algorithm = HashSignAlgo,
private_key = PrivateKey,
+ session = #session{ecc = ECCCurve},
connection_states = ConnectionStates0,
negotiated_version = Version
} = State0, Connection)
when Algo == ecdhe_ecdsa; Algo == ecdhe_rsa;
Algo == ecdh_anon ->
- ECDHKeys = public_key:generate_key(select_curve(State0)),
- ConnectionState =
+ ECDHKeys = public_key:generate_key(ECCCurve),
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {ecdh, ECDHKeys,
- HashSignAlgo, ClientRandom,
- ServerRandom,
- PrivateKey}),
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version),
+ {ecdh, ECDHKeys,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
State = Connection:queue_handshake(Msg, State0),
State#state{diffie_hellman_keys = ECDHKeys};
@@ -1341,14 +1508,14 @@ key_exchange(#state{role = server, key_algorithm = psk,
connection_states = ConnectionStates0,
negotiated_version = Version
} = State0, Connection) ->
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {psk, PskIdentityHint,
- HashSignAlgo, ClientRandom,
- ServerRandom,
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version),
+ {psk, PskIdentityHint,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
PrivateKey}),
Connection:queue_handshake(Msg, State0);
@@ -1361,16 +1528,16 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk,
negotiated_version = Version
} = State0, Connection) ->
DHKeys = public_key:generate_key(Params),
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- 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, ssl:tls_version(Version),
+ {dhe_psk,
+ PskIdentityHint, DHKeys, Params,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
State = Connection:queue_handshake(Msg, State0),
State#state{diffie_hellman_keys = DHKeys};
@@ -1384,15 +1551,15 @@ key_exchange(#state{role = server, key_algorithm = rsa_psk,
connection_states = ConnectionStates0,
negotiated_version = Version
} = State0, Connection) ->
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {psk, PskIdentityHint,
- HashSignAlgo, ClientRandom,
- ServerRandom,
- PrivateKey}),
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version),
+ {psk, PskIdentityHint,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = server, key_algorithm = Algo,
@@ -1413,15 +1580,15 @@ key_exchange(#state{role = server, key_algorithm = Algo,
Keys0 = {_,_} ->
Keys0
end,
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- Msg = ssl_handshake:key_exchange(server, Version, {srp, Keys, SrpParams,
- HashSignAlgo, ClientRandom,
- ServerRandom,
- PrivateKey}),
+ Msg = ssl_handshake:key_exchange(server, ssl:tls_version(Version),
+ {srp, Keys, SrpParams,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
State = Connection:queue_handshake(Msg, State0),
State#state{srp_params = SrpParams,
srp_keys = Keys};
@@ -1431,7 +1598,7 @@ key_exchange(#state{role = client,
public_key_info = PublicKeyInfo,
negotiated_version = Version,
premaster_secret = PremasterSecret} = State0, Connection) ->
- Msg = rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo),
+ Msg = rsa_key_exchange(ssl:tls_version(Version), PremasterSecret, PublicKeyInfo),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
@@ -1442,7 +1609,7 @@ key_exchange(#state{role = client,
when Algorithm == dhe_dss;
Algorithm == dhe_rsa;
Algorithm == dh_anon ->
- Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}),
+ Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {dh, DhPubKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
@@ -1452,14 +1619,14 @@ key_exchange(#state{role = client,
when Algorithm == ecdhe_ecdsa; Algorithm == ecdhe_rsa;
Algorithm == ecdh_ecdsa; Algorithm == ecdh_rsa;
Algorithm == ecdh_anon ->
- Msg = ssl_handshake:key_exchange(client, Version, {ecdh, Keys}),
+ Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {ecdh, Keys}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{role = client,
ssl_options = SslOpts,
key_algorithm = psk,
negotiated_version = Version} = State0, Connection) ->
- Msg = ssl_handshake:key_exchange(client, Version,
+ Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{psk, SslOpts#ssl_options.psk_identity}),
Connection:queue_handshake(Msg, State0);
@@ -1468,7 +1635,7 @@ key_exchange(#state{role = client,
key_algorithm = dhe_psk,
negotiated_version = Version,
diffie_hellman_keys = {DhPubKey, _}} = State0, Connection) ->
- Msg = ssl_handshake:key_exchange(client, Version,
+ Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{dhe_psk,
SslOpts#ssl_options.psk_identity, DhPubKey}),
Connection:queue_handshake(Msg, State0);
@@ -1479,7 +1646,7 @@ key_exchange(#state{role = client,
negotiated_version = Version,
premaster_secret = PremasterSecret}
= State0, Connection) ->
- Msg = rsa_psk_key_exchange(Version, SslOpts#ssl_options.psk_identity,
+ Msg = rsa_psk_key_exchange(ssl:tls_version(Version), SslOpts#ssl_options.psk_identity,
PremasterSecret, PublicKeyInfo),
Connection:queue_handshake(Msg, State0);
@@ -1491,7 +1658,7 @@ key_exchange(#state{role = client,
when Algorithm == srp_dss;
Algorithm == srp_rsa;
Algorithm == srp_anon ->
- Msg = ssl_handshake:key_exchange(client, Version, {srp, ClientPubKey}),
+ Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {srp, ClientPubKey}),
Connection:queue_handshake(Msg, State0).
rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
@@ -1504,7 +1671,7 @@ rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
Algorithm == ?sha384WithRSAEncryption;
Algorithm == ?sha512WithRSAEncryption
->
- ssl_handshake:key_exchange(client, Version,
+ ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{premaster_secret, PremasterSecret,
PublicKeyInfo});
rsa_key_exchange(_, _, _) ->
@@ -1521,7 +1688,7 @@ rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret,
Algorithm == ?sha384WithRSAEncryption;
Algorithm == ?sha512WithRSAEncryption
->
- ssl_handshake:key_exchange(client, Version,
+ ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{psk_premaster_secret, PskIdentity, PremasterSecret,
PublicKeyInfo});
rsa_psk_key_exchange(_, _, _, _) ->
@@ -1533,12 +1700,14 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer,
cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
negotiated_version = Version} = State0, Connection) ->
- #connection_state{security_parameters =
- #security_parameters{cipher_suite = CipherSuite}} =
+ #{security_parameters :=
+ #security_parameters{cipher_suite = CipherSuite}} =
ssl_record:pending_connection_state(ConnectionStates0, read),
- HashSigns = ssl_handshake:available_signature_algs(SupportedHashSigns, Version, [Version]),
+ TLSVersion = ssl:tls_version(Version),
+ HashSigns = ssl_handshake:available_signature_algs(SupportedHashSigns,
+ TLSVersion, [TLSVersion]),
Msg = ssl_handshake:certificate_request(CipherSuite, CertDbHandle, CertDbRef,
- HashSigns, Version),
+ HashSigns, TLSVersion),
State = Connection:queue_handshake(Msg, State0),
State#state{client_certificate_requested = true};
@@ -1551,7 +1720,7 @@ calculate_master_secret(PremasterSecret,
connection_states = ConnectionStates0,
session = Session0} = State0, Connection,
_Current, Next) ->
- case ssl_handshake:master_secret(record_cb(Connection), Version, PremasterSecret,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret,
ConnectionStates0, server) of
{MasterSecret, ConnectionStates} ->
Session = Session0#session{master_secret = MasterSecret},
@@ -1560,7 +1729,7 @@ calculate_master_secret(PremasterSecret,
{Record, State} = Connection:next_record(State1),
Connection:next_event(Next, Record, State);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, certify, State0)
+ handle_own_alert(Alert, Version, certify, State0)
end.
finalize_handshake(State0, StateName, Connection) ->
@@ -1593,7 +1762,7 @@ finished(#state{role = Role, negotiated_version = Version,
connection_states = ConnectionStates0,
tls_handshake_history = Handshake0} = State0, StateName, Connection) ->
MasterSecret = Session#session.master_secret,
- Finished = ssl_handshake:finished(Version, Role,
+ Finished = ssl_handshake:finished(ssl:tls_version(Version), Role,
get_current_prf(ConnectionStates0, write),
MasterSecret, Handshake0),
ConnectionStates = save_verify_data(Role, Finished, ConnectionStates0, StateName),
@@ -1620,12 +1789,13 @@ calculate_secret(#server_dh_params{dh_p = Prime, dh_g = Base,
Connection, certify, certify);
calculate_secret(#server_ecdh_params{curve = ECCurve, public = ECServerPubKey},
- State, Connection) ->
+ State=#state{session=Session}, Connection) ->
ECDHKeys = public_key:generate_key(ECCurve),
PremasterSecret =
ssl_handshake:premaster_secret(#'ECPoint'{point = ECServerPubKey}, ECDHKeys),
calculate_master_secret(PremasterSecret,
- State#state{diffie_hellman_keys = ECDHKeys},
+ State#state{diffie_hellman_keys = ECDHKeys,
+ session = Session#session{ecc = ECCurve}},
Connection, certify, certify);
calculate_secret(#server_psk_params{
@@ -1658,7 +1828,7 @@ master_secret(#alert{} = Alert, _) ->
master_secret(PremasterSecret, #state{session = Session,
negotiated_version = Version, role = Role,
connection_states = ConnectionStates0} = State) ->
- case ssl_handshake:master_secret(tls_record, Version, PremasterSecret,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret,
ConnectionStates0, Role) of
{MasterSecret, ConnectionStates} ->
State#state{
@@ -1719,16 +1889,11 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0
Connection) ->
ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data,
ConnectionStates0),
- State1 =
+ {State1, Actions} =
finalize_handshake(State0#state{connection_states = ConnectionStates1,
session = Session}, cipher, Connection),
{Record, State} = prepare_connection(State1, Connection),
- Connection:next_event(connection, Record, State).
-
-select_curve(#state{client_ecc = {[Curve|_], _}}) ->
- {namedCurve, Curve};
-select_curve(_) ->
- {namedCurve, ?secp256r1}.
+ Connection:next_event(connection, Record, State, Actions).
is_anonymous(Algo) when Algo == dh_anon;
Algo == ecdh_anon;
@@ -1741,11 +1906,11 @@ is_anonymous(_) ->
false.
get_current_prf(CStates, Direction) ->
- CS = ssl_record:current_connection_state(CStates, Direction),
- CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+ #{security_parameters := SecParams} = ssl_record:current_connection_state(CStates, Direction),
+ SecParams#security_parameters.prf_algorithm.
get_pending_prf(CStates, Direction) ->
- CS = ssl_record:pending_connection_state(CStates, Direction),
- CS#connection_state.security_parameters#security_parameters.prf_algorithm.
+ #{security_parameters := SecParams} = ssl_record:pending_connection_state(CStates, Direction),
+ SecParams#security_parameters.prf_algorithm.
opposite_role(client) ->
server;
@@ -1787,7 +1952,7 @@ get_socket_opts(Transport, Socket, [active | Tags], SockOpts, Acc) ->
get_socket_opts(Transport, Socket, Tags, SockOpts,
[{active, SockOpts#socket_options.active} | Acc]);
get_socket_opts(Transport, Socket, [Tag | Tags], SockOpts, Acc) ->
- try ssl_socket:getopts(Transport, Socket, [Tag]) of
+ try tls_socket:getopts(Transport, Socket, [Tag]) of
{ok, [Opt]} ->
get_socket_opts(Transport, Socket, Tags, SockOpts, [Opt | Acc]);
{error, Error} ->
@@ -1803,7 +1968,7 @@ set_socket_opts(_,_, [], SockOpts, []) ->
{ok, SockOpts};
set_socket_opts(Transport, Socket, [], SockOpts, Other) ->
%% Set non emulated options
- try ssl_socket:setopts(Transport, Socket, Other) of
+ try tls_socket:setopts(Transport, Socket, Other) of
ok ->
{ok, SockOpts};
{error, InetError} ->
@@ -1869,17 +2034,17 @@ hibernate_after(connection = StateName,
hibernate_after(StateName, State, Actions) ->
{next_state, StateName, State, Actions}.
-terminate_alert(normal, Version, ConnectionStates) ->
- ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
+terminate_alert(normal, Version, ConnectionStates, Connection) ->
+ Connection:encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
Version, ConnectionStates);
-terminate_alert({Reason, _}, Version, ConnectionStates) when Reason == close;
- Reason == shutdown ->
- ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
+terminate_alert({Reason, _}, Version, ConnectionStates, Connection) when Reason == close;
+ Reason == shutdown ->
+ Connection:encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
Version, ConnectionStates);
-terminate_alert(_, Version, ConnectionStates) ->
- {BinAlert, _} = ssl_alert:encode(?ALERT_REC(?FATAL, ?INTERNAL_ERROR),
- Version, ConnectionStates),
+terminate_alert(_, Version, ConnectionStates, Connection) ->
+ {BinAlert, _} = Connection:encode_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR),
+ Version, ConnectionStates),
BinAlert.
handle_trusted_certs_db(#state{ssl_options =
@@ -1967,7 +2132,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
session_cache = Cache,
session_cache_cb = CacheCb} = State0) ->
Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}),
- case ssl_handshake:master_secret(tls_record, Version, Session,
+ case ssl_handshake:master_secret(ssl:tls_version(Version), Session,
ConnectionStates0, client) of
{_, ConnectionStates} ->
{Record, State} =
@@ -1976,7 +2141,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
session = Session}),
Connection:next_event(abbreviated, Record, State);
#alert{} = Alert ->
- Connection:handle_own_alert(Alert, Version, hello, State0)
+ handle_own_alert(Alert, Version, hello, State0)
end.
make_premaster_secret({MajVer, MinVer}, rsa) ->
@@ -2043,7 +2208,7 @@ handle_active_option(_, StateName, To, Reply, #state{user_data_buffer = <<>>} =
%% user_data_buffer =/= <<>>
handle_active_option(_, StateName0, To, Reply, #state{protocol_cb = Connection} = State0) ->
- case Connection:read_application_data(<<>>, State0) of
+ case read_application_data(<<>>, State0) of
{stop, Reason, State} ->
{stop, Reason, State};
{Record, State1} ->
@@ -2057,3 +2222,277 @@ handle_active_option(_, StateName0, To, Reply, #state{protocol_cb = Connection}
Stop
end
end.
+
+encode_packet(Data, #socket_options{packet=Packet}) ->
+ case Packet of
+ 1 -> encode_size_packet(Data, 8, (1 bsl 8) - 1);
+ 2 -> encode_size_packet(Data, 16, (1 bsl 16) - 1);
+ 4 -> encode_size_packet(Data, 32, (1 bsl 32) - 1);
+ _ -> Data
+ end.
+
+encode_size_packet(Bin, Size, Max) ->
+ Len = erlang:byte_size(Bin),
+ case Len > Max of
+ true -> throw({error, {badarg, {packet_to_large, Len, Max}}});
+ false -> <<Len:Size, Bin/binary>>
+ end.
+
+time_to_renegotiate(_Data,
+ #{current_write := #{sequence_number := Num}},
+ RenegotiateAt) ->
+
+ %% We could do test:
+ %% is_time_to_renegotiate((erlang:byte_size(_Data) div ?MAX_PLAIN_TEXT_LENGTH) + 1, RenegotiateAt),
+ %% but we chose to have a some what lower renegotiateAt and a much cheaper test
+ is_time_to_renegotiate(Num, RenegotiateAt).
+
+is_time_to_renegotiate(N, M) when N < M->
+ false;
+is_time_to_renegotiate(_,_) ->
+ true.
+
+
+%% Picks ClientData
+get_data(_, _, <<>>) ->
+ {more, <<>>};
+%% Recv timed out save buffer data until next recv
+get_data(#socket_options{active=false}, undefined, Buffer) ->
+ {passive, Buffer};
+get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
+ when Raw =:= raw; Raw =:= 0 -> %% Raw Mode
+ if
+ Active =/= false orelse BytesToRead =:= 0 ->
+ %% Active true or once, or passive mode recv(0)
+ {ok, Buffer, <<>>};
+ byte_size(Buffer) >= BytesToRead ->
+ %% Passive Mode, recv(Bytes)
+ <<Data:BytesToRead/binary, Rest/binary>> = Buffer,
+ {ok, Data, Rest};
+ true ->
+ %% Passive Mode not enough data
+ {more, Buffer}
+ end;
+get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) ->
+ PacketOpts = [{packet_size, Size}],
+ case decode_packet(Type, Buffer, PacketOpts) of
+ {more, _} ->
+ {more, Buffer};
+ Decoded ->
+ Decoded
+ end.
+
+decode_packet({http, headers}, Buffer, PacketOpts) ->
+ decode_packet(httph, Buffer, PacketOpts);
+decode_packet({http_bin, headers}, Buffer, PacketOpts) ->
+ decode_packet(httph_bin, Buffer, PacketOpts);
+decode_packet(Type, Buffer, PacketOpts) ->
+ erlang:decode_packet(Type, Buffer, PacketOpts).
+
+%% Just like with gen_tcp sockets, an ssl socket that has been configured with
+%% {packet, http} (or {packet, http_bin}) will automatically switch to expect
+%% HTTP headers after it sees a HTTP Request or HTTP Response line. We
+%% represent the current state as follows:
+%% #socket_options.packet =:= http: Expect a HTTP Request/Response line
+%% #socket_options.packet =:= {http, headers}: Expect HTTP Headers
+%% Note that if the user has explicitly configured the socket to expect
+%% HTTP headers using the {packet, httph} option, we don't do any automatic
+%% switching of states.
+deliver_app_data(Transport, Socket, SOpts = #socket_options{active=Active, packet=Type},
+ Data, Pid, From, Tracker, Connection) ->
+ send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data, Tracker, Connection)),
+ SO = case Data of
+ {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)),
+ ((Type =:= http) or (Type =:= http_bin)) ->
+ SOpts#socket_options{packet={Type, headers}};
+ http_eoh when tuple_size(Type) =:= 2 ->
+ % End of headers - expect another Request/Response line
+ {Type1, headers} = Type,
+ SOpts#socket_options{packet=Type1};
+ _ ->
+ SOpts
+ end,
+ case Active of
+ once ->
+ SO#socket_options{active=false};
+ _ ->
+ SO
+ end.
+
+format_reply(_, _,#socket_options{active = false, mode = Mode, packet = Packet,
+ header = Header}, Data, _, _) ->
+ {ok, do_format_reply(Mode, Packet, Header, Data)};
+format_reply(Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet,
+ header = Header}, Data, Tracker, Connection) ->
+ {ssl, Connection:socket(self(), Transport, Socket, Connection, Tracker),
+ do_format_reply(Mode, Packet, Header, Data)}.
+
+deliver_packet_error(Transport, Socket, SO= #socket_options{active = Active}, Data, Pid, From, Tracker, Connection) ->
+ send_or_reply(Active, Pid, From, format_packet_error(Transport, Socket, SO, Data, Tracker, Connection)).
+
+format_packet_error(_, _,#socket_options{active = false, mode = Mode}, Data, _, _) ->
+ {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}};
+format_packet_error(Transport, Socket, #socket_options{active = _, mode = Mode}, Data, Tracker, Connection) ->
+ {ssl_error, Connection:socket(self(), Transport, Socket, Connection, Tracker),
+ {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}.
+
+do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode
+ header(N, Data);
+do_format_reply(binary, _, _, Data) ->
+ Data;
+do_format_reply(list, Packet, _, Data)
+ when Packet == http; Packet == {http, headers};
+ Packet == http_bin; Packet == {http_bin, headers};
+ Packet == httph; Packet == httph_bin ->
+ Data;
+do_format_reply(list, _,_, Data) ->
+ binary_to_list(Data).
+
+header(0, <<>>) ->
+ <<>>;
+header(_, <<>>) ->
+ [];
+header(0, Binary) ->
+ Binary;
+header(N, Binary) ->
+ <<?BYTE(ByteN), NewBinary/binary>> = Binary,
+ [ByteN | header(N-1, NewBinary)].
+
+send_or_reply(false, _Pid, From, Data) when From =/= undefined ->
+ 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, _, _) ->
+ ok;
+send_or_reply(_, Pid, _From, Data) ->
+ send_user(Pid, Data).
+
+send_user(Pid, Msg) ->
+ Pid ! Msg.
+
+alert_user(Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role, Connection) ->
+ alert_user(Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role, Connection);
+alert_user(Transport, Tracker, Socket,_, _, _, From, Alert, Role, Connection) ->
+ alert_user(Transport, Tracker, Socket, From, Alert, Role, Connection).
+
+alert_user(Transport, Tracker, Socket, From, Alert, Role, Connection) ->
+ alert_user(Transport, Tracker, Socket, false, no_pid, From, Alert, Role, Connection).
+
+alert_user(_, _, _, false = Active, Pid, From, Alert, Role, _) when From =/= undefined ->
+ %% If there is an outstanding ssl_accept | recv
+ %% From will be defined and send_or_reply will
+ %% send the appropriate error message.
+ ReasonCode = ssl_alert:reason_code(Alert, Role),
+ send_or_reply(Active, Pid, From, {error, ReasonCode});
+alert_user(Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Connection) ->
+ case ssl_alert:reason_code(Alert, Role) of
+ closed ->
+ send_or_reply(Active, Pid, From,
+ {ssl_closed, Connection:socket(self(),
+ Transport, Socket, Connection, Tracker)});
+ ReasonCode ->
+ send_or_reply(Active, Pid, From,
+ {ssl_error, Connection:socket(self(),
+ Transport, Socket, Connection, Tracker), ReasonCode})
+ end.
+
+log_alert(true, Info, Alert) ->
+ Txt = ssl_alert:alert_txt(Alert),
+ error_logger:format("SSL: ~p: ~s\n", [Info, Txt]);
+log_alert(false, _, _) ->
+ ok.
+
+handle_own_alert(Alert, Version, StateName,
+ #state{transport_cb = Transport,
+ socket = Socket,
+ protocol_cb = Connection,
+ connection_states = ConnectionStates,
+ ssl_options = SslOpts} = State) ->
+ try %% Try to tell the other side
+ {BinMsg, _} =
+ Connection:encode_alert(Alert, Version, ConnectionStates),
+ Connection:send(Transport, Socket, BinMsg)
+ catch _:_ -> %% Can crash if we are in a uninitialized state
+ ignore
+ end,
+ try %% Try to tell the local user
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
+ handle_normal_shutdown(Alert,StateName, State)
+ catch _:_ ->
+ ok
+ end,
+ {stop, {shutdown, own_alert}}.
+
+handle_normal_shutdown(Alert, _, #state{socket = Socket,
+ transport_cb = Transport,
+ protocol_cb = Connection,
+ start_or_recv_from = StartFrom,
+ tracker = Tracker,
+ role = Role, renegotiation = {false, first}}) ->
+ alert_user(Transport, Tracker,Socket, StartFrom, Alert, Role, Connection);
+
+handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
+ socket_options = Opts,
+ transport_cb = Transport,
+ protocol_cb = Connection,
+ user_application = {_Mon, Pid},
+ tracker = Tracker,
+ start_or_recv_from = RecvFrom, role = Role}) ->
+ alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, Connection).
+
+invalidate_session(client, Host, Port, Session) ->
+ ssl_manager:invalidate_session(Host, Port, Session);
+invalidate_session(server, _, Port, Session) ->
+ ssl_manager:invalidate_session(Port, Session).
+
+handle_sni_extension(undefined, State) ->
+ State;
+handle_sni_extension(#sni{hostname = Hostname}, State0) ->
+ NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname),
+ case NewOptions of
+ undefined ->
+ State0;
+ _ ->
+ {ok, #{cert_db_ref := Ref,
+ cert_db_handle := CertDbHandle,
+ fileref_db_handle := FileRefHandle,
+ session_cache := CacheHandle,
+ crl_db_info := CRLDbHandle,
+ private_key := Key,
+ dh_params := DHParams,
+ own_certificate := OwnCert}} =
+ ssl_config:init(NewOptions, State0#state.role),
+ State0#state{
+ session = State0#state.session#session{own_certificate = OwnCert},
+ file_ref_db = FileRefHandle,
+ cert_db_ref = Ref,
+ cert_db = CertDbHandle,
+ crl_db = CRLDbHandle,
+ session_cache = CacheHandle,
+ private_key = Key,
+ diffie_hellman_params = DHParams,
+ ssl_options = NewOptions,
+ sni_hostname = Hostname
+ }
+ end.
+
+update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) ->
+ SSLOption =
+ case OrigSSLOptions#ssl_options.sni_fun of
+ undefined ->
+ proplists:get_value(SNIHostname,
+ OrigSSLOptions#ssl_options.sni_hosts);
+ SNIFun ->
+ SNIFun(SNIHostname)
+ end,
+ case SSLOption of
+ undefined ->
+ undefined;
+ _ ->
+ ssl:handle_options(SSLOption, OrigSSLOptions)
+ end.
+
+new_emulated([], EmOpts) ->
+ EmOpts;
+new_emulated(NewEmOpts, _) ->
+ NewEmOpts.
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index 4b54943ddf..016cc4b525 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2013-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,11 +43,12 @@
error_tag :: atom(), % ex tcp_error
host :: string() | inet:ip_address(),
port :: integer(),
- socket :: port(),
+ socket :: port() | tuple(), %% TODO: dtls socket
ssl_options :: #ssl_options{},
socket_options :: #socket_options{},
- connection_states :: #connection_states{} | secret_printout(),
+ connection_states :: ssl_record:connection_states() | secret_printout(),
protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl
+ unprocessed_handshake_events = 0 :: integer(),
tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout()
| 'undefined',
cert_db :: reference() | 'undefined',
@@ -56,6 +57,7 @@
session_cache_cb :: atom(),
crl_db :: term(),
negotiated_version :: ssl_record:ssl_version() | 'undefined',
+ client_hello_version :: ssl_record:ssl_version() | 'undefined',
client_certificate_requested = false :: boolean(),
key_algorithm :: ssl_cipher:key_algo(),
hashsign_algorithm = {undefined, undefined},
@@ -80,18 +82,18 @@
allow_renegotiate = true ::boolean(),
expecting_next_protocol_negotiation = false ::boolean(),
expecting_finished = false ::boolean(),
- negotiated_protocol = undefined :: undefined | binary(),
- client_ecc, % {Curves, PointFmt}
+ next_protocol = undefined :: undefined | binary(),
+ negotiated_protocol,
tracker :: pid() | 'undefined', %% Tracker process for listen socket
sni_hostname = undefined,
downgrade,
- flight_buffer = [] :: list() %% Buffer of TLS/DTLS records, used during the TLS handshake
- %% to when possible pack more than on TLS record into the
- %% underlaying packet format. Introduced by DTLS - RFC 4347.
- %% The mecahnism is also usefull in TLS although we do not
- %% need to worry about packet loss in TLS.
+ flight_buffer = [] :: list() | map(), %% Buffer of TLS/DTLS records, used during the TLS handshake
+ %% to when possible pack more than on TLS record into the
+ %% underlaying packet format. Introduced by DTLS - RFC 4347.
+ %% The mecahnism is also usefull in TLS although we do not
+ %% need to worry about packet loss in TLS. In DTLS we need to track DTLS handshake seqnr
+ flight_state = reliable %% reliable | {retransmit, integer()}| {waiting, ref(), integer()} - last two is used in DTLS over udp.
}).
-
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
#'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME,
base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}).
diff --git a/lib/ssl/src/ssl_connection_sup.erl b/lib/ssl/src/ssl_connection_sup.erl
new file mode 100644
index 0000000000..1a1f43e683
--- /dev/null
+++ b/lib/ssl/src/ssl_connection_sup.erl
@@ -0,0 +1,101 @@
+%%
+%% %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(ssl_connection_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+
+-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+
+init([]) ->
+
+ TLSConnetionManager = tls_connection_manager_child_spec(),
+ %% Handles emulated options so that they inherited by the accept
+ %% socket, even when setopts is performed on the listen socket
+ ListenOptionsTracker = listen_options_tracker_child_spec(),
+
+ DTLSConnetionManager = dtls_connection_manager_child_spec(),
+ DTLSUdpListeners = dtls_udp_listeners_spec(),
+
+ {ok, {{one_for_one, 10, 3600}, [TLSConnetionManager,
+ ListenOptionsTracker,
+ DTLSConnetionManager,
+ DTLSUdpListeners
+ ]}}.
+
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+tls_connection_manager_child_spec() ->
+ Name = tls_connection,
+ StartFunc = {tls_connection_sup, start_link, []},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [tls_connection_sup],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+dtls_connection_manager_child_spec() ->
+ Name = dtls_connection,
+ StartFunc = {dtls_connection_sup, start_link, []},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [dtls_connection_sup],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+listen_options_tracker_child_spec() ->
+ Name = tls_socket,
+ StartFunc = {ssl_listen_tracker_sup, start_link, []},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [tls_socket],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+dtls_udp_listeners_spec() ->
+ Name = dtls_udp_listener,
+ StartFunc = {dtls_udp_sup, start_link, []},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
diff --git a/lib/ssl/src/ssl_crl.erl b/lib/ssl/src/ssl_crl.erl
index d9f21e04ac..888a75bfd6 100644
--- a/lib/ssl/src/ssl_crl.erl
+++ b/lib/ssl/src/ssl_crl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2017. 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.
@@ -29,7 +29,7 @@
-export([trusted_cert_and_path/3]).
-trusted_cert_and_path(CRL, {SerialNumber, Issuer},{Db, DbRef} = DbHandle) ->
+trusted_cert_and_path(CRL, {SerialNumber, Issuer},{_, {Db, DbRef}} = DbHandle) ->
case ssl_pkix_db:lookup_trusted_cert(Db, DbRef, SerialNumber, Issuer) of
undefined ->
trusted_cert_and_path(CRL, issuer_not_found, DbHandle);
@@ -37,17 +37,34 @@ trusted_cert_and_path(CRL, {SerialNumber, Issuer},{Db, DbRef} = DbHandle) ->
{ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef),
{ok, Root, lists:reverse(Chain)}
end;
-
-trusted_cert_and_path(CRL, issuer_not_found, {Db, DbRef} = DbHandle) ->
- case find_issuer(CRL, DbHandle) of
+trusted_cert_and_path(CRL, issuer_not_found, {CertPath, {Db, DbRef}}) ->
+ case find_issuer(CRL, {certpath,
+ [{Der, public_key:pkix_decode_cert(Der,otp)} || Der <- CertPath]}) of
{ok, OtpCert} ->
{ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef),
{ok, Root, lists:reverse(Chain)};
{error, issuer_not_found} ->
- {ok, unknown_crl_ca, []}
- end.
+ trusted_cert_and_path(CRL, issuer_not_found, {Db, DbRef})
+ end;
+trusted_cert_and_path(CRL, issuer_not_found, {Db, DbRef} = DbInfo) ->
+ case find_issuer(CRL, DbInfo) of
+ {ok, OtpCert} ->
+ {ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef),
+ {ok, Root, lists:reverse(Chain)};
+ {error, issuer_not_found} ->
+ {error, unknown_ca}
+ end.
-find_issuer(CRL, {Db,_}) ->
+find_issuer(CRL, {certpath = Db, DbRef}) ->
+ Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)),
+ IsIssuerFun =
+ fun({_Der,ErlCertCandidate}, Acc) ->
+ verify_crl_issuer(CRL, ErlCertCandidate, Issuer, Acc);
+ (_, Acc) ->
+ Acc
+ end,
+ find_issuer(IsIssuerFun, Db, DbRef);
+find_issuer(CRL, {Db, DbRef}) ->
Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)),
IsIssuerFun =
fun({_Key, {_Der,ErlCertCandidate}}, Acc) ->
@@ -55,13 +72,32 @@ find_issuer(CRL, {Db,_}) ->
(_, Acc) ->
Acc
end,
-
+ find_issuer(IsIssuerFun, Db, DbRef).
+
+find_issuer(IsIssuerFun, certpath, Certs) ->
+ try lists:foldl(IsIssuerFun, issuer_not_found, Certs) of
+ issuer_not_found ->
+ {error, issuer_not_found}
+ catch
+ {ok, _} = Result ->
+ Result
+ end;
+find_issuer(IsIssuerFun, extracted, CertsData) ->
+ Certs = [Entry || {decoded, Entry} <- CertsData],
+ try lists:foldl(IsIssuerFun, issuer_not_found, Certs) of
+ issuer_not_found ->
+ {error, issuer_not_found}
+ catch
+ {ok, _} = Result ->
+ Result
+ end;
+find_issuer(IsIssuerFun, Db, _) ->
try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, Db) of
- issuer_not_found ->
- {error, issuer_not_found}
- catch
- {ok, _} = Result ->
- Result
+ issuer_not_found ->
+ {error, issuer_not_found}
+ catch
+ {ok, _} = Result ->
+ Result
end.
verify_crl_issuer(CRL, ErlCertCandidate, Issuer, NotIssuer) ->
diff --git a/lib/ssl/src/ssl_crl_cache.erl b/lib/ssl/src/ssl_crl_cache.erl
index 647e0465fe..86c0207515 100644
--- a/lib/ssl/src/ssl_crl_cache.erl
+++ b/lib/ssl/src/ssl_crl_cache.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-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.
diff --git a/lib/ssl/src/ssl_crl_cache_api.erl b/lib/ssl/src/ssl_crl_cache_api.erl
index c3a57e2f49..d5380583e7 100644
--- a/lib/ssl/src/ssl_crl_cache_api.erl
+++ b/lib/ssl/src/ssl_crl_cache_api.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-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.
diff --git a/lib/ssl/src/ssl_dist_admin_sup.erl b/lib/ssl/src/ssl_dist_admin_sup.erl
new file mode 100644
index 0000000000..f60806c4cb
--- /dev/null
+++ b/lib/ssl/src/ssl_dist_admin_sup.erl
@@ -0,0 +1,74 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016-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(ssl_dist_admin_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+
+-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+
+init([]) ->
+ PEMCache = pem_cache_child_spec(),
+ SessionCertManager = session_and_cert_manager_child_spec(),
+ {ok, {{rest_for_one, 10, 3600}, [PEMCache, SessionCertManager]}}.
+
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+pem_cache_child_spec() ->
+ Name = ssl_pem_cache_dist,
+ StartFunc = {ssl_pem_cache, start_link_dist, [[]]},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [ssl_pem_cache],
+ Type = worker,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+session_and_cert_manager_child_spec() ->
+ Opts = ssl_admin_sup:manager_opts(),
+ Name = ssl_dist_manager,
+ StartFunc = {ssl_manager, start_link_dist, [Opts]},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [ssl_manager],
+ Type = worker,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
diff --git a/lib/ssl/src/ssl_dist_connection_sup.erl b/lib/ssl/src/ssl_dist_connection_sup.erl
new file mode 100644
index 0000000000..e5842c866e
--- /dev/null
+++ b/lib/ssl/src/ssl_dist_connection_sup.erl
@@ -0,0 +1,79 @@
+%%
+%% %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(ssl_dist_connection_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+
+-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+
+init([]) ->
+
+ TLSConnetionManager = tls_connection_manager_child_spec(),
+ %% Handles emulated options so that they inherited by the accept
+ %% socket, even when setopts is performed on the listen socket
+ ListenOptionsTracker = listen_options_tracker_child_spec(),
+
+ {ok, {{one_for_one, 10, 3600}, [TLSConnetionManager,
+ ListenOptionsTracker
+ ]}}.
+
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+tls_connection_manager_child_spec() ->
+ Name = dist_tls_connection,
+ StartFunc = {tls_connection_sup, start_link_dist, []},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [tls_connection_sup],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+listen_options_tracker_child_spec() ->
+ Name = dist_tls_socket,
+ StartFunc = {ssl_listen_tracker_sup, start_link_dist, []},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [tls_socket],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl
index a6eb1be1f6..690b896919 100644
--- a/lib/ssl/src/ssl_dist_sup.erl
+++ b/lib/ssl/src/ssl_dist_sup.erl
@@ -44,34 +44,29 @@ start_link() ->
%%%=========================================================================
init([]) ->
- SessionCertManager = session_and_cert_manager_child_spec(),
- ConnetionManager = connection_manager_child_spec(),
- ListenOptionsTracker = listen_options_tracker_child_spec(),
+ AdminSup = ssl_admin_child_spec(),
+ ConnectionSup = ssl_connection_sup(),
ProxyServer = proxy_server_child_spec(),
-
- {ok, {{one_for_all, 10, 3600}, [SessionCertManager, ConnetionManager,
- ListenOptionsTracker,
- ProxyServer]}}.
+ {ok, {{one_for_all, 10, 3600}, [AdminSup, ProxyServer, ConnectionSup]}}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-session_and_cert_manager_child_spec() ->
- Opts = ssl_sup:manager_opts(),
- Name = ssl_manager_dist,
- StartFunc = {ssl_manager, start_link_dist, [Opts]},
+ssl_admin_child_spec() ->
+ Name = ssl_dist_admin_sup,
+ StartFunc = {ssl_dist_admin_sup, start_link , []},
Restart = permanent,
Shutdown = 4000,
- Modules = [ssl_manager],
- Type = worker,
+ Modules = [ssl_admin_sup],
+ Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-connection_manager_child_spec() ->
- Name = ssl_connection_dist,
- StartFunc = {tls_connection_sup, start_link_dist, []},
- Restart = permanent,
- Shutdown = infinity,
- Modules = [tls_connection_sup],
+ssl_connection_sup() ->
+ Name = ssl_dist_connection_sup,
+ StartFunc = {ssl_dist_connection_sup, start_link, []},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [ssl_connection_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
@@ -83,12 +78,3 @@ proxy_server_child_spec() ->
Modules = [ssl_tls_dist_proxy],
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-
-listen_options_tracker_child_spec() ->
- Name = ssl_socket_dist,
- StartFunc = {ssl_listen_tracker_sup, start_link_dist, []},
- Restart = permanent,
- Shutdown = 4000,
- Modules = [ssl_socket],
- Type = supervisor,
- {Name, StartFunc, Restart, Shutdown, Type, Modules}.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 9c3fe9d73b..e84473f215 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. 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.
@@ -51,8 +51,8 @@
%% Handle handshake messages
-export([certify/10, client_certificate_verify/6, certificate_verify/6, verify_signature/5,
- master_secret/5, server_key_exchange_hash/2, verify_connection/6,
- init_handshake_history/0, update_handshake_history/2, verify_server_key/5
+ master_secret/4, server_key_exchange_hash/2, verify_connection/6,
+ init_handshake_history/0, update_handshake_history/3, verify_server_key/5
]).
%% Encode/Decode
@@ -70,11 +70,11 @@
%% Extensions handling
-export([client_hello_extensions/6,
handle_client_hello_extensions/9, %% Returns server hello extensions
- handle_server_hello_extensions/9, select_curve/2
+ handle_server_hello_extensions/9, select_curve/2, select_curve/3
]).
%% MISC
--export([select_version/3, prf/6, select_hashsign/5,
+-export([select_version/3, prf/6, select_hashsign/4, select_hashsign/5,
select_hashsign_algs/3,
premaster_secret/2, premaster_secret/3, premaster_secret/4]).
@@ -94,15 +94,14 @@ hello_request() ->
#hello_request{}.
%%--------------------------------------------------------------------
--spec server_hello(#session{}, ssl_record:ssl_version(), #connection_states{},
+-spec server_hello(#session{}, ssl_record:ssl_version(), ssl_record:connection_states(),
#hello_extensions{}) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
%%--------------------------------------------------------------------
server_hello(SessionId, Version, ConnectionStates, Extensions) ->
- Pending = ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = Pending#connection_state.security_parameters,
-
+ #{security_parameters := SecParams} =
+ ssl_record:pending_connection_state(ConnectionStates, read),
#server_hello{server_version = Version,
cipher_suite = SecParams#security_parameters.cipher_suite,
compression_method =
@@ -121,11 +120,13 @@ server_hello_done() ->
#server_hello_done{}.
client_hello_extensions(Host, Version, CipherSuites,
- #ssl_options{signature_algs = SupportedHashSigns, versions = AllVersions} = SslOpts, ConnectionStates, Renegotiation) ->
+ #ssl_options{signature_algs = SupportedHashSigns,
+ eccs = SupportedECCs,
+ versions = AllVersions} = SslOpts, ConnectionStates, Renegotiation) ->
{EcPointFormats, EllipticCurves} =
case advertises_ec_ciphers(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites)) of
true ->
- client_ecc_extensions(tls_v1, Version);
+ client_ecc_extensions(SupportedECCs);
false ->
{undefined, undefined}
end,
@@ -335,9 +336,8 @@ verify_server_key(#server_key_params{params_bin = EncParams,
signature = Signature},
HashSign = {HashAlgo, _},
ConnectionStates, Version, PubKeyInfo) ->
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
Hash = server_key_exchange_hash(HashAlgo,
@@ -397,14 +397,13 @@ verify_signature(_, Hash, {HashAlgo, _SignAlg}, Signature,
%%--------------------------------------------------------------------
certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
MaxPathLen, _Verify, ValidationFunAndState0, PartialChain, CRLCheck, CRLDbHandle, Role) ->
- [PeerCert | _] = ASN1Certs,
-
- ValidationFunAndState = validation_fun_and_state(ValidationFunAndState0, Role,
- CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle),
-
+ [PeerCert | _] = ASN1Certs,
try
{TrustedCert, CertPath} =
ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef, PartialChain),
+ ValidationFunAndState = validation_fun_and_state(ValidationFunAndState0, Role,
+ CertDbHandle, CertDbRef,
+ CRLCheck, CRLDbHandle, CertPath),
case public_key:pkix_path_validation(TrustedCert,
CertPath,
[{max_path_length, MaxPathLen},
@@ -447,7 +446,7 @@ init_handshake_history() ->
{[], []}.
%%--------------------------------------------------------------------
--spec update_handshake_history(ssl_handshake:ssl_handshake_history(), Data ::term()) ->
+-spec update_handshake_history(ssl_handshake:ssl_handshake_history(), Data ::term(), boolean()) ->
ssl_handshake:ssl_handshake_history().
%%
%% Description: Update the handshake history buffer with Data.
@@ -457,14 +456,14 @@ update_handshake_history(Handshake, % special-case SSL2 client hello
?UINT16(CSLength), ?UINT16(0),
?UINT16(CDLength),
CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>) ->
+ ChallengeData:CDLength/binary>>, true) ->
update_handshake_history(Handshake,
<<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor),
?UINT16(CSLength), ?UINT16(0),
?UINT16(CDLength),
CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>);
-update_handshake_history({Handshake0, _Prev}, Data) ->
+ ChallengeData:CDLength/binary>>, true);
+update_handshake_history({Handshake0, _Prev}, Data, _) ->
{[Data|Handshake0], Handshake0}.
%% %%--------------------------------------------------------------------
@@ -581,7 +580,7 @@ prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) ->
{atom(), atom()} | undefined | #alert{}.
%%
-%% Description: Handles signature_algorithms extension
+%% Description: Handles signature_algorithms hello extension (server)
%%--------------------------------------------------------------------
select_hashsign(_, undefined, _, _, _Version) ->
{null, anon};
@@ -593,14 +592,17 @@ select_hashsign(HashSigns, Cert, KeyExAlgo,
select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, KeyExAlgo, SupportedHashSigns,
{Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
#'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- Sign = cert_sign(Algo),
- case lists:filter(fun({sha, dsa = S}) when S == Sign ->
- true;
- ({_, dsa}) ->
- false;
- ({_, _} = Algos) ->
- is_acceptable_hash_sign(Algos, Sign, KeyExAlgo, SupportedHashSigns);
+ #'OTPCertificate'{tbsCertificate = TBSCert,
+ signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} =
+ TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
+
+ Sign = sign_algo(SignAlgo),
+ SubSing = sign_algo(SubjAlgo),
+
+ case lists:filter(fun({_, S} = Algos) when S == Sign ->
+ is_acceptable_hash_sign(Algos, Sign,
+ SubSing, KeyExAlgo, SupportedHashSigns);
(_) ->
false
end, HashSigns) of
@@ -613,6 +615,49 @@ select_hashsign(_, Cert, _, _, Version) ->
#'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
#'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
select_hashsign_algs(undefined, Algo, Version).
+%%--------------------------------------------------------------------
+-spec select_hashsign(#certificate_request{}, binary(),
+ [atom()], ssl_record:ssl_version()) ->
+ {atom(), atom()} | #alert{}.
+
+%%
+%% Description: Handles signature algorithms selection for certificate requests (client)
+%%--------------------------------------------------------------------
+select_hashsign(#certificate_request{}, undefined, _, {Major, Minor}) when Major >= 3 andalso Minor >= 3->
+ %% There client does not have a certificate and will send an empty reply, the server may fail
+ %% or accept the connection by its own preference. No signature algorihms needed as there is
+ %% no certificate to verify.
+ {undefined, undefined};
+
+select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSigns},
+ certificate_types = Types}, Cert, SupportedHashSigns,
+ {Major, Minor}) when Major >= 3 andalso Minor >= 3->
+ #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPCertificate'{tbsCertificate = TBSCert,
+ signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} =
+ TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
+
+ Sign = sign_algo(SignAlgo),
+ SubSign = sign_algo(SubjAlgo),
+
+ case is_acceptable_cert_type(SubSign, HashSigns, Types) andalso is_supported_sign(Sign, HashSigns) of
+ true ->
+ case lists:filter(fun({_, S} = Algos) when S == SubSign ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+ (_) ->
+ false
+ end, HashSigns) of
+ [] ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm);
+ [HashSign | _] ->
+ HashSign
+ end;
+ false ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)
+ end;
+select_hashsign(#certificate_request{}, Cert, _, Version) ->
+ select_hashsign(undefined, Cert, undefined, [], Version).
%%--------------------------------------------------------------------
-spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) ->
@@ -648,34 +693,34 @@ select_hashsign_algs(undefined, ?rsaEncryption, _) ->
select_hashsign_algs(undefined, ?'id-dsa', _) ->
{sha, dsa}.
+
%%--------------------------------------------------------------------
--spec master_secret(atom(), ssl_record:ssl_version(), #session{} | binary(), #connection_states{},
- client | server) -> {binary(), #connection_states{}} | #alert{}.
+-spec master_secret(ssl_record:ssl_version(), #session{} | binary(), ssl_record:connection_states(),
+ client | server) -> {binary(), ssl_record:connection_states()} | #alert{}.
%%
%% Description: Sets or calculates the master secret and calculate keys,
%% updating the pending connection states. The Mastersecret and the update
%% connection states are returned or an alert if the calculation fails.
%%-------------------------------------------------------------------
-master_secret(RecordCB, Version, #session{master_secret = Mastersecret},
+master_secret(Version, #session{master_secret = Mastersecret},
ConnectionStates, Role) ->
- ConnectionState =
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = ConnectionState#connection_state.security_parameters,
- try master_secret(RecordCB, Version, Mastersecret, SecParams,
+ try master_secret(Version, Mastersecret, SecParams,
ConnectionStates, Role)
catch
exit:_ ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, key_calculation_failure)
end;
-master_secret(RecordCB, Version, PremasterSecret, ConnectionStates, Role) ->
- ConnectionState =
+master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
+ #{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = ConnectionState#connection_state.security_parameters,
+
#security_parameters{prf_algorithm = PrfAlgo,
client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
- try master_secret(RecordCB, Version,
+ try master_secret(Version,
calc_master_secret(Version,PrfAlgo,PremasterSecret,
ClientRandom, ServerRandom),
SecParams, ConnectionStates, Role)
@@ -1125,8 +1170,9 @@ select_session(SuggestedSessionId, CipherSuites, HashSigns, Compressions, Port,
{resumed, Resumed}
end.
-supported_ecc({Major, Minor} = Version) when ((Major == 3) and (Minor >= 1)) orelse (Major > 3) ->
- Curves = tls_v1:ecc_curves(Version),
+%% Deprecated?
+supported_ecc({Major, Minor}) when ((Major == 3) and (Minor >= 1)) orelse (Major > 3) ->
+ Curves = tls_v1:ecc_curves(Minor),
#elliptic_curves{elliptic_curve_list = Curves};
supported_ecc(_) ->
#elliptic_curves{elliptic_curve_list = []}.
@@ -1143,11 +1189,13 @@ certificate_types(_, {N, M}) when N >= 3 andalso M >= 3 ->
end;
certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == rsa;
+ KeyExchange == dh_rsa;
KeyExchange == dhe_rsa;
KeyExchange == ecdhe_rsa ->
<<?BYTE(?RSA_SIGN)>>;
-certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dhe_dss;
+certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dh_dss;
+ KeyExchange == dhe_dss;
KeyExchange == srp_dss ->
<<?BYTE(?DSS_SIGN)>>;
@@ -1170,13 +1218,18 @@ certificate_authorities(CertDbHandle, CertDbRef) ->
end,
list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]).
-certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
+certificate_authorities_from_db(CertDbHandle, CertDbRef) when is_reference(CertDbRef) ->
ConnectionCerts = fun({{Ref, _, _}, Cert}, Acc) when Ref == CertDbRef ->
[Cert | Acc];
(_, Acc) ->
Acc
end,
- ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle).
+ ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle);
+certificate_authorities_from_db(_CertDbHandle, {extracted, CertDbData}) ->
+ %% Cache disabled, Ref contains data
+ lists:foldl(fun({decoded, {_Key,Cert}}, Acc) -> [Cert | Acc] end,
+ [], CertDbData).
+
%%-------------Extension handling --------------------------------
@@ -1256,35 +1309,67 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
end.
select_version(RecordCB, ClientVersion, Versions) ->
- ServerVersion = RecordCB:highest_protocol_version(Versions),
- RecordCB:lowest_protocol_version(ClientVersion, ServerVersion).
+ do_select_version(RecordCB, ClientVersion, Versions).
+
+do_select_version(_, ClientVersion, []) ->
+ ClientVersion;
+do_select_version(RecordCB, ClientVersion, [Version | Versions]) ->
+ case RecordCB:is_higher(Version, ClientVersion) of
+ true ->
+ %% Version too high for client - keep looking
+ do_select_version(RecordCB, ClientVersion, Versions);
+ false ->
+ %% Version ok for client - look for a higher
+ do_select_version(RecordCB, ClientVersion, Versions, Version)
+ end.
+%%
+do_select_version(_, _, [], GoodVersion) ->
+ GoodVersion;
+do_select_version(
+ RecordCB, ClientVersion, [Version | Versions], GoodVersion) ->
+ BetterVersion =
+ case RecordCB:is_higher(Version, ClientVersion) of
+ true ->
+ %% Version too high for client
+ GoodVersion;
+ false ->
+ %% Version ok for client
+ case RecordCB:is_higher(Version, GoodVersion) of
+ true ->
+ %% Use higher version
+ Version;
+ false ->
+ GoodVersion
+ end
+ end,
+ do_select_version(RecordCB, ClientVersion, Versions, BetterVersion).
renegotiation_info(_, client, _, false) ->
#renegotiation_info{renegotiated_connection = undefined};
renegotiation_info(_RecordCB, server, ConnectionStates, false) ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- case CS#connection_state.secure_renegotiation of
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case maps:get(secure_renegotiation, ConnectionState) of
true ->
#renegotiation_info{renegotiated_connection = ?byte(0)};
false ->
#renegotiation_info{renegotiated_connection = undefined}
end;
renegotiation_info(_RecordCB, client, ConnectionStates, true) ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- case CS#connection_state.secure_renegotiation of
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case maps:get(secure_renegotiation, ConnectionState) of
true ->
- Data = CS#connection_state.client_verify_data,
+ Data = maps:get(client_verify_data, ConnectionState),
#renegotiation_info{renegotiated_connection = Data};
false ->
#renegotiation_info{renegotiated_connection = undefined}
end;
renegotiation_info(_RecordCB, server, ConnectionStates, true) ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- case CS#connection_state.secure_renegotiation of
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case maps:get(secure_renegotiation, ConnectionState) of
true ->
- CData = CS#connection_state.client_verify_data,
- SData =CS#connection_state.server_verify_data,
+ CData = maps:get(client_verify_data, ConnectionState),
+ SData = maps:get(server_verify_data, ConnectionState),
#renegotiation_info{renegotiated_connection = <<CData/binary, SData/binary>>};
false ->
#renegotiation_info{renegotiated_connection = undefined}
@@ -1307,9 +1392,9 @@ handle_renegotiation_info(_RecordCB, _, undefined, ConnectionStates, false, _, _
handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
ConnectionStates, true, _, _) ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- CData = CS#connection_state.client_verify_data,
- SData = CS#connection_state.server_verify_data,
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ CData = maps:get(client_verify_data, ConnectionState),
+ SData = maps:get(server_verify_data, ConnectionState),
case <<CData/binary, SData/binary>> == ClientServerVerify of
true ->
{ok, ConnectionStates};
@@ -1323,8 +1408,8 @@ handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_co
true ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv});
false ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- Data = CS#connection_state.client_verify_data,
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ Data = maps:get(client_verify_data, ConnectionState),
case Data == ClientVerify of
true ->
{ok, ConnectionStates};
@@ -1345,8 +1430,8 @@ handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, S
end.
handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) ->
- CS = ssl_record:current_connection_state(ConnectionStates, read),
- case {SecureRenegotation, CS#connection_state.secure_renegotiation} of
+ ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
+ case {SecureRenegotation, maps:get(secure_renegotiation, ConnectionState)} of
{_, true} ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure);
{true, false} ->
@@ -1371,12 +1456,12 @@ srp_user(#ssl_options{srp_identity = {UserName, _}}) ->
srp_user(_) ->
undefined.
-client_ecc_extensions(Module, Version) ->
+client_ecc_extensions(SupportedECCs) ->
CryptoSupport = proplists:get_value(public_keys, crypto:supports()),
case proplists:get_bool(ecdh, CryptoSupport) of
true ->
EcPointFormats = #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]},
- EllipticCurves = #elliptic_curves{elliptic_curve_list = Module:ecc_curves(Version)},
+ EllipticCurves = SupportedECCs,
{EcPointFormats, EllipticCurves};
_ ->
{undefined, undefined}
@@ -1410,22 +1495,34 @@ advertises_ec_ciphers([{ecdh_anon, _,_,_} | _]) ->
true;
advertises_ec_ciphers([_| Rest]) ->
advertises_ec_ciphers(Rest).
-select_curve(#elliptic_curves{elliptic_curve_list = ClientCurves},
- #elliptic_curves{elliptic_curve_list = ServerCurves}) ->
- select_curve(ClientCurves, ServerCurves);
-select_curve(undefined, _) ->
+
+select_curve(Client, Server) ->
+ select_curve(Client, Server, false).
+
+select_curve(#elliptic_curves{elliptic_curve_list = ClientCurves},
+ #elliptic_curves{elliptic_curve_list = ServerCurves},
+ ServerOrder) ->
+ case ServerOrder of
+ false ->
+ select_shared_curve(ClientCurves, ServerCurves);
+ true ->
+ select_shared_curve(ServerCurves, ClientCurves)
+ end;
+select_curve(undefined, _, _) ->
%% Client did not send ECC extension use default curve if
%% ECC cipher is negotiated
- {namedCurve, ?secp256r1};
-select_curve(_, []) ->
+ {namedCurve, ?secp256r1}.
+
+select_shared_curve([], _) ->
no_curve;
-select_curve(Curves, [Curve| Rest]) ->
+select_shared_curve([Curve | Rest], Curves) ->
case lists:member(Curve, Curves) of
true ->
{namedCurve, Curve};
false ->
- select_curve(Curves, Rest)
+ select_shared_curve(Rest, Curves)
end.
+
%% RFC 6066, Section 3: Currently, the only server names supported are
%% DNS hostnames
sni(_, disable) ->
@@ -1443,7 +1540,8 @@ sni1(Hostname) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-validation_fun_and_state({Fun, UserState0}, Role, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle) ->
+validation_fun_and_state({Fun, UserState0}, Role, CertDbHandle, CertDbRef,
+ CRLCheck, CRLDbHandle, CertPath) ->
{fun(OtpCert, {extension, _} = Extension, {SslState, UserState}) ->
case ssl_certificate:validate(OtpCert,
Extension,
@@ -1452,22 +1550,25 @@ validation_fun_and_state({Fun, UserState0}, Role, CertDbHandle, CertDbRef, CRLC
{valid, {NewSslState, UserState}};
{fail, Reason} ->
apply_user_fun(Fun, OtpCert, Reason, UserState,
- SslState);
+ SslState, CertPath);
{unknown, _} ->
apply_user_fun(Fun, OtpCert,
- Extension, UserState, SslState)
+ Extension, UserState, SslState, CertPath)
end;
(OtpCert, VerifyResult, {SslState, UserState}) ->
apply_user_fun(Fun, OtpCert, VerifyResult, UserState,
- SslState)
+ SslState, CertPath)
end, {{Role, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle}, UserState0}};
-validation_fun_and_state(undefined, Role, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle) ->
+validation_fun_and_state(undefined, Role, CertDbHandle, CertDbRef,
+ CRLCheck, CRLDbHandle, CertPath) ->
{fun(OtpCert, {extension, _} = Extension, SslState) ->
ssl_certificate:validate(OtpCert,
Extension,
SslState);
- (OtpCert, VerifyResult, SslState) when (VerifyResult == valid) or (VerifyResult == valid_peer) ->
- case crl_check(OtpCert, CRLCheck, CertDbHandle, CertDbRef, CRLDbHandle, VerifyResult) of
+ (OtpCert, VerifyResult, SslState) when (VerifyResult == valid) or
+ (VerifyResult == valid_peer) ->
+ case crl_check(OtpCert, CRLCheck, CertDbHandle, CertDbRef,
+ CRLDbHandle, VerifyResult, CertPath) of
valid ->
{VerifyResult, SslState};
Reason ->
@@ -1480,20 +1581,21 @@ validation_fun_and_state(undefined, Role, CertDbHandle, CertDbRef, CRLCheck, CRL
end, {Role, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle}}.
apply_user_fun(Fun, OtpCert, VerifyResult, UserState0,
- {_, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle} = SslState) when
+ {_, CertDbHandle, CertDbRef, CRLCheck, CRLDbHandle} = SslState, CertPath) when
(VerifyResult == valid) or (VerifyResult == valid_peer) ->
case Fun(OtpCert, VerifyResult, UserState0) of
{Valid, UserState} when (Valid == valid) or (Valid == valid_peer) ->
- case crl_check(OtpCert, CRLCheck, CertDbHandle, CertDbRef, CRLDbHandle, VerifyResult) of
+ case crl_check(OtpCert, CRLCheck, CertDbHandle, CertDbRef,
+ CRLDbHandle, VerifyResult, CertPath) of
valid ->
{Valid, {SslState, UserState}};
Result ->
- apply_user_fun(Fun, OtpCert, Result, UserState, SslState)
+ apply_user_fun(Fun, OtpCert, Result, UserState, SslState, CertPath)
end;
{fail, _} = Fail ->
Fail
end;
-apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
+apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState, _CertPath) ->
case Fun(OtpCert, ExtensionOrError, UserState0) of
{Valid, UserState} when (Valid == valid) or (Valid == valid_peer)->
{Valid, {SslState, UserState}};
@@ -1564,7 +1666,7 @@ calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) ->
calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) ->
tls_v1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)).
-master_secret(_RecordCB, Version, MasterSecret,
+master_secret(Version, MasterSecret,
#security_parameters{
bulk_cipher_algorithm = BCA,
client_random = ClientRandom,
@@ -1647,18 +1749,16 @@ hello_pending_connection_states(_RecordCB, Role, Version, CipherSuite, Random, C
NewWriteSecParams,
ConnectionStates).
-hello_security_parameters(client, Version, ConnectionState, CipherSuite, Random,
+hello_security_parameters(client, Version, #{security_parameters := SecParams}, CipherSuite, Random,
Compression) ->
- SecParams = ConnectionState#connection_state.security_parameters,
NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
NewSecParams#security_parameters{
server_random = Random,
compression_algorithm = Compression
};
-hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random,
+hello_security_parameters(server, Version, #{security_parameters := SecParams}, CipherSuite, Random,
Compression) ->
- SecParams = ConnectionState#connection_state.security_parameters,
NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams),
NewSecParams#security_parameters{
client_random = Random,
@@ -2091,13 +2191,14 @@ handle_psk_identity(_PSKIdentity, LookupFun)
handle_psk_identity(PSKIdentity, {Fun, UserState}) ->
Fun(psk, PSKIdentity, UserState).
-crl_check(_, false, _,_,_, _) ->
+crl_check(_, false, _,_,_, _, _) ->
valid;
-crl_check(_, peer, _, _,_, valid) -> %% Do not check CAs with this option.
+crl_check(_, peer, _, _,_, valid, _) -> %% Do not check CAs with this option.
valid;
-crl_check(OtpCert, Check, CertDbHandle, CertDbRef, {Callback, CRLDbHandle}, _) ->
+crl_check(OtpCert, Check, CertDbHandle, CertDbRef, {Callback, CRLDbHandle}, _, CertPath) ->
Options = [{issuer_fun, {fun(_DP, CRL, Issuer, DBInfo) ->
- ssl_crl:trusted_cert_and_path(CRL, Issuer, DBInfo)
+ ssl_crl:trusted_cert_and_path(CRL, Issuer, {CertPath,
+ DBInfo})
end, {CertDbHandle, CertDbRef}}},
{update_crl, fun(DP, CRL) -> Callback:fresh_crl(DP, CRL) end}
],
@@ -2133,7 +2234,8 @@ dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) ->
no_dps;
DistPoints ->
Issuer = OtpCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.issuer,
- distpoints_lookup(DistPoints, Issuer, Callback, CRLDbHandle)
+ CRLs = distpoints_lookup(DistPoints, Issuer, Callback, CRLDbHandle),
+ dps_and_crls(DistPoints, CRLs, [])
end;
dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) ->
@@ -2146,7 +2248,13 @@ dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) ->
end, GenNames),
[{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs].
-distpoints_lookup([], _, _, _) ->
+dps_and_crls([], _, Acc) ->
+ Acc;
+dps_and_crls([DP | Rest], CRLs, Acc) ->
+ DpCRL = [{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs],
+ dps_and_crls(Rest, CRLs, DpCRL ++ Acc).
+
+distpoints_lookup([],_, _, _) ->
[];
distpoints_lookup([DistPoint | Rest], Issuer, Callback, CRLDbHandle) ->
Result =
@@ -2161,30 +2269,76 @@ distpoints_lookup([DistPoint | Rest], Issuer, Callback, CRLDbHandle) ->
not_available ->
distpoints_lookup(Rest, Issuer, Callback, CRLDbHandle);
CRLs ->
- [{DistPoint, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs]
+ CRLs
end.
-cert_sign(?rsaEncryption) ->
+sign_algo(?rsaEncryption) ->
rsa;
-cert_sign(?'id-ecPublicKey') ->
+sign_algo(?'id-ecPublicKey') ->
ecdsa;
-cert_sign(?'id-dsa') ->
+sign_algo(?'id-dsa') ->
dsa;
-cert_sign(Alg) ->
+sign_algo(Alg) ->
{_, Sign} =public_key:pkix_sign_types(Alg),
Sign.
-is_acceptable_hash_sign({_, Sign} = Algos, Sign, _, SupportedHashSigns) ->
- is_acceptable_hash_sign(Algos, SupportedHashSigns);
-is_acceptable_hash_sign(Algos,_, KeyExAlgo, SupportedHashSigns) when KeyExAlgo == dh_ecdsa;
- KeyExAlgo == ecdh_rsa;
- KeyExAlgo == ecdh_ecdsa ->
+is_acceptable_hash_sign(Algos, _, _, KeyExAlgo, SupportedHashSigns) when
+ KeyExAlgo == dh_dss;
+ KeyExAlgo == dh_rsa;
+ KeyExAlgo == dh_ecdsa ->
+ %% dh_* could be called only dh in TLS-1.2
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign(Algos, rsa, ecdsa, ecdh_rsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, dhe_rsa, SupportedHashSigns) ->
is_acceptable_hash_sign(Algos, SupportedHashSigns);
-is_acceptable_hash_sign(_,_,_,_) ->
- false.
+is_acceptable_hash_sign({_, rsa} = Algos, rsa, rsa, ecdhe_rsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, rsa} = Algos, rsa, rsa, rsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, srp_rsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, rsa_psk, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, dsa} = Algos, dsa, _, dhe_dss, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, dsa} = Algos, dsa, _, srp_dss, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, ecdsa} = Algos, ecdsa, _, dhe_ecdsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, ecdsa} = Algos, ecdsa, ecdsa, ecdhe_ecdsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign(_, _, _, KeyExAlgo, _) when
+ KeyExAlgo == psk;
+ KeyExAlgo == dhe_psk;
+ KeyExAlgo == srp_anon;
+ KeyExAlgo == dh_anon;
+ KeyExAlgo == ecdhe_anon
+ ->
+ true;
+is_acceptable_hash_sign(_,_, _,_,_) ->
+ false.
+
is_acceptable_hash_sign(Algos, SupportedHashSigns) ->
lists:member(Algos, SupportedHashSigns).
+is_acceptable_cert_type(Sign, _HashSigns, Types) ->
+ lists:member(sign_type(Sign), binary_to_list(Types)).
+
+is_supported_sign(Sign, HashSigns) ->
+ [] =/= lists:dropwhile(fun({_, S}) when S =/= Sign ->
+ true;
+ (_)->
+ false
+ end, HashSigns).
+sign_type(rsa) ->
+ ?RSA_SIGN;
+sign_type(dsa) ->
+ ?DSS_SIGN;
+sign_type(ecdsa) ->
+ ?ECDSA_SIGN.
+
+
bad_key(#'DSAPrivateKey'{}) ->
unacceptable_dsa_key;
bad_key(#'RSAPrivateKey'{}) ->
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index fde92035a2..324b7dbde3 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -80,6 +80,9 @@
-define(CLIENT_KEY_EXCHANGE, 16).
-define(FINISHED, 20).
+-define(MAX_UNIT24, 8388607).
+-define(DEFAULT_MAX_HANDSHAKE_SIZE, (256*1024)).
+
-record(random, {
gmt_unix_time, % uint32
random_bytes % opaque random_bytes[28]
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index c19c1787ff..ac212a56d8 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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.
@@ -76,7 +76,7 @@
-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1]).
-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1]).
-define(ALL_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]).
--define(MIN_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]).
+-define(MIN_DATAGRAM_SUPPORTED_VERSIONS, [dtlsv1]).
-define('24H_in_msec', 86400000).
-define('24H_in_sec', 86400).
@@ -140,7 +140,10 @@
crl_check :: boolean() | peer | best_effort,
crl_cache,
signature_algs,
- v2_hello_compatible :: boolean()
+ eccs,
+ honor_ecc_order :: boolean(),
+ v2_hello_compatible :: boolean(),
+ max_handshake_size :: integer()
}).
-record(socket_options,
@@ -154,7 +157,8 @@
-record(config, {ssl, %% SSL parameters
inet_user, %% User set inet options
- emulated, %% Emulated option list or "inherit_tracker" pid
+ emulated, %% Emulated option list or "inherit_tracker" pid
+ udp_handler,
inet_ssl, %% inet options for internal ssl socket
transport_info, %% Callback info
connection_cb
diff --git a/lib/ssl/src/ssl_listen_tracker_sup.erl b/lib/ssl/src/ssl_listen_tracker_sup.erl
index 7f685a2ead..f7e97bcb76 100644
--- a/lib/ssl/src/ssl_listen_tracker_sup.erl
+++ b/lib/ssl/src/ssl_listen_tracker_sup.erl
@@ -57,10 +57,10 @@ init(_O) ->
MaxT = 3600,
Name = undefined, % As simple_one_for_one is used.
- StartFunc = {ssl_socket, start_link, []},
+ StartFunc = {tls_socket, start_link, []},
Restart = temporary, % E.g. should not be restarted
Shutdown = 4000,
- Modules = [ssl_socket],
+ Modules = [tls_socket],
Type = worker,
ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index c7dcbaabe9..ca9aaf4660 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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,10 +32,9 @@
new_session_id/1, clean_cert_db/2,
register_session/2, register_session/3, invalidate_session/2,
insert_crls/2, insert_crls/3, delete_crls/1, delete_crls/2,
- invalidate_session/3, invalidate_pem/1, clear_pem_cache/0, manager_name/1]).
+ invalidate_session/3, name/1]).
-% Spawn export
--export([init_session_validator/1, init_pem_cache_validator/1]).
+-export([init_session_validator/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -52,9 +51,7 @@
session_lifetime :: integer(),
certificate_db :: db_handle(),
session_validation_timer :: reference(),
- last_delay_timer = {undefined, undefined},%% Keep for testing purposes
- last_pem_check :: erlang:timestamp(),
- clear_pem_cache :: integer(),
+ last_delay_timer = {undefined, undefined},%% Keep for testing purposes
session_cache_client_max :: integer(),
session_cache_server_max :: integer(),
session_server_invalidator :: undefined | pid(),
@@ -63,7 +60,6 @@
-define(GEN_UNIQUE_ID_MAX_TRIES, 10).
-define(SESSION_VALIDATION_INTERVAL, 60000).
--define(CLEAR_PEM_CACHE, 120000).
-define(CLEAN_SESSION_DB, 60000).
-define(CLEAN_CERT_DB, 500).
-define(DEFAULT_MAX_SESSION_CACHE, 1000).
@@ -74,14 +70,14 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec manager_name(normal | dist) -> atom().
+-spec name(normal | dist) -> atom().
%%
%% Description: Returns the registered name of the ssl manager process
%% in the operation modes 'normal' and 'dist'.
%%--------------------------------------------------------------------
-manager_name(normal) ->
+name(normal) ->
?MODULE;
-manager_name(dist) ->
+name(dist) ->
list_to_atom(atom_to_list(?MODULE) ++ "dist").
%%--------------------------------------------------------------------
@@ -91,9 +87,10 @@ manager_name(dist) ->
%% and certificate caching.
%%--------------------------------------------------------------------
start_link(Opts) ->
- DistMangerName = manager_name(normal),
- gen_server:start_link({local, DistMangerName},
- ?MODULE, [DistMangerName, Opts], []).
+ MangerName = name(normal),
+ CacheName = ssl_pem_cache:name(normal),
+ gen_server:start_link({local, MangerName},
+ ?MODULE, [MangerName, CacheName, Opts], []).
%%--------------------------------------------------------------------
-spec start_link_dist(list()) -> {ok, pid()} | ignore | {error, term()}.
@@ -102,24 +99,21 @@ start_link(Opts) ->
%% be used by the erlang distribution. Note disables soft upgrade!
%%--------------------------------------------------------------------
start_link_dist(Opts) ->
- DistMangerName = manager_name(dist),
+ DistMangerName = name(dist),
+ DistCacheName = ssl_pem_cache:name(dist),
gen_server:start_link({local, DistMangerName},
- ?MODULE, [DistMangerName, Opts], []).
+ ?MODULE, [DistMangerName, DistCacheName, Opts], []).
%%--------------------------------------------------------------------
-spec connection_init(binary()| {der, list()}, client | server,
{Cb :: atom(), Handle:: term()}) ->
- {ok, certdb_ref(), db_handle(), db_handle(),
- db_handle(), db_handle(), CRLInfo::term()}.
+ {ok, map()}.
%%
%% Description: Do necessary initializations for a new connection.
%%--------------------------------------------------------------------
connection_init({der, _} = Trustedcerts, Role, CRLCache) ->
- call({connection_init, Trustedcerts, Role, CRLCache});
-
-connection_init(<<>> = Trustedcerts, Role, CRLCache) ->
- call({connection_init, Trustedcerts, Role, CRLCache});
-
+ {ok, Extracted} = ssl_pkix_db:extract_trusted_certs(Trustedcerts),
+ call({connection_init, Extracted, Role, CRLCache});
connection_init(Trustedcerts, Role, CRLCache) ->
call({connection_init, Trustedcerts, Role, CRLCache}).
@@ -129,26 +123,14 @@ connection_init(Trustedcerts, Role, CRLCache) ->
%% Description: Cache a pem file and return its content.
%%--------------------------------------------------------------------
cache_pem_file(File, DbHandle) ->
- case ssl_pkix_db:lookup_cached_pem(DbHandle, File) of
- [{Content,_}] ->
- {ok, Content};
- [Content] ->
+ case ssl_pkix_db:lookup(File, DbHandle) of
+ [Content] ->
{ok, Content};
undefined ->
- call({cache_pem, File})
+ ssl_pem_cache:insert(File)
end.
%%--------------------------------------------------------------------
--spec clear_pem_cache() -> ok.
-%%
-%% Description: Clear the PEM cache
-%%--------------------------------------------------------------------
-clear_pem_cache() ->
- %% Not supported for distribution at the moement, should it be?
- put(ssl_manager, manager_name(normal)),
- call(unconditionally_clear_pem_cache).
-
-%%--------------------------------------------------------------------
-spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) ->
undefined |
{ok, {der_cert(), #'OTPCertificate'{}}}.
@@ -205,26 +187,22 @@ invalidate_session(Port, Session) ->
load_mitigation(),
cast({invalidate_session, Port, Session}).
--spec invalidate_pem(File::binary()) -> ok.
-invalidate_pem(File) ->
- cast({invalidate_pem, File}).
-
insert_crls(Path, CRLs)->
insert_crls(Path, CRLs, normal).
insert_crls(?NO_DIST_POINT_PATH = Path, CRLs, ManagerType)->
- put(ssl_manager, manager_name(ManagerType)),
+ put(ssl_manager, name(ManagerType)),
cast({insert_crls, Path, CRLs});
insert_crls(Path, CRLs, ManagerType)->
- put(ssl_manager, manager_name(ManagerType)),
+ put(ssl_manager, name(ManagerType)),
call({insert_crls, Path, CRLs}).
delete_crls(Path)->
delete_crls(Path, normal).
delete_crls(?NO_DIST_POINT_PATH = Path, ManagerType)->
- put(ssl_manager, manager_name(ManagerType)),
+ put(ssl_manager, name(ManagerType)),
cast({delete_crls, Path});
delete_crls(Path, ManagerType)->
- put(ssl_manager, manager_name(ManagerType)),
+ put(ssl_manager, name(ManagerType)),
call({delete_crls, Path}).
%%====================================================================
@@ -238,13 +216,14 @@ delete_crls(Path, ManagerType)->
%%
%% Description: Initiates the server
%%--------------------------------------------------------------------
-init([Name, Opts]) ->
- put(ssl_manager, Name),
+init([ManagerName, PemCacheName, Opts]) ->
+ put(ssl_manager, ManagerName),
+ put(ssl_pem_cache, PemCacheName),
process_flag(trap_exit, true),
CacheCb = proplists:get_value(session_cb, Opts, ssl_session_cache),
SessionLifeTime =
proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'),
- CertDb = ssl_pkix_db:create(),
+ CertDb = ssl_pkix_db:create(PemCacheName),
ClientSessionCache =
CacheCb:init([{role, client} |
proplists:get_value(session_cb_init_args, Opts, [])]),
@@ -253,16 +232,12 @@ init([Name, Opts]) ->
proplists:get_value(session_cb_init_args, Opts, [])]),
Timer = erlang:send_after(SessionLifeTime * 1000 + 5000,
self(), validate_sessions),
- Interval = pem_check_interval(),
- erlang:send_after(Interval, self(), clear_pem_cache),
{ok, #state{certificate_db = CertDb,
session_cache_client = ClientSessionCache,
session_cache_server = ServerSessionCache,
session_cache_cb = CacheCb,
session_lifetime = SessionLifeTime,
session_validation_timer = Timer,
- last_pem_check = os:timestamp(),
- clear_pem_cache = Interval,
session_cache_client_max =
max_session_cache_size(session_cache_client_max),
session_cache_server_max =
@@ -285,18 +260,25 @@ init([Name, Opts]) ->
handle_call({{connection_init, <<>>, Role, {CRLCb, UserCRLDb}}, _Pid}, _From,
#state{certificate_db = [CertDb, FileRefDb, PemChace | _] = Db} = State) ->
Ref = make_ref(),
- Result = {ok, Ref, CertDb, FileRefDb, PemChace,
- session_cache(Role, State), {CRLCb, crl_db_info(Db, UserCRLDb)}},
- {reply, Result, State#state{certificate_db = Db}};
+ {reply, {ok, #{cert_db_ref => Ref,
+ cert_db_handle => CertDb,
+ fileref_db_handle => FileRefDb,
+ pem_cache => PemChace,
+ session_cache => session_cache(Role, State),
+ crl_db_info => {CRLCb, crl_db_info(Db, UserCRLDb)}}}, State};
handle_call({{connection_init, Trustedcerts, Role, {CRLCb, UserCRLDb}}, Pid}, _From,
#state{certificate_db = [CertDb, FileRefDb, PemChace | _] = Db} = State) ->
case add_trusted_certs(Pid, Trustedcerts, Db) of
{ok, Ref} ->
- {reply, {ok, Ref, CertDb, FileRefDb, PemChace, session_cache(Role, State),
- {CRLCb, crl_db_info(Db, UserCRLDb)}}, State};
- {error, _} = Error ->
- {reply, Error, State}
+ {reply, {ok, #{cert_db_ref => Ref,
+ cert_db_handle => CertDb,
+ fileref_db_handle => FileRefDb,
+ pem_cache => PemChace,
+ session_cache => session_cache(Role, State),
+ crl_db_info => {CRLCb, crl_db_info(Db, UserCRLDb)}}}, State};
+ {error, _} = Error ->
+ {reply, Error, State}
end;
handle_call({{insert_crls, Path, CRLs}, _}, _From,
@@ -313,21 +295,7 @@ handle_call({{new_session_id, Port}, _},
_, #state{session_cache_cb = CacheCb,
session_cache_server = Cache} = State) ->
Id = new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb),
- {reply, Id, State};
-
-handle_call({{cache_pem,File}, _Pid}, _,
- #state{certificate_db = Db} = State) ->
- try ssl_pkix_db:cache_pem_file(File, Db) of
- Result ->
- {reply, Result, State}
- catch
- _:Reason ->
- {reply, {error, Reason}, State}
- end;
-handle_call({unconditionally_clear_pem_cache, _},_,
- #state{certificate_db = [_,_,PemChace | _]} = State) ->
- ssl_pkix_db:clear(PemChace),
- {reply, ok, State}.
+ {reply, Id, State}.
%%--------------------------------------------------------------------
-spec handle_cast(msg(), #state{}) -> {noreply, #state{}}.
@@ -365,11 +333,6 @@ handle_cast({insert_crls, Path, CRLs},
handle_cast({delete_crls, CRLsOrPath},
#state{certificate_db = Db} = State) ->
ssl_pkix_db:remove_crls(Db, CRLsOrPath),
- {noreply, State};
-
-handle_cast({invalidate_pem, File},
- #state{certificate_db = [_, _, PemCache | _]} = State) ->
- ssl_pkix_db:remove(File, PemCache),
{noreply, State}.
%%--------------------------------------------------------------------
@@ -401,22 +364,14 @@ handle_info({delayed_clean_session, Key, Cache}, #state{session_cache_cb = Cache
CacheCb:delete(Cache, Key),
{noreply, State};
-handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace | _],
- clear_pem_cache = Interval,
- last_pem_check = CheckPoint} = State) ->
- NewCheckPoint = os:timestamp(),
- start_pem_cache_validator(PemChace, CheckPoint),
- erlang:send_after(Interval, self(), clear_pem_cache),
- {noreply, State#state{last_pem_check = NewCheckPoint}};
-
handle_info({clean_cert_db, Ref, File},
- #state{certificate_db = [CertDb,RefDb, PemCache | _]} = State) ->
+ #state{certificate_db = [CertDb, {RefDb, FileMapDb} | _]} = State) ->
case ssl_pkix_db:lookup(Ref, RefDb) of
undefined -> %% Alredy cleaned
ok;
_ ->
- clean_cert_db(Ref, CertDb, RefDb, PemCache, File)
+ clean_cert_db(Ref, CertDb, RefDb, FileMapDb, File)
end,
{noreply, State};
@@ -569,16 +524,11 @@ new_id(Port, Tries, Cache, CacheCb) ->
new_id(Port, Tries - 1, Cache, CacheCb)
end.
-clean_cert_db(Ref, CertDb, RefDb, PemCache, File) ->
+clean_cert_db(Ref, CertDb, RefDb, FileMapDb, File) ->
case ssl_pkix_db:ref_count(Ref, RefDb, 0) of
0 ->
- case ssl_pkix_db:lookup_cached_pem(PemCache, File) of
- [{Content, Ref}] ->
- ssl_pkix_db:insert(File, Content, PemCache);
- _ ->
- ok
- end,
ssl_pkix_db:remove(Ref, RefDb),
+ ssl_pkix_db:remove(File, FileMapDb),
ssl_pkix_db:remove_trusted_certs(Ref, CertDb);
_ ->
ok
@@ -662,42 +612,6 @@ exists_equivalent(#session{
exists_equivalent(Session, [ _ | Rest]) ->
exists_equivalent(Session, Rest).
-start_pem_cache_validator(PemCache, CheckPoint) ->
- spawn_link(?MODULE, init_pem_cache_validator,
- [[get(ssl_manager), PemCache, CheckPoint]]).
-
-init_pem_cache_validator([SslManagerName, PemCache, CheckPoint]) ->
- put(ssl_manager, SslManagerName),
- ssl_pkix_db:foldl(fun pem_cache_validate/2,
- CheckPoint, PemCache).
-
-pem_cache_validate({File, _}, CheckPoint) ->
- case file:read_file_info(File, []) of
- {ok, #file_info{mtime = Time}} ->
- case is_before_checkpoint(Time, CheckPoint) of
- true ->
- ok;
- false ->
- invalidate_pem(File)
- end;
- _ ->
- invalidate_pem(File)
- end,
- CheckPoint.
-
-pem_check_interval() ->
- case application:get_env(ssl, ssl_pem_cache_clean) of
- {ok, Interval} when is_integer(Interval) ->
- Interval;
- _ ->
- ?CLEAR_PEM_CACHE
- end.
-
-is_before_checkpoint(Time, CheckPoint) ->
- calendar:datetime_to_gregorian_seconds(
- calendar:now_to_datetime(CheckPoint)) -
- calendar:datetime_to_gregorian_seconds(Time) > 0.
-
add_trusted_certs(Pid, Trustedcerts, Db) ->
try
ssl_pkix_db:add_trusted_certs(Pid, Trustedcerts, Db)
diff --git a/lib/ssl/src/ssl_pem_cache.erl b/lib/ssl/src/ssl_pem_cache.erl
new file mode 100644
index 0000000000..6cc0729208
--- /dev/null
+++ b/lib/ssl/src/ssl_pem_cache.erl
@@ -0,0 +1,266 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 20016-2017. 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%
+%%
+
+%%----------------------------------------------------------------------
+%% Purpose: Manages ssl sessions and trusted certifacates
+%%----------------------------------------------------------------------
+
+-module(ssl_pem_cache).
+-behaviour(gen_server).
+
+%% Internal application API
+-export([start_link/1,
+ start_link_dist/1,
+ name/1,
+ insert/1,
+ clear/0]).
+
+% Spawn export
+-export([init_pem_cache_validator/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-include("ssl_handshake.hrl").
+-include("ssl_internal.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-record(state, {
+ pem_cache,
+ last_pem_check :: erlang:timestamp(),
+ clear :: integer()
+ }).
+
+-define(CLEAR_PEM_CACHE, 120000).
+-define(DEFAULT_MAX_SESSION_CACHE, 1000).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec name(normal | dist) -> atom().
+%%
+%% Description: Returns the registered name of the ssl cache process
+%% in the operation modes 'normal' and 'dist'.
+%%--------------------------------------------------------------------
+name(normal) ->
+ ?MODULE;
+name(dist) ->
+ list_to_atom(atom_to_list(?MODULE) ++ "dist").
+
+%%--------------------------------------------------------------------
+-spec start_link(list()) -> {ok, pid()} | ignore | {error, term()}.
+%%
+%% Description: Starts the ssl pem cache handler
+%%--------------------------------------------------------------------
+start_link(_) ->
+ CacheName = name(normal),
+ gen_server:start_link({local, CacheName},
+ ?MODULE, [CacheName], []).
+
+%%--------------------------------------------------------------------
+-spec start_link_dist(list()) -> {ok, pid()} | ignore | {error, term()}.
+%%
+%% Description: Starts a special instance of the ssl manager to
+%% be used by the erlang distribution. Note disables soft upgrade!
+%%--------------------------------------------------------------------
+start_link_dist(_) ->
+ DistCacheName = name(dist),
+ gen_server:start_link({local, DistCacheName},
+ ?MODULE, [DistCacheName], []).
+
+
+%%--------------------------------------------------------------------
+-spec insert(binary()) -> {ok, term()} | {error, reason()}.
+%%
+%% Description: Cache a pem file and return its content.
+%%--------------------------------------------------------------------
+insert(File) ->
+ {ok, PemBin} = file:read_file(File),
+ Content = public_key:pem_decode(PemBin),
+ case bypass_cache() of
+ true ->
+ {ok, Content};
+ false ->
+ cast({cache_pem, File, Content}),
+ {ok, Content}
+ end.
+
+%%--------------------------------------------------------------------
+-spec clear() -> ok.
+%%
+%% Description: Clear the PEM cache
+%%--------------------------------------------------------------------
+clear() ->
+ %% Not supported for distribution at the moement, should it be?
+ put(ssl_pem_cache, name(normal)),
+ call(unconditionally_clear_pem_cache).
+
+-spec invalidate_pem(File::binary()) -> ok.
+invalidate_pem(File) ->
+ cast({invalidate_pem, File}).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec init(list()) -> {ok, #state{}}.
+%% Possible return values not used now.
+%% | {ok, #state{}, timeout()} | ignore | {stop, term()}.
+%%
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init([Name]) ->
+ put(ssl_pem_cache, Name),
+ process_flag(trap_exit, true),
+ PemCache = ssl_pkix_db:create_pem_cache(Name),
+ Interval = pem_check_interval(),
+ erlang:send_after(Interval, self(), clear_pem_cache),
+ {ok, #state{pem_cache = PemCache,
+ last_pem_check = os:timestamp(),
+ clear = Interval
+ }}.
+
+%%--------------------------------------------------------------------
+-spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}}.
+%% Possible return values not used now.
+%% {reply, reply(), #state{}, timeout()} |
+%% {noreply, #state{}} |
+%% {noreply, #state{}, timeout()} |
+%% {stop, reason(), reply(), #state{}} |
+%% {stop, reason(), #state{}}.
+%%
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call({unconditionally_clear_pem_cache, _},_,
+ #state{pem_cache = PemCache} = State) ->
+ ssl_pkix_db:clear(PemCache),
+ {reply, ok, State}.
+
+%%--------------------------------------------------------------------
+-spec handle_cast(msg(), #state{}) -> {noreply, #state{}}.
+%% Possible return values not used now.
+%% | {noreply, #state{}, timeout()} |
+%% {stop, reason(), #state{}}.
+%%
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast({cache_pem, File, Content}, #state{pem_cache = Db} = State) ->
+ ssl_pkix_db:insert(File, Content, Db),
+ {noreply, State};
+
+handle_cast({invalidate_pem, File}, #state{pem_cache = Db} = State) ->
+ ssl_pkix_db:remove(File, Db),
+ {noreply, State}.
+
+
+%%--------------------------------------------------------------------
+-spec handle_info(msg(), #state{}) -> {noreply, #state{}}.
+%% Possible return values not used now.
+%% |{noreply, #state{}, timeout()} |
+%% {stop, reason(), #state{}}.
+%%
+%% Description: Handling all non call/cast messages
+%%-------------------------------------------------------------------
+handle_info(clear_pem_cache, #state{pem_cache = PemCache,
+ clear = Interval,
+ last_pem_check = CheckPoint} = State) ->
+ NewCheckPoint = os:timestamp(),
+ start_pem_cache_validator(PemCache, CheckPoint),
+ erlang:send_after(Interval, self(), clear_pem_cache),
+ {noreply, State#state{last_pem_check = NewCheckPoint}};
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+-spec terminate(reason(), #state{}) -> ok.
+%%
+%% Description: This function is called by a gen_server 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_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, #state{}) ->
+ ok.
+
+%%--------------------------------------------------------------------
+-spec code_change(term(), #state{}, list()) -> {ok, #state{}}.
+%%
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+call(Msg) ->
+ gen_server:call(get(ssl_pem_cache), {Msg, self()}, infinity).
+
+cast(Msg) ->
+ gen_server:cast(get(ssl_pem_cache), Msg).
+
+start_pem_cache_validator(PemCache, CheckPoint) ->
+ spawn_link(?MODULE, init_pem_cache_validator,
+ [[get(ssl_pem_cache), PemCache, CheckPoint]]).
+
+init_pem_cache_validator([CacheName, PemCache, CheckPoint]) ->
+ put(ssl_pem_cache, CacheName),
+ ssl_pkix_db:foldl(fun pem_cache_validate/2,
+ CheckPoint, PemCache).
+
+pem_cache_validate({File, _}, CheckPoint) ->
+ case file:read_file_info(File, []) of
+ {ok, #file_info{mtime = Time}} ->
+ case is_before_checkpoint(Time, CheckPoint) of
+ true ->
+ ok;
+ false ->
+ invalidate_pem(File)
+ end;
+ _ ->
+ invalidate_pem(File)
+ end,
+ CheckPoint.
+
+is_before_checkpoint(Time, CheckPoint) ->
+ calendar:datetime_to_gregorian_seconds(
+ calendar:now_to_datetime(CheckPoint)) -
+ calendar:datetime_to_gregorian_seconds(Time) > 0.
+
+pem_check_interval() ->
+ case application:get_env(ssl, ssl_pem_cache_clean) of
+ {ok, Interval} when is_integer(Interval) ->
+ Interval;
+ _ ->
+ ?CLEAR_PEM_CACHE
+ end.
+
+bypass_cache() ->
+ case application:get_env(ssl, bypass_pem_cache) of
+ {ok, Bool} when is_boolean(Bool) ->
+ Bool;
+ _ ->
+ false
+ end.
diff --git a/lib/ssl/src/ssl_pkix_db.erl b/lib/ssl/src/ssl_pkix_db.erl
index b16903d7c7..b28636569d 100644
--- a/lib/ssl/src/ssl_pkix_db.erl
+++ b/lib/ssl/src/ssl_pkix_db.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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.
@@ -28,36 +28,43 @@
-include_lib("public_key/include/public_key.hrl").
-include_lib("kernel/include/file.hrl").
--export([create/0, add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3,
+-export([create/1, create_pem_cache/1,
+ add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3,
+ extract_trusted_certs/1,
remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1,
ref_count/3, lookup_trusted_cert/4, foldl/3, select_cert_by_issuer/2,
- lookup_cached_pem/2, cache_pem_file/2, cache_pem_file/3,
- lookup/2]).
+ decode_pem_file/1, lookup/2]).
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
--spec create() -> [db_handle(),...].
+-spec create(atom()) -> [db_handle(),...].
%%
%% Description: Creates a new certificate db.
%% Note: lookup_trusted_cert/4 may be called from any process but only
%% the process that called create may call the other functions.
%%--------------------------------------------------------------------
-create() ->
+create(PEMCacheName) ->
[%% Let connection process delete trusted certs
%% that can only belong to one connection. (Supplied directly
%% on DER format to ssl:connect/listen.)
ets:new(ssl_otp_cacertificate_db, [set, public]),
%% Let connection processes call ref_count/3 directly
- ets:new(ssl_otp_ca_file_ref, [set, public]),
- ets:new(ssl_otp_pem_cache, [set, protected]),
+ {ets:new(ssl_otp_ca_file_ref, [set, public]),
+ ets:new(ssl_otp_ca_ref_file_mapping, [set, protected])
+ },
+ %% Lookups in named table owned by ssl_pem_cache process
+ PEMCacheName,
%% Default cache
{ets:new(ssl_otp_crl_cache, [set, protected]),
ets:new(ssl_otp_crl_issuer_mapping, [bag, protected])}
].
+create_pem_cache(Name) ->
+ ets:new(Name, [named_table, set, protected]).
+
%%--------------------------------------------------------------------
-spec remove([db_handle()]) -> ok.
%%
@@ -69,6 +76,10 @@ remove(Dbs) ->
true = ets:delete(Db1);
(undefined) ->
ok;
+ (ssl_pem_cache) ->
+ ok;
+ (ssl_pem_cache_dist) ->
+ ok;
(Db) ->
true = ets:delete(Db)
end, Dbs).
@@ -82,19 +93,24 @@ remove(Dbs) ->
%% <SerialNumber, Issuer>. Ref is used as it is specified
%% for each connection which certificates are trusted.
%%--------------------------------------------------------------------
-lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
+lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) when is_reference(Ref) ->
case lookup({Ref, SerialNumber, Issuer}, DbHandle) of
undefined ->
undefined;
[Certs] ->
{ok, Certs}
+ end;
+lookup_trusted_cert(_DbHandle, {extracted,Certs}, SerialNumber, Issuer) ->
+ try
+ [throw(Cert)
+ || {decoded, {{_Ref,CertSerial,CertIssuer}, Cert}} <- Certs,
+ CertSerial =:= SerialNumber, CertIssuer =:= Issuer],
+ undefined
+ catch
+ Cert ->
+ {ok, Cert}
end.
-lookup_cached_pem([_, _, PemChache | _], File) ->
- lookup_cached_pem(PemChache, File);
-lookup_cached_pem(PemChache, File) ->
- lookup(File, PemChache).
-
%%--------------------------------------------------------------------
-spec add_trusted_certs(pid(), {erlang:timestamp(), string()} |
{der, list()}, [db_handle()]) -> {ok, [db_handle()]}.
@@ -103,43 +119,48 @@ lookup_cached_pem(PemChache, File) ->
%% runtime database. Returns Ref that should be handed to lookup_trusted_cert
%% together with the cert serialnumber and issuer.
%%--------------------------------------------------------------------
+add_trusted_certs(_Pid, {extracted, _} = Certs, _) ->
+ {ok, Certs};
+
add_trusted_certs(_Pid, {der, DerList}, [CertDb, _,_ | _]) ->
NewRef = make_ref(),
add_certs_from_der(DerList, NewRef, CertDb),
{ok, NewRef};
-add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache | _] = Db) ->
- case lookup_cached_pem(Db, File) of
- [{_Content, Ref}] ->
+add_trusted_certs(_Pid, File, [ _, {RefDb, FileMapDb} | _] = Db) ->
+ case lookup(File, FileMapDb) of
+ [Ref] ->
ref_count(Ref, RefDb, 1),
{ok, Ref};
- [Content] ->
- Ref = make_ref(),
- update_counter(Ref, 1, RefDb),
- insert(File, {Content, Ref}, PemChache),
- add_certs_from_pem(Content, Ref, CertsDb),
- {ok, Ref};
undefined ->
new_trusted_cert_entry(File, Db)
end.
-%%--------------------------------------------------------------------
-%%
-%% Description: Cache file as binary in DB
-%%--------------------------------------------------------------------
--spec cache_pem_file(binary(), [db_handle()]) -> {ok, term()}.
-cache_pem_file(File, [_CertsDb, _RefDb, PemChache | _]) ->
- {ok, PemBin} = file:read_file(File),
- Content = public_key:pem_decode(PemBin),
- insert(File, Content, PemChache),
- {ok, Content}.
+extract_trusted_certs({der, DerList}) ->
+ {ok, {extracted, certs_from_der(DerList)}};
+extract_trusted_certs(File) ->
+ case file:read_file(File) of
+ {ok, PemBin} ->
+ Content = public_key:pem_decode(PemBin),
+ DerList = [Cert || {'Certificate', Cert, not_encrypted} <- Content],
+ {ok, {extracted, certs_from_der(DerList)}};
+ Error ->
+ %% Have to simulate a failure happening in a server for
+ %% external handlers.
+ {error, {badmatch, Error}}
+ end.
--spec cache_pem_file(reference(), binary(), [db_handle()]) -> {ok, term()}.
-cache_pem_file(Ref, File, [_CertsDb, _RefDb, PemChache| _]) ->
- {ok, PemBin} = file:read_file(File),
- Content = public_key:pem_decode(PemBin),
- insert(File, {Content, Ref}, PemChache),
- {ok, Content}.
+-spec decode_pem_file(binary()) -> {ok, term()}.
+decode_pem_file(File) ->
+ case file:read_file(File) of
+ {ok, PemBin} ->
+ Content = public_key:pem_decode(PemBin),
+ {ok, Content};
+ Error ->
+ %% Have to simulate a failure happening in a server for
+ %% external handlers.
+ {error, {badmatch, Error}}
+ end.
%%--------------------------------------------------------------------
-spec remove_trusted_certs(reference(), db_handle()) -> ok.
@@ -203,6 +224,10 @@ select_cert_by_issuer(Cache, Issuer) ->
%%
%% Description: Updates a reference counter in a <Db>.
%%--------------------------------------------------------------------
+ref_count({extracted, _}, _Db, _N) ->
+ not_cached;
+ref_count(Key, {Db, _}, N) ->
+ ref_count(Key, Db, N);
ref_count(Key, Db, N) ->
ets:update_counter(Db,Key,N).
@@ -235,9 +260,9 @@ insert(Key, Data, Db) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-update_counter(Key, Count, Db) ->
- true = ets:insert(Db, {Key, Count}),
- ok.
+init_ref_db(Ref, File, {RefDb, FileMapDb}) ->
+ true = ets:insert(RefDb, {Ref, 1}),
+ true = ets:insert(FileMapDb, {File, Ref}).
remove_certs(Ref, CertsDb) ->
true = ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}),
@@ -248,29 +273,45 @@ add_certs_from_der(DerList, Ref, CertsDb) ->
[Add(Cert) || Cert <- DerList],
ok.
+certs_from_der(DerList) ->
+ Ref = make_ref(),
+ [Decoded || Cert <- DerList,
+ Decoded <- [decode_certs(Ref, Cert)],
+ Decoded =/= undefined].
+
add_certs_from_pem(PemEntries, Ref, CertsDb) ->
Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
[Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries],
ok.
add_certs(Cert, Ref, CertsDb) ->
+ try
+ {decoded, {Key, Val}} = decode_certs(Ref, Cert),
+ insert(Key, Val, CertsDb)
+ catch
+ error:_ ->
+ ok
+ end.
+
+decode_certs(Ref, Cert) ->
try ErlCert = public_key:pkix_decode_cert(Cert, otp),
TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber,
Issuer = public_key:pkix_normalize_name(
TBSCertificate#'OTPTBSCertificate'.issuer),
- insert({Ref, SerialNumber, Issuer}, {Cert,ErlCert}, CertsDb)
+ {decoded, {{Ref, SerialNumber, Issuer}, {Cert, ErlCert}}}
catch
error:_ ->
Report = io_lib:format("SSL WARNING: Ignoring a CA cert as "
"it could not be correctly decoded.~n", []),
- error_logger:info_report(Report)
+ error_logger:info_report(Report),
+ undefined
end.
-new_trusted_cert_entry(File, [CertsDb, RefDb, _ | _] = Db) ->
+new_trusted_cert_entry(File, [CertsDb, RefsDb, _ | _]) ->
Ref = make_ref(),
- update_counter(Ref, 1, RefDb),
- {ok, Content} = cache_pem_file(Ref, File, Db),
+ init_ref_db(Ref, File, RefsDb),
+ {ok, Content} = ssl_pem_cache:insert(File),
add_certs_from_pem(Content, Ref, CertsDb),
{ok, Ref}.
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 5bb1c92c2d..b10069c3cb 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -30,8 +30,7 @@
-include("ssl_alert.hrl").
%% Connection state handling
--export([init_connection_states/2,
- current_connection_state/2, pending_connection_state/2,
+-export([initial_security_params/1, current_connection_state/2, pending_connection_state/2,
activate_pending_connection_state/2,
set_security_params/3,
set_mac_secret/4,
@@ -39,11 +38,8 @@
set_pending_cipher_state/4,
set_renegotiation_flag/2,
set_client_verify_data/3,
- set_server_verify_data/3]).
-
-%% Encoding records
--export([encode_handshake/3, encode_alert_record/3,
- encode_change_cipher_spec/2, encode_data/3]).
+ set_server_verify_data/3,
+ empty_connection_state/2, initial_connection_state/2, record_protocol_role/1]).
%% Compression
-export([compress/3, uncompress/3, compressions/0]).
@@ -52,122 +48,95 @@
-export([cipher/4, decipher/4, is_correct_mac/2,
cipher_aead/4, decipher_aead/4]).
--export_type([ssl_version/0, ssl_atom_version/0]).
+%% Encoding
+-export([encode_plain_text/4]).
+
+-export_type([ssl_version/0, ssl_atom_version/0, connection_states/0, connection_state/0]).
-type ssl_version() :: {integer(), integer()}.
-type ssl_atom_version() :: tls_record:tls_atom_version().
-
+-type connection_states() :: term(). %% Map
+-type connection_state() :: term(). %% Map
%%====================================================================
%% Internal application API
%%====================================================================
+
%%--------------------------------------------------------------------
--spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled ) ->
- #connection_states{}.
-%%
-%% Description: Creates a connection_states record with appropriate
-%% values for the initial SSL connection setup.
-%%--------------------------------------------------------------------
-init_connection_states(Role, BeastMitigation) ->
- ConnectionEnd = record_protocol_role(Role),
- Current = initial_connection_state(ConnectionEnd, BeastMitigation),
- Pending = empty_connection_state(ConnectionEnd, BeastMitigation),
- #connection_states{dtls_write_msg_seq = 1, % only used by dtls
- current_read = Current,
- pending_read = Pending,
- current_write = Current,
- pending_write = Pending
- }.
-
-%%--------------------------------------------------------------------
--spec current_connection_state(#connection_states{}, read | write) ->
- #connection_state{}.
+-spec current_connection_state(connection_states(), read | write) ->
+ connection_state().
%%
-%% Description: Returns the instance of the connection_state record
+%% Description: Returns the instance of the connection_state map
%% that is currently defined as the current conection state.
%%--------------------------------------------------------------------
-current_connection_state(#connection_states{current_read = Current},
- read) ->
- Current;
-current_connection_state(#connection_states{current_write = Current},
- write) ->
- Current.
+current_connection_state(ConnectionStates, read) ->
+ maps:get(current_read, ConnectionStates);
+current_connection_state(ConnectionStates, write) ->
+ maps:get(current_write, ConnectionStates).
%%--------------------------------------------------------------------
--spec pending_connection_state(#connection_states{}, read | write) ->
- term().
+-spec pending_connection_state(connection_states(), read | write) ->
+ connection_state().
%%
-%% Description: Returns the instance of the connection_state record
-%% that is currently defined as the pending conection state.
+%% Description: Returns the instance of the connection_state map
+%% that is pendingly defined as the pending conection state.
%%--------------------------------------------------------------------
-pending_connection_state(#connection_states{pending_read = Pending},
- read) ->
- Pending;
-pending_connection_state(#connection_states{pending_write = Pending},
- write) ->
- Pending.
-
+pending_connection_state(ConnectionStates, read) ->
+ maps:get(pending_read, ConnectionStates);
+pending_connection_state(ConnectionStates, write) ->
+ maps:get(pending_write, ConnectionStates).
%%--------------------------------------------------------------------
--spec activate_pending_connection_state(#connection_states{}, read | write) ->
- #connection_states{}.
+-spec activate_pending_connection_state(connection_states(), read | write) ->
+ connection_states().
%%
%% Description: Creates a new instance of the connection_states record
%% where the pending state of <Type> has been activated.
%%--------------------------------------------------------------------
-activate_pending_connection_state(States =
- #connection_states{current_read = Current,
- pending_read = Pending},
+activate_pending_connection_state(#{current_read := Current,
+ pending_read := Pending} = States,
read) ->
- NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current),
- sequence_number = 0},
- BeastMitigation = Pending#connection_state.beast_mitigation,
- SecParams = Pending#connection_state.security_parameters,
+ #{secure_renegotiation := SecureRenegotation} = Current,
+ #{beast_mitigation := BeastMitigation,
+ security_parameters := SecParams} = Pending,
+ NewCurrent = Pending#{sequence_number => 0},
ConnectionEnd = SecParams#security_parameters.connection_end,
EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation),
- SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
- NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
- States#connection_states{current_read = NewCurrent,
- pending_read = NewPending
- };
-
-activate_pending_connection_state(States =
- #connection_states{current_write = Current,
- pending_write = Pending},
+ NewPending = EmptyPending#{secure_renegotiation => SecureRenegotation},
+ States#{current_read => NewCurrent,
+ pending_read => NewPending
+ };
+
+activate_pending_connection_state(#{current_write := Current,
+ pending_write := Pending} = States,
write) ->
- NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current),
- sequence_number = 0},
- BeastMitigation = Pending#connection_state.beast_mitigation,
- SecParams = Pending#connection_state.security_parameters,
+ NewCurrent = Pending#{sequence_number => 0},
+ #{secure_renegotiation := SecureRenegotation} = Current,
+ #{beast_mitigation := BeastMitigation,
+ security_parameters := SecParams} = Pending,
ConnectionEnd = SecParams#security_parameters.connection_end,
EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation),
- SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
- NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
- States#connection_states{current_write = NewCurrent,
- pending_write = NewPending
- }.
-
+ NewPending = EmptyPending#{secure_renegotiation => SecureRenegotation},
+ States#{current_write => NewCurrent,
+ pending_write => NewPending
+ }.
%%--------------------------------------------------------------------
-spec set_security_params(#security_parameters{}, #security_parameters{},
- #connection_states{}) -> #connection_states{}.
+ connection_states()) -> connection_states().
%%
%% Description: Creates a new instance of the connection_states record
%% where the pending states gets its security parameters updated.
%%--------------------------------------------------------------------
-set_security_params(ReadParams, WriteParams, States =
- #connection_states{pending_read = Read,
- pending_write = Write}) ->
- States#connection_states{pending_read =
- Read#connection_state{security_parameters =
- ReadParams},
- pending_write =
- Write#connection_state{security_parameters =
- WriteParams}
- }.
+set_security_params(ReadParams, WriteParams,
+ #{pending_read := Read,
+ pending_write := Write} = States) ->
+ States#{pending_read => Read#{security_parameters => ReadParams},
+ pending_write => Write#{security_parameters => WriteParams}
+ }.
%%--------------------------------------------------------------------
-spec set_mac_secret(binary(), binary(), client | server,
- #connection_states{}) -> #connection_states{}.
+ connection_states()) -> connection_states().
%%
%% Description: update the mac_secret field in pending connection states
%%--------------------------------------------------------------------
@@ -177,203 +146,151 @@ set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, server, States) ->
set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, States).
set_mac_secret(ReadMacSecret, WriteMacSecret,
- States = #connection_states{pending_read = Read,
- pending_write = Write}) ->
- States#connection_states{
- pending_read = Read#connection_state{mac_secret = ReadMacSecret},
- pending_write = Write#connection_state{mac_secret = WriteMacSecret}
+ States = #{pending_read := Read,
+ pending_write := Write}) ->
+ States#{pending_read => Read#{mac_secret => ReadMacSecret},
+ pending_write => Write#{mac_secret => WriteMacSecret}
}.
%%--------------------------------------------------------------------
--spec set_master_secret(binary(), #connection_states{}) -> #connection_states{}.
+-spec set_master_secret(binary(), connection_states()) -> connection_states().
%%
%% Description: Set master_secret in pending connection states
%%--------------------------------------------------------------------
set_master_secret(MasterSecret,
- States = #connection_states{pending_read = Read,
- pending_write = Write}) ->
- ReadSecPar = Read#connection_state.security_parameters,
- Read1 = Read#connection_state{
- security_parameters = ReadSecPar#security_parameters{
- master_secret = MasterSecret}},
- WriteSecPar = Write#connection_state.security_parameters,
- Write1 = Write#connection_state{
- security_parameters = WriteSecPar#security_parameters{
- master_secret = MasterSecret}},
- States#connection_states{pending_read = Read1, pending_write = Write1}.
-
-%%--------------------------------------------------------------------
--spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}.
+ States = #{pending_read := Read = #{security_parameters := ReadSecPar},
+ pending_write := Write = #{security_parameters := WriteSecPar}}) ->
+ Read1 = Read#{security_parameters => ReadSecPar#security_parameters{
+ master_secret = MasterSecret}},
+ Write1 = Write#{security_parameters => WriteSecPar#security_parameters{
+ master_secret = MasterSecret}},
+ States#{pending_read => Read1, pending_write => Write1}.
+
+%%--------------------------------------------------------------------
+-spec set_renegotiation_flag(boolean(), connection_states()) -> connection_states().
%%
%% Description: Set secure_renegotiation in pending connection states
%%--------------------------------------------------------------------
-set_renegotiation_flag(Flag, #connection_states{
- current_read = CurrentRead0,
- current_write = CurrentWrite0,
- pending_read = PendingRead0,
- pending_write = PendingWrite0}
+set_renegotiation_flag(Flag, #{current_read := CurrentRead0,
+ current_write := CurrentWrite0,
+ pending_read := PendingRead0,
+ pending_write := PendingWrite0}
= ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{secure_renegotiation = Flag},
- CurrentWrite = CurrentWrite0#connection_state{secure_renegotiation = Flag},
- PendingRead = PendingRead0#connection_state{secure_renegotiation = Flag},
- PendingWrite = PendingWrite0#connection_state{secure_renegotiation = Flag},
- ConnectionStates#connection_states{current_read = CurrentRead,
- current_write = CurrentWrite,
- pending_read = PendingRead,
- pending_write = PendingWrite}.
+ CurrentRead = CurrentRead0#{secure_renegotiation => Flag},
+ CurrentWrite = CurrentWrite0#{secure_renegotiation => Flag},
+ PendingRead = PendingRead0#{secure_renegotiation => Flag},
+ PendingWrite = PendingWrite0#{secure_renegotiation => Flag},
+ ConnectionStates#{current_read => CurrentRead,
+ current_write => CurrentWrite,
+ pending_read => PendingRead,
+ pending_write => PendingWrite}.
%%--------------------------------------------------------------------
-spec set_client_verify_data(current_read | current_write | current_both,
- binary(), #connection_states{})->
- #connection_states{}.
+ binary(), connection_states())->
+ connection_states().
%%
%% Description: Set verify data in connection states.
%%--------------------------------------------------------------------
set_client_verify_data(current_read, Data,
- #connection_states{current_read = CurrentRead0,
- pending_write = PendingWrite0}
+ #{current_read := CurrentRead0,
+ pending_write := PendingWrite0}
= ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
- PendingWrite = PendingWrite0#connection_state{client_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- pending_write = PendingWrite};
+ CurrentRead = CurrentRead0#{client_verify_data => Data},
+ PendingWrite = PendingWrite0#{client_verify_data => Data},
+ ConnectionStates#{current_read => CurrentRead,
+ pending_write => PendingWrite};
set_client_verify_data(current_write, Data,
- #connection_states{pending_read = PendingRead0,
- current_write = CurrentWrite0}
+ #{pending_read := PendingRead0,
+ current_write := CurrentWrite0}
= ConnectionStates) ->
- PendingRead = PendingRead0#connection_state{client_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
- ConnectionStates#connection_states{pending_read = PendingRead,
- current_write = CurrentWrite};
+ PendingRead = PendingRead0#{client_verify_data => Data},
+ CurrentWrite = CurrentWrite0#{client_verify_data => Data},
+ ConnectionStates#{pending_read => PendingRead,
+ current_write => CurrentWrite};
set_client_verify_data(current_both, Data,
- #connection_states{current_read = CurrentRead0,
- current_write = CurrentWrite0}
+ #{current_read := CurrentRead0,
+ current_write := CurrentWrite0}
= ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- current_write = CurrentWrite}.
+ CurrentRead = CurrentRead0#{client_verify_data => Data},
+ CurrentWrite = CurrentWrite0#{client_verify_data => Data},
+ ConnectionStates#{current_read => CurrentRead,
+ current_write => CurrentWrite}.
%%--------------------------------------------------------------------
-spec set_server_verify_data(current_read | current_write | current_both,
- binary(), #connection_states{})->
- #connection_states{}.
+ binary(), connection_states())->
+ connection_states().
%%
%% Description: Set verify data in pending connection states.
%%--------------------------------------------------------------------
set_server_verify_data(current_write, Data,
- #connection_states{pending_read = PendingRead0,
- current_write = CurrentWrite0}
+ #{pending_read := PendingRead0,
+ current_write := CurrentWrite0}
= ConnectionStates) ->
- PendingRead = PendingRead0#connection_state{server_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data},
- ConnectionStates#connection_states{pending_read = PendingRead,
- current_write = CurrentWrite};
+ PendingRead = PendingRead0#{server_verify_data => Data},
+ CurrentWrite = CurrentWrite0#{server_verify_data => Data},
+ ConnectionStates#{pending_read => PendingRead,
+ current_write => CurrentWrite};
set_server_verify_data(current_read, Data,
- #connection_states{current_read = CurrentRead0,
- pending_write = PendingWrite0}
+ #{current_read := CurrentRead0,
+ pending_write := PendingWrite0}
= ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{server_verify_data = Data},
- PendingWrite = PendingWrite0#connection_state{server_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- pending_write = PendingWrite};
+ CurrentRead = CurrentRead0#{server_verify_data => Data},
+ PendingWrite = PendingWrite0#{server_verify_data => Data},
+ ConnectionStates#{current_read => CurrentRead,
+ pending_write => PendingWrite};
set_server_verify_data(current_both, Data,
- #connection_states{current_read = CurrentRead0,
- current_write = CurrentWrite0}
+ #{current_read := CurrentRead0,
+ current_write := CurrentWrite0}
= ConnectionStates) ->
- CurrentRead = CurrentRead0#connection_state{server_verify_data = Data},
- CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data},
- ConnectionStates#connection_states{current_read = CurrentRead,
- current_write = CurrentWrite}.
+ CurrentRead = CurrentRead0#{server_verify_data => Data},
+ CurrentWrite = CurrentWrite0#{server_verify_data => Data},
+ ConnectionStates#{current_read => CurrentRead,
+ current_write => CurrentWrite}.
%%--------------------------------------------------------------------
--spec set_pending_cipher_state(#connection_states{}, #cipher_state{},
+-spec set_pending_cipher_state(connection_states(), #cipher_state{},
#cipher_state{}, client | server) ->
- #connection_states{}.
+ connection_states().
%%
%% Description: Set the cipher state in the specified pending connection state.
%%--------------------------------------------------------------------
-set_pending_cipher_state(#connection_states{pending_read = Read,
- pending_write = Write} = States,
+set_pending_cipher_state(#{pending_read := Read,
+ pending_write := Write} = States,
ClientState, ServerState, server) ->
- States#connection_states{
- pending_read = Read#connection_state{cipher_state = ClientState},
- pending_write = Write#connection_state{cipher_state = ServerState}};
+ States#{
+ pending_read => Read#{cipher_state => ClientState},
+ pending_write => Write#{cipher_state => ServerState}};
-set_pending_cipher_state(#connection_states{pending_read = Read,
- pending_write = Write} = States,
+set_pending_cipher_state(#{pending_read := Read,
+ pending_write := Write} = States,
ClientState, ServerState, client) ->
- States#connection_states{
- pending_read = Read#connection_state{cipher_state = ServerState},
- pending_write = Write#connection_state{cipher_state = ClientState}}.
-
-
-%%--------------------------------------------------------------------
--spec encode_handshake(iolist(), ssl_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
-%%
-%% Description: Encodes a handshake message to send on the ssl-socket.
-%%--------------------------------------------------------------------
-encode_handshake(Frag, Version,
- #connection_states{current_write =
- #connection_state{
- beast_mitigation = BeastMitigation,
- security_parameters =
- #security_parameters{bulk_cipher_algorithm = BCA}}} =
- ConnectionStates)
-when is_list(Frag) ->
- case iolist_size(Frag) of
- N when N > ?MAX_PLAIN_TEXT_LENGTH ->
- Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation),
- encode_iolist(?HANDSHAKE, Data, Version, ConnectionStates);
- _ ->
- encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates)
- end;
-%% TODO: this is a workarround for DTLS
-%%
-%% DTLS need to select the connection write state based on Epoch it wants to
-%% send this fragment in. That Epoch does not nessarily has to be the same
-%% as the current_write epoch.
-%% The right solution might be to pass the WriteState instead of the ConnectionStates,
-%% however, this will require substantion API changes.
-encode_handshake(Frag, Version, ConnectionStates) ->
- encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
-
-%%--------------------------------------------------------------------
--spec encode_alert_record(#alert{}, ssl_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
-%%
-%% Description: Encodes an alert message to send on the ssl-socket.
-%%--------------------------------------------------------------------
-encode_alert_record(#alert{level = Level, description = Description},
- Version, ConnectionStates) ->
- encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>,
- ConnectionStates).
-
-%%--------------------------------------------------------------------
--spec encode_change_cipher_spec(ssl_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
-%%
-%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
-%%--------------------------------------------------------------------
-encode_change_cipher_spec(Version, ConnectionStates) ->
- encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
-
-%%--------------------------------------------------------------------
--spec encode_data(binary(), ssl_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
-%%
-%% Description: Encodes data to send on the ssl-socket.
-%%--------------------------------------------------------------------
-encode_data(Frag, Version,
- #connection_states{current_write = #connection_state{
- beast_mitigation = BeastMitigation,
- security_parameters =
- #security_parameters{bulk_cipher_algorithm = BCA}}} =
- ConnectionStates) ->
- Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation),
- encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates).
+ States#{
+ pending_read => Read#{cipher_state => ServerState},
+ pending_write => Write#{cipher_state => ClientState}}.
+
+encode_plain_text(Type, Version, Data, #{compression_state := CompS0,
+ security_parameters :=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm = CompAlg}
+ } = WriteState0) ->
+ {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
+ WriteState1 = WriteState0#{compression_state => CompS1},
+ AAD = ssl_cipher:calc_aad(Type, Version, WriteState1),
+ ssl_record:cipher_aead(Version, Comp, WriteState1, AAD);
+encode_plain_text(Type, Version, Data, #{compression_state := CompS0,
+ security_parameters :=
+ #security_parameters{compression_algorithm = CompAlg}
+ }= WriteState0) ->
+ {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
+ WriteState1 = WriteState0#{compression_state => CompS1},
+ MacHash = ssl_cipher:calc_mac_hash(Type, Version, Comp, WriteState1),
+ ssl_record:cipher(Version, Comp, WriteState1, MacHash);
+encode_plain_text(_,_,_,CS) ->
+ exit({cs, CS}).
uncompress(?NULL, Data, CS) ->
{Data, CS}.
@@ -390,73 +307,74 @@ compressions() ->
[?byte(?NULL)].
%%--------------------------------------------------------------------
--spec cipher(ssl_version(), iodata(), #connection_state{}, MacHash::binary()) ->
- {CipherFragment::binary(), #connection_state{}}.
+-spec cipher(ssl_version(), iodata(), connection_state(), MacHash::binary()) ->
+ {CipherFragment::binary(), connection_state()}.
%%
%% Description: Payload encryption
%%--------------------------------------------------------------------
cipher(Version, Fragment,
- #connection_state{cipher_state = CipherS0,
- security_parameters=
- #security_parameters{bulk_cipher_algorithm =
- BulkCipherAlgo}
- } = WriteState0, MacHash) ->
-
+ #{cipher_state := CipherS0,
+ security_parameters :=
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo}
+ } = WriteState0, MacHash) ->
+
{CipherFragment, CipherS1} =
ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version),
- {CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}.
+ {CipherFragment, WriteState0#{cipher_state => CipherS1}}.
%%--------------------------------------------------------------------
--spec cipher_aead(ssl_version(), iodata(), #connection_state{}, MacHash::binary()) ->
- {CipherFragment::binary(), #connection_state{}}.
+-spec cipher_aead(ssl_version(), iodata(), connection_state(), MacHash::binary()) ->
+ {CipherFragment::binary(), connection_state()}.
%%
%% Description: Payload encryption
%%--------------------------------------------------------------------
cipher_aead(Version, Fragment,
- #connection_state{cipher_state = CipherS0,
- sequence_number = SeqNo,
- security_parameters=
- #security_parameters{bulk_cipher_algorithm =
- BulkCipherAlgo}
- } = WriteState0, AAD) ->
-
+ #{cipher_state := CipherS0,
+ sequence_number := SeqNo,
+ security_parameters :=
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo}
+ } = WriteState0, AAD) ->
+
{CipherFragment, CipherS1} =
ssl_cipher:cipher_aead(BulkCipherAlgo, CipherS0, SeqNo, AAD, Fragment, Version),
- {CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}.
+ {CipherFragment, WriteState0#{cipher_state => CipherS1}}.
%%--------------------------------------------------------------------
--spec decipher(ssl_version(), binary(), #connection_state{}, boolean()) -> {binary(), binary(), #connection_state{}} | #alert{}.
+-spec decipher(ssl_version(), binary(), connection_state(), boolean()) -> {binary(), binary(), connection_state} | #alert{}.
%%
%% Description: Payload decryption
%%--------------------------------------------------------------------
decipher(Version, CipherFragment,
- #connection_state{security_parameters =
- #security_parameters{bulk_cipher_algorithm =
- BulkCipherAlgo,
- hash_size = HashSz},
- cipher_state = CipherS0
- } = ReadState, PaddingCheck) ->
+ #{security_parameters :=
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo,
+ hash_size = HashSz},
+ cipher_state := CipherS0
+ } = ReadState, PaddingCheck) ->
case ssl_cipher:decipher(BulkCipherAlgo, HashSz, CipherS0, CipherFragment, Version, PaddingCheck) of
{PlainFragment, Mac, CipherS1} ->
- CS1 = ReadState#connection_state{cipher_state = CipherS1},
+ CS1 = ReadState#{cipher_state => CipherS1},
{PlainFragment, Mac, CS1};
#alert{} = Alert ->
Alert
end.
%%--------------------------------------------------------------------
--spec decipher_aead(ssl_version(), binary(), #connection_state{}, binary()) -> {binary(), binary(), #connection_state{}} | #alert{}.
+-spec decipher_aead(ssl_version(), binary(), connection_state(), binary()) ->
+ {binary(), binary(), connection_state()} | #alert{}.
%%
%% Description: Payload decryption
%%--------------------------------------------------------------------
decipher_aead(Version, CipherFragment,
- #connection_state{sequence_number = SeqNo,
- security_parameters =
- #security_parameters{bulk_cipher_algorithm =
- BulkCipherAlgo},
- cipher_state = CipherS0
- } = ReadState, AAD) ->
+ #{sequence_number := SeqNo,
+ security_parameters :=
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo},
+ cipher_state := CipherS0
+ } = ReadState, AAD) ->
case ssl_cipher:decipher_aead(BulkCipherAlgo, CipherS0, SeqNo, AAD, CipherFragment, Version) of
{PlainFragment, CipherS1} ->
- CS1 = ReadState#connection_state{cipher_state = CipherS1},
+ CS1 = ReadState#{cipher_state => CipherS1},
{PlainFragment, CS1};
#alert{} = Alert ->
Alert
@@ -466,8 +384,15 @@ decipher_aead(Version, CipherFragment,
%%--------------------------------------------------------------------
empty_connection_state(ConnectionEnd, BeastMitigation) ->
SecParams = empty_security_params(ConnectionEnd),
- #connection_state{security_parameters = SecParams,
- beast_mitigation = BeastMitigation}.
+ #{security_parameters => SecParams,
+ beast_mitigation => BeastMitigation,
+ compression_state => undefined,
+ cipher_state => undefined,
+ mac_secret => undefined,
+ secure_renegotiation => undefined,
+ client_verify_data => undefined,
+ server_verify_data => undefined
+ }.
empty_security_params(ConnectionEnd = ?CLIENT) ->
#security_parameters{connection_end = ConnectionEnd,
@@ -481,11 +406,6 @@ random() ->
Random_28_bytes = ssl_cipher:random_bytes(28),
<<?UINT32(Secs_since_1970), Random_28_bytes/binary>>.
-dtls_next_epoch(#connection_state{epoch = undefined}) -> %% SSL/TLS
- undefined;
-dtls_next_epoch(#connection_state{epoch = Epoch}) -> %% DTLS
- Epoch + 1.
-
is_correct_mac(Mac, Mac) ->
true;
is_correct_mac(_M,_H) ->
@@ -497,58 +417,20 @@ record_protocol_role(server) ->
?SERVER.
initial_connection_state(ConnectionEnd, BeastMitigation) ->
- #connection_state{security_parameters =
- initial_security_params(ConnectionEnd),
- sequence_number = 0,
- beast_mitigation = BeastMitigation
- }.
+ #{security_parameters =>
+ initial_security_params(ConnectionEnd),
+ sequence_number => 0,
+ beast_mitigation => BeastMitigation,
+ compression_state => undefined,
+ cipher_state => undefined,
+ mac_secret => undefined,
+ secure_renegotiation => undefined,
+ client_verify_data => undefined,
+ server_verify_data => undefined
+ }.
initial_security_params(ConnectionEnd) ->
SecParams = #security_parameters{connection_end = ConnectionEnd,
compression_algorithm = ?NULL},
ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL, SecParams).
-
-encode_plain_text(Type, Version, Data, ConnectionStates) ->
- RecordCB = protocol_module(Version),
- RecordCB:encode_plain_text(Type, Version, Data, ConnectionStates).
-
-encode_iolist(Type, Data, Version, ConnectionStates0) ->
- RecordCB = protocol_module(Version),
- {ConnectionStates, EncodedMsg} =
- lists:foldl(fun(Text, {CS0, Encoded}) ->
- {Enc, CS1} =
- RecordCB:encode_plain_text(Type, Version, Text, CS0),
- {CS1, [Enc | Encoded]}
- end, {ConnectionStates0, []}, Data),
- {lists:reverse(EncodedMsg), ConnectionStates}.
-
-%% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are
-%% not vulnerable to this attack.
-split_bin(<<FirstByte:8, Rest/binary>>, ChunkSize, Version, BCA, one_n_minus_one) when
- BCA =/= ?RC4 andalso ({3, 1} == Version orelse
- {3, 0} == Version) ->
- do_split_bin(Rest, ChunkSize, [[FirstByte]]);
-%% 0/n splitting countermeasure for clients that are incompatible with 1/n-1
-%% splitting.
-split_bin(Bin, ChunkSize, Version, BCA, zero_n) when
- BCA =/= ?RC4 andalso ({3, 1} == Version orelse
- {3, 0} == Version) ->
- do_split_bin(Bin, ChunkSize, [[<<>>]]);
-split_bin(Bin, ChunkSize, _, _, _) ->
- do_split_bin(Bin, ChunkSize, []).
-
-do_split_bin(<<>>, _, Acc) ->
- lists:reverse(Acc);
-do_split_bin(Bin, ChunkSize, Acc) ->
- case Bin of
- <<Chunk:ChunkSize/binary, Rest/binary>> ->
- do_split_bin(Rest, ChunkSize, [Chunk | Acc]);
- _ ->
- lists:reverse(Acc, [Bin])
- end.
-
-protocol_module({3, _}) ->
- tls_record;
-protocol_module({254, _}) ->
- dtls_record.
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index a41264ff9b..ed007f58d7 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -30,29 +30,27 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Connection states - RFC 4346 section 6.1
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--record(connection_state, {
- security_parameters,
- compression_state,
- cipher_state,
- mac_secret,
- epoch, %% Only used by DTLS
- sequence_number,
- %% RFC 5746
- secure_renegotiation,
- client_verify_data,
- server_verify_data,
- %% How to do BEAST mitigation?
- beast_mitigation
- }).
-
--record(connection_states, {
- dtls_write_msg_seq, %% Only used by DTLS
+%% For documentation purposes are now maps in implementation
+%% -record(connection_state, {
+%% security_parameters,
+%% compression_state,
+%% cipher_state,
+%% mac_secret,
+%% sequence_number,
+%% %% RFC 5746
+%% secure_renegotiation,
+%% client_verify_data,
+%% server_verify_data,
+%% %% How to do BEAST mitigation?
+%% beast_mitigation
+%% }).
- current_read,
- pending_read,
- current_write,
- pending_write
- }).
+%% -record(connection_states, {
+%% current_read,
+%% pending_read,
+%% current_write,
+%% pending_write,
+%% }).
-record(security_parameters, {
cipher_suite,
diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl
index 7fa1f7dc9e..05a7aaaa82 100644
--- a/lib/ssl/src/ssl_sup.erl
+++ b/lib/ssl/src/ssl_sup.erl
@@ -25,7 +25,7 @@
-behaviour(supervisor).
%% API
--export([start_link/0, manager_opts/0]).
+-export([start_link/0]).
%% Supervisor callback
-export([init/1]).
@@ -44,78 +44,28 @@ start_link() ->
%%%=========================================================================
init([]) ->
- SessionCertManager = session_and_cert_manager_child_spec(),
- TLSConnetionManager = tls_connection_manager_child_spec(),
- %% Not supported yet
- %%DTLSConnetionManager = tls_connection_manager_child_spec(),
- %% Handles emulated options so that they inherited by the accept socket, even when setopts is performed on
- %% the listen socket
- ListenOptionsTracker = listen_options_tracker_child_spec(),
- {ok, {{one_for_all, 10, 3600}, [SessionCertManager, TLSConnetionManager, ListenOptionsTracker]}}.
+ {ok, {{rest_for_one, 10, 3600}, [ssl_admin_child_spec(),
+ ssl_connection_sup()
+ ]}}.
-
-manager_opts() ->
- CbOpts = case application:get_env(ssl, session_cb) of
- {ok, Cb} when is_atom(Cb) ->
- InitArgs = session_cb_init_args(),
- [{session_cb, Cb}, {session_cb_init_args, InitArgs}];
- _ ->
- []
- end,
- case application:get_env(ssl, session_lifetime) of
- {ok, Time} when is_integer(Time) ->
- [{session_lifetime, Time}| CbOpts];
- _ ->
- CbOpts
- end.
-
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-
-session_and_cert_manager_child_spec() ->
- Opts = manager_opts(),
- Name = ssl_manager,
- StartFunc = {ssl_manager, start_link, [Opts]},
- Restart = permanent,
- Shutdown = 4000,
- Modules = [ssl_manager],
- Type = worker,
- {Name, StartFunc, Restart, Shutdown, Type, Modules}.
-
-tls_connection_manager_child_spec() ->
- Name = tls_connection,
- StartFunc = {tls_connection_sup, start_link, []},
+ssl_admin_child_spec() ->
+ Name = ssl_admin_sup,
+ StartFunc = {ssl_admin_sup, start_link, []},
Restart = permanent,
Shutdown = 4000,
- Modules = [tls_connection_sup],
+ Modules = [ssl_admin_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-%% dtls_connection_manager_child_spec() ->
-%% Name = dtls_connection,
-%% StartFunc = {dtls_connection_sup, start_link, []},
-%% Restart = permanent,
-%% Shutdown = 4000,
-%% Modules = [dtls_connection, ssl_connection],
-%% Type = supervisor,
-%% {Name, StartFunc, Restart, Shutdown, Type, Modules}.
-
-
-listen_options_tracker_child_spec() ->
- Name = ssl_socket,
- StartFunc = {ssl_listen_tracker_sup, start_link, []},
- Restart = permanent,
+ssl_connection_sup() ->
+ Name = ssl_connection_sup,
+ StartFunc = {ssl_connection_sup, start_link, []},
+ Restart = permanent,
Shutdown = 4000,
- Modules = [ssl_socket],
+ Modules = [ssl_connection_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-
-session_cb_init_args() ->
- case application:get_env(ssl, session_cb_init_args) of
- {ok, Args} when is_list(Args) ->
- Args;
- _ ->
- []
- end.
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index a920f54ed2..08947f24dd 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -117,7 +117,7 @@ handle_call({listen, Driver, Name}, _From, State) ->
{ok, WorldTcpAddress} = get_tcp_address(World),
{_,Port} = WorldTcpAddress#net_address.address,
ErlEpmd = net_kernel:epmd_module(),
- case ErlEpmd:register_node(Name, Port) of
+ case ErlEpmd:register_node(Name, Port, Driver) of
{ok, Creation} ->
{reply, {ok, {Socket, TcpAddress, Creation}},
State#state{listen={Socket, World}}};
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 9880befa94..b18c69c740 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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.
@@ -45,33 +45,30 @@
%% Setup
-export([start_fsm/8, start_link/7, init/1]).
+-export([encode_data/3, encode_alert/3]).
+
%% State transition handling
--export([next_record/1, next_event/3]).
+-export([next_record/1, next_event/3, next_event/4]).
%% Handshake handling
-export([renegotiate/2, send_handshake/2,
queue_handshake/2, queue_change_cipher/2,
- reinit_handshake_data/1, handle_sni_extension/2]).
+ reinit_handshake_data/1, select_sni_extension/1]).
%% Alert and close handling
--export([send_alert/2, handle_own_alert/4, handle_close_alert/3,
- handle_normal_shutdown/3,
- close/5, alert_user/6, alert_user/9
- ]).
+-export([send_alert/2, close/5]).
%% Data handling
--export([write_application_data/3, read_application_data/2,
- passive_receive/2, next_record_if_active/1, handle_common_event/4]).
+-export([passive_receive/2, next_record_if_active/1, handle_common_event/4, send/3,
+ socket/5]).
%% 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]).
+-export([callback_mode/0, terminate/3, code_change/4, format_status/2]).
--define(GEN_STATEM_CB_MODE, state_functions).
-
%%====================================================================
%% Internal application API
%%====================================================================
@@ -109,9 +106,10 @@ send_handshake(Handshake, State) ->
queue_handshake(Handshake, #state{negotiated_version = Version,
tls_handshake_history = Hist0,
flight_buffer = Flight0,
+ ssl_options = #ssl_options{v2_hello_compatible = V2HComp},
connection_states = ConnectionStates0} = State0) ->
{BinHandshake, ConnectionStates, Hist} =
- encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
+ encode_handshake(Handshake, Version, ConnectionStates0, Hist0, V2HComp),
State0#state{connection_states = ConnectionStates,
tls_handshake_history = Hist,
flight_buffer = Flight0 ++ [BinHandshake]}.
@@ -119,8 +117,8 @@ queue_handshake(Handshake, #state{negotiated_version = Version,
send_handshake_flight(#state{socket = Socket,
transport_cb = Transport,
flight_buffer = Flight} = State0) ->
- Transport:send(Socket, Flight),
- State0#state{flight_buffer = []}.
+ send(Transport, Socket, Flight),
+ {State0#state{flight_buffer = []}, []}.
queue_change_cipher(Msg, #state{negotiated_version = Version,
flight_buffer = Flight0,
@@ -135,8 +133,8 @@ send_alert(Alert, #state{negotiated_version = Version,
transport_cb = Transport,
connection_states = ConnectionStates0} = State0) ->
{BinMsg, ConnectionStates} =
- ssl_alert:encode(Alert, Version, ConnectionStates0),
- Transport:send(Socket, BinMsg),
+ encode_alert(Alert, Version, ConnectionStates0),
+ send(Transport, Socket, BinMsg),
State0#state{connection_states = ConnectionStates}.
reinit_handshake_data(State) ->
@@ -149,6 +147,23 @@ reinit_handshake_data(State) ->
tls_handshake_history = ssl_handshake:init_handshake_history()
}.
+select_sni_extension(#client_hello{extensions = HelloExtensions}) ->
+ HelloExtensions#hello_extensions.sni;
+select_sni_extension(_) ->
+ undefined.
+
+encode_data(Data, Version, ConnectionStates0)->
+ tls_record:encode_data(Data, Version, ConnectionStates0).
+
+%%--------------------------------------------------------------------
+-spec encode_alert(#alert{}, ssl_record:ssl_version(), ssl_record:connection_states()) ->
+ {iolist(), ssl_record:connection_states()}.
+%%
+%% Description: Encodes an alert
+%%--------------------------------------------------------------------
+encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
+ tls_record:encode_alert_record(Alert, Version, ConnectionStates).
+
%%====================================================================
%% tls_connection_sup API
%%====================================================================
@@ -169,11 +184,18 @@ init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
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)
+ gen_statem:enter_loop(?MODULE, [], init, State)
catch throw:Error ->
- gen_statem:enter_loop(?MODULE, [], ?GEN_STATEM_CB_MODE, error, {Error, State0})
+ gen_statem:enter_loop(?MODULE, [], error, {Error, State0})
end.
+callback_mode() ->
+ state_functions.
+
+socket(Pid, Transport, Socket, Connection, Tracker) ->
+ tls_socket:socket(Pid, Transport, Socket, Connection, Tracker).
+
+
%%--------------------------------------------------------------------
%% State functions
%%--------------------------------------------------------------------
@@ -185,7 +207,7 @@ init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
init({call, From}, {start, Timeout},
#state{host = Host, port = Port, role = client,
- ssl_options = SslOpts,
+ ssl_options = #ssl_options{v2_hello_compatible = V2HComp} = SslOpts,
session = #session{own_certificate = Cert} = Session0,
transport_cb = Transport, socket = Socket,
connection_states = ConnectionStates0,
@@ -201,8 +223,8 @@ init({call, From}, {start, Timeout},
HelloVersion = tls_record:lowest_protocol_version(SslOpts#ssl_options.versions),
Handshake0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates, Handshake} =
- encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0),
- Transport:send(Socket, BinMsg),
+ encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0, V2HComp),
+ send(Transport, Socket, BinMsg),
State1 = State0#state{connection_states = ConnectionStates,
negotiated_version = Version, %% Requested version
session =
@@ -213,7 +235,7 @@ init({call, From}, {start, Timeout},
{Record, State} = next_record(State1),
next_event(hello, Record, State);
init(Type, Event, State) ->
- ssl_connection:init(Type, Event, State, ?MODULE).
+ gen_handshake(ssl_connection, init, Type, Event, State).
%%--------------------------------------------------------------------
-spec error(gen_statem:event_type(),
@@ -234,9 +256,7 @@ error(_, _, _) ->
#state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
-hello(internal, #client_hello{client_version = ClientVersion,
- extensions = #hello_extensions{ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves}} = Hello,
+hello(internal, #client_hello{client_version = ClientVersion} = Hello,
#state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
@@ -249,7 +269,7 @@ hello(internal, #client_hello{client_version = ClientVersion,
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
#alert{} = Alert ->
- handle_own_alert(Alert, ClientVersion, hello, State);
+ ssl_connection:handle_own_alert(Alert, ClientVersion, hello, State);
{Version, {Type, Session},
ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
Protocol = case Protocol0 of
@@ -257,13 +277,13 @@ hello(internal, #client_hello{client_version = ClientVersion,
_ -> Protocol0
end,
- ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt},
+ gen_handshake(ssl_connection, hello, internal, {common_client_hello, Type, ServerHelloExt},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
+ client_hello_version = ClientVersion,
hashsign_algorithm = HashSign,
session = Session,
- client_ecc = {EllipticCurves, EcPointFormats},
- negotiated_protocol = Protocol}, ?MODULE)
+ negotiated_protocol = Protocol})
end;
hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
@@ -273,42 +293,42 @@ hello(internal, #server_hello{} = Hello,
ssl_options = SslOptions} = State) ->
case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
- handle_own_alert(Alert, ReqVersion, hello, State);
+ ssl_connection:handle_own_alert(Alert, ReqVersion, hello, State);
{Version, NewId, ConnectionStates, ProtoExt, Protocol} ->
ssl_connection:handle_session(Hello,
Version, NewId, ConnectionStates, ProtoExt, Protocol, State)
end;
hello(info, Event, State) ->
- handle_info(Event, hello, State);
+ gen_info(Event, hello, State);
hello(Type, Event, State) ->
- ssl_connection:hello(Type, Event, State, ?MODULE).
+ gen_handshake(ssl_connection, hello, Type, Event, State).
%%--------------------------------------------------------------------
-spec abbreviated(gen_statem:event_type(), term(), #state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
abbreviated(info, Event, State) ->
- handle_info(Event, abbreviated, State);
+ gen_info(Event, abbreviated, State);
abbreviated(Type, Event, State) ->
- ssl_connection:abbreviated(Type, Event, State, ?MODULE).
+ gen_handshake(ssl_connection, abbreviated, Type, Event, State).
%%--------------------------------------------------------------------
-spec certify(gen_statem:event_type(), term(), #state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
certify(info, Event, State) ->
- handle_info(Event, certify, State);
+ gen_info(Event, certify, State);
certify(Type, Event, State) ->
- ssl_connection:certify(Type, Event, State, ?MODULE).
+ gen_handshake(ssl_connection, certify, Type, Event, State).
%%--------------------------------------------------------------------
-spec cipher(gen_statem:event_type(), term(), #state{}) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
cipher(info, Event, State) ->
- handle_info(Event, cipher, State);
+ gen_info(Event, cipher, State);
cipher(Type, Event, State) ->
- ssl_connection:cipher(Type, Event, State, ?MODULE).
+ gen_handshake(ssl_connection, cipher, Type, Event, State).
%%--------------------------------------------------------------------
-spec connection(gen_statem:event_type(),
@@ -316,7 +336,7 @@ cipher(Type, Event, State) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
connection(info, Event, State) ->
- handle_info(Event, connection, State);
+ gen_info(Event, connection, State);
connection(internal, #hello_request{},
#state{role = client, host = Host, port = Port,
session = #session{own_certificate = Cert} = Session0,
@@ -326,12 +346,12 @@ connection(internal, #hello_request{},
renegotiation = {Renegotiation, _}} = State0) ->
Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
- State1 = send_handshake(Hello, State0),
+ {State1, Actions} = send_handshake(Hello, State0),
{Record, State} =
next_record(
State1#state{session = Session0#session{session_id
= Hello#client_hello.session_id}}),
- next_event(hello, Record, State);
+ next_event(hello, Record, State, Actions);
connection(internal, #client_hello{} = Hello,
#state{role = server, allow_renegotiate = true} = State0) ->
%% Mitigate Computational DoS attack
@@ -373,34 +393,47 @@ handle_info({Protocol, _, Data}, StateName,
{Record, State} ->
next_event(StateName, Record, State);
#alert{} = Alert ->
- handle_normal_shutdown(Alert, StateName, State0),
+ ssl_connection:handle_normal_shutdown(Alert, StateName, State0),
{stop, {shutdown, own_alert}}
end;
handle_info({CloseTag, Socket}, StateName,
#state{socket = Socket, close_tag = CloseTag,
+ socket_options = #socket_options{active = Active},
+ protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs},
negotiated_version = Version} = State) ->
+
%% Note that as of TLS 1.1,
%% failure to properly close a connection no longer requires that a
%% session not be resumed. This is a change from TLS 1.0 to conform
%% with widespread implementation practice.
- case Version of
- {1, N} when N >= 1 ->
- ok;
- _ ->
- %% As invalidate_sessions here causes performance issues,
- %% we will conform to the widespread implementation
- %% practice and go aginst the spec
- %%invalidate_session(Role, Host, Port, Session)
- ok
- end,
- handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
- {stop, {shutdown, transport_closed}};
+
+ case (Active == false) andalso (CTs =/= []) of
+ false ->
+ case Version of
+ {1, N} when N >= 1 ->
+ ok;
+ _ ->
+ %% As invalidate_sessions here causes performance issues,
+ %% we will conform to the widespread implementation
+ %% practice and go aginst the spec
+ %%invalidate_session(Role, Host, Port, Session)
+ ok
+ end,
+
+ ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
+ {stop, {shutdown, transport_closed}};
+ true ->
+ %% Fixes non-delivery of final TLS record in {active, once}.
+ %% Basically allows the application the opportunity to set {active, once} again
+ %% and then receive the final message.
+ next_event(StateName, no_record, State)
+ end;
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);
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State);
%%% TLS record protocol level handshake messages
handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
@@ -410,18 +443,26 @@ handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
ssl_options = Options} = State0) ->
try
{Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0, Options),
- State =
+ State1 =
State0#state{protocol_buffers =
Buffers#protocol_buffers{tls_handshake_buffer = Buf}},
- Events = tls_handshake_events(Packets),
- case StateName of
- connection ->
- ssl_connection:hibernate_after(StateName, State, Events);
- _ ->
- {next_state, StateName, State, Events}
- end
+ case Packets of
+ [] ->
+ assert_buffer_sanity(Buf, Options),
+ {Record, State} = next_record(State1),
+ next_event(StateName, Record, State);
+ _ ->
+ Events = tls_handshake_events(Packets),
+ case StateName of
+ connection ->
+ ssl_connection:hibernate_after(StateName, State1, Events);
+ _ ->
+ {next_state, StateName,
+ State1#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events}
+ end
+ end
catch throw:#alert{} = Alert ->
- handle_own_alert(Alert, Version, StateName, State0)
+ ssl_connection: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) ->
@@ -432,16 +473,27 @@ handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Da
%%% 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
+ try decode_alerts(EncAlerts) of
Alerts = [_|_] ->
handle_alerts(Alerts, {next_state, StateName, State});
+ [] ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, empty_alert),
+ Version, StateName, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, StateName, State)
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State)
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, alert_decode_error),
+ 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}.
+send(Transport, Socket, Data) ->
+ tls_socket:send(Transport, Socket, Data).
+
%%--------------------------------------------------------------------
%% gen_statem callbacks
%%--------------------------------------------------------------------
@@ -457,22 +509,22 @@ format_status(Type, Data) ->
%%--------------------------------------------------------------------
code_change(_OldVsn, StateName, State0, {Direction, From, To}) ->
State = convert_state(State0, Direction, From, To),
- {?GEN_STATEM_CB_MODE, StateName, State};
+ {ok, StateName, State};
code_change(_OldVsn, StateName, State, _) ->
- {?GEN_STATEM_CB_MODE, StateName, State}.
+ {ok, StateName, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-encode_handshake(Handshake, Version, ConnectionStates0, Hist0) ->
+encode_handshake(Handshake, Version, ConnectionStates0, Hist0, V2HComp) ->
Frag = tls_handshake:encode_handshake(Handshake, Version),
- Hist = ssl_handshake:update_handshake_history(Hist0, Frag),
+ Hist = ssl_handshake:update_handshake_history(Hist0, Frag, V2HComp),
{Encoded, ConnectionStates} =
- ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
+ tls_record:encode_handshake(Frag, Version, ConnectionStates0),
{Encoded, ConnectionStates, Hist}.
encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
- ssl_record:encode_change_cipher_spec(Version, ConnectionStates).
+ tls_record:encode_change_cipher_spec(Version, ConnectionStates).
decode_alerts(Bin) ->
ssl_alert:decode(Bin).
@@ -480,7 +532,7 @@ decode_alerts(Bin) ->
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
#ssl_options{beast_mitigation = BeastMitigation} = SSLOptions,
- ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation),
+ ConnectionStates = tls_record:init_connection_states(Role, BeastMitigation),
SessionCacheCb = case application:get_env(ssl, session_cb) of
{ok, Cb} when is_atom(Cb) ->
@@ -515,23 +567,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, Us
flight_buffer = []
}.
-
-update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) ->
- SSLOption =
- case OrigSSLOptions#ssl_options.sni_fun of
- undefined ->
- proplists:get_value(SNIHostname,
- OrigSSLOptions#ssl_options.sni_hosts);
- SNIFun ->
- SNIFun(SNIHostname)
- end,
- case SSLOption of
- undefined ->
- undefined;
- _ ->
- ssl:handle_options(SSLOption, OrigSSLOptions)
- end.
-
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
@@ -543,7 +578,9 @@ next_tls_record(Data, #state{protocol_buffers = #protocol_buffers{tls_record_buf
#alert{} = Alert ->
Alert
end.
-
+next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
+ {no_record, State#state{unprocessed_handshake_events = N-1}};
+
next_record(#state{protocol_buffers =
#protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]}
= Buffers,
@@ -560,7 +597,7 @@ next_record(#state{protocol_buffers =
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}]),
+ tls_socket:setopts(Transport, Socket, [{active,once}]),
{no_record, State};
next_record(State) ->
{no_record, State}.
@@ -579,7 +616,7 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
{Record, State} = next_record(State0),
next_event(StateName, Record, State);
_ ->
- {Record, State} = read_application_data(<<>>, State0),
+ {Record, State} = ssl_connection:read_application_data(<<>>, State0),
next_event(StateName, Record, State)
end.
@@ -591,7 +628,7 @@ next_event(connection = StateName, no_record, State0, Actions) ->
{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]};
+ {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
{#alert{} = Alert, State} ->
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end;
@@ -600,225 +637,17 @@ next_event(StateName, Record, State, Actions) ->
no_record ->
{next_state, StateName, State, Actions};
#ssl_tls{} = Record ->
- {next_state, StateName, State, [{next_event, internal, {tls_record, Record}} | Actions]};
+ {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]};
#alert{} = Alert ->
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end.
-read_application_data(Data, #state{user_application = {_Mon, Pid},
- socket = Socket,
- transport_cb = Transport,
- socket_options = SOpts,
- bytes_to_read = BytesToRead,
- start_or_recv_from = RecvFrom,
- timer = Timer,
- user_data_buffer = Buffer0,
- tracker = Tracker} = State0) ->
- Buffer1 = if
- Buffer0 =:= <<>> -> Data;
- Data =:= <<>> -> Buffer0;
- true -> <<Buffer0/binary, Data/binary>>
- end,
- case get_data(SOpts, BytesToRead, Buffer1) of
- {ok, ClientData, Buffer} -> % Send data
- SocketOpt = deliver_app_data(Transport, Socket, SOpts, ClientData, Pid, RecvFrom, Tracker),
- cancel_timer(Timer),
- State = State0#state{user_data_buffer = Buffer,
- start_or_recv_from = undefined,
- timer = undefined,
- bytes_to_read = undefined,
- socket_options = SocketOpt
- },
- if
- SocketOpt#socket_options.active =:= false; Buffer =:= <<>> ->
- %% Passive mode, wait for active once or recv
- %% Active and empty, get more data
- next_record_if_active(State);
- true -> %% We have more data
- read_application_data(<<>>, State)
- end;
- {more, Buffer} -> % no reply, we need more data
- next_record(State0#state{user_data_buffer = Buffer});
- {passive, Buffer} ->
- next_record_if_active(State0#state{user_data_buffer = Buffer});
- {error,_Reason} -> %% Invalid packet in packet mode
- deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker),
- {stop, normal, State0}
- end.
-
-%% Picks ClientData
-get_data(_, _, <<>>) ->
- {more, <<>>};
-%% Recv timed out save buffer data until next recv
-get_data(#socket_options{active=false}, undefined, Buffer) ->
- {passive, Buffer};
-get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
- when Raw =:= raw; Raw =:= 0 -> %% Raw Mode
- if
- Active =/= false orelse BytesToRead =:= 0 ->
- %% Active true or once, or passive mode recv(0)
- {ok, Buffer, <<>>};
- byte_size(Buffer) >= BytesToRead ->
- %% Passive Mode, recv(Bytes)
- <<Data:BytesToRead/binary, Rest/binary>> = Buffer,
- {ok, Data, Rest};
- true ->
- %% Passive Mode not enough data
- {more, Buffer}
- end;
-get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) ->
- PacketOpts = [{packet_size, Size}],
- case decode_packet(Type, Buffer, PacketOpts) of
- {more, _} ->
- {more, Buffer};
- Decoded ->
- Decoded
- end.
-
-decode_packet({http, headers}, Buffer, PacketOpts) ->
- decode_packet(httph, Buffer, PacketOpts);
-decode_packet({http_bin, headers}, Buffer, PacketOpts) ->
- decode_packet(httph_bin, Buffer, PacketOpts);
-decode_packet(Type, Buffer, PacketOpts) ->
- erlang:decode_packet(Type, Buffer, PacketOpts).
-
-%% Just like with gen_tcp sockets, an ssl socket that has been configured with
-%% {packet, http} (or {packet, http_bin}) will automatically switch to expect
-%% HTTP headers after it sees a HTTP Request or HTTP Response line. We
-%% represent the current state as follows:
-%% #socket_options.packet =:= http: Expect a HTTP Request/Response line
-%% #socket_options.packet =:= {http, headers}: Expect HTTP Headers
-%% Note that if the user has explicitly configured the socket to expect
-%% HTTP headers using the {packet, httph} option, we don't do any automatic
-%% switching of states.
-deliver_app_data(Transport, Socket, SOpts = #socket_options{active=Active, packet=Type},
- Data, Pid, From, Tracker) ->
- send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data, Tracker)),
- SO = case Data of
- {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)),
- ((Type =:= http) or (Type =:= http_bin)) ->
- SOpts#socket_options{packet={Type, headers}};
- http_eoh when tuple_size(Type) =:= 2 ->
- % End of headers - expect another Request/Response line
- {Type1, headers} = Type,
- SOpts#socket_options{packet=Type1};
- _ ->
- SOpts
- end,
- case Active of
- once ->
- SO#socket_options{active=false};
- _ ->
- SO
- end.
-
-format_reply(_, _,#socket_options{active = false, mode = Mode, packet = Packet,
- header = Header}, Data, _) ->
- {ok, do_format_reply(Mode, Packet, Header, Data)};
-format_reply(Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet,
- header = Header}, Data, Tracker) ->
- {ssl, ssl_socket:socket(self(), Transport, Socket, ?MODULE, Tracker),
- do_format_reply(Mode, Packet, Header, Data)}.
-
-deliver_packet_error(Transport, Socket, SO= #socket_options{active = Active}, Data, Pid, From, Tracker) ->
- send_or_reply(Active, Pid, From, format_packet_error(Transport, Socket, SO, Data, Tracker)).
-
-format_packet_error(_, _,#socket_options{active = false, mode = Mode}, Data, _) ->
- {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}};
-format_packet_error(Transport, Socket, #socket_options{active = _, mode = Mode}, Data, Tracker) ->
- {ssl_error, ssl_socket:socket(self(), Transport, Socket, ?MODULE, Tracker),
- {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}.
-
-do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode
- header(N, Data);
-do_format_reply(binary, _, _, Data) ->
- Data;
-do_format_reply(list, Packet, _, Data)
- when Packet == http; Packet == {http, headers};
- Packet == http_bin; Packet == {http_bin, headers};
- Packet == httph; Packet == httph_bin ->
- Data;
-do_format_reply(list, _,_, Data) ->
- binary_to_list(Data).
-
-header(0, <<>>) ->
- <<>>;
-header(_, <<>>) ->
- [];
-header(0, Binary) ->
- Binary;
-header(N, Binary) ->
- <<?BYTE(ByteN), NewBinary/binary>> = Binary,
- [ByteN | header(N-1, NewBinary)].
-
-send_or_reply(false, _Pid, From, Data) when From =/= undefined ->
- 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, _, _) ->
- ok;
-send_or_reply(_, Pid, _From, Data) ->
- send_user(Pid, Data).
-
-send_user(Pid, Msg) ->
- Pid ! Msg.
-
-tls_handshake_events([]) ->
- throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake));
tls_handshake_events(Packets) ->
lists:map(fun(Packet) ->
{next_event, internal, {handshake, Packet}}
end, Packets).
-write_application_data(Data0, From,
- #state{socket = Socket,
- negotiated_version = Version,
- transport_cb = Transport,
- connection_states = ConnectionStates0,
- 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{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),
- ssl_connection:hibernate_after(connection, State#state{connection_states = ConnectionStates},
- [{reply, From, Result}])
- end.
-
-encode_packet(Data, #socket_options{packet=Packet}) ->
- case Packet of
- 1 -> encode_size_packet(Data, 8, (1 bsl 8) - 1);
- 2 -> encode_size_packet(Data, 16, (1 bsl 16) - 1);
- 4 -> encode_size_packet(Data, 32, (1 bsl 32) - 1);
- _ -> Data
- end.
-encode_size_packet(Bin, Size, Max) ->
- Len = erlang:byte_size(Bin),
- case Len > Max of
- true -> throw({error, {badarg, {packet_to_large, Len, Max}}});
- false -> <<Len:Size, Bin/binary>>
- end.
-
-time_to_renegotiate(_Data,
- #connection_states{current_write =
- #connection_state{sequence_number = Num}},
- RenegotiateAt) ->
-
- %% We could do test:
- %% is_time_to_renegotiate((erlang:byte_size(_Data) div ?MAX_PLAIN_TEXT_LENGTH) + 1, RenegotiateAt),
- %% but we chose to have a some what lower renegotiateAt and a much cheaper test
- is_time_to_renegotiate(Num, RenegotiateAt).
-
-is_time_to_renegotiate(N, M) when N < M->
- false;
-is_time_to_renegotiate(_,_) ->
- true.
renegotiate(#state{role = client} = State, Actions) ->
%% Handle same way as if server requested
%% the renegotiation
@@ -835,8 +664,8 @@ renegotiate(#state{role = server,
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),
+ tls_record:encode_handshake(Frag, Version, ConnectionStates0),
+ send(Transport, Socket, BinMsg),
State1 = State0#state{connection_states =
ConnectionStates,
tls_handshake_history = Hs0},
@@ -848,135 +677,14 @@ handle_alerts([], Result) ->
handle_alerts(_, {stop,_} = Stop) ->
Stop;
handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
- handle_alerts(Alerts, handle_alert(Alert, StateName, State));
+ handle_alerts(Alerts, ssl_connection: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}) ->
- 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};
-
-handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
- StateName, State) ->
- handle_normal_shutdown(Alert, StateName, 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}};
-
-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_statem:reply(From, {error, renegotiation_rejected}),
- {Record, State} = next_record(State0),
- %% Go back to connection!
- next_event(connection, Record, State);
+ handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, 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_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);
-alert_user(Transport, Tracker, Socket,_, _, _, From, Alert, Role) ->
- alert_user(Transport, Tracker, Socket, From, Alert, Role).
-
-alert_user(Transport, Tracker, Socket, From, Alert, Role) ->
- alert_user(Transport, Tracker, Socket, false, no_pid, From, Alert, Role).
-
-alert_user(_, _, _, false = Active, Pid, From, Alert, Role) when From =/= undefined ->
- %% If there is an outstanding ssl_accept | recv
- %% From will be defined and send_or_reply will
- %% send the appropriate error message.
- ReasonCode = ssl_alert:reason_code(Alert, Role),
- send_or_reply(Active, Pid, From, {error, ReasonCode});
-alert_user(Transport, Tracker, Socket, Active, Pid, From, Alert, Role) ->
- case ssl_alert:reason_code(Alert, Role) of
- closed ->
- send_or_reply(Active, Pid, From,
- {ssl_closed, ssl_socket:socket(self(),
- Transport, Socket, ?MODULE, Tracker)});
- ReasonCode ->
- send_or_reply(Active, Pid, From,
- {ssl_error, ssl_socket:socket(self(),
- Transport, Socket, ?MODULE, Tracker), ReasonCode})
- end.
-
-log_alert(true, Info, Alert) ->
- Txt = ssl_alert:alert_txt(Alert),
- error_logger:format("SSL: ~p: ~s\n", [Info, Txt]);
-log_alert(false, _, _) ->
- ok.
-
-handle_own_alert(Alert, Version, StateName,
- #state{transport_cb = Transport,
- socket = Socket,
- connection_states = ConnectionStates,
- ssl_options = SslOpts} = State) ->
- try %% Try to tell the other side
- {BinMsg, _} =
- ssl_alert:encode(Alert, Version, ConnectionStates),
- Transport:send(Socket, BinMsg)
- catch _:_ -> %% Can crash if we are in a uninitialized state
- ignore
- end,
- try %% Try to tell the local user
- log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
- handle_normal_shutdown(Alert,StateName, State)
- catch _:_ ->
- ok
- end,
- {stop, {shutdown, own_alert}}.
-
-handle_normal_shutdown(Alert, _, #state{socket = Socket,
- transport_cb = Transport,
- start_or_recv_from = StartFrom,
- tracker = Tracker,
- role = Role, renegotiation = {false, first}}) ->
- alert_user(Transport, Tracker,Socket, StartFrom, Alert, Role);
-
-handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
- socket_options = Opts,
- transport_cb = Transport,
- user_application = {_Mon, Pid},
- tracker = Tracker,
- start_or_recv_from = RecvFrom, role = Role}) ->
- alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role).
-
-handle_close_alert(Data, StateName, State0) ->
- case next_tls_record(Data, State0) of
- {#ssl_tls{type = ?ALERT, fragment = EncAlerts}, State} ->
- [Alert|_] = decode_alerts(EncAlerts),
- handle_normal_shutdown(Alert, StateName, State);
- _ ->
- ok
- end.
-
-cancel_timer(undefined) ->
- ok;
-cancel_timer(Timer) ->
- erlang:cancel_timer(Timer),
- ok.
-
-invalidate_session(client, Host, Port, Session) ->
- ssl_manager:invalidate_session(Host, Port, Session);
-invalidate_session(server, _, Port, Session) ->
- ssl_manager:invalidate_session(Port, Session).
%% User closes or recursive call!
close({close, Timeout}, Socket, Transport = gen_tcp, _,_) ->
- ssl_socket:setopts(Transport, Socket, [{active, false}]),
+ tls_socket:setopts(Transport, Socket, [{active, false}]),
Transport:shutdown(Socket, write),
_ = Transport:recv(Socket, 0, Timeout),
ok;
@@ -1011,31 +719,66 @@ convert_options_partial_chain(Options, up) ->
convert_options_partial_chain(Options, down) ->
list_to_tuple(proplists:delete(partial_chain, tuple_to_list(Options))).
-handle_sni_extension(#client_hello{extensions = HelloExtensions}, State0) ->
- case HelloExtensions#hello_extensions.sni of
- undefined ->
- State0;
- #sni{hostname = Hostname} ->
- NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname),
- case NewOptions of
- undefined ->
- State0;
- _ ->
- {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbHandle, OwnCert, Key, DHParams} =
- ssl_config:init(NewOptions, State0#state.role),
- State0#state{
- session = State0#state.session#session{own_certificate = OwnCert},
- file_ref_db = FileRefHandle,
- cert_db_ref = Ref,
- cert_db = CertDbHandle,
- crl_db = CRLDbHandle,
- session_cache = CacheHandle,
- private_key = Key,
- diffie_hellman_params = DHParams,
- ssl_options = NewOptions,
- sni_hostname = Hostname
- }
- end
+gen_handshake(GenConnection, StateName, Type, Event,
+ #state{negotiated_version = Version} = State) ->
+ try GenConnection:StateName(Type, Event, State, ?MODULE) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
+ malformed_handshake_data),
+ Version, StateName, State)
+ end.
+
+gen_info(Event, connection = StateName, #state{negotiated_version = Version} = State) ->
+ try handle_info(Event, StateName, State) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR,
+ malformed_data),
+ Version, StateName, State)
end;
-handle_sni_extension(_, State) ->
- State.
+
+gen_info(Event, StateName, #state{negotiated_version = Version} = State) ->
+ try handle_info(Event, StateName, State) of
+ Result ->
+ Result
+ catch
+ _:_ ->
+ ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
+ malformed_handshake_data),
+ Version, StateName, State)
+ end.
+
+unprocessed_events(Events) ->
+ %% The first handshake event will be processed immediately
+ %% as it is entered first in the event queue and
+ %% when it is processed there will be length(Events)-1
+ %% handshake events left to process before we should
+ %% process more TLS-records received on the socket.
+ erlang:length(Events)-1.
+
+
+assert_buffer_sanity(<<?BYTE(_Type), ?UINT24(Length), Rest/binary>>, #ssl_options{max_handshake_size = Max}) when
+ Length =< Max ->
+ case size(Rest) of
+ N when N < Length ->
+ true;
+ N when N > Length ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
+ too_big_handshake_data));
+ _ ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
+ malformed_handshake_data))
+ end;
+assert_buffer_sanity(Bin, _) ->
+ case size(Bin) of
+ N when N < 3 ->
+ true;
+ _ ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
+ malformed_handshake_data))
+ end.
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 566b7db332..80d0239498 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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.
@@ -41,7 +41,7 @@
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
--spec client_hello(host(), inet:port_number(), #connection_states{},
+-spec client_hello(host(), inet:port_number(), ssl_record:connection_states(),
#ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
@@ -54,8 +54,7 @@ client_hello(Host, Port, ConnectionStates,
} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
Version = tls_record:highest_protocol_version(Versions),
- Pending = ssl_record:pending_connection_state(ConnectionStates, read),
- SecParams = Pending#connection_state.security_parameters,
+ #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read),
AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version),
Extensions = ssl_handshake:client_hello_extensions(Host, Version,
AvailableCipherSuites,
@@ -78,14 +77,14 @@ client_hello(Host, Port, ConnectionStates,
%%--------------------------------------------------------------------
-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
- #connection_states{} | {inet:port_number(), #session{}, db_handle(),
- atom(), #connection_states{},
+ ssl_record:connection_states() | {inet:port_number(), #session{}, db_handle(),
+ atom(), ssl_record:connection_states(),
binary() | undefined, ssl_cipher:key_algo()},
boolean()) ->
{tls_record:tls_version(), session_id(),
- #connection_states{}, alpn | npn, binary() | undefined}|
+ ssl_record:connection_states(), alpn | npn, binary() | undefined}|
{tls_record:tls_version(), {resumed | new, #session{}},
- #connection_states{}, binary() | undefined,
+ ssl_record:connection_states(), binary() | undefined,
#hello_extensions{}, {ssl_cipher:hash(), ssl_cipher:sign_algo()} | undefined} |
#alert{}.
%%
@@ -109,19 +108,25 @@ hello(#client_hello{client_version = ClientVersion,
cipher_suites = CipherSuites} = Hello,
#ssl_options{versions = Versions} = SslOpts,
Info, Renegotiation) ->
- Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions),
- case ssl_cipher:is_fallback(CipherSuites) of
+ try
+ Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions),
+ case ssl_cipher:is_fallback(CipherSuites) of
true ->
- Highest = tls_record:highest_protocol_version(Versions),
- case tls_record:is_higher(Highest, Version) of
- true ->
- ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK);
- false ->
- handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation)
- end;
- false ->
- handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation)
- end.
+ Highest = tls_record:highest_protocol_version(Versions),
+ case tls_record:is_higher(Highest, Version) of
+ true ->
+ ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK);
+ false ->
+ handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation)
+ end;
+ false ->
+ handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation)
+ end
+ catch
+ _:_ ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data)
+ end.
+
%%--------------------------------------------------------------------
-spec encode_handshake(tls_handshake(), tls_record:tls_version()) -> iolist().
%%
@@ -155,13 +160,15 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId,
extensions = #hello_extensions{elliptic_curves = Curves,
signature_algs = ClientHashSigns} = HelloExt},
#ssl_options{versions = Versions,
- signature_algs = SupportedHashSigns} = SslOpts,
+ signature_algs = SupportedHashSigns,
+ eccs = SupportedECCs,
+ honor_ecc_order = ECCOrder} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) ->
case tls_record:is_acceptable_version(Version, Versions) of
true ->
AvailableHashSigns = ssl_handshake:available_signature_algs(
ClientHashSigns, SupportedHashSigns, Cert, Version),
- ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(Version)),
+ ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder),
{Type, #session{cipher_suite = CipherSuite} = Session1}
= ssl_handshake:select_session(SugesstedId, CipherSuites, AvailableHashSigns, Compressions,
Port, Session0#session{ecc = ECCCurve}, Version,
@@ -185,37 +192,33 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId,
end.
get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),
- Body:Length/binary,Rest/binary>>, #ssl_options{v2_hello_compatible = V2Hello} = Opts, Acc) ->
+ Body:Length/binary,Rest/binary>>,
+ #ssl_options{v2_hello_compatible = V2Hello} = Opts, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
- Handshake = decode_handshake(Version, Type, Body, V2Hello),
- get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]);
+ try decode_handshake(Version, Type, Body, V2Hello) of
+ Handshake ->
+ get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc])
+ catch
+ _:_ ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, handshake_decode_error))
+ end;
get_tls_handshake_aux(_Version, Data, _, Acc) ->
{lists:reverse(Acc), Data}.
decode_handshake(_, ?HELLO_REQUEST, <<>>, _) ->
#hello_request{};
-%% Client hello v2.
-%% The server must be able to receive such messages, from clients that
-%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
-decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>, true) ->
- #client_hello{client_version = {Major, Minor},
- random = ssl_v2:client_random(ChallengeData, CDLength),
- session_id = 0,
- cipher_suites = ssl_handshake:decode_suites('3_bytes', CipherSuites),
- compression_methods = [?NULL],
- extensions = #hello_extensions{}
- };
-decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(_), ?BYTE(_),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- _CipherSuites:CSLength/binary,
- _ChallengeData:CDLength/binary>>, false) ->
- throw(?ALERT_REC(?FATAL, ?PROTOCOL_VERSION, ssl_v2_client_hello_no_supported));
+decode_handshake(_Version, ?CLIENT_HELLO, Bin, true) ->
+ try decode_hello(Bin) of
+ Hello ->
+ Hello
+ catch
+ _:_ ->
+ decode_v2_hello(Bin)
+ end;
+decode_handshake(_Version, ?CLIENT_HELLO, Bin, false) ->
+ decode_hello(Bin);
+
decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
@@ -232,10 +235,40 @@ decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:3
compression_methods = Comp_methods,
extensions = DecodedExtensions
};
-
decode_handshake(Version, Tag, Msg, _) ->
ssl_handshake:decode_handshake(Version, Tag, Msg).
+
+decode_hello(<<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ ?BYTE(SID_length), Session_ID:SID_length/binary,
+ ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
+ ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
+ Extensions/binary>>) ->
+ DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}),
+
+ #client_hello{
+ client_version = {Major,Minor},
+ random = Random,
+ session_id = Session_ID,
+ cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites),
+ compression_methods = Comp_methods,
+ extensions = DecodedExtensions
+ }.
+%% The server must be able to receive such messages, from clients that
+%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
+decode_v2_hello(<<?BYTE(Major), ?BYTE(Minor),
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>) ->
+ #client_hello{client_version = {Major, Minor},
+ random = ssl_v2:client_random(ChallengeData, CDLength),
+ session_id = 0,
+ cipher_suites = ssl_handshake:decode_suites('3_bytes', CipherSuites),
+ compression_methods = [?NULL],
+ extensions = #hello_extensions{}
+ }.
+
enc_handshake(#hello_request{}, _Version) ->
{?HELLO_REQUEST, <<>>};
enc_handshake(#client_hello{client_version = {Major, Minor},
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 9348c8bbdd..993a1622fe 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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,12 +32,11 @@
-include("ssl_cipher.hrl").
%% Handling of incoming data
--export([get_tls_records/2]).
+-export([get_tls_records/2, init_connection_states/2]).
-%% Decoding
--export([decode_cipher_text/3]).
-
-%% Encoding
+%% Encoding TLS records
+-export([encode_handshake/3, encode_alert_record/3,
+ encode_change_cipher_spec/2, encode_data/3]).
-export([encode_plain_text/4]).
%% Protocol version handling
@@ -46,6 +45,9 @@
is_higher/2, supported_protocol_versions/0,
is_acceptable_version/1, is_acceptable_version/2]).
+%% Decoding
+-export([decode_cipher_text/3]).
+
-export_type([tls_version/0, tls_atom_version/0]).
-type tls_version() :: ssl_record:ssl_version().
@@ -56,12 +58,28 @@
%%====================================================================
%% Internal application API
%%====================================================================
+%%--------------------------------------------------------------------
+-spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled) ->
+ ssl_record:connection_states().
+%% %
+ %
+%% Description: Creates a connection_states record with appropriate
+%% values for the initial SSL connection setup.
+%%--------------------------------------------------------------------
+init_connection_states(Role, BeastMitigation) ->
+ ConnectionEnd = ssl_record:record_protocol_role(Role),
+ Current = initial_connection_state(ConnectionEnd, BeastMitigation),
+ Pending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation),
+ #{current_read => Current,
+ pending_read => Pending,
+ current_write => Current,
+ pending_write => Pending}.
%%--------------------------------------------------------------------
-spec get_tls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}.
%%
-%% Description: Given old buffer and new data from TCP, packs up a records
%% and returns it as a list of tls_compressed binaries also returns leftover
+%% Description: Given old buffer and new data from TCP, packs up a records
%% data
%%--------------------------------------------------------------------
get_tls_records(Data, <<>>) ->
@@ -69,155 +87,61 @@ get_tls_records(Data, <<>>) ->
get_tls_records(Data, Buffer) ->
get_tls_records_aux(list_to_binary([Buffer, Data]), []).
-get_tls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Length), Data:Length/binary, Rest/binary>>,
- Acc) ->
- get_tls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
-get_tls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Length),
- Data:Length/binary, Rest/binary>>, Acc) ->
- get_tls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
-get_tls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Length), Data:Length/binary,
- Rest/binary>>, Acc) ->
- get_tls_records_aux(Rest, [#ssl_tls{type = ?ALERT,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
-get_tls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Length), Data:Length/binary, Rest/binary>>,
- Acc) ->
- get_tls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
-%% Matches an ssl v2 client hello message.
-%% The server must be able to receive such messages, from clients that
-%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
-get_tls_records_aux(<<1:1, Length0:15, Data0:Length0/binary, Rest/binary>>,
- Acc) ->
- case Data0 of
- <<?BYTE(?CLIENT_HELLO), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>> ->
- Length = Length0-1,
- <<?BYTE(_), Data1:Length/binary>> = Data0,
- Data = <<?BYTE(?CLIENT_HELLO), ?UINT24(Length), Data1/binary>>,
- get_tls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
- _ ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
-
- end;
-
-get_tls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
- ?UINT16(Length), _/binary>>,
- _Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH ->
- ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
+%%--------------------------------------------------------------------
+-spec encode_handshake(iolist(), tls_version(), ssl_record:connection_states()) ->
+ {iolist(), ssl_record:connection_states()}.
+%
+%% Description: Encodes a handshake message to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_handshake(Frag, Version,
+ #{current_write :=
+ #{beast_mitigation := BeastMitigation,
+ security_parameters :=
+ #security_parameters{bulk_cipher_algorithm = BCA}}} =
+ ConnectionStates) ->
+ case iolist_size(Frag) of
+ N when N > ?MAX_PLAIN_TEXT_LENGTH ->
+ Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation),
+ encode_iolist(?HANDSHAKE, Data, Version, ConnectionStates);
+ _ ->
+ encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates)
+ end.
-get_tls_records_aux(<<1:1, Length0:15, _/binary>>,_Acc)
- when Length0 > ?MAX_CIPHER_TEXT_LENGTH ->
- ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
+%%--------------------------------------------------------------------
+-spec encode_alert_record(#alert{}, tls_version(), ssl_record:connection_states()) ->
+ {iolist(), ssl_record:connection_states()}.
+%%
+%% Description: Encodes an alert message to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_alert_record(#alert{level = Level, description = Description},
+ Version, ConnectionStates) ->
+ encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>,
+ ConnectionStates).
-get_tls_records_aux(Data, Acc) ->
- case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of
- true ->
- {lists:reverse(Acc), Data};
- false ->
- ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
- end.
+%%--------------------------------------------------------------------
+-spec encode_change_cipher_spec(tls_version(), ssl_record:connection_states()) ->
+ {iolist(), ssl_record:connection_states()}.
+%%
+%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
+%%--------------------------------------------------------------------
+encode_change_cipher_spec(Version, ConnectionStates) ->
+ encode_plain_text(?CHANGE_CIPHER_SPEC, Version, ?byte(?CHANGE_CIPHER_SPEC_PROTO), ConnectionStates).
-encode_plain_text(Type, Version, Data,
- #connection_states{current_write =
- #connection_state{
- sequence_number = Seq,
- compression_state=CompS0,
- security_parameters=
- #security_parameters{
- cipher_type = ?AEAD,
- compression_algorithm=CompAlg}
- }= WriteState0} = ConnectionStates) ->
- {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#connection_state{compression_state = CompS1},
- AAD = calc_aad(Type, Version, WriteState1),
- {CipherFragment, WriteState} = ssl_record:cipher_aead(Version, Comp, WriteState1, AAD),
- CipherText = encode_tls_cipher_text(Type, Version, CipherFragment),
- {CipherText, ConnectionStates#connection_states{current_write = WriteState#connection_state{sequence_number = Seq +1}}};
-
-encode_plain_text(Type, Version, Data,
- #connection_states{current_write =
- #connection_state{
- sequence_number = Seq,
- compression_state=CompS0,
- security_parameters=
- #security_parameters{compression_algorithm=CompAlg}
- }= WriteState0} = ConnectionStates) ->
- {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
- WriteState1 = WriteState0#connection_state{compression_state = CompS1},
- MacHash = calc_mac_hash(Type, Version, Comp, WriteState1),
- {CipherFragment, WriteState} = ssl_record:cipher(Version, Comp, WriteState1, MacHash),
- CipherText = encode_tls_cipher_text(Type, Version, CipherFragment),
- {CipherText, ConnectionStates#connection_states{current_write = WriteState#connection_state{sequence_number = Seq +1}}}.
-
-%%--------------------------------------------------------------------
--spec decode_cipher_text(#ssl_tls{}, #connection_states{}, boolean()) ->
- {#ssl_tls{}, #connection_states{}}| #alert{}.
+%%--------------------------------------------------------------------
+-spec encode_data(binary(), tls_version(), ssl_record:connection_states()) ->
+ {iolist(), ssl_record:connection_states()}.
%%
-%% Description: Decode cipher text
+%% Description: Encodes data to send on the ssl-socket.
%%--------------------------------------------------------------------
-decode_cipher_text(#ssl_tls{type = Type, version = Version,
- fragment = CipherFragment} = CipherText,
- #connection_states{current_read =
- #connection_state{
- compression_state = CompressionS0,
- sequence_number = Seq,
- security_parameters=
- #security_parameters{
- cipher_type = ?AEAD,
- compression_algorithm=CompAlg}
- } = ReadState0} = ConnnectionStates0, _) ->
- AAD = calc_aad(Type, Version, ReadState0),
- case ssl_record:decipher_aead(Version, CipherFragment, ReadState0, AAD) of
- {PlainFragment, ReadState1} ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
- PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- sequence_number = Seq + 1,
- compression_state = CompressionS1}},
- {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
- #alert{} = Alert ->
- Alert
- end;
+encode_data(Frag, Version,
+ #{current_write := #{beast_mitigation := BeastMitigation,
+ security_parameters :=
+ #security_parameters{bulk_cipher_algorithm = BCA}}} =
+ ConnectionStates) ->
+ Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation),
+ encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates).
+
-decode_cipher_text(#ssl_tls{type = Type, version = Version,
- fragment = CipherFragment} = CipherText,
- #connection_states{current_read =
- #connection_state{
- compression_state = CompressionS0,
- sequence_number = Seq,
- security_parameters=
- #security_parameters{compression_algorithm=CompAlg}
- } = ReadState0} = ConnnectionStates0, PaddingCheck) ->
- case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of
- {PlainFragment, Mac, ReadState1} ->
- MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState1),
- case ssl_record:is_correct_mac(Mac, MacHash) of
- true ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
- PlainFragment, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- sequence_number = Seq + 1,
- compression_state = CompressionS1}},
- {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
- false ->
- ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
- end;
- #alert{} = Alert ->
- Alert
- end.
%%--------------------------------------------------------------------
-spec protocol_version(tls_atom_version() | tls_version()) ->
tls_version() | tls_atom_version().
@@ -375,6 +299,82 @@ is_acceptable_version(_,_) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+initial_connection_state(ConnectionEnd, BeastMitigation) ->
+ #{security_parameters =>
+ ssl_record:initial_security_params(ConnectionEnd),
+ sequence_number => 0,
+ beast_mitigation => BeastMitigation,
+ compression_state => undefined,
+ cipher_state => undefined,
+ mac_secret => undefined,
+ secure_renegotiation => undefined,
+ client_verify_data => undefined,
+ server_verify_data => undefined
+ }.
+
+get_tls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Length), Data:Length/binary, Rest/binary>>,
+ Acc) ->
+ get_tls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA,
+ version = {MajVer, MinVer},
+ fragment = Data} | Acc]);
+get_tls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Length),
+ Data:Length/binary, Rest/binary>>, Acc) ->
+ get_tls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
+ version = {MajVer, MinVer},
+ fragment = Data} | Acc]);
+get_tls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Length), Data:Length/binary,
+ Rest/binary>>, Acc) ->
+ get_tls_records_aux(Rest, [#ssl_tls{type = ?ALERT,
+ version = {MajVer, MinVer},
+ fragment = Data} | Acc]);
+get_tls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Length), Data:Length/binary, Rest/binary>>,
+ Acc) ->
+ get_tls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
+ version = {MajVer, MinVer},
+ fragment = Data} | Acc]);
+%% Matches an ssl v2 client hello message.
+%% The server must be able to receive such messages, from clients that
+%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
+get_tls_records_aux(<<1:1, Length0:15, Data0:Length0/binary, Rest/binary>>,
+ Acc) ->
+ case Data0 of
+ <<?BYTE(?CLIENT_HELLO), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>> ->
+ Length = Length0-1,
+ <<?BYTE(_), Data1:Length/binary>> = Data0,
+ Data = <<?BYTE(?CLIENT_HELLO), ?UINT24(Length), Data1/binary>>,
+ get_tls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
+ version = {MajVer, MinVer},
+ fragment = Data} | Acc]);
+ _ ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+
+ end;
+
+get_tls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
+ ?UINT16(Length), _/binary>>,
+ _Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH ->
+ ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
+
+get_tls_records_aux(<<1:1, Length0:15, _/binary>>,_Acc)
+ when Length0 > ?MAX_CIPHER_TEXT_LENGTH ->
+ ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
+
+get_tls_records_aux(Data, Acc) ->
+ case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of
+ true ->
+ {lists:reverse(Acc), Data};
+ false ->
+ ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
+ end.
+
+encode_plain_text(Type, Version, Data, #{current_write := Write0} = ConnectionStates) ->
+ {CipherFragment, Write1} = ssl_record:encode_plain_text(Type, Version, Data, Write0),
+ {CipherText, Write} = encode_tls_cipher_text(Type, Version, CipherFragment, Write1),
+ {CipherText, ConnectionStates#{current_write => Write}}.
lowest_list_protocol_version(Ver, []) ->
Ver;
@@ -386,20 +386,10 @@ highest_list_protocol_version(Ver, []) ->
highest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
highest_list_protocol_version(highest_protocol_version(Ver1, Ver2), Rest).
-encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
+encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment, #{sequence_number := Seq} = Write) ->
Length = erlang:iolist_size(Fragment),
- [<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment].
-
-
-mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
- _Length, _Fragment) ->
- <<>>;
-mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
- ssl_v3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment);
-mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
- when N =:= 1; N =:= 2; N =:= 3 ->
- tls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
- Length, Fragment).
+ {[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment],
+ Write#{sequence_number => Seq +1}}.
highest_protocol_version() ->
highest_protocol_version(supported_protocol_versions()).
@@ -407,21 +397,96 @@ highest_protocol_version() ->
lowest_protocol_version() ->
lowest_protocol_version(supported_protocol_versions()).
-
sufficient_tlsv1_2_crypto_support() ->
CryptoSupport = crypto:supports(),
proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
-calc_mac_hash(Type, Version,
- PlainFragment, #connection_state{sequence_number = SeqNo,
- mac_secret = MacSecret,
- security_parameters =
- SecPars}) ->
- Length = erlang:iolist_size(PlainFragment),
- mac_hash(Version, SecPars#security_parameters.mac_algorithm,
- MacSecret, SeqNo, Type,
- Length, PlainFragment).
-
-calc_aad(Type, {MajVer, MinVer},
- #connection_state{sequence_number = SeqNo}) ->
- <<SeqNo:64/integer, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>.
+encode_iolist(Type, Data, Version, ConnectionStates0) ->
+ {ConnectionStates, EncodedMsg} =
+ lists:foldl(fun(Text, {CS0, Encoded}) ->
+ {Enc, CS1} =
+ encode_plain_text(Type, Version, Text, CS0),
+ {CS1, [Enc | Encoded]}
+ end, {ConnectionStates0, []}, Data),
+ {lists:reverse(EncodedMsg), ConnectionStates}.
+
+%% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are
+%% not vulnerable to this attack.
+split_bin(<<FirstByte:8, Rest/binary>>, ChunkSize, Version, BCA, one_n_minus_one) when
+ BCA =/= ?RC4 andalso ({3, 1} == Version orelse
+ {3, 0} == Version) ->
+ do_split_bin(Rest, ChunkSize, [[FirstByte]]);
+%% 0/n splitting countermeasure for clients that are incompatible with 1/n-1
+%% splitting.
+split_bin(Bin, ChunkSize, Version, BCA, zero_n) when
+ BCA =/= ?RC4 andalso ({3, 1} == Version orelse
+ {3, 0} == Version) ->
+ do_split_bin(Bin, ChunkSize, [[<<>>]]);
+split_bin(Bin, ChunkSize, _, _, _) ->
+ do_split_bin(Bin, ChunkSize, []).
+
+do_split_bin(<<>>, _, Acc) ->
+ lists:reverse(Acc);
+do_split_bin(Bin, ChunkSize, Acc) ->
+ case Bin of
+ <<Chunk:ChunkSize/binary, Rest/binary>> ->
+ do_split_bin(Rest, ChunkSize, [Chunk | Acc]);
+ _ ->
+ lists:reverse(Acc, [Bin])
+ end.
+
+%%--------------------------------------------------------------------
+-spec decode_cipher_text(#ssl_tls{}, ssl_record:connection_states(), boolean()) ->
+ {#ssl_tls{}, ssl_record:connection_states()}| #alert{}.
+%%
+%% Description: Decode cipher text
+%%--------------------------------------------------------------------
+decode_cipher_text(#ssl_tls{type = Type, version = Version,
+ fragment = CipherFragment} = CipherText,
+ #{current_read :=
+ #{compression_state := CompressionS0,
+ sequence_number := Seq,
+ security_parameters :=
+ #security_parameters{
+ cipher_type = ?AEAD,
+ compression_algorithm = CompAlg}
+ } = ReadState0} = ConnnectionStates0, _) ->
+ AAD = ssl_cipher:calc_aad(Type, Version, ReadState0),
+ case ssl_record:decipher_aead(Version, CipherFragment, ReadState0, AAD) of
+ {PlainFragment, ReadState1} ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
+ PlainFragment, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#{
+ current_read => ReadState1#{sequence_number => Seq + 1,
+ compression_state => CompressionS1}},
+ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end;
+
+decode_cipher_text(#ssl_tls{type = Type, version = Version,
+ fragment = CipherFragment} = CipherText,
+ #{current_read :=
+ #{compression_state := CompressionS0,
+ sequence_number := Seq,
+ security_parameters :=
+ #security_parameters{compression_algorithm = CompAlg}
+ } = ReadState0} = ConnnectionStates0, PaddingCheck) ->
+ case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of
+ {PlainFragment, Mac, ReadState1} ->
+ MacHash = ssl_cipher:calc_mac_hash(Type, Version, PlainFragment, ReadState1),
+ case ssl_record:is_correct_mac(Mac, MacHash) of
+ true ->
+ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg,
+ PlainFragment, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#{
+ current_read => ReadState1#{
+ sequence_number => Seq + 1,
+ compression_state => CompressionS1}},
+ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates};
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end;
+ #alert{} = Alert ->
+ Alert
+ end.
diff --git a/lib/ssl/src/ssl_socket.erl b/lib/ssl/src/tls_socket.erl
index b2aea2ba9c..e76d9c100a 100644
--- a/lib/ssl/src/ssl_socket.erl
+++ b/lib/ssl/src/tls_socket.erl
@@ -17,16 +17,19 @@
%%
%% %CopyrightEnd%
%%
--module(ssl_socket).
+-module(tls_socket).
-behaviour(gen_server).
-include("ssl_internal.hrl").
-include("ssl_api.hrl").
--export([socket/5, setopts/3, getopts/3, getstat/3, peername/2, sockname/2, port/2]).
+-export([send/3, listen/3, accept/3, socket/5, connect/4, upgrade/3,
+ setopts/3, getopts/3, getstat/3, peername/2, sockname/2, port/2]).
+-export([split_options/1, get_socket_opts/3]).
-export([emulated_options/0, internal_inet_values/0, default_inet_values/0,
- init/1, start_link/3, terminate/2, inherit_tracker/3, get_emulated_opts/1,
+ init/1, start_link/3, terminate/2, inherit_tracker/3,
+ emulated_socket_options/2, get_emulated_opts/1,
set_emulated_opts/2, get_all_opts/1, handle_call/3, handle_cast/2,
handle_info/2, code_change/3]).
@@ -39,6 +42,76 @@
%%--------------------------------------------------------------------
%%% Internal API
%%--------------------------------------------------------------------
+send(Transport, Socket, Data) ->
+ Transport:send(Socket, Data).
+
+listen(Transport, Port, #config{transport_info = {Transport, _, _, _},
+ inet_user = Options,
+ ssl = SslOpts, emulated = EmOpts} = Config) ->
+ case Transport:listen(Port, Options ++ internal_inet_values()) of
+ {ok, ListenSocket} ->
+ {ok, Tracker} = inherit_tracker(ListenSocket, EmOpts, SslOpts),
+ {ok, #sslsocket{pid = {ListenSocket, Config#config{emulated = Tracker}}}};
+ Err = {error, _} ->
+ Err
+ end.
+
+accept(ListenSocket, #config{transport_info = {Transport,_,_,_} = CbInfo,
+ connection_cb = ConnectionCb,
+ ssl = SslOpts,
+ emulated = Tracker}, Timeout) ->
+ case Transport:accept(ListenSocket, Timeout) of
+ {ok, Socket} ->
+ {ok, EmOpts} = get_emulated_opts(Tracker),
+ {ok, Port} = tls_socket:port(Transport, Socket),
+ ConnArgs = [server, "localhost", Port, Socket,
+ {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), Tracker}, self(), CbInfo],
+ case tls_connection_sup:start_child(ConnArgs) of
+ {ok, Pid} ->
+ ssl_connection:socket_control(ConnectionCb, Socket, Pid, Transport, Tracker);
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+upgrade(Socket, #config{transport_info = {Transport,_,_,_}= CbInfo,
+ ssl = SslOptions,
+ emulated = EmOpts, connection_cb = ConnectionCb}, Timeout) ->
+ ok = setopts(Transport, Socket, tls_socket:internal_inet_values()),
+ case peername(Transport, Socket) of
+ {ok, {Address, Port}} ->
+ ssl_connection:connect(ConnectionCb, Address, Port, Socket,
+ {SslOptions,
+ emulated_socket_options(EmOpts, #socket_options{}), undefined},
+ self(), CbInfo, Timeout);
+ {error, Error} ->
+ {error, Error}
+ end.
+
+connect(Address, Port,
+ #config{transport_info = CbInfo, inet_user = UserOpts, ssl = SslOpts,
+ emulated = EmOpts, inet_ssl = SocketOpts, connection_cb = ConnetionCb},
+ Timeout) ->
+ {Transport, _, _, _} = CbInfo,
+ try Transport:connect(Address, Port, SocketOpts, Timeout) of
+ {ok, Socket} ->
+ ssl_connection:connect(ConnetionCb, Address, Port, Socket,
+ {SslOpts,
+ emulated_socket_options(EmOpts, #socket_options{}), undefined},
+ self(), CbInfo, Timeout);
+ {error, Reason} ->
+ {error, Reason}
+ catch
+ exit:{function_clause, _} ->
+ {error, {options, {cb_info, CbInfo}}};
+ exit:badarg ->
+ {error, {options, {socket_options, UserOpts}}};
+ exit:{badarg, _} ->
+ {error, {options, {socket_options, UserOpts}}}
+ end.
+
socket(Pid, Transport, Socket, ConnectionCb, Tracker) ->
#sslsocket{pid = Pid,
%% "The name "fd" is keept for backwards compatibility
@@ -241,3 +314,17 @@ get_emulated_opts(TrackerPid, EmOptNames) ->
lists:map(fun(Name) -> {value, Value} = lists:keysearch(Name, 1, EmOpts),
Value end,
EmOptNames).
+
+emulated_socket_options(InetValues, #socket_options{
+ mode = Mode,
+ header = Header,
+ active = Active,
+ packet = Packet,
+ packet_size = Size}) ->
+ #socket_options{
+ mode = proplists:get_value(mode, InetValues, Mode),
+ header = proplists:get_value(header, InetValues, Header),
+ active = proplists:get_value(active, InetValues, Active),
+ packet = proplists:get_value(packet, InetValues, Packet),
+ packet_size = proplists:get_value(packet_size, InetValues, Size)
+ }.
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index 711db77708..6797b290d4 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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.
@@ -31,9 +31,18 @@
-export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7,
setup_keys/8, suites/1, prf/5,
- ecc_curves/1, oid_to_enum/1, enum_to_oid/1,
+ ecc_curves/1, ecc_curves/2, oid_to_enum/1, enum_to_oid/1,
default_signature_algs/1, signature_algs/2]).
+-type named_curve() :: sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1 |
+ sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1 |
+ sect283k1 | sect283r1 | brainpoolP256r1 | secp256k1 | secp256r1 |
+ sect239k1 | sect233k1 | sect233r1 | secp224k1 | secp224r1 |
+ sect193r1 | sect193r2 | secp192k1 | secp192r1 | sect163k1 |
+ sect163r1 | sect163r2 | secp160k1 | secp160r1 | secp160r2.
+-type curves() :: [named_curve()].
+-export_type([curves/0, named_curve/0]).
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -195,21 +204,21 @@ suites(Minor) when Minor == 1; Minor == 2 ->
?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
?TLS_RSA_WITH_AES_256_CBC_SHA,
- ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
- ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
- ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
- ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
- ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
- ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
- ?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
-
?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
- ?TLS_RSA_WITH_AES_128_CBC_SHA
+ ?TLS_RSA_WITH_AES_128_CBC_SHA,
+
+ ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_WITH_3DES_EDE_CBC_SHA
];
suites(3) ->
[
@@ -399,13 +408,20 @@ is_pair(Hash, rsa, Hashs) ->
lists:member(Hash, AtLeastMd5).
%% list ECC curves in prefered order
-ecc_curves(_Minor) ->
- TLSCurves = [sect571r1,sect571k1,secp521r1,brainpoolP512r1,
- sect409k1,sect409r1,brainpoolP384r1,secp384r1,
- sect283k1,sect283r1,brainpoolP256r1,secp256k1,secp256r1,
- sect239k1,sect233k1,sect233r1,secp224k1,secp224r1,
- sect193r1,sect193r2,secp192k1,secp192r1,sect163k1,
- sect163r1,sect163r2,secp160k1,secp160r1,secp160r2],
+-spec ecc_curves(1..3 | all) -> [named_curve()].
+ecc_curves(all) ->
+ [sect571r1,sect571k1,secp521r1,brainpoolP512r1,
+ sect409k1,sect409r1,brainpoolP384r1,secp384r1,
+ sect283k1,sect283r1,brainpoolP256r1,secp256k1,secp256r1,
+ sect239k1,sect233k1,sect233r1,secp224k1,secp224r1,
+ sect193r1,sect193r2,secp192k1,secp192r1,sect163k1,
+ sect163r1,sect163r2,secp160k1,secp160r1,secp160r2];
+ecc_curves(Minor) ->
+ TLSCurves = ecc_curves(all),
+ ecc_curves(Minor, TLSCurves).
+
+-spec ecc_curves(1..3, [named_curve()]) -> [named_curve()].
+ecc_curves(_Minor, TLSCurves) ->
CryptoCurves = crypto:ec_curves(),
lists:foldr(fun(Curve, Curves) ->
case proplists:get_bool(Curve, CryptoCurves) of
@@ -414,6 +430,7 @@ ecc_curves(_Minor) ->
end
end, [], TLSCurves).
+
%% ECC curves from draft-ietf-tls-ecc-12.txt (Oct. 17, 2005)
oid_to_enum(?sect163k1) -> 1;
oid_to_enum(?sect163r1) -> 2;
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 009bcd81ad..e14f7f60c4 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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.
@@ -172,8 +172,8 @@ revoke(Root, CA, User, C) ->
gencrl(Root, CA, C).
gencrl(Root, CA, C) ->
- %% By default, the CRL is valid for 24 hours from now.
- gencrl(Root, CA, C, 24).
+ %% By default, the CRL is valid for a week from now.
+ gencrl(Root, CA, C, 24*7).
gencrl(Root, CA, C, CrlHours) ->
CACnfFile = filename:join([Root, CA, "ca.cnf"]),
diff --git a/lib/ssl/test/ssl.spec b/lib/ssl/test/ssl.spec
index 86e14c033e..0ad94e22bc 100644
--- a/lib/ssl/test/ssl.spec
+++ b/lib/ssl/test/ssl.spec
@@ -1,4 +1,5 @@
{suites,"../ssl_test",all}.
{skip_cases, "../ssl_test",
- ssl_bench_SUITE, [setup_sequential, setup_concurrent, payload_simple],
+ ssl_bench_SUITE, [setup_sequential, setup_concurrent, payload_simple,
+ use_pem_cache, bypass_pem_cache],
"Benchmarks run separately"}.
diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl
index b8a03f578d..f779765b18 100644
--- a/lib/ssl/test/ssl_ECC_SUITE.erl
+++ b/lib/ssl/test/ssl_ECC_SUITE.erl
@@ -46,7 +46,7 @@ groups() ->
{'tlsv1', [], all_versions_groups()},
{'erlang_server', [], key_cert_combinations()},
{'erlang_client', [], key_cert_combinations()},
- {'erlang', [], key_cert_combinations() ++ misc()}
+ {'erlang', [], key_cert_combinations() ++ misc() ++ ecc_negotiation()}
].
all_versions_groups ()->
@@ -68,6 +68,23 @@ key_cert_combinations() ->
misc()->
[client_ecdsa_server_ecdsa_with_raw_key].
+ecc_negotiation() ->
+ [ecc_default_order,
+ ecc_default_order_custom_curves,
+ ecc_client_order,
+ ecc_client_order_custom_curves,
+ ecc_unknown_curve,
+ client_ecdh_server_ecdh_ecc_server_custom,
+ client_rsa_server_ecdh_ecc_server_custom,
+ client_ecdh_server_rsa_ecc_server_custom,
+ client_rsa_server_rsa_ecc_server_custom,
+ client_ecdsa_server_ecdsa_ecc_server_custom,
+ client_ecdsa_server_rsa_ecc_server_custom,
+ client_rsa_server_ecdsa_ecc_server_custom,
+ client_ecdsa_server_ecdsa_ecc_client_custom,
+ client_rsa_server_ecdsa_ecc_client_custom
+ ].
+
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
end_per_suite(Config0),
@@ -145,7 +162,7 @@ init_per_testcase(TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:log("Ciphers: ~p~n ", [ ssl:cipher_suites()]),
end_per_testcase(TestCase, Config),
- ssl:start(),
+ ssl_test_lib:clean_start(),
ct:timetrap({seconds, 15}),
Config.
@@ -159,42 +176,42 @@ end_per_testcase(_TestCase, Config) ->
client_ecdh_server_ecdh(Config) when is_list(Config) ->
COpts = proplists:get_value(client_ecdh_rsa_opts, Config),
- SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config),
+ SOpts = proplists:get_value(server_ecdh_rsa_opts, Config),
basic_test(COpts, SOpts, Config).
client_ecdh_server_rsa(Config) when is_list(Config) ->
COpts = proplists:get_value(client_ecdh_rsa_opts, Config),
- SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config),
+ SOpts = proplists:get_value(server_opts, Config),
basic_test(COpts, SOpts, Config).
client_rsa_server_ecdh(Config) when is_list(Config) ->
- COpts = proplists:get_value(client_ecdh_rsa_opts, Config),
- SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config),
+ COpts = proplists:get_value(client_opts, Config),
+ SOpts = proplists:get_value(server_ecdh_rsa_opts, Config),
basic_test(COpts, SOpts, Config).
client_rsa_server_rsa(Config) when is_list(Config) ->
- COpts = proplists:get_value(client_verification_opts, Config),
- SOpts = proplists:get_value(server_verification_opts, Config),
+ COpts = proplists:get_value(client_opts, Config),
+ SOpts = proplists:get_value(server_opts, Config),
basic_test(COpts, SOpts, Config).
client_ecdsa_server_ecdsa(Config) when is_list(Config) ->
COpts = proplists:get_value(client_ecdsa_opts, Config),
- SOpts = proplists:get_value(server_ecdsa_verify_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
basic_test(COpts, SOpts, Config).
client_ecdsa_server_rsa(Config) when is_list(Config) ->
COpts = proplists:get_value(client_ecdsa_opts, Config),
- SOpts = proplists:get_value(server_ecdsa_verify_opts, Config),
+ SOpts = proplists:get_value(server_opts, Config),
basic_test(COpts, SOpts, Config).
client_rsa_server_ecdsa(Config) when is_list(Config) ->
- COpts = proplists:get_value(client_ecdsa_opts, Config),
- SOpts = proplists:get_value(server_ecdsa_verify_opts, Config),
+ COpts = proplists:get_value(client_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
basic_test(COpts, SOpts, Config).
client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) ->
COpts = proplists:get_value(client_ecdsa_opts, Config),
- SOpts = proplists:get_value(server_ecdsa_verify_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
ServerCert = proplists:get_value(certfile, SOpts),
ServerKeyFile = proplists:get_value(keyfile, SOpts),
{ok, PemBin} = file:read_file(ServerKeyFile),
@@ -218,6 +235,132 @@ client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) ->
check_result(Server, SType, Client, CType),
close(Server, Client).
+ecc_default_order(Config) ->
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ ECCOpts = [],
+ case supported_eccs([{eccs, [sect571r1]}]) of
+ true -> ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+ecc_default_order_custom_curves(Config) ->
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ ECCOpts = [{eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+ecc_client_order(Config) ->
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ ECCOpts = [{honor_ecc_order, false}],
+ case supported_eccs([{eccs, [sect571r1]}]) of
+ true -> ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+ecc_client_order_custom_curves(Config) ->
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ ECCOpts = [{honor_ecc_order, false}, {eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+ecc_unknown_curve(Config) ->
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ ECCOpts = [{eccs, ['123_fake_curve']}],
+ ecc_test_error(COpts, SOpts, [], ECCOpts, Config).
+
+%% We can only expect to see a named curve on a conn with
+%% a server supporting ecdsa. Otherwise the curve is selected
+%% but not used and communicated to the client?
+client_ecdh_server_ecdh_ecc_server_custom(Config) ->
+ COpts = proplists:get_value(client_ecdh_rsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdh_rsa_opts, Config),
+ ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+client_ecdh_server_rsa_ecc_server_custom(Config) ->
+ COpts = proplists:get_value(client_ecdh_rsa_opts, Config),
+ SOpts = proplists:get_value(server_opts, Config),
+ ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+client_rsa_server_ecdh_ecc_server_custom(Config) ->
+ COpts = proplists:get_value(client_opts, Config),
+ SOpts = proplists:get_value(server_ecdh_rsa_opts, Config),
+ ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+client_rsa_server_rsa_ecc_server_custom(Config) ->
+ COpts = proplists:get_value(client_opts, Config),
+ SOpts = proplists:get_value(server_opts, Config),
+ ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+client_ecdsa_server_ecdsa_ecc_server_custom(Config) ->
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+client_ecdsa_server_rsa_ecc_server_custom(Config) ->
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_opts, Config),
+ ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(undefined, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+client_rsa_server_ecdsa_ecc_server_custom(Config) ->
+ COpts = proplists:get_value(client_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+client_ecdsa_server_ecdsa_ecc_client_custom(Config) ->
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ ECCOpts = [{eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(secp256r1, COpts, SOpts, ECCOpts, [], Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
+client_rsa_server_ecdsa_ecc_client_custom(Config) ->
+ COpts = proplists:get_value(client_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ ECCOpts = [{eccs, [secp256r1, sect571r1]}],
+ case supported_eccs(ECCOpts) of
+ true -> ecc_test(secp256r1, COpts, SOpts, ECCOpts, [], Config);
+ false -> {skip, "unsupported named curves"}
+ end.
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -244,20 +387,48 @@ basic_test(ClientCert, ClientKey, ClientCA, ServerCert, ServerKey, ServerCA, Con
check_result(Server, SType, Client, CType),
close(Server, Client).
-start_client(openssl, Port, CA, OwnCa, Cert, Key, Config) ->
+
+ecc_test(Expect, COpts, SOpts, CECCOpts, SECCOpts, Config) ->
+ CCA = proplists:get_value(cacertfile, COpts),
+ CCert = proplists:get_value(certfile, COpts),
+ CKey = proplists:get_value(keyfile, COpts),
+ SCA = proplists:get_value(cacertfile, SOpts),
+ SCert = proplists:get_value(certfile, SOpts),
+ SKey = proplists:get_value(keyfile, SOpts),
+ {Server, Port} = start_server_ecc(erlang, CCA, SCA, SCert, SKey, Expect, SECCOpts, Config),
+ Client = start_client_ecc(erlang, Port, SCA, CCA, CCert, CKey, Expect, CECCOpts, Config),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ close(Server, Client).
+
+ecc_test_error(COpts, SOpts, CECCOpts, SECCOpts, Config) ->
+ CCA = proplists:get_value(cacertfile, COpts),
+ CCert = proplists:get_value(certfile, COpts),
+ CKey = proplists:get_value(keyfile, COpts),
+ SCA = proplists:get_value(cacertfile, SOpts),
+ SCert = proplists:get_value(certfile, SOpts),
+ SKey = proplists:get_value(keyfile, SOpts),
+ {Server, Port} = start_server_ecc_error(erlang, CCA, SCA, SCert, SKey, SECCOpts, Config),
+ Client = start_client_ecc_error(erlang, Port, SCA, CCA, CCert, CKey, CECCOpts, Config),
+ Error = {error, {tls_alert, "insufficient security"}},
+ ssl_test_lib:check_result(Server, Error, Client, Error).
+
+
+start_client(openssl, Port, PeerCA, OwnCa, Cert, Key, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa),
+ CA = new_openssl_ca(filename:join(PrivDir, "openssl_client_ca.pem"), PeerCA, OwnCa),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
Exe = "openssl",
Args = ["s_client", "-verify", "2", "-port", integer_to_list(Port),
ssl_test_lib:version_flag(Version),
- "-cert", Cert, "-CAfile", NewCA,
+ "-cert", Cert, "-CAfile", CA,
"-key", Key, "-host","localhost", "-msg", "-debug"],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, "Hello world"),
OpenSslPort;
-start_client(erlang, Port, CA, _, Cert, Key, Config) ->
+start_client(erlang, Port, PeerCA, OwnCa, Cert, Key, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ CA = new_ca(filename:join(PrivDir,"erlang_client_ca.pem"), PeerCA, OwnCa),
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -267,20 +438,48 @@ start_client(erlang, Port, CA, _, Cert, Key, Config) ->
{cacertfile, CA},
{certfile, Cert}, {keyfile, Key}]}]).
-start_server(openssl, CA, OwnCa, Cert, Key, Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa),
+start_client_ecc(erlang, Port, PeerCA, OwnCa, Cert, Key, Expect, ECCOpts, Config) ->
+ CA = new_ca("erlang_client_ca", PeerCA, OwnCa),
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, check_ecc, [client, Expect]}},
+ {options,
+ ECCOpts ++
+ [{verify, verify_peer},
+ {cacertfile, CA},
+ {certfile, Cert}, {keyfile, Key}]}]).
+
+start_client_ecc_error(erlang, Port, PeerCA, OwnCa, Cert, Key, ECCOpts, Config) ->
+ CA = new_ca("erlang_client_ca", PeerCA, OwnCa),
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+ ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options,
+ ECCOpts ++
+ [{verify, verify_peer},
+ {cacertfile, CA},
+ {certfile, Cert}, {keyfile, Key}]}]).
+
+
+start_server(openssl, PeerCA, OwnCa, Cert, Key, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ CA = new_openssl_ca(filename:join(PrivDir,"openssl_server_ca.pem"), PeerCA, OwnCa),
Port = ssl_test_lib:inet_port(node()),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
Exe = "openssl",
Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-verify", "2", "-cert", Cert, "-CAfile", NewCA,
+ "-verify", "2", "-cert", Cert, "-CAfile", CA,
"-key", Key, "-msg", "-debug"],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, "Hello world"),
{OpenSslPort, Port};
-start_server(erlang, CA, _, Cert, Key, Config) ->
+start_server(erlang, PeerCA, OwnCa, Cert, Key, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ CA = new_ca(filename:join(PrivDir,"erlang_server_ca.pem"), PeerCA, OwnCa),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
@@ -291,16 +490,42 @@ start_server(erlang, CA, _, Cert, Key, Config) ->
[{verify, verify_peer}, {cacertfile, CA},
{certfile, Cert}, {keyfile, Key}]}]),
{Server, ssl_test_lib:inet_port(Server)}.
-start_server_with_raw_key(erlang, CA, _, Cert, Key, Config) ->
+
+start_server_with_raw_key(erlang, PeerCA, OwnCa, Cert, Key, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ CA = new_ca(filename:join(PrivDir, "erlang_server_ca.pem"), PeerCA, OwnCa),
{_, ServerNode, _} = 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,
- [{verify, verify_peer}, {cacertfile, CA},
- {certfile, Cert}, {key, Key}]}]),
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ send_recv_result_active,
+ []}},
+ {options,
+ [{verify, verify_peer}, {cacertfile, CA},
+ {certfile, Cert}, {key, Key}]}]),
+ {Server, ssl_test_lib:inet_port(Server)}.
+
+start_server_ecc(erlang, PeerCA, OwnCa, Cert, Key, Expect, ECCOpts, Config) ->
+ CA = new_ca("erlang_server_ca", PeerCA, OwnCa),
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, check_ecc, [server, Expect]}},
+ {options,
+ ECCOpts ++
+ [{verify, verify_peer}, {cacertfile, CA},
+ {certfile, Cert}, {keyfile, Key}]}]),
+ {Server, ssl_test_lib:inet_port(Server)}.
+
+start_server_ecc_error(erlang, PeerCA, OwnCa, Cert, Key, ECCOpts, Config) ->
+ CA = new_ca("erlang_server_ca", PeerCA, OwnCa),
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options,
+ ECCOpts ++
+ [{verify, verify_peer}, {cacertfile, CA},
+ {certfile, Cert}, {keyfile, Key}]}]),
{Server, ssl_test_lib:inet_port(Server)}.
check_result(Server, erlang, Client, erlang) ->
@@ -336,19 +561,34 @@ close(Client, Server) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-%% Work around OpenSSL bug, apparently the same bug as we had fixed in
-%% 11629690ba61f8e0c93ef9b2b6102fd279825977
new_ca(FileName, CA, OwnCa) ->
{ok, P1} = file:read_file(CA),
E1 = public_key:pem_decode(P1),
{ok, P2} = file:read_file(OwnCa),
E2 = public_key:pem_decode(P2),
- case os:cmd("openssl version") of
- "OpenSSL 1.0.1p-freebsd" ++ _ ->
- Pem = public_key:pem_encode(E1 ++E2),
- file:write_file(FileName, Pem);
- _ ->
- Pem = public_key:pem_encode(E2 ++E1),
- file:write_file(FileName, Pem)
- end,
+ Pem = public_key:pem_encode(E1 ++E2),
+ file:write_file(FileName, Pem),
FileName.
+
+new_openssl_ca(FileName, CA, OwnCa) ->
+ {ok, P1} = file:read_file(CA),
+ E1 = public_key:pem_decode(P1),
+ {ok, P2} = file:read_file(OwnCa),
+ E2 = public_key:pem_decode(P2),
+ Pem = public_key:pem_encode(E2 ++E1),
+ file:write_file(FileName, Pem),
+ FileName.
+
+supported_eccs(Opts) ->
+ ToCheck = proplists:get_value(eccs, Opts, []),
+ Supported = ssl:eccs(),
+ lists:all(fun(Curve) -> lists:member(Curve, Supported) end, ToCheck).
+
+check_ecc(SSL, Role, Expect) ->
+ {ok, Data} = ssl:connection_information(SSL),
+ case lists:keyfind(ecc, 1, Data) of
+ {ecc, {named_curve, Expect}} -> ok;
+ false when Expect =:= undefined -> ok;
+ Other -> {error, Role, Expect, Other}
+ end.
+
diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
index da181faf64..158b3524ac 100644
--- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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.
@@ -71,7 +71,7 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config),
proplists:get_value(priv_dir, Config)),
ssl_test_lib:cert_options(Config)
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index efa5faa218..c846913598 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2017. 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.
@@ -40,6 +40,7 @@
-define(SLEEP, 500).
-define(RENEGOTIATION_DISABLE_TIME, 12000).
-define(CLEAN_SESSION_DB, 60000).
+-define(SEC_RENEGOTIATION_TIMEOUT, 30).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -51,8 +52,9 @@ all() ->
{group, options},
{group, options_tls},
{group, session},
- %%{group, 'dtlsv1.2'},
- %%{group, 'dtlsv1'},
+ {group, 'dtlsv1.2'},
+ %% {group, 'dtlsv1'}, Breaks dtls in cert_verify_SUITE enable later when
+ %% problem is identified and fixed
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
@@ -64,14 +66,15 @@ groups() ->
{basic_tls, [], basic_tests_tls()},
{options, [], options_tests()},
{options_tls, [], options_tests_tls()},
- %%{'dtlsv1.2', [], all_versions_groups()},
- %%{'dtlsv1', [], all_versions_groups()},
+ {'dtlsv1.2', [], all_versions_groups()},
+ {'dtlsv1', [], all_versions_groups()},
{'tlsv1.2', [], all_versions_groups() ++ tls_versions_groups() ++ [conf_signature_algs, no_common_signature_algs]},
{'tlsv1.1', [], all_versions_groups() ++ tls_versions_groups()},
{'tlsv1', [], all_versions_groups() ++ tls_versions_groups() ++ rizzo_tests()},
{'sslv3', [], all_versions_groups() ++ tls_versions_groups() ++ rizzo_tests() ++ [tls_ciphersuite_vs_version]},
{api,[], api_tests()},
{api_tls,[], api_tests_tls()},
+ {tls_ciphers,[], tls_cipher_tests()},
{session, [], session_tests()},
{renegotiate, [], renegotiate_tests()},
{ciphers, [], cipher_tests()},
@@ -81,12 +84,13 @@ groups() ->
].
tls_versions_groups ()->
- [{group, api_tls},
+ [{group, renegotiate}, %% Should be in all_versions_groups not fixed for DTLS yet
+ {group, api_tls},
+ {group, tls_ciphers},
{group, error_handling_tests_tls}].
all_versions_groups ()->
[{group, api},
- {group, renegotiate},
{group, ciphers},
{group, ciphers_ec},
{group, error_handling_tests}].
@@ -134,7 +138,8 @@ options_tests() ->
honor_server_cipher_order,
honor_client_cipher_order,
unordered_protocol_versions_server,
- unordered_protocol_versions_client
+ unordered_protocol_versions_client,
+ max_handshake_size
].
options_tests_tls() ->
@@ -144,11 +149,10 @@ options_tests_tls() ->
api_tests() ->
[connection_info,
connection_information,
- peername,
peercert,
peercert_with_client_cert,
- sockname,
versions,
+ eccs,
controlling_process,
getstat,
close_with_timeout,
@@ -158,7 +162,6 @@ api_tests() ->
ssl_recv_timeout,
server_name_indication_option,
accept_pool,
- new_options_in_accept,
prf
].
@@ -171,7 +174,10 @@ api_tests_tls() ->
tls_shutdown,
tls_shutdown_write,
tls_shutdown_both,
- tls_shutdown_error
+ tls_shutdown_error,
+ peername,
+ sockname,
+ new_options_in_accept
].
session_tests() ->
@@ -193,6 +199,11 @@ renegotiate_tests() ->
renegotiate_dos_mitigate_passive,
renegotiate_dos_mitigate_absolute].
+tls_cipher_tests() ->
+ [rc4_rsa_cipher_suites,
+ rc4_ecdh_rsa_cipher_suites,
+ rc4_ecdsa_cipher_suites].
+
cipher_tests() ->
[cipher_suites,
cipher_suites_mix,
@@ -208,9 +219,6 @@ cipher_tests() ->
srp_cipher_suites,
srp_anon_cipher_suites,
srp_dsa_cipher_suites,
- rc4_rsa_cipher_suites,
- rc4_ecdh_rsa_cipher_suites,
- rc4_ecdsa_cipher_suites,
des_rsa_cipher_suites,
des_ecdh_rsa_cipher_suites,
default_reject_anonymous].
@@ -222,15 +230,15 @@ cipher_tests_ec() ->
ciphers_ecdh_rsa_signed_certs_openssl_names].
error_handling_tests()->
- [controller_dies,
- close_transport_accept,
+ [close_transport_accept,
recv_active,
recv_active_once,
recv_error_handling
].
error_handling_tests_tls()->
- [tls_client_closes_socket,
+ [controller_dies,
+ tls_client_closes_socket,
tls_tcp_error_propagation_in_active_mode,
tls_tcp_connect,
tls_tcp_connect_big,
@@ -249,7 +257,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
%% make rsa certs using oppenssl
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
@@ -306,6 +314,7 @@ init_per_testcase(protocol_versions, Config) ->
init_per_testcase(reuse_session_expired, Config) ->
ssl:stop(),
application:load(ssl),
+ ssl_test_lib:clean_env(),
application:set_env(ssl, session_lifetime, ?EXPIRE),
application:set_env(ssl, session_delay_cleanup_time, 500),
ssl:start(),
@@ -315,6 +324,7 @@ init_per_testcase(reuse_session_expired, Config) ->
init_per_testcase(empty_protocol_versions, Config) ->
ssl:stop(),
application:load(ssl),
+ ssl_test_lib:clean_env(),
application:set_env(ssl, protocol_version, []),
ssl:start(),
ct:timetrap({seconds, 5}),
@@ -340,7 +350,7 @@ init_per_testcase(TestCase, Config) when TestCase == client_renegotiate;
TestCase == renegotiate_dos_mitigate_passive;
TestCase == renegotiate_dos_mitigate_absolute ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, 30}),
+ ct:timetrap({seconds, ?SEC_RENEGOTIATION_TIMEOUT + 5}),
Config;
init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
@@ -350,12 +360,27 @@ init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
TestCase == ciphers_dsa_signed_certs;
TestCase == ciphers_dsa_signed_certs_openssl_names;
TestCase == anonymous_cipher_suites;
+ TestCase == ciphers_ecdsa_signed_certs;
+ TestCase == ciphers_ecdsa_signed_certs_openssl_names;
+ TestCase == anonymous_cipher_suites;
+ TestCase == psk_anon_cipher_suites;
+ TestCase == psk_anon_with_hint_cipher_suites;
TestCase == versions_option,
TestCase == tls_tcp_connect_big ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 60}),
Config;
+init_per_testcase(version_option, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 10}),
+ Config;
+
+init_per_testcase(reuse_session, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 10}),
+ Config;
+
init_per_testcase(rizzo, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 40}),
@@ -408,8 +433,13 @@ init_per_testcase(TestCase, Config) when TestCase == tls_ssl_accept_timeout;
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 15}),
Config;
-init_per_testcase(clear_pem_cache, Config) ->
+init_per_testcase(TestCase, Config) when TestCase == clear_pem_cache;
+ TestCase == der_input;
+ TestCase == defaults ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ %% White box test need clean start
+ ssl:stop(),
+ ssl:start(),
ct:timetrap({seconds, 20}),
Config;
init_per_testcase(raw_ssl_option, Config) ->
@@ -430,7 +460,18 @@ init_per_testcase(accept_pool, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
Config
end;
-
+init_per_testcase(controller_dies, Config) ->
+ ct:timetrap({seconds, 10}),
+ Config;
+init_per_testcase(eccs, Config) ->
+ case ssl:eccs() of
+ [] ->
+ {skip, "named curves not supported"};
+ [_|_] ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 5}),
+ Config
+ end;
init_per_testcase(_TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 5}),
@@ -441,6 +482,11 @@ end_per_testcase(reuse_session_expired, Config) ->
application:unset_env(ssl, session_delay_cleanup_time),
end_per_testcase(default_action, Config);
+end_per_testcase(Case, Config) when Case == protocol_versions;
+ Case == empty_protocol_versions->
+ application:unset_env(ssl, protocol_versions),
+ end_per_testcase(default_action, Config);
+
end_per_testcase(_TestCase, Config) ->
Config.
@@ -567,8 +613,8 @@ prf(Config) when is_list(Config) ->
connection_info() ->
[{doc,"Test the API function ssl:connection_information/1"}].
connection_info(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -801,8 +847,7 @@ controller_dies(Config) when is_list(Config) ->
Server ! listen,
Tester = self(),
Connect = fun(Pid) ->
- {ok, Socket} = ssl:connect(Hostname, Port,
- [{reuseaddr,true},{ssl_imp,new}]),
+ {ok, Socket} = ssl:connect(Hostname, Port, ClientOpts),
%% Make sure server finishes and verification
%% and is in coonection state before
%% killing client
@@ -919,9 +964,9 @@ clear_pem_cache(Config) when is_list(Config) ->
{status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
[_, _,_, _, Prop] = StatusInfo,
State = ssl_test_lib:state(Prop),
- [_,FilRefDb |_] = element(6, State),
+ [_,{FilRefDb, _} |_] = element(6, State),
{Server, Client} = basic_verify_test_no_close(Config),
- CountReferencedFiles = fun({_,-1}, Acc) ->
+ CountReferencedFiles = fun({_, -1}, Acc) ->
Acc;
({_, N}, Acc) ->
N + Acc
@@ -1144,8 +1189,8 @@ cipher_suites_mix() ->
cipher_suites_mix(Config) when is_list(Config) ->
CipherSuites = [{ecdh_rsa,aes_128_cbc,sha256,sha256}, {rsa,aes_128_cbc,sha}],
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -1474,6 +1519,25 @@ versions(Config) when is_list(Config) ->
[_|_] = Versions = ssl:versions(),
ct:log("~p~n", [Versions]).
+
+%%--------------------------------------------------------------------
+eccs() ->
+ [{doc, "Test API functions eccs/0 and eccs/1"}].
+
+eccs(Config) when is_list(Config) ->
+ [_|_] = All = ssl:eccs(),
+ [] = SSL3 = ssl:eccs({3,0}),
+ [_|_] = Tls = ssl:eccs({3,1}),
+ [_|_] = Tls1 = ssl:eccs({3,2}),
+ [_|_] = Tls2 = ssl:eccs({3,3}),
+ [] = SSL3 = ssl:eccs(sslv3),
+ [_|_] = Tls = ssl:eccs(tlsv1),
+ [_|_] = Tls1 = ssl:eccs('tlsv1.1'),
+ [_|_] = Tls2 = ssl:eccs('tlsv1.2'),
+ %% ordering is currently unverified by the test
+ true = lists:sort(All) =:= lists:usort(SSL3 ++ Tls ++ Tls1 ++ Tls2),
+ ok.
+
%%--------------------------------------------------------------------
send_recv() ->
[{doc,""}].
@@ -2133,8 +2197,9 @@ ciphers_dsa_signed_certs() ->
[{doc,"Test all dsa ssl cipher suites in highest support ssl/tls version"}].
ciphers_dsa_signed_certs(Config) when is_list(Config) ->
+ NVersion = ssl_test_lib:protocol_version(Config, tuple),
Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:dsa_suites(),
+ Ciphers = ssl_test_lib:dsa_suites(NVersion),
ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]),
run_suites(Ciphers, Version, Config, dsa).
%%-------------------------------------------------------------------
@@ -2151,35 +2216,39 @@ anonymous_cipher_suites()->
[{doc,"Test the anonymous ciphersuites"}].
anonymous_cipher_suites(Config) when is_list(Config) ->
Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:anonymous_suites(),
+ Ciphers = ssl_test_lib:anonymous_suites(Version),
run_suites(Ciphers, Version, Config, anonymous).
%%-------------------------------------------------------------------
psk_cipher_suites() ->
[{doc, "Test the PSK ciphersuites WITHOUT server supplied identity hint"}].
psk_cipher_suites(Config) when is_list(Config) ->
+ NVersion = tls_record:highest_protocol_version([]),
Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:psk_suites(),
+ Ciphers = ssl_test_lib:psk_suites(NVersion),
run_suites(Ciphers, Version, Config, psk).
%%-------------------------------------------------------------------
psk_with_hint_cipher_suites()->
[{doc, "Test the PSK ciphersuites WITH server supplied identity hint"}].
psk_with_hint_cipher_suites(Config) when is_list(Config) ->
+ NVersion = tls_record:highest_protocol_version([]),
Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:psk_suites(),
+ Ciphers = ssl_test_lib:psk_suites(NVersion),
run_suites(Ciphers, Version, Config, psk_with_hint).
%%-------------------------------------------------------------------
psk_anon_cipher_suites() ->
[{doc, "Test the anonymous PSK ciphersuites WITHOUT server supplied identity hint"}].
psk_anon_cipher_suites(Config) when is_list(Config) ->
+ NVersion = tls_record:highest_protocol_version([]),
Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:psk_anon_suites(),
+ Ciphers = ssl_test_lib:psk_anon_suites(NVersion),
run_suites(Ciphers, Version, Config, psk_anon).
%%-------------------------------------------------------------------
psk_anon_with_hint_cipher_suites()->
[{doc, "Test the anonymous PSK ciphersuites WITH server supplied identity hint"}].
psk_anon_with_hint_cipher_suites(Config) when is_list(Config) ->
+ NVersion = tls_record:highest_protocol_version([]),
Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:psk_anon_suites(),
+ Ciphers = ssl_test_lib:psk_anon_suites(NVersion),
run_suites(Ciphers, Version, Config, psk_anon_with_hint).
%%-------------------------------------------------------------------
srp_cipher_suites()->
@@ -2230,18 +2299,17 @@ rc4_ecdsa_cipher_suites(Config) when is_list(Config) ->
%%-------------------------------------------------------------------
des_rsa_cipher_suites()->
- [{doc, "Test the RC4 ciphersuites"}].
+ [{doc, "Test the des_rsa ciphersuites"}].
des_rsa_cipher_suites(Config) when is_list(Config) ->
- NVersion = tls_record:highest_protocol_version([]),
- Version = tls_record:protocol_version(NVersion),
- Ciphers = ssl_test_lib:des_suites(NVersion),
+ Version = ssl_test_lib:protocol_version(Config),
+ Ciphers = ssl_test_lib:des_suites(Config),
run_suites(Ciphers, Version, Config, des_rsa).
%-------------------------------------------------------------------
des_ecdh_rsa_cipher_suites()->
- [{doc, "Test the RC4 ciphersuites"}].
+ [{doc, "Test ECDH rsa signed ciphersuites"}].
des_ecdh_rsa_cipher_suites(Config) when is_list(Config) ->
- NVersion = tls_record:highest_protocol_version([]),
- Version = tls_record:protocol_version(NVersion),
+ NVersion = ssl_test_lib:protocol_version(Config, tuple),
+ Version = ssl_test_lib:protocol_version(Config),
Ciphers = ssl_test_lib:des_suites(NVersion),
run_suites(Ciphers, Version, Config, des_dhe_rsa).
@@ -2252,9 +2320,11 @@ default_reject_anonymous(Config) when is_list(Config) ->
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- [Cipher | _] = ssl_test_lib:anonymous_suites(),
-
+ Version = ssl_test_lib:protocol_version(Config),
+ TLSVersion = ssl_test_lib:tls_version(Version),
+
+ [CipherSuite | _] = ssl_test_lib:anonymous_suites(TLSVersion),
+
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
{options, ServerOpts}]),
@@ -2263,7 +2333,7 @@ default_reject_anonymous(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{options,
- [{ciphers,[Cipher]} |
+ [{ciphers,[CipherSuite]} |
ClientOpts]}]),
ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}},
@@ -2274,8 +2344,9 @@ ciphers_ecdsa_signed_certs() ->
[{doc, "Test all ecdsa ssl cipher suites in highest support ssl/tls version"}].
ciphers_ecdsa_signed_certs(Config) when is_list(Config) ->
+ NVersion = ssl_test_lib:protocol_version(Config, tuple),
Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:ecdsa_suites(),
+ Ciphers = ssl_test_lib:ecdsa_suites(NVersion),
ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]),
run_suites(Ciphers, Version, Config, ecdsa).
%%--------------------------------------------------------------------
@@ -2292,8 +2363,9 @@ ciphers_ecdh_rsa_signed_certs() ->
[{doc, "Test all ecdh_rsa ssl cipher suites in highest support ssl/tls version"}].
ciphers_ecdh_rsa_signed_certs(Config) when is_list(Config) ->
+ NVersion = ssl_test_lib:protocol_version(Config, tuple),
Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:ecdh_rsa_suites(),
+ Ciphers = ssl_test_lib:ecdh_rsa_suites(NVersion),
ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]),
run_suites(Ciphers, Version, Config, ecdh_rsa).
%%--------------------------------------------------------------------
@@ -3265,11 +3337,11 @@ hibernate(Config) ->
process_info(Pid, current_function),
ssl_test_lib:check_result(Server, ok, Client, ok),
- timer:sleep(1100),
-
+
+ timer:sleep(1500),
{current_function, {erlang, hibernate, 3}} =
process_info(Pid, current_function),
-
+
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
@@ -3302,13 +3374,12 @@ hibernate_right_away(Config) ->
[{port, Port1}, {options, [{hibernate_after, 0}|ClientOpts]}]),
ssl_test_lib:check_result(Server1, ok, Client1, ok),
-
- {current_function, {erlang, hibernate, 3}} =
+
+ {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{pid = Pid2}} = ssl_test_lib:start_client(StartClientOpts ++
@@ -3316,8 +3387,8 @@ hibernate_right_away(Config) ->
ssl_test_lib:check_result(Server2, ok, Client2, ok),
- ct:sleep(100), %% Schedule out
-
+ ct:sleep(1000), %% Schedule out
+
{current_function, {erlang, hibernate, 3}} =
process_info(Pid2, current_function),
@@ -3604,9 +3675,10 @@ no_rizzo_rc4() ->
[{doc,"Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"}].
no_rizzo_rc4(Config) when is_list(Config) ->
- Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(),Y == rc4_128],
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
+ Ciphers = [ssl_cipher:erl_suite_definition(Suite) ||
+ Suite <- ssl_test_lib:rc4_suites(tls_record:protocol_version(Version))],
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -3614,9 +3686,10 @@ rizzo_one_n_minus_one() ->
[{doc,"Test that the 1/n-1-split mitigation of Rizzo/Dungon attack can be explicitly selected"}].
rizzo_one_n_minus_one(Config) when is_list(Config) ->
- Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
+ AllSuites = ssl_test_lib:available_suites(tls_record:protocol_version(Version)),
+ Ciphers = [X || X ={_,Y,_} <- AllSuites, Y =/= rc4_128],
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_rizzo, []}).
@@ -3624,9 +3697,10 @@ rizzo_zero_n() ->
[{doc,"Test that the 0/n-split mitigation of Rizzo/Dungon attack can be explicitly selected"}].
rizzo_zero_n(Config) when is_list(Config) ->
- Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
Prop = proplists:get_value(tc_group_properties, Config),
Version = proplists:get_value(name, Prop),
+ AllSuites = ssl_test_lib:available_suites(tls_record:protocol_version(Version)),
+ Ciphers = [X || X ={_,Y,_} <- AllSuites, Y =/= rc4_128],
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
@@ -3797,6 +3871,29 @@ unordered_protocol_versions_client(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
%%--------------------------------------------------------------------
+max_handshake_size() ->
+ [{doc,"Test that we can set max_handshake_size to max value."}].
+
+max_handshake_size(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(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, [{max_handshake_size, 8388607} |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, [{max_handshake_size, 8388607} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+%%--------------------------------------------------------------------
server_name_indication_option() ->
[{doc,"Test API server_name_indication option to connect."}].
@@ -4288,7 +4385,7 @@ erlang_ssl_receive(Socket, Data) ->
erlang_ssl_receive(Socket, tl(Data));
Other ->
ct:fail({unexpected_message, Other})
- after ?SLEEP * 3 * test_server:timetrap_scale_factor() ->
+ after timer:seconds(?SEC_RENEGOTIATION_TIMEOUT) * test_server:timetrap_scale_factor() ->
ct:fail({did_not_get, Data})
end.
@@ -4377,7 +4474,7 @@ rizzo_test(Cipher, Config, Version, Mfa) ->
{host, Hostname},
{from, self()},
{mfa, Mfa},
- {options, [{active, true} | ClientOpts]}]),
+ {options, [{active, true}, {ciphers, [Cipher]}| ClientOpts]}]),
Result = ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
@@ -4409,27 +4506,32 @@ run_suites(Ciphers, Version, Config, Type) ->
{ClientOpts, ServerOpts} =
case Type of
rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
- ssl_test_lib:ssl_options(server_opts, Config)};
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ssl_test_lib:ssl_options(server_verification_opts, Config)};
dsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
ssl_test_lib:ssl_options(server_dsa_opts, Config)};
anonymous ->
%% No certs in opts!
- {ssl_test_lib:ssl_options(client_opts, Config),
- ssl_test_lib:ssl_options(server_anon, Config)};
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
+ [{reuseaddr, true}, {ciphers, ssl_test_lib:anonymous_suites(Version)}]};
psk ->
{ssl_test_lib:ssl_options(client_psk, Config),
- ssl_test_lib:ssl_options(server_psk, Config)};
+ [{ciphers, ssl_test_lib:psk_suites(Version)} |
+ ssl_test_lib:ssl_options(server_psk, Config)]};
psk_with_hint ->
{ssl_test_lib:ssl_options(client_psk, Config),
- ssl_test_lib:ssl_options(server_psk_hint, Config)};
+ [{ciphers, ssl_test_lib:psk_suites(Version)} |
+ ssl_test_lib:ssl_options(server_psk_hint, Config)
+ ]};
psk_anon ->
{ssl_test_lib:ssl_options(client_psk, Config),
- ssl_test_lib:ssl_options(server_psk_anon, Config)};
+ [{ciphers, ssl_test_lib:psk_anon_suites(Version)} |
+ ssl_test_lib:ssl_options(server_psk_anon, Config)]};
psk_anon_with_hint ->
{ssl_test_lib:ssl_options(client_psk, Config),
- ssl_test_lib:ssl_options(server_psk_anon_hint, Config)};
+ [{ciphers, ssl_test_lib:psk_anon_suites(Version)} |
+ ssl_test_lib:ssl_options(server_psk_anon_hint, Config)]};
srp ->
{ssl_test_lib:ssl_options(client_srp, Config),
ssl_test_lib:ssl_options(server_srp, Config)};
@@ -4440,36 +4542,36 @@ run_suites(Ciphers, Version, Config, Type) ->
{ssl_test_lib:ssl_options(client_srp_dsa, Config),
ssl_test_lib:ssl_options(server_srp_dsa, Config)};
ecdsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
ssl_test_lib:ssl_options(server_ecdsa_opts, Config)};
ecdh_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)};
rc4_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
[{ciphers, Ciphers} |
- ssl_test_lib:ssl_options(server_opts, Config)]};
+ ssl_test_lib:ssl_options(server_verification_opts, Config)]};
rc4_ecdh_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
[{ciphers, Ciphers} |
ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)]};
rc4_ecdsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
[{ciphers, Ciphers} |
ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]};
des_dhe_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
[{ciphers, Ciphers} |
- ssl_test_lib:ssl_options(server_opts, Config)]};
+ ssl_test_lib:ssl_options(server_verification_opts, Config)]};
des_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
[{ciphers, Ciphers} |
- ssl_test_lib:ssl_options(server_opts, Config)]}
+ ssl_test_lib:ssl_options(server_verification_opts, Config)]}
end,
Result = lists:map(fun(Cipher) ->
cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end,
- ssl_test_lib:filter_suites(Ciphers)),
+ ssl_test_lib:filter_suites(Ciphers, Version)),
case lists:flatten(Result) of
[] ->
ok;
@@ -4668,3 +4770,4 @@ first_rsa_suite([_ | Rest]) ->
wait_for_send(Socket) ->
%% Make sure TLS process processed send message event
_ = ssl:connection_information(Socket).
+
diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl
index ed439a425f..21989f8d99 100644
--- a/lib/ssl/test/ssl_bench_SUITE.erl
+++ b/lib/ssl/test/ssl_bench_SUITE.erl
@@ -25,11 +25,12 @@
suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}].
-all() -> [{group, setup}, {group, payload}].
+all() -> [{group, setup}, {group, payload}, {group, pem_cache}].
groups() ->
[{setup, [{repeat, 3}], [setup_sequential, setup_concurrent]},
- {payload, [{repeat, 3}], [payload_simple]}
+ {payload, [{repeat, 3}], [payload_simple]},
+ {pem_cache, [{repeat, 3}], [use_pem_cache, bypass_pem_cache]}
].
init_per_group(_GroupName, Config) ->
@@ -49,9 +50,33 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
+init_per_testcase(use_pem_cache, Conf) ->
+ case bypass_pem_cache_supported() of
+ false -> {skipped, "PEM cache bypass support required"};
+ true ->
+ application:set_env(ssl, bypass_pem_cache, false),
+ Conf
+ end;
+init_per_testcase(bypass_pem_cache, Conf) ->
+ case bypass_pem_cache_supported() of
+ false -> {skipped, "PEM cache bypass support required"};
+ true ->
+ application:set_env(ssl, bypass_pem_cache, true),
+ Conf
+ end;
init_per_testcase(_Func, Conf) ->
Conf.
+end_per_testcase(use_pem_cache, _Config) ->
+ case bypass_pem_cache_supported() of
+ false -> ok;
+ true -> application:set_env(ssl, bypass_pem_cache, false)
+ end;
+end_per_testcase(bypass_pem_cache, _Config) ->
+ case bypass_pem_cache_supported() of
+ false -> ok;
+ true -> application:set_env(ssl, bypass_pem_cache, false)
+ end;
end_per_testcase(_Func, _Conf) ->
ok.
@@ -94,6 +119,18 @@ payload_simple(Config) ->
{suite, "ssl"}, {name, "Payload simple"}]}),
ok.
+use_pem_cache(_Config) ->
+ {ok, Result} = do_test(ssl, pem_cache, 100, 500, node()),
+ ct_event:notify(#event{name = benchmark_data,
+ data=[{value, Result},
+ {suite, "ssl"}, {name, "Use PEM cache"}]}).
+
+bypass_pem_cache(_Config) ->
+ {ok, Result} = do_test(ssl, pem_cache, 100, 500, node()),
+ ct_event:notify(#event{name = benchmark_data,
+ data=[{value, Result},
+ {suite, "ssl"}, {name, "Bypass PEM cache"}]}).
+
ssl() ->
test(ssl, ?COUNT, node()).
@@ -172,6 +209,18 @@ server_init(ssl, payload, Loop, _, Server) ->
ssl:close(TSocket)
end,
setup_server_connection(Socket, Test);
+server_init(ssl, pem_cache, Loop, _, Server) ->
+ {ok, Socket} = ssl:listen(0, ssl_opts(listen_der)),
+ {ok, {_Host, Port}} = ssl:sockname(Socket),
+ {ok, Host} = inet:gethostname(),
+ Server ! {self(), {init, Host, Port}},
+ Test = fun(TSocket) ->
+ ok = ssl:ssl_accept(TSocket),
+ Size = byte_size(msg()),
+ server_echo(TSocket, Size, Loop),
+ ssl:close(TSocket)
+ end,
+ setup_server_connection(Socket, Test);
server_init(Type, Tc, _, _, Server) ->
io:format("No server init code for ~p ~p~n",[Type, Tc]),
@@ -185,6 +234,11 @@ client_init(Master, ssl, payload, Host, Port) ->
Master ! {self(), init},
Size = byte_size(msg()),
{Sock, Size};
+client_init(Master, ssl, pem_cache, Host, Port) ->
+ {ok, Sock} = ssl:connect(Host, Port, ssl_opts(connect_der)),
+ Master ! {self(), init},
+ Size = byte_size(msg()),
+ {Sock, Size};
client_init(_Me, Type, Tc, Host, Port) ->
io:format("No client init code for ~p ~p~n",[Type, Tc]),
{Host, Port}.
@@ -228,6 +282,13 @@ payload(Loop, ssl, D = {Socket, Size}) when Loop > 0 ->
payload(_, _, {Socket, _}) ->
ssl:close(Socket).
+pem_cache(N, ssl, Data = {Socket, Size}) when N > 0 ->
+ ok = ssl:send(Socket, msg()),
+ {ok, _} = ssl:recv(Socket, Size),
+ pem_cache(N-1, ssl, Data);
+pem_cache(_, _, {Socket, _}) ->
+ ssl:close(Socket).
+
msg() ->
<<"Hello",
0:(512*8),
@@ -352,16 +413,43 @@ stop_profile(fprof, File) ->
ssl_opts(listen) ->
[{backlog, 500} | ssl_opts("server")];
ssl_opts(connect) ->
- [{verify, verify_peer}
- | ssl_opts("client")];
+ [{verify, verify_peer} | ssl_opts("client")];
+ssl_opts(listen_der) ->
+ [{backlog, 500} | ssl_opts("server_der")];
+ssl_opts(connect_der) ->
+ [{verify, verify_peer} | ssl_opts("client_der")];
ssl_opts(Role) ->
- Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ CertData = cert_data(Role),
[{active, false},
{depth, 2},
{reuseaddr, true},
{mode,binary},
{nodelay, true},
- {ciphers, [{dhe_rsa,aes_256_cbc,sha}]},
- {cacertfile, filename:join([Dir, Role, "cacerts.pem"])},
+ {ciphers, [{dhe_rsa,aes_256_cbc,sha}]}
+ |CertData].
+
+cert_data(Der) when Der =:= "server_der"; Der =:= "client_der" ->
+ [Role,_] = string:tokens(Der, "_"),
+ Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ {ok, CaCert0} = file:read_file(filename:join([Dir, Role, "cacerts.pem"])),
+ {ok, Cert0} = file:read_file(filename:join([Dir, Role, "cert.pem"])),
+ {ok, Key0} = file:read_file(filename:join([Dir, Role, "key.pem"])),
+ [{_, Cert, _}] = public_key:pem_decode(Cert0),
+ CaCert1 = public_key:pem_decode(CaCert0),
+ CaCert = [CCert || {_, CCert, _} <- CaCert1],
+ [{KeyType, Key, _}] = public_key:pem_decode(Key0),
+ [{cert, Cert},
+ {cacerts, CaCert},
+ {key, {KeyType, Key}}];
+cert_data(Role) ->
+ Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ [{cacertfile, filename:join([Dir, Role, "cacerts.pem"])},
{certfile, filename:join([Dir, Role, "cert.pem"])},
{keyfile, filename:join([Dir, Role, "key.pem"])}].
+
+bypass_pem_cache_supported() ->
+ %% This function is currently critical to support cache bypass
+ %% and did not exist in prior versions.
+ catch ssl_pkix_db:module_info(), % ensure module is loaded
+ erlang:function_exported(ssl_pkix_db, extract_trusted_certs, 1).
+
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 20165c70f0..bed6f87c74 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2017. 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.
@@ -39,17 +39,26 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
all() ->
- [{group, active},
- {group, passive},
- {group, active_once},
- {group, error_handling}].
-
+ [
+ {group, tls},
+ {group, dtls}
+ ].
groups() ->
- [{active, [], tests()},
+ [
+ {tls, [], all_protocol_groups()},
+ {dtls, [], all_protocol_groups()},
+ {active, [], tests()},
{active_once, [], tests()},
{passive, [], tests()},
- {error_handling, [],error_handling_tests()}].
+ {error_handling, [],error_handling_tests()}
+ ].
+
+all_protocol_groups() ->
+ [{group, active},
+ {group, passive},
+ {group, active_once},
+ {group, error_handling}].
tests() ->
[verify_peer,
@@ -65,9 +74,10 @@ tests() ->
cert_expired,
invalid_signature_client,
invalid_signature_server,
- extended_key_usage_verify_peer,
- extended_key_usage_verify_none,
- critical_extension_verify_peer,
+ extended_key_usage_verify_client,
+ extended_key_usage_verify_server,
+ critical_extension_verify_client,
+ critical_extension_verify_server,
critical_extension_verify_none].
error_handling_tests()->
@@ -84,7 +94,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
%% make rsa certs using oppenssl
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
@@ -98,6 +108,26 @@ end_per_suite(_Config) ->
ssl:stop(),
application:stop(crypto).
+init_per_group(tls, Config) ->
+ Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, protocol_version, Version),
+ application:set_env(ssl, bypass_pem_cache, Version),
+ ssl:start(),
+ NewConfig = proplists:delete(protocol, Config),
+ [{protocol, tls}, {version, tls_record:protocol_version(Version)} | NewConfig];
+
+init_per_group(dtls, Config) ->
+ Version = dtls_record:protocol_version(dtls_record:highest_protocol_version([])),
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, protocol_version, Version),
+ application:set_env(ssl, bypass_pem_cache, Version),
+ ssl:start(),
+ NewConfig = proplists:delete(protocol_opts, proplists:delete(protocol, Config)),
+ [{protocol, dtls}, {protocol_opts, [{protocol, dtls}]}, {version, dtls_record:protocol_version(Version)} | NewConfig];
+
init_per_group(active, Config) ->
[{active, true}, {receive_function, send_recv_result_active} | Config];
init_per_group(active_once, Config) ->
@@ -122,6 +152,8 @@ init_per_testcase(TestCase, Config) when TestCase == cert_expired;
ssl:clear_pem_cache(),
init_per_testcase(common, Config);
init_per_testcase(_TestCase, Config) ->
+ ssl:stop(),
+ ssl:start(),
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 5}),
Config.
@@ -136,7 +168,7 @@ end_per_testcase(_TestCase, Config) ->
verify_peer() ->
[{doc,"Test option verify_peer"}].
verify_peer(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
@@ -190,7 +222,7 @@ server_verify_client_once() ->
[{doc,"Test server option verify_client_once"}].
server_verify_client_once(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, []),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
@@ -230,7 +262,7 @@ server_require_peer_cert_ok() ->
server_require_peer_cert_ok(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -259,7 +291,7 @@ server_require_peer_cert_fail() ->
server_require_peer_cert_fail(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- BadClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ BadClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
@@ -290,7 +322,7 @@ server_require_peer_cert_partial_chain() ->
server_require_peer_cert_partial_chain(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
{ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)),
@@ -325,13 +357,13 @@ server_require_peer_cert_allow_partial_chain() ->
server_require_peer_cert_allow_partial_chain(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
- {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)),
- [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ServerCAs),
+ {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)),
+ [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ClientCAs),
PartialChain = fun(CertChain) ->
case lists:member(IntermidiateCA, CertChain) of
@@ -367,7 +399,7 @@ server_require_peer_cert_do_not_allow_partial_chain() ->
server_require_peer_cert_do_not_allow_partial_chain(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
{ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)),
@@ -408,7 +440,7 @@ server_require_peer_cert_partial_chain_fun_fail() ->
server_require_peer_cert_partial_chain_fun_fail(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = proplists:get_value(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
{ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)),
@@ -448,7 +480,7 @@ verify_fun_always_run_client() ->
verify_fun_always_run_client(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
@@ -492,7 +524,7 @@ verify_fun_always_run_client(Config) when is_list(Config) ->
verify_fun_always_run_server() ->
[{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}].
verify_fun_always_run_server(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -524,9 +556,7 @@ verify_fun_always_run_server(Config) when is_list(Config) ->
{from, self()},
{mfa, {ssl_test_lib,
no_result, []}},
- {options,
- [{verify, verify_peer}
- | ClientOpts]}]),
+ {options, ClientOpts}]),
%% Client error may be {tls_alert, "handshake failure" } or closed depending on timing
%% this is not a bug it is a circumstance of how tcp works!
@@ -544,7 +574,7 @@ cert_expired() ->
cert_expired(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
PrivDir = proplists:get_value(priv_dir, Config),
KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
@@ -607,11 +637,11 @@ two_digits_str(N) ->
lists:flatten(io_lib:format("~p", [N])).
%%--------------------------------------------------------------------
-extended_key_usage_verify_peer() ->
- [{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode"}].
+extended_key_usage_verify_server() ->
+ [{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode for server"}].
-extended_key_usage_verify_peer(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+extended_key_usage_verify_server(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
PrivDir = proplists:get_value(priv_dir, Config),
Active = proplists:get_value(active, Config),
@@ -660,7 +690,7 @@ extended_key_usage_verify_peer(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} |
+ {options, [{verify, verify_none}, {active, Active} |
NewClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -669,12 +699,12 @@ extended_key_usage_verify_peer(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-extended_key_usage_verify_none() ->
- [{doc,"Test cert that has a critical extended_key_usage extension in verify_none mode"}].
+extended_key_usage_verify_client() ->
+ [{doc,"Test cert that has a critical extended_key_usage extension in client verify_peer mode"}].
-extended_key_usage_verify_none(Config) when is_list(Config) ->
+extended_key_usage_verify_client(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
PrivDir = proplists:get_value(priv_dir, Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
@@ -730,11 +760,11 @@ extended_key_usage_verify_none(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-critical_extension_verify_peer() ->
+critical_extension_verify_server() ->
[{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
-critical_extension_verify_peer(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+critical_extension_verify_server(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
PrivDir = proplists:get_value(priv_dir, Config),
Active = proplists:get_value(active, Config),
@@ -766,7 +796,7 @@ critical_extension_verify_peer(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} | NewClientOpts]}]),
+ {options, [{verify, verify_none}, {active, Active} | NewClientOpts]}]),
%% This certificate has a critical extension that we don't
%% understand. Therefore, verification should fail.
@@ -775,14 +805,60 @@ critical_extension_verify_peer(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ok.
+%%--------------------------------------------------------------------
+
+critical_extension_verify_client() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
+
+critical_extension_verify_client(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Active = proplists:get_value(active, Config),
+ ReceiveFunction = proplists:get_value(receive_function, Config),
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ NewCertName = integer_to_list(erlang:unique_integer()) ++ ".pem",
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ NewServerCertFile = filename:join([PrivDir, "server", NewCertName]),
+ add_critical_netscape_cert_type(ServerCertFile, NewServerCertFile, KeyFile),
+ NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ NewClientCertFile = filename:join([PrivDir, "client", NewCertName]),
+ add_critical_netscape_cert_type(ClientCertFile, NewClientCertFile, KeyFile),
+ NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error(
+ [{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_none}, {active, Active} | NewServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error(
+ [{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_peer}, {active, Active} | NewClientOpts]}]),
+
+ %% This certificate has a critical extension that we don't
+ %% understand. Therefore, verification should fail.
+ tcp_delivery_workaround(Server, {error, {tls_alert, "unsupported certificate"}},
+ Client, {error, {tls_alert, "unsupported certificate"}}),
+
+ ssl_test_lib:close(Server),
+ ok.
%%--------------------------------------------------------------------
critical_extension_verify_none() ->
[{doc,"Test cert that has a critical unknown extension in verify_none mode"}].
critical_extension_verify_none(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
PrivDir = proplists:get_value(priv_dir, Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
@@ -1044,13 +1120,15 @@ client_with_cert_cipher_suites_handshake() ->
client_with_cert_cipher_suites_handshake(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts_digital_signature_only, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_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},
- {ciphers, ssl_test_lib:rsa_non_signed_suites()}
+ {ciphers,
+ ssl_test_lib:rsa_non_signed_suites(proplists:get_value(version, Config))}
| ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
@@ -1070,7 +1148,7 @@ client_with_cert_cipher_suites_handshake(Config) when is_list(Config) ->
server_verify_no_cacerts() ->
[{doc,"Test server must have cacerts if it wants to verify client"}].
server_verify_no_cacerts(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ ServerOpts = proplists:delete(cacertfile, ssl_test_lib:ssl_options(server_opts, Config)),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
@@ -1084,7 +1162,7 @@ server_verify_no_cacerts(Config) when is_list(Config) ->
unknown_server_ca_fail() ->
[{doc,"Test that the client fails if the ca is unknown in verify_peer mode"}].
unknown_server_ca_fail(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
@@ -1128,7 +1206,7 @@ unknown_server_ca_fail(Config) when is_list(Config) ->
unknown_server_ca_accept_verify_none() ->
[{doc,"Test that the client succeds if the ca is unknown in verify_none mode"}].
unknown_server_ca_accept_verify_none(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -1153,8 +1231,8 @@ unknown_server_ca_accept_verify_peer() ->
[{doc, "Test that the client succeds if the ca is unknown in verify_peer mode"
" with a verify_fun that accepts the unknown ca error"}].
unknown_server_ca_accept_verify_peer(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
@@ -1192,7 +1270,7 @@ unknown_server_ca_accept_verify_peer(Config) when is_list(Config) ->
unknown_server_ca_accept_backwardscompatibility() ->
[{doc,"Test that old style verify_funs will work"}].
unknown_server_ca_accept_backwardscompatibility(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index 00636e5660..e293d183f7 100644
--- a/lib/ssl/test/ssl_crl_SUITE.erl
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -72,7 +72,7 @@ init_per_suite(Config) ->
false ->
{skip, io_lib:format("Bad openssl version: ~p",[OpenSSL_version])};
_ ->
- catch crypto:stop(),
+ end_per_suite(Config),
try crypto:start() of
ok ->
{ok, Hostname0} = inet:gethostname(),
@@ -99,32 +99,37 @@ init_per_group(check_peer, Config) ->
init_per_group(check_best_effort, Config) ->
[{crl_check, best_effort} | Config];
init_per_group(Group, Config0) ->
- case is_idp(Group) of
- true ->
- [{idp_crl, true} | Config0];
- false ->
- DataDir = proplists:get_value(data_dir, Config0),
- CertDir = filename:join(proplists:get_value(priv_dir, Config0), Group),
- {CertOpts, Config} = init_certs(CertDir, Group, Config0),
- {ok, _} = make_certs:all(DataDir, CertDir, CertOpts),
- case Group of
- crl_hash_dir ->
- CrlDir = filename:join(CertDir, "crls"),
- %% Copy CRLs to their hashed filenames.
- %% Find the hashes with 'openssl crl -noout -hash -in crl.pem'.
- populate_crl_hash_dir(CertDir, CrlDir,
- [{"erlangCA", "d6134ed3"},
- {"otpCA", "d4c8d7e5"}],
- replace),
- CrlCacheOpts = [{crl_cache,
- {ssl_crl_hash_dir,
- {internal, [{dir, CrlDir}]}}}];
- _ ->
- CrlCacheOpts = []
- end,
- [{crl_cache_opts, CrlCacheOpts},
- {cert_dir, CertDir},
- {idp_crl, false} | Config]
+ try
+ case is_idp(Group) of
+ true ->
+ [{idp_crl, true} | Config0];
+ false ->
+ DataDir = proplists:get_value(data_dir, Config0),
+ CertDir = filename:join(proplists:get_value(priv_dir, Config0), Group),
+ {CertOpts, Config} = init_certs(CertDir, Group, Config0),
+ {ok, _} = make_certs:all(DataDir, CertDir, CertOpts),
+ CrlCacheOpts = case Group of
+ crl_hash_dir ->
+ CrlDir = filename:join(CertDir, "crls"),
+ %% Copy CRLs to their hashed filenames.
+ %% Find the hashes with 'openssl crl -noout -hash -in crl.pem'.
+ populate_crl_hash_dir(CertDir, CrlDir,
+ [{"erlangCA", "d6134ed3"},
+ {"otpCA", "d4c8d7e5"}],
+ replace),
+ [{crl_cache,
+ {ssl_crl_hash_dir,
+ {internal, [{dir, CrlDir}]}}}];
+ _ ->
+ []
+ end,
+ [{crl_cache_opts, CrlCacheOpts},
+ {cert_dir, CertDir},
+ {idp_crl, false} | Config]
+ end
+ catch
+ _:_ ->
+ {skip, "Unable to create crls"}
end.
end_per_group(_GroupName, Config) ->
@@ -136,7 +141,7 @@ init_per_testcase(Case, Config0) ->
true ->
end_per_testcase(Case, Config0),
inets:start(),
- ssl:start(),
+ ssl_test_lib:clean_start(),
ServerRoot = make_dir_path([proplists:get_value(priv_dir, Config0), idp_crl, tmp]),
%% start a HTTP server to serve the CRLs
{ok, Httpd} = inets:start(httpd, [{ipfamily, proplists:get_value(ipfamily, Config0)},
@@ -155,7 +160,7 @@ init_per_testcase(Case, Config0) ->
[{cert_dir, CertDir} | Config];
false ->
end_per_testcase(Case, Config0),
- ssl:start(),
+ ssl_test_lib:clean_start(),
Config0
end.
@@ -187,7 +192,7 @@ crl_verify_valid(Config) when is_list(Config) ->
{crl_cache, {ssl_crl_cache, {internal, [{http, 5000}]}}},
{verify, verify_peer}];
false ->
- ?config(crl_cache_opts, Config) ++
+ proplists:get_value(crl_cache_opts, Config) ++
[{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
{crl_check, Check},
{verify, verify_peer}]
@@ -220,7 +225,7 @@ crl_verify_revoked(Config) when is_list(Config) ->
{crl_check, Check},
{verify, verify_peer}];
false ->
- ?config(crl_cache_opts, Config) ++
+ proplists:get_value(crl_cache_opts, Config) ++
[{cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])},
{crl_check, Check},
{verify, verify_peer}]
@@ -279,8 +284,8 @@ crl_verify_no_crl(Config) when is_list(Config) ->
crl_hash_dir_collision() ->
[{doc,"Verify ssl_crl_hash_dir behaviour with hash collisions"}].
crl_hash_dir_collision(Config) when is_list(Config) ->
- PrivDir = ?config(cert_dir, Config),
- Check = ?config(crl_check, Config),
+ PrivDir = proplists:get_value(cert_dir, Config),
+ Check = proplists:get_value(crl_check, Config),
%% Create two CAs whose names hash to the same value
CA1 = "hash-collision-0000000000",
@@ -307,13 +312,17 @@ crl_hash_dir_collision(Config) when is_list(Config) ->
{CA2, "b68fc624"}],
replace),
- ClientOpts = ?config(crl_cache_opts, Config) ++
- [{cacertfile, filename:join([PrivDir, "erlangCA", "cacerts.pem"])},
+ NewCA = new_ca(filename:join([PrivDir, "new_ca"]),
+ filename:join([PrivDir, "erlangCA", "cacerts.pem"]),
+ filename:join([PrivDir, "server", "cacerts.pem"])),
+
+ ClientOpts = proplists:get_value(crl_cache_opts, Config) ++
+ [{cacertfile, NewCA},
{crl_check, Check},
{verify, verify_peer}],
-
+
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
+
%% Neither certificate revoked; both succeed.
crl_verify_valid(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts),
crl_verify_valid(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts),
@@ -346,8 +355,8 @@ crl_hash_dir_collision(Config) when is_list(Config) ->
crl_hash_dir_expired() ->
[{doc,"Verify ssl_crl_hash_dir behaviour with expired CRLs"}].
crl_hash_dir_expired(Config) when is_list(Config) ->
- PrivDir = ?config(cert_dir, Config),
- Check = ?config(crl_check, Config),
+ PrivDir = proplists:get_value(cert_dir, Config),
+ Check = proplists:get_value(crl_check, Config),
CA = "CRL-maybe-expired-CA",
%% Add "issuing distribution point", to ensure that verification
@@ -362,7 +371,7 @@ crl_hash_dir_expired(Config) when is_list(Config) ->
ServerOpts = [{keyfile, filename:join([PrivDir, EndUser, "key.pem"])},
{certfile, filename:join([PrivDir, EndUser, "cert.pem"])},
{cacertfile, filename:join([PrivDir, EndUser, "cacerts.pem"])}],
- ClientOpts = ?config(crl_cache_opts, Config) ++
+ ClientOpts = proplists:get_value(crl_cache_opts, Config) ++
[{cacertfile, filename:join([PrivDir, CA, "cacerts.pem"])},
{crl_check, Check},
{verify, verify_peer}],
@@ -492,3 +501,12 @@ find_free_name(CrlDir, Hash, N) ->
false ->
Name
end.
+
+new_ca(FileName, CA1, CA2) ->
+ {ok, P1} = file:read_file(CA1),
+ E1 = public_key:pem_decode(P1),
+ {ok, P2} = file:read_file(CA2),
+ E2 = public_key:pem_decode(P2),
+ Pem = public_key:pem_encode(E1 ++E2),
+ file:write_file(FileName, Pem),
+ FileName.
diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl
index 5ebf9bb2de..8740e8c8f0 100644
--- a/lib/ssl/test/ssl_dist_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_SUITE.erl
@@ -109,11 +109,11 @@ common_end(_, _Config) ->
basic() ->
[{doc,"Test that two nodes can connect via ssl distribution"}].
basic(Config) when is_list(Config) ->
- NH1 = start_ssl_node(Config),
+ gen_dist_test(basic_test, Config).
+
+basic_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(Config),
Node2 = NH2#node_handle.nodename,
-
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
@@ -161,18 +161,16 @@ basic(Config) when is_list(Config) ->
ok
end
end)
- end,
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ end.
%%--------------------------------------------------------------------
payload() ->
[{doc,"Test that send a lot of data between the ssl distributed noes"}].
payload(Config) when is_list(Config) ->
- NH1 = start_ssl_node(Config),
+ gen_dist_test(payload_test, Config).
+
+payload_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(Config),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
@@ -204,10 +202,8 @@ payload(Config) when is_list(Config) ->
ok
end
end)
- end,
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ end.
+
%%--------------------------------------------------------------------
plain_options() ->
[{doc,"Test specifying additional options"}].
@@ -218,20 +214,17 @@ plain_options(Config) when is_list(Config) ->
"client_verify verify_none server_verify verify_none "
"server_depth 1 client_depth 1 "
"server_hibernate_after 500 client_hibernate_after 500",
+ gen_dist_test(plain_options_test, [{additional_dist_opts, DistOpts} | Config]).
- NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
+plain_options_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
+ [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end).
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
%%--------------------------------------------------------------------
plain_verify_options() ->
[{doc,"Test specifying additional options"}].
@@ -240,20 +233,18 @@ plain_verify_options(Config) when is_list(Config) ->
"client_secure_renegotiate true "
"server_reuse_sessions true client_reuse_sessions true "
"server_hibernate_after 500 client_hibernate_after 500",
+ gen_dist_test(plain_verify_options_test, [{additional_dist_opts, DistOpts} | Config]).
- NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
+plain_verify_options_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
Node2 = NH2#node_handle.nodename,
-
+
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
-
+
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
+ [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end).
+
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
%%--------------------------------------------------------------------
nodelay_option() ->
[{doc,"Test specifying dist_nodelay option"}].
@@ -265,6 +256,7 @@ nodelay_option(Config) ->
after
application:unset_env(kernel, dist_nodelay)
end.
+%%--------------------------------------------------------------------
listen_port_options() ->
[{doc, "Test specifying listening ports"}].
@@ -285,32 +277,39 @@ listen_port_options(Config) when is_list(Config) ->
#node_handle{} ->
%% If the node was able to start, it didn't take the port
%% option into account.
+ stop_ssl_node(NH1),
exit(unexpected_success)
catch
exit:{accept_failed, timeout} ->
%% The node failed to start, as expected.
ok
end,
-
+
%% Try again, now specifying a high max port.
PortOpt2 = "-kernel inet_dist_listen_min " ++ integer_to_list(Port1) ++
- " inet_dist_listen_max 65535",
+ " inet_dist_listen_max 65535",
NH2 = start_ssl_node([{additional_dist_opts, PortOpt2} | Config]),
- Node2 = NH2#node_handle.nodename,
- Name2 = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node2)),
- {ok, NodesPorts2} = apply_on_ssl_node(NH2, fun net_adm:names/0),
- {Name2, Port2} = lists:keyfind(Name2, 1, NodesPorts2),
-
- %% The new port should be higher:
- if Port2 > Port1 ->
- ok;
- true ->
- error({port, Port2, not_higher_than, Port1})
+
+ try
+ Node2 = NH2#node_handle.nodename,
+ Name2 = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node2)),
+ {ok, NodesPorts2} = apply_on_ssl_node(NH2, fun net_adm:names/0),
+ {Name2, Port2} = lists:keyfind(Name2, 1, NodesPorts2),
+
+ %% The new port should be higher:
+ if Port2 > Port1 ->
+ ok;
+ true ->
+ error({port, Port2, not_higher_than, Port1})
+ end
+ catch
+ _:Reason ->
+ stop_ssl_node(NH2),
+ ct:fail(Reason)
end,
-
- stop_ssl_node(NH1),
stop_ssl_node(NH2),
success(Config).
+
%%--------------------------------------------------------------------
listen_options() ->
[{doc, "Test inet_dist_listen_options"}].
@@ -329,28 +328,25 @@ do_listen_options(Prio, Config) ->
end,
Options = "-kernel inet_dist_listen_options " ++ PriorityString,
-
- NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]),
- NH2 = start_ssl_node([{additional_dist_opts, Options} | Config]),
- Node2 = NH2#node_handle.nodename,
-
+ gen_dist_test(listen_options_test, [{prio, Prio}, {additional_dist_opts, Options} | Config]).
+
+listen_options_test(NH1, NH2, Config) ->
+ Prio = proplists:get_value(prio, Config),
+ Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
PrioritiesNode1 =
apply_on_ssl_node(NH1, fun get_socket_priorities/0),
PrioritiesNode2 =
apply_on_ssl_node(NH2, fun get_socket_priorities/0),
-
+
Elevated1 = [P || P <- PrioritiesNode1, P =:= Prio],
- ?t:format("Elevated1: ~p~n", [Elevated1]),
+ ct:pal("Elevated1: ~p~n", [Elevated1]),
Elevated2 = [P || P <- PrioritiesNode2, P =:= Prio],
- ?t:format("Elevated2: ~p~n", [Elevated2]),
+ ct:pal("Elevated2: ~p~n", [Elevated2]),
[_|_] = Elevated1,
- [_|_] = Elevated2,
+ [_|_] = Elevated2.
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
%%--------------------------------------------------------------------
connect_options() ->
[{doc, "Test inet_dist_connect_options"}].
@@ -369,9 +365,11 @@ do_connect_options(Prio, Config) ->
end,
Options = "-kernel inet_dist_connect_options " ++ PriorityString,
+ gen_dist_test(connect_options_test,
+ [{prio, Prio}, {additional_dist_opts, Options} | Config]).
- NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]),
- NH2 = start_ssl_node([{additional_dist_opts, Options} | Config]),
+connect_options_test(NH1, NH2, Config) ->
+ Prio = proplists:get_value(prio, Config),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
@@ -382,17 +380,14 @@ do_connect_options(Prio, Config) ->
apply_on_ssl_node(NH2, fun get_socket_priorities/0),
Elevated1 = [P || P <- PrioritiesNode1, P =:= Prio],
- ?t:format("Elevated1: ~p~n", [Elevated1]),
+ ct:pal("Elevated1: ~p~n", [Elevated1]),
Elevated2 = [P || P <- PrioritiesNode2, P =:= Prio],
- ?t:format("Elevated2: ~p~n", [Elevated2]),
+ ct:pal("Elevated2: ~p~n", [Elevated2]),
%% Node 1 will have a socket with elevated priority.
[_|_] = Elevated1,
%% Node 2 will not, since it only applies to outbound connections.
- [] = Elevated2,
+ [] = Elevated2.
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
%%--------------------------------------------------------------------
use_interface() ->
[{doc, "Test inet_dist_use_interface"}].
@@ -403,22 +398,28 @@ use_interface(Config) when is_list(Config) ->
%% Start a node, and get the port number it's listening on.
NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]),
- Node1 = NH1#node_handle.nodename,
- Name = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node1)),
- {ok, NodesPorts} = apply_on_ssl_node(NH1, fun net_adm:names/0),
- {Name, Port} = lists:keyfind(Name, 1, NodesPorts),
-
- %% Now find the socket listening on that port, and check its sockname.
- Sockets = apply_on_ssl_node(
- NH1,
- fun() ->
- [inet:sockname(P) ||
- P <- inet_ports(),
- {ok, Port} =:= (catch inet:port(P))]
- end),
- %% And check that it's actually listening on localhost.
- [{ok,{{127,0,0,1},Port}}] = Sockets,
-
+
+ try
+ Node1 = NH1#node_handle.nodename,
+ Name = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node1)),
+ {ok, NodesPorts} = apply_on_ssl_node(NH1, fun net_adm:names/0),
+ {Name, Port} = lists:keyfind(Name, 1, NodesPorts),
+
+ %% Now find the socket listening on that port, and check its sockname.
+ Sockets = apply_on_ssl_node(
+ NH1,
+ fun() ->
+ [inet:sockname(P) ||
+ P <- inet_ports(),
+ {ok, Port} =:= (catch inet:port(P))]
+ end),
+ %% And check that it's actually listening on localhost.
+ [{ok,{{127,0,0,1},Port}}] = Sockets
+ catch
+ _:Reason ->
+ stop_ssl_node(NH1),
+ ct:fail(Reason)
+ end,
stop_ssl_node(NH1),
success(Config).
%%--------------------------------------------------------------------
@@ -430,11 +431,11 @@ verify_fun_fail(Config) when is_list(Config) ->
"\"{ssl_dist_SUITE,verify_fail_always,{}}\" "
"client_verify verify_peer client_verify_fun "
"\"{ssl_dist_SUITE,verify_fail_always,{}}\" ",
+ gen_dist_test(verify_fun_fail_test, [{additional_dist_opts, DistOpts} | Config]).
- NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
- NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
+verify_fun_fail_test(NH1, NH2, _) ->
Node2 = NH2#node_handle.nodename,
-
+
pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[] = apply_on_ssl_node(NH1, fun () -> nodes() end),
@@ -446,25 +447,9 @@ verify_fun_fail(Config) when is_list(Config) ->
%% On the server node, it wouldn't run, because the server didn't
%% request a certificate from the client.
undefined =
- apply_on_ssl_node(NH2, fun () -> ets:info(verify_fun_ran) end),
+ apply_on_ssl_node(NH2, fun () -> ets:info(verify_fun_ran) end).
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
-verify_fail_always(_Certificate, _Event, _State) ->
- %% Create an ETS table, to record the fact that the verify function ran.
- %% Spawn a new process, to avoid the ETS table disappearing.
- Parent = self(),
- spawn(
- fun() ->
- ets:new(verify_fun_ran, [public, named_table]),
- ets:insert(verify_fun_ran, {verify_fail_always_ran, true}),
- Parent ! go_ahead,
- timer:sleep(infinity)
- end),
- receive go_ahead -> ok end,
- {fail, bad_certificate}.
%%--------------------------------------------------------------------
verify_fun_pass() ->
@@ -476,10 +461,10 @@ verify_fun_pass(Config) when is_list(Config) ->
"server_fail_if_no_peer_cert true "
"client_verify verify_peer client_verify_fun "
"\"{ssl_dist_SUITE,verify_pass_always,{}}\" ",
+ gen_dist_test(verify_fun_pass_test, [{additional_dist_opts, DistOpts} | Config]).
- NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
+verify_fun_pass_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
@@ -494,25 +479,8 @@ verify_fun_pass(Config) when is_list(Config) ->
%% requested and verified the client's certificate because we
%% passed fail_if_no_peer_cert.
[{verify_pass_always_ran, true}] =
- apply_on_ssl_node(NH2, fun () -> ets:tab2list(verify_fun_ran) end),
+ apply_on_ssl_node(NH2, fun () -> ets:tab2list(verify_fun_ran) end).
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
-
-verify_pass_always(_Certificate, _Event, State) ->
- %% Create an ETS table, to record the fact that the verify function ran.
- %% Spawn a new process, to avoid the ETS table disappearing.
- Parent = self(),
- spawn(
- fun() ->
- ets:new(verify_fun_ran, [public, named_table]),
- ets:insert(verify_fun_ran, {verify_pass_always_ran, true}),
- Parent ! go_ahead,
- timer:sleep(infinity)
- end),
- receive go_ahead -> ok end,
- {valid, State}.
%%--------------------------------------------------------------------
crl_check_pass() ->
[{doc,"Test crl_check with non-revoked certificate"}].
@@ -520,10 +488,10 @@ crl_check_pass(Config) when is_list(Config) ->
DistOpts = "-ssl_dist_opt client_crl_check true",
NewConfig =
[{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config,
+ gen_dist_test(crl_check_pass_test, NewConfig).
- NH1 = start_ssl_node(NewConfig),
+crl_check_pass_test(NH1, NH2, Config) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(NewConfig),
Node2 = NH2#node_handle.nodename,
PrivDir = ?config(priv_dir, Config),
@@ -533,11 +501,7 @@ crl_check_pass(Config) when is_list(Config) ->
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
-
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end).
%%--------------------------------------------------------------------
crl_check_fail() ->
@@ -549,10 +513,9 @@ crl_check_fail(Config) when is_list(Config) ->
%% The server uses a revoked certificate.
{server_cert_dir, "revoked"},
{additional_dist_opts, DistOpts}] ++ Config,
+ gen_dist_test(crl_check_fail_test, NewConfig).
- NH1 = start_ssl_node(NewConfig),
- %%Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(NewConfig),
+crl_check_fail_test(NH1, NH2, Config) ->
Node2 = NH2#node_handle.nodename,
PrivDir = ?config(priv_dir, Config),
@@ -562,11 +525,7 @@ crl_check_fail(Config) when is_list(Config) ->
pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [] = apply_on_ssl_node(NH2, fun () -> nodes() end),
-
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ [] = apply_on_ssl_node(NH2, fun () -> nodes() end).
%%--------------------------------------------------------------------
crl_check_best_effort() ->
@@ -576,22 +535,18 @@ crl_check_best_effort(Config) when is_list(Config) ->
"server_verify verify_peer server_crl_check best_effort",
NewConfig =
[{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config,
+ gen_dist_test(crl_check_best_effort_test, NewConfig).
+crl_check_best_effort_test(NH1, NH2, _Config) ->
%% We don't have the correct CRL at hand, but since crl_check is
%% best_effort, we accept it anyway.
- NH1 = start_ssl_node(NewConfig),
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(NewConfig),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
-
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end).
%%--------------------------------------------------------------------
crl_cache_check_pass() ->
@@ -605,20 +560,16 @@ crl_cache_check_pass(Config) when is_list(Config) ->
"\"{ssl_dist_SUITE,{\\\"" ++ NodeDir ++ "\\\",[]}}\"",
NewConfig =
[{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config,
+ gen_dist_test(crl_cache_check_pass_test, NewConfig).
- NH1 = start_ssl_node(NewConfig),
+crl_cache_check_pass_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(NewConfig),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
-
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end).
%%--------------------------------------------------------------------
crl_cache_check_fail() ->
@@ -636,44 +587,31 @@ crl_cache_check_fail(Config) when is_list(Config) ->
{server_cert_dir, "revoked"},
{additional_dist_opts, DistOpts}] ++ Config,
- NH1 = start_ssl_node(NewConfig),
- NH2 = start_ssl_node(NewConfig),
- Node2 = NH2#node_handle.nodename,
+ gen_dist_test(crl_cache_check_fail_test, NewConfig).
+crl_cache_check_fail_test(NH1, NH2, _) ->
+ Node2 = NH2#node_handle.nodename,
pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [] = apply_on_ssl_node(NH2, fun () -> nodes() end),
-
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
-
-%% ssl_crl_cache_api callbacks
-lookup(_DistributionPoint, _DbHandle) ->
- not_available.
-
-select({rdnSequence, NameParts}, {NodeDir, _}) ->
- %% Extract the CN from the issuer name...
- [CN] = [CN ||
- [#'AttributeTypeAndValue'{
- type = ?'id-at-commonName',
- value = <<_, _, CN/binary>>}] <- NameParts],
- %% ...and use that as the directory name to find the CRL.
- error_logger:info_report([{found_cn, CN}]),
- CRLFile = filename:join([NodeDir, CN, "crl.pem"]),
- {ok, PemBin} = file:read_file(CRLFile),
- PemEntries = public_key:pem_decode(PemBin),
- CRLs = [ CRL || {'CertificateList', CRL, not_encrypted}
- <- PemEntries],
- CRLs.
-
-fresh_crl(_DistributionPoint, CRL) ->
- CRL.
-
+ [] = apply_on_ssl_node(NH2, fun () -> nodes() end).
%%--------------------------------------------------------------------
%%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
+gen_dist_test(Test, Config) ->
+ NH1 = start_ssl_node(Config),
+ NH2 = start_ssl_node(Config),
+ try
+ ?MODULE:Test(NH1, NH2, Config)
+ catch
+ _:Reason ->
+ stop_ssl_node(NH1),
+ stop_ssl_node(NH2),
+ ct:fail(Reason)
+ end,
+ stop_ssl_node(NH1),
+ stop_ssl_node(NH2),
+ success(Config).
%% ssl_node side api
%%
@@ -742,13 +680,15 @@ stop_ssl_node(#node_handle{connection_handler = Handler,
receive
{'DOWN', Mon, process, Handler, Reason} ->
case Reason of
- normal -> ok;
- _ -> exit(Reason)
+ normal ->
+ ok;
+ _ ->
+ ct:pal("Down ~p ~n", [Reason])
end
end;
Error ->
erlang:demonitor(Mon, [flush]),
- exit(Error)
+ ct:pal("Warning ~p ~n", [Error])
end.
start_ssl_node(Config) ->
@@ -1226,3 +1166,53 @@ vsn(App) ->
after
application:stop(ssl)
end.
+
+verify_fail_always(_Certificate, _Event, _State) ->
+ %% Create an ETS table, to record the fact that the verify function ran.
+ %% Spawn a new process, to avoid the ETS table disappearing.
+ Parent = self(),
+ spawn(
+ fun() ->
+ ets:new(verify_fun_ran, [public, named_table]),
+ ets:insert(verify_fun_ran, {verify_fail_always_ran, true}),
+ Parent ! go_ahead,
+ timer:sleep(infinity)
+ end),
+ receive go_ahead -> ok end,
+ {fail, bad_certificate}.
+
+verify_pass_always(_Certificate, _Event, State) ->
+ %% Create an ETS table, to record the fact that the verify function ran.
+ %% Spawn a new process, to avoid the ETS table disappearing.
+ Parent = self(),
+ spawn(
+ fun() ->
+ ets:new(verify_fun_ran, [public, named_table]),
+ ets:insert(verify_fun_ran, {verify_pass_always_ran, true}),
+ Parent ! go_ahead,
+ timer:sleep(infinity)
+ end),
+ receive go_ahead -> ok end,
+ {valid, State}.
+
+%% ssl_crl_cache_api callbacks
+lookup(_DistributionPoint, _DbHandle) ->
+ not_available.
+
+select({rdnSequence, NameParts}, {NodeDir, _}) ->
+ %% Extract the CN from the issuer name...
+ [CN] = [CN ||
+ [#'AttributeTypeAndValue'{
+ type = ?'id-at-commonName',
+ value = <<_, _, CN/binary>>}] <- NameParts],
+ %% ...and use that as the directory name to find the CRL.
+ error_logger:info_report([{found_cn, CN}]),
+ CRLFile = filename:join([NodeDir, CN, "crl.pem"]),
+ {ok, PemBin} = file:read_file(CRLFile),
+ PemEntries = public_key:pem_decode(PemBin),
+ CRLs = [ CRL || {'CertificateList', CRL, not_encrypted}
+ <- PemEntries],
+ CRLs.
+
+fresh_crl(_DistributionPoint, CRL) ->
+ CRL.
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index a671e3e307..9658cb5f56 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -33,6 +33,7 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
all() -> [decode_hello_handshake,
+ decode_hello_handshake_version_confusion,
decode_single_hello_extension_correctly,
decode_supported_elliptic_curves_hello_extension_correctly,
decode_unknown_hello_extension_correctly,
@@ -60,7 +61,7 @@ init_per_testcase(ignore_hassign_extension_pre_tls_1_2, Config0) ->
ok ->
case is_supported(sha512) of
true ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
%% make rsa certs using oppenssl
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
@@ -106,6 +107,14 @@ decode_hello_handshake(_Config) ->
#renegotiation_info{renegotiated_connection = <<0>>}
= (Hello#server_hello.extensions)#hello_extensions.renegotiation_info.
+
+decode_hello_handshake_version_confusion(_) ->
+ HelloPacket = <<3,3,0,0,0,0,0,63,210,235,149,6,244,140,108,13,177,74,16,218,33,108,219,41,73,228,3,82,132,123,73,144,118,100,0,0,32,192,4,0,10,192,45,192,38,0,47,192,18,0,163,0,22,0,165,192,29,192,18,192,30,0,103,0,57,192,48,0,47,1,0>>,
+ Version = {3,3},
+ ClientHello = 1,
+ Hello = tls_handshake:decode_handshake({3,3}, ClientHello, HelloPacket, false),
+ Hello = tls_handshake:decode_handshake({3,3}, ClientHello, HelloPacket, true).
+
decode_single_hello_extension_correctly(_Config) ->
Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>,
Extensions = ssl_handshake:decode_hello_extensions(Renegotiation),
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
index c55fa73cfb..a02881f1ae 100644
--- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
@@ -68,7 +68,7 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config),
proplists:get_value(priv_dir, Config)),
ssl_test_lib:cert_options(Config)
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
index 00eb9fee4f..35af666e9e 100644
--- a/lib/ssl/test/ssl_npn_hello_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -41,6 +41,19 @@ all() ->
create_server_hello_with_advertised_protocols_test,
create_server_hello_with_no_advertised_protocols_test].
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ %% This function is required since init_per_suite/1 exists.
+ ok.
+
init_per_testcase(_TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 5}),
@@ -126,15 +139,12 @@ create_server_handshake(Npn) ->
}, Vsn).
create_connection_states() ->
- #connection_states{
- pending_read = #connection_state{
- security_parameters = #security_parameters{
+ #{pending_read => #{security_parameters => #security_parameters{
server_random = <<1:256>>,
compression_algorithm = 1,
cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA
}
- },
- current_read = #connection_state {
- secure_renegotiation = false
- }
- }.
+ },
+ current_read => #{secure_renegotiation => false
+ }
+ }.
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index e49d432c21..3446a566c4 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -41,9 +41,9 @@
-define(MANY, 1000).
-define(SOME, 50).
--define(BASE_TIMEOUT_SECONDS, 15).
--define(SOME_SCALE, 20).
--define(MANY_SCALE, 20).
+-define(BASE_TIMEOUT_SECONDS, 5).
+-define(SOME_SCALE, 2).
+-define(MANY_SCALE, 3).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -140,7 +140,7 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config),
proplists:get_value(priv_dir, Config)),
ssl_test_lib:cert_options(Config)
@@ -162,6 +162,7 @@ init_per_group(GroupName, Config) ->
{skip, "Missing crypto support"}
end;
_ ->
+ ssl:stop(),
ssl:start(),
Config
end.
@@ -276,6 +277,7 @@ packet_raw_active_once_many_small() ->
[{doc,"Test packet option {packet, raw} in active once mode."}].
packet_raw_active_once_many_small(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?MANY_SCALE}),
Data = "Packet option is {packet, raw}",
packet(Config, Data, send_raw, active_once_raw, ?MANY, raw, once).
@@ -392,6 +394,7 @@ packet_0_active_some_big() ->
[{doc,"Test packet option {packet, 0} in active mode."}].
packet_0_active_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, active_raw, ?SOME, 0, true).
@@ -427,6 +430,7 @@ packet_2_active_some_big() ->
[{doc,"Test packet option {packet, 2} in active mode"}].
packet_2_active_some_big(Config) when is_list(Config) ->
+ ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS * ?SOME_SCALE}),
Data = lists:append(lists:duplicate(100, "1234567890")),
packet(Config, Data, send, active_packet, ?SOME, 2, true).
@@ -1900,6 +1904,31 @@ header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
+
+packet(Config, Data, Send, Recv, Quantity, Packet, Active) when Packet == 0;
+ Packet == raw ->
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, Send ,[Data, Quantity]}},
+ {options, [{nodelay, true},{packet, Packet} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, Recv, [Data, Quantity]}},
+ {options, [{active, Active}, {nodelay, true},
+ {packet, Packet} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client);
+
packet(Config, Data, Send, Recv, Quantity, Packet, Active) ->
ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
@@ -1982,26 +2011,19 @@ active_once_raw(Socket, Data, N) ->
active_once_raw(_, _, 0, _) ->
ok;
-active_once_raw(Socket, Data, N, Acc) ->
- receive
- {ssl, Socket, Byte} when length(Byte) == 1 ->
- ssl:setopts(Socket, [{active, once}]),
+active_once_raw(Socket, Data, N, Acc0) ->
+ case lists:prefix(Data, Acc0) of
+ true ->
+ DLen = length(Data),
+ Start = DLen + 1,
+ Len = length(Acc0) - DLen,
+ Acc = string:substr(Acc0, Start, Len),
+ active_once_raw(Socket, Data, N-1, Acc);
+ false ->
receive
- {ssl, Socket, _} ->
- ssl:setopts(Socket, [{active, once}]),
- active_once_raw(Socket, Data, N-1, [])
- end;
- {ssl, Socket, Data} ->
- ssl:setopts(Socket, [{active, once}]),
- active_once_raw(Socket, Data, N-1, []);
- {ssl, Socket, Other} ->
- case Acc ++ Other of
- Data ->
- ssl:setopts(Socket, [{active, once}]),
- active_once_raw(Socket, Data, N-1, []);
- NewAcc ->
+ {ssl, Socket, Info} ->
ssl:setopts(Socket, [{active, once}]),
- active_once_raw(Socket, Data, N, NewAcc)
+ active_once_raw(Socket, Data, N, Acc0 ++ Info)
end
end.
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index cb0571d0a7..cb1957327a 100644
--- a/lib/ssl/test/ssl_payload_SUITE.erl
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -70,7 +70,7 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)),
ssl_test_lib:cert_options(Config)
catch _:_ ->
@@ -104,8 +104,13 @@ init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_huge;
TestCase == client_echos_passive_huge;
TestCase == client_echos_active_once_huge;
TestCase == client_echos_active_huge ->
- ct:timetrap({seconds, 90}),
- Config;
+ case erlang:system_info(system_architecture) of
+ "sparc-sun-solaris2.10" ->
+ {skip,"Will take to long time on an old Sparc"};
+ _ ->
+ ct:timetrap({seconds, 90}),
+ Config
+ end;
init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_big;
TestCase == server_echos_active_once_big;
diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl
index 13b0ce8ed9..96b15d9b51 100644
--- a/lib/ssl/test/ssl_pem_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-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,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
%% make rsa certs using oppenssl
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
@@ -63,14 +63,15 @@ end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(pem_cleanup = Case, Config) ->
- end_per_testcase(Case, Config) ,
application:load(ssl),
+ end_per_testcase(Case, Config) ,
application:set_env(ssl, ssl_pem_cache_clean, ?CLEANUP_INTERVAL),
ssl:start(),
ct:timetrap({minutes, 1}),
Config.
end_per_testcase(_TestCase, Config) ->
+ ssl_test_lib:clean_env(),
ssl:stop(),
Config.
@@ -81,8 +82,8 @@ pem_cleanup() ->
[{doc, "Test pem cache invalidate mechanism"}].
pem_cleanup(Config)when is_list(Config) ->
process_flag(trap_exit, true),
- ClientOpts = proplists:get_value(client_opts, Config),
- ServerOpts = proplists:get_value(server_opts, Config),
+ ClientOpts = proplists:get_value(client_verification_opts, Config),
+ ServerOpts = proplists:get_value(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server =
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index b352844ba0..9862b3ce64 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.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.
@@ -58,7 +58,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
%% make rsa certs using
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl
index 34ef2e6af9..4e916a7f03 100644
--- a/lib/ssl/test/ssl_sni_SUITE.erl
+++ b/lib/ssl/test/ssl_sni_SUITE.erl
@@ -41,7 +41,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
ssl_test_lib:cert_options(Config0)
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 27c670cdc2..56b8a8a22d 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -278,8 +278,11 @@ check_result(Server, ServerMsg, Client, ClientMsg) ->
check_result(Server, ServerMsg);
{Port, {data,Debug}} when is_port(Port) ->
- ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
+ ct:log("~p:~p~n Openssl ~s~n",[?MODULE,?LINE, Debug]),
check_result(Server, ServerMsg, Client, ClientMsg);
+ {Port,closed} when is_port(Port) ->
+ ct:log("~p:~p~n Openssl port ~n",[?MODULE,?LINE]),
+ check_result(Server, ServerMsg, Client, ClientMsg);
Unexpected ->
Reason = {{expected, {Client, ClientMsg}},
{expected, {Server, ServerMsg}}, {got, Unexpected}},
@@ -291,11 +294,11 @@ check_result(Pid, Msg) ->
{Pid, Msg} ->
ok;
{Port, {data,Debug}} when is_port(Port) ->
- ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
+ ct:log("~p:~p~n Openssl ~s~n",[?MODULE,?LINE, Debug]),
check_result(Pid,Msg);
- %% {Port, {exit_status, Status}} when is_port(Port) ->
- %% ct:log("~p:~p Exit status: ~p~n",[?MODULE,?LINE, Status]),
- %% check_result(Pid, Msg);
+ {Port,closed} when is_port(Port)->
+ ct:log("~p:~p Openssl port closed ~n",[?MODULE,?LINE]),
+ check_result(Pid, Msg);
Unexpected ->
Reason = {{expected, {Pid, Msg}},
{got, Unexpected}},
@@ -385,7 +388,9 @@ cert_options(Config) ->
SNIServerAKeyFile = filename:join([proplists:get_value(priv_dir, Config), "a.server", "key.pem"]),
SNIServerBCertFile = filename:join([proplists:get_value(priv_dir, Config), "b.server", "cert.pem"]),
SNIServerBKeyFile = filename:join([proplists:get_value(priv_dir, Config), "b.server", "key.pem"]),
- [{client_opts, []},
+ [{client_opts, [{cacertfile, ClientCaCertFile},
+ {certfile, ClientCertFile},
+ {keyfile, ClientKeyFile}]},
{client_verification_opts, [{cacertfile, ServerCaCertFile},
{certfile, ClientCertFile},
{keyfile, ClientKeyFile},
@@ -394,29 +399,24 @@ cert_options(Config) ->
{certfile, ClientCertFileDigitalSignatureOnly},
{keyfile, ClientKeyFile},
{ssl_imp, new}]},
- {server_opts, [{ssl_imp, new},{reuseaddr, true},
+ {server_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
- {server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]},
- {client_psk, [{ssl_imp, new},{reuseaddr, true},
+ {client_psk, [{ssl_imp, new},
{psk_identity, "Test-User"},
{user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
{server_psk, [{ssl_imp, new},{reuseaddr, true},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
- {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}},
- {ciphers, psk_suites()}]},
+ {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
{server_psk_hint, [{ssl_imp, new},{reuseaddr, true},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
{psk_identity, "HINT"},
- {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}},
- {ciphers, psk_suites()}]},
+ {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
{server_psk_anon, [{ssl_imp, new},{reuseaddr, true},
- {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}},
- {ciphers, psk_anon_suites()}]},
+ {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
{server_psk_anon_hint, [{ssl_imp, new},{reuseaddr, true},
{psk_identity, "HINT"},
- {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}},
- {ciphers, psk_anon_suites()}]},
- {client_srp, [{ssl_imp, new},{reuseaddr, true},
+ {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
+ {client_srp, [{ssl_imp, new},
{srp_identity, {"Test-User", "secret"}}]},
{server_srp, [{ssl_imp, new},{reuseaddr, true},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
@@ -460,9 +460,10 @@ cert_options(Config) ->
make_dsa_cert(Config) ->
-
- {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, dsa, dsa, ""),
- {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, dsa, dsa, ""),
+ {ServerCaCertFile, ServerCertFile, ServerKeyFile} =
+ make_cert_files("server", Config, dsa, dsa, "", []),
+ {ClientCaCertFile, ClientCertFile, ClientKeyFile} =
+ make_cert_files("client", Config, dsa, dsa, "", []),
[{server_dsa_opts, [{ssl_imp, new},{reuseaddr, true},
{cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
@@ -470,7 +471,7 @@ make_dsa_cert(Config) ->
{cacertfile, ClientCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
{verify, verify_peer}]},
- {client_dsa_opts, [{ssl_imp, new},{reuseaddr, true},
+ {client_dsa_opts, [{ssl_imp, new},
{cacertfile, ClientCaCertFile},
{certfile, ClientCertFile}, {keyfile, ClientKeyFile}]},
{server_srp_dsa, [{ssl_imp, new},{reuseaddr, true},
@@ -478,7 +479,7 @@ make_dsa_cert(Config) ->
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
{user_lookup_fun, {fun user_lookup/3, undefined}},
{ciphers, srp_dss_suites()}]},
- {client_srp_dsa, [{ssl_imp, new},{reuseaddr, true},
+ {client_srp_dsa, [{ssl_imp, new},
{srp_identity, {"Test-User", "secret"}},
{cacertfile, ClientCaCertFile},
{certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}
@@ -488,16 +489,18 @@ make_ecdsa_cert(Config) ->
CryptoSupport = crypto:supports(),
case proplists:get_bool(ecdsa, proplists:get_value(public_keys, CryptoSupport)) of
true ->
- {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, ec, ec, ""),
- {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, ec, ec, ""),
+ {ServerCaCertFile, ServerCertFile, ServerKeyFile} =
+ make_cert_files("server", Config, ec, ec, "", [{digest, appropriate_sha(CryptoSupport)}]),
+ {ClientCaCertFile, ClientCertFile, ClientKeyFile} =
+ make_cert_files("client", Config, ec, ec, "", [{digest, appropriate_sha(CryptoSupport)}]),
[{server_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true},
{cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
{server_ecdsa_verify_opts, [{ssl_imp, new},{reuseaddr, true},
- {cacertfile, ServerCaCertFile},
+ {cacertfile, ClientCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
{verify, verify_peer}]},
- {client_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true},
+ {client_ecdsa_opts, [{ssl_imp, new},
{cacertfile, ClientCaCertFile},
{certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}
| Config];
@@ -505,6 +508,14 @@ make_ecdsa_cert(Config) ->
Config
end.
+appropriate_sha(CryptoSupport) ->
+ case proplists:get_bool(sha256, CryptoSupport) of
+ true ->
+ sha256;
+ false ->
+ sha1
+ end.
+
%% RFC 4492, Sect. 2.3. ECDH_RSA
%%
%% This key exchange algorithm is the same as ECDH_ECDSA except that the
@@ -513,16 +524,18 @@ make_ecdh_rsa_cert(Config) ->
CryptoSupport = crypto:supports(),
case proplists:get_bool(ecdh, proplists:get_value(public_keys, CryptoSupport)) of
true ->
- {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, rsa, ec, "rsa_"),
- {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, rsa, ec, "rsa_"),
+ {ServerCaCertFile, ServerCertFile, ServerKeyFile} =
+ make_cert_files("server", Config, rsa, ec, "rsa_", []),
+ {ClientCaCertFile, ClientCertFile, ClientKeyFile} =
+ make_cert_files("client", Config, rsa, ec, "rsa_",[]),
[{server_ecdh_rsa_opts, [{ssl_imp, new},{reuseaddr, true},
{cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
{server_ecdh_rsa_verify_opts, [{ssl_imp, new},{reuseaddr, true},
- {cacertfile, ServerCaCertFile},
+ {cacertfile, ClientCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
{verify, verify_peer}]},
- {client_ecdh_rsa_opts, [{ssl_imp, new},{reuseaddr, true},
+ {client_ecdh_rsa_opts, [{ssl_imp, new},
{cacertfile, ClientCaCertFile},
{certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}
| Config];
@@ -532,9 +545,9 @@ make_ecdh_rsa_cert(Config) ->
make_mix_cert(Config) ->
{ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, dsa,
- rsa, "mix"),
+ rsa, "mix", []),
{ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, dsa,
- rsa, "mix"),
+ rsa, "mix", []),
[{server_mix_opts, [{ssl_imp, new},{reuseaddr, true},
{cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
@@ -542,16 +555,16 @@ make_mix_cert(Config) ->
{cacertfile, ClientCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
{verify, verify_peer}]},
- {client_mix_opts, [{ssl_imp, new},{reuseaddr, true},
+ {client_mix_opts, [{ssl_imp, new},
{cacertfile, ClientCaCertFile},
{certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}
| Config].
-make_cert_files(RoleStr, Config, Alg1, Alg2, Prefix) ->
+make_cert_files(RoleStr, Config, Alg1, Alg2, Prefix, Opts) ->
Alg1Str = atom_to_list(Alg1),
Alg2Str = atom_to_list(Alg2),
- CaInfo = {CaCert, _} = erl_make_certs:make_cert([{key, Alg1}]),
- {Cert, CertKey} = erl_make_certs:make_cert([{key, Alg2}, {issuer, CaInfo}]),
+ CaInfo = {CaCert, _} = erl_make_certs:make_cert([{key, Alg1}| Opts]),
+ {Cert, CertKey} = erl_make_certs:make_cert([{key, Alg2}, {issuer, CaInfo} | Opts]),
CaCertFile = filename:join([proplists:get_value(priv_dir, Config),
RoleStr, Prefix ++ Alg1Str ++ "_cacerts.pem"]),
CertFile = filename:join([proplists:get_value(priv_dir, Config),
@@ -805,16 +818,24 @@ send_selected_port(_,_,_) ->
rsa_suites(CounterPart) ->
ECC = is_sane_ecc(CounterPart),
FIPS = is_fips(CounterPart),
+ CryptoSupport = crypto:supports(),
+ Ciphers = proplists:get_value(ciphers, CryptoSupport),
lists:filter(fun({rsa, des_cbc, sha}) when FIPS == true ->
false;
({dhe_rsa, des_cbc, sha}) when FIPS == true ->
false;
- ({rsa, _, _}) ->
- true;
- ({dhe_rsa, _, _}) ->
- true;
- ({ecdhe_rsa, _, _}) when ECC == true ->
- true;
+ ({rsa, Cipher, _}) ->
+ lists:member(cipher_atom(Cipher), Ciphers);
+ ({dhe_rsa, Cipher, _}) ->
+ lists:member(cipher_atom(Cipher), Ciphers);
+ ({ecdhe_rsa, Cipher, _}) when ECC == true ->
+ lists:member(cipher_atom(Cipher), Ciphers);
+ ({rsa, Cipher, _, _}) ->
+ lists:member(cipher_atom(Cipher), Ciphers);
+ ({dhe_rsa, Cipher, _,_}) ->
+ lists:member(cipher_atom(Cipher), Ciphers);
+ ({ecdhe_rsa, Cipher, _,_}) when ECC == true ->
+ lists:member(cipher_atom(Cipher), Ciphers);
(_) ->
false
end,
@@ -830,37 +851,42 @@ common_ciphers(openssl) ->
lists:member(ssl_cipher:openssl_suite_name(S), OpenSslSuites)
].
-rsa_non_signed_suites() ->
+available_suites(Version) ->
+ [ssl_cipher:erl_suite_definition(Suite) ||
+ Suite <- ssl_cipher:filter_suites(ssl_cipher:suites(Version))].
+
+
+rsa_non_signed_suites(Version) ->
lists:filter(fun({rsa, _, _}) ->
true;
(_) ->
false
end,
- ssl:cipher_suites()).
+ available_suites(Version)).
-dsa_suites() ->
+dsa_suites(Version) ->
lists:filter(fun({dhe_dss, _, _}) ->
true;
(_) ->
false
end,
- ssl:cipher_suites()).
+ available_suites(Version)).
-ecdsa_suites() ->
+ecdsa_suites(Version) ->
lists:filter(fun({ecdhe_ecdsa, _, _}) ->
true;
(_) ->
false
end,
- ssl:cipher_suites()).
+ available_suites(Version)).
-ecdh_rsa_suites() ->
+ecdh_rsa_suites(Version) ->
lists:filter(fun({ecdh_rsa, _, _}) ->
true;
(_) ->
false
end,
- ssl:cipher_suites()).
+ available_suites(Version)).
openssl_rsa_suites(CounterPart) ->
Ciphers = ssl:cipher_suites(openssl),
@@ -898,59 +924,16 @@ string_regex_filter(Str, Search) when is_list(Str) ->
string_regex_filter(_Str, _Search) ->
false.
-anonymous_suites() ->
- Suites =
- [{dh_anon, rc4_128, md5},
- {dh_anon, des_cbc, sha},
- {dh_anon, '3des_ede_cbc', sha},
- {dh_anon, aes_128_cbc, sha},
- {dh_anon, aes_256_cbc, sha},
- {dh_anon, aes_128_gcm, null, sha256},
- {dh_anon, aes_256_gcm, null, sha384},
- {ecdh_anon,rc4_128,sha},
- {ecdh_anon,'3des_ede_cbc',sha},
- {ecdh_anon,aes_128_cbc,sha},
- {ecdh_anon,aes_256_cbc,sha}],
+anonymous_suites(Version) ->
+ Suites = ssl_cipher:anonymous_suites(Version),
ssl_cipher:filter_suites(Suites).
-psk_suites() ->
- Suites =
- [{psk, rc4_128, sha},
- {psk, '3des_ede_cbc', sha},
- {psk, aes_128_cbc, sha},
- {psk, aes_256_cbc, sha},
- {psk, aes_128_cbc, sha256},
- {psk, aes_256_cbc, sha384},
- {dhe_psk, rc4_128, sha},
- {dhe_psk, '3des_ede_cbc', sha},
- {dhe_psk, aes_128_cbc, sha},
- {dhe_psk, aes_256_cbc, sha},
- {dhe_psk, aes_128_cbc, sha256},
- {dhe_psk, aes_256_cbc, sha384},
- {rsa_psk, rc4_128, sha},
- {rsa_psk, '3des_ede_cbc', sha},
- {rsa_psk, aes_128_cbc, sha},
- {rsa_psk, aes_256_cbc, sha},
- {rsa_psk, aes_128_cbc, sha256},
- {rsa_psk, aes_256_cbc, sha384},
- {psk, aes_128_gcm, null, sha256},
- {psk, aes_256_gcm, null, sha384},
- {dhe_psk, aes_128_gcm, null, sha256},
- {dhe_psk, aes_256_gcm, null, sha384},
- {rsa_psk, aes_128_gcm, null, sha256},
- {rsa_psk, aes_256_gcm, null, sha384}],
+psk_suites(Version) ->
+ Suites = ssl_cipher:psk_suites(Version),
ssl_cipher:filter_suites(Suites).
-psk_anon_suites() ->
- Suites =
- [{psk, rc4_128, sha},
- {psk, '3des_ede_cbc', sha},
- {psk, aes_128_cbc, sha},
- {psk, aes_256_cbc, sha},
- {dhe_psk, rc4_128, sha},
- {dhe_psk, '3des_ede_cbc', sha},
- {dhe_psk, aes_128_cbc, sha},
- {dhe_psk, aes_256_cbc, sha}],
+psk_anon_suites(Version) ->
+ Suites = [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite)],
ssl_cipher:filter_suites(Suites).
srp_suites() ->
@@ -1072,14 +1055,16 @@ init_tls_version(Version, Config)
application:load(ssl),
application:set_env(ssl, dtls_protocol_version, Version),
ssl:start(),
- [{protocol, dtls}, {protocol_opts, [{protocol, dtls}]}|Config];
+ NewConfig = proplists:delete(protocol_opts, proplists:delete(protocol, Config)),
+ [{protocol, dtls}, {protocol_opts, [{protocol, dtls}]} | NewConfig];
init_tls_version(Version, Config) ->
ssl:stop(),
application:load(ssl),
application:set_env(ssl, protocol_version, Version),
ssl:start(),
- [{protocol, tls}|Config].
+ NewConfig = proplists:delete(protocol_opts, proplists:delete(protocol, Config)),
+ [{protocol, tls} | NewConfig].
sufficient_crypto_support(Version)
when Version == 'tlsv1.2'; Version == 'dtlsv1.2' ->
@@ -1175,14 +1160,15 @@ is_fips(_) ->
false.
cipher_restriction(Config0) ->
+ Version = tls_record:protocol_version(protocol_version(Config0)),
case is_sane_ecc(openssl) of
false ->
Opts = proplists:get_value(server_opts, Config0),
Config1 = proplists:delete(server_opts, Config0),
VerOpts = proplists:get_value(server_verification_opts, Config1),
Config = proplists:delete(server_verification_opts, Config1),
- Restricted0 = ssl:cipher_suites() -- ecdsa_suites(),
- Restricted = Restricted0 -- ecdh_rsa_suites(),
+ Restricted0 = ssl:cipher_suites() -- ecdsa_suites(Version),
+ Restricted = Restricted0 -- ecdh_rsa_suites(Version),
[{server_opts, [{ciphers, Restricted} | Opts]}, {server_verification_opts, [{ciphers, Restricted} | VerOpts] } | Config];
true ->
Config0
@@ -1213,19 +1199,37 @@ check_sane_openssl_version(Version) ->
enough_openssl_crl_support("OpenSSL 0." ++ _) -> false;
enough_openssl_crl_support(_) -> true.
-wait_for_openssl_server(Port) ->
- wait_for_openssl_server(Port, 10).
-wait_for_openssl_server(_, 0) ->
+wait_for_openssl_server(Port, tls) ->
+ do_wait_for_openssl_tls_server(Port, 10);
+wait_for_openssl_server(Port, dtls) ->
+ do_wait_for_openssl_dtls_server(Port, 10).
+
+do_wait_for_openssl_tls_server(_, 0) ->
exit(failed_to_connect_to_openssl);
-wait_for_openssl_server(Port, N) ->
+do_wait_for_openssl_tls_server(Port, N) ->
case gen_tcp:connect("localhost", Port, []) of
{ok, S} ->
gen_tcp:close(S);
_ ->
ct:sleep(?SLEEP),
- wait_for_openssl_server(Port, N-1)
+ do_wait_for_openssl_tls_server(Port, N-1)
end.
+do_wait_for_openssl_dtls_server(_, 0) ->
+ %%exit(failed_to_connect_to_openssl);
+ ok;
+do_wait_for_openssl_dtls_server(Port, N) ->
+ %% case gen_udp:open(0) of
+ %% {ok, S} ->
+ %% gen_udp:connect(S, "localhost", Port),
+ %% gen_udp:close(S);
+ %% _ ->
+ %% ct:sleep(?SLEEP),
+ %% do_wait_for_openssl_dtls_server(Port, N-1)
+ %% end.
+ ct:sleep(500),
+ do_wait_for_openssl_dtls_server(Port, N-1).
+
version_flag(tlsv1) ->
"-tls1";
version_flag('tlsv1.1') ->
@@ -1235,10 +1239,14 @@ version_flag('tlsv1.2') ->
version_flag(sslv3) ->
"-ssl3";
version_flag(sslv2) ->
- "-ssl2".
-
-filter_suites(Ciphers0) ->
- Version = tls_record:highest_protocol_version([]),
+ "-ssl2";
+version_flag('dtlsv1.2') ->
+ "-dtls1_2";
+version_flag('dtlsv1') ->
+ "-dtls1".
+
+filter_suites(Ciphers0, AtomVersion) ->
+ Version = tls_version(AtomVersion),
Supported0 = ssl_cipher:suites(Version)
++ ssl_cipher:anonymous_suites(Version)
++ ssl_cipher:psk_suites(Version)
@@ -1320,7 +1328,7 @@ protocol_version(Config) ->
protocol_version(Config, tuple) ->
case proplists:get_value(protocol, Config) of
dtls ->
- dtls_record:protocol_version(dtls_record:highest_protocol_version([]));
+ dtls_record:highest_protocol_version(dtls_record:supported_protocol_versions());
_ ->
tls_record:highest_protocol_version(tls_record:supported_protocol_versions())
end;
@@ -1345,3 +1353,122 @@ ct_log_supported_protocol_versions(Config) ->
_ ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()])
end.
+
+clean_env() ->
+ application:unset_env(ssl, protocol_version),
+ application:unset_env(ssl, session_lifetime),
+ application:unset_env(ssl, session_cb),
+ application:unset_env(ssl, session_cb_init_args),
+ application:unset_env(ssl, session_cache_client_max),
+ application:unset_env(ssl, session_cache_server_max),
+ application:unset_env(ssl, ssl_pem_cache_clean),
+ application:unset_env(ssl, bypass_pem_cache),
+ application:unset_env(ssl, alert_timeout).
+
+clean_start() ->
+ ssl:stop(),
+ application:load(ssl),
+ clean_env(),
+ ssl:start().
+
+is_psk_anon_suite({psk, _,_}) ->
+ true;
+is_psk_anon_suite({dhe_psk,_,_}) ->
+ true;
+is_psk_anon_suite({psk, _,_,_}) ->
+ true;
+is_psk_anon_suite({dhe_psk, _,_,_}) ->
+ true;
+is_psk_anon_suite(_) ->
+ false.
+
+cipher_atom(aes_256_cbc) ->
+ aes_cbc256;
+cipher_atom(aes_128_cbc) ->
+ aes_cbc128;
+cipher_atom('3des_ede_cbc') ->
+ des_ede3;
+cipher_atom(Atom) ->
+ Atom.
+tls_version('dtlsv1' = Atom) ->
+ dtls_v1:corresponding_tls_version(dtls_record:protocol_version(Atom));
+tls_version('dtlsv1.2' = Atom) ->
+ dtls_v1:corresponding_tls_version(dtls_record:protocol_version(Atom));
+tls_version(Atom) ->
+ tls_record:protocol_version(Atom).
+
+dtls_hello() ->
+ [1,
+ <<0,1,4>>,
+ <<0,0>>,
+ <<0,0,0>>,
+ <<0,1,4>>,
+ <<254,253,88,
+ 156,129,61,
+ 131,216,15,
+ 131,194,242,
+ 46,154,190,
+ 20,228,234,
+ 234,150,44,
+ 62,96,96,103,
+ 127,95,103,
+ 23,24,42,138,
+ 13,142,32,57,
+ 230,177,32,
+ 210,154,152,
+ 188,121,134,
+ 136,53,105,
+ 118,96,106,
+ 103,231,223,
+ 133,10,165,
+ 50,32,211,
+ 227,193,14,
+ 181,143,48,
+ 66,0,0,100,0,
+ 255,192,44,
+ 192,48,192,
+ 36,192,40,
+ 192,46,192,
+ 50,192,38,
+ 192,42,0,159,
+ 0,163,0,107,
+ 0,106,0,157,
+ 0,61,192,43,
+ 192,47,192,
+ 35,192,39,
+ 192,45,192,
+ 49,192,37,
+ 192,41,0,158,
+ 0,162,0,103,
+ 0,64,0,156,0,
+ 60,192,10,
+ 192,20,0,57,
+ 0,56,192,5,
+ 192,15,0,53,
+ 192,8,192,18,
+ 0,22,0,19,
+ 192,3,192,13,
+ 0,10,192,9,
+ 192,19,0,51,
+ 0,50,192,4,
+ 192,14,0,47,
+ 1,0,0,86,0,0,
+ 0,14,0,12,0,
+ 0,9,108,111,
+ 99,97,108,
+ 104,111,115,
+ 116,0,10,0,
+ 58,0,56,0,14,
+ 0,13,0,25,0,
+ 28,0,11,0,12,
+ 0,27,0,24,0,
+ 9,0,10,0,26,
+ 0,22,0,23,0,
+ 8,0,6,0,7,0,
+ 20,0,21,0,4,
+ 0,5,0,18,0,
+ 19,0,1,0,2,0,
+ 3,0,15,0,16,
+ 0,17,0,11,0,
+ 2,1,0>>].
+
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index b3109b5de9..42cc8b57ad 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -42,7 +42,9 @@ all() ->
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'}
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
].
groups() ->
@@ -50,12 +52,17 @@ groups() ->
{'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
{'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
{'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
- {'sslv3', [], all_versions_tests()}].
+ {'sslv3', [], all_versions_tests()},
+ {'dtlsv1.2', [], dtls_all_versions_tests()},
+ {'dtlsv1', [], dtls_all_versions_tests()}
+ ].
basic_tests() ->
[basic_erlang_client_openssl_server,
basic_erlang_server_openssl_client,
- expired_session].
+ expired_session,
+ ssl2_erlang_server_openssl_client_comp
+ ].
all_versions_tests() ->
[
@@ -74,7 +81,27 @@ all_versions_tests() ->
ciphers_dsa_signed_certs,
erlang_client_bad_openssl_server,
expired_session,
- ssl2_erlang_server_openssl_client].
+ ssl2_erlang_server_openssl_client
+ ].
+dtls_all_versions_tests() ->
+ [
+ %%erlang_client_openssl_server,
+ erlang_server_openssl_client,
+ %%erlang_client_openssl_server_dsa_cert,
+ erlang_server_openssl_client_dsa_cert
+ %% This one works but gets port EXIT first some times
+ %%erlang_server_openssl_client_reuse_session
+ %%erlang_client_openssl_server_renegotiate,
+ %%erlang_client_openssl_server_nowrap_seqnum,
+ %%erlang_server_openssl_client_nowrap_seqnum,
+ %%erlang_client_openssl_server_no_server_ca_cert,
+ %%erlang_client_openssl_server_client_cert,
+ %%erlang_server_openssl_client_client_cert
+ %%ciphers_rsa_signed_certs,
+ %%ciphers_dsa_signed_certs,
+ %%erlang_client_bad_openssl_server,
+ %%expired_session
+ ].
alpn_tests() ->
[erlang_client_alpn_openssl_server_alpn,
@@ -116,7 +143,7 @@ init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl:start(),
+ ssl_test_lib:clean_start(),
{ok, _} = make_certs:all(proplists:get_value(data_dir, Config0),
proplists:get_value(priv_dir, Config0)),
Config1 = ssl_test_lib:make_dsa_cert(Config0),
@@ -180,7 +207,8 @@ special_init(TestCase, Config)
{ok, Version} = application:get_env(ssl, protocol_version),
check_sane_openssl_renegotaite(Config, Version);
-special_init(ssl2_erlang_server_openssl_client, Config) ->
+special_init(Case, Config) when Case == ssl2_erlang_server_openssl_client;
+ Case == ssl2_erlang_server_openssl_client_comp ->
case ssl_test_lib:supports_ssl_tls_version(sslv2) of
true ->
Config;
@@ -280,7 +308,8 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) ->
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+
+ ssl_test_lib:wait_for_openssl_server(Port, tls),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -353,7 +382,7 @@ erlang_client_openssl_server(Config) when is_list(Config) ->
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -427,7 +456,7 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -547,7 +576,7 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -596,7 +625,7 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -677,7 +706,7 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -720,7 +749,7 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -830,7 +859,7 @@ ciphers_dsa_signed_certs() ->
[{doc,"Test cipher suites that uses dsa certs"}].
ciphers_dsa_signed_certs(Config) when is_list(Config) ->
Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:dsa_suites(),
+ Ciphers = ssl_test_lib:dsa_suites(tls_record:protocol_version(Version)),
run_suites(Ciphers, Version, Config, dsa).
%%--------------------------------------------------------------------
@@ -852,7 +881,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
"-cert", CertFile, "-key", KeyFile],
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -907,7 +936,7 @@ expired_session(Config) when is_list(Config) ->
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, tls),
Client0 =
ssl_test_lib:start_client([{node, ClientNode},
@@ -954,8 +983,52 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
Data = "From openssl to erlang",
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Exe = "openssl",
+ Args = ["s_client", "-connect", "localhost:" ++ integer_to_list(Port),
+ "-ssl2", "-msg"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+ true = port_command(OpenSslPort, Data),
+
+ ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
+ receive
+ {'EXIT', OpenSslPort, _} = Exit ->
+ ct:log("Received: ~p ~n", [Exit]),
+ ok
+ end,
+ receive
+ {'EXIT', _, _} = UnkownExit ->
+ Msg = lists:flatten(io_lib:format("Received: ~p ~n", [UnkownExit])),
+ ct:log(Msg),
+ ct:comment(Msg),
+ ok
+ after 0 ->
+ ok
+ end,
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}),
+ process_flag(trap_exit, false).
+%%--------------------------------------------------------------------
+ssl2_erlang_server_openssl_client_comp() ->
+ [{doc,"Test that ssl v2 clients are rejected"}].
+
+ssl2_erlang_server_openssl_client_comp(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ V2Compat = proplists:get_value(v2_hello_compatible, Config),
+
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
- {options, ServerOpts}]),
+ {options, [{v2_hello_compatible, V2Compat} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
Exe = "openssl",
@@ -1264,7 +1337,7 @@ client_check_result(Port, DataExpected, DataReceived) ->
_ ->
client_check_result(Port, DataExpected, NewData)
end
- after 3000 ->
+ after 20000 ->
ct:fail({"Time out on openSSL Client", {expected, DataExpected},
{got, DataReceived}})
end.
@@ -1351,7 +1424,7 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
ConnectionInfo = {ok, {Version, CipherSuite}},
@@ -1421,7 +1494,7 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -1457,7 +1530,7 @@ start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callba
Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
"-cert", CertFile, "-key", KeyFile],
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -1526,7 +1599,7 @@ start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, Ca
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -1591,7 +1664,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac
"-cert", CertFile, "-key", KeyFile],
OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
diff --git a/lib/ssl/test/ssl_upgrade_SUITE.erl b/lib/ssl/test/ssl_upgrade_SUITE.erl
index 113b3b4158..875399db76 100644
--- a/lib/ssl/test/ssl_upgrade_SUITE.erl
+++ b/lib/ssl/test/ssl_upgrade_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2014-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.
@@ -29,7 +29,8 @@
server,
client,
soft,
- result_proxy
+ result_proxy,
+ skip
}).
all() ->
@@ -73,8 +74,15 @@ major_upgrade(Config) when is_list(Config) ->
minor_upgrade(Config) when is_list(Config) ->
ct_release_test:upgrade(ssl, minor,{?MODULE, #state{config = Config}}, Config).
-upgrade_init(CTData, #state{config = Config} = State) ->
- {ok, {_, _, Up, _Down}} = ct_release_test:get_appup(CTData, ssl),
+upgrade_init(CtData, State) ->
+ {ok,{FromVsn,ToVsn}} = ct_release_test:get_app_vsns(CtData, ssl),
+ upgrade_init(FromVsn, ToVsn, CtData, State).
+
+upgrade_init(_, "8.0.2", _, State) ->
+ %% Requires stdlib upgrade so it will be a node upgrade!
+ State#state{skip = true};
+upgrade_init(_, _, CtData, #state{config = Config} = State) ->
+ {ok, {_, _, Up, _Down}} = ct_release_test:get_appup(CtData, ssl),
ct:pal("Up: ~p", [Up]),
Soft = is_soft(Up), %% It is symmetrical, if upgrade is soft so is downgrade
Pid = spawn(?MODULE, result_proxy_init, [[]]),
@@ -88,6 +96,8 @@ upgrade_init(CTData, #state{config = Config} = State) ->
State#state{soft = Soft, result_proxy = Pid}
end.
+upgrade_upgraded(_, #state{skip = true} = State) ->
+ State;
upgrade_upgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = State) ->
ct:pal("Restart upgrade ~n", []),
{Server, Client} = restart_start_connection(Config, Pid),
@@ -96,7 +106,6 @@ upgrade_upgraded(_, #state{soft = false, config = Config, result_proxy = Pid} =
ssl_test_lib:close(Client),
ok = Result,
State;
-
upgrade_upgraded(_, #state{server = Server0, client = Client0,
config = Config, soft = true,
result_proxy = Pid} = State) ->
@@ -110,6 +119,8 @@ upgrade_upgraded(_, #state{server = Server0, client = Client0,
{Server, Client} = soft_start_connection(Config, Pid),
State#state{server = Server, client = Client}.
+upgrade_downgraded(_, #state{skip = true} = State) ->
+ State;
upgrade_downgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = State) ->
ct:pal("Restart downgrade: ~n", []),
{Server, Client} = restart_start_connection(Config, Pid),
@@ -119,7 +130,6 @@ upgrade_downgraded(_, #state{soft = false, config = Config, result_proxy = Pid}
Pid ! stop,
ok = Result,
State;
-
upgrade_downgraded(_, #state{server = Server, client = Client, soft = true, result_proxy = Pid} = State) ->
ct:pal("Soft downgrade: ~n", []),
Server ! changed_version,
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 3b51fa8c6b..8d955cd080 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 8.0
+SSL_VSN = 8.1.3.1.1
diff --git a/lib/stdlib/doc/src/assert_hrl.xml b/lib/stdlib/doc/src/assert_hrl.xml
index e2dfc2ab9b..cb91b1f126 100644
--- a/lib/stdlib/doc/src/assert_hrl.xml
+++ b/lib/stdlib/doc/src/assert_hrl.xml
@@ -4,7 +4,7 @@
<fileref>
<header>
<copyright>
- <year>2012</year><year>2015</year>
+ <year>2012</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/dets.xml b/lib/stdlib/doc/src/dets.xml
index 3b134d00b7..2e4261d72e 100644
--- a/lib/stdlib/doc/src/dets.xml
+++ b/lib/stdlib/doc/src/dets.xml
@@ -391,7 +391,7 @@
<item>
<p><c>{hash, Hash}</c> - Describes which BIF is
used to calculate the hash values of the objects stored in the
- <c>dets</c> table. Possible values of <c>Hash</c>:</p>
+ Dets table. Possible values of <c>Hash</c>:</p>
<list>
<item>
<p><c>hash</c> - Implies that the <c>erlang:hash/2</c> BIF
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index b8e262208d..5f5d2b7f36 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -369,7 +369,7 @@
variable that in turn is passed to the function.</p>
<p>The parse transform is provided in the <c>ms_transform</c>
module and the source <em>must</em> include
- file <c>ms_transform.hrl</c> in <c>STDLIB</c> for this
+ file <c>ms_transform.hrl</c> in STDLIB for this
pseudo function to work. Failing to include the hrl file in
the source results in a runtime error, not a compile
time error. The include file is easiest included by adding line
@@ -1458,7 +1458,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
specification returned <c>true</c>.</fsummary>
<desc>
<p>Matches the objects in table <c><anno>Tab</anno></c> using a
- <seealso marker="#match_spec">match specificationc</seealso>. If the
+ <seealso marker="#match_spec">match specification</seealso>. If the
match specification returns <c>true</c> for an object, that object
considered a match and is counted. For any other result from
the match specification the object is not considered a match and is
@@ -1644,7 +1644,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</taglist>
<p>Whenever option <c>extended_info</c> is used, it
results in a file not readable by versions of ETS before
- that in <c>STDLIB</c> 1.15.1</p>
+ that in STDLIB 1.15.1</p>
<p>If option <c>sync</c> is set to <c>true</c>, it ensures that
the content of the file is written to the disk before
<c>tab2file</c> returns. Defaults to <c>{sync, false}</c>.</p>
@@ -1725,7 +1725,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
<p>A tuple <c>{<anno>Major</anno>,<anno>Minor</anno>}</c>
containing the major and
minor version of the file format for ETS table dumps. This
- version field was added beginning with <c>STDLIB</c> 1.5.1.
+ version field was added beginning with STDLIB 1.5.1.
Files dumped with older versions return <c>{0,0}</c> in this
field.</p>
</item>
diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml
index 2a413835d0..f20500709c 100644
--- a/lib/stdlib/doc/src/filename.xml
+++ b/lib/stdlib/doc/src/filename.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -511,6 +511,33 @@ true
</func>
<func>
+ <name name="safe_relative_path" arity="1"/>
+ <fsummary>Sanitize a relative path to avoid directory traversal attacks.</fsummary>
+ <desc>
+ <p>Sanitizes the relative path by eliminating ".." and "."
+ components to protect against directory traversal attacks.
+ Either returns the sanitized path name, or the atom
+ <c>unsafe</c> if the path is unsafe.
+ The path is considered unsafe in the following circumstances:</p>
+ <list type="bulleted">
+ <item><p>The path is not relative.</p></item>
+ <item><p>A ".." component would climb up above the root of
+ the relative path.</p></item>
+ </list>
+ <p><em>Examples:</em></p>
+ <pre>
+1> <input>filename:safe_relative_path("dir/sub_dir/..").</input>
+"dir"
+2> <input>filename:safe_relative_path("dir/..").</input>
+[]
+3> <input>filename:safe_relative_path("dir/../..").</input>
+unsafe
+4> <input>filename:safe_relative_path("/abs/path").</input>
+unsafe</pre>
+ </desc>
+ </func>
+
+ <func>
<name name="split" arity="1"/>
<fsummary>Split a filename into its path components.</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml
index d677dd6f83..7bfe477a11 100644
--- a/lib/stdlib/doc/src/gb_sets.xml
+++ b/lib/stdlib/doc/src/gb_sets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2001</year><year>2015</year>
+ <year>2001</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml
index 9a49d66820..790d4b8bf1 100644
--- a/lib/stdlib/doc/src/gb_trees.xml
+++ b/lib/stdlib/doc/src/gb_trees.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2001</year><year>2015</year>
+ <year>2001</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index c57a31fa21..5eb13db1aa 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2016</year>
+ <year>2016-2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -54,7 +54,8 @@
<p>
This is a new behavior in Erlang/OTP 19.0.
It has been thoroughly reviewed, is stable enough
- to be used by at least two heavy OTP applications, and is here to stay.
+ to be used by at least two heavy OTP applications,
+ and is here to stay.
Depending on user feedback, we do not expect
but can find it necessary to make minor
not backward compatible changes into Erlang/OTP 20.0.
@@ -70,6 +71,7 @@
<item>The state can be any term.</item>
<item>Events can be postponed.</item>
<item>Events can be self-generated.</item>
+ <item>Automatic state enter code can be called.</item>
<item>A reply can be sent from a later state.</item>
<item>There can be multiple <c>sys</c> traceable replies.</item>
</list>
@@ -97,6 +99,9 @@ gen_statem module Callback module
gen_statem:start
gen_statem:start_link -----> Module:init/1
+Server start or code change
+ -----> Module:callback_mode/0
+
gen_statem:stop -----> Module:terminate/3
gen_statem:call
@@ -116,37 +121,40 @@ erlang:'!' -----> Module:StateName/3
</p>
<p>
If a callback function fails or returns a bad value,
- the <c>gen_statem</c> terminates. However, an exception of class
+ the <c>gen_statem</c> terminates, unless otherwise stated.
+ However, an exception of class
<seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>
- is not regarded as an error but as a valid return.
+ is not regarded as an error but as a valid return
+ from all callback functions.
</p>
- <marker id="state_function"/>
+ <marker id="state callback"/>
<p>
- The "<em>state function</em>" for a specific
+ The "<em>state callback</em>" for a specific
<seealso marker="#type-state">state</seealso>
in a <c>gen_statem</c> is the callback function that is called
for all events in this state. It is selected depending on which
<seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
- that the implementation specifies when the server starts.
+ that the callback module defines with the callback function
+ <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>.
</p>
<p>
When the
<seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
is <c>state_functions</c>, the state must be an atom and
- is used as the state function name; see
+ is used as the state callback name; see
<seealso marker="#Module:StateName/3"><c>Module:StateName/3</c></seealso>.
This gathers all code for a specific state
in one function as the <c>gen_statem</c> engine
branches depending on state name.
- Notice that in this mode the mandatory callback function
+ Notice the fact that there is a mandatory callback function
<seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
- makes the state name <c>terminate</c> unusable.
+ makes the state name <c>terminate</c> unusable in this mode.
</p>
<p>
When the
<seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
is <c>handle_event_function</c>, the state can be any term
- and the state function name is
+ and the state callback name is
<seealso marker="#Module:handle_event/4"><c>Module:handle_event/4</c></seealso>.
This makes it easy to branch depending on state or event as you desire.
Be careful about which events you handle in which
@@ -156,8 +164,8 @@ erlang:'!' -----> Module:StateName/3
<p>
The <c>gen_statem</c> enqueues incoming events in order of arrival
and presents these to the
- <seealso marker="#state_function">state function</seealso>
- in that order. The state function can postpone an event
+ <seealso marker="#state callback">state callback</seealso>
+ in that order. The state callback can postpone an event
so it is not retried in the current state.
After a state change the queue restarts with the postponed events.
</p>
@@ -169,12 +177,12 @@ erlang:'!' -----> Module:StateName/3
to entering a new receive statement.
</p>
<p>
- The <seealso marker="#state_function">state function</seealso>
+ The <seealso marker="#state callback">state callback</seealso>
can insert events using the
<seealso marker="#type-action"><c>action()</c></seealso>
<c>next_event</c>
and such an event is inserted as the next to present
- to the state function. That is, as if it is
+ to the state callback. That is, as if it is
the oldest incoming event. A dedicated
<seealso marker="#type-event_type"><c>event_type()</c></seealso>
<c>internal</c> can be used for such events making them impossible
@@ -187,9 +195,19 @@ erlang:'!' -----> Module:StateName/3
<seealso marker="gen_fsm"><c>gen_fsm</c></seealso>
to force processing an inserted event before others.
</p>
+ <p>
+ The <c>gen_statem</c> engine can automatically
+ make a specialized call to the
+ <seealso marker="#state callback">state callback</seealso>
+ whenever a new state is entered; see
+ <seealso marker="#type-state_enter"><c>state_enter()</c></seealso>.
+ This is for writing code common to all state entries.
+ Another way to do it is to insert events at state transitions,
+ but you have to do so everywhere it is needed.
+ </p>
<note>
<p>If you in <c>gen_statem</c>, for example, postpone
- an event in one state and then call another state function
+ an event in one state and then call another state callback
of yours, you have not changed states and hence the postponed event
is not retried, which is logical but can be confusing.
</p>
@@ -218,7 +236,7 @@ erlang:'!' -----> Module:StateName/3
The <c>gen_statem</c> process can go into hibernation; see
<seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>.
It is done when a
- <seealso marker="#state_function">state function</seealso> or
+ <seealso marker="#state callback">state callback</seealso> or
<seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
specifies <c>hibernate</c> in the returned
<seealso marker="#type-action"><c>Actions</c></seealso>
@@ -249,11 +267,10 @@ erlang:'!' -----> Module:StateName/3
-behaviour(gen_statem).
-export([start/0,push/0,get_count/0,stop/0]).
--export([terminate/3,code_change/4,init/1]).
+-export([terminate/3,code_change/4,init/1,callback_mode/0]).
-export([on/3,off/3]).
name() -> pushbutton_statem. % The registered server name
-callback_mode() -> state_functions.
%% API. This example uses a registered name name()
%% and does not link to the caller.
@@ -270,15 +287,14 @@ stop() ->
terminate(_Reason, _State, _Data) ->
void.
code_change(_Vsn, State, Data, _Extra) ->
- {callback_mode(),State,Data}.
+ {ok,State,Data}.
init([]) ->
- %% Set the callback mode and initial state + data.
- %% Data is used only as a counter.
+ %% Set the initial state + data. Data is used only as a counter.
State = off, Data = 0,
- {callback_mode(),State,Data}.
-
+ {ok,State,Data}.
+callback_mode() -> state_functions.
-%%% State functions
+%%% state callback(s)
off({call,From}, push, Data) ->
%% Go to 'on', increment count and reply
@@ -326,18 +342,13 @@ ok
To compare styles, here follows the same example using
<seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
<c>state_functions</c>, or rather the code to replace
- from function <c>init/1</c> of the <c>pushbutton.erl</c>
+ after function <c>init/1</c> of the <c>pushbutton.erl</c>
example file above:
</p>
<code type="erl">
-init([]) ->
- %% Set the callback mode and initial state + data.
- %% Data is used only as a counter.
- State = off, Data = 0,
- {handle_event_function,State,Data}.
+callback_mode() -> handle_event_function.
-
-%%% Event handling
+%%% state callback(s)
handle_event({call,From}, push, off, Data) ->
%% Go to 'on', increment count and reply
@@ -426,8 +437,8 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
Debug option that can be used when starting
- a <c>gen_statem</c> server through, for example,
- <seealso marker="#enter_loop/5"><c>enter_loop/5</c></seealso>.
+ a <c>gen_statem</c> server through,
+ <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>.
</p>
<p>
For every entry in <c><anno>Dbgs</anno></c>,
@@ -471,6 +482,10 @@ handle_event(_, _, State, Data) ->
<name name="state"/>
<desc>
<p>
+ If the
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ is <c>handle_event_function</c>,
+ the state can be any term.
After a state change (<c>NextState =/= State</c>),
all postponed events are retried.
</p>
@@ -484,6 +499,8 @@ handle_event(_, _, State, Data) ->
<seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
is <c>state_functions</c>,
the state must be of this type.
+ After a state change (<c>NextState =/= State</c>),
+ all postponed events are retried.
</p>
</desc>
</datatype>
@@ -516,7 +533,22 @@ handle_event(_, _, State, Data) ->
Type <c>info</c> originates from regular process messages sent
to the <c>gen_statem</c>. Also, the state machine
implementation can generate events of types
- <c>timeout</c> and <c>internal</c> to itself.
+ <c>timeout</c>, <c>state_timeout</c>,
+ and <c>internal</c> to itself.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="callback_mode_result"/>
+ <desc>
+ <p>
+ This is the return type from
+ <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>
+ and selects
+ <seealso marker="#type-callback_mode">callback mode</seealso>
+ and whether to do
+ <seealso marker="#type-state_enter">state enter calls</seealso>,
+ or not.
</p>
</desc>
</datatype>
@@ -525,12 +557,9 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
The <em>callback mode</em> is selected when starting the
- <c>gen_statem</c> using the return value from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
- or when calling
- <seealso marker="#enter_loop/5"><c>enter_loop/5,6,7</c></seealso>,
- and with the return value from
- <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso>.
+ <c>gen_statem</c> and after code change
+ using the return value from
+ <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>.
</p>
<taglist>
<tag><c>state_functions</c></tag>
@@ -555,16 +584,93 @@ handle_event(_, _, State, Data) ->
</desc>
</datatype>
<datatype>
+ <name name="state_enter"/>
+ <desc>
+ <p>
+ Whether the state machine should use <em>state enter calls</em>
+ or not is selected when starting the <c>gen_statem</c>
+ and after code change using the return value from
+ <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>.
+ </p>
+ <p>
+ If
+ <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>
+ returns a list containing <c>state_enter</c>,
+ the <c>gen_statem</c> engine will, at every state change,
+ call the
+ <seealso marker="#state callback">state callback</seealso>
+ with arguments <c>(enter, OldState, Data)</c>.
+ This may look like an event but is really a call
+ performed after the previous state callback returned
+ and before any event is delivered to the new state callback.
+ See
+ <seealso marker="#Module:StateName/3"><c>Module:StateName/3</c></seealso>
+ and
+ <seealso marker="#Module:handle_event/4"><c>Module:handle_event/4</c></seealso>.
+ Such a call can be repeated by returning a
+ <seealso marker="#type-state_callback_result">
+ <c>repeat_state</c>
+ </seealso>
+ or
+ <seealso marker="#type-state_callback_result">
+ <c>repeat_state_and_data</c>
+ </seealso>
+ tuple from the state callback.
+ </p>
+ <p>
+ If
+ <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>
+ does not return such a list, no state enter calls are done.
+ </p>
+ <p>
+ If
+ <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso>
+ should transform the state to a state with a different
+ name it is still regarded as the same state so this
+ does not cause a state enter call.
+ </p>
+ <p>
+ Note that a state enter call <em>will</em> be done
+ right before entering the initial state even though this
+ formally is not a state change.
+ In this case <c>OldState</c> will be the same as <c>State</c>,
+ which can not happen for a subsequent state change,
+ but will happen when repeating the state enter call.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="transition_option"/>
<desc>
<p>
Transition options can be set by
<seealso marker="#type-action">actions</seealso>
- and they modify the following in how
- the state transition is done:
+ and they modify how the state transition is done:
</p>
<list type="ordered">
<item>
+ <p>
+ If the state changes, is the initial state,
+ <seealso marker="#type-state_callback_result">
+ <c>repeat_state</c>
+ </seealso>
+ or
+ <seealso marker="#type-state_callback_result">
+ <c>repeat_state_and_data</c>
+ </seealso>
+ is used, and also
+ <seealso marker="#type-state_enter"><em>state enter calls</em></seealso>
+ are used, the <c>gen_statem</c> calls
+ the new state callback with arguments
+ <seealso marker="#type-state_enter">(enter, OldState, Data)</seealso>.
+ Any
+ <seealso marker="#type-enter_action"><c>actions</c></seealso>
+ returned from this call are handled as if they were
+ appended to the actions
+ returned by the state callback that changed states.
+ </p>
+ </item>
+ <item>
<p>
All
<seealso marker="#type-action">actions</seealso>
@@ -590,27 +696,46 @@ handle_event(_, _, State, Data) ->
All events stored with
<seealso marker="#type-action"><c>action()</c></seealso>
<c>next_event</c>
- are inserted in the queue to be processed before
- all other events.
+ are inserted to be processed before the other queued events.
</p>
</item>
<item>
<p>
- If an
+ Timeout timers
+ <seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
+ and
<seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>
- is set through
- <seealso marker="#type-action"><c>action()</c></seealso>
- <c>timeout</c>,
- an event timer can be started or a time-out zero event
- can be enqueued.
+ are handled. Time-outs with zero time are guaranteed to be
+ delivered to the state machine before any external
+ not yet received event so if there is such a timeout requested,
+ the corresponding time-out zero event is enqueued as
+ the newest event.
+ </p>
+ <p>
+ Any event cancels an
+ <seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>
+ so a zero time event time-out is only generated
+ if the event queue is empty.
+ </p>
+ <p>
+ A state change cancels a
+ <seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
+ and any new transition option of this type
+ belongs to the new state.
</p>
</item>
<item>
<p>
- The (possibly new)
- <seealso marker="#state_function">state function</seealso>
- is called with the oldest enqueued event if there is any,
- otherwise the <c>gen_statem</c> goes into <c>receive</c>
+ If there are enqueued events the
+ <seealso marker="#state callback">state callback</seealso>
+ for the possibly new state
+ is called with the oldest enqueued event,
+ and we start again from the top of this list.
+ </p>
+ </item>
+ <item>
+ <p>
+ Otherwise the <c>gen_statem</c> goes into <c>receive</c>
or hibernation
(if
<seealso marker="#type-hibernate"><c>hibernate()</c></seealso>
@@ -618,8 +743,11 @@ handle_event(_, _, State, Data) ->
to wait for the next message. In hibernation the next
non-system event awakens the <c>gen_statem</c>, or rather
the next incoming message awakens the <c>gen_statem</c>,
- but if it is a system event
- it goes right back into hibernation.
+ but if it is a system event it goes right back into hibernation.
+ When a new message arrives the
+ <seealso marker="#state callback">state callback</seealso>
+ is called with the corresponding event,
+ and we start again from the top of this list.
</p>
</item>
</list>
@@ -661,37 +789,69 @@ handle_event(_, _, State, Data) ->
<seealso marker="#type-event_type"><c>event_type()</c></seealso>
<c>timeout</c>
after this time (in milliseconds) unless another
- event arrives in which case this time-out is cancelled.
- Notice that a retried or inserted event
- counts like a new in this respect.
+ event arrives or has arrived
+ in which case this time-out is cancelled.
+ Note that a retried or inserted event counts as arrived.
+ So does a state time-out zero event, if it was generated
+ before this timer is requested.
</p>
<p>
If the value is <c>infinity</c>, no timer is started, as
- it never triggers anyway.
+ it never would trigger anyway.
</p>
<p>
- If the value is <c>0</c>, the time-out event is immediately enqueued
- unless there already are enqueued events, as the
- time-out is then immediately cancelled.
- This is a feature ensuring that a time-out <c>0</c> event
- is processed before any not yet received external event.
+ If the value is <c>0</c> no timer is actually started,
+ instead the the time-out event is enqueued to ensure
+ that it gets processed before any not yet
+ received external event.
</p>
<p>
- Notice that it is not possible or needed to cancel this time-out,
+ Note that it is not possible or needed to cancel this time-out,
as it is cancelled automatically by any other event.
</p>
</desc>
</datatype>
<datatype>
+ <name name="state_timeout"/>
+ <desc>
+ <p>
+ Generates an event of
+ <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <c>state_timeout</c>
+ after this time (in milliseconds) unless the <c>gen_statem</c>
+ changes states (<c>NewState =/= OldState</c>)
+ which case this time-out is cancelled.
+ </p>
+ <p>
+ If the value is <c>infinity</c>, no timer is started, as
+ it never would trigger anyway.
+ </p>
+ <p>
+ If the value is <c>0</c> no timer is actually started,
+ instead the the time-out event is enqueued to ensure
+ that it gets processed before any not yet
+ received external event.
+ </p>
+ <p>
+ Setting this timer while it is running will restart it with
+ the new time-out value. Therefore it is possible to cancel
+ this time-out by setting it to <c>infinity</c>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="action"/>
<desc>
<p>
These state transition actions can be invoked by
returning them from the
- <seealso marker="#state_function">state function</seealso>, from
+ <seealso marker="#state callback">state callback</seealso>
+ when it is called with an
+ <seealso marker="#type-event_type">event</seealso>,
+ from
<seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
or by giving them to
- <seealso marker="#enter_loop/6"><c>enter_loop/6,7</c></seealso>.
+ <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
</p>
<p>
Actions are executed in the containing list order.
@@ -702,8 +862,8 @@ handle_event(_, _, State, Data) ->
override any previous of the same type,
so the last in the containing list wins.
For example, the last
- <seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>
- overrides any other <c>event_timeout()</c> in the list.
+ <seealso marker="#type-postpone"><c>postpone()</c></seealso>
+ overrides any previous <c>postpone()</c> in the list.
</p>
<taglist>
<tag><c>postpone</c></tag>
@@ -720,6 +880,53 @@ handle_event(_, _, State, Data) ->
as there is no event to postpone in those cases.
</p>
</item>
+ <tag><c>next_event</c></tag>
+ <item>
+ <p>
+ Stores the specified <c><anno>EventType</anno></c>
+ and <c><anno>EventContent</anno></c> for insertion after all
+ actions have been executed.
+ </p>
+ <p>
+ The stored events are inserted in the queue as the next to process
+ before any already queued events. The order of these stored events
+ is preserved, so the first <c>next_event</c> in the containing
+ list becomes the first to process.
+ </p>
+ <p>
+ An event of type
+ <seealso marker="#type-event_type"><c>internal</c></seealso>
+ is to be used when you want to reliably distinguish
+ an event inserted this way from any external event.
+ </p>
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="enter_action"/>
+ <desc>
+ <p>
+ These state transition actions can be invoked by
+ returning them from the
+ <seealso marker="#state callback">state callback</seealso>, from
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ or by giving them to
+ <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
+ </p>
+ <p>
+ Actions are executed in the containing list order.
+ </p>
+ <p>
+ Actions that set
+ <seealso marker="#type-transition_option">transition options</seealso>
+ override any previous of the same type,
+ so the last in the containing list wins.
+ For example, the last
+ <seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>
+ overrides any previous <c>event_timeout()</c> in the list.
+ </p>
+ <taglist>
<tag><c>hibernate</c></tag>
<item>
<p>
@@ -735,7 +942,7 @@ handle_event(_, _, State, Data) ->
Short for <c>{timeout,Timeout,Timeout}</c>, that is,
the time-out message is the time-out time.
This form exists to make the
- <seealso marker="#state_function">state function</seealso>
+ <seealso marker="#state callback">state callback</seealso>
return value <c>{next_state,NextState,NewData,Timeout}</c>
allowed like for <c>gen_fsm</c>'s
<seealso marker="gen_fsm#Module:StateName/2"><c>Module:StateName/2</c></seealso>.
@@ -750,30 +957,13 @@ handle_event(_, _, State, Data) ->
to <c><anno>Time</anno></c> with <c><anno>EventContent</anno></c>.
</p>
</item>
- <tag><c>reply_action()</c></tag>
- <item>
- <p>
- Replies to a caller.
- </p>
- </item>
- <tag><c>next_event</c></tag>
+ <tag><c>state_timeout</c></tag>
<item>
<p>
- Stores the specified <c><anno>EventType</anno></c>
- and <c><anno>EventContent</anno></c> for insertion after all
- actions have been executed.
- </p>
- <p>
- The stored events are inserted in the queue as the next to process
- before any already queued events. The order of these stored events
- is preserved, so the first <c>next_event</c> in the containing
- list becomes the first to process.
- </p>
- <p>
- An event of type
- <seealso marker="#type-event_type"><c>internal</c></seealso>
- is to be used when you want to reliably distinguish
- an event inserted this way from any external event.
+ Sets the
+ <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>
+ <seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
+ to <c><anno>Time</anno></c> with <c><anno>EventContent</anno></c>.
</p>
</item>
</taglist>
@@ -783,39 +973,97 @@ handle_event(_, _, State, Data) ->
<name name="reply_action"/>
<desc>
<p>
- Replies to a caller waiting for a reply in
+ This state transition action can be invoked by
+ returning it from the
+ <seealso marker="#state callback">state callback</seealso>, from
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ or by giving it to
+ <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
+ </p>
+ <p>
+ It replies to a caller waiting for a reply in
<seealso marker="#call/2"><c>call/2</c></seealso>.
<c><anno>From</anno></c> must be the term from argument
<seealso marker="#type-event_type"><c>{call,<anno>From</anno>}</c></seealso>
- to the
- <seealso marker="#state_function">state function</seealso>.
+ in a call to a
+ <seealso marker="#state callback">state callback</seealso>.
+ </p>
+ <p>
+ Note that using this action from
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ or
+ <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>
+ would be weird on the border of whichcraft
+ since there has been no earlier call to a
+ <seealso marker="#state callback">state callback</seealso>
+ in this server.
</p>
</desc>
</datatype>
<datatype>
- <name name="state_function_result"/>
+ <name name="init_result"/>
<desc>
+ <p>
+ For a succesful initialization,
+ <c><anno>State</anno></c> is the initial
+ <seealso marker="#type-state"><c>state()</c></seealso>
+ and <c><anno>Data</anno></c> the initial server
+ <seealso marker="#type-data"><c>data()</c></seealso>
+ of the <c>gen_statem</c>.
+ </p>
+ <p>
+ The <seealso marker="#type-action"><c>Actions</c></seealso>
+ are executed when entering the first
+ <seealso marker="#type-state">state</seealso> just as for a
+ <seealso marker="#state callback">state callback</seealso>,
+ except that the action <c>postpone</c> is forced to
+ <c>false</c> since there is no event to postpone.
+ </p>
+ <p>
+ For an unsuccesful initialization,
+ <c>{stop,<anno>Reason</anno>}</c>
+ or <c>ignore</c> should be used; see
+ <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="state_enter_result"/>
+ <desc>
+ <p>
+ <c><anno>State</anno></c> is the current state
+ and it can not be changed since the state callback
+ was called with a
+ <seealso marker="#type-state_enter"><em>state enter call</em></seealso>.
+ </p>
<taglist>
<tag><c>next_state</c></tag>
<item>
<p>
The <c>gen_statem</c> does a state transition to
- <c><anno>NextStateName</anno></c>
- (which can be the same as the current state),
+ <c><anno>State</anno></c>, which has to be
+ the current state,
sets <c><anno>NewData</anno></c>,
and executes all <c><anno>Actions</anno></c>.
</p>
</item>
</taglist>
- <p>
- All these terms are tuples or atoms and this property
- will hold in any future version of <c>gen_statem</c>.
- </p>
</desc>
</datatype>
<datatype>
- <name name="handle_event_result"/>
+ <name name="event_handler_result"/>
<desc>
+ <p>
+ <c><anno>StateType</anno></c> is
+ <seealso marker="#type-state_name"><c>state_name()</c></seealso>
+ if
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ is <c>state_functions</c>, or
+ <seealso marker="#type-state"><c>state()</c></seealso>
+ if
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ is <c>handle_event_function</c>.
+ </p>
<taglist>
<tag><c>next_state</c></tag>
<item>
@@ -828,55 +1076,91 @@ handle_event(_, _, State, Data) ->
</p>
</item>
</taglist>
- <p>
- All these terms are tuples or atoms and this property
- will hold in any future version of <c>gen_statem</c>.
- </p>
</desc>
</datatype>
<datatype>
- <name name="common_state_callback_result"/>
+ <name name="state_callback_result"/>
<desc>
+ <p>
+ <c><anno>ActionType</anno></c> is
+ <seealso marker="#type-enter_action"><c>enter_action()</c></seealso>
+ if the state callback was called with a
+ <seealso marker="#type-state_enter"><em>state enter call</em></seealso>
+ and
+ <seealso marker="#type-action"><c>action()</c></seealso>
+ if the state callback was called with an event.
+ </p>
<taglist>
- <tag><c>stop</c></tag>
+ <tag><c>keep_state</c></tag>
<item>
<p>
- Terminates the <c>gen_statem</c> by calling
- <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
- with <c>Reason</c> and
- <c><anno>NewData</anno></c>, if specified.
+ The <c>gen_statem</c> keeps the current state, or
+ does a state transition to the current state if you like,
+ sets <c><anno>NewData</anno></c>,
+ and executes all <c><anno>Actions</anno></c>.
+ This is the same as
+ <c>{next_state,CurrentState,<anno>NewData</anno>,<anno>Actions</anno>}</c>.
</p>
</item>
- <tag><c>stop_and_reply</c></tag>
+ <tag><c>keep_state_and_data</c></tag>
<item>
<p>
- Sends all <c><anno>Replies</anno></c>,
- then terminates the <c>gen_statem</c> by calling
- <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
- with <c>Reason</c> and
- <c><anno>NewData</anno></c>, if specified.
+ The <c>gen_statem</c> keeps the current state or
+ does a state transition to the current state if you like,
+ keeps the current server data,
+ and executes all <c><anno>Actions</anno></c>.
+ This is the same as
+ <c>{next_state,CurrentState,CurrentData,<anno>Actions</anno>}</c>.
</p>
</item>
- <tag><c>keep_state</c></tag>
+ <tag><c>repeat_state</c></tag>
<item>
<p>
The <c>gen_statem</c> keeps the current state, or
does a state transition to the current state if you like,
sets <c><anno>NewData</anno></c>,
and executes all <c><anno>Actions</anno></c>.
- This is the same as
- <c>{next_state,CurrentState,<anno>NewData</anno>,<anno>Actions</anno>}</c>.
+ If the <c>gen_statem</c> runs with
+ <seealso marker="#type-state_enter"><em>state enter calls</em></seealso>,
+ the state enter call is repeated, see type
+ <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>,
+ otherwise <c>repeat_state</c> is the same as
+ <c>keep_state</c>.
</p>
</item>
- <tag><c>keep_state_and_data</c></tag>
+ <tag><c>repeat_state_and_data</c></tag>
<item>
<p>
- The <c>gen_statem</c> keeps the current state or
+ The <c>gen_statem</c> keeps the current state and data, or
does a state transition to the current state if you like,
- keeps the current server data,
and executes all <c><anno>Actions</anno></c>.
This is the same as
- <c>{next_state,CurrentState,CurrentData,<anno>Actions</anno>}</c>.
+ <c>{repeat_state,CurrentData,<anno>Actions</anno>}</c>.
+ If the <c>gen_statem</c> runs with
+ <seealso marker="#type-state_enter"><em>state enter calls</em></seealso>,
+ the state enter call is repeated, see type
+ <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>,
+ otherwise <c>repeat_state_and_data</c> is the same as
+ <c>keep_state_and_data</c>.
+ </p>
+ </item>
+ <tag><c>stop</c></tag>
+ <item>
+ <p>
+ Terminates the <c>gen_statem</c> by calling
+ <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
+ with <c>Reason</c> and
+ <c><anno>NewData</anno></c>, if specified.
+ </p>
+ </item>
+ <tag><c>stop_and_reply</c></tag>
+ <item>
+ <p>
+ Sends all <c><anno>Replies</anno></c>,
+ then terminates the <c>gen_statem</c> by calling
+ <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
+ with <c>Reason</c> and
+ <c><anno>NewData</anno></c>, if specified.
</p>
</item>
</taglist>
@@ -900,14 +1184,14 @@ handle_event(_, _, State, Data) ->
by sending a request
and waiting until its reply arrives.
The <c>gen_statem</c> calls the
- <seealso marker="#state_function">state function</seealso> with
+ <seealso marker="#state callback">state callback</seealso> with
<seealso marker="#type-event_type"><c>event_type()</c></seealso>
<c>{call,From}</c> and event content
<c><anno>Request</anno></c>.
</p>
<p>
A <c><anno>Reply</anno></c> is generated when a
- <seealso marker="#state_function">state function</seealso>
+ <seealso marker="#state callback">state callback</seealso>
returns with
<c>{reply,From,<anno>Reply</anno>}</c> as one
<seealso marker="#type-action"><c>action()</c></seealso>,
@@ -923,17 +1207,40 @@ handle_event(_, _, State, Data) ->
</p>
<note>
<p>
- To avoid getting a late reply in the caller's
- inbox, this function spawns a proxy process that
+ For <c><anno>Timeout</anno> &lt; infinity</c>,
+ to avoid getting a late reply in the caller's
+ inbox if the caller should catch exceptions,
+ this function spawns a proxy process that
does the call. A late reply gets delivered to the
dead proxy process, hence gets discarded. This is
less efficient than using
- <c><anno>Timeout</anno> =:= infinity</c>.
+ <c><anno>Timeout</anno> == infinity</c>.
</p>
</note>
<p>
- The call can fail, for example, if the <c>gen_statem</c> dies
- before or during this function call.
+ <c><anno>Timeout</anno></c> can also be a tuple
+ <c>{clean_timeout,<anno>T</anno>}</c> or
+ <c>{dirty_timeout,<anno>T</anno>}</c>, where
+ <c><anno>T</anno></c> is the time-out time.
+ <c>{clean_timeout,<anno>T</anno>}</c> works like
+ just <c>T</c> described in the note above
+ and uses a proxy process for <c>T &lt; infinity</c>,
+ while <c>{dirty_timeout,<anno>T</anno>}</c>
+ bypasses the proxy process which is more lightweight.
+ </p>
+ <note>
+ <p>
+ If you combine catching exceptions from this function
+ with <c>{dirty_timeout,<anno>T</anno>}</c>
+ to avoid that the calling process dies when the call
+ times out, you will have to be prepared to handle
+ a late reply.
+ So why not just allow the calling process to die?
+ </p>
+ </note>
+ <p>
+ The call can also fail, for example, if the <c>gen_statem</c>
+ dies before or during this function call.
</p>
</desc>
</func>
@@ -949,7 +1256,7 @@ handle_event(_, _, State, Data) ->
ignoring if the destination node or <c>gen_statem</c>
does not exist.
The <c>gen_statem</c> calls the
- <seealso marker="#state_function">state function</seealso> with
+ <seealso marker="#state callback">state callback</seealso> with
<seealso marker="#type-event_type"><c>event_type()</c></seealso>
<c>cast</c> and event content
<c><anno>Msg</anno></c>.
@@ -958,35 +1265,36 @@ handle_event(_, _, State, Data) ->
</func>
<func>
- <name name="enter_loop" arity="5"/>
+ <name name="enter_loop" arity="4"/>
<fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
<desc>
<p>
The same as
- <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
- except that no
+ <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
+ with <c>Actions = []</c> except that no
<seealso marker="#type-server_name"><c>server_name()</c></seealso>
- must have been registered.
+ must have been registered. This creates an anonymous server.
</p>
</desc>
</func>
<func>
- <name name="enter_loop" arity="6"/>
+ <name name="enter_loop" arity="5"/>
<fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
<desc>
<p>
If <c><anno>Server_or_Actions</anno></c> is a <c>list()</c>,
the same as
- <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
+ <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
except that no
<seealso marker="#type-server_name"><c>server_name()</c></seealso>
must have been registered and
<c>Actions = <anno>Server_or_Actions</anno></c>.
+ This creates an anonymous server.
</p>
<p>
Otherwise the same as
- <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
+ <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
with
<c>Server = <anno>Server_or_Actions</anno></c> and
<c>Actions = []</c>.
@@ -995,7 +1303,7 @@ handle_event(_, _, State, Data) ->
</func>
<func>
- <name name="enter_loop" arity="7"/>
+ <name name="enter_loop" arity="6"/>
<fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
<desc>
<p>
@@ -1015,21 +1323,31 @@ handle_event(_, _, State, Data) ->
the <c>gen_statem</c> behavior provides.
</p>
<p>
- <c><anno>Module</anno></c>, <c><anno>Opts</anno></c>, and
- <c><anno>Server</anno></c> have the same meanings
- as when calling
+ <c><anno>Module</anno></c>, <c><anno>Opts</anno></c>
+ have the same meaning as when calling
<seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>.
+ </p>
+ <p>
+ If <c><anno>Server</anno></c> is <c>self()</c> an anonymous
+ server is created just as when using
+ <seealso marker="#start_link/3"><c>start[_link]/3</c></seealso>.
+ If <c><anno>Server</anno></c> is a
+ <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ a named server is created just as when using
+ <seealso marker="#start_link/4"><c>start[_link]/4</c></seealso>.
However, the
<seealso marker="#type-server_name"><c>server_name()</c></seealso>
name must have been registered accordingly
- <em>before</em> this function is called.</p>
+ <em>before</em> this function is called.
+ </p>
<p>
- <c><anno>CallbackMode</anno></c>, <c><anno>State</anno></c>,
- <c><anno>Data</anno></c>, and <c><anno>Actions</anno></c>
+ <c><anno>State</anno></c>, <c><anno>Data</anno></c>,
+ and <c><anno>Actions</anno></c>
have the same meanings as in the return value of
<seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>.
- Also, the callback module <c><anno>Module</anno></c>
- does not need to export an <c>init/1</c> function.
+ Also, the callback module does not need to export a
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ function.
</p>
<p>
The function fails if the calling process was not started by a
@@ -1052,17 +1370,18 @@ handle_event(_, _, State, Data) ->
<seealso marker="#call/2"><c>call/2</c></seealso>
when the reply cannot be defined in
the return value of a
- <seealso marker="#state_function">state function</seealso>.
+ <seealso marker="#state callback">state callback</seealso>.
</p>
<p>
<c><anno>From</anno></c> must be the term from argument
<seealso marker="#type-event_type"><c>{call,<anno>From</anno>}</c></seealso>
to the
- <seealso marker="#state_function">state function</seealso>.
- <c><anno>From</anno></c> and <c><anno>Reply</anno></c>
- can also be specified using a
- <seealso marker="#type-reply_action"><c>reply_action()</c></seealso>
- and multiple replies with a list of them.
+ <seealso marker="#state callback">state callback</seealso>.
+ A reply or multiple replies canalso be sent
+ using one or several
+ <seealso marker="#type-reply_action"><c>reply_action()</c></seealso>s
+ from a
+ <seealso marker="#state callback">state callback</seealso>.
</p>
<note>
<p>
@@ -1253,6 +1572,57 @@ handle_event(_, _, State, Data) ->
<funcs>
<func>
+ <name>Module:callback_mode() -> CallbackMode</name>
+ <fsummary>Update the internal state during upgrade/downgrade.</fsummary>
+ <type>
+ <v>
+ CallbackMode =
+ <seealso marker="#type-callback_mode">callback_mode()</seealso> |
+ [ <seealso marker="#type-callback_mode">callback_mode()</seealso>
+ | <seealso marker="#type-state_enter">state_enter()</seealso> ]
+ </v>
+ </type>
+ <desc>
+ <p>
+ This function is called by a <c>gen_statem</c>
+ when it needs to find out the
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ of the callback module. The value is cached by <c>gen_statem</c>
+ for efficiency reasons, so this function is only called
+ once after server start and after code change,
+ but before the first
+ <seealso marker="#state callback">state callback</seealso>
+ in the current code version is called.
+ More occasions may be added in future versions
+ of <c>gen_statem</c>.
+ </p>
+ <p>
+ Server start happens either when
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ returns or when
+ <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>
+ is called. Code change happens when
+ <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso>
+ returns.
+ </p>
+ <p>
+ The <c>CallbackMode</c> is either just
+ <seealso marker="#type-callback_mode"><c>callback_mode()</c></seealso>
+ or a list containing
+ <seealso marker="#type-callback_mode"><c>callback_mode()</c></seealso>
+ and possibly the atom
+ <seealso marker="#type-state_enter"><c>state_enter</c></seealso>.
+ </p>
+ <note>
+ <p>
+ If this function's body does not return an inline constant
+ value the callback module is doing something strange.
+ </p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
<name>Module:code_change(OldVsn, OldState, OldData, Extra) ->
Result
</name>
@@ -1262,11 +1632,7 @@ handle_event(_, _, State, Data) ->
<v>&nbsp;&nbsp;Vsn = term()</v>
<v>OldState = NewState = term()</v>
<v>Extra = term()</v>
- <v>Result = {NewCallbackMode,NewState,NewData} | Reason</v>
- <v>
- NewCallbackMode =
- <seealso marker="#type-callback_mode">callback_mode()</seealso>
- </v>
+ <v>Result = {ok,NewState,NewData} | Reason</v>
<v>
OldState = NewState =
<seealso marker="#type-state">state()</seealso>
@@ -1295,21 +1661,6 @@ handle_event(_, _, State, Data) ->
<c>Module</c>. If no such attribute is defined, the version
is the checksum of the Beam file.
</p>
- <note>
- <p>
- If you would dare to change
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
- during release upgrade/downgrade, the upgrade is no problem,
- as the new code surely knows what <em>callback mode</em>
- it needs. However, for a downgrade this function must
- know from argument <c>Extra</c> that comes from the
- <seealso marker="sasl:appup"><c>sasl:appup</c></seealso>
- file what <em>callback mode</em> the old code did use.
- It can also be possible to figure this out
- from argument <c>{down,Vsn}</c>, as <c>Vsn</c>
- in effect defines the old callback module version.
- </p>
- </note>
<p>
<c>OldState</c> and <c>OldData</c> is the internal state
of the <c>gen_statem</c>.
@@ -1321,41 +1672,46 @@ handle_event(_, _, State, Data) ->
<p>
If successful, the function must return the updated
internal state in an
- <c>{NewCallbackMode,NewState,NewData}</c> tuple.
- </p>
- <p>
- If the function returns <c>Reason</c>, the ongoing
- upgrade fails and rolls back to the old release.</p>
- <p>
- This function can use
- <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
- to return <c>Result</c> or <c>Reason</c>.
+ <c>{ok,NewState,NewData}</c> tuple.
+ </p>
+ <p>
+ If the function returns a failure <c>Reason</c>, the ongoing
+ upgrade fails and rolls back to the old release.
+ Note that <c>Reason</c> can not be an <c>{ok,_,_}</c> tuple
+ since that will be regarded as a
+ <c>{ok,NewState,NewData}</c> tuple,
+ and that a tuple matching <c>{ok,_}</c>
+ is an also invalid failure <c>Reason</c>.
+ It is recommended to use an atom as <c>Reason</c> since
+ it will be wrapped in an <c>{error,Reason}</c> tuple.
+ </p>
+ <p>
+ Also note when upgrading a <c>gen_statem</c>,
+ this function and hence
+ the <c>Change={advanced,Extra}</c> parameter in the
+ <seealso marker="sasl:appup"><c>appup</c></seealso> file
+ is not only needed to update the internal state
+ or to act on the <c>Extra</c> argument.
+ It is also needed if an upgrade or downgrade should change
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>,
+ or else the callback mode after the code change
+ will not be honoured,
+ most probably causing a server crash.
</p>
</desc>
</func>
<func>
- <name>Module:init(Args) -> Result</name>
- <fsummary>Initialize process and internal state.</fsummary>
+ <name>Module:init(Args) -> Result(StateType)</name>
+ <fsummary>
+ Optional function for initializing process and internal state.
+ </fsummary>
<type>
<v>Args = term()</v>
- <v>Result = {CallbackMode,State,Data}</v>
- <v>&nbsp;| {CallbackMode,State,Data,Actions}</v>
- <v>&nbsp;| {stop,Reason} | ignore</v>
<v>
- CallbackMode =
- <seealso marker="#type-callback_mode">callback_mode()</seealso>
+ Result(StateType) =
+ <seealso marker="#type-init_result">init_result(StateType)</seealso>
</v>
- <v>State = <seealso marker="#type-state">state()</seealso></v>
- <v>
- Data = <seealso marker="#type-data">data()</seealso>
- </v>
- <v>
- Actions =
- [<seealso marker="#type-action">action()</seealso>] |
- <seealso marker="#type-action">action()</seealso>
- </v>
- <v>Reason = term()</v>
</type>
<desc>
<marker id="Module:init-1"/>
@@ -1364,42 +1720,23 @@ handle_event(_, _, State, Data) ->
<seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>
or
<seealso marker="#start/3"><c>start/3,4</c></seealso>,
- this function is called by the new process to initialize
+ this optional function is called by the new process to initialize
the implementation state and server data.
</p>
<p>
- <c>Args</c> is the <c>Args</c> argument provided to the start
+ <c>Args</c> is the <c>Args</c> argument provided to that start
function.
</p>
- <p>
- If the initialization is successful, the function is to
- return <c>{CallbackMode,State,Data}</c> or
- <c>{CallbackMode,State,Data,Actions}</c>.
- <c>CallbackMode</c> selects the
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
- of the <c>gen_statem</c>.
- <c>State</c> is the initial
- <seealso marker="#type-state"><c>state()</c></seealso>
- and <c>Data</c> the initial server
- <seealso marker="#type-data"><c>data()</c></seealso>.
- </p>
- <p>
- The <seealso marker="#type-action"><c>Actions</c></seealso>
- are executed when entering the first
- <seealso marker="#type-state">state</seealso> just as for a
- <seealso marker="#state_function">state function</seealso>.
- </p>
- <p>
- If the initialization fails,
- the function is to return <c>{stop,Reason}</c>
- or <c>ignore</c>; see
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
- </p>
- <p>
- This function can use
- <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
- to return <c>Result</c>.
- </p>
+ <note>
+ <p>
+ This callback is optional, so a callback module does not need
+ to export it, but most do. If this function is not exported,
+ the <c>gen_statem</c> should be started through
+ <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ and
+ <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>.
+ </p>
+ </note>
</desc>
</func>
@@ -1430,10 +1767,14 @@ handle_event(_, _, State, Data) ->
This callback is optional, so a callback module does not need
to export it. The <c>gen_statem</c> module provides a default
implementation of this function that returns
- <c>{State,Data}</c>. If this callback fails, the default
- function returns <c>{State,Info}</c>,
- where <c>Info</c> informs of the crash but no details,
- to hide possibly sensitive data.
+ <c>{State,Data}</c>.
+ </p>
+ <p>
+ If this callback is exported but fails,
+ to hide possibly sensitive data,
+ the default function will instead return <c>{State,Info}</c>,
+ where <c>Info</c> says nothing but the fact that
+ <c>format_status/2</c> has crashed.
</p>
</note>
<p>This function is called by a <c>gen_statem</c> process when
@@ -1474,7 +1815,8 @@ handle_event(_, _, State, Data) ->
</p>
<p>
The function is to return <c>Status</c>, a term that
- changes the details of the current state and status of
+ contains the appropriate details
+ of the current state and status of
the <c>gen_statem</c>. There are no restrictions on the
form <c>Status</c> can take, but for the
<seealso marker="sys#get_status/1"><c>sys:get_status/1,2</c></seealso>
@@ -1494,20 +1836,21 @@ handle_event(_, _, State, Data) ->
printed in log files. Another use is to hide sensitive data from
being written to the error log.
</p>
- <p>
- This function can use
- <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
- to return <c>Status</c>.
- </p>
</desc>
</func>
<func>
+ <name>Module:StateName(enter, OldState, Data) ->
+ StateEnterResult(StateName)
+ </name>
<name>Module:StateName(EventType, EventContent, Data) ->
StateFunctionResult
</name>
- <name>Module:handle_event(EventType, EventContent,
- State, Data) -> HandleEventResult
+ <name>Module:handle_event(enter, OldState, State, Data) ->
+ StateEnterResult(State)
+ </name>
+ <name>Module:handle_event(EventType, EventContent, State, Data) ->
+ HandleEventResult
</name>
<fsummary>Handle an event.</fsummary>
<type>
@@ -1525,12 +1868,20 @@ handle_event(_, _, State, Data) ->
<seealso marker="#type-data">data()</seealso>
</v>
<v>
+ StateEnterResult(StateName) =
+ <seealso marker="#type-state_enter_result">state_enter_result(StateName)</seealso>
+ </v>
+ <v>
StateFunctionResult =
- <seealso marker="#type-state_function_result">state_function_result()</seealso>
+ <seealso marker="#type-event_handler_result">event_handler_result</seealso>(<seealso marker="#type-state_name">state_name()</seealso>)
+ </v>
+ <v>
+ StateEnterResult(State) =
+ <seealso marker="#type-state_enter_result">state_enter_result(State)</seealso>
</v>
<v>
HandleEventResult =
- <seealso marker="#type-handle_event_result">handle_event_result()</seealso>
+ <seealso marker="#type-event_handler_result">event_handler_result</seealso>(<seealso marker="#type-state">state()</seealso>)
</v>
</type>
<desc>
@@ -1549,7 +1900,7 @@ handle_event(_, _, State, Data) ->
<seealso marker="#type-event_type"><c>{call,From}</c></seealso>,
the caller waits for a reply. The reply can be sent
from this or from any other
- <seealso marker="#state_function">state function</seealso>
+ <seealso marker="#state callback">state callback</seealso>
by returning with <c>{reply,From,Reply}</c> in
<seealso marker="#type-action"><c>Actions</c></seealso>, in
<seealso marker="#type-reply_action"><c>Replies</c></seealso>,
@@ -1573,9 +1924,41 @@ handle_event(_, _, State, Data) ->
see <seealso marker="#type-action"><c>action()</c></seealso>.
</p>
<p>
- These functions can use
- <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>,
- to return the result.
+ When the <c>gen_statem</c> runs with
+ <seealso marker="#type-state_enter">state enter calls</seealso>,
+ these functions are also called with arguments
+ <c>(enter, OldState, ...)</c> whenever the state changes.
+ In this case there are some restrictions on the
+ <seealso marker="#type-enter_action">actions</seealso>
+ that may be returned:
+ <seealso marker="#type-postpone"><c>postpone()</c></seealso>
+ is not allowed since a <em>state enter call</em> is not
+ an event so there is no event to postpone, and
+ <seealso marker="#type-action"><c>{next_event,_,_}</c></seealso>
+ is not allowed since using <em>state enter calls</em>
+ should not affect how events are consumed and produced.
+ You may also not change states from this call.
+ Should you return <c>{next_state,NextState, ...}</c>
+ with <c>NextState =/= State</c> the <c>gen_statem</c> crashes.
+ It is possible to use <c>{repeat_state, ...}</c>,
+ <c>{repeat_state_and_data,_}</c> or
+ <c>repeat_state_and_data</c> but all of them makes little
+ sense since you immediately will be called again with a new
+ <em>state enter call</em> making this just a weird way
+ of looping, and there are better ways to loop in Erlang.
+ You are advised to use <c>{keep_state,...}</c>,
+ <c>{keep_state_and_data,_}</c> or
+ <c>keep_state_and_data</c> since you can not change states
+ from a <em>state enter call</em> anyway.
+ </p>
+ <p>
+ Note the fact that you can use
+ <seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>
+ to return the result, which can be useful.
+ For example to bail out with <c>throw(keep_state_and_data)</c>
+ from deep within complex code that can not
+ return <c>{next_state,State,Data}</c> because
+ <c>State</c> or <c>Data</c> is no longer in scope.
</p>
</desc>
</func>
@@ -1590,6 +1973,11 @@ handle_event(_, _, State, Data) ->
<v>Ignored = term()</v>
</type>
<desc>
+ <note>
+ <p>This callback is optional, so callback modules need not
+ export it. The <c>gen_statem</c> module provides a default
+ implementation without cleanup.</p>
+ </note>
<p>
This function is called by a <c>gen_statem</c>
when it is about to terminate. It is to be the opposite of
@@ -1648,11 +2036,6 @@ handle_event(_, _, State, Data) ->
and an error report is issued using
<seealso marker="kernel:error_logger#format/2"><c>error_logger:format/2</c></seealso>.
</p>
- <p>
- This function can use
- <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
- to return <c>Ignored</c>, which is ignored anyway.
- </p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/introduction.xml b/lib/stdlib/doc/src/introduction.xml
index 5bf545c65f..642ca02430 100644
--- a/lib/stdlib/doc/src/introduction.xml
+++ b/lib/stdlib/doc/src/introduction.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2013</year>
+ <year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml
index e1edbadcd3..8c7270816b 100644
--- a/lib/stdlib/doc/src/maps.xml
+++ b/lib/stdlib/doc/src/maps.xml
@@ -160,7 +160,7 @@ val1
<p><em>Example:</em></p>
<code type="none">
> Map = #{"42" => value}.
-#{"42"> => value}
+#{"42" => value}
> maps:is_key("42",Map).
true
> maps:is_key(value,Map).
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index ad2599c5a0..bb35224182 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,6 +31,227 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>An escript with only two lines would not work.</p>
+ <p>
+ Own Id: OTP-14098</p>
+ </item>
+ <item>
+ <p> Characters (<c>$char</c>) can be used in constant
+ pattern expressions. They can also be used in types and
+ contracts. </p>
+ <p>
+ Own Id: OTP-14103 Aux Id: ERL-313 </p>
+ </item>
+ <item>
+ <p> The signatures of <c>erl_parse:anno_to_term/1</c> and
+ <c>erl_parse:anno_from_term/1</c> are corrected. Using
+ these functions no longer results in false Dialyzer
+ warnings. </p>
+ <p>
+ Own Id: OTP-14131</p>
+ </item>
+ <item>
+ <p>Pretty-printing of maps is improved. </p>
+ <p>
+ Own Id: OTP-14175 Aux Id: seq13277 </p>
+ </item>
+ <item>
+ <p>If any of the following functions in the <c>zip</c>
+ module crashed, a file would be left open:
+ <c>extract()</c>, <c>unzip()</c>, <c>create()</c>, or
+ <c>zip()</c>. This has been corrected.</p>
+ <p>A <c>zip</c> file having a "Unix header" could not be
+ unpacked.</p>
+ <p>
+ Own Id: OTP-14189 Aux Id: ERL-348, ERL-349 </p>
+ </item>
+ <item>
+ <p> Improve the Erlang shell's tab-completion of long
+ names. </p>
+ <p>
+ Own Id: OTP-14200 Aux Id: ERL-352 </p>
+ </item>
+ <item>
+ <p>
+ The reference manual for <c>sys</c> had some faulty
+ information about the 'get_modules' message used by
+ processes where modules change dynamically during
+ runtime. The documentation is now corrected.</p>
+ <p>
+ Own Id: OTP-14248 Aux Id: ERL-367 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Bug fixes, new features and improvements to gen_statem:</p>
+ <p>
+ A new type init_result/1 has replaced the old
+ init_result/0, so if you used that old type (that was
+ never documented) you have to change your code, which may
+ be regarded as a potential incompatibility.</p>
+ <p>
+ Changing callback modes after code change did not work
+ since the new callback mode was not recorded. This bug
+ has been fixed.</p>
+ <p>
+ The event types state_timeout and {call,From} could not
+ be generated with a {next_event,EventType,EventContent}
+ action since they did not pass the runtime type check.
+ This bug has now been corrected.</p>
+ <p>
+ State entry calls can now be repeated using (new) state
+ callback returns {repeat_state,...},
+ {repeat_state_and_data,_} and repeat_state_and_data.</p>
+ <p>
+ There have been lots of code cleanup in particular
+ regarding timer handling. For example is async
+ cancel_timer now used. Error handling has also been
+ cleaned up.</p>
+ <p>
+ To align with probable future changes to the rest of
+ gen_*, terminate/3 has now got a fallback and
+ code_change/4 is not mandatory.</p>
+ <p>
+ Own Id: OTP-14114</p>
+ </item>
+ <item>
+ <p><c>filename:safe_relative_path/1</c> to sanitize a
+ relative path has been added.</p>
+ <p>
+ Own Id: OTP-14215</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When a simple_one_for_one supervisor is shutting down,
+ and a child exits with an exit reason of the form
+ {shutdown, Term}, an error report was earlier printed.
+ This is now corrected.</p>
+ <p>
+ Own Id: OTP-13907 Aux Id: PR-1158, ERL-163 </p>
+ </item>
+ <item>
+ <p> Allow empty list as parameter of the fun used with
+ <c>dbg:fun2ms/1</c>. </p>
+ <p>
+ Own Id: OTP-13974</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The new behaviour gen_statem has been improved with 3 new
+ features: the possibility to use old style non-proxy
+ timeouts for gen_statem:call/2,3, state entry code, and
+ state timeouts. These are backwards compatible. Minor
+ code and documentation improvements has been performed
+ including a borderline semantics correction of timeout
+ zero handling.</p>
+ <p>
+ Own Id: OTP-13929 Aux Id: PR-1170, ERL-284 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The <c>zip:unzip/1,2</c> and <c>zip:extract/1,2</c>
+ functions have been updated to handle directory traversal
+ exploits. Any element in the zip file that contains a
+ path that points to a directory above the top level
+ working directory, <c>cwd</c>, will instead be extracted
+ in <c>cwd</c>. An error message is printed for any such
+ element in the zip file during the unzip operation. The
+ <c>keep_old_files</c> option determines if a file will
+ overwrite a previous file with the same name within the
+ zip file.</p>
+ <p>
+ Own Id: OTP-13633</p>
+ </item>
+ <item>
+ <p> Correct the contracts for
+ <c>ets:match_object/1,3</c>. </p>
+ <p>
+ Own Id: OTP-13721 Aux Id: PR-1113 </p>
+ </item>
+ <item>
+ <p>
+ Errors in type specification and Emacs template
+ generation for <c>gen_statem:code_change/4</c> has been
+ fixed from bugs.erlang.org's Jira cases ERL-172 and
+ ERL-187.</p>
+ <p>
+ Own Id: OTP-13746 Aux Id: ERL-172, ERL-187 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ gen_statem has been changed to set the callback mode for
+ a server to what Module:callback_mode/0 returns. This
+ facilitates e.g code downgrade since the callback mode
+ now becomes a property of the currently active code, not
+ of the server process.</p>
+ <p>
+ Exception handling from Module:init/1 has also been
+ improved.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13752</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 3.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct a bug regarding typed records in the Erlang
+ shell. The bug was introduced in OTP-19.0. </p>
+ <p>
+ Own Id: OTP-13719 Aux Id: ERL-182 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -511,7 +732,7 @@
</item>
<item>
<p>
- The <c>stdlib</c> reference manual is updated to show
+ The STDLIB reference manual is updated to show
correct information about the return value of
<c>gen_fsm:reply/2</c>.</p>
<p>
@@ -6221,7 +6442,7 @@
documentation for <c>compile</c> on how to provide the key
for encrypting, and the documentation for <c>beam_lib</c>
on how to provide the key for decryption so that tools such
- as the Debugger, <c>xref</c>, or <c>cover</c> can be used.</p>
+ as the Debugger, Xref, or Cover can be used.</p>
<p>The <c>beam_lib:chunks/2</c> functions now accepts an
additional chunk type <c>compile_info</c> to retrieve
the compilation information directly as a term. (Thanks
diff --git a/lib/stdlib/doc/src/orddict.xml b/lib/stdlib/doc/src/orddict.xml
index 076b06fc38..d048983c61 100644
--- a/lib/stdlib/doc/src/orddict.xml
+++ b/lib/stdlib/doc/src/orddict.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2000</year><year>2015</year>
+ <year>2000</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml
index 58ca5644cf..da03c39a26 100644
--- a/lib/stdlib/doc/src/proc_lib.xml
+++ b/lib/stdlib/doc/src/proc_lib.xml
@@ -59,9 +59,9 @@
<p>When a process that is started using <c>proc_lib</c> terminates
abnormally (that is, with another exit reason than <c>normal</c>,
<c>shutdown</c>, or <c>{shutdown,Term}</c>), a <em>crash report</em>
- is generated, which is written to terminal by the default <c>SASL</c>
+ is generated, which is written to terminal by the default SASL
event handler. That is, the crash report is normally only visible
- if the <c>SASL</c> application is started; see
+ if the SASL application is started; see
<seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso> and section
<seealso marker="sasl:error_logging">SASL Error Logging</seealso>
in the SASL User's Guide.</p>
diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml
index a46ca47033..9f3aff03a3 100644
--- a/lib/stdlib/doc/src/queue.xml
+++ b/lib/stdlib/doc/src/queue.xml
@@ -401,7 +401,7 @@
</func>
<func>
- <name name="liat" arity="1"/>v
+ <name name="liat" arity="1"/>
<fsummary>Remove the tail item from a queue.</fsummary>
<desc>
<p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing
diff --git a/lib/stdlib/doc/src/rand.xml b/lib/stdlib/doc/src/rand.xml
index 1dcc3de000..eb7870e367 100644
--- a/lib/stdlib/doc/src/rand.xml
+++ b/lib/stdlib/doc/src/rand.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2015</year>
+ <year>2015</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/sets.xml b/lib/stdlib/doc/src/sets.xml
index f7668af1ed..44dc104645 100644
--- a/lib/stdlib/doc/src/sets.xml
+++ b/lib/stdlib/doc/src/sets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2000</year><year>2015</year>
+ <year>2000</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/shell_default.xml b/lib/stdlib/doc/src/shell_default.xml
index 81c99bce10..75bf89ba8d 100644
--- a/lib/stdlib/doc/src/shell_default.xml
+++ b/lib/stdlib/doc/src/shell_default.xml
@@ -51,7 +51,7 @@
<p>In command one, module <seealso marker="lists"><c>lists</c></seealso> is
called. In command two, no module name is specified. The shell searches
module <c>user_default</c> followed by module <c>shell_default</c> for
- function <c>foo/1</c>.</p>
+ function <c>c/1</c>.</p>
<p><c>shell_default</c> is intended for "system wide"
customizations to the shell. <c>user_default</c> is intended for
diff --git a/lib/stdlib/doc/src/stdlib_app.xml b/lib/stdlib/doc/src/stdlib_app.xml
index cde73269a8..f857cc394b 100644
--- a/lib/stdlib/doc/src/stdlib_app.xml
+++ b/lib/stdlib/doc/src/stdlib_app.xml
@@ -31,14 +31,14 @@
<app>STDLIB</app>
<appsummary>The STDLIB application.</appsummary>
<description>
- <p>The <c>STDLIB</c> application is mandatory in the sense that the minimal
- system based on Erlang/OTP consists of <c>Kernel</c> and <c>STDLIB</c>.
- The <c>STDLIB</c> application contains no services.</p>
+ <p>The STDLIB application is mandatory in the sense that the minimal
+ system based on Erlang/OTP consists of Kernel and STDLIB.
+ The STDLIB application contains no services.</p>
</description>
<section>
<title>Configuration</title>
- <p>The following configuration parameters are defined for the <c>STDLIB</c>
+ <p>The following configuration parameters are defined for the STDLIB
application. For more information about configuration parameters, see the
<seealso marker="kernel:app"><c>app(4)</c></seealso> module in Kernel.</p>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index 1120b926d5..0fed649660 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2014</year>
+ <year>1996</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -83,8 +83,8 @@
<p>If the modules used to implement the process change dynamically
during runtime, the process must understand one more message. An
example is the <seealso marker="gen_event"><c>gen_event</c></seealso>
- processes. The message is <c>{get_modules, From}</c>.
- The reply to this message is <c>From ! {modules, Modules}</c>, where
+ processes. The message is <c>{_Label, {From, Ref}, get_modules}</c>.
+ The reply to this message is <c>From ! {Ref, Modules}</c>, where
<c>Modules</c> is a list of the currently active modules in the
process.</p>
<p>This message is used by the release handler to find which
diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml
index 8f2ce36b06..fcaccdb2cb 100644
--- a/lib/stdlib/doc/src/timer.xml
+++ b/lib/stdlib/doc/src/timer.xml
@@ -253,7 +253,7 @@
is needed. This is useful during development, but in a
target system the server is to be started explicitly. Use
configuration parameters for
- <seealso marker="kernel:index"><c>Kernel</c></seealso> for this.</p>
+ <seealso marker="kernel:index">Kernel</seealso> for this.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index 7f79ac88a1..efc8b75075 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -274,8 +274,8 @@
marker="stdlib:io"><c>io</c></seealso> module, the file
handling, the <seealso
marker="stdlib:unicode"><c>unicode</c></seealso> module, and
- the bit syntax). Today most modules in <c>Kernel</c> and
- <c>STDLIB</c>, as well as the VM are Unicode-aware.</p>
+ the bit syntax). Today most modules in Kernel and
+ STDLIB, as well as the VM are Unicode-aware.</p>
</item>
<tag>File I/O</tag>
<item>
@@ -765,7 +765,7 @@ Eshell V5.10.1 (abort with ^G)
file system). The Unicode character list is used to denote
filenames or directory names. If the file system content is
listed, you also get Unicode lists as return value. The support
- lies in the <c>Kernel</c> and <c>STDLIB</c> modules, which is why
+ lies in the Kernel and STDLIB modules, which is why
most applications (that does not explicitly require the filenames
to be in the ISO Latin-1 range) benefit from the Unicode support
without change.</p>
@@ -843,7 +843,7 @@ Eshell V5.10.1 (abort with ^G)
<title>Notes About Raw Filenames</title>
<marker id="notes-about-raw-filenames"/>
<p>Raw filenames were introduced together with Unicode filename support
- in <c>ERTS</c> 5.8.2 (Erlang/OTP R14B01). The reason &quot;raw
+ in ERTS 5.8.2 (Erlang/OTP R14B01). The reason &quot;raw
filenames&quot; were introduced in the system was
to be able to represent
filenames, specified in different encodings on the same system,
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index de23608046..0b5eac1e16 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -138,7 +138,7 @@
<p>File information as in
<seealso marker="kernel:file#read_file_info/1">
<c>file:read_file_info/1</c></seealso>
- in <c>Kernel</c></p>
+ in Kernel</p>
</item>
<tag><c>comment</c></tag>
<item>
@@ -345,7 +345,7 @@
prepended to filenames when extracting them from the
zip archive. (Acting like
<seealso marker="kernel:file#set_cwd/1">
- <c>file:set_cwd/1</c></seealso> in <c>Kernel</c>,
+ <c>file:set_cwd/1</c></seealso> in Kernel,
but without changing the global <c>cwd</c> property.)</p>
</item>
</taglist>
@@ -420,7 +420,7 @@
(<c>cwd</c>). This is prepended to filenames when adding them,
although not in the zip archive (acting like
<seealso marker="kernel:file#set_cwd/1">
- <c>file:set_cwd/1</c></seealso> in <c>Kernel</c>, but without
+ <c>file:set_cwd/1</c></seealso> in Kernel, but without
changing the global <c>cwd</c> property.).</p>
</item>
<tag><c>{compress, <anno>What</anno>}</c></tag>
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index fe9df601eb..d7ee5c1f5d 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -55,7 +55,7 @@
-type beam() :: module() | file:filename() | binary().
--type forms() :: [erl_parse:abstract_form()].
+-type forms() :: [erl_parse:abstract_form() | erl_parse:form_info()].
-type abst_code() :: {AbstVersion :: atom(), forms()} | 'no_abstract_code'.
-type dataB() :: binary().
diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl
index ec64470461..a1a97af4c5 100644
--- a/lib/stdlib/src/edlin_expand.erl
+++ b/lib/stdlib/src/edlin_expand.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2017. 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.
@@ -101,44 +101,77 @@ match(Prefix, Alts, Extra0) ->
%% Return the list of names L in multiple columns.
format_matches(L) ->
- S = format_col(lists:sort(L), []),
+ {S1, Dots} = format_col(lists:sort(L), []),
+ S = case Dots of
+ true ->
+ {_, Prefix} = longest_common_head(vals(L)),
+ PrefixLen = length(Prefix),
+ case PrefixLen =< 3 of
+ true -> S1; % Do not replace the prefix with "...".
+ false ->
+ LeadingDotsL = leading_dots(L, PrefixLen),
+ {S2, _} = format_col(lists:sort(LeadingDotsL), []),
+ S2
+ end;
+ false -> S1
+ end,
["\n" | S].
format_col([], _) -> [];
-format_col(L, Acc) -> format_col(L, field_width(L), 0, Acc).
-
-format_col(X, Width, Len, Acc) when Width + Len > 79 ->
- format_col(X, Width, 0, ["\n" | Acc]);
-format_col([A|T], Width, Len, Acc0) ->
- H = case A of
- %% If it's a tuple {string(), integer()}, we assume it's an
- %% arity, and meant to be printed.
- {H0, I} when is_integer(I) ->
- H0 ++ "/" ++ integer_to_list(I);
- {H1, _} -> H1;
- H2 -> H2
- end,
- Acc = [io_lib:format("~-*s", [Width,H]) | Acc0],
- format_col(T, Width, Len+Width, Acc);
-format_col([], _, _, Acc) ->
- lists:reverse(Acc, "\n").
-
-field_width(L) -> field_width(L, 0).
-
-field_width([{H,_}|T], W) ->
+format_col(L, Acc) ->
+ LL = 79,
+ format_col(L, field_width(L, LL), 0, Acc, LL, false).
+
+format_col(X, Width, Len, Acc, LL, Dots) when Width + Len > LL ->
+ format_col(X, Width, 0, ["\n" | Acc], LL, Dots);
+format_col([A|T], Width, Len, Acc0, LL, Dots) ->
+ {H0, R} = format_val(A),
+ Hmax = LL - length(R),
+ {H, NewDots} =
+ case length(H0) > Hmax of
+ true -> {io_lib:format("~-*ts", [Hmax - 3, H0]) ++ "...", true};
+ false -> {H0, Dots}
+ end,
+ Acc = [io_lib:format("~-*ts", [Width, H ++ R]) | Acc0],
+ format_col(T, Width, Len+Width, Acc, LL, NewDots);
+format_col([], _, _, Acc, _LL, Dots) ->
+ {lists:reverse(Acc, "\n"), Dots}.
+
+format_val({H, I}) when is_integer(I) ->
+ %% If it's a tuple {string(), integer()}, we assume it's an
+ %% arity, and meant to be printed.
+ {H, "/" ++ integer_to_list(I)};
+format_val({H, _}) ->
+ {H, ""};
+format_val(H) ->
+ {H, ""}.
+
+field_width(L, LL) -> field_width(L, 0, LL).
+
+field_width([{H,_}|T], W, LL) ->
case length(H) of
- L when L > W -> field_width(T, L);
- _ -> field_width(T, W)
+ L when L > W -> field_width(T, L, LL);
+ _ -> field_width(T, W, LL)
end;
-field_width([H|T], W) ->
+field_width([H|T], W, LL) ->
case length(H) of
- L when L > W -> field_width(T, L);
- _ -> field_width(T, W)
+ L when L > W -> field_width(T, L, LL);
+ _ -> field_width(T, W, LL)
end;
-field_width([], W) when W < 40 ->
+field_width([], W, LL) when W < LL - 3 ->
W + 4;
-field_width([], _) ->
- 40.
+field_width([], _, LL) ->
+ LL.
+
+vals([]) -> [];
+vals([{S, _}|L]) -> [S|vals(L)];
+vals([S|L]) -> [S|vals(L)].
+
+leading_dots([], _Len) -> [];
+leading_dots([{H, I}|L], Len) ->
+ [{"..." ++ nthtail(Len, H), I}|leading_dots(L, Len)];
+leading_dots([H|L], Len) ->
+ ["..." ++ nthtail(Len, H)|leading_dots(L, Len)].
longest_common_head([]) ->
no;
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 40a34aa30f..eafee346eb 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -1306,6 +1306,7 @@ partial_eval(Expr) ->
ev_expr({op,_,Op,L,R}) -> erlang:Op(ev_expr(L), ev_expr(R));
ev_expr({op,_,Op,A}) -> erlang:Op(ev_expr(A));
ev_expr({integer,_,X}) -> X;
+ev_expr({char,_,X}) -> X;
ev_expr({float,_,X}) -> X;
ev_expr({atom,_,X}) -> X;
ev_expr({tuple,_,Es}) ->
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 85b2816451..d2dd2848b5 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -156,6 +156,7 @@ type -> '#' atom '{' field_types '}' : {type, ?anno('$1'),
record, ['$2'|'$4']}.
type -> binary_type : '$1'.
type -> integer : '$1'.
+type -> char : '$1'.
type -> 'fun' '(' ')' : {type, ?anno('$1'), 'fun', []}.
type -> 'fun' '(' fun_type_100 ')' : '$3'.
@@ -612,11 +613,11 @@ Erlang code.
| af_bin(abstract_expr())
| af_binary_op(abstract_expr())
| af_unary_op(abstract_expr())
- | af_record_access(abstract_expr())
+ | af_record_creation(abstract_expr())
| af_record_update(abstract_expr())
| af_record_index()
| af_record_field_access(abstract_expr())
- | af_map_access(abstract_expr())
+ | af_map_creation(abstract_expr())
| af_map_update(abstract_expr())
| af_catch()
| af_local_call()
@@ -720,26 +721,25 @@ Erlang code.
| af_bin(af_guard_test())
| af_binary_op(af_guard_test())
| af_unary_op(af_guard_test())
- | af_record_access(af_guard_test())
+ | af_record_creation(af_guard_test())
| af_record_index()
| af_record_field_access(af_guard_test())
- | af_map_access(abstract_expr()) % FIXME
- | af_map_update(abstract_expr()) % FIXME
+ | af_map_creation(abstract_expr())
+ | af_map_update(abstract_expr())
| af_guard_call()
| af_remote_guard_call().
-type af_record_field_access(T) ::
{'record_field', anno(), T, record_name(), af_field_name()}.
--type af_map_access(T) :: {'map', anno(), [af_map_field(T)]}.
-
--type af_map_update(T) :: {'map', anno(), T, [af_map_field(T)]}.
+-type af_map_creation(T) :: {'map', anno(), [af_assoc(T)]}.
--type af_map_field(T) :: af_map_field_assoc(T) | af_map_field_exact(T).
+-type af_map_update(T) :: {'map', anno(), T, [af_assoc(T)]}.
--type af_map_field_assoc(T) :: {'map_field_assoc', anno(), T, T}.
+-type af_assoc(T) :: {'map_field_assoc', anno(), T, T}
+ | af_assoc_exact(T).
--type af_map_field_exact(T) :: {'map_field_exact', anno(), T, T}.
+-type af_assoc_exact(T) :: {'map_field_exact', anno(), T, T}.
-type af_guard_call() :: {'call', anno(), function_name(), [af_guard_test()]}.
@@ -757,20 +757,20 @@ Erlang code.
| af_bin(af_pattern())
| af_binary_op(af_pattern())
| af_unary_op(af_pattern())
- | af_record_access(af_pattern())
+ | af_record_creation(af_pattern())
| af_record_index()
| af_map_pattern().
-type af_record_index() ::
{'record_index', anno(), record_name(), af_field_name()}.
--type af_record_access(T) ::
+-type af_record_creation(T) ::
{'record', anno(), record_name(), [af_record_field(T)]}.
-type af_record_field(T) :: {'record_field', anno(), af_field_name(), T}.
-type af_map_pattern() ::
- {'map', anno(), [af_map_field_exact(abstract_expr)]}. % FIXME?
+ {'map', anno(), [af_assoc_exact(abstract_expr)]}.
-type abstract_type() :: af_annotated_type()
| af_atom()
@@ -807,9 +807,9 @@ Erlang code.
{'type', anno(), 'range', [af_singleton_integer_type()]}.
-type af_map_type() :: {'type', anno(), 'map', 'any'}
- | {'type', anno(), 'map', [af_map_pair_type()]}.
+ | {'type', anno(), 'map', [af_assoc_type()]}.
--type af_map_pair_type() ::
+-type af_assoc_type() ::
{'type', anno(), 'map_field_assoc', [abstract_type()]}
| {'type', anno(), 'map_field_exact', [abstract_type()]}.
@@ -1558,13 +1558,17 @@ new_anno(Term) ->
Abstr :: erl_parse_tree().
anno_to_term(Abstract) ->
- map_anno(fun erl_anno:to_term/1, Abstract).
+ F = fun(Anno, Acc) -> {erl_anno:to_term(Anno), Acc} end,
+ {NewAbstract, []} = modify_anno1(Abstract, [], F),
+ NewAbstract.
-spec anno_from_term(Term) -> erl_parse_tree() when
Term :: term().
anno_from_term(Term) ->
- map_anno(fun erl_anno:from_term/1, Term).
+ F = fun(T, Acc) -> {erl_anno:from_term(T), Acc} end,
+ {NewTerm, []} = modify_anno1(Term, [], F),
+ NewTerm.
%% Forms.
%% Recognize what sys_pre_expand does:
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index f53b0e2246..c42ae981e7 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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.
@@ -481,46 +481,49 @@ find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst, Sections) ->
%% Look for special comment on second line
Line2 = get_line(Fd),
{ok, HeaderSz2} = file:position(Fd, cur),
- case classify_line(Line2) of
- emu_args ->
- %% Skip special comment on second line
- Line3 = get_line(Fd),
- {HeaderSz2, LineNo + 2, Fd,
- Sections#sections{type = guess_type(Line3),
- comment = undefined,
- emu_args = Line2}};
- Line2Type ->
- %% Look for special comment on third line
- Line3 = get_line(Fd),
- {ok, HeaderSz3} = file:position(Fd, cur),
- Line3Type = classify_line(Line3),
- if
- Line3Type =:= emu_args ->
- %% Skip special comment on third line
- Line4 = get_line(Fd),
- {HeaderSz3, LineNo + 3, Fd,
- Sections#sections{type = guess_type(Line4),
- comment = Line2,
- emu_args = Line3}};
- Sections#sections.shebang =:= undefined,
- KeepFirst =:= true ->
- %% No shebang. Use the entire file
- {HeaderSz0, LineNo, Fd,
- Sections#sections{type = guess_type(Line2)}};
- Sections#sections.shebang =:= undefined ->
- %% No shebang. Skip the first line
- {HeaderSz1, LineNo, Fd,
- Sections#sections{type = guess_type(Line2)}};
- Line2Type =:= comment ->
- %% Skip shebang on first line and comment on second
- {HeaderSz2, LineNo + 2, Fd,
- Sections#sections{type = guess_type(Line3),
- comment = Line2}};
- true ->
- %% Just skip shebang on first line
- {HeaderSz1, LineNo + 1, Fd,
- Sections#sections{type = guess_type(Line2)}}
- end
+ if
+ Sections#sections.shebang =:= undefined,
+ KeepFirst =:= true ->
+ %% No shebang. Use the entire file
+ {HeaderSz0, LineNo, Fd,
+ Sections#sections{type = guess_type(Line2)}};
+ Sections#sections.shebang =:= undefined ->
+ %% No shebang. Skip the first line
+ {HeaderSz1, LineNo, Fd,
+ Sections#sections{type = guess_type(Line2)}};
+ true ->
+ case classify_line(Line2) of
+ emu_args ->
+ %% Skip special comment on second line
+ Line3 = get_line(Fd),
+ {HeaderSz2, LineNo + 2, Fd,
+ Sections#sections{type = guess_type(Line3),
+ comment = undefined,
+ emu_args = Line2}};
+ comment ->
+ %% Look for special comment on third line
+ Line3 = get_line(Fd),
+ {ok, HeaderSz3} = file:position(Fd, cur),
+ Line3Type = classify_line(Line3),
+ if
+ Line3Type =:= emu_args ->
+ %% Skip special comment on third line
+ Line4 = get_line(Fd),
+ {HeaderSz3, LineNo + 3, Fd,
+ Sections#sections{type = guess_type(Line4),
+ comment = Line2,
+ emu_args = Line3}};
+ true ->
+ %% Skip shebang on first line and comment on second
+ {HeaderSz2, LineNo + 2, Fd,
+ Sections#sections{type = guess_type(Line3),
+ comment = Line2}}
+ end;
+ _ ->
+ %% Just skip shebang on first line
+ {HeaderSz1, LineNo + 1, Fd,
+ Sections#sections{type = guess_type(Line2)}}
+ end
end.
classify_line(Line) ->
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 3f74e01692..20de06fd0b 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -232,20 +232,20 @@ match(_) ->
match_object(_, _) ->
erlang:nif_error(undef).
--spec match_object(Tab, Pattern, Limit) -> {[Match], Continuation} |
+-spec match_object(Tab, Pattern, Limit) -> {[Object], Continuation} |
'$end_of_table' when
Tab :: tab(),
Pattern :: match_pattern(),
Limit :: pos_integer(),
- Match :: [term()],
+ Object :: tuple(),
Continuation :: continuation().
match_object(_, _, _) ->
erlang:nif_error(undef).
--spec match_object(Continuation) -> {[Match], Continuation} |
+-spec match_object(Continuation) -> {[Object], Continuation} |
'$end_of_table' when
- Match :: [term()],
+ Object :: tuple(),
Continuation :: continuation().
match_object(_) ->
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index c4586171ca..9eeba874e1 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. 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.
@@ -34,7 +34,8 @@
-export([absname/1, absname/2, absname_join/2,
basename/1, basename/2, dirname/1,
extension/1, join/1, join/2, pathtype/1,
- rootname/1, rootname/2, split/1, nativename/1]).
+ rootname/1, rootname/2, split/1, nativename/1,
+ safe_relative_path/1]).
-export([find_src/1, find_src/2, flatten/1]).
-export([basedir/2, basedir/3]).
@@ -750,6 +751,39 @@ separators() ->
_ -> {false, false}
end.
+-spec safe_relative_path(Filename) -> 'unsafe' | SafeFilename when
+ Filename :: file:name_all(),
+ SafeFilename :: file:name_all().
+
+safe_relative_path(Path) ->
+ case pathtype(Path) of
+ relative ->
+ Cs0 = split(Path),
+ safe_relative_path_1(Cs0, []);
+ _ ->
+ unsafe
+ end.
+
+safe_relative_path_1(["."|T], Acc) ->
+ safe_relative_path_1(T, Acc);
+safe_relative_path_1([<<".">>|T], Acc) ->
+ safe_relative_path_1(T, Acc);
+safe_relative_path_1([".."|T], Acc) ->
+ climb(T, Acc);
+safe_relative_path_1([<<"..">>|T], Acc) ->
+ climb(T, Acc);
+safe_relative_path_1([H|T], Acc) ->
+ safe_relative_path_1(T, [H|Acc]);
+safe_relative_path_1([], []) ->
+ [];
+safe_relative_path_1([], Acc) ->
+ join(lists:reverse(Acc)).
+
+climb(_, []) ->
+ unsafe;
+climb(T, [_|Acc]) ->
+ safe_relative_path_1(T, Acc).
+
%% find_src(Module) --
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index ccacf658e9..0ded1e9fa6 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -742,7 +742,7 @@ stop_handlers([], _) ->
[].
%% Message from the release_handler.
-%% The list of modules got to be a set !
+%% The list of modules got to be a set, i.e. no duplicate elements!
get_modules(MSL) ->
Mods = [Handler#handler.module || Handler <- MSL],
ordsets:to_list(ordsets:from_list(Mods)).
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 23bddafeed..cacc932ec4 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2016. All Rights Reserved.
+%% Copyright Ericsson AB 2016-2017. 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.
@@ -24,7 +24,7 @@
[start/3,start/4,start_link/3,start_link/4,
stop/1,stop/3,
cast/2,call/2,call/3,
- enter_loop/5,enter_loop/6,enter_loop/7,
+ enter_loop/4,enter_loop/5,enter_loop/6,
reply/1,reply/2]).
%% gen callbacks
@@ -44,15 +44,22 @@
-export(
[wakeup_from_hibernate/3]).
-%% Type exports for templates
+%% Type exports for templates and callback modules
-export_type(
[event_type/0,
- callback_mode/0,
- state_function_result/0,
- handle_event_result/0,
+ callback_mode_result/0,
+ init_result/1,
+ state_enter_result/1,
+ event_handler_result/1,
+ reply_action/0,
+ enter_action/0,
action/0]).
+%% Old types, not advertised
+-export_type(
+ [state_function_result/0,
+ handle_event_result/0]).
-%% Fix problem for doc build
+%% Type that is exported just to be documented
-export_type([transition_option/0]).
%%%==========================================================================
@@ -63,8 +70,8 @@
{To :: pid(), Tag :: term()}. % Reply-to specifier for call
-type state() ::
- state_name() | % For state callback function StateName/5
- term(). % For state callback function handle_event/5
+ state_name() | % For StateName/3 callback functions
+ term(). % For handle_event/4 callback function
-type state_name() :: atom().
@@ -72,12 +79,16 @@
-type event_type() ::
{'call',From :: from()} | 'cast' |
- 'info' | 'timeout' | 'internal'.
+ 'info' | 'timeout' | 'state_timeout' | 'internal'.
+-type callback_mode_result() ::
+ callback_mode() | [callback_mode() | state_enter()].
-type callback_mode() :: 'state_functions' | 'handle_event_function'.
+-type state_enter() :: 'state_enter'.
-type transition_option() ::
- postpone() | hibernate() | event_timeout().
+ postpone() | hibernate() |
+ event_timeout() | state_timeout().
-type postpone() ::
%% If 'true' postpone the current event
%% and retry it when the state changes (=/=)
@@ -89,6 +100,10 @@
%% Generate a ('timeout', EventContent, ...) event after Time
%% unless some other event is delivered
Time :: timeout().
+-type state_timeout() ::
+ %% Generate a ('state_timeout', EventContent, ...) event after Time
+ %% unless the state is changed
+ Time :: timeout().
-type action() ::
%% During a state change:
@@ -96,7 +111,7 @@
%% * All action()s are executed in order of apperance.
%% * Postponing the current event is performed
%% iff 'postpone' is 'true'.
- %% * A state timer is started iff 'timeout' is set.
+ %% * A state timeout is started iff 'timeout' is set.
%% * Pending events are handled or if there are
%% no pending events the server goes into receive
%% or hibernate (iff 'hibernate' is 'true')
@@ -108,100 +123,137 @@
'postpone' | % Set the postpone option
{'postpone', Postpone :: postpone()} |
%%
+ %% All 'next_event' events are kept in a list and then
+ %% inserted at state changes so the first in the
+ %% action() list is the first to be delivered.
+ {'next_event', % Insert event as the next to handle
+ EventType :: event_type(),
+ EventContent :: term()} |
+ enter_action().
+-type enter_action() ::
'hibernate' | % Set the hibernate option
{'hibernate', Hibernate :: hibernate()} |
%%
(Timeout :: event_timeout()) | % {timeout,Timeout}
- {'timeout', % Set the event timeout option
+ {'timeout', % Set the event_timeout option
Time :: event_timeout(), EventContent :: term()} |
+ {'state_timeout', % Set the state_timeout option
+ Time :: state_timeout(), EventContent :: term()} |
%%
- reply_action() |
- %%
- %% All 'next_event' events are kept in a list and then
- %% inserted at state changes so the first in the
- %% action() list is the first to be delivered.
- {'next_event', % Insert event as the next to handle
- EventType :: event_type(),
- EventContent :: term()}.
+ reply_action().
-type reply_action() ::
{'reply', % Reply to a caller
From :: from(), Reply :: term()}.
+-type init_result(StateType) ::
+ {ok, State :: StateType, Data :: data()} |
+ {ok, State :: StateType, Data :: data(),
+ Actions :: [action()] | action()} |
+ 'ignore' |
+ {'stop', Reason :: term()}.
+
+%% Old, not advertised
-type state_function_result() ::
- {'next_state', % {next_state,NextStateName,NewData,[]}
- NextStateName :: state_name(),
+ event_handler_result(state_name()).
+-type handle_event_result() ::
+ event_handler_result(state()).
+%%
+-type state_enter_result(State) ::
+ {'next_state', % {next_state,NextState,NewData,[]}
+ State,
NewData :: data()} |
{'next_state', % State transition, maybe to the same state
- NextStateName :: state_name(),
+ State,
NewData :: data(),
- Actions :: [action()] | action()} |
- common_state_callback_result().
--type handle_event_result() ::
+ Actions :: [enter_action()] | enter_action()} |
+ state_callback_result(enter_action()).
+-type event_handler_result(StateType) ::
{'next_state', % {next_state,NextState,NewData,[]}
- NextState :: state(),
+ NextState :: StateType,
NewData :: data()} |
{'next_state', % State transition, maybe to the same state
- NextState :: state(),
+ NextState :: StateType,
NewData :: data(),
Actions :: [action()] | action()} |
- common_state_callback_result().
--type common_state_callback_result() ::
+ state_callback_result(action()).
+-type state_callback_result(ActionType) ::
+ {'keep_state', % {keep_state,NewData,[]}
+ NewData :: data()} |
+ {'keep_state', % Keep state, change data
+ NewData :: data(),
+ Actions :: [ActionType] | ActionType} |
+ 'keep_state_and_data' | % {keep_state_and_data,[]}
+ {'keep_state_and_data', % Keep state and data -> only actions
+ Actions :: [ActionType] | ActionType} |
+ %%
+ {'repeat_state', % {repeat_state,NewData,[]}
+ NewData :: data()} |
+ {'repeat_state', % Repeat state, change data
+ NewData :: data(),
+ Actions :: [ActionType] | ActionType} |
+ 'repeat_state_and_data' | % {repeat_state_and_data,[]}
+ {'repeat_state_and_data', % Repeat state and data -> only actions
+ Actions :: [ActionType] | ActionType} |
+ %%
'stop' | % {stop,normal}
{'stop', % Stop the server
Reason :: term()} |
{'stop', % Stop the server
Reason :: term(),
NewData :: data()} |
+ %%
{'stop_and_reply', % Reply then stop the server
Reason :: term(),
Replies :: [reply_action()] | reply_action()} |
{'stop_and_reply', % Reply then stop the server
Reason :: term(),
Replies :: [reply_action()] | reply_action(),
- NewData :: data()} |
- {'keep_state', % {keep_state,NewData,[]}
- NewData :: data()} |
- {'keep_state', % Keep state, change data
- NewData :: data(),
- Actions :: [action()] | action()} |
- 'keep_state_and_data' | % {keep_state_and_data,[]}
- {'keep_state_and_data', % Keep state and data -> only actions
- Actions :: [action()] | action()}.
+ NewData :: data()}.
%% The state machine init function. It is called only once and
%% the server is not running until this function has returned
%% an {ok, ...} tuple. Thereafter the state callbacks are called
%% for all events to this server.
--callback init(Args :: term()) ->
- {callback_mode(), state(), data()} |
- {callback_mode(), state(), data(), [action()] | action()} |
- 'ignore' |
- {'stop', Reason :: term()}.
+-callback init(Args :: term()) -> init_result(state()).
+
+%% This callback shall return the callback mode of the callback module.
+%%
+%% It is called once after init/0 and code_change/4 but before
+%% the first state callback StateName/3 or handle_event/4.
+-callback callback_mode() -> callback_mode_result().
-%% Example state callback for callback_mode() =:= state_functions
-%% state name 'state_name'.
+%% Example state callback for StateName = 'state_name'
+%% when callback_mode() =:= state_functions.
%%
-%% In this mode all states has to be type state_name() i.e atom().
+%% In this mode all states has to be of type state_name() i.e atom().
%%
-%% Note that state callbacks and only state callbacks have arity 5
-%% and that is intended.
+%% Note that the only callbacks that have arity 3 are these
+%% StateName/3 callbacks and terminate/3, so the state name
+%% 'terminate' is unusable in this mode.
-callback state_name(
- event_type(),
+ 'enter',
+ OldStateName :: state_name(),
+ Data :: data()) ->
+ state_enter_result('state_name');
+ (event_type(),
EventContent :: term(),
Data :: data()) ->
- state_function_result().
+ event_handler_result(state_name()).
%%
-%% State callback for callback_mode() =:= handle_event_function.
-%%
-%% Note that state callbacks and only state callbacks have arity 5
-%% and that is intended.
+%% State callback for all states
+%% when callback_mode() =:= handle_event_function.
-callback handle_event(
- event_type(),
+ 'enter',
+ OldState :: state(),
+ State, % Current state
+ Data :: data()) ->
+ state_enter_result(State);
+ (event_type(),
EventContent :: term(),
State :: state(), % Current state
Data :: data()) ->
- handle_event_result().
+ event_handler_result(state()).
%% Clean up before the server terminates.
-callback terminate(
@@ -219,9 +271,8 @@
OldState :: state(),
OldData :: data(),
Extra :: term()) ->
- {NewCallbackMode :: callback_mode(),
- NewState :: state(),
- NewData :: data()}.
+ {ok, NewState :: state(), NewData :: data()} |
+ (Reason :: term()).
%% Format the callback module state in some sensible that is
%% often condensed way. For StatusOption =:= 'normal' the perferred
@@ -238,11 +289,16 @@
-optional_callbacks(
[init/1, % One may use enter_loop/5,6,7 instead
format_status/2, % Has got a default implementation
+ terminate/3, % Has got a default implementation
+ code_change/4, % Only needed by advanced soft upgrade
%%
- state_name/3, % Example for callback_mode =:= state_functions:
- %% there has to be a StateName/5 callback function for every StateName.
+ state_name/3, % Example for callback_mode() =:= state_functions:
+ %% there has to be a StateName/3 callback function
+ %% for every StateName in your state machine but the state name
+ %% 'state_name' does of course not have to be used.
%%
- handle_event/4]). % For callback_mode =:= handle_event_function
+ handle_event/4 % For callback_mode() =:= handle_event_function
+ ]).
%% Type validation functions
callback_mode(CallbackMode) ->
@@ -264,12 +320,16 @@ event_type({call,From}) ->
from(From);
event_type(Type) ->
case Type of
+ {call,From} ->
+ from(From);
cast ->
true;
info ->
true;
timeout ->
true;
+ state_timeout ->
+ true;
internal ->
true;
_ ->
@@ -378,53 +438,79 @@ call(ServerRef, Request) ->
-spec call(
ServerRef :: server_ref(),
Request :: term(),
- Timeout :: timeout()) ->
+ Timeout ::
+ timeout() |
+ {'clean_timeout',T :: timeout()} |
+ {'dirty_timeout',T :: timeout()}) ->
Reply :: term().
-call(ServerRef, Request, infinity) ->
- try gen:call(ServerRef, '$gen_call', Request, infinity) of
- {ok,Reply} ->
- Reply
- catch
- Class:Reason ->
- erlang:raise(
- Class,
- {Reason,{?MODULE,call,[ServerRef,Request,infinity]}},
- erlang:get_stacktrace())
- end;
call(ServerRef, Request, Timeout) ->
- %% Call server through proxy process to dodge any late reply
- Ref = make_ref(),
- Self = self(),
- Pid = spawn(
- fun () ->
- Self !
- try gen:call(
- ServerRef, '$gen_call', Request, Timeout) of
- Result ->
- {Ref,Result}
- catch Class:Reason ->
- {Ref,Class,Reason,erlang:get_stacktrace()}
- end
- end),
- Mref = monitor(process, Pid),
- receive
- {Ref,Result} ->
- demonitor(Mref, [flush]),
- case Result of
+ case parse_timeout(Timeout) of
+ {dirty_timeout,T} ->
+ try gen:call(ServerRef, '$gen_call', Request, T) of
{ok,Reply} ->
Reply
+ catch
+ Class:Reason ->
+ erlang:raise(
+ Class,
+ {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
+ erlang:get_stacktrace())
+ end;
+ {clean_timeout,T} ->
+ %% Call server through proxy process to dodge any late reply
+ Ref = make_ref(),
+ Self = self(),
+ Pid = spawn(
+ fun () ->
+ Self !
+ try gen:call(
+ ServerRef, '$gen_call', Request, T) of
+ Result ->
+ {Ref,Result}
+ catch Class:Reason ->
+ {Ref,Class,Reason,
+ erlang:get_stacktrace()}
+ end
+ end),
+ Mref = monitor(process, Pid),
+ receive
+ {Ref,Result} ->
+ demonitor(Mref, [flush]),
+ case Result of
+ {ok,Reply} ->
+ Reply
+ end;
+ {Ref,Class,Reason,Stacktrace} ->
+ demonitor(Mref, [flush]),
+ erlang:raise(
+ Class,
+ {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
+ Stacktrace);
+ {'DOWN',Mref,_,_,Reason} ->
+ %% There is a theoretical possibility that the
+ %% proxy process gets killed between try--of and !
+ %% so this clause is in case of that
+ exit(Reason)
end;
- {Ref,Class,Reason,Stacktrace} ->
- demonitor(Mref, [flush]),
- erlang:raise(
- Class,
- {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
- Stacktrace);
- {'DOWN',Mref,_,_,Reason} ->
- %% There is a theoretical possibility that the
- %% proxy process gets killed between try--of and !
- %% so this clause is in case of that
- exit(Reason)
+ Error when is_atom(Error) ->
+ erlang:error(Error, [ServerRef,Request,Timeout])
+ end.
+
+parse_timeout(Timeout) ->
+ case Timeout of
+ {clean_timeout,infinity} ->
+ {dirty_timeout,infinity};
+ {clean_timeout,_} ->
+ Timeout;
+ {dirty_timeout,_} ->
+ Timeout;
+ {_,_} ->
+ %% Be nice and throw a badarg for speling errors
+ badarg;
+ infinity ->
+ {dirty_timeout,infinity};
+ T ->
+ {clean_timeout,T}
end.
%% Reply from a state machine callback to whom awaits in call/2
@@ -450,43 +536,35 @@ reply({To,Tag}, Reply) when is_pid(To) ->
%% the same arguments as you would have returned from init/1
-spec enter_loop(
Module :: module(), Opts :: [debug_opt()],
- CallbackMode :: callback_mode(),
State :: state(), Data :: data()) ->
no_return().
-enter_loop(Module, Opts, CallbackMode, State, Data) ->
- enter_loop(Module, Opts, CallbackMode, State, Data, self()).
+enter_loop(Module, Opts, State, Data) ->
+ enter_loop(Module, Opts, State, Data, self()).
%%
-spec enter_loop(
Module :: module(), Opts :: [debug_opt()],
- CallbackMode :: callback_mode(),
State :: state(), Data :: data(),
Server_or_Actions ::
server_name() | pid() | [action()]) ->
no_return().
-enter_loop(Module, Opts, CallbackMode, State, Data, Server_or_Actions) ->
+enter_loop(Module, Opts, State, Data, Server_or_Actions) ->
if
is_list(Server_or_Actions) ->
- enter_loop(
- Module, Opts, CallbackMode, State, Data,
- self(), Server_or_Actions);
+ enter_loop(Module, Opts, State, Data, self(), Server_or_Actions);
true ->
- enter_loop(
- Module, Opts, CallbackMode, State, Data,
- Server_or_Actions, [])
+ enter_loop(Module, Opts, State, Data, Server_or_Actions, [])
end.
%%
-spec enter_loop(
Module :: module(), Opts :: [debug_opt()],
- CallbackMode :: callback_mode(),
State :: state(), Data :: data(),
Server :: server_name() | pid(),
Actions :: [action()] | action()) ->
no_return().
-enter_loop(Module, Opts, CallbackMode, State, Data, Server, Actions) ->
+enter_loop(Module, Opts, State, Data, Server, Actions) ->
is_atom(Module) orelse error({atom,Module}),
- callback_mode(CallbackMode) orelse error({callback_mode,CallbackMode}),
Parent = gen:get_parent(),
- enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent).
+ enter(Module, Opts, State, Data, Server, Actions, Parent).
%%---------------------------------------------------------------------------
%% API helpers
@@ -514,12 +592,13 @@ send(Proc, Msg) ->
end.
%% Here the init_it/6 and enter_loop/5,6,7 functions converge
-enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) ->
+enter(Module, Opts, State, Data, Server, Actions, Parent) ->
%% The values should already have been type checked
Name = gen:get_proc_name(Server),
Debug = gen:debug_options(Name, Opts),
- P = Events = [],
- Event = {internal,initial_state},
+ Events = [],
+ P = [],
+ Event = {internal,init_state},
%% We enforce {postpone,false} to ensure that
%% our fake Event gets discarded, thought it might get logged
NewActions =
@@ -529,21 +608,50 @@ enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) ->
true ->
[Actions,{postpone,false}]
end,
+ TimerRefs = #{},
+ %% Key: timer ref
+ %% Value: the timer type i.e the timer's event type
+ %%
+ TimerTypes = #{},
+ %% Key: timer type i.e the timer's event type
+ %% Value: timer ref
+ %%
+ %% We add a timer to both timer_refs and timer_types
+ %% when we start it. When we request an asynchronous
+ %% timer cancel we remove it from timer_types. When
+ %% the timer cancel message arrives we remove it from
+ %% timer_refs.
+ %%
+ Hibernate = false,
+ CancelTimers = 0,
S = #{
- callback_mode => CallbackMode,
+ callback_mode => undefined,
+ state_enter => false,
module => Module,
name => Name,
- %% All fields below will be replaced according to the arguments to
- %% loop_event_actions/10 when it finally loops back to loop/3
state => State,
data => Data,
postponed => P,
- hibernate => false,
- timer => undefined},
+ %%
+ %% The following fields are finally set from to the arguments to
+ %% loop_event_actions/9 when it finally loops back to loop/3
+ %% in loop_event_result/11
+ timer_refs => TimerRefs,
+ timer_types => TimerTypes,
+ hibernate => Hibernate,
+ cancel_timers => CancelTimers
+ },
NewDebug = sys_debug(Debug, S, State, {enter,Event,State}),
- loop_event_actions(
- Parent, NewDebug, S, Events,
- State, Data, P, Event, State, NewActions).
+ case call_callback_mode(S) of
+ {ok,NewS} ->
+ loop_event_actions(
+ Parent, NewDebug, NewS,
+ Events, Event, State, Data, NewActions, true);
+ {Class,Reason,Stacktrace} ->
+ terminate(
+ Class, Reason, Stacktrace, NewDebug,
+ S, [Event|Events])
+ end.
%%%==========================================================================
%%% gen callbacks
@@ -558,9 +666,17 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
Result ->
init_result(Starter, Parent, ServerRef, Module, Result, Opts);
Class:Reason ->
+ Stacktrace = erlang:get_stacktrace(),
+ Name = gen:get_proc_name(ServerRef),
gen:unregister_name(ServerRef),
proc_lib:init_ack(Starter, {error,Reason}),
- erlang:raise(Class, Reason, erlang:get_stacktrace())
+ error_info(
+ Class, Reason, Stacktrace,
+ #{name => Name,
+ callback_mode => undefined,
+ state_enter => false},
+ [], [], undefined),
+ erlang:raise(Class, Reason, Stacktrace)
end.
%%---------------------------------------------------------------------------
@@ -568,30 +684,12 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
case Result of
- {CallbackMode,State,Data} ->
- case callback_mode(CallbackMode) of
- true ->
- proc_lib:init_ack(Starter, {ok,self()}),
- enter(
- Module, Opts, CallbackMode, State, Data,
- ServerRef, [], Parent);
- false ->
- Error = {callback_mode,CallbackMode},
- proc_lib:init_ack(Starter, {error,Error}),
- exit(Error)
- end;
- {CallbackMode,State,Data,Actions} ->
- case callback_mode(CallbackMode) of
- true ->
- proc_lib:init_ack(Starter, {ok,self()}),
- enter(
- Module, Opts, CallbackMode, State, Data,
- ServerRef, Actions, Parent);
- false ->
- Error = {callback_mode,CallbackMode},
- proc_lib:init_ack(Starter, {error,Error}),
- exit(Error)
- end;
+ {ok,State,Data} ->
+ proc_lib:init_ack(Starter, {ok,self()}),
+ enter(Module, Opts, State, Data, ServerRef, [], Parent);
+ {ok,State,Data,Actions} ->
+ proc_lib:init_ack(Starter, {ok,self()}),
+ enter(Module, Opts, State, Data, ServerRef, Actions, Parent);
{stop,Reason} ->
gen:unregister_name(ServerRef),
proc_lib:init_ack(Starter, {error,Reason}),
@@ -601,8 +699,16 @@ init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
proc_lib:init_ack(Starter, ignore),
exit(normal);
_ ->
- Error = {bad_return_value,Result},
+ Name = gen:get_proc_name(ServerRef),
+ gen:unregister_name(ServerRef),
+ Error = {bad_return_from_init,Result},
proc_lib:init_ack(Starter, {error,Error}),
+ error_info(
+ error, Error, ?STACKTRACE(),
+ #{name => Name,
+ callback_mode => undefined,
+ state_enter => false},
+ [], [], undefined),
exit(Error)
end.
@@ -612,12 +718,8 @@ init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
system_continue(Parent, Debug, S) ->
loop(Parent, Debug, S).
-system_terminate(
- Reason, _Parent, Debug,
- #{state := State, data := Data, postponed := P} = S) ->
- terminate(
- exit, Reason, ?STACKTRACE(),
- Debug, S, [], State, Data, P).
+system_terminate(Reason, _Parent, Debug, S) ->
+ terminate(exit, Reason, ?STACKTRACE(), Debug, S, []).
system_code_change(
#{module := Module,
@@ -630,11 +732,9 @@ system_code_change(
Result -> Result
end
of
- {NewCallbackMode,NewState,NewData} ->
- callback_mode(NewCallbackMode) orelse
- error({callback_mode,NewCallbackMode}),
+ {ok,NewState,NewData} ->
{ok,
- S#{callback_mode := NewCallbackMode,
+ S#{callback_mode := undefined,
state := NewState,
data := NewData}};
{ok,_} = Error ->
@@ -656,7 +756,7 @@ system_replace_state(
format_status(
Opt,
[PDict,SysState,Parent,Debug,
- #{name := Name, postponed := P, state := State, data := Data} = S]) ->
+ #{name := Name, postponed := P} = S]) ->
Header = gen:format_status_header("Status for state machine", Name),
Log = sys:get_debug(log, Debug, []),
[{header,Header},
@@ -665,7 +765,7 @@ format_status(
{"Parent",Parent},
{"Logged Events",Log},
{"Postponed",P}]} |
- case format_status(Opt, PDict, S, State, Data) of
+ case format_status(Opt, PDict, S) of
L when is_list(L) -> L;
T -> [T]
end].
@@ -675,14 +775,18 @@ format_status(
%% them, not as the real erlang messages. Use trace for that.
%%---------------------------------------------------------------------------
-print_event(Dev, {in,Event}, {Name,_}) ->
+print_event(Dev, {in,Event}, {Name,State}) ->
+ io:format(
+ Dev, "*DBG* ~p receive ~s in state ~p~n",
+ [Name,event_string(Event),State]);
+print_event(Dev, {out,Reply,{To,_Tag}}, {Name,State}) ->
io:format(
- Dev, "*DBG* ~p received ~s~n",
- [Name,event_string(Event)]);
-print_event(Dev, {out,Reply,{To,_Tag}}, {Name,_}) ->
+ Dev, "*DBG* ~p send ~p to ~p from state ~p~n",
+ [Name,Reply,To,State]);
+print_event(Dev, {terminate,Reason}, {Name,State}) ->
io:format(
- Dev, "*DBG* ~p sent ~p to ~p~n",
- [Name,Reply,To]);
+ Dev, "*DBG* ~p terminate ~p in state ~p~n",
+ [Name,Reason,State]);
print_event(Dev, {Tag,Event,NextState}, {Name,State}) ->
StateString =
case NextState of
@@ -726,22 +830,22 @@ wakeup_from_hibernate(Parent, Debug, S) ->
%% and detours through sys:handle_system_message/7 and proc_lib:hibernate/3
%% Entry point for system_continue/3
-loop(Parent, Debug, #{hibernate := Hibernate} = S) ->
- case Hibernate of
- true ->
- %% Does not return but restarts process at
- %% wakeup_from_hibernate/3 that jumps to loop_receive/3
- proc_lib:hibernate(
- ?MODULE, wakeup_from_hibernate, [Parent,Debug,S]),
- error(
- {should_not_have_arrived_here_but_instead_in,
- {wakeup_from_hibernate,3}});
- false ->
- loop_receive(Parent, Debug, S)
- end.
+loop(Parent, Debug, #{hibernate := true, cancel_timers := 0} = S) ->
+ loop_hibernate(Parent, Debug, S);
+loop(Parent, Debug, S) ->
+ loop_receive(Parent, Debug, S).
+
+loop_hibernate(Parent, Debug, S) ->
+ %% Does not return but restarts process at
+ %% wakeup_from_hibernate/3 that jumps to loop_receive/3
+ proc_lib:hibernate(
+ ?MODULE, wakeup_from_hibernate, [Parent,Debug,S]),
+ error(
+ {should_not_have_arrived_here_but_instead_in,
+ {wakeup_from_hibernate,3}}).
%% Entry point for wakeup_from_hibernate/3
-loop_receive(Parent, Debug, #{timer := Timer} = S) ->
+loop_receive(Parent, Debug, S) ->
receive
Msg ->
case Msg of
@@ -750,36 +854,87 @@ loop_receive(Parent, Debug, #{timer := Timer} = S) ->
%% Does not return but tail recursively calls
%% system_continue/3 that jumps to loop/3
sys:handle_system_msg(
- Req, Pid, Parent, ?MODULE, Debug, S, Hibernate);
+ Req, Pid, Parent, ?MODULE, Debug, S,
+ Hibernate);
{'EXIT',Parent,Reason} = EXIT ->
- #{state := State, data := Data, postponed := P} = S,
- %% EXIT is not a 2-tuple and therefore
- %% not an event and has no event_type(),
- %% but this will stand out in the crash report...
- terminate(
- exit, Reason, ?STACKTRACE(),
- Debug, S, [EXIT], State, Data, P);
- {timeout,Timer,Content} when Timer =/= undefined ->
- loop_receive_result(
- Parent, Debug, S, {timeout,Content});
- _ ->
- %% Cancel Timer if running
- case Timer of
- undefined ->
- ok;
+ %% EXIT is not a 2-tuple therefore
+ %% not an event but this will stand out
+ %% in the crash report...
+ Q = [EXIT],
+ terminate(exit, Reason, ?STACKTRACE(), Debug, S, Q);
+ {timeout,TimerRef,TimerMsg} ->
+ #{timer_refs := TimerRefs,
+ timer_types := TimerTypes,
+ hibernate := Hibernate} = S,
+ case TimerRefs of
+ #{TimerRef := TimerType} ->
+ %% We know of this timer; is it a running
+ %% timer or a timer being cancelled that
+ %% managed to send a late timeout message?
+ case TimerTypes of
+ #{TimerType := TimerRef} ->
+ %% The timer type maps back to this
+ %% timer ref, so it was a running timer
+ Event = {TimerType,TimerMsg},
+ %% Unregister the triggered timeout
+ NewTimerRefs =
+ maps:remove(TimerRef, TimerRefs),
+ NewTimerTypes =
+ maps:remove(TimerType, TimerTypes),
+ loop_receive_result(
+ Parent, Debug,
+ S#{
+ timer_refs := NewTimerRefs,
+ timer_types := NewTimerTypes},
+ Hibernate,
+ Event);
+ _ ->
+ %% This was a late timeout message
+ %% from timer being cancelled, so
+ %% ignore it and expect a cancel_timer
+ %% msg shortly
+ loop_receive(Parent, Debug, S)
+ end;
+ _ ->
+ %% Not our timer; present it as an event
+ Event = {info,Msg},
+ loop_receive_result(
+ Parent, Debug, S, Hibernate, Event)
+ end;
+ {cancel_timer,TimerRef,_} ->
+ #{timer_refs := TimerRefs,
+ cancel_timers := CancelTimers,
+ hibernate := Hibernate} = S,
+ case TimerRefs of
+ #{TimerRef := _} ->
+ %% We must have requested a cancel
+ %% of this timer so it is already
+ %% removed from TimerTypes
+ NewTimerRefs =
+ maps:remove(TimerRef, TimerRefs),
+ NewCancelTimers = CancelTimers - 1,
+ NewS =
+ S#{
+ timer_refs := NewTimerRefs,
+ cancel_timers := NewCancelTimers},
+ if
+ Hibernate =:= true, NewCancelTimers =:= 0 ->
+ %% No more cancel_timer msgs to expect;
+ %% we can hibernate
+ loop_hibernate(Parent, Debug, NewS);
+ NewCancelTimers >= 0 -> % Assert
+ loop_receive(Parent, Debug, NewS)
+ end;
_ ->
- case erlang:cancel_timer(Timer) of
- TimeLeft when is_integer(TimeLeft) ->
- ok;
- false ->
- receive
- {timeout,Timer,_} ->
- ok
- after 0 ->
- ok
- end
- end
- end,
+ %% Not our cancel_timer msg;
+ %% present it as an event
+ Event = {info,Msg},
+ loop_receive_result(
+ Parent, Debug, S, Hibernate, Event)
+ end;
+ _ ->
+ %% External msg
+ #{hibernate := Hibernate} = S,
Event =
case Msg of
{'$gen_call',From,Request} ->
@@ -789,388 +944,613 @@ loop_receive(Parent, Debug, #{timer := Timer} = S) ->
_ ->
{info,Msg}
end,
- loop_receive_result(Parent, Debug, S, Event)
+ loop_receive_result(
+ Parent, Debug, S, Hibernate, Event)
end
end.
loop_receive_result(
Parent, Debug,
#{state := State,
- data := Data,
- postponed := P} = S,
- Event) ->
- %% The engine state map S is now dismantled
- %% and will not be restored until we return to loop/3.
- %%
- %% The fields 'callback_mode', 'module', and 'name' are still valid.
- %% The fields 'state', 'data', and 'postponed' are held in arguments.
- %% The fields 'timer' and 'hibernate' will be recalculated.
- %%
+ timer_types := TimerTypes, cancel_timers := CancelTimers} = S,
+ Hibernate, Event) ->
+ %% From now the 'hibernate' field in S is invalid
+ %% and will be restored when looping back
+ %% in loop_event_result/11
NewDebug = sys_debug(Debug, S, State, {in,Event}),
- %% Here the queue of not yet handled events is created
+ %% Here is the queue of not yet handled events created
Events = [],
- Hibernate = false,
- loop_event(
- Parent, NewDebug, S, Events, State, Data, P, Event, Hibernate).
-
-%% Process the event queue, or if it is empty
-%% loop back to loop/3 to receive a new event
-loop_events(
- Parent, Debug, S, [Event|Events],
- State, Data, P, Hibernate, _Timeout) ->
- %%
- %% If there was a state timer requested we just ignore that
- %% since we have events to handle which cancels the timer
- loop_event(
- Parent, Debug, S, Events, State, Data, P, Event, Hibernate);
-loop_events(
- Parent, Debug, S, [],
- State, Data, P, Hibernate, Timeout) ->
- case Timeout of
- {timeout,0,EventContent} ->
- %% Immediate timeout - simulate it
- %% so we do not get the timeout message
- %% after any received event
- loop_event(
- Parent, Debug, S, [],
- State, Data, P, {timeout,EventContent}, Hibernate);
- {timeout,Time,EventContent} ->
- %% Actually start a timer
- Timer = erlang:start_timer(Time, self(), EventContent),
- loop_events_done(
- Parent, Debug, S, Timer, State, Data, P, Hibernate);
- undefined ->
- %% No state timeout has been requested
- Timer = undefined,
- loop_events_done(
- Parent, Debug, S, Timer, State, Data, P, Hibernate)
+ %% Cancel any running event timer
+ case
+ cancel_timer_by_type(timeout, TimerTypes, CancelTimers)
+ of
+ {_,CancelTimers} ->
+ %% No timer cancelled
+ loop_event(Parent, NewDebug, S, Events, Event, Hibernate);
+ {NewTimerTypes,NewCancelTimers} ->
+ %% The timer is removed from NewTimerTypes but
+ %% remains in TimerRefs until we get
+ %% the cancel_timer msg
+ NewS =
+ S#{
+ timer_types := NewTimerTypes,
+ cancel_timers := NewCancelTimers},
+ loop_event(Parent, NewDebug, NewS, Events, Event, Hibernate)
end.
-%%
-loop_events_done(Parent, Debug, S, Timer, State, Data, P, Hibernate) ->
- NewS =
- S#{
- state := State,
- data := Data,
- postponed := P,
- hibernate := Hibernate,
- timer := Timer},
- loop(Parent, Debug, NewS).
+%% Entry point for handling an event, received or enqueued
loop_event(
Parent, Debug,
- #{callback_mode := CallbackMode,
- module := Module} = S,
- Events,
- State, Data, P, {Type,Content} = Event, Hibernate) ->
+ #{state := State, data := Data} = S,
+ Events, {Type,Content} = Event, Hibernate) ->
%%
- %% If Hibernate is true here it can only be
+ %% If (this old) Hibernate is true here it can only be
%% because it was set from an event action
- %% and we did not go into hibernation since there
- %% were events in queue, so we do what the user
- %% might depend on i.e collect garbage which
+ %% and we did not go into hibernation since there were
+ %% events in queue, so we do what the user
+ %% might rely on i.e collect garbage which
%% would have happened if we actually hibernated
%% and immediately was awakened
Hibernate andalso garbage_collect(),
+ case call_state_function(S, Type, Content, State, Data) of
+ {ok,Result,NewS} ->
+ {NextState,NewData,Actions,EnterCall} =
+ parse_event_result(
+ true, Debug, NewS,
+ Events, Event, State, Data, Result),
+ loop_event_actions(
+ Parent, Debug, NewS,
+ Events, Event, NextState, NewData, Actions, EnterCall);
+ {Class,Reason,Stacktrace} ->
+ terminate(
+ Class, Reason, Stacktrace, Debug, S,
+ [Event|Events])
+ end.
+
+loop_event_actions(
+ Parent, Debug,
+ #{state := State, state_enter := StateEnter} = S,
+ Events, Event, NextState, NewData,
+ Actions, EnterCall) ->
+ %% Hibernate is reborn here as false being
+ %% the default value from parse_actions/4
+ case parse_actions(Debug, S, State, Actions) of
+ {ok,NewDebug,Hibernate,TimeoutsR,Postpone,NextEventsR} ->
+ if
+ StateEnter, EnterCall ->
+ loop_event_enter(
+ Parent, NewDebug, S,
+ Events, Event, NextState, NewData,
+ Hibernate, TimeoutsR, Postpone, NextEventsR);
+ true ->
+ loop_event_result(
+ Parent, NewDebug, S,
+ Events, Event, NextState, NewData,
+ Hibernate, TimeoutsR, Postpone, NextEventsR)
+ end;
+ {Class,Reason,Stacktrace} ->
+ terminate(
+ Class, Reason, Stacktrace, Debug, S,
+ [Event|Events])
+ end.
+
+loop_event_enter(
+ Parent, Debug, #{state := State} = S,
+ Events, Event, NextState, NewData,
+ Hibernate, TimeoutsR, Postpone, NextEventsR) ->
+ case call_state_function(S, enter, State, NextState, NewData) of
+ {ok,Result,NewS} ->
+ case parse_event_result(
+ false, Debug, NewS,
+ Events, Event, NextState, NewData, Result) of
+ {_,NewerData,Actions,EnterCall} ->
+ loop_event_enter_actions(
+ Parent, Debug, NewS,
+ Events, Event, NextState, NewerData,
+ Hibernate, TimeoutsR, Postpone, NextEventsR,
+ Actions, EnterCall)
+ end;
+ {Class,Reason,Stacktrace} ->
+ terminate(
+ Class, Reason, Stacktrace, Debug,
+ S#{
+ state := NextState,
+ data := NewData,
+ hibernate := Hibernate},
+ [Event|Events])
+ end.
+
+loop_event_enter_actions(
+ Parent, Debug, #{state_enter := StateEnter} = S,
+ Events, Event, NextState, NewData,
+ Hibernate, TimeoutsR, Postpone, NextEventsR,
+ Actions, EnterCall) ->
+ case
+ parse_enter_actions(
+ Debug, S, NextState, Actions, Hibernate, TimeoutsR)
+ of
+ {ok,NewDebug,NewHibernate,NewTimeoutsR,_,_} ->
+ if
+ StateEnter, EnterCall ->
+ loop_event_enter(
+ Parent, NewDebug, S,
+ Events, Event, NextState, NewData,
+ NewHibernate, NewTimeoutsR, Postpone, NextEventsR);
+ true ->
+ loop_event_result(
+ Parent, NewDebug, S,
+ Events, Event, NextState, NewData,
+ NewHibernate, NewTimeoutsR, Postpone, NextEventsR)
+ end;
+ {Class,Reason,Stacktrace} ->
+ terminate(
+ Class, Reason, Stacktrace, Debug,
+ S#{
+ state := NextState,
+ data := NewData,
+ hibernate := Hibernate},
+ [Event|Events])
+ end.
+
+loop_event_result(
+ Parent, Debug_0,
+ #{state := State, postponed := P_0,
+ timer_refs := TimerRefs_0, timer_types := TimerTypes_0,
+ cancel_timers := CancelTimers_0} = S_0,
+ Events_0, Event_0, NextState, NewData,
+ Hibernate, TimeoutsR, Postpone, NextEventsR) ->
%%
+ %% All options have been collected and next_events are buffered.
+ %% Do the actual state transition.
+ %%
+ {Debug_1,P_1} = % Move current event to postponed if Postpone
+ case Postpone of
+ true ->
+ {sys_debug(Debug_0, S_0, State, {postpone,Event_0,State}),
+ [Event_0|P_0]};
+ false ->
+ {sys_debug(Debug_0, S_0, State, {consume,Event_0,State}),
+ P_0}
+ end,
+ {Events_1,P_2,{TimerTypes_1,CancelTimers_1}} =
+ %% Move all postponed events to queue and cancel the
+ %% state timeout if the state changes
+ if
+ NextState =:= State ->
+ {Events_0,P_1,{TimerTypes_0,CancelTimers_0}};
+ true ->
+ {lists:reverse(P_1, Events_0),
+ [],
+ cancel_timer_by_type(
+ state_timeout, TimerTypes_0, CancelTimers_0)}
+ %% The state timer is removed from TimerTypes_1
+ %% but remains in TimerRefs_0 until we get
+ %% the cancel_timer msg
+ end,
+ {TimerRefs_2,TimerTypes_2,CancelTimers_2,TimeoutEvents} =
+ %% Stop and start non-event timers
+ parse_timers(TimerRefs_0, TimerTypes_1, CancelTimers_1, TimeoutsR),
+ %% Place next events last in reversed queue
+ Events_2R = lists:reverse(Events_1, NextEventsR),
+ %% Enqueue immediate timeout events and start event timer
+ Events_3R = prepend_timeout_events(TimeoutEvents, Events_2R),
+ S_1 =
+ S_0#{
+ state := NextState,
+ data := NewData,
+ postponed := P_2,
+ timer_refs := TimerRefs_2,
+ timer_types := TimerTypes_2,
+ cancel_timers := CancelTimers_2,
+ hibernate := Hibernate},
+ case lists:reverse(Events_3R) of
+ [] ->
+ %% Get a new event
+ loop(Parent, Debug_1, S_1);
+ [Event|Events] ->
+ %% Loop until out of enqueued events
+ loop_event(Parent, Debug_1, S_1, Events, Event, Hibernate)
+ end.
+
+
+%%---------------------------------------------------------------------------
+%% Server loop helpers
+
+call_callback_mode(#{module := Module} = S) ->
+ try Module:callback_mode() of
+ CallbackMode ->
+ callback_mode_result(S, CallbackMode)
+ catch
+ CallbackMode ->
+ callback_mode_result(S, CallbackMode);
+ Class:Reason ->
+ {Class,Reason,erlang:get_stacktrace()}
+ end.
+
+callback_mode_result(S, CallbackMode) ->
+ case
+ parse_callback_mode(
+ if
+ is_atom(CallbackMode) ->
+ [CallbackMode];
+ true ->
+ CallbackMode
+ end, undefined, false)
+ of
+ {undefined,_} ->
+ {error,
+ {bad_return_from_callback_mode,CallbackMode},
+ ?STACKTRACE()};
+ {CBMode,StateEnter} ->
+ {ok,
+ S#{
+ callback_mode := CBMode,
+ state_enter := StateEnter}}
+ end.
+
+parse_callback_mode([], CBMode, StateEnter) ->
+ {CBMode,StateEnter};
+parse_callback_mode([H|T], CBMode, StateEnter) ->
+ case callback_mode(H) of
+ true ->
+ parse_callback_mode(T, H, StateEnter);
+ false ->
+ case H of
+ state_enter ->
+ parse_callback_mode(T, CBMode, true);
+ _ ->
+ {undefined,StateEnter}
+ end
+ end;
+parse_callback_mode(_, _CBMode, StateEnter) ->
+ {undefined,StateEnter}.
+
+
+call_state_function(
+ #{callback_mode := undefined} = S, Type, Content, State, Data) ->
+ case call_callback_mode(S) of
+ {ok,NewS} ->
+ call_state_function(NewS, Type, Content, State, Data);
+ Error ->
+ Error
+ end;
+call_state_function(
+ #{callback_mode := CallbackMode, module := Module} = S,
+ Type, Content, State, Data) ->
try
case CallbackMode of
state_functions ->
Module:State(Type, Content, Data);
handle_event_function ->
Module:handle_event(Type, Content, State, Data)
- end of
+ end
+ of
Result ->
- loop_event_result(
- Parent, Debug, S, Events, State, Data, P, Event, Result)
+ {ok,Result,S}
catch
Result ->
- loop_event_result(
- Parent, Debug, S, Events, State, Data, P, Event, Result);
- error:badarg when CallbackMode =:= state_functions ->
- case erlang:get_stacktrace() of
- [{erlang,apply,[Module,State,_],_}|Stacktrace] ->
- Args = [Type,Content,Data],
- terminate(
- error,
- {undef_state_function,{Module,State,Args}},
- Stacktrace,
- Debug, S, [Event|Events], State, Data, P);
- Stacktrace ->
- terminate(
- error, badarg, Stacktrace,
- Debug, S, [Event|Events], State, Data, P)
- end;
- error:undef ->
- %% Process an undef to check for the simple mistake
- %% of calling a nonexistent state function
- case erlang:get_stacktrace() of
- [{Module,State,
- [Type,Content,Data]=Args,
- _}
- |Stacktrace]
- when CallbackMode =:= state_functions ->
- terminate(
- error,
- {undef_state_function,{Module,State,Args}},
- Stacktrace,
- Debug, S, [Event|Events], State, Data, P);
- [{Module,handle_event,
- [Type,Content,State,Data]=Args,
- _}
- |Stacktrace]
- when CallbackMode =:= handle_event_function ->
- terminate(
- error,
- {undef_state_function,{Module,handle_event,Args}},
- Stacktrace,
- Debug, S, [Event|Events], State, Data, P);
- Stacktrace ->
- terminate(
- error, undef, Stacktrace,
- Debug, S, [Event|Events], State, Data, P)
- end;
+ {ok,Result,S};
Class:Reason ->
- Stacktrace = erlang:get_stacktrace(),
- terminate(
- Class, Reason, Stacktrace,
- Debug, S, [Event|Events], State, Data, P)
+ {Class,Reason,erlang:get_stacktrace()}
end.
+
%% Interpret all callback return variants
-loop_event_result(
- Parent, Debug, S, Events, State, Data, P, Event, Result) ->
+parse_event_result(
+ AllowStateChange, Debug, S,
+ Events, Event, State, Data, Result) ->
case Result of
stop ->
terminate(
- exit, normal, ?STACKTRACE(),
- Debug, S, [Event|Events], State, Data, P);
+ exit, normal, ?STACKTRACE(), Debug,
+ S#{state := State, data := Data},
+ [Event|Events]);
{stop,Reason} ->
terminate(
- exit, Reason, ?STACKTRACE(),
- Debug, S, [Event|Events], State, Data, P);
+ exit, Reason, ?STACKTRACE(), Debug,
+ S#{state := State, data := Data},
+ [Event|Events]);
{stop,Reason,NewData} ->
terminate(
- exit, Reason, ?STACKTRACE(),
- Debug, S, [Event|Events], State, NewData, P);
+ exit, Reason, ?STACKTRACE(), Debug,
+ S#{state := State, data := NewData},
+ [Event|Events]);
+ %%
{stop_and_reply,Reason,Replies} ->
- Q = [Event|Events],
reply_then_terminate(
- exit, Reason, ?STACKTRACE(),
- Debug, S, Q, State, Data, P, Replies);
+ exit, Reason, ?STACKTRACE(), Debug,
+ S#{state := State, data := Data},
+ [Event|Events], Replies);
{stop_and_reply,Reason,Replies,NewData} ->
- Q = [Event|Events],
reply_then_terminate(
- exit, Reason, ?STACKTRACE(),
- Debug, S, Q, State, NewData, P, Replies);
- {next_state,NextState,NewData} ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, []);
- {next_state,NextState,NewData,Actions} ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, Actions);
+ exit, Reason, ?STACKTRACE(), Debug,
+ S#{state := State, data := NewData},
+ [Event|Events], Replies);
+ %%
+ {next_state,State,NewData} ->
+ {State,NewData,[],false};
+ {next_state,NextState,NewData} when AllowStateChange ->
+ {NextState,NewData,[],true};
+ {next_state,State,NewData,Actions} ->
+ {State,NewData,Actions,false};
+ {next_state,NextState,NewData,Actions} when AllowStateChange ->
+ {NextState,NewData,Actions,true};
+ %%
{keep_state,NewData} ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, State, []);
+ {State,NewData,[],false};
{keep_state,NewData,Actions} ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, State, Actions);
+ {State,NewData,Actions,false};
keep_state_and_data ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, Data, P, Event, State, []);
+ {State,Data,[],false};
{keep_state_and_data,Actions} ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, Data, P, Event, State, Actions);
+ {State,Data,Actions,false};
+ %%
+ {repeat_state,NewData} ->
+ {State,NewData,[],true};
+ {repeat_state,NewData,Actions} ->
+ {State,NewData,Actions,true};
+ repeat_state_and_data ->
+ {State,Data,[],true};
+ {repeat_state_and_data,Actions} ->
+ {State,Data,Actions,true};
+ %%
_ ->
terminate(
- error, {bad_return_value,Result}, ?STACKTRACE(),
- Debug, S, [Event|Events], State, Data, P)
+ error,
+ {bad_return_from_state_function,Result},
+ ?STACKTRACE(), Debug,
+ S#{state := State, data := Data},
+ [Event|Events])
end.
-loop_event_actions(
- Parent, Debug, S, Events, State, NewData, P, Event, NextState, Actions) ->
- Postpone = false, % Shall we postpone this event; boolean()
+
+parse_enter_actions(Debug, S, State, Actions, Hibernate, TimeoutsR) ->
+ Postpone = forbidden,
+ NextEventsR = forbidden,
+ parse_actions(
+ Debug, S, State, listify(Actions),
+ Hibernate, TimeoutsR, Postpone, NextEventsR).
+
+parse_actions(Debug, S, State, Actions) ->
Hibernate = false,
- Timeout = undefined,
- NextEvents = [],
- loop_event_actions(
- Parent, Debug, S, Events, State, NewData, P, Event, NextState,
- if
- is_list(Actions) ->
- Actions;
- true ->
- [Actions]
- end,
- Postpone, Hibernate, Timeout, NextEvents).
+ TimeoutsR = [{timeout,infinity,infinity}], %% Will cancel event timer
+ Postpone = false,
+ NextEventsR = [],
+ parse_actions(
+ Debug, S, State, listify(Actions),
+ Hibernate, TimeoutsR, Postpone, NextEventsR).
%%
-%% Process all actions
-loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, [Action|Actions],
- Postpone, Hibernate, Timeout, NextEvents) ->
+parse_actions(
+ Debug, _S, _State, [],
+ Hibernate, TimeoutsR, Postpone, NextEventsR) ->
+ {ok,Debug,Hibernate,TimeoutsR,Postpone,NextEventsR};
+parse_actions(
+ Debug, S, State, [Action|Actions],
+ Hibernate, TimeoutsR, Postpone, NextEventsR) ->
case Action of
%% Actual actions
{reply,From,Reply} ->
case from(From) of
true ->
NewDebug = do_reply(Debug, S, State, From, Reply),
- loop_event_actions(
- Parent, NewDebug, S, Events,
- State, NewData, P, Event, NextState, Actions,
- Postpone, Hibernate, Timeout, NextEvents);
- false ->
- terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
- Debug, S, [Event|Events], State, NewData, P)
- end;
- {next_event,Type,Content} ->
- case event_type(Type) of
- true ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, Actions,
- Postpone, Hibernate, Timeout,
- [{Type,Content}|NextEvents]);
+ parse_actions(
+ NewDebug, S, State, Actions,
+ Hibernate, TimeoutsR, Postpone, NextEventsR);
false ->
- terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
- Debug, S, [Event|Events], State, NewData, P)
+ {error,
+ {bad_action_from_state_function,Action},
+ ?STACKTRACE()}
end;
+ %%
%% Actions that set options
- {postpone,NewPostpone} when is_boolean(NewPostpone) ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, Actions,
- NewPostpone, Hibernate, Timeout, NextEvents);
- {postpone,_} ->
- terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
- Debug, S, [Event|Events], State, NewData, P);
- postpone ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, Actions,
- true, Hibernate, Timeout, NextEvents);
{hibernate,NewHibernate} when is_boolean(NewHibernate) ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, Actions,
- Postpone, NewHibernate, Timeout, NextEvents);
- {hibernate,_} ->
- terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
- Debug, S, [Event|Events], State, NewData, P);
+ parse_actions(
+ Debug, S, State, Actions,
+ NewHibernate, TimeoutsR, Postpone, NextEventsR);
hibernate ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, Actions,
- Postpone, true, Timeout, NextEvents);
- {timeout,infinity,_} -> % Clear timer - it will never trigger
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, Actions,
- Postpone, Hibernate, undefined, NextEvents);
- {timeout,Time,_} = NewTimeout when is_integer(Time), Time >= 0 ->
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, Actions,
- Postpone, Hibernate, NewTimeout, NextEvents);
- {timeout,_,_} ->
- terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
- Debug, S, [Event|Events], State, NewData, P);
- infinity -> % Clear timer - it will never trigger
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, Actions,
- Postpone, Hibernate, undefined, NextEvents);
- Time when is_integer(Time), Time >= 0 ->
- NewTimeout = {timeout,Time,Time},
- loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P, Event, NextState, Actions,
- Postpone, Hibernate, NewTimeout, NextEvents);
- _ ->
- terminate(
- error, {bad_action,Action}, ?STACKTRACE(),
- Debug, S, [Event|Events], State, NewData, P)
- end;
-%%
-%% End of actions list
-loop_event_actions(
- Parent, Debug, S, Events,
- State, NewData, P0, Event, NextState, [],
- Postpone, Hibernate, Timeout, NextEvents) ->
- %%
- %% All options have been collected and next_events are buffered.
- %% Do the actual state transition.
- %%
- P1 = % Move current event to postponed if Postpone
- case Postpone of
- true ->
- [Event|P0];
- false ->
- P0
- end,
- {Q2,P} = % Move all postponed events to queue if state change
- if
- NextState =:= State ->
- {Events,P1};
- true ->
- {lists:reverse(P1, Events),[]}
+ NewHibernate = true,
+ parse_actions(
+ Debug, S, State, Actions,
+ NewHibernate, TimeoutsR, Postpone, NextEventsR);
+ %%
+ {postpone,NewPostpone}
+ when is_boolean(NewPostpone), Postpone =/= forbidden ->
+ parse_actions(
+ Debug, S, State, Actions,
+ Hibernate, TimeoutsR, NewPostpone, NextEventsR);
+ postpone when Postpone =/= forbidden ->
+ NewPostpone = true,
+ parse_actions(
+ Debug, S, State, Actions,
+ Hibernate, TimeoutsR, NewPostpone, NextEventsR);
+ %%
+ {next_event,Type,Content} ->
+ case event_type(Type) of
+ true when NextEventsR =/= forbidden ->
+ NewDebug =
+ sys_debug(Debug, S, State, {in,{Type,Content}}),
+ parse_actions(
+ NewDebug, S, State, Actions,
+ Hibernate, TimeoutsR, Postpone,
+ [{Type,Content}|NextEventsR]);
+ _ ->
+ {error,
+ {bad_action_from_state_function,Action},
+ ?STACKTRACE()}
+ end;
+ %%
+ {state_timeout,_,_} = Timeout ->
+ parse_actions_timeout(
+ Debug, S, State, Actions,
+ Hibernate, TimeoutsR, Postpone, NextEventsR, Timeout);
+ {timeout,_,_} = Timeout ->
+ parse_actions_timeout(
+ Debug, S, State, Actions,
+ Hibernate, TimeoutsR, Postpone, NextEventsR, Timeout);
+ Time ->
+ parse_actions_timeout(
+ Debug, S, State, Actions,
+ Hibernate, TimeoutsR, Postpone, NextEventsR, Time)
+ end.
+
+parse_actions_timeout(
+ Debug, S, State, Actions,
+ Hibernate, TimeoutsR, Postpone, NextEventsR, Timeout) ->
+ Time =
+ case Timeout of
+ {_,T,_} -> T;
+ T -> T
end,
- %% Place next events first in queue
- Q = lists:reverse(NextEvents, Q2),
- %%
- NewDebug =
- sys_debug(
- Debug, S, State,
- case Postpone of
- true ->
- {postpone,Event,NextState};
- false ->
- {consume,Event,NextState}
- end),
- loop_events(
- Parent, NewDebug, S, Q, NextState, NewData, P, Hibernate, Timeout).
+ case validate_time(Time) of
+ true ->
+ parse_actions(
+ Debug, S, State, Actions,
+ Hibernate, [Timeout|TimeoutsR],
+ Postpone, NextEventsR);
+ false ->
+ {error,
+ {bad_action_from_state_function,Timeout},
+ ?STACKTRACE()}
+ end.
+
+validate_time(Time) when is_integer(Time), Time >= 0 -> true;
+validate_time(infinity) -> true;
+validate_time(_) -> false.
+
+%% Stop and start timers as well as create timeout zero events
+%% and pending event timer
+%%
+%% Stop and start timers non-event timers
+parse_timers(TimerRefs, TimerTypes, CancelTimers, TimeoutsR) ->
+ parse_timers(TimerRefs, TimerTypes, CancelTimers, TimeoutsR, #{}, []).
+%%
+parse_timers(
+ TimerRefs, TimerTypes, CancelTimers, [], _Seen, TimeoutEvents) ->
+ {TimerRefs,TimerTypes,CancelTimers,TimeoutEvents};
+parse_timers(
+ TimerRefs, TimerTypes, CancelTimers, [Timeout|TimeoutsR],
+ Seen, TimeoutEvents) ->
+ case Timeout of
+ {TimerType,Time,TimerMsg} ->
+ parse_timers(
+ TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
+ Seen, TimeoutEvents,
+ TimerType, Time, TimerMsg);
+ Time ->
+ parse_timers(
+ TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
+ Seen, TimeoutEvents,
+ timeout, Time, Time)
+ end.
+
+parse_timers(
+ TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
+ Seen, TimeoutEvents,
+ TimerType, Time, TimerMsg) ->
+ case Seen of
+ #{TimerType := _} ->
+ %% Type seen before - ignore
+ parse_timers(
+ TimerRefs, TimerTypes, CancelTimers, TimeoutsR,
+ Seen, TimeoutEvents);
+ #{} ->
+ %% Unseen type - handle
+ NewSeen = Seen#{TimerType => true},
+ case Time of
+ infinity ->
+ %% Cancel any running timer
+ {NewTimerTypes,NewCancelTimers} =
+ cancel_timer_by_type(
+ TimerType, TimerTypes, CancelTimers),
+ parse_timers(
+ TimerRefs, NewTimerTypes, NewCancelTimers, TimeoutsR,
+ NewSeen, TimeoutEvents);
+ 0 ->
+ %% Cancel any running timer
+ {NewTimerTypes,NewCancelTimers} =
+ cancel_timer_by_type(
+ TimerType, TimerTypes, CancelTimers),
+ %% Handle zero time timeouts later
+ TimeoutEvent = {TimerType,TimerMsg},
+ parse_timers(
+ TimerRefs, NewTimerTypes, NewCancelTimers, TimeoutsR,
+ NewSeen, [TimeoutEvent|TimeoutEvents]);
+ _ ->
+ %% (Re)start the timer
+ TimerRef =
+ erlang:start_timer(Time, self(), TimerMsg),
+ case TimerTypes of
+ #{TimerType := OldTimerRef} ->
+ %% Cancel the running timer
+ cancel_timer(OldTimerRef),
+ NewCancelTimers = CancelTimers + 1,
+ %% Insert the new timer into
+ %% both TimerRefs and TimerTypes
+ parse_timers(
+ TimerRefs#{TimerRef => TimerType},
+ TimerTypes#{TimerType => TimerRef},
+ NewCancelTimers, TimeoutsR,
+ NewSeen, TimeoutEvents);
+ #{} ->
+ parse_timers(
+ TimerRefs#{TimerRef => TimerType},
+ TimerTypes#{TimerType => TimerRef},
+ CancelTimers, TimeoutsR,
+ NewSeen, TimeoutEvents)
+ end
+ end
+ end.
+
+%% Enqueue immediate timeout events (timeout 0 events)
+%%
+%% Event timer timeout 0 events gets special treatment since
+%% an event timer is cancelled by any received event,
+%% so if there are enqueued events before the event timer
+%% timeout 0 event - the event timer is cancelled hence no event.
+%%
+%% Other (state_timeout) timeout 0 events that are after
+%% the event timer timeout 0 events are considered to
+%% belong to timers that were started after the event timer
+%% timeout 0 event fired, so they do not cancel the event timer.
+%%
+prepend_timeout_events([], EventsR) ->
+ EventsR;
+prepend_timeout_events([{timeout,_} = TimeoutEvent|TimeoutEvents], []) ->
+ prepend_timeout_events(TimeoutEvents, [TimeoutEvent]);
+prepend_timeout_events([{timeout,_}|TimeoutEvents], EventsR) ->
+ prepend_timeout_events(TimeoutEvents, EventsR);
+prepend_timeout_events([TimeoutEvent|TimeoutEvents], EventsR) ->
+ %% Just prepend all others
+ prepend_timeout_events(TimeoutEvents, [TimeoutEvent|EventsR]).
+
+
%%---------------------------------------------------------------------------
%% Server helpers
reply_then_terminate(
- Class, Reason, Stacktrace,
- Debug, S, Q, State, Data, P, Replies) ->
- if
- is_list(Replies) ->
- do_reply_then_terminate(
- Class, Reason, Stacktrace,
- Debug, S, Q, State, Data, P, Replies);
- true ->
- do_reply_then_terminate(
- Class, Reason, Stacktrace,
- Debug, S, Q, State, Data, P, [Replies])
- end.
+ Class, Reason, Stacktrace, Debug,
+ #{state := State} = S, Q, Replies) ->
+ do_reply_then_terminate(
+ Class, Reason, Stacktrace, Debug,
+ S, Q, listify(Replies), State).
%%
do_reply_then_terminate(
- Class, Reason, Stacktrace, Debug, S, Q, State, Data, P, []) ->
- terminate(Class, Reason, Stacktrace, Debug, S, Q, State, Data, P);
+ Class, Reason, Stacktrace, Debug, S, Q, [], _State) ->
+ terminate(Class, Reason, Stacktrace, Debug, S, Q);
do_reply_then_terminate(
- Class, Reason, Stacktrace, Debug, S, Q, State, Data, P, [R|Rs]) ->
+ Class, Reason, Stacktrace, Debug, S, Q, [R|Rs], State) ->
case R of
{reply,{_To,_Tag}=From,Reply} ->
NewDebug = do_reply(Debug, S, State, From, Reply),
do_reply_then_terminate(
- Class, Reason, Stacktrace,
- NewDebug, S, Q, State, Data, P, Rs);
+ Class, Reason, Stacktrace, NewDebug, S, Q, Rs, State);
_ ->
terminate(
- error, {bad_action,R}, ?STACKTRACE(),
- Debug, S, Q, State, Data, P)
+ error,
+ {bad_reply_action_from_state_function,R},
+ ?STACKTRACE(),
+ Debug, S, Q)
end.
do_reply(Debug, S, State, From, Reply) ->
@@ -1179,28 +1559,40 @@ do_reply(Debug, S, State, From, Reply) ->
terminate(
- Class, Reason, Stacktrace,
- Debug, #{module := Module} = S, Q, State, Data, P) ->
- try Module:terminate(Reason, State, Data) of
- _ -> ok
- catch
- _ -> ok;
- C:R ->
- ST = erlang:get_stacktrace(),
- error_info(
- C, R, ST, Debug, S, Q, P,
- format_status(terminate, get(), S, State, Data)),
- erlang:raise(C, R, ST)
- end,
- case Reason of
- normal -> ok;
- shutdown -> ok;
- {shutdown,_} -> ok;
- _ ->
- error_info(
- Class, Reason, Stacktrace, Debug, S, Q, P,
- format_status(terminate, get(), S, State, Data))
+ Class, Reason, Stacktrace, Debug,
+ #{module := Module, state := State, data := Data, postponed := P} = S,
+ Q) ->
+ case erlang:function_exported(Module, terminate, 3) of
+ true ->
+ try Module:terminate(Reason, State, Data) of
+ _ -> ok
+ catch
+ _ -> ok;
+ C:R ->
+ ST = erlang:get_stacktrace(),
+ error_info(
+ C, R, ST, S, Q, P,
+ format_status(terminate, get(), S)),
+ sys:print_log(Debug),
+ erlang:raise(C, R, ST)
+ end;
+ false ->
+ ok
end,
+ _ =
+ case Reason of
+ normal ->
+ sys_debug(Debug, S, State, {terminate,Reason});
+ shutdown ->
+ sys_debug(Debug, S, State, {terminate,Reason});
+ {shutdown,_} ->
+ sys_debug(Debug, S, State, {terminate,Reason});
+ _ ->
+ error_info(
+ Class, Reason, Stacktrace, S, Q, P,
+ format_status(terminate, get(), S)),
+ sys:print_log(Debug)
+ end,
case Stacktrace of
[] ->
erlang:Class(Reason);
@@ -1209,8 +1601,10 @@ terminate(
end.
error_info(
- Class, Reason, Stacktrace, Debug,
- #{name := Name, callback_mode := CallbackMode},
+ Class, Reason, Stacktrace,
+ #{name := Name,
+ callback_mode := CallbackMode,
+ state_enter := StateEnter},
Q, P, FmtData) ->
{FixedReason,FixedStacktrace} =
case Stacktrace of
@@ -1237,6 +1631,13 @@ error_info(
end;
_ -> {Reason,Stacktrace}
end,
+ CBMode =
+ case StateEnter of
+ true ->
+ [CallbackMode,state_enter];
+ false ->
+ CallbackMode
+ end,
error_logger:format(
"** State machine ~p terminating~n" ++
case Q of
@@ -1263,8 +1664,9 @@ error_info(
[] -> [];
[Event|_] -> [Event]
end] ++
- [FmtData,Class,FixedReason,
- CallbackMode] ++
+ [FmtData,
+ Class,FixedReason,
+ CBMode] ++
case Q of
[_|[_|_] = Events] -> [Events];
_ -> []
@@ -1276,13 +1678,13 @@ error_info(
case FixedStacktrace of
[] -> [];
_ -> [FixedStacktrace]
- end),
- sys:print_log(Debug),
- ok.
+ end).
%% Call Module:format_status/2 or return a default value
-format_status(Opt, PDict, #{module := Module}, State, Data) ->
+format_status(
+ Opt, PDict,
+ #{module := Module, state := State, data := Data}) ->
case erlang:function_exported(Module, format_status, 2) of
true ->
try Module:format_status(Opt, [PDict,State,Data])
@@ -1291,7 +1693,7 @@ format_status(Opt, PDict, #{module := Module}, State, Data) ->
_:_ ->
format_status_default(
Opt, State,
- "Module:format_status/2 crashed")
+ atom_to_list(Module) ++ ":format_status/2 crashed")
end;
false ->
format_status_default(Opt, State, Data)
@@ -1299,10 +1701,35 @@ format_status(Opt, PDict, #{module := Module}, State, Data) ->
%% The default Module:format_status/2
format_status_default(Opt, State, Data) ->
- SSD = {State,Data},
+ StateData = {State,Data},
case Opt of
terminate ->
- SSD;
+ StateData;
_ ->
- [{data,[{"State",SSD}]}]
+ [{data,[{"State",StateData}]}]
+ end.
+
+listify(Item) when is_list(Item) ->
+ Item;
+listify(Item) ->
+ [Item].
+
+%% Cancel timer if running, otherwise no op
+%%
+%% This is an asynchronous cancel so the timer is not really cancelled
+%% until we get a cancel_timer msg i.e {cancel_timer,TimerRef,_}.
+%% In the mean time we might get a timeout message.
+%%
+%% Remove the timer from TimerTypes.
+%% When we get the cancel_timer msg we remove it from TimerRefs.
+cancel_timer_by_type(TimerType, TimerTypes, CancelTimers) ->
+ case TimerTypes of
+ #{TimerType := TimerRef} ->
+ cancel_timer(TimerRef),
+ {maps:remove(TimerType, TimerTypes),CancelTimers + 1};
+ #{} ->
+ {TimerTypes,CancelTimers}
end.
+
+cancel_timer(TimerRef) ->
+ ok = erlang:cancel_timer(TimerRef, [{async,true}]).
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index 16ca2f41dc..ba2cffdcb3 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. 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.
@@ -128,6 +128,10 @@ max_cs(M, _Len) ->
M.
-define(ATM(T), is_list(element(1, T))).
+-define(ATM_PAIR(Pair),
+ ?ATM(element(2, element(1, Pair))) % Key
+ andalso
+ ?ATM(element(3, element(1, Pair)))). % Value
-define(ATM_FLD(Field), ?ATM(element(4, element(1, Field)))).
pp({_S, Len} = If, Col, Ll, M, _TInd, _Ind, LD, W)
@@ -140,9 +144,8 @@ pp({{tuple,true,L}, _Len}, Col, Ll, M, TInd, Ind, LD, W) ->
pp({{tuple,false,L}, _Len}, Col, Ll, M, TInd, Ind, LD, W) ->
[${, pp_list(L, Col + 1, Ll, M, TInd, indent(1, Ind), LD, $,, W + 1), $}];
pp({{map,Pairs},_Len}, Col, Ll, M, TInd, Ind, LD, W) ->
- [$#,${, pp_list(Pairs, Col + 2, Ll, M, TInd, indent(2, Ind), LD, $,, W + 1), $}];
-pp({{map_pair,K,V},_Len}, Col, Ll, M, TInd, Ind, LD, W) ->
- [pp(K, Col, Ll, M, TInd, Ind, LD, W), " => ", pp(V, Col, Ll, M, TInd, Ind, LD, W)];
+ [$#, ${, pp_map(Pairs, Col + 2, Ll, M, TInd, indent(2, Ind), LD, W + 1),
+ $}];
pp({{record,[{Name,NLen} | L]}, _Len}, Col, Ll, M, TInd, Ind, LD, W) ->
[Name, ${, pp_record(L, NLen, Col, Ll, M, TInd, Ind, LD, W + NLen+1), $}];
pp({{bin,S}, _Len}, Col, Ll, M, _TInd, Ind, LD, W) ->
@@ -166,6 +169,46 @@ pp_tag_tuple([{Tag,Tlen} | L], Col, Ll, M, TInd, Ind, LD, W) ->
[Tag, S | pp_list(L, Tcol, Ll, M, TInd, Indent, LD, S, W+Tlen+1)]
end.
+pp_map([], _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
+ "";
+pp_map({dots, _}, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
+ "...";
+pp_map([P | Ps], Col, Ll, M, TInd, Ind, LD, W) ->
+ {PS, PW} = pp_pair(P, Col, Ll, M, TInd, Ind, last_depth(Ps, LD), W),
+ [PS | pp_pairs_tail(Ps, Col, Col + PW, Ll, M, TInd, Ind, LD, PW)].
+
+pp_pairs_tail([], _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
+ "";
+pp_pairs_tail({dots, _}, _Col0, _Col, _M, _Ll, _TInd, _Ind, _LD, _W) ->
+ ",...";
+pp_pairs_tail([{_, Len}=P | Ps], Col0, Col, Ll, M, TInd, Ind, LD, W) ->
+ LD1 = last_depth(Ps, LD),
+ ELen = 1 + Len,
+ if
+ LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_PAIR(P);
+ LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_PAIR(P) ->
+ [$,, write_pair(P) |
+ pp_pairs_tail(Ps, Col0, Col+ELen, Ll, M, TInd, Ind, LD, W+ELen)];
+ true ->
+ {PS, PW} = pp_pair(P, Col0, Ll, M, TInd, Ind, LD1, 0),
+ [$,, $\n, Ind, PS |
+ pp_pairs_tail(Ps, Col0, Col0 + PW, Ll, M, TInd, Ind, LD, PW)]
+ end.
+
+pp_pair({_, Len}=Pair, Col, Ll, M, _TInd, _Ind, LD, W)
+ when Len < Ll - Col - LD, Len + W + LD =< M ->
+ {write_pair(Pair), if
+ ?ATM_PAIR(Pair) ->
+ Len;
+ true ->
+ Ll % force nl
+ end};
+pp_pair({{map_pair, K, V}, _Len}, Col0, Ll, M, TInd, Ind0, LD, W) ->
+ I = map_value_indent(TInd),
+ Ind = indent(I, Ind0),
+ {[pp(K, Col0, Ll, M, TInd, Ind0, LD, W), " =>\n",
+ Ind | pp(V, Col0 + I, Ll, M, TInd, Ind, LD, 0)], Ll}. % force nl
+
pp_record([], _Nlen, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
"";
pp_record({dots, _}, _Nlen, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
@@ -204,7 +247,11 @@ pp_field({_, Len}=Fl, Col, Ll, M, _TInd, _Ind, LD, W)
end};
pp_field({{field, Name, NameL, F}, _Len}, Col0, Ll, M, TInd, Ind0, LD, W0) ->
{Col, Ind, S, W} = rec_indent(NameL, TInd, Col0, Ind0, W0 + NameL),
- {[Name, " = ", S | pp(F, Col, Ll, M, TInd, Ind, LD, W)], Ll}. % force nl
+ Sep = case S of
+ [$\n | _] -> " =";
+ _ -> " = "
+ end,
+ {[Name, Sep, S | pp(F, Col, Ll, M, TInd, Ind, LD, W)], Ll}. % force nl
rec_indent(RInd, TInd, Col0, Ind0, W0) ->
Nl = (TInd > 0) and (RInd > TInd),
@@ -291,8 +338,8 @@ write({{list, L}, _}) ->
[$[, write_list(L, $|), $]];
write({{map, Pairs}, _}) ->
[$#,${, write_list(Pairs, $,), $}];
-write({{map_pair, K, V}, _}) ->
- [write(K)," => ",write(V)];
+write({{map_pair, _K, _V}, _}=Pair) ->
+ write_pair(Pair);
write({{record, [{Name,_} | L]}, _}) ->
[Name, ${, write_fields(L), $}];
write({{bin, S}, _}) ->
@@ -300,6 +347,9 @@ write({{bin, S}, _}) ->
write({S, _}) ->
S.
+write_pair({{map_pair, K, V}, _}) ->
+ [write(K), " => ", write(V)].
+
write_fields([]) ->
"";
write_fields({dots, _}) ->
@@ -333,7 +383,7 @@ write_tail(E, S) ->
%% The depth (D) is used for extracting and counting the characters to
%% print. The structure is kept so that the returned intermediate
-%% format can be formatted. The separators (list, tuple, record) are
+%% format can be formatted. The separators (list, tuple, record, map) are
%% counted but need to be added later.
%% D =/= 0
@@ -406,21 +456,32 @@ print_length(Term, _D, _RF, _Enc, _Str) ->
print_length_map(_Map, 1, _RF, _Enc, _Str) ->
{"#{...}", 6};
print_length_map(Map, D, RF, Enc, Str) when is_map(Map) ->
- Pairs = print_length_map_pairs(maps:to_list(Map), D, RF, Enc, Str),
+ Pairs = print_length_map_pairs(maps_to_list(Map, D), D, RF, Enc, Str),
{{map, Pairs}, list_length(Pairs, 3)}.
+maps_to_list(Map, D) when D < 0; map_size(Map) =< D ->
+ maps:to_list(Map);
+maps_to_list(Map, D) ->
+ F = fun(_K, _V, {N, L}) when N =:= D ->
+ throw(L);
+ (K, V, {N, L}) ->
+ {N+1, [{K, V} | L]}
+ end,
+ lists:reverse(catch maps:fold(F, {0, []}, Map)).
+
print_length_map_pairs([], _D, _RF, _Enc, _Str) ->
[];
print_length_map_pairs(_Pairs, 1, _RF, _Enc, _Str) ->
{dots, 3};
-print_length_map_pairs([{K,V}|Pairs], D, RF, Enc, Str) ->
- [print_length_map_pair(K,V,D-1,RF,Enc,Str) |
- print_length_map_pairs(Pairs,D-1,RF,Enc,Str)].
+print_length_map_pairs([{K, V} | Pairs], D, RF, Enc, Str) ->
+ [print_length_map_pair(K, V, D - 1, RF, Enc, Str) |
+ print_length_map_pairs(Pairs, D - 1, RF, Enc, Str)].
print_length_map_pair(K, V, D, RF, Enc, Str) ->
{KS, KL} = print_length(K, D, RF, Enc, Str),
{VS, VL} = print_length(V, D, RF, Enc, Str),
- {{map_pair, {KS,KL}, {VS,VL}}, KL + VL}.
+ KL1 = KL + 4,
+ {{map_pair, {KS, KL1}, {VS, VL}}, KL1 + VL}.
print_length_tuple(_Tuple, 1, _RF, _Enc, _Str) ->
{"{...}", 5};
@@ -612,6 +673,8 @@ cind({{tuple,true,L}, _Len}, Col, Ll, M, Ind, LD, W) ->
cind_tag_tuple(L, Col, Ll, M, Ind, LD, W + 1);
cind({{tuple,false,L}, _Len}, Col, Ll, M, Ind, LD, W) ->
cind_list(L, Col + 1, Ll, M, Ind, LD, W + 1);
+cind({{map,Pairs},_Len}, Col, Ll, M, Ind, LD, W) ->
+ cind_map(Pairs, Col + 2, Ll, M, Ind, LD, W + 2);
cind({{record,[{_Name,NLen} | L]}, _Len}, Col, Ll, M, Ind, LD, W) ->
cind_record(L, NLen, Col, Ll, M, Ind, LD, W + NLen + 1);
cind({{bin,_S}, _Len}, _Col, _Ll, _M, Ind, _LD, _W) ->
@@ -637,6 +700,48 @@ cind_tag_tuple([{_Tag,Tlen} | L], Col, Ll, M, Ind, LD, W) ->
throw(no_good)
end.
+cind_map([P | Ps], Col, Ll, M, Ind, LD, W) ->
+ PW = cind_pair(P, Col, Ll, M, Ind, last_depth(Ps, LD), W),
+ cind_pairs_tail(Ps, Col, Col + PW, Ll, M, Ind, LD, W + PW);
+cind_map(_, _Col, _Ll, _M, Ind, _LD, _W) ->
+ Ind.
+
+cind_pairs_tail([{_, Len}=P | Ps], Col0, Col, Ll, M, Ind, LD, W) ->
+ LD1 = last_depth(Ps, LD),
+ ELen = 1 + Len,
+ if
+ LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_PAIR(P);
+ LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_PAIR(P) ->
+ cind_pairs_tail(Ps, Col0, Col + ELen, Ll, M, Ind, LD, W + ELen);
+ true ->
+ PW = cind_pair(P, Col0, Ll, M, Ind, LD1, 0),
+ cind_pairs_tail(Ps, Col0, Col0 + PW, Ll, M, Ind, LD, PW)
+ end;
+cind_pairs_tail(_, _Col0, _Col, _Ll, _M, Ind, _LD, _W) ->
+ Ind.
+
+cind_pair({{map_pair, _Key, _Value}, Len}=Pair, Col, Ll, M, _Ind, LD, W)
+ when Len < Ll - Col - LD, Len + W + LD =< M ->
+ if
+ ?ATM_PAIR(Pair) ->
+ Len;
+ true ->
+ Ll
+ end;
+cind_pair({{map_pair, K, V}, _Len}, Col0, Ll, M, Ind, LD, W0) ->
+ cind(K, Col0, Ll, M, Ind, LD, W0),
+ I = map_value_indent(Ind),
+ cind(V, Col0 + I, Ll, M, Ind, LD, 0),
+ Ll.
+
+map_value_indent(TInd) ->
+ case TInd > 0 of
+ true ->
+ TInd;
+ false ->
+ 4
+ end.
+
cind_record([F | Fs], Nlen, Col0, Ll, M, Ind, LD, W0) ->
Nind = Nlen + 1,
{Col, W} = cind_rec(Nind, Col0, Ll, M, Ind, W0),
diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl
index c0eea652e7..98745b13f3 100644
--- a/lib/stdlib/src/ms_transform.erl
+++ b/lib/stdlib/src/ms_transform.erl
@@ -451,6 +451,8 @@ check_type(_,[{record,_,_,_}],ets) ->
ok;
check_type(_,[{cons,_,_,_}],dbg) ->
ok;
+check_type(_,[{nil,_}],dbg) ->
+ ok;
check_type(Line0,[{match,_,{var,_,_},X}],Any) ->
check_type(Line0,[X],Any);
check_type(Line0,[{match,_,X,{var,_,_}}],Any) ->
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index f3665824f2..8c4d835432 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -1292,6 +1292,10 @@ abstr_term(Fun, Line) when is_function(Fun) ->
end;
abstr_term(PPR, Line) when is_pid(PPR); is_port(PPR); is_reference(PPR) ->
{special, Line, lists:flatten(io_lib:write(PPR))};
+abstr_term(Map, Line) when is_map(Map) ->
+ {map,Line,
+ [{map_field_assoc,Line,abstr_term(K, Line),abstr_term(V, Line)} ||
+ {K,V} <- maps:to_list(Map)]};
abstr_term(Simple, Line) ->
erl_parse:abstract(Simple, erl_anno:line(Line)).
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 82a3a2be4f..28f37ef8bf 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.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.
@@ -768,6 +768,8 @@ used_records({call,_,{atom,_,record_info},[A,{atom,_,Name}]}) ->
{name, Name, A};
used_records({call,Line,{tuple,_,[M,F]},As}) ->
used_records({call,Line,{remote,Line,M,F},As});
+used_records({type,_,record,[{atom,_,Name}|Fs]}) ->
+ {name, Name, Fs};
used_records(T) when is_tuple(T) ->
{expr, tuple_to_list(T)};
used_records(E) ->
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 9877662743..3aa4ec6dab 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2017. 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.
@@ -18,9 +18,9 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"3\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.*
%% Down to - max one major revision back
- [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ [{<<"3\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
{<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.*
}.
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index c81e72689c..1cd65fbf18 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -1087,6 +1087,10 @@ wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz,
wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
TRef, EStack);
+ {'DOWN', _MRef, process, Pid, {shutdown, _}} ->
+ wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
+ TRef, EStack);
+
{'DOWN', _MRef, process, Pid, normal} when RType =/= permanent ->
wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
TRef, EStack);
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index f8ba6f18e9..81f927f399 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -179,19 +179,6 @@
external_attr,
local_header_offset}).
-%% Unix extra fields (not yet supported)
--define(UNIX_EXTRA_FIELD_TAG, 16#000d).
--record(unix_extra_field, {atime,
- mtime,
- uid,
- gid}).
-
-%% extended timestamps (not yet supported)
--define(EXTENDED_TIMESTAMP_TAG, 16#5455).
-%% -record(extended_timestamp, {mtime,
-%% atime,
-%% ctime}).
-
-define(END_OF_CENTRAL_DIR_MAGIC, 16#06054b50).
-define(END_OF_CENTRAL_DIR_SZ, (4+2+2+2+2+4+4+2)).
@@ -279,7 +266,8 @@ do_openzip_get(F, #openzip{files = Files, in = In0, input = Input,
case file_name_search(F, Files) of
{#zip_file{offset = Offset},_}=ZFile ->
In1 = Input({seek, bof, Offset}, In0),
- case get_z_file(In1, Z, Input, Output, [], fun silent/1, CWD, ZFile) of
+ case get_z_file(In1, Z, Input, Output, [], fun silent/1,
+ CWD, ZFile, fun all/1) of
{file, R, _In2} -> {ok, R};
_ -> throw(file_not_found)
end;
@@ -380,9 +368,12 @@ do_unzip(F, Options) ->
{Info, In1} = get_central_dir(In0, RawIterator, Input),
%% get rid of zip-comment
Z = zlib:open(),
- Files = get_z_files(Info, Z, In1, Opts, []),
- zlib:close(Z),
- Input(close, In1),
+ Files = try
+ get_z_files(Info, Z, In1, Opts, [])
+ after
+ zlib:close(Z),
+ Input(close, In1)
+ end,
{ok, Files}.
%% Iterate over all files in a zip archive
@@ -459,11 +450,20 @@ do_zip(F, Files, Options) ->
#zip_opts{output = Output, open_opts = OpO} = Opts,
Out0 = Output({open, F, OpO}, []),
Z = zlib:open(),
- {Out1, LHS, Pos} = put_z_files(Files, Z, Out0, 0, Opts, []),
- zlib:close(Z),
- Out2 = put_central_dir(LHS, Pos, Out1, Opts),
- Out3 = Output({close, F}, Out2),
- {ok, Out3}.
+ try
+ {Out1, LHS, Pos} = put_z_files(Files, Z, Out0, 0, Opts, []),
+ zlib:close(Z),
+ Out2 = put_central_dir(LHS, Pos, Out1, Opts),
+ Out3 = Output({close, F}, Out2),
+ {ok, Out3}
+ catch
+ C:R ->
+ Stk = erlang:get_stacktrace(),
+ zlib:close(Z),
+ Output({close, F}, Out0),
+ erlang:raise(C, R, Stk)
+ end.
+
%% List zip directory contents
%%
@@ -1378,12 +1378,7 @@ cd_file_header_to_file_info(FileName,
gid = 0},
add_extra_info(FI, ExtraField).
-%% add extra info to file (some day when we implement it)
-add_extra_info(FI, <<?EXTENDED_TIMESTAMP_TAG:16/little, _Rest/binary>>) ->
- FI; % not yet supported, some other day...
-add_extra_info(FI, <<?UNIX_EXTRA_FIELD_TAG:16/little, Rest/binary>>) ->
- _UnixExtra = unix_extra_field_and_var_from_bin(Rest),
- FI; % not yet supported, and not widely used
+%% Currently, we ignore all the extra fields.
add_extra_info(FI, _) ->
FI.
@@ -1403,9 +1398,10 @@ get_z_files([{#zip_file{offset = Offset},_} = ZFile | Rest], Z, In0,
true ->
In1 = Input({seek, bof, Offset}, In0),
{In2, Acc1} =
- case get_z_file(In1, Z, Input, Output, OpO, FB, CWD, ZFile) of
+ case get_z_file(In1, Z, Input, Output, OpO, FB,
+ CWD, ZFile, Filter) of
{file, GZD, Inx} -> {Inx, [GZD | Acc0]};
- {dir, Inx} -> {Inx, Acc0}
+ {_, Inx} -> {Inx, Acc0}
end,
get_z_files(Rest, Z, In2, Opts, Acc1);
_ ->
@@ -1413,7 +1409,8 @@ get_z_files([{#zip_file{offset = Offset},_} = ZFile | Rest], Z, In0,
end.
%% get a file from the archive, reading chunks
-get_z_file(In0, Z, Input, Output, OpO, FB, CWD, {ZipFile,Extra}) ->
+get_z_file(In0, Z, Input, Output, OpO, FB,
+ CWD, {ZipFile,Extra}, Filter) ->
case Input({read, ?LOCAL_FILE_HEADER_SZ}, In0) of
{eof, In1} ->
{eof, In1};
@@ -1433,29 +1430,64 @@ get_z_file(In0, Z, Input, Output, OpO, FB, CWD, {ZipFile,Extra}) ->
end,
{BFileN, In3} = Input({read, FileNameLen + ExtraLen}, In1),
{FileName, _} = get_file_name_extra(FileNameLen, ExtraLen, BFileN),
- FileName1 = add_cwd(CWD, FileName),
- case lists:last(FileName) of
- $/ ->
- %% perhaps this should always be done?
- Output({ensure_dir,FileName1},[]),
- {dir, In3};
- _ ->
- %% FileInfo = local_file_header_to_file_info(LH)
- %%{Out, In4, CRC, UncompSize} =
- {Out, In4, CRC, _UncompSize} =
- get_z_data(CompMethod, In3, FileName1,
- CompSize, Input, Output, OpO, Z),
- In5 = skip_z_data_descriptor(GPFlag, Input, In4),
- %% TODO This should be fixed some day:
- %% In5 = Input({set_file_info, FileName, FileInfo#file_info{size=UncompSize}}, In4),
- FB(FileName),
- CRC =:= CRC32 orelse throw({bad_crc, FileName}),
- {file, Out, In5}
+ ReadAndWrite =
+ case check_valid_location(CWD, FileName) of
+ {true,FileName1} ->
+ true;
+ {false,FileName1} ->
+ Filter({ZipFile#zip_file{name = FileName1},Extra})
+ end,
+ case ReadAndWrite of
+ true ->
+ case lists:last(FileName) of
+ $/ ->
+ %% perhaps this should always be done?
+ Output({ensure_dir,FileName1},[]),
+ {dir, In3};
+ _ ->
+ %% FileInfo = local_file_header_to_file_info(LH)
+ %%{Out, In4, CRC, UncompSize} =
+ {Out, In4, CRC, _UncompSize} =
+ get_z_data(CompMethod, In3, FileName1,
+ CompSize, Input, Output, OpO, Z),
+ In5 = skip_z_data_descriptor(GPFlag, Input, In4),
+ %% TODO This should be fixed some day:
+ %% In5 = Input({set_file_info, FileName,
+ %% FileInfo#file_info{size=UncompSize}}, In4),
+ FB(FileName),
+ CRC =:= CRC32 orelse throw({bad_crc, FileName}),
+ {file, Out, In5}
+ end;
+ false ->
+ {ignore, In3}
end;
_ ->
throw(bad_local_file_header)
end.
+%% make sure FileName doesn't have relative path that points over CWD
+check_valid_location(CWD, FileName) ->
+ %% check for directory traversal exploit
+ case check_dir_level(filename:split(FileName), 0) of
+ {FileOrDir,Level} when Level < 0 ->
+ CWD1 = if CWD == "" -> "./";
+ true -> CWD
+ end,
+ error_logger:format("Illegal path: ~ts, extracting in ~ts~n",
+ [add_cwd(CWD,FileName),CWD1]),
+ {false,add_cwd(CWD, FileOrDir)};
+ _ ->
+ {true,add_cwd(CWD, FileName)}
+ end.
+
+check_dir_level([FileOrDir], Level) ->
+ {FileOrDir,Level};
+check_dir_level(["." | Parts], Level) ->
+ check_dir_level(Parts, Level);
+check_dir_level([".." | Parts], Level) ->
+ check_dir_level(Parts, Level-1);
+check_dir_level([_Dir | Parts], Level) ->
+ check_dir_level(Parts, Level+1).
get_file_name_extra(FileNameLen, ExtraLen, B) ->
case B of
@@ -1534,20 +1566,6 @@ dos_date_time_from_datetime({{Year, Month, Day}, {Hour, Min, Sec}}) ->
<<DosDate:16>> = <<YearFrom1980:7, Month:4, Day:5>>,
{DosDate, DosTime}.
-unix_extra_field_and_var_from_bin(<<TSize:16/little,
- ATime:32/little,
- MTime:32/little,
- UID:16/little,
- GID:16/little,
- Var:TSize/binary>>) ->
- {#unix_extra_field{atime = ATime,
- mtime = MTime,
- uid = UID,
- gid = GID},
- Var};
-unix_extra_field_and_var_from_bin(_) ->
- throw(bad_unix_extra_field).
-
%% A pwrite-like function for iolists (used by memory-option)
pwrite_binary(B, Pos, Bin) when byte_size(B) =:= Pos ->
diff --git a/lib/stdlib/test/base64_SUITE.erl b/lib/stdlib/test/base64_SUITE.erl
index 9176a3664a..d0abe5c961 100644
--- a/lib/stdlib/test/base64_SUITE.erl
+++ b/lib/stdlib/test/base64_SUITE.erl
@@ -23,9 +23,7 @@
-include_lib("common_test/include/ct.hrl").
%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0, groups/0, group/1]).
%% Test cases must be exported.
-export([base64_encode/1, base64_decode/1, base64_otp_5635/1,
@@ -33,41 +31,26 @@
mime_decode_to_string/1,
roundtrip_1/1, roundtrip_2/1, roundtrip_3/1, roundtrip_4/1]).
-init_per_testcase(_, Config) ->
- Config.
-
-end_per_testcase(_, _Config) ->
- ok.
-
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
+
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,4}}].
-all() ->
+all() ->
[base64_encode, base64_decode, base64_otp_5635,
base64_otp_6279, big, illegal, mime_decode, mime_decode_to_string,
{group, roundtrip}].
-groups() ->
+groups() ->
[{roundtrip, [parallel],
[roundtrip_1, roundtrip_2, roundtrip_3, roundtrip_4]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
+group(roundtrip) ->
+ %% valgrind needs a lot of time
+ [{timetrap,{minutes,10}}].
%%-------------------------------------------------------------------------
%% Test base64:encode/1.
@@ -78,9 +61,9 @@ base64_encode(Config) when is_list(Config) ->
%% One pad
<<"SGVsbG8gV29ybGQ=">> = base64:encode(<<"Hello World">>),
%% No pad
- "QWxhZGRpbjpvcGVuIHNlc2Ft" =
+ "QWxhZGRpbjpvcGVuIHNlc2Ft" =
base64:encode_to_string("Aladdin:open sesam"),
-
+
"MDEyMzQ1Njc4OSFAIzBeJiooKTs6PD4sLiBbXXt9" =
base64:encode_to_string(<<"0123456789!@#0^&*();:<>,. []{}">>),
ok.
@@ -93,7 +76,7 @@ base64_decode(Config) when is_list(Config) ->
%% One pad
<<"Hello World">> = base64:decode(<<"SGVsbG8gV29ybGQ=">>),
%% No pad
- <<"Aladdin:open sesam">> =
+ <<"Aladdin:open sesam">> =
base64:decode("QWxhZGRpbjpvcGVuIHNlc2Ft"),
Alphabet = list_to_binary(lists:seq(0, 255)),
@@ -208,7 +191,7 @@ mime_decode_to_string(Config) when is_list(Config) ->
%% One pad to ignore, followed by more text
"Hello World!!" = base64:mime_decode_to_string(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>),
%% No pad
- "Aladdin:open sesam" =
+ "Aladdin:open sesam" =
base64:mime_decode_to_string("QWxhZGRpbjpvcG¤\")(VuIHNlc2Ft"),
%% Encoded base 64 strings may be divided by non base 64 chars.
%% In this cases whitespaces.
@@ -314,7 +297,7 @@ interleaved_ws_roundtrip_1([], Base64List, Bin, List) ->
random_byte_list(0, Acc) ->
Acc;
-random_byte_list(N, Acc) ->
+random_byte_list(N, Acc) ->
random_byte_list(N-1, [rand:uniform(255)|Acc]).
make_big_binary(N) ->
diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl
index ccffa2e244..1f694ea549 100644
--- a/lib/stdlib/test/edlin_expand_SUITE.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -21,7 +21,8 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_testcase/2, end_per_testcase/2,
init_per_group/2,end_per_group/2]).
--export([normal/1, quoted_fun/1, quoted_module/1, quoted_both/1]).
+-export([normal/1, quoted_fun/1, quoted_module/1, quoted_both/1, erl_1152/1,
+ erl_352/1]).
-include_lib("common_test/include/ct.hrl").
@@ -36,7 +37,7 @@ suite() ->
{timetrap,{minutes,1}}].
all() ->
- [normal, quoted_fun, quoted_module, quoted_both].
+ [normal, quoted_fun, quoted_module, quoted_both, erl_1152, erl_352].
groups() ->
[].
@@ -149,5 +150,84 @@ quoted_both(Config) when is_list(Config) ->
{yes,"weird-fun-name'()",[]} = do_expand("'ExpandTestCaps1':'#"),
ok.
+erl_1152(Config) when is_list(Config) ->
+ "\n"++"foo"++" "++[1089]++_ = do_format(["foo",[1089]]),
+ ok.
+
+erl_352(Config) when is_list(Config) ->
+ erl_352_test(3, 3),
+
+ erl_352_test(3, 75),
+ erl_352_test(3, 76, [trailing]),
+ erl_352_test(4, 74),
+ erl_352_test(4, 75, [leading]),
+ erl_352_test(4, 76, [leading, trailing]),
+
+ erl_352_test(75, 3),
+ erl_352_test(76, 3, [leading]),
+ erl_352_test(74, 4),
+ erl_352_test(75, 4, [leading]),
+ erl_352_test(76, 4, [leading]),
+
+ erl_352_test(74, 74, [leading]),
+ erl_352_test(74, 75, [leading]),
+ erl_352_test(74, 76, [leading, trailing]).
+
+erl_352_test(PrefixLen, SuffixLen) ->
+ erl_352_test(PrefixLen, SuffixLen, []).
+
+erl_352_test(PrefixLen, SuffixLen, Dots) ->
+ io:format("\nPrefixLen = ~w, SuffixLen = ~w\n", [PrefixLen, SuffixLen]),
+
+ PrefixM = lists:duplicate(PrefixLen, $p),
+ SuffixM = lists:duplicate(SuffixLen, $s),
+ LM = [PrefixM ++ S ++ SuffixM || S <- ["1", "2"]],
+ StrM = do_format(LM),
+ check_leading(StrM, "", PrefixM, SuffixM, Dots),
+
+ PrefixF = lists:duplicate(PrefixLen, $p),
+ SuffixF = lists:duplicate(SuffixLen-2, $s),
+ LF = [{PrefixF ++ S ++ SuffixF, 1} || S <- ["1", "2"]],
+ StrF = do_format(LF),
+ true = check_leading(StrF, "/1", PrefixF, SuffixF, Dots),
+
+ ok.
+
+check_leading(FormStr, ArityStr, Prefix, Suffix, Dots) ->
+ List = string:tokens(FormStr, "\n "),
+ io:format("~p\n", [List]),
+ true = lists:all(fun(L) -> length(L) < 80 end, List),
+ case lists:member(leading, Dots) of
+ true ->
+ true = lists:all(fun(L) ->
+ {"...", Rest} = lists:split(3, L),
+ check_trailing(Rest, ArityStr,
+ Suffix, Dots)
+ end, List);
+ false ->
+ true = lists:all(fun(L) ->
+ {Prefix, Rest} =
+ lists:split(length(Prefix), L),
+ check_trailing(Rest, ArityStr,
+ Suffix, Dots)
+ end, List)
+ end.
+
+check_trailing([I|Str], ArityStr, Suffix, Dots) ->
+ true = lists:member(I, [$1, $2]),
+ case lists:member(trailing, Dots) of
+ true ->
+ {Rest, "..." ++ ArityStr} =
+ lists:split(length(Str) - (3 + length(ArityStr)), Str),
+ true = lists:prefix(Rest, Suffix);
+ false ->
+ {Rest, ArityStr} =
+ lists:split(length(Str) - length(ArityStr), Str),
+ Rest =:= Suffix
+ end.
+
do_expand(String) ->
edlin_expand:expand(lists:reverse(String)).
+
+do_format(StringList) ->
+ lists:flatten(edlin_expand:format_matches(StringList)).
diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl
index 28d69232a0..0b9106a99c 100644
--- a/lib/stdlib/test/escript_SUITE.erl
+++ b/lib/stdlib/test/escript_SUITE.erl
@@ -28,6 +28,7 @@
strange_name/1,
emulator_flags/1,
emulator_flags_no_shebang/1,
+ two_lines/1,
module_script/1,
beam_script/1,
archive_script/1,
@@ -49,7 +50,7 @@ suite() ->
all() ->
[basic, errors, strange_name, emulator_flags,
- emulator_flags_no_shebang,
+ emulator_flags_no_shebang, two_lines,
module_script, beam_script, archive_script, epp,
create_and_extract, foldl, overflow,
archive_script_file_access, unicode].
@@ -153,6 +154,18 @@ emulator_flags(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+two_lines(Config) when is_list(Config) ->
+ Data = proplists:get_value(data_dir, Config),
+ Dir = filename:absname(Data), %Get rid of trailing slash.
+ run(Dir, "two_lines -arg1 arg2 arg3",
+ [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
+ "ERL_FLAGS=false\n"
+ "unknown:[]\n"
+ "ExitCode:0">>]),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
emulator_flags_no_shebang(Config) when is_list(Config) ->
Data = proplists:get_value(data_dir, Config),
Dir = filename:absname(Data), %Get rid of trailing slash.
diff --git a/lib/stdlib/test/escript_SUITE_data/two_lines b/lib/stdlib/test/escript_SUITE_data/two_lines
new file mode 100755
index 0000000000..cf4e99639c
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/two_lines
@@ -0,0 +1,2 @@
+#! /usr/bin/env escript
+main(MainArgs) -> io:format("main:~p\n", [MainArgs]), ErlArgs = init:get_arguments(), io:format("ERL_FLAGS=~p\n", [os:getenv("ERL_FLAGS")]), io:format("unknown:~p\n",[[E || E <- ErlArgs, element(1, E) =:= unknown]]).
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 8c1c625676..00e02a06cc 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -19,7 +19,7 @@
%%
-module(ets_SUITE).
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
-export([default/1,setbag/1,badnew/1,verybadnew/1,named/1,keypos2/1,
privacy/1,privacy_owner/2]).
@@ -31,15 +31,14 @@
-export([match_delete3/1]).
-export([firstnext/1,firstnext_concurrent/1]).
-export([slot/1]).
--export([ match1/1, match2/1, match_object/1, match_object2/1]).
--export([ dups/1, misc1/1, safe_fixtable/1, info/1, tab2list/1]).
--export([ tab2file/1, tab2file2/1, tabfile_ext1/1,
- tabfile_ext2/1, tabfile_ext3/1, tabfile_ext4/1, badfile/1]).
--export([ heavy_lookup/1, heavy_lookup_element/1, heavy_concurrent/1]).
--export([ lookup_element_mult/1]).
--export([]).
+-export([match1/1, match2/1, match_object/1, match_object2/1]).
+-export([dups/1, misc1/1, safe_fixtable/1, info/1, tab2list/1]).
+-export([tab2file/1, tab2file2/1, tabfile_ext1/1,
+ tabfile_ext2/1, tabfile_ext3/1, tabfile_ext4/1, badfile/1]).
+-export([heavy_lookup/1, heavy_lookup_element/1, heavy_concurrent/1]).
+-export([lookup_element_mult/1]).
-export([foldl_ordered/1, foldr_ordered/1, foldl/1, foldr/1, fold_empty/1]).
--export([t_delete_object/1, t_init_table/1, t_whitebox/1,
+-export([t_delete_object/1, t_init_table/1, t_whitebox/1,
t_delete_all_objects/1, t_insert_list/1, t_test_ms/1,
t_select_delete/1,t_ets_dets/1]).
@@ -47,6 +46,7 @@
fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1,
update_element/1, update_counter/1, evil_update_counter/1, partly_bound/1, match_heavy/1]).
-export([update_counter_with_default/1]).
+-export([update_counter_table_growth/1]).
-export([member/1]).
-export([memory/1]).
-export([select_fail/1]).
@@ -60,8 +60,7 @@
-export([otp_7665/1]).
-export([meta_wb/1]).
-export([grow_shrink/1, grow_pseudo_deleted/1, shrink_pseudo_deleted/1]).
--export([
- meta_lookup_unnamed_read/1, meta_lookup_unnamed_write/1,
+-export([meta_lookup_unnamed_read/1, meta_lookup_unnamed_write/1,
meta_lookup_named_read/1, meta_lookup_named_write/1,
meta_newdel_unnamed/1, meta_newdel_named/1]).
-export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1,
@@ -94,12 +93,13 @@
rename_do/1, rename_unnamed_do/1, interface_equality_do/1, ordered_match_do/1,
ordered_do/1, privacy_do/1, empty_do/1, badinsert_do/1, time_lookup_do/1,
lookup_order_do/1, lookup_element_mult_do/1, delete_tab_do/1, delete_elem_do/1,
- match_delete_do/1, match_delete3_do/1, firstnext_do/1,
+ match_delete_do/1, match_delete3_do/1, firstnext_do/1,
slot_do/1, match1_do/1, match2_do/1, match_object_do/1, match_object2_do/1,
misc1_do/1, safe_fixtable_do/1, info_do/1, dups_do/1, heavy_lookup_do/1,
heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1,
do_heavy_concurrent/1, tab2file2_do/2, exit_large_table_owner_do/2,
types_do/1, sleeper/0, memory_do/1, update_counter_with_default_do/1,
+ update_counter_table_growth_do/1,
ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4
]).
@@ -127,7 +127,7 @@ suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,5}}].
-all() ->
+all() ->
[{group, new}, {group, insert}, {group, lookup},
{group, delete}, firstnext, firstnext_concurrent, slot,
{group, match}, t_match_spec_run,
@@ -137,6 +137,7 @@ all() ->
rename, rename_unnamed, evil_rename, update_element,
update_counter, evil_update_counter,
update_counter_with_default, partly_bound,
+ update_counter_table_growth,
match_heavy, {group, fold}, member, t_delete_object,
t_init_table, t_whitebox, t_delete_all_objects,
t_insert_list, t_test_ms, t_select_delete, t_ets_dets,
@@ -158,7 +159,7 @@ all() ->
memory_check_summary]. % MUST BE LAST
-groups() ->
+groups() ->
[{new, [],
[default, setbag, badnew, verybadnew, named, keypos2,
privacy]},
@@ -246,6 +247,7 @@ t_bucket_disappears_do(Opts) ->
%% Check ets:match_spec_run/2.
t_match_spec_run(Config) when is_list(Config) ->
+ ct:timetrap({minutes,30}), %% valgrind needs a lot
init_externals(),
EtsMem = etsmem(),
@@ -706,7 +708,7 @@ adjust_xmem([_T1,_T2,_T3,_T4], {A0,B0,C0,D0} = _Mem0) ->
{A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff}.
%% Misc. whitebox tests
-t_whitebox(Config) when is_list(Config) ->
+t_whitebox(Config) when is_list(Config) ->
EtsMem = etsmem(),
repeat_for_opts(whitebox_1),
repeat_for_opts(whitebox_1),
@@ -1047,6 +1049,7 @@ do_reverse_chunked({L,C},Acc) ->
%% Test the ets:select_delete/2 and ets:select_count/2 BIFs.
t_select_delete(Config) when is_list(Config) ->
+ ct:timetrap({minutes,30}), %% valgrind needs a lot
EtsMem = etsmem(),
Tables = fill_sets_int(10000) ++ fill_sets_int(10000,[{write_concurrency,true}]),
lists:foreach
@@ -1492,15 +1495,15 @@ update_element(Config) when is_list(Config) ->
verify_etsmem(EtsMem).
update_element_opts(Opts) ->
- TupleCases = [{{key,val}, 1 ,2},
- {{val,key}, 2, 1},
- {{key,val}, 1 ,[2]},
+ TupleCases = [{{key,val}, 1 ,2},
+ {{val,key}, 2, 1},
+ {{key,val}, 1 ,[2]},
{{key,val,val}, 1, [2,3]},
{{val,key,val,val}, 2, [3,4,1]},
{{val,val,key,val}, 3, [1,4,1,2]}, % update pos1 twice
{{val,val,val,key}, 4, [2,1,2,3]}],% update pos2 twice
- lists:foreach(fun({Tuple,KeyPos,UpdPos}) -> update_element_opts(Tuple,KeyPos,UpdPos,Opts) end,
+ lists:foreach(fun({Tuple,KeyPos,UpdPos}) -> update_element_opts(Tuple,KeyPos,UpdPos,Opts) end,
TupleCases),
update_element_neg(Opts).
@@ -1516,9 +1519,9 @@ update_element_opts(Tuple,KeyPos,UpdPos,Opts) ->
true = ets:delete(OrdSet),
ok.
-update_element(T,Tuple,KeyPos,UpdPos) ->
+update_element(T,Tuple,KeyPos,UpdPos) ->
KeyList = [17,"seventeen",<<"seventeen">>,{17},list_to_binary(lists:seq(1,100)),make_ref(), self()],
- lists:foreach(fun(Key) ->
+ lists:foreach(fun(Key) ->
TupleWithKey = setelement(KeyPos,Tuple,Key),
update_element_do(T,TupleWithKey,Key,UpdPos)
end,
@@ -1553,29 +1556,29 @@ update_element_do(Tab,Tuple,Key,UpdPos) ->
{Pos, element(ToIx+1,Values)} % single {pos,value} arg
end,
- UpdateF = fun(ToIx,Rand) ->
- PosValArg = PosValArgF(ToIx,[],UpdPos,Rand,PosValArgF),
- %%io:format("update_element(~p)~n",[PosValArg]),
- ArgHash = erlang:phash2({Tab,Key,PosValArg}),
- true = ets:update_element(Tab, Key, PosValArg),
- ArgHash = erlang:phash2({Tab,Key,PosValArg}),
- NewTuple = update_tuple(PosValArg,Tuple),
- [NewTuple] = ets:lookup(Tab,Key)
+ UpdateF = fun(ToIx,Rand) ->
+ PosValArg = PosValArgF(ToIx,[],UpdPos,Rand,PosValArgF),
+ %%io:format("update_element(~p)~n",[PosValArg]),
+ ArgHash = erlang:phash2({Tab,Key,PosValArg}),
+ true = ets:update_element(Tab, Key, PosValArg),
+ ArgHash = erlang:phash2({Tab,Key,PosValArg}),
+ NewTuple = update_tuple(PosValArg,Tuple),
+ [NewTuple] = ets:lookup(Tab,Key)
end,
- LoopF = fun(_FromIx, Incr, _Times, Checksum, _MeF) when Incr >= Length ->
+ LoopF = fun(_FromIx, Incr, _Times, Checksum, _MeF) when Incr >= Length ->
Checksum; % done
- (FromIx, Incr, 0, Checksum, MeF) ->
+ (FromIx, Incr, 0, Checksum, MeF) ->
MeF(FromIx, Incr+1, Length, Checksum, MeF);
- (FromIx, Incr, Times, Checksum, MeF) ->
+ (FromIx, Incr, Times, Checksum, MeF) ->
ToIx = (FromIx + Incr) rem Length,
UpdateF(ToIx,Checksum),
- if
+ if
Incr =:= 0 -> UpdateF(ToIx,Checksum); % extra update to same value
true -> true
- end,
+ end,
MeF(ToIx, Incr, Times-1, Checksum+ToIx+1, MeF)
end,
@@ -1619,7 +1622,7 @@ update_element_neg_do(T) ->
Object = {key, 0, "Hej"},
true = ets:insert(T,Object),
- UpdateF = fun(Arg3) ->
+ UpdateF = fun(Arg3) ->
ArgHash = erlang:phash2({T,key,Arg3}),
{'EXIT',{badarg,_}} = (catch ets:update_element(T,key,Arg3)),
ArgHash = erlang:phash2({T,key,Arg3}),
@@ -1694,7 +1697,7 @@ update_counter_for(T) ->
true = ets:lookup(T, b) =:= [setelement(1, NewObj, b)],
ets:delete(T, b),
Myself(NewObj,Times-1,Arg3,Myself)
- end,
+ end,
LoopF = fun(Obj, Times, Arg3) ->
%%io:format("Loop start:\nObj = ~p\nArg3=~p\n",[Obj,Arg3]),
@@ -1803,7 +1806,7 @@ uc_mimic(Obj, [Pits|Tail], Acc) ->
uc_adder(Init, {_Pos, Add}) ->
Init + Add;
-uc_adder(Init, {_Pos, Add, Thres, Warp}) ->
+uc_adder(Init, {_Pos, Add, Thres, Warp}) ->
case Init + Add of
X when X > Thres, Add > 0 ->
Warp;
@@ -1835,7 +1838,7 @@ update_counter_neg_for(T) ->
Object = {key,0,false,1},
true = ets:insert(T,Object),
- UpdateF = fun(Arg3) ->
+ UpdateF = fun(Arg3) ->
ArgHash = erlang:phash2({T,key,Arg3}),
{'EXIT',{badarg,_}} = (catch ets:update_counter(T,key,Arg3)),
ArgHash = erlang:phash2({T,key,Arg3}),
@@ -1955,6 +1958,16 @@ update_counter_with_default_do(Opts) ->
ok.
+update_counter_table_growth(_Config) ->
+ repeat_for_opts(update_counter_table_growth_do).
+
+update_counter_table_growth_do(Opts) ->
+ Set = ets_new(b, [set | Opts]),
+ [ets:update_counter(Set, N, {2, 1}, {N, 1}) || N <- lists:seq(1,10000)],
+ OrderedSet = ets_new(b, [ordered_set | Opts]),
+ [ets:update_counter(OrderedSet, N, {2, 1}, {N, 1}) || N <- lists:seq(1,10000)],
+ ok.
+
%% Check that a first-next sequence always works on a fixed table.
fixtable_next(Config) when is_list(Config) ->
repeat_for_opts(fixtable_next_do, [write_concurrency,all_types]).
@@ -1965,15 +1978,16 @@ fixtable_next_do(Opts) ->
verify_etsmem(EtsMem).
do_fixtable_next(Tab) ->
- F = fun(X,T,FF) -> case X of
- 0 -> true;
- _ ->
- ets:insert(T, {X,
- integer_to_list(X),
- X rem 10}),
- FF(X-1,T,FF)
- end
- end,
+ F = fun(X,T,FF) ->
+ case X of
+ 0 -> true;
+ _ ->
+ ets:insert(T, {X,
+ integer_to_list(X),
+ X rem 10}),
+ FF(X-1,T,FF)
+ end
+ end,
F(100,Tab,F),
ets:safe_fixtable(Tab,true),
First = ets:first(Tab),
@@ -1988,7 +2002,7 @@ do_fixtable_next(Tab) ->
%% Check inserts of deleted keys in fixed bags.
fixtable_insert(Config) when is_list(Config) ->
- Combos = [[Type,{write_concurrency,WC}] || Type<- [bag,duplicate_bag],
+ Combos = [[Type,{write_concurrency,WC}] || Type<- [bag,duplicate_bag],
WC <- [false,true]],
lists:foreach(fun(Opts) -> fixtable_insert_do(Opts) end,
Combos),
@@ -2104,7 +2118,7 @@ heir_do(Opts) ->
%% Different types of heir data and link/monitor relations
TestFun = fun(Arg) -> {EtsMem,Arg} end,
- Combos = [{Data,Mode} || Data<-[foo_data, <<"binary">>,
+ Combos = [{Data,Mode} || Data<-[foo_data, <<"binary">>,
lists:seq(1,10), {17,TestFun,self()},
"The busy heir"],
Mode<-[none,link,monitor]],
@@ -2144,7 +2158,7 @@ heir_do(Opts) ->
Founder4 ! {go, Heir4},
{'DOWN', MrefH4, process, Heir4, normal} = receive_any(),
erts_debug:set_internal_state(next_pid, NextPidIx),
- DoppelGanger = spawn_monitor_with_pid(Heir4,
+ DoppelGanger = spawn_monitor_with_pid(Heir4,
fun()-> die_please = receive_any() end),
Founder4 ! die_please,
{'DOWN', MrefF4, process, Founder4, normal} = receive_any(),
@@ -2157,12 +2171,12 @@ heir_do(Opts) ->
failed ->
io:format("Failed to spawn process with pid ~p\n", [Heir4]),
true % try again
- end
+ end
end),
verify_etsmem(EtsMem).
-heir_founder(Master, HeirData, Opts) ->
+heir_founder(Master, HeirData, Opts) ->
{go,Heir} = receive_any(),
HeirTpl = case Heir of
none -> {heir,none};
@@ -2235,7 +2249,7 @@ heir_1(HeirData,Mode,Opts) ->
{'DOWN', Mref, process, Heir, normal} = receive_any().
%% Test ets:give_way/3.
-give_away(Config) when is_list(Config) ->
+give_away(Config) when is_list(Config) ->
repeat_for_opts(give_away_do).
give_away_do(Opts) ->
@@ -2374,7 +2388,7 @@ bad_table(Config) when is_list(Config) ->
ok.
bad_table_do(Opts, DummyFile) ->
- Parent = self(),
+ Parent = self(),
{Pid,Mref} = my_spawn_opt(fun()-> ets_new(priv,[private,named_table | Opts]),
Priv = ets_new(priv,[private | Opts]),
ets_new(prot,[protected,named_table | Opts]),
@@ -2429,7 +2443,7 @@ bad_table_do(Opts, DummyFile) ->
],
Info = {Opts, Priv, Prot},
lists:foreach(fun(Op) -> bad_table_op(Info, Op) end,
- OpList),
+ OpList),
Pid ! die_please,
{'DOWN', Mref, process, Pid, normal} = receive_any(),
ok.
@@ -2564,14 +2578,14 @@ interface_equality_do(Opts) ->
Set = ets_new(set,[set | Opts]),
OrderedSet = ets_new(ordered_set,[ordered_set | Opts]),
F = fun(X,T,FF) -> case X of
- 0 -> true;
- _ ->
- ets:insert(T, {X,
- integer_to_list(X),
- X rem 10}),
- FF(X-1,T,FF)
- end
- end,
+ 0 -> true;
+ _ ->
+ ets:insert(T, {X,
+ integer_to_list(X),
+ X rem 10}),
+ FF(X-1,T,FF)
+ end
+ end,
F(100,Set,F),
F(100,OrderedSet,F),
equal_results(ets, insert, Set, OrderedSet, [{a,"a"}]),
@@ -2640,20 +2654,20 @@ ordered_match_do(Opts) ->
F(3000,T1,F),
[[3,3],[3,3],[3,3]] = ets:match(T1, {'_','_','$1','$2',3}),
F2 = fun(X,Rem,Res,FF) -> case X of
- 0 -> [];
- _ ->
+ 0 -> [];
+ _ ->
case X rem Rem of
Res ->
FF(X-1,Rem,Res,FF) ++
[{X,
- integer_to_list(X),
+ integer_to_list(X),
X rem 10,
X rem 100,
X rem 1000}];
_ ->
FF(X-1,Rem,Res,FF)
end
- end
+ end
end,
OL1 = F2(3000,100,2,F2),
OL1 = ets:match_object(T1, {'_','_','_',2,'_'}),
@@ -2731,7 +2745,7 @@ pick_all_backwards(T) ->
%% Small test case for both set and bag type ets tables.
-setbag(Config) when is_list(Config) ->
+setbag(Config) when is_list(Config) ->
EtsMem = etsmem(),
Set = ets_new(set,[set]),
Bag = ets_new(bag,[bag]),
@@ -2808,7 +2822,7 @@ privacy_do(Opts) ->
privacy_check(pub,prot,priv),
- Owner ! {shift,1,{pub,prot,priv}},
+ Owner ! {shift,1,{pub,prot,priv}},
receive
{Pub1,Prot1,Priv1} ->
ok = privacy_check(Pub1,Prot1,Priv1),
@@ -2947,7 +2961,7 @@ badlookup(Config) when is_list(Config) ->
verify_etsmem(EtsMem).
%% Test that lookup returns objects in order of insertion for bag and dbag.
-lookup_order(Config) when is_list(Config) ->
+lookup_order(Config) when is_list(Config) ->
EtsMem = etsmem(),
repeat_for_opts(lookup_order_do, [write_concurrency,[bag,duplicate_bag]]),
verify_etsmem(EtsMem),
@@ -2969,7 +2983,7 @@ lookup_order_2(Opts, Fixed) ->
case Fixed of
true -> ets:safe_fixtable(T,true);
false -> ok
- end,
+ end,
S10 = {T,[],key},
S20 = check_insert(S10,A),
S30 = check_insert(S20,B),
@@ -2981,7 +2995,7 @@ lookup_order_2(Opts, Fixed) ->
S80 = check_delete(S70,D2b),
S90 = check_insert(S80,D2a),
SA0 = check_delete(S90,D3a),
- SB0 = check_delete(SA0,D3b),
+ SB0 = check_delete(SA0,D3b),
check_insert_new(SB0,D3b),
true = ets:delete(T)
@@ -2994,7 +3008,7 @@ check_insert({T,List0,Key},Val) ->
ets:insert(T,{Key,Val}),
List1 = case (ets:info(T,type) =:= bag andalso
lists:member({Key,Val},List0)) of
- true -> List0;
+ true -> List0;
false -> [{Key,Val} | List0]
end,
check_check({T,List1,Key}).
@@ -3027,8 +3041,6 @@ check_check(S={T,List,Key}) ->
Items = length(List),
S.
-
-
fill_tab(Tab,Val) ->
ets:insert(Tab,{key,Val}),
ets:insert(Tab,{{a,144},Val}),
@@ -3056,13 +3068,11 @@ lookup_element_mult_do(Opts) ->
verify_etsmem(EtsMem).
lem_data() ->
- [
- {service,'eddie2@boromir',{150,236,14,103},httpd88,self()},
+ [{service,'eddie2@boromir',{150,236,14,103},httpd88,self()},
{service,'eddie2@boromir',{150,236,14,103},httpd80,self()},
{service,'eddie3@boromir',{150,236,14,107},httpd88,self()},
{service,'eddie3@boromir',{150,236,14,107},httpd80,self()},
- {service,'eddie4@boromir',{150,236,14,108},httpd88,self()}
- ].
+ {service,'eddie4@boromir',{150,236,14,108},httpd88,self()}].
lem_crash(T) ->
L = ets:lookup_element(T, 'eddie2@boromir', 3),
@@ -3113,6 +3123,7 @@ delete_tab_do(Opts) ->
%% Check that ets:delete/1 works and that other processes can run.
delete_large_tab(Config) when is_list(Config) ->
+ ct:timetrap({minutes,30}), %% valgrind needs a lot
Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 200000)],
EtsMem = etsmem(),
repeat_for_opts(fun(Opts) -> delete_large_tab_do(Opts,Data) end),
@@ -3135,7 +3146,7 @@ delete_large_tab_1(Name, Flags, Data, Fix) ->
lists:foreach(fun({K,_}) -> ets:delete(Tab, K) end, Data)
end,
- {priority, Prio} = process_info(self(), priority),
+ {priority, Prio} = process_info(self(), priority),
Deleter = self(),
[SchedTracer]
= start_loopers(1,
@@ -3182,7 +3193,7 @@ delete_large_tab_1(Name, Flags, Data, Fix) ->
%% Delete a large name table and try to create a new table with
%% the same name in another process.
-delete_large_named_table(Config) when is_list(Config) ->
+delete_large_named_table(Config) when is_list(Config) ->
Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 200000)],
EtsMem = etsmem(),
repeat_for_opts(fun(Opts) -> delete_large_named_table_do(Opts,Data) end),
@@ -3553,7 +3564,7 @@ dyn_lookup(T) -> dyn_lookup(T, ets:first(T)).
dyn_lookup(_T, '$end_of_table') -> [];
dyn_lookup(T, K) ->
- NextKey=ets:next(T,K),
+ NextKey = ets:next(T,K),
case ets:next(T,K) of
NextKey ->
dyn_lookup(T, NextKey);
@@ -4072,9 +4083,9 @@ tabfile_ext2_do(Opts,Config) ->
Name = make_ref(),
[ets:insert(T,{X,integer_to_list(X)}) || X <- L],
ok = ets:tab2file(T,FName,[{extended_info,[md5sum]}]),
- true = lists:sort(ets:tab2list(T)) =:=
+ true = lists:sort(ets:tab2list(T)) =:=
lists:sort(ets:tab2list(element(2,ets:file2tab(FName)))),
- true = lists:sort(ets:tab2list(T)) =:=
+ true = lists:sort(ets:tab2list(T)) =:=
lists:sort(ets:tab2list(
element(2,ets:file2tab(FName,[{verify,true}])))),
{ok, Name} = disk_log:open([{name,Name},{file,FName}]),
@@ -4089,9 +4100,9 @@ tabfile_ext2_do(Opts,Config) ->
ets:tab2list(
element(2,ets:file2tab(FName2)))),
{error,checksum_error} = ets:file2tab(FName2,[{verify,true}]),
- {value,{extended_info,[md5sum]}} =
+ {value,{extended_info,[md5sum]}} =
lists:keysearch(extended_info,1,element(2,ets:tabfile_info(FName2))),
- {value,{extended_info,[md5sum]}} =
+ {value,{extended_info,[md5sum]}} =
lists:keysearch(extended_info,1,element(2,ets:tabfile_info(FName))),
file:delete(FName),
file:delete(FName2),
@@ -4136,15 +4147,14 @@ tabfile_ext4(Config) when is_list(Config) ->
Name2 = make_ref(),
[ets:insert(TL,{X,integer_to_list(X)}) || X <- LL],
ok = ets:tab2file(TL,FName,[{extended_info,[md5sum]}]),
- {ok, Name2} = disk_log:open([{name, Name2}, {file, FName},
+ {ok, Name2} = disk_log:open([{name, Name2}, {file, FName},
{mode, read_only}]),
{C,[_|_]} = disk_log:chunk(Name2,start),
{_,[_|_]} = disk_log:chunk(Name2,C),
disk_log:close(Name2),
- true = lists:sort(ets:tab2list(TL)) =:=
+ true = lists:sort(ets:tab2list(TL)) =:=
lists:sort(ets:tab2list(element(2,ets:file2tab(FName)))),
- Res = [
- begin
+ Res = [begin
{ok,FD} = file:open(FName,[binary,read,write]),
{ok, Bin} = file:pread(FD,0,1000),
<<B1:N/binary,Ch:8,B2/binary>> = Bin,
@@ -4154,7 +4164,7 @@ tabfile_ext4(Config) when is_list(Config) ->
ok = file:close(FD),
X = case ets:file2tab(FName) of
{ok,TL2} ->
- true = lists:sort(ets:tab2list(TL)) =/=
+ true = lists:sort(ets:tab2list(TL)) =/=
lists:sort(ets:tab2list(TL2));
_ ->
totally_broken
@@ -4162,7 +4172,7 @@ tabfile_ext4(Config) when is_list(Config) ->
{error,Y} = ets:file2tab(FName,[{verify,true}]),
ets:tab2file(TL,FName,[{extended_info,[md5sum]}]),
{X,Y}
- end || N <- lists:seq(500,600) ],
+ end || N <- lists:seq(500,600)],
io:format("~p~n",[Res]),
file:delete(FName),
ok.
@@ -4391,16 +4401,14 @@ member_do(Opts) ->
build_table(L1,L2,Num) ->
- T = ets_new(xxx, [ordered_set]
- ),
+ T = ets_new(xxx, [ordered_set]),
lists:foreach(
fun(X1) ->
lists:foreach(
fun(X2) ->
F = fun(FF,N) ->
- ets:insert(T,{{X1,X2,N},
- X1, X2, N}),
- case N of
+ ets:insert(T,{{X1,X2,N}, X1, X2, N}),
+ case N of
0 ->
ok;
_ ->
@@ -4413,16 +4421,14 @@ build_table(L1,L2,Num) ->
T.
build_table2(L1,L2,Num) ->
- T = ets_new(xxx, [ordered_set]
- ),
+ T = ets_new(xxx, [ordered_set]),
lists:foreach(
fun(X1) ->
lists:foreach(
fun(X2) ->
F = fun(FF,N) ->
- ets:insert(T,{{N,X1,X2},
- N, X1, X2}),
- case N of
+ ets:insert(T,{{N,X1,X2}, N, X1, X2}),
+ case N of
0 ->
ok;
_ ->
@@ -4713,7 +4719,7 @@ del_one_by_one_dbag_3(T,From,To) ->
N = (ets:info(T,size) + 1),
Obj2 = {From, integer_to_list(From)},
ets:delete_object(T,Obj2),
- N = (ets:info(T,size) + 2)
+ N = (ets:info(T,size) + 2)
end,
Next = if
From < To ->
@@ -4760,14 +4766,14 @@ gen_dets_filename(Config,N) ->
filename:join(proplists:get_value(priv_dir,Config),
"testdets_" ++ integer_to_list(N) ++ ".dets").
-otp_6842_select_1000(Config) when is_list(Config) ->
+otp_6842_select_1000(Config) when is_list(Config) ->
Tab = ets_new(xxx,[ordered_set]),
[ets:insert(Tab,{X,X}) || X <- lists:seq(1,10000)],
AllTrue = lists:duplicate(10,true),
AllTrue =
[ length(
element(1,
- ets:select(Tab,[{'_',[],['$_']}],X*1000))) =:=
+ ets:select(Tab,[{'_',[],['$_']}],X*1000))) =:=
X*1000 || X <- lists:seq(1,10) ],
Sequences = [[1000,1000,1000,1000,1000,1000,1000,1000,1000,1000],
[2000,2000,2000,2000,2000],
@@ -4793,7 +4799,13 @@ check_seq(A,B,C) ->
false.
otp_6338(Config) when is_list(Config) ->
- L = binary_to_term(<<131,108,0,0,0,2,104,2,108,0,0,0,2,103,100,0,19,112,112,98,49,95,98,115,49,50,64,98,108,97,100,101,95,48,95,53,0,0,33,50,0,0,0,4,1,98,0,0,23,226,106,100,0,4,101,120,105,116,104,2,108,0,0,0,2,104,2,100,0,3,115,98,109,100,0,19,112,112,98,50,95,98,115,49,50,64,98,108,97,100,101,95,48,95,56,98,0,0,18,231,106,100,0,4,114,101,99,118,106>>),
+ L = binary_to_term(<<131,108,0,0,0,2,104,2,108,0,0,0,2,103,100,0,19,112,112,
+ 98,49,95,98,115,49,50,64,98,108,97,100,101,95,48,95,53,
+ 0,0,33,50,0,0,0,4,1,98,0,0,23,226,106,100,0,4,101,120,
+ 105,116,104,2,108,0,0,0,2,104,2,100,0,3,115,98,109,100,
+ 0,19,112,112,98,50,95,98,115,49,50,64,98,108,97,100,
+ 101,95,48,95,56,98,0,0,18,231,106,100,0,4,114,101,99,
+ 118,106>>),
T = ets_new(xxx,[ordered_set]),
lists:foreach(fun(X) -> ets:insert(T,X) end,L),
[[4839,recv]] = ets:match(T,{[{sbm,ppb2_bs12@blade_0_8},'$1'],'$2'}),
@@ -4812,7 +4824,7 @@ otp_5340_do(Opts) ->
ets:delete(T).
w(_,0, _) -> ok;
-w(T,N, Id) ->
+w(T,N, Id) ->
ets:insert(T, {N, Id}),
w(T,N-1,Id).
@@ -4902,7 +4914,7 @@ meta_wb_new(Name, _, Tabs, Opts) ->
case (catch ets_new(Name,[named_table|Opts])) of
Name ->
false = lists:member(Name, Tabs),
- [Name | Tabs];
+ [Name | Tabs];
{'EXIT',{badarg,_}} ->
true = lists:member(Name, Tabs),
Tabs
@@ -5077,7 +5089,7 @@ meta_lookup_unnamed_read(Config) when is_list(Config) ->
Tab
end,
ExecF = fun(Tab) -> [{key,data}] = ets:lookup(Tab,key),
- Tab
+ Tab
end,
FiniF = fun(Tab) -> true = ets:delete(Tab)
end,
@@ -5101,7 +5113,7 @@ meta_lookup_named_read(Config) when is_list(Config) ->
Tab
end,
ExecF = fun(Tab) -> [{key,data}] = ets:lookup(Tab,key),
- Tab
+ Tab
end,
FiniF = fun(Tab) -> true = ets:delete(Tab)
end,
@@ -5160,9 +5172,9 @@ smp_fixed_delete_do() ->
ets:safe_fixtable(T,true),
Buckets = num_of_buckets(T),
InitF = fun([ProcN,NumOfProcs|_]) -> {ProcN,NumOfProcs} end,
- ExecF = fun({Key,_}) when Key > NumOfObjs ->
+ ExecF = fun({Key,_}) when Key > NumOfObjs ->
[end_of_work];
- ({Key,Increment}) ->
+ ({Key,Increment}) ->
true = ets:delete(T,Key),
{Key+Increment,Increment}
end,
@@ -5191,7 +5203,7 @@ smp_unfix_fix_do() ->
T = ets_new(foo,[public,{write_concurrency,true}]),
%%Mem = ets:info(T,memory),
NumOfObjs = 100000,
- Deleted = 50000,
+ Deleted = 50000,
filltabint(T,NumOfObjs),
ets:safe_fixtable(T,true),
Buckets = num_of_buckets(T),
@@ -5204,7 +5216,7 @@ smp_unfix_fix_do() ->
true = ets:info(T,fixed),
Deleted = get_kept_objects(T),
- {Child, Mref} =
+ {Child, Mref} =
my_spawn_opt(
fun()->
true = ets:info(T,fixed),
@@ -5263,22 +5275,19 @@ otp_8166_do(WC) ->
NumOfObjs = 3000, %% Need more than 1000 live objects for match_object to trap one time
Deleted = NumOfObjs div 2,
filltabint(T,NumOfObjs),
- {ReaderPid, ReaderMref} =
- my_spawn_opt(fun()-> otp_8166_reader(T,NumOfObjs) end,
- [link, monitor, {scheduler,2}]),
- {ZombieCrPid, ZombieCrMref} =
- my_spawn_opt(fun()-> otp_8166_zombie_creator(T,Deleted) end,
- [link, monitor, {scheduler,3}]),
+ {ReaderPid, ReaderMref} = my_spawn_opt(fun()-> otp_8166_reader(T,NumOfObjs) end,
+ [link, monitor, {scheduler,2}]),
+ {ZombieCrPid, ZombieCrMref} = my_spawn_opt(fun()-> otp_8166_zombie_creator(T,Deleted) end,
+ [link, monitor, {scheduler,3}]),
repeat(fun() -> ZombieCrPid ! {loop, self()},
zombies_created = receive_any(),
otp_8166_trapper(T, 10, ZombieCrPid)
- end,
- 100),
+ end, 100),
ReaderPid ! quit,
{'DOWN', ReaderMref, process, ReaderPid, normal} = receive_any(),
- ZombieCrPid ! quit,
+ ZombieCrPid ! quit,
{'DOWN', ZombieCrMref, process, ZombieCrPid, normal} = receive_any(),
false = ets:info(T,fixed),
0 = get_kept_objects(T),
@@ -5288,7 +5297,7 @@ otp_8166_do(WC) ->
%% Keep reading the table
otp_8166_reader(T, NumOfObjs) ->
- repeat_while(fun(0) ->
+ repeat_while(fun(0) ->
receive quit -> {false,done}
after 0 -> {true,NumOfObjs}
end;
@@ -5302,14 +5311,14 @@ otp_8166_reader(T, NumOfObjs) ->
otp_8166_trapper(T, Try, ZombieCrPid) ->
[] = ets:match_object(T,{'_',"Pink Unicorn"}),
case {ets:info(T,fixed),Try} of
- {true,1} ->
+ {true,1} ->
io:format("failed to provoke unsafe unfix, give up...\n",[]),
ZombieCrPid ! unfix;
- {true,_} ->
+ {true,_} ->
io:format("trapper too fast, trying again...\n",[]),
otp_8166_trapper(T, Try-1, ZombieCrPid);
{false,_} -> done
- end.
+ end.
%% Fixate table and create some pseudo-deleted objects (zombies)
@@ -5329,7 +5338,7 @@ otp_8166_zombie_creator(T,Deleted) ->
repeat_while(fun() -> case ets:info(T,safe_fixed_monotonic_time) of
{_,[_P1,_P2]} ->
false;
- _ ->
+ _ ->
receive unfix -> false
after 0 -> true
end
@@ -5386,7 +5395,7 @@ smp_select_delete(Config) when is_list(Config) ->
Mod = 17,
Zeros = erlang:make_tuple(Mod,0),
InitF = fun(_) -> Zeros end,
- ExecF = fun(Diffs0) ->
+ ExecF = fun(Diffs0) ->
case rand:uniform(20) of
1 ->
Mod = 17,
@@ -5408,7 +5417,7 @@ smp_select_delete(Config) when is_list(Config) ->
Diffs1;
false -> Diffs0
end
- end
+ end
end,
FiniF = fun(Result) -> Result end,
Results = run_workers_do(InitF,ExecF,FiniF,20000),
@@ -5419,7 +5428,7 @@ smp_select_delete(Config) when is_list(Config) ->
0, TotCnts),
io:format("LeftInTab = ~p\n",[LeftInTab]),
LeftInTab = ets:info(T,size),
- lists:foldl(fun(Cnt,Eq) ->
+ lists:foldl(fun(Cnt,Eq) ->
WasCnt = ets:select_count(T,
[{{'_', '$1'},
[{'=:=', {'rem', '$1', Mod}, Eq}],
@@ -5427,7 +5436,7 @@ smp_select_delete(Config) when is_list(Config) ->
io:format("~p: ~p =?= ~p\n",[Eq,Cnt,WasCnt]),
Cnt = WasCnt,
Eq+1
- end,
+ end,
0, TotCnts),
verify_table_load(T),
LeftInTab = ets:select_delete(T, [{{'$1','$1'}, [], [true]}]),
@@ -5465,8 +5474,8 @@ types_do(Opts) ->
%% OTP-9932: Memory overwrite when inserting large integers in compressed bag.
%% Will crash with segv on 64-bit opt if not fixed.
otp_9932(Config) when is_list(Config) ->
- T = ets:new(xxx, [bag, compressed]),
- Fun = fun(N) ->
+ T = ets:new(xxx, [bag, compressed]),
+ Fun = fun(N) ->
Key = {1316110174588445 bsl N,1316110174588583 bsl N},
S = {Key, Key},
true = ets:insert(T, S),
@@ -5482,9 +5491,9 @@ otp_9932(Config) when is_list(Config) ->
%% write_concurrency table.
otp_9423(Config) when is_list(Config) ->
InitF = fun(_) -> {0,0} end,
- ExecF = fun({S,F}) ->
- receive
- stop ->
+ ExecF = fun({S,F}) ->
+ receive
+ stop ->
io:format("~p got stop\n", [self()]),
[end_of_work | {"Succeded=",S,"Failed=",F}]
after 0 ->
@@ -5580,12 +5589,12 @@ take(Config) when is_list(Config) ->
%% Utility functions:
%%
-add_lists(L1,L2) ->
+add_lists(L1,L2) ->
add_lists(L1,L2,[]).
add_lists([],[],Acc) ->
lists:reverse(Acc);
add_lists([E1|T1], [E2|T2], Acc) ->
- add_lists(T1, T2, [E1+E2 | Acc]).
+ add_lists(T1, T2, [E1+E2 | Acc]).
run_workers(InitF,ExecF,FiniF,Laps) ->
run_workers(InitF,ExecF,FiniF,Laps, 0).
@@ -5631,9 +5640,9 @@ worker_loop(infinite, ExecF, State) ->
worker_loop(N, ExecF, State) ->
worker_loop(N-1,ExecF,ExecF(State)).
-wait_pids(Pids) ->
+wait_pids(Pids) ->
wait_pids(Pids,[]).
-wait_pids([],Acc) ->
+wait_pids([],Acc) ->
Acc;
wait_pids(Pids, Acc) ->
receive
@@ -5670,7 +5679,7 @@ etsmem() ->
wait_for_memory_deallocations(),
AllTabs = lists:map(fun(T) -> {T,ets:info(T,name),ets:info(T,size),
- ets:info(T,memory),ets:info(T,type)}
+ ets:info(T,memory),ets:info(T,type)}
end, ets:all()),
EtsAllocInfo = erlang:system_info({allocator,ets_alloc}),
@@ -5882,7 +5891,7 @@ receive_any() ->
receive_any_spinning() ->
receive_any_spinning(1000000).
receive_any_spinning(Loops) ->
- receive_any_spinning(Loops,Loops,1).
+ receive_any_spinning(Loops,Loops,1).
receive_any_spinning(Loops,0,Tries) ->
receive M ->
io:format("Spinning process ~p got msg ~p after ~p tries\n", [self(),M,Tries]),
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index b7c4d3a6e5..d48b75fde8 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. 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.
@@ -29,6 +29,7 @@
dirname_bin/1, extension_bin/1, join_bin/1, t_nativename_bin/1]).
-export([pathtype_bin/1,rootname_bin/1,split_bin/1]).
-export([t_basedir_api/1, t_basedir_xdg/1, t_basedir_windows/1]).
+-export([safe_relative_path/1]).
-include_lib("common_test/include/ct.hrl").
@@ -41,7 +42,8 @@ all() ->
find_src,
absname_bin, absname_bin_2,
{group,p},
- t_basedir_xdg, t_basedir_windows].
+ t_basedir_xdg, t_basedir_windows,
+ safe_relative_path].
groups() ->
[{p, [parallel],
@@ -768,6 +770,71 @@ t_nativename_bin(Config) when is_list(Config) ->
filename:nativename(<<"/usr/tmp//arne/">>)
end.
+safe_relative_path(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Root = filename:join(PrivDir, ?FUNCTION_NAME),
+ ok = file:make_dir(Root),
+ ok = file:set_cwd(Root),
+
+ ok = file:make_dir("a"),
+ ok = file:set_cwd("a"),
+ ok = file:make_dir("b"),
+ ok = file:set_cwd("b"),
+ ok = file:make_dir("c"),
+
+ ok = file:set_cwd(Root),
+
+ "a" = test_srp("a"),
+ "a/b" = test_srp("a/b"),
+ "a/b" = test_srp("a/./b"),
+ "a/b" = test_srp("a/./b/."),
+
+ "" = test_srp("a/.."),
+ "" = test_srp("a/./.."),
+ "" = test_srp("a/../."),
+ "a" = test_srp("a/b/.."),
+ "a" = test_srp("a/../a"),
+ "a" = test_srp("a/../a/../a"),
+ "a/b/c" = test_srp("a/../a/b/c"),
+
+ unsafe = test_srp("a/../.."),
+ unsafe = test_srp("a/../../.."),
+ unsafe = test_srp("a/./../.."),
+ unsafe = test_srp("a/././../../.."),
+ unsafe = test_srp("a/b/././../../.."),
+
+ unsafe = test_srp(PrivDir), %Absolute path.
+
+ ok.
+
+test_srp(RelPath) ->
+ Res = do_test_srp(RelPath),
+ Res = case do_test_srp(list_to_binary(RelPath)) of
+ Bin when is_binary(Bin) ->
+ binary_to_list(Bin);
+ Other ->
+ Other
+ end.
+
+do_test_srp(RelPath) ->
+ {ok,Root} = file:get_cwd(),
+ ok = file:set_cwd(RelPath),
+ {ok,Cwd} = file:get_cwd(),
+ ok = file:set_cwd(Root),
+ case filename:safe_relative_path(RelPath) of
+ unsafe ->
+ true = length(Cwd) < length(Root),
+ unsafe;
+ "" ->
+ "";
+ SafeRelPath ->
+ ok = file:set_cwd(SafeRelPath),
+ {ok,Cwd} = file:get_cwd(),
+ true = length(Cwd) >= length(Root),
+ ok = file:set_cwd(Root),
+ SafeRelPath
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% basedirs
t_basedir_api(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 364314f91b..99ac0c3951 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2016. All Rights Reserved.
+%% Copyright Ericsson AB 2016-2017. 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.
@@ -37,33 +37,33 @@ all() ->
{group, stop_handle_event},
{group, abnormal},
{group, abnormal_handle_event},
- shutdown, stop_and_reply, event_order,
+ shutdown, stop_and_reply, state_enter, event_order,
+ state_timeout, event_types, code_change,
{group, sys},
hibernate, enter_loop].
groups() ->
- [{start, [],
- [start1, start2, start3, start4, start5, start6, start7,
- start8, start9, start10, start11, start12, next_events]},
- {start_handle_event, [],
- [start1, start2, start3, start4, start5, start6, start7,
- start8, start9, start10, start11, start12, next_events]},
- {stop, [],
- [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]},
- {stop_handle_event, [],
- [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]},
- {abnormal, [], [abnormal1, abnormal2]},
- {abnormal_handle_event, [], [abnormal1, abnormal2]},
- {sys, [],
- [sys1, code_change,
- call_format_status,
- error_format_status, terminate_crash_format,
- get_state, replace_state]},
- {sys_handle_event, [],
- [sys1,
- call_format_status,
- error_format_status, terminate_crash_format,
- get_state, replace_state]}].
+ [{start, [], tcs(start)},
+ {start_handle_event, [], tcs(start)},
+ {stop, [], tcs(stop)},
+ {stop_handle_event, [], tcs(stop)},
+ {abnormal, [], tcs(abnormal)},
+ {abnormal_handle_event, [], tcs(abnormal)},
+ {sys, [], tcs(sys)},
+ {sys_handle_event, [], tcs(sys)}].
+
+tcs(start) ->
+ [start1, start2, start3, start4, start5, start6, start7,
+ start8, start9, start10, start11, start12, next_events];
+tcs(stop) ->
+ [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10];
+tcs(abnormal) ->
+ [abnormal1, abnormal1clean, abnormal1dirty, abnormal2];
+tcs(sys) ->
+ [sys1, call_format_status,
+ error_format_status, terminate_crash_format,
+ get_state, replace_state].
+
init_per_suite(Config) ->
Config.
@@ -452,8 +452,52 @@ abnormal1(Config) ->
gen_statem:call(Name, {delayed_answer,1000}, 10),
Reason),
ok = gen_statem:stop(Name),
+ ?t:sleep(1100),
+ ok = verify_empty_msgq().
+
+%% Check that time outs in calls work
+abnormal1clean(Config) ->
+ Name = abnormal1clean,
+ LocalSTM = {local,Name},
+
+ {ok, _Pid} =
+ gen_statem:start(LocalSTM, ?MODULE, start_arg(Config, []), []),
+
+ %% timeout call.
+ delayed =
+ gen_statem:call(Name, {delayed_answer,1}, {clean_timeout,100}),
+ {timeout,_} =
+ ?EXPECT_FAILURE(
+ gen_statem:call(
+ Name, {delayed_answer,1000}, {clean_timeout,10}),
+ Reason),
+ ok = gen_statem:stop(Name),
+ ?t:sleep(1100),
ok = verify_empty_msgq().
+%% Check that time outs in calls work
+abnormal1dirty(Config) ->
+ Name = abnormal1dirty,
+ LocalSTM = {local,Name},
+
+ {ok, _Pid} =
+ gen_statem:start(LocalSTM, ?MODULE, start_arg(Config, []), []),
+
+ %% timeout call.
+ delayed =
+ gen_statem:call(Name, {delayed_answer,1}, {dirty_timeout,100}),
+ {timeout,_} =
+ ?EXPECT_FAILURE(
+ gen_statem:call(
+ Name, {delayed_answer,1000}, {dirty_timeout,10}),
+ Reason),
+ ok = gen_statem:stop(Name),
+ ?t:sleep(1100),
+ case flush() of
+ [{Ref,delayed}] when is_reference(Ref) ->
+ ok
+ end.
+
%% Check that bad return values makes the stm crash. Note that we must
%% trap exit since we must link to get the real bad_return_ error
abnormal2(Config) ->
@@ -461,10 +505,10 @@ abnormal2(Config) ->
{ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),
%% bad return value in the gen_statem loop
- {{bad_return_value,badreturn},_} =
+ {{bad_return_from_state_function,badreturn},_} =
?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),
receive
- {'EXIT',Pid,{bad_return_value,badreturn}} -> ok
+ {'EXIT',Pid,{bad_return_from_state_function,badreturn}} -> ok
after 5000 ->
ct:fail(gen_statem_did_not_die)
end,
@@ -513,7 +557,8 @@ stop_and_reply(_Config) ->
{stop_and_reply,Reason,
[R1,{reply,From2,Reply2}]}
end},
- {ok,STM} = gen_statem:start_link(?MODULE, {map_statem,Machine}, []),
+ {ok,STM} =
+ gen_statem:start_link(?MODULE, {map_statem,Machine,[]}, []),
Self = self(),
Tag1 = make_ref(),
@@ -538,6 +583,76 @@ stop_and_reply(_Config) ->
+state_enter(_Config) ->
+ process_flag(trap_exit, true),
+ Self = self(),
+
+ Machine =
+ %% Abusing the internal format of From...
+ #{init =>
+ fun () ->
+ {ok,start,1}
+ end,
+ start =>
+ fun (enter, Prev, N) ->
+ Self ! {enter,start,Prev,N},
+ {keep_state,N + 1};
+ (internal, Prev, N) ->
+ Self ! {internal,start,Prev,N},
+ {keep_state,N + 1};
+ ({call,From}, repeat, N) ->
+ {repeat_state,N + 1,
+ [{reply,From,{repeat,start,N}}]};
+ ({call,From}, echo, N) ->
+ {next_state,wait,N + 1,
+ {reply,From,{echo,start,N}}};
+ ({call,From}, {stop,Reason}, N) ->
+ {stop_and_reply,Reason,
+ [{reply,From,{stop,N}}],N + 1}
+ end,
+ wait =>
+ fun (enter, Prev, N) when N < 5 ->
+ {repeat_state,N + 1,
+ {reply,{Self,N},{enter,Prev}}};
+ (enter, Prev, N) ->
+ Self ! {enter,wait,Prev,N},
+ {keep_state,N + 1};
+ ({call,From}, repeat, N) ->
+ {repeat_state_and_data,
+ [{reply,From,{repeat,wait,N}}]};
+ ({call,From}, echo, N) ->
+ {next_state,start,N + 1,
+ [{next_event,internal,wait},
+ {reply,From,{echo,wait,N}}]}
+ end},
+ {ok,STM} =
+ gen_statem:start_link(
+ ?MODULE, {map_statem,Machine,[state_enter]}, []),
+
+ [{enter,start,start,1}] = flush(),
+ {echo,start,2} = gen_statem:call(STM, echo),
+ [{3,{enter,start}},{4,{enter,start}},{enter,wait,start,5}] = flush(),
+ {wait,[6|_]} = sys:get_state(STM),
+ {repeat,wait,6} = gen_statem:call(STM, repeat),
+ [{enter,wait,wait,6}] = flush(),
+ {echo,wait,7} = gen_statem:call(STM, echo),
+ [{enter,start,wait,8},{internal,start,wait,9}] = flush(),
+ {repeat,start,10} = gen_statem:call(STM, repeat),
+ [{enter,start,start,11}] = flush(),
+ {stop,12} = gen_statem:call(STM, {stop,bye}),
+ [{'EXIT',STM,bye}] = flush(),
+
+ {noproc,_} =
+ ?EXPECT_FAILURE(gen_statem:call(STM, hej), Reason),
+ case flush() of
+ [] ->
+ ok;
+ Other2 ->
+ ct:fail({unexpected,Other2})
+ end.
+
+
+
event_order(_Config) ->
process_flag(trap_exit, true),
@@ -580,7 +695,7 @@ event_order(_Config) ->
Result
end},
- {ok,STM} = gen_statem:start_link(?MODULE, {map_statem,Machine}, []),
+ {ok,STM} = gen_statem:start_link(?MODULE, {map_statem,Machine,[]}, []),
Self = self(),
Tag1 = make_ref(),
gen_statem:cast(STM, {reply,{Self,Tag1},ok1}),
@@ -610,6 +725,165 @@ event_order(_Config) ->
+state_timeout(_Config) ->
+ process_flag(trap_exit, true),
+
+ Machine =
+ #{init =>
+ fun () ->
+ {ok,start,0}
+ end,
+ start =>
+ fun
+ ({call,From}, {go,Time}, 0) ->
+ self() ! message_to_self,
+ {next_state, state1, {Time,From},
+ %% Verify that internal events goes before external
+ [{state_timeout,Time,1},
+ {next_event,internal,1}]}
+ end,
+ state1 =>
+ fun
+ (internal, 1, Data) ->
+ %% Verify that a state change cancels timeout 1
+ {next_state, state2, Data,
+ [{timeout,0,2},
+ {state_timeout,0,2},
+ {next_event,internal,2}]}
+ end,
+ state2 =>
+ fun
+ (internal, 2, Data) ->
+ %% Verify that {state_timeout,0,_}
+ %% comes after next_event and that
+ %% {timeout,0,_} is cancelled by
+ %% pending {state_timeout,0,_}
+ {keep_state, {ok,2,Data},
+ [{timeout,0,3}]};
+ (state_timeout, 2, {ok,2,Data}) ->
+ %% Verify that timeout 0's are processed
+ %% in order
+ {keep_state, {ok,3,Data},
+ [{timeout,0,4},{state_timeout,0,5}]};
+ (timeout, 4, {ok,3,Data}) ->
+ %% Verify that timeout 0 is cancelled by
+ %% enqueued state_timeout 0 and that
+ %% multiple state_timeout 0 can be enqueued
+ {keep_state, {ok,4,Data},
+ [{state_timeout,0,6},{timeout,0,7}]};
+ (state_timeout, 5, {ok,4,Data}) ->
+ {keep_state, {ok,5,Data}};
+ (state_timeout, 6, {ok,5,{Time,From}}) ->
+ {next_state, state3, 6,
+ [{reply,From,ok},
+ {state_timeout,Time,8}]}
+ end,
+ state3 =>
+ fun
+ (info, message_to_self, 6) ->
+ {keep_state, 7};
+ ({call,From}, check, 7) ->
+ {keep_state, From};
+ (state_timeout, 8, From) ->
+ {stop_and_reply, normal,
+ {reply,From,ok}}
+ end},
+
+ {ok,STM} = gen_statem:start_link(?MODULE, {map_statem,Machine,[]}, []),
+ sys:trace(STM, true),
+ TRef = erlang:start_timer(1000, self(), kull),
+ ok = gen_statem:call(STM, {go,500}),
+ ok = gen_statem:call(STM, check),
+ receive
+ {timeout,TRef,kull} ->
+ ct:fail(late_timeout)
+ after 0 ->
+ receive
+ {timeout,TRef,kull} ->
+ ok
+ after 1000 ->
+ ct:fail(no_check_timeout)
+ end
+ end,
+ receive
+ {'EXIT',STM,normal} ->
+ ok
+ after 500 ->
+ ct:fail(did_not_stop)
+ end,
+
+ verify_empty_msgq().
+
+
+
+%% Test that all event types can be sent with {next_event,EventType,_}
+event_types(_Config) ->
+ process_flag(trap_exit, true),
+
+ Machine =
+ %% Abusing the internal format of From...
+ #{init =>
+ fun () ->
+ {ok, start, undefined}
+ end,
+ start =>
+ fun ({call,_} = Call, Req, undefined) ->
+ {next_state, state1, undefined,
+ [{next_event,internal,1},
+ {next_event,state_timeout,2},
+ {next_event,timeout,3},
+ {next_event,info,4},
+ {next_event,cast,5},
+ {next_event,Call,Req}]}
+ end,
+ state1 =>
+ fun (internal, 1, undefined) ->
+ {next_state, state2, undefined}
+ end,
+ state2 =>
+ fun (state_timeout, 2, undefined) ->
+ {next_state, state3, undefined}
+ end,
+ state3 =>
+ fun (timeout, 3, undefined) ->
+ {next_state, state4, undefined}
+ end,
+ state4 =>
+ fun (info, 4, undefined) ->
+ {next_state, state5, undefined}
+ end,
+ state5 =>
+ fun (cast, 5, undefined) ->
+ {next_state, state6, undefined}
+ end,
+ state6 =>
+ fun ({call,From}, stop, undefined) ->
+ {stop_and_reply, shutdown,
+ [{reply,From,stopped}]}
+ end},
+ {ok,STM} =
+ gen_statem:start_link(
+ ?MODULE, {map_statem,Machine,[]}, [{debug,[trace]}]),
+
+ stopped = gen_statem:call(STM, stop),
+ receive
+ {'EXIT',STM,shutdown} ->
+ ok
+ after 500 ->
+ ct:fail(did_not_stop)
+ end,
+
+ {noproc,_} =
+ ?EXPECT_FAILURE(gen_statem:call(STM, hej), Reason),
+ case flush() of
+ [] ->
+ ok;
+ Other2 ->
+ ct:fail({unexpected,Other2})
+ end.
+
+
+
sys1(Config) ->
{ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
{status, Pid, {module,gen_statem}, _} = sys:get_status(Pid),
@@ -633,11 +907,13 @@ sys1(Config) ->
sys:resume(Pid),
stop_it(Pid).
-code_change(Config) ->
- Mode = handle_event_function,
- {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+code_change(_Config) ->
+ {ok,Pid} =
+ gen_statem:start(
+ ?MODULE, {callback_mode,state_functions,[]}, []),
{idle,data} = sys:get_state(Pid),
sys:suspend(Pid),
+ Mode = handle_event_function,
sys:change_code(Pid, ?MODULE, old_vsn, Mode),
sys:resume(Pid),
{idle,{old_vsn,data,Mode}} = sys:get_state(Pid),
@@ -708,7 +984,7 @@ error_format_status(Config) ->
gen_statem:start(
?MODULE, start_arg(Config, {data,Data}), []),
%% bad return value in the gen_statem loop
- {{bad_return_value,badreturn},_} =
+ {{bad_return_from_state_function,badreturn},_} =
?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),
receive
{error,_,
@@ -716,7 +992,7 @@ error_format_status(Config) ->
"** State machine"++_,
[Pid,{{call,_},badreturn},
{formatted,idle,Data},
- error,{bad_return_value,badreturn}|_]}} ->
+ error,{bad_return_from_state_function,badreturn}|_]}} ->
ok;
Other when is_tuple(Other), element(1, Other) =:= error ->
error_logger_forwarder:unregister(),
@@ -1029,11 +1305,7 @@ enter_loop(_Config) ->
end,
%% Process not started using proc_lib
- CallbackMode = state_functions,
- Pid4 =
- spawn_link(
- gen_statem, enter_loop,
- [?MODULE,[],CallbackMode,state0,[]]),
+ Pid4 = spawn_link(gen_statem, enter_loop, [?MODULE,[],state0,[]]),
receive
{'EXIT',Pid4,process_was_not_started_by_proc_lib} ->
ok
@@ -1107,21 +1379,18 @@ enter_loop(Reg1, Reg2) ->
anon -> ignore
end,
proc_lib:init_ack({ok, self()}),
- CallbackMode = state_functions,
case Reg2 of
local ->
gen_statem:enter_loop(
- ?MODULE, [], CallbackMode, state0, [], {local,armitage});
+ ?MODULE, [], state0, [], {local,armitage});
global ->
gen_statem:enter_loop(
- ?MODULE, [], CallbackMode, state0, [], {global,armitage});
+ ?MODULE, [], state0, [], {global,armitage});
via ->
gen_statem:enter_loop(
- ?MODULE, [], CallbackMode, state0, [],
- {via, dummy_via, armitage});
+ ?MODULE, [], state0, [], {via, dummy_via, armitage});
anon ->
- gen_statem:enter_loop(
- ?MODULE, [], CallbackMode, state0, [])
+ gen_statem:enter_loop(?MODULE, [], state0, [])
end.
@@ -1266,33 +1535,39 @@ init(stop_shutdown) ->
{stop,shutdown};
init(sleep) ->
?t:sleep(1000),
- {state_functions,idle,data};
+ {ok,idle,data};
init(hiber) ->
- {state_functions,hiber_idle,[]};
+ {ok,hiber_idle,[]};
init(hiber_now) ->
- {state_functions,hiber_idle,[],[hibernate]};
+ {ok,hiber_idle,[],[hibernate]};
init({data, Data}) ->
- {state_functions,idle,Data};
+ {ok,idle,Data};
init({callback_mode,CallbackMode,Arg}) ->
- case init(Arg) of
- {_,State,Data,Ops} ->
- {CallbackMode,State,Data,Ops};
- {_,State,Data} ->
- {CallbackMode,State,Data};
- Other ->
- Other
- end;
-init({map_statem,#{init := Init}=Machine}) ->
+ ets:new(?MODULE, [named_table,private]),
+ ets:insert(?MODULE, {callback_mode,CallbackMode}),
+ init(Arg);
+init({map_statem,#{init := Init}=Machine,Modes}) ->
+ ets:new(?MODULE, [named_table,private]),
+ ets:insert(?MODULE, {callback_mode,[handle_event_function|Modes]}),
case Init() of
{ok,State,Data,Ops} ->
- {handle_event_function,State,[Data|Machine],Ops};
+ {ok,State,[Data|Machine],Ops};
{ok,State,Data} ->
- {handle_event_function,State,[Data|Machine]};
+ {ok,State,[Data|Machine]};
Other ->
Other
end;
init([]) ->
- {state_functions,idle,data}.
+ {ok,idle,data}.
+
+callback_mode() ->
+ try ets:lookup(?MODULE, callback_mode) of
+ [{callback_mode,CallbackMode}] ->
+ CallbackMode
+ catch
+ error:badarg ->
+ state_functions
+ end.
terminate(_, _State, crash_terminate) ->
exit({crash,terminate});
@@ -1530,6 +1805,10 @@ handle_event(
{keep_state,[NewData|Machine]};
{keep_state,NewData,Ops} ->
{keep_state,[NewData|Machine],Ops};
+ {repeat_state,NewData} ->
+ {repeat_state,[NewData|Machine]};
+ {repeat_state,NewData,Ops} ->
+ {repeat_state,[NewData|Machine],Ops};
Other ->
Other
end;
@@ -1568,7 +1847,12 @@ wrap_result(Result) ->
code_change(OldVsn, State, Data, CallbackMode) ->
- {CallbackMode,State,{OldVsn,Data,CallbackMode}}.
+ io:format(
+ "code_change(~p, ~p, ~p, ~p)~n", [OldVsn,State,Data,CallbackMode]),
+ ets:insert(?MODULE, {callback_mode,CallbackMode}),
+ io:format(
+ "code_change(~p, ~p, ~p, ~p)~n", [OldVsn,State,Data,CallbackMode]),
+ {ok,State,{OldVsn,Data,CallbackMode}}.
format_status(terminate, [_Pdict,State,Data]) ->
{formatted,State,Data};
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index 7d48cbc97c..6e99619324 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2017. 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.
@@ -30,7 +30,7 @@
io_lib_print_binary_depth_one/1, otp_10302/1, otp_10755/1,
otp_10836/1, io_lib_width_too_small/1,
io_with_huge_message_queue/1, format_string/1,
- maps/1, coverage/1]).
+ maps/1, coverage/1, otp_14175/1]).
-export([pretty/2]).
@@ -61,7 +61,7 @@ all() ->
printable_range, bad_printable_range,
io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836,
io_lib_width_too_small, io_with_huge_message_queue,
- format_string, maps, coverage].
+ format_string, maps, coverage, otp_14175].
%% Error cases for output.
error_1(Config) when is_list(Config) ->
@@ -415,13 +415,13 @@ otp_6354(Config) when is_list(Config) ->
bt(<<"#rrrrr{\n"
" f1 = 1,\n"
" f2 = #rrrrr{f1 = a,f2 = b,f3 = c},\n"
- " f3 = \n"
+ " f3 =\n"
" #rrrrr{\n"
" f1 = h,f2 = i,\n"
- " f3 = \n"
+ " f3 =\n"
" #rrrrr{\n"
" f1 = aa,\n"
- " f2 = \n"
+ " f2 =\n"
" #rrrrr{\n"
" f1 = #rrrrr{f1 = a,f2 = b,f3 = c},\n"
" f2 = 2,f3 = 3},\n"
@@ -431,17 +431,17 @@ otp_6354(Config) when is_list(Config) ->
2,3},bb}}},
-1)),
bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,\n"
- " bbbbbbbbbbbbbbbbbbbb = \n"
+ " bbbbbbbbbbbbbbbbbbbb =\n"
" #d{aaaaaaaaaaaaaaaaaaaa = a,bbbbbbbbbbbbbbbbbbbb = b,\n"
" cccccccccccccccccccc = c,dddddddddddddddddddd = d,\n"
" eeeeeeeeeeeeeeeeeeee = e},\n"
" cccccccccccccccccccc = 3,\n"
- " dddddddddddddddddddd = \n"
+ " dddddddddddddddddddd =\n"
" #d{aaaaaaaaaaaaaaaaaaaa = h,bbbbbbbbbbbbbbbbbbbb = i,\n"
- " cccccccccccccccccccc = \n"
+ " cccccccccccccccccccc =\n"
" #d{aaaaaaaaaaaaaaaaaaaa = aa,"
"bbbbbbbbbbbbbbbbbbbb = bb,\n"
- " cccccccccccccccccccc = \n"
+ " cccccccccccccccccccc =\n"
" #d{aaaaaaaaaaaaaaaaaaaa = 1,"
"bbbbbbbbbbbbbbbbbbbb = 2,\n"
" cccccccccccccccccccc = 3,"
@@ -534,21 +534,21 @@ otp_6354(Config) when is_list(Config) ->
p({A,{A,{A,{A,{A,{A,{A,
{g,{h,{i,{j,{k,{l,{m,{n,{o,{a}}}}}}}}}}}}}}}}}, 100)),
bt(<<"#c{\n"
- " f1 = \n"
+ " f1 =\n"
" #c{\n"
- " f1 = \n"
+ " f1 =\n"
" #c{\n"
- " f1 = \n"
+ " f1 =\n"
" #c{\n"
- " f1 = \n"
+ " f1 =\n"
" #c{\n"
- " f1 = \n"
+ " f1 =\n"
" #c{\n"
- " f1 = \n"
+ " f1 =\n"
" #c{\n"
- " f1 = \n"
+ " f1 =\n"
" #c{\n"
- " f1 = \n"
+ " f1 =\n"
" #c{\n"
" f1 = #c{f1 = #c{f1 = #c{f1 = a,"
"f2 = b},f2 = b},f2 = b},\n"
@@ -564,13 +564,13 @@ otp_6354(Config) when is_list(Config) ->
p({c,{c,{c,{c,{c,{c,{c,{c,{c,{c,{c,{c,a,b},b},b},b},b},b},
b},b},b},b},b},b}, -1)),
bt(<<"#rrrrr{\n"
- " f1 = \n"
+ " f1 =\n"
" #rrrrr{\n"
- " f1 = \n"
+ " f1 =\n"
" #rrrrr{\n"
- " f1 = \n"
+ " f1 =\n"
" #rrrrr{\n"
- " f1 = \n"
+ " f1 =\n"
" {rrrrr,{rrrrr,a,#rrrrr{f1 = {rrrrr,1,2},f2 = a,"
"f3 = b}},b},\n"
" f2 = {rrrrr,c,d},\n"
@@ -2106,3 +2106,200 @@ coverage(_Config) ->
io:format("~s\n", [S2]),
ok.
+
+otp_14175(_Config) ->
+ "..." = p(#{}, 0),
+ "#{}" = p(#{}, 1),
+ "#{...}" = p(#{a => 1}, 1),
+ "#{#{} => a}" = p(#{#{} => a}, 2),
+ "#{a => 1,...}" = p(#{a => 1, b => 2}, 2),
+ "#{a => 1,b => 2}" = p(#{a => 1, b => 2}, -1),
+
+ M = #{kaaaaaaaaaaaaaaaaaaa => v1,kbbbbbbbbbbbbbbbbbbb => v2,
+ kccccccccccccccccccc => v3,kddddddddddddddddddd => v4,
+ keeeeeeeeeeeeeeeeeee => v5},
+ "#{...}" = p(M, 1),
+ mt("#{kaaaaaaaaaaaaaaaaaaaa => v1,...}", p(M, 2)),
+ mt("#{kaaaaaaaaaaaaaaaaaaaa => 1,kbbbbbbbbbbbbbbbbbbbb => 2,...}",
+ p(M, 3)),
+
+ mt("#{kaaaaaaaaaaaaaaaaaaa => v1,kbbbbbbbbbbbbbbbbbbb => v2,\n"
+ " kccccccccccccccccccc => v3,...}", p(M, 4)),
+
+ mt("#{kaaaaaaaaaaaaaaaaaaa => v1,kbbbbbbbbbbbbbbbbbbb => v2,\n"
+ " kccccccccccccccccccc => v3,kddddddddddddddddddd => v4,...}",
+ p(M, 5)),
+
+ mt("#{kaaaaaaaaaaaaaaaaaaa => v1,kbbbbbbbbbbbbbbbbbbb => v2,\n"
+ " kccccccccccccccccccc => v3,kddddddddddddddddddd => v4,\n"
+ " keeeeeeeeeeeeeeeeeee => v5}", p(M, 6)),
+
+ weak("#{aaaaaaaaaaaaaaaaaaa => 1,bbbbbbbbbbbbbbbbbbbb => 2,\n"
+ " cccccccccccccccccccc => {3},\n"
+ " dddddddddddddddddddd => 4,eeeeeeeeeeeeeeeeeeee => 5}",
+ p(#{aaaaaaaaaaaaaaaaaaa => 1,bbbbbbbbbbbbbbbbbbbb => 2,
+ cccccccccccccccccccc => {3},
+ dddddddddddddddddddd => 4,eeeeeeeeeeeeeeeeeeee => 5}, -1)),
+
+ M2 = #{dddddddddddddddddddd => {1}, {aaaaaaaaaaaaaaaaaaaa} => 2,
+ {bbbbbbbbbbbbbbbbbbbb} => 3,{cccccccccccccccccccc} => 4,
+ {eeeeeeeeeeeeeeeeeeee} => 5},
+ "#{...}" = p(M2, 1),
+ weak("#{dddddddddddddddddddd => {...},...}", p(M2, 2)),
+ weak("#{dddddddddddddddddddd => {1},{...} => 2,...}", p(M2, 3)),
+
+ weak("#{dddddddddddddddddddd => {1},\n"
+ " {aaaaaaaaaaaaaaaaaaaa} => 2,\n"
+ " {...} => 3,...}", p(M2, 4)),
+
+ weak("#{dddddddddddddddddddd => {1},\n"
+ " {aaaaaaaaaaaaaaaaaaaa} => 2,\n"
+ " {bbbbbbbbbbbbbbbbbbbb} => 3,\n"
+ " {...} => 4,...}", p(M2, 5)),
+
+ weak("#{dddddddddddddddddddd => {1},\n"
+ " {aaaaaaaaaaaaaaaaaaaa} => 2,\n"
+ " {bbbbbbbbbbbbbbbbbbbb} => 3,\n"
+ " {cccccccccccccccccccc} => 4,\n"
+ " {...} => 5}", p(M2, 6)),
+
+ weak("#{dddddddddddddddddddd => {1},\n"
+ " {aaaaaaaaaaaaaaaaaaaa} => 2,\n"
+ " {bbbbbbbbbbbbbbbbbbbb} => 3,\n"
+ " {cccccccccccccccccccc} => 4,\n"
+ " {eeeeeeeeeeeeeeeeeeee} => 5}", p(M2, 7)),
+
+ M3 = #{kaaaaaaaaaaaaaaaaaaa => vuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,
+ kbbbbbbbbbbbbbbbbbbb => vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,
+ kccccccccccccccccccc => vxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
+ kddddddddddddddddddd => vyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,
+ keeeeeeeeeeeeeeeeeee => vzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz},
+
+ mt("#{aaaaaaaaaaaaaaaaaaaa =>\n"
+ " uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,\n"
+ " bbbbbbbbbbbbbbbbbbbb =>\n"
+ " vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,\n"
+ " cccccccccccccccccccc =>\n"
+ " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,\n"
+ " dddddddddddddddddddd =>\n"
+ " yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,\n"
+ " eeeeeeeeeeeeeeeeeeee =>\n"
+ " zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz}", p(M3, -1)),
+
+ R4 = {c,{c,{c,{c,{c,{c,{c,{c,{c,{c,{c,{c,a,b},b},b},b},b},b},
+ b},b},b},b},b},b},
+ M4 = #{aaaaaaaaaaaaaaaaaaaa => R4,
+ bbbbbbbbbbbbbbbbbbbb => R4,
+ cccccccccccccccccccc => R4,
+ dddddddddddddddddddd => R4,
+ eeeeeeeeeeeeeeeeeeee => R4},
+
+ weak("#{aaaaaaaaaaaaaaaaaaaa =>\n"
+ " #c{f1 = #c{f1 = #c{...},f2 = b},f2 = b},\n"
+ " bbbbbbbbbbbbbbbbbbbb => #c{f1 = #c{f1 = {...},...},f2 = b},\n"
+ " cccccccccccccccccccc => #c{f1 = #c{...},f2 = b},\n"
+ " dddddddddddddddddddd => #c{f1 = {...},...},\n"
+ " eeeeeeeeeeeeeeeeeeee => #c{...}}", p(M4, 7)),
+
+ M5 = #{aaaaaaaaaaaaaaaaaaaa => R4},
+ mt("#{aaaaaaaaaaaaaaaaaaaa =>\n"
+ " #c{\n"
+ " f1 =\n"
+ " #c{\n"
+ " f1 =\n"
+ " #c{\n"
+ " f1 =\n"
+ " #c{\n"
+ " f1 =\n"
+ " #c{\n"
+ " f1 =\n"
+ " #c{\n"
+ " f1 =\n"
+ " #c{\n"
+ " f1 =\n"
+ " #c{\n"
+ " f1 =\n"
+ " #c{\n"
+ " f1 = #c{f1 = #c{f1 = #c{f1 = a,f2 = b},f2 = b},"
+ "f2 = b},\n"
+ " f2 = b},\n"
+ " f2 = b},\n"
+ " f2 = b},\n"
+ " f2 = b},\n"
+ " f2 = b},\n"
+ " f2 = b},\n"
+ " f2 = b},\n"
+ " f2 = b},\n"
+ " f2 = b}}", p(M5, -1)),
+ ok.
+
+%% Just check number of newlines and dots ('...').
+-define(WEAK, true).
+
+-ifdef(WEAK).
+
+weak(S, R) ->
+ (nl(S) =:= nl(R) andalso
+ dots(S) =:= dots(S)).
+
+nl(S) ->
+ [C || C <- S, C =:= $\n].
+
+dots(S) ->
+ [C || C <- S, C =:= $\.].
+
+-else. % WEAK
+
+weak(S, R) ->
+ mt(S, R).
+
+-endif. % WEAK
+
+%% If EXACT is defined: mt() matches strings exactly.
+%%
+%% if EXACT is not defined: do not match the strings exactly, but
+%% compare them assuming that all map keys and all map values are
+%% equal (by assuming all map keys and all map values have the same
+%% length and begin with $k and $v respectively).
+
+%-define(EXACT, true).
+
+-ifdef(EXACT).
+
+mt(S, R) ->
+ S =:= R.
+
+-else. % EXACT
+
+mt(S, R) ->
+ anon(S) =:= anon(R).
+
+anon(S) ->
+ {ok, Ts0, _} = erl_scan:string(S, 1, [text]),
+ Ts = anon1(Ts0),
+ text(Ts).
+
+anon1([]) -> [];
+anon1([{atom,Anno,Atom}=T|Ts]) ->
+ case erl_anno:text(Anno) of
+ "k" ++ _ ->
+ NewAnno = erl_anno:set_text("key", Anno),
+ [{atom,NewAnno,Atom}|anon1(Ts)];
+ "v" ++ _ ->
+ NewAnno = erl_anno:set_text("val", Anno),
+ [{atom,NewAnno,Atom}|anon1(Ts)];
+ _ ->
+ [T|anon1(Ts)]
+ end;
+anon1([T|Ts]) ->
+ [T|anon1(Ts)].
+
+text(Ts) ->
+ lists:append(text1(Ts)).
+
+text1([]) -> [];
+text1([T|Ts]) ->
+ Anno = element(2, T),
+ [erl_anno:text(Anno) | text1(Ts)].
+
+-endif. % EXACT
diff --git a/lib/stdlib/test/ms_transform_SUITE.erl b/lib/stdlib/test/ms_transform_SUITE.erl
index 1c5faa960b..f35013b1b2 100644
--- a/lib/stdlib/test/ms_transform_SUITE.erl
+++ b/lib/stdlib/test/ms_transform_SUITE.erl
@@ -296,6 +296,8 @@ basic_dbg(Config) when is_list(Config) ->
compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> bindings() end)">>),
[{['$1','$2'],[],['$_']}] =
compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> object() end)">>),
+ [{[],[],[{return_trace}]}] =
+ compile_and_run(<<"dbg:fun2ms(fun([]) -> return_trace() end)">>),
ok.
%% Test calling of ets/dbg:fun2ms from the shell.
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 2bd940020c..846c2c56f4 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2017. 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.
@@ -869,11 +869,12 @@ eval_unique(Config) when is_list(Config) ->
[a] = qlc:e(Q2, {unique_all, true})
">>,
- <<"Q = qlc:q([SQV || SQV <- qlc:q([X || X <- [1,2,1]],unique)],
+ <<"Q = qlc:q([SQV || SQV <- qlc:q([X || X <- [1,2,1,#{a => 1}]],
+ unique)],
unique),
{call,_,_,[{lc,_,{var,_,'X'},[{generate,_,{var,_,'X'},_}]},_]} =
qlc:info(Q, [{format,abstract_code},unique_all]),
- [1,2] = qlc:e(Q)">>,
+ [1,2,#{a := 1}] = qlc:e(Q)">>,
<<"Q = qlc:q([X || X <- [1,2,1]]),
{call,_,_,[{lc,_,{var,_,'X'},[{generate,_,{var,_,'X'},_}]},_]} =
@@ -2620,7 +2621,16 @@ info(Config) when is_list(Config) ->
{cons, _, _, _}]},
{nil,_}}]}]} = i(QH, {format, abstract_code}),
[{5},{6}] = qlc:e(QH),
- [{4},{5},{6}] = qlc:e(F(3))">>
+ [{4},{5},{6}] = qlc:e(F(3))">>,
+
+ <<"Fun = fun ?MODULE:i/2,
+ L = [{#{k => #{v => Fun}}, Fun}],
+ H = qlc:q([Q || Q <- L, Q =:= {#{k => #{v => Fun}}, Fun}]),
+ L = qlc:e(H),
+ {call,_,_,[{lc,_,{var,_,'Q'},
+ [{generate,_,_,_},
+ {op,_,_,_,_}]}]} =
+ qlc:info(H, [{format,abstract_code}])">>
],
run(Config, Ts),
diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl
index cb778c96d4..47e7c4f03d 100644
--- a/lib/stdlib/test/rand_SUITE.erl
+++ b/lib/stdlib/test/rand_SUITE.erl
@@ -18,7 +18,11 @@
%% %CopyrightEnd%
-module(rand_SUITE).
--export([all/0, suite/0,groups/0]).
+-compile({nowarn_deprecated_function,[{random,seed,1},
+ {random,uniform_s,1},
+ {random,uniform_s,2}]}).
+
+-export([all/0, suite/0, groups/0, group/1]).
-export([interval_int/1, interval_float/1, seed/1,
api_eq/1, reference/1,
@@ -47,18 +51,22 @@ groups() ->
[{basic_stats, [parallel],
[basic_stats_uniform_1, basic_stats_uniform_2, basic_stats_normal]}].
+group(basic_stats) ->
+ %% valgrind needs a lot of time
+ [{timetrap,{minutes,10}}].
+
%% A simple helper to test without test_server during dev
test() ->
Tests = all(),
lists:foreach(fun(Test) ->
- try
- ok = ?MODULE:Test([]),
- io:format("~p: ok~n", [Test])
- catch _:Reason ->
- io:format("Failed: ~p: ~p ~p~n",
- [Test, Reason, erlang:get_stacktrace()])
- end
- end, Tests).
+ try
+ ok = ?MODULE:Test([]),
+ io:format("~p: ok~n", [Test])
+ catch _:Reason ->
+ io:format("Failed: ~p: ~p ~p~n",
+ [Test, Reason, erlang:get_stacktrace()])
+ end
+ end, Tests).
algs() ->
[exs64, exsplus, exs1024].
@@ -267,13 +275,13 @@ gen(_, _, Acc) -> lists:reverse(Acc).
%% Check that the algorithms generate sound values.
basic_stats_uniform_1(Config) when is_list(Config) ->
- ct:timetrap({minutes,6}), %% valgrind needs a lot of time
+ ct:timetrap({minutes,15}), %% valgrind needs a lot of time
[basic_uniform_1(?LOOP, rand:seed_s(Alg), 0.0, array:new([{default, 0}]))
|| Alg <- algs()],
ok.
basic_stats_uniform_2(Config) when is_list(Config) ->
- ct:timetrap({minutes,6}), %% valgrind needs a lot of time
+ ct:timetrap({minutes,15}), %% valgrind needs a lot of time
[basic_uniform_2(?LOOP, rand:seed_s(Alg), 0, array:new([{default, 0}]))
|| Alg <- algs()],
ok.
@@ -380,7 +388,7 @@ crypto_uniform_n(N, State0) ->
%% Not a test but measures the time characteristics of the different algorithms
measure(Suite) when is_atom(Suite) -> [];
measure(_Config) ->
- ct:timetrap({minutes,6}), %% valgrind needs a lot of time
+ ct:timetrap({minutes,15}), %% valgrind needs a lot of time
Algos = [crypto64|algs()],
io:format("RNG uniform integer performance~n",[]),
_ = measure_1(random, fun(State) -> {int, random:uniform_s(10000, State)} end),
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index dc82e92876..80585ca359 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -30,7 +30,7 @@
progex_bit_syntax/1, progex_records/1,
progex_lc/1, progex_funs/1,
otp_5990/1, otp_6166/1, otp_6554/1,
- otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1]).
+ otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1, otp_13719/1]).
-export([ start_restricted_from_shell/1,
start_restricted_on_command_line/1,restricted_local/1]).
@@ -91,7 +91,7 @@ groups() ->
progex_funs]},
{tickets, [],
[otp_5990, otp_6166, otp_6554, otp_7184,
- otp_7232, otp_8393, otp_10302]}].
+ otp_7232, otp_8393, otp_10302, otp_13719]}].
init_per_suite(Config) ->
Config.
@@ -2325,7 +2325,7 @@ otp_6554(Config) when is_list(Config) ->
"[unproper | list]).">>),
%% Cheating:
"exception error: no function clause matching "
- "erl_eval:do_apply(4)" ++ _ =
+ "shell:apply_fun(4)" ++ _ =
comm_err(<<"erlang:error(function_clause, [4]).">>),
"exception error: no function clause matching "
"lists:reverse(" ++ _ =
@@ -2810,6 +2810,19 @@ otp_10302(Config) when is_list(Config) ->
test_server:stop_node(Node),
ok.
+otp_13719(Config) when is_list(Config) ->
+ Test = <<"-module(otp_13719).
+ -record(bar, {}).
+ -record(foo, {bar :: #bar{}}).">>,
+ File = filename("otp_13719.erl", Config),
+ Beam = filename("otp_13719.beam", Config),
+ ok = compile_file(Config, File, Test, []),
+ RR = "rr(\"" ++ Beam ++ "\"). #foo{}.",
+ "[bar,foo]\n#foo{bar = undefined}.\n" = t(RR),
+ file:delete(filename("test.beam", Config)),
+ file:delete(File),
+ ok.
+
scan(B) ->
F = fun(Ts) ->
case erl_parse:parse_term(Ts) of
diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl
index 64dd41e75a..6f3979bb77 100644
--- a/lib/stdlib/test/tar_SUITE.erl
+++ b/lib/stdlib/test/tar_SUITE.erl
@@ -720,20 +720,25 @@ memory(Config) when is_list(Config) ->
%% Test filenames with characters outside the US ASCII range.
unicode(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- do_unicode(PrivDir),
+ run_unicode_node(Config, "+fnu"),
case has_transparent_naming() of
true ->
- Pa = filename:dirname(code:which(?MODULE)),
- Node = start_node(unicode, "+fnl -pa "++Pa),
- ok = rpc:call(Node, erlang, apply,
- [fun() -> do_unicode(PrivDir) end,[]]),
- true = test_server:stop_node(Node),
- ok;
+ run_unicode_node(Config, "+fnl");
false ->
ok
end.
+run_unicode_node(Config, Option) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Pa = filename:dirname(code:which(?MODULE)),
+ Args = Option ++ " -pa "++Pa,
+ io:format("~s\n", [Args]),
+ Node = start_node(unicode, Args),
+ ok = rpc:call(Node, erlang, apply,
+ [fun() -> do_unicode(PrivDir) end,[]]),
+ true = test_server:stop_node(Node),
+ ok.
+
has_transparent_naming() ->
case os:type() of
{unix,darwin} -> false;
@@ -745,10 +750,11 @@ do_unicode(PrivDir) ->
ok = file:set_cwd(PrivDir),
ok = file:make_dir("unicöde"),
- Names = unicode_create_files(),
+ Names = lists:sort(unicode_create_files()),
Tar = "unicöde.tar",
ok = erl_tar:create(Tar, ["unicöde"], []),
- {ok,Names} = erl_tar:table(Tar, []),
+ {ok,Names0} = erl_tar:table(Tar, []),
+ Names = lists:sort(Names0),
_ = [ok = file:delete(Name) || Name <- Names],
ok = erl_tar:extract(Tar),
_ = [{ok,_} = file:read_file(Name) || Name <- Names],
diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl
index 2add5a39a2..1dfcda4ed0 100644
--- a/lib/stdlib/test/zip_SUITE.erl
+++ b/lib/stdlib/test/zip_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -25,8 +25,9 @@
zip_to_binary/1,
unzip_options/1, zip_options/1, list_dir_options/1, aliases/1,
openzip_api/1, zip_api/1, open_leak/1, unzip_jar/1,
+ unzip_traversal_exploit/1,
compress_control/1,
- foldl/1]).
+ foldl/1,fd_leak/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
@@ -38,7 +39,8 @@ all() ->
[borderline, atomic, bad_zip, unzip_from_binary,
unzip_to_binary, zip_to_binary, unzip_options,
zip_options, list_dir_options, aliases, openzip_api,
- zip_api, open_leak, unzip_jar, compress_control, foldl].
+ zip_api, open_leak, unzip_jar, compress_control, foldl,
+ unzip_traversal_exploit,fd_leak].
groups() ->
[].
@@ -377,6 +379,52 @@ unzip_options(Config) when is_list(Config) ->
0 = delete_files([Subdir]),
ok.
+%% Test that unzip handles directory traversal exploit (OTP-13633)
+unzip_traversal_exploit(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ ZipName = filename:join(DataDir, "exploit.zip"),
+
+ %% $ zipinfo -1 test/zip_SUITE_data/exploit.zip
+ %% clash.txt
+ %% ../clash.txt
+ %% ../above.txt
+ %% subdir/../in_root_dir.txt
+
+ %% create a temp directory
+ SubDir = filename:join(PrivDir, "exploit_test"),
+ ok = file:make_dir(SubDir),
+
+ ClashFile = filename:join(SubDir,"clash.txt"),
+ AboveFile = filename:join(SubDir,"above.txt"),
+ RelativePathFile = filename:join(SubDir,"subdir/../in_root_dir.txt"),
+
+ %% unzip in SubDir
+ {ok, [ClashFile, ClashFile, AboveFile, RelativePathFile]} =
+ zip:unzip(ZipName, [{cwd,SubDir}]),
+
+ {ok,<<"This file will overwrite other file.\n">>} =
+ file:read_file(ClashFile),
+ {ok,_} = file:read_file(AboveFile),
+ {ok,_} = file:read_file(RelativePathFile),
+
+ %% clean up
+ delete_files([SubDir]),
+
+ %% create the temp directory again
+ ok = file:make_dir(SubDir),
+
+ %% unzip in SubDir
+ {ok, [ClashFile, AboveFile, RelativePathFile]} =
+ zip:unzip(ZipName, [{cwd,SubDir},keep_old_files]),
+
+ {ok,<<"This is the original file.\n">>} =
+ file:read_file(ClashFile),
+
+ %% clean up
+ delete_files([SubDir]),
+ ok.
+
%% Test unzip a jar file (OTP-7382).
unzip_jar(Config) when is_list(Config) ->
DataDir = proplists:get_value(data_dir, Config),
@@ -834,3 +882,35 @@ foldl(Config) ->
{error, enoent} = zip:foldl(ZipFun, [], File),
ok.
+
+fd_leak(Config) ->
+ ok = file:set_cwd(proplists:get_value(priv_dir, Config)),
+ DataDir = proplists:get_value(data_dir, Config),
+ Name = filename:join(DataDir, "bad_file_header.zip"),
+ BadExtract = fun() ->
+ {error,bad_file_header} = zip:extract(Name),
+ ok
+ end,
+ do_fd_leak(BadExtract, 1),
+
+ BadCreate = fun() ->
+ {error,enoent} = zip:zip("failed.zip",
+ ["none"]),
+ ok
+ end,
+ do_fd_leak(BadCreate, 1),
+
+ ok.
+
+do_fd_leak(_Bad, 10000) ->
+ ok;
+do_fd_leak(Bad, N) ->
+ try Bad() of
+ ok ->
+ do_fd_leak(Bad, N + 1)
+ catch
+ C:R ->
+ Stk = erlang:get_stacktrace(),
+ io:format("Bad error after ~p attempts\n", [N]),
+ erlang:raise(C, R, Stk)
+ end.
diff --git a/lib/stdlib/test/zip_SUITE_data/exploit.zip b/lib/stdlib/test/zip_SUITE_data/exploit.zip
new file mode 100644
index 0000000000..afb8dbd192
--- /dev/null
+++ b/lib/stdlib/test/zip_SUITE_data/exploit.zip
Binary files differ
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 46e3ceac03..f7bd21472c 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.0
+STDLIB_VSN = 3.3
diff --git a/lib/syntax_tools/doc/specs/.gitignore b/lib/syntax_tools/doc/specs/.gitignore
new file mode 100644
index 0000000000..322eebcb06
--- /dev/null
+++ b/lib/syntax_tools/doc/specs/.gitignore
@@ -0,0 +1 @@
+specs_*.xml
diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile
index ff4f3f78ff..e55222e59c 100644
--- a/lib/syntax_tools/doc/src/Makefile
+++ b/lib/syntax_tools/doc/src/Makefile
@@ -81,10 +81,15 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+
+TOP_SPECS_FILE = specs.xml
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
XML_FLAGS +=
+SPECS_FLAGS = -I../../include
DVIPS_FLAGS +=
# ----------------------------------------------------
@@ -93,7 +98,7 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-docs: pdf html man
+docs: man pdf html
$(TOP_PDF_FILE): $(XML_FILES)
@@ -120,6 +125,7 @@ clean clean_docs:
rm -f $(MAN3DIR)/*
rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f $(SPECDIR)/*
rm -f errs core *~
# ----------------------------------------------------
diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index ef207f7c3d..acc465a322 100644
--- a/lib/syntax_tools/doc/src/notes.xml
+++ b/lib/syntax_tools/doc/src/notes.xml
@@ -32,6 +32,70 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 2.1.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix a bug regarding reverting map types. </p>
+ <p>
+ Own Id: OTP-15098 Aux Id: ERIERL-177 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Syntax_Tools 2.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The address to the FSF in the license header has been
+ updated.</p>
+ <p>
+ Own Id: OTP-14084</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Syntax_Tools 2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix a bug where <c>erl_tidy</c> crashed on the tilde
+ character when printing to standard output. </p>
+ <p>
+ Own Id: OTP-13725 Aux Id: ERL-151, PR-1071 </p>
+ </item>
+ <item>
+ <p><c>merl_transform</c> could get into an infinite loop
+ when syntactically incorrect text was passed to a
+ <c>merl:qquote/2,3</c> call.</p>
+ <p>
+ Own Id: OTP-13755</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Improve types and specs in OTP documentation generated
+ from Erlang source files. </p>
+ <p>
+ Own Id: OTP-13720 Aux Id: ERL-120 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 2.0</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/syntax_tools/doc/src/specs.xml b/lib/syntax_tools/doc/src/specs.xml
new file mode 100644
index 0000000000..04c3a494e7
--- /dev/null
+++ b/lib/syntax_tools/doc/src/specs.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<specs xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="../specs/specs_epp_dodger.xml"/>
+ <xi:include href="../specs/specs_erl_comment_scan.xml"/>
+ <xi:include href="../specs/specs_erl_prettypr.xml"/>
+ <xi:include href="../specs/specs_erl_recomment.xml"/>
+ <xi:include href="../specs/specs_erl_syntax.xml"/>
+ <xi:include href="../specs/specs_erl_syntax_lib.xml"/>
+ <xi:include href="../specs/specs_erl_tidy.xml"/>
+ <xi:include href="../specs/specs_igor.xml"/>
+ <xi:include href="../specs/specs_merl.xml"/>
+ <xi:include href="../specs/specs_merl_transform.xml"/>
+ <xi:include href="../specs/specs_prettypr.xml"/>
+</specs>
diff --git a/lib/syntax_tools/src/epp_dodger.erl b/lib/syntax_tools/src/epp_dodger.erl
index 39c522fd11..43e42d8195 100644
--- a/lib/syntax_tools/src/epp_dodger.erl
+++ b/lib/syntax_tools/src/epp_dodger.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 2001-2006 Richard Carlsson
diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl
index 03429d4d42..8d0e2a10a3 100644
--- a/lib/syntax_tools/src/erl_comment_scan.erl
+++ b/lib/syntax_tools/src/erl_comment_scan.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% =====================================================================
@@ -30,8 +30,14 @@
%% =====================================================================
--type comment() :: {integer(), integer(), integer(), [string()]}.
--type commentLine() :: {integer(), integer(), integer(), string()}.
+-type comment() :: {Line:: integer(),
+ Column:: integer(),
+ Indentation :: integer(),
+ Text :: [string()]}.
+-type commentLine() :: {Line :: integer(),
+ Column :: integer(),
+ Indent :: integer(),
+ Text :: string()}.
%% =====================================================================
%% @spec file(FileName::file:filename()) -> [Comment]
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index f1615b2610..6c812aaa6c 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 1997-2006 Richard Carlsson
@@ -195,10 +195,16 @@ format(Node) ->
%% =====================================================================
%% @spec format(Tree::syntaxTree(), Options::[term()]) -> string()
-%% syntaxTree() = erl_syntax:syntaxTree()
%%
-%% @type hook() = (syntaxTree(), context(), Continuation) -> document()
-%% Continuation = (syntaxTree(), context()) -> document().
+%% @type syntaxTree() = erl_syntax:syntaxTree().
+%%
+%% An abstract syntax tree. See the {@link erl_syntax} module for
+%% details.
+%%
+%% @type hook() = (syntaxTree(), context(), Continuation) ->
+%% prettypr:document()
+%% Continuation = (syntaxTree(), context()) ->
+%% prettypr:document().
%%
%% A call-back function for user-controlled formatting. See {@link
%% format/2}.
@@ -277,7 +283,7 @@ format(Node, Options) ->
%% =====================================================================
-%% @spec best(Tree::syntaxTree()) -> empty | document()
+%% @spec best(Tree::syntaxTree()) -> empty | prettypr:document()
%% @equiv best(Tree, [])
-spec best(erl_syntax:syntaxTree()) -> 'empty' | prettypr:document().
@@ -288,7 +294,7 @@ best(Node) ->
%% =====================================================================
%% @spec best(Tree::syntaxTree(), Options::[term()]) ->
-%% empty | document()
+%% empty | prettypr:document()
%%
%% @doc Creates a fixed "best" abstract layout for a syntax tree. This
%% is similar to the `layout/2' function, except that here, the final
@@ -310,7 +316,7 @@ best(Node, Options) ->
%% =====================================================================
-%% @spec layout(Tree::syntaxTree()) -> document()
+%% @spec layout(Tree::syntaxTree()) -> prettypr:document()
%% @equiv layout(Tree, [])
-spec layout(erl_syntax:syntaxTree()) -> prettypr:document().
@@ -320,8 +326,7 @@ layout(Node) ->
%% =====================================================================
-%% @spec layout(Tree::syntaxTree(), Options::[term()]) -> document()
-%% document() = prettypr:document()
+%% @spec layout(Tree::syntaxTree(), Options::[term()]) -> prettypr:document()
%%
%% @doc Creates an abstract document layout for a syntax tree. The
%% result represents a set of possible layouts (cf. module `prettypr').
diff --git a/lib/syntax_tools/src/erl_recomment.erl b/lib/syntax_tools/src/erl_recomment.erl
index c1141b2bc6..a774b898e8 100644
--- a/lib/syntax_tools/src/erl_recomment.erl
+++ b/lib/syntax_tools/src/erl_recomment.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 1997-2006 Richard Carlsson
@@ -30,6 +30,9 @@
-export([recomment_forms/2, quick_recomment_forms/2, recomment_tree/2]).
+%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax
+%% tree. See the {@link erl_syntax} module for details.
+
%% =====================================================================
%% @spec quick_recomment_forms(Forms, Comments::[Comment]) ->
%% syntaxTree()
@@ -55,7 +58,6 @@ quick_recomment_forms(Tree, Cs) ->
%% =====================================================================
%% @spec recomment_forms(Forms, Comments::[Comment]) -> syntaxTree()
%%
-%% syntaxTree() = erl_syntax:syntaxTree()
%% Forms = syntaxTree() | [syntaxTree()]
%% Comment = {Line, Column, Indentation, Text}
%% Line = integer()
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index ee42e56172..91da3ad571 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 1997-2006 Richard Carlsson
@@ -5300,7 +5300,7 @@ 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]}.
+ {type, Pos, map_field_assoc, [Name, Value]}.
%% =====================================================================
@@ -5358,7 +5358,7 @@ 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]}.
+ {type, Pos, map_field_exact, [Name, Value]}.
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index 9815559779..aae6755639 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 1997-2006 Richard Carlsson
@@ -280,7 +280,7 @@ mapfoldl(_, S, []) ->
%% =====================================================================
%% @spec variables(syntaxTree()) -> set(atom())
%%
-%% set(T) = //stdlib/sets:set(T)
+%% @type set(T) = //stdlib/sets:set(T)
%%
%% @doc Returns the names of variables occurring in a syntax tree, The
%% result is a set of variable names represented by atoms. Macro names
@@ -1955,7 +1955,7 @@ analyze_application(Node) ->
%% =====================================================================
-%% @spec analyze_type_application(Node::syntaxTree()) -> typeName()
+%% @spec analyze_type_application(Node::syntaxTree()) -> TypeName
%%
%% TypeName = {atom(), integer()}
%% | {ModuleName, {atom(), integer()}}
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index f2de12b410..2e0ee209f8 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 1999-2014 Richard Carlsson
@@ -36,6 +36,11 @@
%% been reasonably well tested, but the possibility of errors remains.
%% Keep backups of your original code safely stored, until you feel
%% confident that the new, modified code can be trusted.
+%%
+%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax
+%% tree. See the {@link erl_syntax} module for details.
+%%
+%% @type filename() = file:filename().
-module(erl_tidy).
@@ -79,7 +84,6 @@ dir(Dir) ->
%% =====================================================================
%% @spec dir(Directory::filename(), Options::[term()]) -> ok
-%% filename() = file:filename()
%%
%% @doc Tidies Erlang source files in a directory and its
%% subdirectories.
@@ -206,7 +210,7 @@ file__defaults() ->
{verbose, false}].
default_printer() ->
- fun (Tree, Options) -> erl_prettypr:format(Tree, Options) end.
+ fun erl_prettypr:format/2.
%% =====================================================================
%% @spec file(Name) -> ok
@@ -253,7 +257,7 @@ file(Name) ->
%%
%% <dt>{printer, Function}</dt>
%% <dd><ul>
-%% <li>`Function = (syntaxTree()) -> string()'</li>
+%% <li>`Function = (syntaxTree(), [term()]) -> string()'</li>
%% </ul>
%%
%% Specifies a function for prettyprinting Erlang syntax trees.
@@ -414,7 +418,7 @@ write_module(Tree, Name, Opts) ->
print_module(Tree, Opts) ->
Printer = proplists:get_value(printer, Opts),
- io:format(Printer(Tree, Opts)).
+ io:put_chars(Printer(Tree, Opts)).
output(FD, Printer, Tree, Opts) ->
io:put_chars(FD, Printer(Tree, Opts)),
@@ -513,7 +517,6 @@ module(Forms) ->
%% @spec module(Forms, Options::[term()]) -> syntaxTree()
%%
%% Forms = syntaxTree() | [syntaxTree()]
-%% syntaxTree() = erl_syntax:syntaxTree()
%%
%% @doc Tidies a syntax tree representation of a module
%% definition. The given `Forms' may be either a single
diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl
index 1d14bd7c3a..4d18f7abcf 100644
--- a/lib/syntax_tools/src/igor.erl
+++ b/lib/syntax_tools/src/igor.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 1998-2014 Richard Carlsson
@@ -151,7 +151,8 @@ default_printer(Tree, Options) ->
%% @spec parse_transform(Forms::[syntaxTree()], Options::[term()]) ->
%% [syntaxTree()]
%%
-%% syntaxTree() = erl_syntax:syntaxTree()
+%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax
+%% tree. See the {@link erl_syntax} module for details.
%%
%% @doc Allows Igor to work as a component of the Erlang compiler.
%% Including the term `{parse_transform, igor}' in the
@@ -212,7 +213,7 @@ merge(Name, Files) ->
%% @spec merge(Name::atom(), Files::[filename()], Options::[term()]) ->
%% [filename()]
%%
-%% filename() = file:filename()
+%% @type filename() = file:filename()
%%
%% @doc Merges source code files to a single file. `Name'
%% specifies the name of the resulting module - not the name of the
@@ -367,6 +368,7 @@ merge_files(Name, Files, Options) ->
%% @spec merge_files(Name::atom(), Sources::[Forms],
%% Files::[filename()], Options::[term()]) ->
%% {syntaxTree(), [stubDescriptor()]}
+%%
%% Forms = syntaxTree() | [syntaxTree()]
%%
%% @doc Merges source code files and syntax trees to a single syntax
diff --git a/lib/syntax_tools/src/merl_tests.erl b/lib/syntax_tools/src/merl_tests.erl
index c1aae3100e..27db594050 100644
--- a/lib/syntax_tools/src/merl_tests.erl
+++ b/lib/syntax_tools/src/merl_tests.erl
@@ -48,6 +48,21 @@ parse_error_test_() ->
f(merl:quote("{")))
].
+transform_parse_error_test_() ->
+ [?_assertEqual("merl:quote(\"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(\"{\")")], []))),
+ ?_assertEqual("merl:quote(2, \"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(2, \"{\")")], []))),
+ ?_assertEqual("merl:qquote(\"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(\"{\", [{var, V}])")], []))),
+ ?_assertEqual("merl:qquote(2, \"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(2, \"{\", [{var, V}])")], [])))
+ ].
+
term_test_() ->
[?_assertEqual(tuple, erl_syntax:type(merl:term({}))),
?_assertEqual("{foo, 42}", f(merl:term({foo, 42})))
diff --git a/lib/syntax_tools/src/merl_transform.erl b/lib/syntax_tools/src/merl_transform.erl
index fe58b6a122..497baddd0a 100644
--- a/lib/syntax_tools/src/merl_transform.erl
+++ b/lib/syntax_tools/src/merl_transform.erl
@@ -104,10 +104,15 @@ expand_qquote([Text, Env], T, Line) ->
case erl_syntax:is_literal(Text) of
true ->
As = [Line, erl_syntax:concrete(Text)],
- %% expand further if possible
- expand(merl:qquote(Line, "merl:subst(_@tree, _@env)",
- [{tree, eval_call(Line, quote, As, T)},
- {env, Env}]));
+ case eval_call(Line, quote, As, failed) of
+ failed ->
+ T;
+ T1 ->
+ %% expand further if possible
+ expand(merl:qquote(Line, "merl:subst(_@tree, _@env)",
+ [{tree, T1},
+ {env, Env}]))
+ end;
false ->
T
end;
diff --git a/lib/syntax_tools/src/prettypr.erl b/lib/syntax_tools/src/prettypr.erl
index 1b5ba6b05a..5156af667b 100644
--- a/lib/syntax_tools/src/prettypr.erl
+++ b/lib/syntax_tools/src/prettypr.erl
@@ -11,7 +11,7 @@
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
-%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
%% USA
%%
%% @copyright 2000-2006 Richard Carlsson
diff --git a/lib/syntax_tools/test/merl_SUITE.erl b/lib/syntax_tools/test/merl_SUITE.erl
index 945972d405..52bbd9b3b8 100644
--- a/lib/syntax_tools/test/merl_SUITE.erl
+++ b/lib/syntax_tools/test/merl_SUITE.erl
@@ -29,12 +29,14 @@
init_per_group/2,end_per_group/2]).
%% Test cases
--export([merl_smoke_test/1]).
+-export([merl_smoke_test/1,
+ transform_parse_error_test/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [merl_smoke_test].
+ [merl_smoke_test,
+ transform_parse_error_test].
groups() ->
[].
@@ -84,6 +86,21 @@ merl_smoke_test(Config) when is_list(Config) ->
end)),
ok.
+transform_parse_error_test(_Config) ->
+ ?assertEqual("merl:quote(\"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(\"{\")")], []))),
+ ?assertEqual("merl:quote(2, \"{\")",
+ f(merl_transform:parse_transform(
+ [?Q("merl:quote(2, \"{\")")], []))),
+ ?assertEqual("merl:qquote(\"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(\"{\", [{var, V}])")], []))),
+ ?assertEqual("merl:qquote(2, \"{\", [{var, V}])",
+ f(merl_transform:parse_transform(
+ [?Q("merl:qquote(2, \"{\", [{var, V}])")], []))),
+ ok.
+
%% utilities
f(Ts) when is_list(Ts) ->
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index b935d42bb7..b03d249a60 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -26,15 +26,16 @@
%% Test cases
-export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1,
+ revert_map_type/1,
t_abstract_type/1,t_erl_parse_type/1,t_epp_dodger/1,
- t_comment_scan/1,t_igor/1]).
+ t_comment_scan/1,t_igor/1,t_erl_tidy/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test,appup_test,smoke_test,revert,revert_map,
+ [app_test,appup_test,smoke_test,revert,revert_map,revert_map_type,
t_abstract_type,t_erl_parse_type,t_epp_dodger,
- t_comment_scan,t_igor].
+ t_comment_scan,t_igor,t_erl_tidy].
groups() ->
[].
@@ -123,7 +124,26 @@ revert_map(Config) when is_list(Config) ->
{map_field_assoc,{atom,17,name},{var,18,'Value'}}}]),
?t:timetrap_cancel(Dog).
-
+%% Testing bug fix for reverting map_field_assoc in types
+revert_map_type(Config) when is_list(Config) ->
+ Dog = ?t:timetrap(?t:minutes(1)),
+ Form1 = {attribute,4,record,
+ {state,
+ [{typed_record_field,
+ {record_field,5,{atom,5,x}},
+ {type,5,map,
+ [{type,5,map_field_exact,[{atom,5,y},{atom,5,z}]}]}}]}},
+ Mapped1 = erl_syntax_lib:map(fun(X) -> X end, Form1),
+ Form1 = erl_syntax:revert(Mapped1),
+ Form2 = {attribute,4,record,
+ {state,
+ [{typed_record_field,
+ {record_field,5,{atom,5,x}},
+ {type,5,map,
+ [{type,5,map_field_assoc,[{atom,5,y},{atom,5,z}]}]}}]}},
+ Mapped2 = erl_syntax_lib:map(fun(X) -> X end, Form2),
+ Form2 = erl_syntax:revert(Mapped2),
+ ?t:timetrap_cancel(Dog).
%% api tests
@@ -237,6 +257,12 @@ t_igor(Config) when is_list(Config) ->
ok.
+t_erl_tidy(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ File = filename:join(DataDir,"erl_tidy_tilde.erl"),
+ ok = erl_tidy:file(File, [{stdout, true}]),
+ ok.
+
test_comment_scan([],_) -> ok;
test_comment_scan([File|Files],DataDir) ->
Filename = filename:join(DataDir,File),
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl
new file mode 100644
index 0000000000..888264bad6
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl
@@ -0,0 +1,13 @@
+%%
+%% File: erl_tidy_tilde.erl
+%% Author: Mark Bucciarelli
+%% Created: 2016-06-05
+%%
+
+-module(erl_tidy_tilde).
+
+-export([start/0]).
+
+start() ->
+ io:put_chars("tilde characters ('~')in source were "
+ "breaking erl_tidy\n").
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index f09c2a01d0..6cd62c7814 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 2.0
+SYNTAX_TOOLS_VSN = 2.1.1.1
diff --git a/lib/tools/doc/src/erlang_mode.xml b/lib/tools/doc/src/erlang_mode.xml
index 00cf5196b4..7fef74813b 100644
--- a/lib/tools/doc/src/erlang_mode.xml
+++ b/lib/tools/doc/src/erlang_mode.xml
@@ -252,7 +252,14 @@
behavior</item>
<item>gen_event - skeleton for the OTP gen_event behavior</item>
<item>gen_fsm - skeleton for the OTP gen_fsm behavior</item>
- <item>gen_statem - skeleton for the OTP gen_statem behavior</item>
+ <item>
+ gen_statem (StateName/3) - skeleton for the OTP gen_statem behavior
+ using state name functions
+ </item>
+ <item>
+ gen_statem (handle_event/4) - skeleton for the OTP gen_statem behavior
+ using one state function
+ </item>
<item>Library module - skeleton for a module that does not
implement a process.</item>
<item>Corba callback - skeleton for a Corba callback module.</item>
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index 0b8a2be715..af20200d49 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -31,6 +31,104 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.9.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved edoc support in emacs mode.</p>
+ <p>
+ Own Id: OTP-14217 Aux Id: PR-1282 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Tools 2.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix unhandled trace event send_to_non_existing_process in
+ fprof.</p>
+ <p>
+ Own Id: OTP-13998</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved edoc support in emacs erlang-mode.</p>
+ <p>
+ Own Id: OTP-13945 Aux Id: PR-1157 </p>
+ </item>
+ <item>
+ <p>
+ Added erldoc to emacs mode which opens html documentation
+ in browser from emacs. For example <c>M-x erldoc-browse
+ RET lists:foreach/2</c>.</p>
+ <p>
+ Own Id: OTP-14018 Aux Id: PR-1197 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Tools 2.8.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Errors in type specification and Emacs template
+ generation for <c>gen_statem:code_change/4</c> has been
+ fixed from bugs.erlang.org's Jira cases ERL-172 and
+ ERL-187.</p>
+ <p>
+ Own Id: OTP-13746 Aux Id: ERL-172, ERL-187 </p>
+ </item>
+ <item>
+ <p>
+ Fix gc_start/gc_end in fprof tags when parsing old trace
+ logs.</p>
+ <p>
+ Own Id: OTP-13778 Aux Id: PR-1136 </p>
+ </item>
+ <item>
+ <p><c>make</c> (tools) and <c>ct_make</c> (common_test)
+ would crash if an Erlang source file contained a
+ <c>-warning()</c> directive.</p>
+ <p>
+ Own Id: OTP-13855</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Tools 2.8.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Correct a bug when adding multiple modules to an Xref
+ server. The bug was introduced in OTP-19.0. </p>
+ <p>
+ Own Id: OTP-13708 Aux Id: ERL-173 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.8.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/doc/src/xref_chapter.xml b/lib/tools/doc/src/xref_chapter.xml
index 8b14e03064..872793bdcb 100644
--- a/lib/tools/doc/src/xref_chapter.xml
+++ b/lib/tools/doc/src/xref_chapter.xml
@@ -234,7 +234,7 @@
operand of the intersection operator <c>*</c> is implicitly
converted to the more special type of the second operand.</item>
<tag><c>xref:q(s, "(Mod) tools").</c></tag>
- <item>All modules of the <c>tools</c> application.</item>
+ <item>All modules of the Tools application.</item>
<tag><c>xref:q(s, '"xref_.*" : Mod').</c></tag>
<item>All modules with a name beginning with <c>xref_</c>.</item>
<tag><c>xref:q(s, "# E&nbsp;|&nbsp;X&nbsp;").</c></tag>
@@ -252,9 +252,9 @@
<tag><c>xref:q(s, "XC * (ME - strict ME)").</c></tag>
<item>External calls within some module.</item>
<tag><c>xref:q(s, "E&nbsp;|||&nbsp;kernel").</c></tag>
- <item>All calls within the <c>kernel</c> application. </item>
+ <item>All calls within the Kernel application. </item>
<tag><c>xref:q(s, "closure&nbsp;E&nbsp;|&nbsp;kernel&nbsp;||&nbsp;kernel").</c></tag>
- <item>All direct and indirect calls within the <c>kernel</c>
+ <item>All direct and indirect calls within the Kernel
application. Both the calling and the used functions of
indirect calls are defined in modules of the kernel
application, but it is possible that some functions outside
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index 585425e5f1..35c93ba4ed 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -38,11 +38,13 @@ MAN_FILES= \
tags.3
EMACS_FILES= \
+ erldoc \
erlang-skels \
erlang-skels-old \
erlang_appwiz \
erlang-start \
erlang-eunit \
+ erlang-edoc \
erlang-flymake \
erlang
diff --git a/lib/tools/emacs/erlang-edoc.el b/lib/tools/emacs/erlang-edoc.el
new file mode 100644
index 0000000000..241590b850
--- /dev/null
+++ b/lib/tools/emacs/erlang-edoc.el
@@ -0,0 +1,172 @@
+;;; erlang-edoc.el --- EDoc support for Erlang mode -*- lexical-binding: t; -*-
+
+;; %CopyrightBegin%
+;;
+;; 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.
+;; 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%
+
+;;; Commentary:
+
+;; Ref: http://www.erlang.org/doc/apps/edoc/users_guide.html
+;;
+;; To use: (add-hook 'erlang-mode-hook 'erlang-edoc-mode)
+
+;;; Code:
+
+(defcustom erlang-edoc-indent-level 2
+ "Indentation level of xhtml in Erlang edoc."
+ :safe 'integerp
+ :group 'erlang)
+
+(defvar erlang-edoc-generic-tags
+ '("clear" "docfile" "end" "headerfile" "todo" "TODO" "type")
+ "Tags that can be used anywhere within a module.")
+
+(defvar erlang-edoc-overview-tags
+ '("author" "copyright" "doc" "reference" "see" "since" "title" "version")
+ "Tags that can be used in an overview file.")
+
+(defvar erlang-edoc-module-tags
+ '("author" "copyright" "deprecated" "doc" "hidden" "private" "reference"
+ "see" "since" "version")
+ "Tags that can be used before a module declaration.")
+
+(defvar erlang-edoc-function-tags
+ '("deprecated" "doc" "equiv" "hidden" "param" "private" "returns"
+ "see" "since" "spec" "throws" "type")
+ "Tags that can be used before a function definition.")
+
+(defvar erlang-edoc-predefined-macros
+ '("date" "docRoot" "link" "module" "package" "section" "time"
+ "type" "version"))
+
+(defface erlang-edoc-tag '((t (:inherit font-lock-constant-face)))
+ "Face used to highlight edoc tags."
+ :group 'erlang)
+
+(defface erlang-edoc-macro '((t (:inherit font-lock-preprocessor-face)))
+ "Face used to highlight edoc macros."
+ :group 'erlang)
+
+(defface erlang-edoc-verbatim
+ '((t (:family "Monospace" :inherit font-lock-keyword-face)))
+ "Face used to highlight verbatim text."
+ :group 'erlang)
+
+(defface erlang-edoc-todo '((t (:inherit font-lock-warning-face)))
+ "Face used to highlight edoc macros."
+ :group 'erlang)
+
+(defface erlang-edoc-heading '((t (:inherit bold)))
+ "Face used to highlight edoc headings."
+ :group 'erlang)
+
+(defvar erlang-edoc-font-lock-keywords
+ '(("^%+\\s-*\\(@\\w+\\)\\_>" 1 'erlang-edoc-tag prepend)
+ ("^%+\\s-*" ("{\\(@\\w+\\)\\_>" nil nil (1 'erlang-edoc-macro prepend)))
+ ("^%+\\s-*" ("\\(?:@@\\)*\\(@[@{}]\\)" nil nil (1 'escape-glyph prepend)))
+ ("^%+\\s-*\\(@deprecated\\)\\_>" 1 font-lock-warning-face prepend)
+ ;; http://www.erlang.org/doc/apps/edoc/chapter.html#Wiki_notation
+ ("^%+\\s-*" ("[^`]`\\([^`]?\\|[^`].*?[^']\\)'"
+ (forward-char -1) nil (1 'erlang-edoc-verbatim prepend)))
+ ("^%+\\s-*" ("\\[\\(\\(?:https?\\|file\\|ftp\\)://[^][]+\\)\\]"
+ nil nil (1 'link prepend)))
+ ("^%+\\s-*\\(?:\\(?1:@todo\\|@TODO\\)\\_>\\|\\(?1:TODO\\):\\)"
+ 1 'erlang-edoc-todo prepend)
+ ("^%+\\s-*\\(\\(=\\{2,4\\}\\)[^=\n].*[^=\n]\\2\\)\\s-*$"
+ 1 'erlang-edoc-heading prepend)))
+
+(defun erlang-edoc-xml-context ()
+ "Parse edoc x(ht)ml context at comment start of current line."
+ (eval-and-compile (require 'xmltok))
+ (save-excursion
+ (beginning-of-line)
+ (when (looking-at "^%+\\s-*")
+ (let ((pt (match-end 0)) context)
+ (forward-comment (- (point)))
+ (while (< (point) pt)
+ (xmltok-forward)
+ (cond ((eq xmltok-type 'start-tag)
+ (push (cons xmltok-type xmltok-start) context))
+ ((eq xmltok-type 'end-tag)
+ (pop context))))
+ (goto-char pt)
+ (xmltok-forward)
+ (push (car (memq xmltok-type '(start-tag end-tag))) context)
+ context))))
+
+(defun erlang-edoc-indent-line ()
+ (let ((context (erlang-edoc-xml-context)))
+ (when context
+ (save-excursion
+ (beginning-of-line)
+ (re-search-forward "^%+\\s-*" (line-end-position))
+ (when (or (car context) (cadr context))
+ (let ((pad (when (cadr context)
+ (save-excursion
+ (goto-char (cdr (cadr context)))
+ (- (current-column)
+ (progn
+ (beginning-of-line)
+ (skip-chars-forward "%")
+ (current-column)))))))
+ (just-one-space (cond ((not pad) 1)
+ ((eq (car context) 'end-tag) pad)
+ (t (+ erlang-edoc-indent-level pad)))))))
+ (when (looking-back "^%*\\s-*" (line-beginning-position))
+ (re-search-forward "\\=%*\\s-*")))))
+
+(defun erlang-edoc-before-module-declaration-p ()
+ (save-excursion
+ (beginning-of-line)
+ (forward-comment (point-max))
+ (or (eobp) (re-search-forward "^-module\\s-*(" nil t))))
+
+(defun erlang-edoc-completion-at-point ()
+ (when (eq (syntax-ppss-context (syntax-ppss)) 'comment)
+ (save-excursion
+ (skip-syntax-backward "w_")
+ (when (= (preceding-char) ?@)
+ (let* ((is-tag (looking-back "^%+\\s-*@" (line-beginning-position)))
+ (beg (point))
+ (end (progn (skip-syntax-forward "w_") (point)))
+ (table (cond
+ ((not is-tag)
+ erlang-edoc-predefined-macros)
+ ((erlang-edoc-before-module-declaration-p)
+ (append erlang-edoc-module-tags
+ erlang-edoc-generic-tags))
+ (t (append erlang-edoc-function-tags
+ erlang-edoc-generic-tags)))))
+ (list beg end table))))))
+
+;;;###autoload
+(define-minor-mode erlang-edoc-mode nil
+ :lighter " EDoc"
+ (cond (erlang-edoc-mode
+ (add-hook 'erlang-indent-line-hook #'erlang-edoc-indent-line nil t)
+ (font-lock-add-keywords nil erlang-edoc-font-lock-keywords t)
+ (add-hook 'completion-at-point-functions
+ #'erlang-edoc-completion-at-point nil t))
+ (t
+ (remove-hook 'erlang-indent-line-hook #'erlang-edoc-indent-line t)
+ (font-lock-remove-keywords nil erlang-edoc-font-lock-keywords)
+ (remove-hook 'completion-at-point-functions
+ #'erlang-edoc-completion-at-point t)))
+ (jit-lock-refontify))
+
+(provide 'erlang-edoc)
+;;; erlang-edoc.el ends here
diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index ce26c83295..bdb3d9ad4a 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -1,7 +1,7 @@
;;
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 2010-2016. All Rights Reserved.
+;; Copyright Ericsson AB 2010-2017. 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.
@@ -56,8 +56,10 @@
erlang-skel-gen-event erlang-skel-header)
("gen_fsm" "gen-fsm"
erlang-skel-gen-fsm erlang-skel-header)
- ("gen_statem" "gen-statem"
- erlang-skel-gen-statem erlang-skel-header)
+ ("gen_statem (StateName/3)" "gen-statem-StateName"
+ erlang-skel-gen-statem-StateName erlang-skel-header)
+ ("gen_statem (handle_event/4)" "gen-statem-handle-event"
+ erlang-skel-gen-statem-handle-event erlang-skel-header)
("wx_object" "wx-object"
erlang-skel-wx-object erlang-skel-header)
("Library module" "gen-lib"
@@ -497,6 +499,7 @@ Please see the function `tempo-define-template'.")
"%% {stop, Reason}" n
(erlang-skel-separator-end 2)
"init([]) ->" n>
+ "process_flag(trap_exit, true)," n>
"{ok, #state{}}." n
n
(erlang-skel-separator-start 2)
@@ -740,6 +743,7 @@ Please see the function `tempo-define-template'.")
"%% {stop, StopReason}" n
(erlang-skel-separator-end 2)
"init([]) ->" n>
+ "process_flag(trap_exit, true)," n>
"{ok, state_name, #state{}}." n
n
(erlang-skel-separator-start 2)
@@ -860,7 +864,7 @@ Please see the function `tempo-define-template'.")
"*The template of a gen_fsm.
Please see the function `tempo-define-template'.")
-(defvar erlang-skel-gen-statem
+(defvar erlang-skel-gen-statem-StateName
'((erlang-skel-include erlang-skel-large-header)
"-behaviour(gen_statem)." n n
@@ -868,9 +872,8 @@ Please see the function `tempo-define-template'.")
"-export([start_link/0])." n
n
"%% gen_statem callbacks" n
- "-export([init/1, terminate/3, code_change/4])." n
+ "-export([callback_mode/0, init/1, terminate/3, code_change/4])." n
"-export([state_name/3])." n
- "-export([handle_event/4])." n
n
"-define(SERVER, ?MODULE)." n
n
@@ -899,48 +902,149 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
+ "%% Define the callback_mode() for this callback module." n
+ (erlang-skel-separator-end 2)
+ "-spec callback_mode() -> gen_statem:callback_mode_result()." n
+ "callback_mode() -> state_functions." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
"%% Whenever a gen_statem is started using gen_statem:start/[3,4] or" n
"%% gen_statem:start_link/[3,4], this function is called by the new" n
"%% process to initialize." n
(erlang-skel-separator-end 2)
- "-spec init(Args :: term()) -> " n>
- "{gen_statem:callback_mode()," n>
- "State :: term(), Data :: term()} |" n>
- "{gen_statem:callback_mode()," n>
- "State :: term(), Data :: term()," n>
- "[gen_statem:action()] | gen_statem:action()} |" n>
- "ignore |" n>
- "{stop, Reason :: term()}." n
+ "-spec init(Args :: term()) ->" n>
+ "gen_statem:init_result(atom())." n
"init([]) ->" n>
- "{state_functions, state_name, #data{}}." n
+ "process_flag(trap_exit, true)," n>
+ "{ok, state_name, #data{}}." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
- "%% If the gen_statem runs with CallbackMode =:= state_functions" n
- "%% there should be one instance of this function for each possible" n
- "%% state name. Whenever a gen_statem receives an event," n
- "%% the instance of this function with the same name" n
- "%% as the current state name StateName is called to" n
- "%% handle the event." n
+ "%% There should be one function like this for each state name." n
+ "%% Whenever a gen_statem receives an event, the function " n
+ "%% with the name of the current state (StateName) " n
+ "%% is called to handle the event." n
(erlang-skel-separator-end 2)
- "-spec state_name(" n>
- "gen_statem:event_type(), Msg :: term()," n>
+ "-spec state_name('enter'," n>
+ "OldState :: atom()," n>
+ "Data :: term()) ->" n>
+ "gen_statem:state_enter_result('state_name');" n>
+ "(gen_statem:event_type()," n>
+ "Msg :: term()," n>
"Data :: term()) ->" n>
- "gen_statem:state_function_result(). " n
+ "gen_statem:event_handler_result(atom())." n
+ ;;
"state_name({call,Caller}, _Msg, Data) ->" n>
"{next_state, state_name, Data, [{reply,Caller,ok}]}." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
- "%% If the gen_statem runs with CallbackMode =:= handle_event_function" n
- "%% this function is called for every event a gen_statem receives." n
+ "%% This function is called by a gen_statem when it is about to" n
+ "%% terminate. It should be the opposite of Module:init/1 and do any" n
+ "%% necessary cleaning up. When it returns, the gen_statem terminates with" n
+ "%% Reason. The return value is ignored." n
+ (erlang-skel-separator-end 2)
+ "-spec terminate(Reason :: term(), State :: term(), Data :: term()) ->" n>
+ "any()." n
+ "terminate(_Reason, _State, _Data) ->" n>
+ "void." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Convert process state when code is changed" n
+ (erlang-skel-separator-end 2)
+ "-spec code_change(" n>
+ "OldVsn :: term() | {down,term()}," n>
+ "State :: term(), Data :: term(), Extra :: term()) ->" n>
+ "{ok, NewState :: term(), NewData :: term()} |" n>
+ "(Reason :: term())." n
+ "code_change(_OldVsn, State, Data, _Extra) ->" n>
+ "{ok, State, Data}." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% Internal functions" n
+ (erlang-skel-double-separator-end 3)
+ )
+ "*The template of a gen_statem (StateName/3).
+Please see the function `tempo-define-template'.")
+
+(defvar erlang-skel-gen-statem-handle-event
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(gen_statem)." n n
+
+ "%% API" n
+ "-export([start_link/0])." n
+ n
+ "%% gen_statem callbacks" n
+ "-export([callback_mode/0, init/1, terminate/3, code_change/4])." n
+ "-export([handle_event/4])." n
+ n
+ "-define(SERVER, ?MODULE)." n
+ n
+ "-record(data, {})." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% API" n
+ (erlang-skel-double-separator-end 3) n
+ (erlang-skel-separator-start 2)
+ "%% @doc" n
+ "%% Creates a gen_statem process which calls Module:init/1 to" n
+ "%% initialize. To ensure a synchronized start-up procedure, this" n
+ "%% function does not return until Module:init/1 has returned." n
+ "%%" n
+ (erlang-skel-separator-end 2)
+ "-spec start_link() ->" n>
+ "{ok, Pid :: pid()} |" n>
+ "ignore |" n>
+ "{error, Error :: term()}." n
+ "start_link() ->" n>
+ "gen_statem:start_link({local, ?SERVER}, ?MODULE, [], [])." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% gen_statem callbacks" n
+ (erlang-skel-double-separator-end 3) n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Define the callback_mode() for this callback module." n
+ (erlang-skel-separator-end 2)
+ "-spec callback_mode() -> gen_statem:callback_mode_result()." n
+ "callback_mode() -> handle_event_function." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Whenever a gen_statem is started using gen_statem:start/[3,4] or" n
+ "%% gen_statem:start_link/[3,4], this function is called by the new" n
+ "%% process to initialize." n
(erlang-skel-separator-end 2)
- "-spec handle_event(" n>
- "gen_statem:event_type(), Msg :: term()," n>
- "State :: term(), Data :: term()) ->" n>
- "gen_statem:handle_event_result(). " n
+ "-spec init(Args :: term()) ->" n>
+ "gen_statem:init_result(term())." n
+ "init([]) ->" n>
+ "process_flag(trap_exit, true)," n>
+ "{ok, state_name, #data{}}." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called for every event a gen_statem receives." n
+ (erlang-skel-separator-end 2)
+ "-spec handle_event('enter'," n>
+ "OldState :: term()," n>
+ "State :: term()," n>
+ "Data :: term()) ->" n>
+ "gen_statem:state_enter_result(term());" n>
+ "(gen_statem:event_type()," n>
+ "Msg :: term()," n>
+ "State :: term()," n>
+ "Data :: term()) ->" n>
+ "gen_statem:event_handler_result(term())." n
+ ;;
"handle_event({call,From}, _Msg, State, Data) ->" n>
"{next_state, State, Data, [{reply,From,ok}]}." n
n
@@ -965,7 +1069,8 @@ Please see the function `tempo-define-template'.")
"-spec code_change(" n>
"OldVsn :: term() | {down,term()}," n>
"State :: term(), Data :: term(), Extra :: term()) ->" n>
- "{ok, NewState :: term(), NewData :: term()}." n
+ "{ok, NewState :: term(), NewData :: term()} |" n>
+ "(Reason :: term())." n
"code_change(_OldVsn, State, Data, _Extra) ->" n>
"{ok, State, Data}." n
n
@@ -973,7 +1078,7 @@ Please see the function `tempo-define-template'.")
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
)
- "*The template of a gen_statem.
+ "*The template of a gen_statem (handle_event/4).
Please see the function `tempo-define-template'.")
(defvar erlang-skel-wx-object
diff --git a/lib/tools/emacs/erlang-start.el b/lib/tools/emacs/erlang-start.el
index 76e0575e68..160057e179 100644
--- a/lib/tools/emacs/erlang-start.el
+++ b/lib/tools/emacs/erlang-start.el
@@ -78,6 +78,21 @@
(autoload 'erlang-find-tag-other-window "erlang"
"Like `find-tag-other-window'. Capable of retreiving Erlang modules.")
+;;
+;; Declare functions in "erlang-edoc.el".
+;;
+
+(autoload 'erlang-edoc-mode "erlang-edoc" "Toggle Erlang-Edoc mode on or off." t)
+
+;;
+;; Declare functions in "erldoc.el".
+;;
+
+(autoload 'erldoc-browse "erldoc" "\n\n(fn MFA)" t nil)
+(autoload 'erldoc-browse-topic "erldoc" "\n\n(fn TOPIC)" t nil)
+(autoload 'erldoc-apropos "erldoc" "\n\n(fn PATTERN)" t nil)
+(autoload 'erldoc-eldoc-function "erldoc" "\
+A function suitable for `eldoc-documentation-function'.\n\n(fn)" nil nil)
;;
;; Associate files extensions ".erl" and ".hrl" with Erlang mode.
diff --git a/lib/tools/emacs/erlang-test.el b/lib/tools/emacs/erlang-test.el
index 9a146632c5..ba6190d194 100644
--- a/lib/tools/emacs/erlang-test.el
+++ b/lib/tools/emacs/erlang-test.el
@@ -52,28 +52,42 @@ concatenated to form an erlang file to test on.")
(ert-deftest erlang-test-tags ()
(let* ((dir (make-temp-file "erlang-test" t))
- (erlang-file (expand-file-name "erlang_test.erl" dir))
- (tags-file (expand-file-name "TAGS" dir))
- tags-file-name tags-table-list erlang-buffer)
- (unwind-protect
- (progn
- (erlang-test-create-erlang-file erlang-file)
- (erlang-test-compile-tags erlang-file tags-file)
- (setq erlang-buffer (find-file-noselect erlang-file))
- (with-current-buffer erlang-buffer
- (setq-local tags-file-name tags-file))
- ;; Setting global tags-file-name is a workaround for
- ;; GNU Emacs bug#23164.
- (setq tags-file-name tags-file)
- (erlang-test-completion-table)
- (erlang-test-xref-find-definitions erlang-file erlang-buffer))
- (when (buffer-live-p erlang-buffer)
- (kill-buffer erlang-buffer))
- (let ((tags-buffer (find-buffer-visiting tags-file)))
- (when (buffer-live-p tags-buffer)
- (kill-buffer tags-buffer)))
- (when (file-exists-p dir)
- (delete-directory dir t)))))
+ (erlang-file (expand-file-name "erlang_test.erl" dir))
+ (tags-file (expand-file-name "TAGS" dir))
+ (old-tags-file-name (default-value 'tags-file-name))
+ (old-tags-table-list (default-value 'tags-table-list))
+ tags-file-name
+ tags-table-list
+ tags-table-set-list
+ erlang-buffer
+ erlang-mode-hook
+ prog-mode-hook
+ erlang-shell-mode-hook
+ tags-add-tables)
+ (unwind-protect
+ (progn
+ (setq-default tags-file-name nil)
+ (setq-default tags-table-list nil)
+ (erlang-test-create-erlang-file erlang-file)
+ (erlang-test-compile-tags erlang-file tags-file)
+ (setq erlang-buffer (find-file-noselect erlang-file))
+ (with-current-buffer erlang-buffer
+ (setq-local tags-file-name tags-file))
+ ;; Setting global tags-file-name is a workaround for
+ ;; GNU Emacs bug#23164.
+ (setq tags-file-name tags-file)
+ (erlang-test-complete-at-point tags-file)
+ (erlang-test-completion-table)
+ (erlang-test-xref-find-definitions erlang-file erlang-buffer))
+ (when (buffer-live-p erlang-buffer)
+ (kill-buffer erlang-buffer))
+ (let ((tags-buffer (find-buffer-visiting tags-file)))
+ (when (buffer-live-p tags-buffer)
+ (kill-buffer tags-buffer)))
+ (when (file-exists-p dir)
+ (delete-directory dir t))
+ (setq-default tags-file-name old-tags-file-name)
+ (setq-default tags-table-list old-tags-table-list))))
(defun erlang-test-create-erlang-file (erlang-file)
(with-temp-file erlang-file
@@ -116,6 +130,54 @@ concatenated to form an erlang file to test on.")
(should (eq expected-line (line-number-at-pos)))
(should (= (point-at-bol) (point))))
+(defun erlang-test-complete-at-point (tags-file)
+ (with-temp-buffer
+ (erlang-mode)
+ (setq-local tags-file-name tags-file)
+ (insert "\nerlang_test:fun")
+ (erlang-complete-tag)
+ (should (looking-back "erlang_test:function"))
+ (insert "\nfun")
+ (erlang-complete-tag)
+ (should (looking-back "function"))
+ (insert "\nerlang_")
+ (erlang-complete-tag)
+ (should (looking-back "erlang_test:"))))
+
+
+(ert-deftest erlang-test-compile-options ()
+ (erlang-test-format-opt t
+ "t")
+ (erlang-test-format-opt nil
+ "nil")
+ (erlang-test-format-opt (cons 1 2)
+ "{1, 2}")
+ (erlang-test-format-opt (list 1)
+ "[1]")
+ (erlang-test-format-opt (list 1 2)
+ "[1, 2]")
+ (erlang-test-format-opt (list 1 2 3)
+ "[1, 2, 3]")
+ (erlang-test-format-opt 'symbol
+ "symbol")
+ (erlang-test-format-opt "string"
+ "\"string\"")
+ (erlang-test-format-opt []
+ "{}")
+ (erlang-test-format-opt [1]
+ "{1}")
+ (erlang-test-format-opt [1 2]
+ "{1, 2}")
+ (erlang-test-format-opt [1 2 (3 [4 5 6] 7)]
+ "{1, 2, [3, {4, 5, 6}, 7]}"))
+
+(defun erlang-test-format-opt (elisp &optional expected-erlang)
+ (let ((erlang (inferior-erlang-format-opt elisp)))
+ (message "%s -> %s" elisp erlang)
+ (when expected-erlang
+ (should (equal erlang expected-erlang)))
+ erlang))
+
(provide 'erlang-test)
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index a2062180f3..39c6065ce4 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -59,7 +59,7 @@
;; Please state as exactly as possible:
;; - Version number of Erlang Mode (see the menu), Emacs, Erlang,
-;; and of any other relevant software.
+;; and of any other relevant software.
;; - What the expected result was.
;; - What you did, preferably in a repeatable step-by-step form.
;; - A description of the unexpected result.
@@ -78,6 +78,10 @@
;; Variables:
+(defgroup erlang nil
+ "The Erlang programming language."
+ :group 'languages)
+
(defconst erlang-version "2.7"
"The version number of Erlang mode.")
@@ -91,20 +95,20 @@ Erlang mode menu.")
(eval-and-compile
(defconst erlang-emacs-major-version
(if (boundp 'emacs-major-version)
- emacs-major-version
+ emacs-major-version
(string-match "\\([0-9]+\\)\\.\\([0-9]+\\)" emacs-version)
(erlang-string-to-int (substring emacs-version
- (match-beginning 1) (match-end 1))))
+ (match-beginning 1) (match-end 1))))
"Major version number of Emacs."))
(eval-and-compile
(defconst erlang-emacs-minor-version
- (if (boundp 'emacs-minor-version)
- emacs-minor-version
- (string-match "\\([0-9]+\\)\\.\\([0-9]+\\)" emacs-version)
- (erlang-string-to-int (substring emacs-version
- (match-beginning 2) (match-end 2))))
- "Minor version number of Emacs."))
+ (if (boundp 'emacs-minor-version)
+ emacs-minor-version
+ (string-match "\\([0-9]+\\)\\.\\([0-9]+\\)" emacs-version)
+ (erlang-string-to-int (substring emacs-version
+ (match-beginning 2) (match-end 2))))
+ "Minor version number of Emacs."))
(defconst erlang-xemacs-p (string-match "Lucid\\|XEmacs" emacs-version)
"Non-nil when running under XEmacs or Lucid Emacs.")
@@ -119,12 +123,12 @@ buffers in Erlang mode, just like under GNU Emacs.
Never EVER set this variable!")
(defvar erlang-menu-items '(erlang-menu-base-items
- erlang-menu-skel-items
- erlang-menu-shell-items
- erlang-menu-compile-items
- erlang-menu-man-items
- erlang-menu-personal-items
- erlang-menu-version-items)
+ erlang-menu-skel-items
+ erlang-menu-shell-items
+ erlang-menu-compile-items
+ erlang-menu-man-items
+ erlang-menu-personal-items
+ erlang-menu-version-items)
"*List of menu item list to combine to create Erlang mode menu.
External programs which temporarily add menu items to the Erlang mode
@@ -170,7 +174,7 @@ variable.")
("TAGS"
(("Find Tag" find-tag)
("Find Next Tag" erlang-find-next-tag)
- ;("Find Regexp" find-tag-regexp)
+ ;("Find Regexp" find-tag-regexp)
("Complete Word" erlang-complete-tag)
("Tags Apropos" tags-apropos)
("Search Files" tags-search))))
@@ -475,6 +479,11 @@ To activate the workaround, place the following in your `~/.emacs' file:
"*Indentation of Erlang calls/clauses within blocks.")
(put 'erlang-indent-level 'safe-local-variable 'integerp)
+(defvar erlang-icr-indent nil
+ "*Indentation of Erlang if/case/receive/ patterns. `nil' means
+ keeping default behavior. When non-nil, indent to th column of
+ if/case/receive.")
+
(defvar erlang-indent-guard 2
"*Indentation of Erlang guards.")
(put 'erlang-indent-guard 'safe-local-variable 'integerp)
@@ -558,35 +567,35 @@ This is an elisp list of options. Each option can be either:
Supporting \_< and \_> This is determined by checking the version of Emacs used."))
(eval-and-compile
- (defconst erlang-atom-quoted-regexp
+ (defconst erlang-atom-quoted-regexp
"'\\(?:[^\\']\\|\\(?:\\\\.\\)\\)*'"
"Regexp describing a single-quoted atom"))
(eval-and-compile
(defconst erlang-atom-regular-regexp
(if erlang-regexp-modern-p
- "\\_<[[:lower:]]\\(?:\\sw\\|\\s_\\)*\\_>"
+ "\\_<[[:lower:]]\\(?:\\sw\\|\\s_\\)*\\_>"
"\\<[[:lower:]]\\(?:\\sw\\|\\s_\\)*\\>")
"Regexp describing a regular (non-quoted) atom"))
(eval-and-compile
- (defconst erlang-atom-regexp
- (concat "\\(" erlang-atom-quoted-regexp "\\|"
- erlang-atom-regular-regexp "\\)")
+ (defconst erlang-atom-regexp
+ (concat "\\(" erlang-atom-quoted-regexp "\\|"
+ erlang-atom-regular-regexp "\\)")
"Regexp describing an Erlang atom."))
(eval-and-compile
(defconst erlang-atom-regexp-matches 1
"Number of regexp parenthesis pairs in `erlang-atom-regexp'.
-
+
This is used to determine parenthesis matches in complex regexps which
contains `erlang-atom-regexp'."))
(eval-and-compile
- (defconst erlang-variable-regexp
- (if erlang-regexp-modern-p
- "\\_<\\([[:upper:]_]\\(?:\\sw\\|\\s_\\)*\\)\\_>"
+ (defconst erlang-variable-regexp
+ (if erlang-regexp-modern-p
+ "\\_<\\([[:upper:]_]\\(?:\\sw\\|\\s_\\)*\\)\\_>"
"\\<\\([[:upper:]_]\\(?:\\sw\\|\\s_\\)*\\)\\>")
"Regexp which should match an Erlang variable.
@@ -605,13 +614,13 @@ This is used to determine matches in complex regexps which contains
"Like `regexp-opt', except if PAREN is `symbols', then the
resulting regexp is surrounded by \\_< and \\_>."
(if (eq paren 'symbols)
- (if erlang-regexp-modern-p
- (concat "\\_<" (regexp-opt strings t) "\\_>")
- (concat "\\<" (regexp-opt strings t) "\\>"))
+ (if erlang-regexp-modern-p
+ (concat "\\_<" (regexp-opt strings t) "\\_>")
+ (concat "\\<" (regexp-opt strings t) "\\>"))
(regexp-opt strings paren))))
-(eval-and-compile
+(eval-and-compile
(defvar erlang-keywords
'("after"
"begin"
@@ -630,7 +639,7 @@ resulting regexp is surrounded by \\_< and \\_>."
(eval-and-compile
(defconst erlang-keywords-regexp (erlang-regexp-opt erlang-keywords 'symbols)))
-
+
(eval-and-compile
(defvar erlang-operators
'("and"
@@ -653,7 +662,7 @@ resulting regexp is surrounded by \\_< and \\_>."
(eval-and-compile
(defconst erlang-operators-regexp (erlang-regexp-opt erlang-operators 'symbols)))
-
+
(eval-and-compile
(defvar erlang-guards
@@ -676,7 +685,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"binary"
"bitstring"
"boolean"
- ;;"float" ; Not included to avoid clashes with the bif float/1
+ ;;"float" ; Not included to avoid clashes with the bif float/1
"function"
"integer"
"list"
@@ -722,7 +731,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"Erlang type specs types"))
(eval-and-compile
- (defconst erlang-predefined-types-regexp
+ (defconst erlang-predefined-types-regexp
(erlang-regexp-opt erlang-predefined-types 'symbols)))
@@ -842,7 +851,7 @@ resulting regexp is surrounded by \\_< and \\_>."
(eval-and-compile
(defconst erlang-int-bif-regexp (erlang-regexp-opt erlang-int-bifs 'symbols)))
-
+
(eval-and-compile
(defvar erlang-ext-bifs
@@ -997,8 +1006,8 @@ behaviour.")
(let ((map (make-sparse-keymap)))
(unless (boundp 'indent-line-function)
(define-key map "\t" 'erlang-indent-command))
- (define-key map ";" 'erlang-electric-semicolon)
- (define-key map "," 'erlang-electric-comma)
+ (define-key map ";" 'erlang-electric-semicolon)
+ (define-key map "," 'erlang-electric-comma)
(define-key map "<" 'erlang-electric-lt)
(define-key map ">" 'erlang-electric-gt)
(define-key map "\C-m" 'erlang-electric-newline)
@@ -1010,7 +1019,7 @@ behaviour.")
(unless (boundp 'beginning-of-defun-function)
(define-key map "\M-\C-a" 'erlang-beginning-of-function)
(define-key map "\M-\C-e" 'erlang-end-of-function)
- (define-key map '(meta control h) 'erlang-mark-function)) ; Xemacs
+ (define-key map '(meta control h) 'erlang-mark-function)) ; Xemacs
(define-key map "\M-\t" 'erlang-complete-tag)
(define-key map "\C-c\M-\t" 'tempo-complete-tag)
(define-key map "\M-+" 'erlang-find-next-tag)
@@ -1064,7 +1073,7 @@ behaviour.")
(defvar erlang-font-lock-keywords-function-header
(list
(list (concat "^" erlang-atom-regexp "\\s-*(")
- 1 'font-lock-function-name-face t))
+ 1 'font-lock-function-name-face t))
"Font lock keyword highlighting a function header.")
(defface erlang-font-lock-exported-function-name-face
@@ -1086,7 +1095,7 @@ behaviour.")
(defvar erlang-font-lock-keywords-exported-function-header
(list
(list #'erlang-match-next-exported-function
- 1 'erlang-font-lock-exported-function-name-face t))
+ 1 'erlang-font-lock-exported-function-name-face t))
"Font lock keyword highlighting an exported function header.")
(defvar erlang-font-lock-keywords-int-bifs
@@ -1098,8 +1107,8 @@ behaviour.")
(defvar erlang-font-lock-keywords-ext-bifs
(list
(list (concat "\\<\\(erlang\\)\\s-*:\\s-*" erlang-ext-bif-regexp "\\s-*(")
- '(1 'font-lock-builtin-face)
- '(2 'font-lock-builtin-face)))
+ '(1 'font-lock-builtin-face)
+ '(2 'font-lock-builtin-face)))
"Font lock keyword highlighting built in functions.")
(defvar erlang-font-lock-keywords-int-function-calls
@@ -1113,7 +1122,7 @@ behaviour.")
(list (concat erlang-atom-regexp "\\s-*:\\s-*"
erlang-atom-regexp "\\s-*(")
'(1 'font-lock-type-face)
- '(2 'font-lock-type-face)))
+ '(2 'font-lock-type-face)))
"Font lock keyword highlighting an external function call.")
(defvar erlang-font-lock-keywords-fun-n
@@ -1131,7 +1140,7 @@ behaviour.")
(defvar erlang-font-lock-keywords-dollar
(list
(list "\\(\\$\\([^\\]\\|\\\\\\([^0-7^\n]\\|[0-7]+\\|\\^[a-zA-Z]\\)\\)\\)"
- 1 'font-lock-constant-face))
+ 1 'font-lock-constant-face))
"Font lock keyword highlighting numbers in ASCII form (e.g. $A).")
(defvar erlang-font-lock-keywords-arrow
@@ -1151,18 +1160,18 @@ behaviour.")
(defvar erlang-font-lock-keywords-attr
(list
- (list (concat "^\\(-" erlang-atom-regexp "\\)\\(\\s-\\|\\.\\|(\\)")
- 1 (if (boundp 'font-lock-preprocessor-face)
- 'font-lock-preprocessor-face
- 'font-lock-constant-face)))
+ (list (concat "^\\(-" erlang-atom-regexp "\\)\\(\\s-\\|\\.\\|(\\)")
+ 1 (if (boundp 'font-lock-preprocessor-face)
+ 'font-lock-preprocessor-face
+ 'font-lock-constant-face)))
"Font lock keyword highlighting attributes.")
(defvar erlang-font-lock-keywords-quotes
(list
(list "`\\([-+a-zA-Z0-9_:*][-+a-zA-Z0-9_:*]+\\)'"
- 1
- 'font-lock-keyword-face
- t))
+ 1
+ 'font-lock-keyword-face
+ t))
"Font lock keyword highlighting words in single quotes in comments.
This is not the highlighting of Erlang strings and atoms, which
@@ -1171,27 +1180,27 @@ are highlighted by syntactic analysis.")
(defvar erlang-font-lock-keywords-guards
(list
(list (concat "[^:]" erlang-guards-regexp "\\s-*(")
- 1 'font-lock-builtin-face))
+ 1 'font-lock-builtin-face))
"Font lock keyword highlighting guards.")
(defvar erlang-font-lock-keywords-predefined-types
(list
(list (concat "[^:]" erlang-predefined-types-regexp "\\s-*(")
- 1 'font-lock-builtin-face))
+ 1 'font-lock-builtin-face))
"Font lock keyword highlighting predefined types.")
(defvar erlang-font-lock-keywords-macros
(list
(list (concat "?\\s-*\\(" erlang-atom-regexp
- "\\|" erlang-variable-regexp "\\)")
- 1 'font-lock-constant-face)
+ "\\|" erlang-variable-regexp "\\)")
+ 1 'font-lock-constant-face)
(list (concat "^\\(-\\(?:define\\|ifn?def\\)\\)\\s-*(\\s-*\\(" erlang-atom-regexp
- "\\|" erlang-variable-regexp "\\)")
- (if (boundp 'font-lock-preprocessor-face)
- (list 1 'font-lock-preprocessor-face t)
- (list 1 'font-lock-constant-face t))
- (list 3 'font-lock-type-face t t))
+ "\\|" erlang-variable-regexp "\\)")
+ (if (boundp 'font-lock-preprocessor-face)
+ (list 1 'font-lock-preprocessor-face t)
+ (list 1 'font-lock-constant-face t))
+ (list 3 'font-lock-type-face t t))
(list "^-e\\(lse\\|ndif\\)\\>" 0 'font-lock-preprocessor-face t))
"Font lock keyword highlighting macros.
This must be placed in front of `erlang-font-lock-keywords-vars'.")
@@ -1202,8 +1211,8 @@ This must be placed in front of `erlang-font-lock-keywords-vars'.")
1 'font-lock-type-face)
;; Don't highlight numerical constants.
(list (if erlang-regexp-modern-p
- "\\_<[0-9]+#\\([0-9a-zA-Z]+\\)"
- "\\<[0-9]+#\\([0-9a-zA-Z]+\\)")
+ "\\_<[0-9]+#\\([0-9a-zA-Z]+\\)"
+ "\\<[0-9]+#\\([0-9a-zA-Z]+\\)")
1 nil t)
(list (concat "^-record\\s-*(\\s-*" erlang-atom-regexp)
1 'font-lock-type-face))
@@ -1212,8 +1221,8 @@ This must be placed in front of `erlang-font-lock-keywords-vars'.")
(defvar erlang-font-lock-keywords-vars
(list
- (list (concat "[^#]" erlang-variable-regexp) ; no numerical constants
- 1 'font-lock-variable-name-face))
+ (list (concat "[^#]" erlang-variable-regexp) ; no numerical constants
+ 1 'font-lock-variable-name-face))
"Font lock keyword highlighting Erlang variables.
Must be preceded by `erlang-font-lock-keywords-macros' to work properly.")
@@ -1236,32 +1245,32 @@ Example:
(defvar erlang-font-lock-keywords-1
(append erlang-font-lock-keywords-function-header
- erlang-font-lock-keywords-dollar
- erlang-font-lock-keywords-arrow
- erlang-font-lock-keywords-keywords
- )
+ erlang-font-lock-keywords-dollar
+ erlang-font-lock-keywords-arrow
+ erlang-font-lock-keywords-keywords
+ )
;; DocStringOrig: erlang-font-lock-keywords
erlang-font-lock-descr-string)
(defvar erlang-font-lock-keywords-2
(append erlang-font-lock-keywords-1
- erlang-font-lock-keywords-int-bifs
- erlang-font-lock-keywords-ext-bifs
- erlang-font-lock-keywords-attr
- erlang-font-lock-keywords-quotes
- erlang-font-lock-keywords-guards
- )
+ erlang-font-lock-keywords-int-bifs
+ erlang-font-lock-keywords-ext-bifs
+ erlang-font-lock-keywords-attr
+ erlang-font-lock-keywords-quotes
+ erlang-font-lock-keywords-guards
+ )
;; DocStringCopy: erlang-font-lock-keywords
erlang-font-lock-descr-string)
(defvar erlang-font-lock-keywords-3
(append erlang-font-lock-keywords-2
- erlang-font-lock-keywords-operators
- erlang-font-lock-keywords-macros
- erlang-font-lock-keywords-records
- erlang-font-lock-keywords-vars
- erlang-font-lock-keywords-predefined-types
- )
+ erlang-font-lock-keywords-operators
+ erlang-font-lock-keywords-macros
+ erlang-font-lock-keywords-records
+ erlang-font-lock-keywords-vars
+ erlang-font-lock-keywords-predefined-types
+ )
;; DocStringCopy: erlang-font-lock-keywords
erlang-font-lock-descr-string)
@@ -1269,10 +1278,10 @@ Example:
(append erlang-font-lock-keywords-3
erlang-font-lock-keywords-exported-function-header
erlang-font-lock-keywords-int-function-calls
- erlang-font-lock-keywords-ext-function-calls
- erlang-font-lock-keywords-fun-n
+ erlang-font-lock-keywords-ext-function-calls
+ erlang-font-lock-keywords-fun-n
erlang-font-lock-keywords-lc
- )
+ )
;; DocStringCopy: erlang-font-lock-keywords
erlang-font-lock-descr-string)
@@ -1333,17 +1342,17 @@ replaced by `erlang-etags-tags-completion-table'.")
(eval-when-compile
(if (or (featurep 'bytecomp)
- (featurep 'byte-compile))
+ (featurep 'byte-compile))
(progn
- (cond ((string-match "Lucid\\|XEmacs" emacs-version)
- (put 'comment-indent-hook 'byte-obsolete-variable nil)
- ;; Do not warn for unused variables
- ;; when compiling under XEmacs.
- (setq byte-compile-warnings
- '(free-vars unresolved callargs redefine))))
- (require 'comint)
- (require 'tempo)
- (require 'compile))))
+ (cond ((string-match "Lucid\\|XEmacs" emacs-version)
+ (put 'comment-indent-hook 'byte-obsolete-variable nil)
+ ;; Do not warn for unused variables
+ ;; when compiling under XEmacs.
+ (setq byte-compile-warnings
+ '(free-vars unresolved callargs redefine))))
+ (require 'comint)
+ (require 'tempo)
+ (require 'compile))))
(defun erlang-version ()
@@ -1351,7 +1360,7 @@ replaced by `erlang-etags-tags-completion-table'.")
(interactive)
(if (erlang-interactive-p)
(message "Erlang mode version %s, written by Anders Lindgren"
- erlang-version))
+ erlang-version))
erlang-version)
(defun erlang-interactive-p ()
@@ -1389,7 +1398,7 @@ useful commands:
C-c C-q - Indent current function.
M-; - Create a comment at the end of the line.
M-q - Fill a comment, i.e. wrap lines so that they (hopefully)
- will look better.
+ will look better.
M-a - Goto the beginning of an Erlang clause.
M-C-a - Ditto for function.
M-e - Goto the end of an Erlang clause.
@@ -1436,8 +1445,11 @@ Other commands:
(erlang-skel-init)
(when (fboundp 'tempo-use-tag-list)
(tempo-use-tag-list 'erlang-tempo-tags))
- (when (boundp 'xref-backend-functions)
- (add-hook 'xref-backend-functions #'erlang-etags--xref-backend nil t))
+ (when (and (fboundp 'add-function) (fboundp 'erldoc-eldoc-function))
+ (or eldoc-documentation-function
+ (setq-local eldoc-documentation-function #'ignore))
+ (add-function :before-until (local 'eldoc-documentation-function)
+ #'erldoc-eldoc-function))
(run-hooks 'erlang-mode-hook)
(if (zerop (buffer-size))
(run-hooks 'erlang-new-file-hook)))
@@ -1450,35 +1462,35 @@ Other commands:
(defun erlang-syntax-table-init ()
(if (null erlang-mode-syntax-table)
(let ((table (make-syntax-table)))
- (modify-syntax-entry ?\n ">" table)
- (modify-syntax-entry ?\" "\"" table)
- (modify-syntax-entry ?# "." table)
-;; (modify-syntax-entry ?$ "\\" table) ;; Creates problems with indention afterwards
-;; (modify-syntax-entry ?$ "'" table) ;; Creates syntax highlighting and indention problems
- (modify-syntax-entry ?$ "/" table) ;; Misses the corner case "string that ends with $"
- ;; we have to live with that for now..it is the best alternative
- ;; that can be worked around with "string hat ends with \$"
- (modify-syntax-entry ?% "<" table)
- (modify-syntax-entry ?& "." table)
- (modify-syntax-entry ?\' "\"" table)
- (modify-syntax-entry ?* "." table)
- (modify-syntax-entry ?+ "." table)
- (modify-syntax-entry ?- "." table)
- (modify-syntax-entry ?/ "." table)
- (modify-syntax-entry ?: "." table)
- (modify-syntax-entry ?< "." table)
- (modify-syntax-entry ?= "." table)
- (modify-syntax-entry ?> "." table)
- (modify-syntax-entry ?\\ "\\" table)
- (modify-syntax-entry ?_ "_" table)
- (modify-syntax-entry ?| "." table)
- (modify-syntax-entry ?^ "'" table)
-
- ;; Pseudo bit-syntax: Latin1 double angle quotes as parens.
- ;;(modify-syntax-entry ?\253 "(?\273" table)
- ;;(modify-syntax-entry ?\273 ")?\253" table)
-
- (setq erlang-mode-syntax-table table)))
+ (modify-syntax-entry ?\n ">" table)
+ (modify-syntax-entry ?\" "\"" table)
+ (modify-syntax-entry ?# "." table)
+ ;; (modify-syntax-entry ?$ "\\" table) ;; Creates problems with indention afterwards
+ ;; (modify-syntax-entry ?$ "'" table) ;; Creates syntax highlighting and indention problems
+ (modify-syntax-entry ?$ "/" table) ;; Misses the corner case "string that ends with $"
+ ;; we have to live with that for now..it is the best alternative
+ ;; that can be worked around with "string hat ends with \$"
+ (modify-syntax-entry ?% "<" table)
+ (modify-syntax-entry ?& "." table)
+ (modify-syntax-entry ?\' "\"" table)
+ (modify-syntax-entry ?* "." table)
+ (modify-syntax-entry ?+ "." table)
+ (modify-syntax-entry ?- "." table)
+ (modify-syntax-entry ?/ "." table)
+ (modify-syntax-entry ?: "." table)
+ (modify-syntax-entry ?< "." table)
+ (modify-syntax-entry ?= "." table)
+ (modify-syntax-entry ?> "." table)
+ (modify-syntax-entry ?\\ "\\" table)
+ (modify-syntax-entry ?_ "_" table)
+ (modify-syntax-entry ?| "." table)
+ (modify-syntax-entry ?^ "'" table)
+
+ ;; Pseudo bit-syntax: Latin1 double angle quotes as parens.
+ ;;(modify-syntax-entry ?\253 "(?\273" table)
+ ;;(modify-syntax-entry ?\273 ")?\253" table)
+
+ (setq erlang-mode-syntax-table table)))
(set-syntax-table erlang-mode-syntax-table))
@@ -1488,12 +1500,12 @@ Other commands:
;; delsel/pending-del mode. Also, set up text properties for bit
;; syntax handling.
(mapc #'(lambda (cmd)
- (put cmd 'delete-selection t) ;for delsel (Emacs)
- (put cmd 'pending-delete t)) ;for pending-del (XEmacs)
- '(erlang-electric-semicolon
- erlang-electric-comma
- erlang-electric-gt))
-
+ (put cmd 'delete-selection t) ;for delsel (Emacs)
+ (put cmd 'pending-delete t)) ;for pending-del (XEmacs)
+ '(erlang-electric-semicolon
+ erlang-electric-comma
+ erlang-electric-gt))
+
(put 'bitsyntax-open-outer 'syntax-table '(4 . ?>))
(put 'bitsyntax-open-outer 'rear-nonsticky '(category))
(put 'bitsyntax-open-inner 'rear-nonsticky '(category))
@@ -1514,8 +1526,6 @@ Other commands:
(setq paragraph-separate paragraph-start)
(make-local-variable 'paragraph-ignore-fill-prefix)
(setq paragraph-ignore-fill-prefix t)
- (make-local-variable 'require-final-newline)
- (setq require-final-newline t)
(make-local-variable 'defun-prompt-regexp)
(setq defun-prompt-regexp erlang-defun-prompt-regexp)
(make-local-variable 'comment-start)
@@ -1548,17 +1558,15 @@ Other commands:
(set (make-local-variable 'outline-regexp) "[[:lower:]0-9_]+ *(.*) *-> *$")
(set (make-local-variable 'outline-level) (lambda () 1))
(set (make-local-variable 'add-log-current-defun-function)
- 'erlang-current-defun)
- (set (make-local-variable 'find-tag-default-function)
- 'erlang-find-tag-for-completion))
+ 'erlang-current-defun))
(defun erlang-font-lock-init ()
"Initialize Font Lock for Erlang mode."
(or erlang-font-lock-syntax-table
(setq erlang-font-lock-syntax-table
- (let ((table (copy-syntax-table erlang-mode-syntax-table)))
- (modify-syntax-entry ?_ "w" table)
- table)))
+ (let ((table (copy-syntax-table erlang-mode-syntax-table)))
+ (modify-syntax-entry ?_ "w" table)
+ table)))
(set (make-local-variable 'font-lock-syntax-table)
erlang-font-lock-syntax-table)
(set (make-local-variable (if (boundp 'syntax-begin-function)
@@ -1567,23 +1575,23 @@ Other commands:
'erlang-beginning-of-clause)
(make-local-variable 'font-lock-keywords)
(let ((level (cond ((boundp 'font-lock-maximum-decoration)
- (symbol-value 'font-lock-maximum-decoration))
- ((boundp 'font-lock-use-maximal-decoration)
- (symbol-value 'font-lock-use-maximal-decoration))
- (t nil))))
+ (symbol-value 'font-lock-maximum-decoration))
+ ((boundp 'font-lock-use-maximal-decoration)
+ (symbol-value 'font-lock-use-maximal-decoration))
+ (t nil))))
(if (consp level)
- (setq level (cdr-safe (or (assq 'erlang-mode level)
- (assq t level)))))
+ (setq level (cdr-safe (or (assq 'erlang-mode level)
+ (assq t level)))))
;; `level' can here be:
;; A number - The fontification level
;; nil - Use the default
;; t - Use maximum
(cond ((eq level nil)
- (set 'font-lock-keywords erlang-font-lock-keywords))
- ((eq level 1)
- (set 'font-lock-keywords erlang-font-lock-keywords-1))
- ((eq level 2)
- (set 'font-lock-keywords erlang-font-lock-keywords-2))
+ (set 'font-lock-keywords erlang-font-lock-keywords))
+ ((eq level 1)
+ (set 'font-lock-keywords erlang-font-lock-keywords-1))
+ ((eq level 2)
+ (set 'font-lock-keywords erlang-font-lock-keywords-2))
((eq level 3)
(set 'font-lock-keywords erlang-font-lock-keywords-3))
(t
@@ -1592,11 +1600,11 @@ Other commands:
;; Modern font-locks can handle the above much more elegantly:
(set (make-local-variable 'font-lock-defaults)
'((erlang-font-lock-keywords erlang-font-lock-keywords-1
- erlang-font-lock-keywords-2
- erlang-font-lock-keywords-3
- erlang-font-lock-keywords-4)
- nil nil ((?_ . "w")) erlang-beginning-of-clause
- (font-lock-mark-block-function . erlang-mark-clause)
+ erlang-font-lock-keywords-2
+ erlang-font-lock-keywords-3
+ erlang-font-lock-keywords-4)
+ nil nil ((?_ . "w")) erlang-beginning-of-clause
+ (font-lock-mark-block-function . erlang-mark-clause)
(font-lock-syntactic-keywords
;; A dollar sign right before the double quote that ends a
;; string is not a character escape.
@@ -1610,8 +1618,8 @@ Other commands:
;; know whether matching started inside a string: limiting
;; search to a single line keeps things sane.
. (("\\(?:^\\|[^$]\\)\"\\(?:[^\"\n]\\|\\\\\"\\)*\\(\\$\\)\"" 1 "w")
- ;; Likewise for atoms
- ("\\(?:^\\|[^$]\\)'\\(?:[^'\n]\\|\\\\'\\)*\\(\\$\\)'" 1 "w")
+ ;; Likewise for atoms
+ ("\\(?:^\\|[^$]\\)'\\(?:[^'\n]\\|\\\\'\\)*\\(\\$\\)'" 1 "w")
;; And the dollar sign in $\" or $\' escapes two
;; characters, not just one.
("\\(\\$\\)\\\\[\"']" 1 "'"))))))
@@ -1657,17 +1665,17 @@ For a more elaborate example, please see the beginning of the file
(let ((res '()))
(while ks
(let* ((regexp (car (car ks)))
- (number (car (cdr (car ks))))
- (new-face (if (and faces (car faces))
- (car faces)
- (car (cdr (cdr (car ks))))))
- (overwrite (car (cdr (cdr (cdr (car ks))))))
- (new-keyword (list regexp number new-face)))
- (if overwrite (nconc new-keyword (list overwrite)))
- (setq res (cons new-keyword res))
- (setq ks (cdr ks))
- (if (and faces (cdr faces))
- (setq faces (cdr faces)))))
+ (number (car (cdr (car ks))))
+ (new-face (if (and faces (car faces))
+ (car faces)
+ (car (cdr (cdr (car ks))))))
+ (overwrite (car (cdr (cdr (cdr (car ks))))))
+ (new-keyword (list regexp number new-face)))
+ (if overwrite (nconc new-keyword (list overwrite)))
+ (setq res (cons new-keyword res))
+ (setq ks (cdr ks))
+ (if (and faces (cdr faces))
+ (setq faces (cdr faces)))))
(nreverse res)))
@@ -1759,57 +1767,57 @@ variable, i.e. it will popup when pressing the right mouse button.
Please see the variable `erlang-menu-base-items'."
(cond (erlang-xemacs-p
- (let ((menu (erlang-menu-xemacs name items keymap)))
- ;; We add the menu to the global menubar.
- ;;(funcall (symbol-function 'set-buffer-menubar)
- ;; (symbol-value 'current-menubar))
- (funcall (symbol-function 'add-submenu) nil menu)
- (setcdr erlang-xemacs-popup-menu (cdr menu))
- (if (and popup (boundp 'mode-popup-menu))
- (funcall (symbol-function 'set)
- 'mode-popup-menu erlang-xemacs-popup-menu))))
- ((>= erlang-emacs-major-version 19)
- (define-key keymap (vector 'menu-bar (intern name))
- (erlang-menu-make-keymap name items)))
- (t nil)))
+ (let ((menu (erlang-menu-xemacs name items keymap)))
+ ;; We add the menu to the global menubar.
+ ;;(funcall (symbol-function 'set-buffer-menubar)
+ ;; (symbol-value 'current-menubar))
+ (funcall (symbol-function 'add-submenu) nil menu)
+ (setcdr erlang-xemacs-popup-menu (cdr menu))
+ (if (and popup (boundp 'mode-popup-menu))
+ (funcall (symbol-function 'set)
+ 'mode-popup-menu erlang-xemacs-popup-menu))))
+ ((>= erlang-emacs-major-version 19)
+ (define-key keymap (vector 'menu-bar (intern name))
+ (erlang-menu-make-keymap name items)))
+ (t nil)))
(defun erlang-menu-make-keymap (name items)
"Build a menu for Emacs 19."
(let ((menumap (funcall (symbol-function 'make-sparse-keymap)
- name))
- (count 0)
- id def first second third)
+ name))
+ (count 0)
+ id def first second third)
(setq items (reverse items))
(while items
;; Replace any occurrence of atoms by their value.
(while (and items (atom (car items)) (not (null (car items))))
- (if (and (boundp (car items))
- (listp (symbol-value (car items))))
- (setq items (append (reverse (symbol-value (car items)))
- (cdr items)))
- (setq items (cdr items))))
+ (if (and (boundp (car items))
+ (listp (symbol-value (car items))))
+ (setq items (append (reverse (symbol-value (car items)))
+ (cdr items)))
+ (setq items (cdr items))))
(setq first (car-safe (car items)))
(setq second (car-safe (cdr-safe (car items))))
(setq third (car-safe (cdr-safe (cdr-safe (car items)))))
(cond ((null first)
- (setq count (+ count 1))
- (setq id (intern (format "separator-%d" count)))
- (setq def '("--" . nil)))
- ((and (consp second) (eq (car second) 'lambda))
- (setq count (+ count 1))
- (setq id (intern (format "lambda-%d" count)))
- (setq def (cons first second)))
- ((symbolp second)
- (setq id second)
- (setq def (cons first second)))
- (t
- (setq count (+ count 1))
- (setq id (intern (format "submenu-%d" count)))
- (setq def (erlang-menu-make-keymap first second))))
+ (setq count (+ count 1))
+ (setq id (intern (format "separator-%d" count)))
+ (setq def '("--" . nil)))
+ ((and (consp second) (eq (car second) 'lambda))
+ (setq count (+ count 1))
+ (setq id (intern (format "lambda-%d" count)))
+ (setq def (cons first second)))
+ ((symbolp second)
+ (setq id second)
+ (setq def (cons first second)))
+ (t
+ (setq count (+ count 1))
+ (setq id (intern (format "submenu-%d" count)))
+ (setq def (erlang-menu-make-keymap first second))))
(define-key menumap (vector id) def)
(if third
- (put id 'menu-enable third))
+ (put id 'menu-enable third))
(setq items (cdr items)))
(cons name menumap)))
@@ -1817,30 +1825,30 @@ Please see the variable `erlang-menu-base-items'."
(defun erlang-menu-xemacs (name items &optional keymap)
"Build a menu for XEmacs."
(let ((res '())
- first second third entry)
+ first second third entry)
(while items
;; Replace any occurrence of atoms by their value.
(while (and items (atom (car items)) (not (null (car items))))
- (if (and (boundp (car items))
- (listp (symbol-value (car items))))
- (setq items (append (reverse (symbol-value (car items)))
- (cdr items)))
- (setq items (cdr items))))
+ (if (and (boundp (car items))
+ (listp (symbol-value (car items))))
+ (setq items (append (reverse (symbol-value (car items)))
+ (cdr items)))
+ (setq items (cdr items))))
(setq first (car-safe (car items)))
(setq second (car-safe (cdr-safe (car items))))
(setq third (car-safe (cdr-safe (cdr-safe (car items)))))
(cond ((null first)
- (setq res (cons "------" res)))
- ((symbolp second)
- (setq res (cons (vector first second (or third t)) res)))
- ((and (consp second) (eq (car second) 'lambda))
- (setq res (cons (vector first (list 'call-interactively second)
- (or third t)) res)))
- (t
- (setq res (cons (cons first
- (cdr (erlang-menu-xemacs
- first second)))
- res))))
+ (setq res (cons "------" res)))
+ ((symbolp second)
+ (setq res (cons (vector first second (or third t)) res)))
+ ((and (consp second) (eq (car second) 'lambda))
+ (setq res (cons (vector first (list 'call-interactively second)
+ (or third t)) res)))
+ (t
+ (setq res (cons (cons first
+ (cdr (erlang-menu-xemacs
+ first second)))
+ res))))
(setq items (cdr items)))
(setq res (reverse res))
;; When adding a menu to a minor-mode keymap under Emacs,
@@ -1849,15 +1857,15 @@ Please see the variable `erlang-menu-base-items'."
;; (This could be expressed much clearer using backquotes,
;; but I don't want to pull in every package.)
(if keymap
- (let ((expr (list 'or
- (list 'eq keymap 'global-map)
- (list 'eq keymap (list 'current-local-map))
- (list 'symbol-value
- (list 'car-safe
- (list 'rassq
- keymap
- 'minor-mode-map-alist))))))
- (setq res (cons ':included (cons expr res)))))
+ (let ((expr (list 'or
+ (list 'eq keymap 'global-map)
+ (list 'eq keymap (list 'current-local-map))
+ (list 'symbol-value
+ (list 'car-safe
+ (list 'rassq
+ keymap
+ 'minor-mode-map-alist))))))
+ (setq res (cons ':included (cons expr res)))))
(cons name res)))
@@ -1872,13 +1880,13 @@ ALIST is list of pairs where the car is the old function and cdr the new."
(setq first (car-safe (car items)))
(setq second (car-safe (cdr-safe (car items))))
(cond ((null first))
- ((symbolp second)
- (setq pair (and second (assq second alist)))
- (if pair
- (setcar (cdr (car items)) (cdr pair))))
- ((and (consp second) (eq (car second) 'lambda)))
- (t
- (erlang-menu-substitute second alist)))
+ ((symbolp second)
+ (setq pair (and second (assq second alist)))
+ (if pair
+ (setcar (cdr (car items)) (cdr pair))))
+ ((and (consp second) (eq (car second) 'lambda)))
+ (t
+ (erlang-menu-substitute second alist)))
(setq items (cdr items)))))
@@ -1913,27 +1921,27 @@ Example:
\(setq erlang-menu-items
(erlang-menu-add-below 'my-erlang-menu-items
- 'erlang-menu-base-items
+ 'erlang-menu-base-items
erlang-menu-items))"
(if (memq entry items)
- items ; Return the original menu.
+ items ; Return the original menu.
(let ((head '())
- (done nil)
- res)
+ (done nil)
+ res)
(while (not done)
- (cond ((null items)
- (setq res (append head (list entry)))
- (setq done t))
- ((eq below (car items))
- (setq res
- (if above-p
- (append head (cons entry items))
- (append head (cons (car items)
- (cons entry (cdr items))))))
- (setq done t))
- (t
- (setq head (append head (list (car items))))
- (setq items (cdr items)))))
+ (cond ((null items)
+ (setq res (append head (list entry)))
+ (setq done t))
+ ((eq below (car items))
+ (setq res
+ (if above-p
+ (append head (cons entry items))
+ (append head (cons (car items)
+ (cons entry (cdr items))))))
+ (setq done t))
+ (t
+ (setq head (append head (list (car items))))
+ (setq items (cdr items)))))
res)))
(defun erlang-menu-delete (entry items)
@@ -1954,16 +1962,16 @@ the location of the manual pages."
(if erlang-man-inhibit
()
(setq erlang-menu-man-items
- '(nil
- ("Man - Function" erlang-man-function)))
+ '(nil
+ ("Man - Function" erlang-man-function)))
(if erlang-man-dirs
- (setq erlang-menu-man-items
- (append erlang-menu-man-items
- (erlang-man-make-top-menu erlang-man-dirs))))
+ (setq erlang-menu-man-items
+ (append erlang-menu-man-items
+ (erlang-man-make-top-menu erlang-man-dirs))))
(setq erlang-menu-items
- (erlang-menu-add-above 'erlang-menu-man-items
- 'erlang-menu-version-items
- erlang-menu-items))
+ (erlang-menu-add-above 'erlang-menu-man-items
+ 'erlang-menu-version-items
+ erlang-menu-items))
(erlang-menu-init)))
@@ -1971,7 +1979,7 @@ the location of the manual pages."
"Remove the man pages from the Erlang mode."
(interactive)
(setq erlang-menu-items
- (erlang-menu-delete 'erlang-menu-man-items erlang-menu-items))
+ (erlang-menu-delete 'erlang-menu-man-items erlang-menu-items))
(erlang-menu-init))
@@ -1985,28 +1993,28 @@ the location of the manual pages."
"Create one menu entry per element of DIR-LIST.
The format is described in the documentation of `erlang-man-dirs'."
(let ((menu '())
- dir)
+ dir)
(while dir-list
(setq dir (cond ((nth 2 (car dir-list))
- ;; Relative to `erlang-root-dir'.
- (and (stringp erlang-root-dir)
- (concat erlang-root-dir (nth 1 (car dir-list)))))
- (t
- ;; Absolute
- (nth 1 (car dir-list)))))
+ ;; Relative to `erlang-root-dir'.
+ (and (stringp erlang-root-dir)
+ (concat erlang-root-dir (nth 1 (car dir-list)))))
+ (t
+ ;; Absolute
+ (nth 1 (car dir-list)))))
(if (and dir
- (file-readable-p dir))
- (setq menu (cons (list (car (car dir-list))
- (erlang-man-make-middle-menu
- (erlang-man-get-files dir)))
- menu)))
+ (file-readable-p dir))
+ (setq menu (cons (list (car (car dir-list))
+ (erlang-man-make-middle-menu
+ (erlang-man-get-files dir)))
+ menu)))
(setq dir-list (cdr dir-list)))
;; Should no menus be found, generate a menu item which
;; will display a help text, when selected.
(if menu
- (nreverse menu)
+ (nreverse menu)
'(("Man Pages"
- (("Error! Why?" erlang-man-describe-error)))))))
+ (("Error! Why?" erlang-man-describe-error)))))))
;; Should the menu be to long, let's split it into a number of
@@ -2020,32 +2028,32 @@ menus is created."
(if (<= (length filelist) erlang-man-max-menu-size)
(erlang-man-make-menu filelist)
(let ((menu '())
- (filelist (copy-sequence filelist))
- segment submenu pair)
+ (filelist (copy-sequence filelist))
+ segment submenu pair)
(while filelist
- (setq pair (nthcdr (- erlang-man-max-menu-size 1) filelist))
- (setq segment filelist)
- (if (null pair)
- (setq filelist nil)
- (setq filelist (cdr pair))
- (setcdr pair nil))
- (setq submenu (erlang-man-make-menu segment))
- (setq menu (cons (list (concat (car (car submenu))
- " -- "
- (car (car (reverse submenu))))
- submenu)
- menu)))
+ (setq pair (nthcdr (- erlang-man-max-menu-size 1) filelist))
+ (setq segment filelist)
+ (if (null pair)
+ (setq filelist nil)
+ (setq filelist (cdr pair))
+ (setcdr pair nil))
+ (setq submenu (erlang-man-make-menu segment))
+ (setq menu (cons (list (concat (car (car submenu))
+ " -- "
+ (car (car (reverse submenu))))
+ submenu)
+ menu)))
(nreverse menu))))
(defun erlang-man-make-menu (filelist)
"Make a leaf menu based on FILELIST."
(let ((menu '())
- item)
+ item)
(while filelist
(setq item (erlang-man-make-menu-item (car filelist)))
(if item
- (setq menu (cons item menu)))
+ (setq menu (cons item menu)))
(setq filelist (cdr filelist)))
(nreverse menu)))
@@ -2054,11 +2062,11 @@ menus is created."
"Create a menu item containing the name of the man page."
(and (string-match ".+/\\([^/]+\\)\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$" file)
(let ((page (substring file (match-beginning 1) (match-end 1))))
- (list (capitalize page)
- (list 'lambda '()
- '(interactive)
- (list 'funcall 'erlang-man-display-function
- file))))))
+ (list (capitalize page)
+ (list 'lambda '()
+ '(interactive)
+ (list 'funcall 'erlang-man-display-function
+ file))))))
(defun erlang-man-get-files (dir)
@@ -2071,33 +2079,33 @@ menus is created."
This function is aware of imported functions."
(interactive
(list (let* ((mod (car-safe (erlang-get-function-under-point)))
- (input (read-string
- (format "Manual entry for module%s: "
- (if (or (null mod) (string= mod ""))
- ""
- (format " (default %s)" mod))))))
- (if (string= input "")
- mod
- input))))
+ (input (read-string
+ (format "Manual entry for module%s: "
+ (if (or (null mod) (string= mod ""))
+ ""
+ (format " (default %s)" mod))))))
+ (if (string= input "")
+ mod
+ input))))
(or module (setq module (car (erlang-get-function-under-point))))
(if (or (null module) (string= module ""))
(error "No Erlang module name given"))
(let ((dir-list erlang-man-dirs)
- (pat (concat "/" (regexp-quote module) "\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$"))
- (file nil)
- file-list)
+ (pat (concat "/" (regexp-quote module) "\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$"))
+ (file nil)
+ file-list)
(while (and dir-list (null file))
(setq file-list (erlang-man-get-files
- (if (nth 2 (car dir-list))
- (concat erlang-root-dir (nth 1 (car dir-list)))
- (nth 1 (car dir-list)))))
+ (if (nth 2 (car dir-list))
+ (concat erlang-root-dir (nth 1 (car dir-list)))
+ (nth 1 (car dir-list)))))
(while (and file-list (null file))
- (if (string-match pat (car file-list))
- (setq file (car file-list)))
- (setq file-list (cdr file-list)))
+ (if (string-match pat (car file-list))
+ (setq file (car file-list)))
+ (setq file-list (cdr file-list)))
(setq dir-list (cdr dir-list)))
(if file
- (funcall erlang-man-display-function file)
+ (funcall erlang-man-display-function file)
(error "No manual page for module %s found" module))))
@@ -2119,55 +2127,55 @@ The entry for `function' is displayed.
This function is aware of imported functions."
(interactive
(list (let* ((mod-func (erlang-get-function-under-point))
- (mod (car-safe mod-func))
- (func (nth 1 mod-func))
- (input (read-string
- (format
- "Manual entry for `module:func' or `module'%s: "
- (if (or (null mod) (string= mod ""))
- ""
- (format " (default %s:%s)" mod func))))))
- (if (string= input "")
- (if (and mod func)
- (concat mod ":" func)
- mod)
- input))))
+ (mod (car-safe mod-func))
+ (func (nth 1 mod-func))
+ (input (read-string
+ (format
+ "Manual entry for `module:func' or `module'%s: "
+ (if (or (null mod) (string= mod ""))
+ ""
+ (format " (default %s:%s)" mod func))))))
+ (if (string= input "")
+ (if (and mod func)
+ (concat mod ":" func)
+ mod)
+ input))))
;; Emacs 18 doesn't provide `man'...
(condition-case nil
(require 'man)
(error nil))
(let ((modname nil)
- (funcname nil))
+ (funcname nil))
(cond ((null name)
- (let ((mod-func (erlang-get-function-under-point)))
- (setq modname (car-safe mod-func))
- (setq funcname (nth 1 mod-func))))
- ((string-match ":" name)
- (setq modname (substring name 0 (match-beginning 0)))
- (setq funcname (substring name (match-end 0) nil)))
- ((stringp name)
- (setq modname name)))
+ (let ((mod-func (erlang-get-function-under-point)))
+ (setq modname (car-safe mod-func))
+ (setq funcname (nth 1 mod-func))))
+ ((string-match ":" name)
+ (setq modname (substring name 0 (match-beginning 0)))
+ (setq funcname (substring name (match-end 0) nil)))
+ ((stringp name)
+ (setq modname name)))
(if (or (null modname) (string= modname ""))
- (error "No Erlang module name given"))
+ (error "No Erlang module name given"))
(cond ((fboundp 'Man-notify-when-ready)
- ;; Emacs 19: The man command could possibly start an
- ;; asynchronous process, i.e. we must hook ourselves into
- ;; the system to be activated when the man-process
- ;; terminates.
- (if (null funcname)
- ()
- (erlang-man-patch-notify)
- (setq erlang-man-function-name funcname))
- (condition-case nil
- (erlang-man-module modname)
- (error (setq erlang-man-function-name nil))))
- (t
- (erlang-man-module modname)
- (if funcname
- (erlang-man-find-function
- (or (get-buffer "*Manual Entry*") ; Emacs 18
- (current-buffer)) ; XEmacs
- funcname))))))
+ ;; Emacs 19: The man command could possibly start an
+ ;; asynchronous process, i.e. we must hook ourselves into
+ ;; the system to be activated when the man-process
+ ;; terminates.
+ (if (null funcname)
+ ()
+ (erlang-man-patch-notify)
+ (setq erlang-man-function-name funcname))
+ (condition-case nil
+ (erlang-man-module modname)
+ (error (setq erlang-man-function-name nil))))
+ (t
+ (erlang-man-module modname)
+ (if funcname
+ (erlang-man-find-function
+ (or (get-buffer "*Manual Entry*") ; Emacs 18
+ (current-buffer)) ; XEmacs
+ funcname))))))
;; Should the defadvice be at the top level, the package `advice' would
@@ -2185,14 +2193,14 @@ command is executed asynchronously."
;; This should never happened since this is only called when
;; running under Emacs 19.
(error (error (concat "This command needs the package `advice', "
- "please upgrade your Emacs."))))
+ "please upgrade your Emacs."))))
(require 'man)
(defadvice Man-notify-when-ready
- (after erlang-Man-notify-when-ready activate)
+ (after erlang-Man-notify-when-ready activate)
"Set point at the documentation of the function name in
`erlang-man-function-name' when the man page is displayed."
(if erlang-man-function-name
- (erlang-man-find-function (ad-get-arg 0) erlang-man-function-name))
+ (erlang-man-find-function (ad-get-arg 0) erlang-man-function-name))
(setq erlang-man-function-name nil)))
@@ -2200,17 +2208,17 @@ command is executed asynchronously."
"Find manual page for function in `erlang-man-function-name' in buffer BUF."
(if func
(let ((win (get-buffer-window buf)))
- (if win
- (progn
- (set-buffer buf)
- (goto-char (point-min))
- (if (re-search-forward
- (concat "^[ \t]+" func " ?(")
- (point-max) t)
- (progn
- (forward-word -1)
- (set-window-point win (point)))
- (message "Could not find function `%s'" func)))))))
+ (if win
+ (progn
+ (set-buffer buf)
+ (goto-char (point-min))
+ (if (re-search-forward
+ (concat "^[ \t]+" func " ?(")
+ (point-max) t)
+ (progn
+ (forward-word -1)
+ (set-window-point win (point)))
+ (message "Could not find function `%s'" func)))))))
(defun erlang-man-display (file)
@@ -2224,25 +2232,25 @@ to be used."
(error nil))
(if file
(let ((process-environment (copy-sequence process-environment)))
- (if (string-match "\\(.*\\)/man[^/]*/\\([^.]+\\)\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$" file)
- (let ((dir (substring file (match-beginning 1) (match-end 1)))
- (page (substring file (match-beginning 2) (match-end 2))))
- (if (fboundp 'setenv)
- (setenv "MANPATH" dir)
- ;; Emacs 18
- (setq process-environment (cons (concat "MANPATH=" dir)
- process-environment)))
- (cond ((not (and (not erlang-xemacs-p)
- (= erlang-emacs-major-version 19)
- (< erlang-emacs-minor-version 29)))
- (manual-entry page))
- (t
- ;; Emacs 19.28 and earlier versions of 19:
- ;; The manual-entry command unconditionally prompts
- ;; the user :-(
- (funcall (symbol-function 'Man-getpage-in-background)
- page))))
- (error "Can't find man page for %s\n" file)))))
+ (if (string-match "\\(.*\\)/man[^/]*/\\([^.]+\\)\\.\\([124-9]\\|3\\(erl\\)?\\)\\(\\.gz\\)?$" file)
+ (let ((dir (substring file (match-beginning 1) (match-end 1)))
+ (page (substring file (match-beginning 2) (match-end 2))))
+ (if (fboundp 'setenv)
+ (setenv "MANPATH" dir)
+ ;; Emacs 18
+ (setq process-environment (cons (concat "MANPATH=" dir)
+ process-environment)))
+ (cond ((not (and (not erlang-xemacs-p)
+ (= erlang-emacs-major-version 19)
+ (< erlang-emacs-minor-version 29)))
+ (manual-entry page))
+ (t
+ ;; Emacs 19.28 and earlier versions of 19:
+ ;; The manual-entry command unconditionally prompts
+ ;; the user :-(
+ (funcall (symbol-function 'Man-getpage-in-background)
+ page))))
+ (error "Can't find man page for %s\n" file)))))
(defun erlang-man-describe-error ()
@@ -2285,43 +2293,43 @@ package not be present, this function does nothing."
(error t))
(if (featurep 'tempo)
(let ((skel erlang-skel)
- (menu '()))
- (while skel
- (cond ((null (car skel))
- (setq menu (cons nil menu)))
- (t
- (funcall (symbol-function 'tempo-define-template)
- (concat "erlang-" (nth 1 (car skel)))
- ;; The tempo template used contains an `include'
- ;; function call only, hence changes to the
- ;; variables describing the templates take effect
- ;; immdiately.
- (list (list 'erlang-skel-include (nth 2 (car skel))))
- (nth 1 (car skel))
- (car (car skel))
- 'erlang-tempo-tags)
- (setq menu (cons (erlang-skel-make-menu-item
- (car skel)) menu))))
- (setq skel (cdr skel)))
- (setq erlang-menu-skel-items
- (list nil (list "Skeletons" (nreverse menu))))
- (setq erlang-menu-items
- (erlang-menu-add-above 'erlang-menu-skel-items
- 'erlang-menu-version-items
- erlang-menu-items))
- (erlang-menu-init))))
+ (menu '()))
+ (while skel
+ (cond ((null (car skel))
+ (setq menu (cons nil menu)))
+ (t
+ (funcall (symbol-function 'tempo-define-template)
+ (concat "erlang-" (nth 1 (car skel)))
+ ;; The tempo template used contains an `include'
+ ;; function call only, hence changes to the
+ ;; variables describing the templates take effect
+ ;; immdiately.
+ (list (list 'erlang-skel-include (nth 2 (car skel))))
+ (nth 1 (car skel))
+ (car (car skel))
+ 'erlang-tempo-tags)
+ (setq menu (cons (erlang-skel-make-menu-item
+ (car skel)) menu))))
+ (setq skel (cdr skel)))
+ (setq erlang-menu-skel-items
+ (list nil (list "Skeletons" (nreverse menu))))
+ (setq erlang-menu-items
+ (erlang-menu-add-above 'erlang-menu-skel-items
+ 'erlang-menu-version-items
+ erlang-menu-items))
+ (erlang-menu-init))))
(defun erlang-skel-make-menu-item (skel)
(let ((func (intern (concat "tempo-template-erlang-" (nth 1 skel)))))
(cond ((null (nth 3 skel))
- (list (car skel) func))
- (t
- (list (car skel)
- (list 'lambda '()
- '(interactive)
- (list 'funcall
- (list 'quote (nth 3 skel))
- (list 'quote func))))))))
+ (list (car skel) func))
+ (t
+ (list (car skel)
+ (list 'lambda '()
+ '(interactive)
+ (list 'funcall
+ (list 'quote (nth 3 skel))
+ (list 'quote func))))))))
;; Functions designed to be added to the skeleton menu.
;; (Not normally used)
@@ -2354,12 +2362,12 @@ Technically, this function returns the `tempo' attribute`(l ...)' which
can contain other `tempo' attributes. Please see the function
`tempo-define-template' for a description of the `(l ...)' attribute."
(let ((res '())
- entry)
+ entry)
(while args
(setq entry (car args))
(while entry
- (setq res (cons (car entry) res))
- (setq entry (cdr entry)))
+ (setq res (cons (car entry) res))
+ (setq entry (cdr entry)))
(setq args (cdr args)))
(cons 'l (nreverse res))))
@@ -2368,25 +2376,25 @@ can contain other `tempo' attributes. Please see the function
(defun erlang-skel-separator (&optional percent)
"Return a comment separator."
(let ((percent (or percent 3)))
- (concat (make-string percent ?%)
- (make-string (- erlang-skel-separator-length percent) ?-)
- "\n")))
+ (concat (make-string percent ?%)
+ (make-string (- erlang-skel-separator-length percent) ?-)
+ "\n")))
(defun erlang-skel-double-separator (&optional percent)
"Return a comment separator."
(let ((percent (or percent 3)))
- (concat (make-string percent ?%)
- (make-string (- erlang-skel-separator-length percent) ?=)
- "\n")))
+ (concat (make-string percent ?%)
+ (make-string (- erlang-skel-separator-length percent) ?=)
+ "\n")))
(defun erlang-skel-dd-mmm-yyyy ()
"Return the current date as a string in \"DD Mon YYYY\" form.
The first character of DD is space if the value is less than 10."
(let ((date (current-time-string)))
(format "%2d %s %s"
- (erlang-string-to-int (substring date 8 10))
- (substring date 4 7)
- (substring date -4))))
+ (erlang-string-to-int (substring date 8 10))
+ (substring date 4 7)
+ (substring date -4))))
;; Indentation code:
@@ -2399,23 +2407,23 @@ rigidly along with this one."
;; If arg, always indent this line as Erlang
;; and shift remaining lines of clause the same amount.
(let ((shift-amt (erlang-indent-line))
- beg end)
- (save-excursion
- (if erlang-tab-always-indent
- (beginning-of-line))
- (setq beg (point))
- (erlang-end-of-clause 1)
- (setq end (point))
- (goto-char beg)
- (forward-line 1)
- (setq beg (point)))
- (if (> end beg)
- (indent-code-rigidly beg end shift-amt "\n")))
+ beg end)
+ (save-excursion
+ (if erlang-tab-always-indent
+ (beginning-of-line))
+ (setq beg (point))
+ (erlang-end-of-clause 1)
+ (setq end (point))
+ (goto-char beg)
+ (forward-line 1)
+ (setq beg (point)))
+ (if (> end beg)
+ (indent-code-rigidly beg end shift-amt "\n")))
(if (and (not erlang-tab-always-indent)
- (save-excursion
- (skip-chars-backward " \t")
- (not (bolp))))
- (insert-tab)
+ (save-excursion
+ (skip-chars-backward " \t")
+ (not (bolp))))
+ (insert-tab)
(erlang-indent-line))))
@@ -2423,33 +2431,34 @@ rigidly along with this one."
"Indent current line as Erlang code.
Return the amount the indentation changed by."
(let ((pos (- (point-max) (point)))
- indent beg
- shift-amt)
+ indent beg
+ shift-amt)
(beginning-of-line 1)
(setq beg (point))
(skip-chars-forward " \t")
(cond ((looking-at "%")
- (setq indent (funcall comment-indent-function))
- (setq shift-amt (- indent (current-column))))
- (t
- (setq indent (erlang-calculate-indent))
- (cond ((null indent)
- (setq indent (current-indentation)))
- ((eq indent t)
- ;; This should never occur here.
- (error "Erlang mode error"))
- ;;((= (char-syntax (following-char)) ?\))
- ;; (setq indent (1- indent)))
- )
- (setq shift-amt (- indent (current-column)))))
+ (setq indent (funcall comment-indent-function))
+ (setq shift-amt (- indent (current-column))))
+ (t
+ (setq indent (erlang-calculate-indent))
+ (cond ((null indent)
+ (setq indent (current-indentation)))
+ ((eq indent t)
+ ;; This should never occur here.
+ (error "Erlang mode error"))
+ ;;((= (char-syntax (following-char)) ?\))
+ ;; (setq indent (1- indent)))
+ )
+ (setq shift-amt (- indent (current-column)))))
(if (zerop shift-amt)
- nil
+ nil
(delete-region beg (point))
(indent-to indent))
;; If initial point was within line's indentation, position
;; after the indentation. Else stay at same point in text.
(if (> (- (point-max) pos) (point))
- (goto-char (- (point-max) pos)))
+ (goto-char (- (point-max) pos)))
+ (run-hooks 'erlang-indent-line-hook)
shift-amt))
@@ -2460,11 +2469,11 @@ This is automagically called by the user level function `indent-region'."
(interactive "r")
(save-excursion
(let ((case-fold-search nil)
- (continue t)
- (from-end (- (point-max) end))
- indent-point;; The beginning of the current line
- indent;; The indent amount
- state)
+ (continue t)
+ (from-end (- (point-max) end))
+ indent-point;; The beginning of the current line
+ indent;; The indent amount
+ state)
(goto-char beg)
(beginning-of-line)
(setq indent-point (point))
@@ -2478,39 +2487,39 @@ This is automagically called by the user level function `indent-region'."
(error "Illegal syntax"))))
;; Indent every line in the region
(while continue
- (goto-char indent-point)
- (skip-chars-forward " \t")
- (cond ((looking-at "%")
- ;; Do not use our stack to help the user to customize
- ;; comment indentation.
- (setq indent (funcall comment-indent-function)))
- ((looking-at "$")
- ;; Don't indent empty lines.
- (setq indent 0))
- (t
- (setq indent
- (save-excursion
- (erlang-calculate-stack-indent (point) state)))
- (cond ((null indent)
- (setq indent (current-indentation)))
- ((eq indent t)
- ;; This should never occur here.
- (error "Erlang mode error"))
- ;;((= (char-syntax (following-char)) ?\))
- ;; (setq indent (1- indent)))
- )))
- (if (zerop (- indent (current-column)))
- nil
- (delete-region indent-point (point))
- (indent-to indent))
- ;; Find the next line in the region
- (goto-char indent-point)
- (save-excursion
- (forward-line 1)
- (setq indent-point (point)))
- (if (>= from-end (- (point-max) indent-point))
- (setq continue nil)
- (while (< (point) indent-point)
+ (goto-char indent-point)
+ (skip-chars-forward " \t")
+ (cond ((looking-at "%")
+ ;; Do not use our stack to help the user to customize
+ ;; comment indentation.
+ (setq indent (funcall comment-indent-function)))
+ ((looking-at "$")
+ ;; Don't indent empty lines.
+ (setq indent 0))
+ (t
+ (setq indent
+ (save-excursion
+ (erlang-calculate-stack-indent (point) state)))
+ (cond ((null indent)
+ (setq indent (current-indentation)))
+ ((eq indent t)
+ ;; This should never occur here.
+ (error "Erlang mode error"))
+ ;;((= (char-syntax (following-char)) ?\))
+ ;; (setq indent (1- indent)))
+ )))
+ (if (zerop (- indent (current-column)))
+ nil
+ (delete-region indent-point (point))
+ (indent-to indent))
+ ;; Find the next line in the region
+ (goto-char indent-point)
+ (save-excursion
+ (forward-line 1)
+ (setq indent-point (point)))
+ (if (>= from-end (- (point-max) indent-point))
+ (setq continue nil)
+ (while (< (point) indent-point)
(let ((pt (point)))
(setq state (erlang-partial-parse
pt indent-point state))
@@ -2532,7 +2541,7 @@ This is automagically called by the user level function `indent-region'."
(interactive)
(save-excursion
(let ((end (progn (erlang-end-of-function 1) (point)))
- (beg (progn (erlang-beginning-of-function 1) (point))))
+ (beg (progn (erlang-beginning-of-function 1) (point))))
(erlang-indent-region beg end))))
@@ -2541,7 +2550,7 @@ This is automagically called by the user level function `indent-region'."
(interactive)
(save-excursion
(let ((end (progn (erlang-end-of-clause 1) (point)))
- (beg (progn (erlang-beginning-of-clause 1) (point))))
+ (beg (progn (erlang-beginning-of-clause 1) (point))))
(erlang-indent-region beg end))))
@@ -2556,11 +2565,11 @@ This is automagically called by the user level function `indent-region'."
Return nil if line starts inside string, t if in a comment."
(save-excursion
(let ((indent-point (point))
- (case-fold-search nil)
- (state nil))
+ (case-fold-search nil)
+ (state nil))
(if parse-start
- (goto-char parse-start)
- (erlang-beginning-of-clause))
+ (goto-char parse-start)
+ (erlang-beginning-of-clause))
(while (< (point) indent-point)
(let ((pt (point)))
(setq state (erlang-partial-parse pt indent-point state))
@@ -2575,25 +2584,25 @@ Return nil if line starts inside string, t if in a comment."
(save-excursion
(let ((starting-point (point))
- (case-fold-search nil)
- (state nil))
+ (case-fold-search nil)
+ (state nil))
(erlang-beginning-of-clause)
(while (< (point) starting-point)
- (setq state (erlang-partial-parse (point) starting-point state)))
+ (setq state (erlang-partial-parse (point) starting-point state)))
(message "%S" state))))
(defun erlang-partial-parse (from to &optional state)
"Parse Erlang syntax starting at FROM until TO, with an optional STATE.
Value is list (stack token-start token-type in-what)."
- (goto-char from) ; Start at the beginning
+ (goto-char from) ; Start at the beginning
(erlang-skip-blank to)
(let ((cs (char-syntax (following-char)))
- (stack (car state))
- (token (point))
- in-what)
- (cond
-
+ (stack (car state))
+ (token (point))
+ in-what)
+ (cond
+
;; Done: Return previous state.
((>= token to)
(setq token (nth 1 state))
@@ -2603,230 +2612,230 @@ Value is list (stack token-start token-type in-what)."
;; Word constituent: check and handle keywords.
((= cs ?w)
(cond ((looking-at "\\(end\\|after\\)[^_a-zA-Z0-9]")
- ;; Must pop top icr layer, `after' will push a new
- ;; layer next.
- (progn
- (while (and stack (eq (car (car stack)) '->))
- (erlang-pop stack))
- (if (and stack (memq (car (car stack)) '(icr begin fun try)))
- (erlang-pop stack))))
- ((looking-at "catch\\b.*of")
- t)
- ((looking-at "catch\\b\\s *\\($\\|%\\|.*->\\)")
- ;; Must pop top icr layer, `catch' in try/catch
- ;;will push a new layer next.
- (progn
- (while (and stack (eq (car (car stack)) '->))
- (erlang-pop stack))
- (if (and stack (memq (car (car stack)) '(icr begin try)))
- (erlang-pop stack))))
- )
+ ;; Must pop top icr layer, `after' will push a new
+ ;; layer next.
+ (progn
+ (while (and stack (eq (car (car stack)) '->))
+ (erlang-pop stack))
+ (if (and stack (memq (car (car stack)) '(icr begin fun try)))
+ (erlang-pop stack))))
+ ((looking-at "catch\\b.*of")
+ t)
+ ((looking-at "catch\\b\\s *\\($\\|%\\|.*->\\)")
+ ;; Must pop top icr layer, `catch' in try/catch
+ ;;will push a new layer next.
+ (progn
+ (while (and stack (eq (car (car stack)) '->))
+ (erlang-pop stack))
+ (if (and stack (memq (car (car stack)) '(icr begin try)))
+ (erlang-pop stack))))
+ )
(cond ((looking-at "\\(if\\|case\\|receive\\)[^_a-zA-Z0-9]")
- ;; Must push a new icr (if/case/receive) layer.
- (erlang-push (list 'icr token (current-column)) stack))
- ((looking-at "\\(try\\|after\\)[^_a-zA-Z0-9]")
- ;; Must handle separately, try catch or try X of -> catch
- ;; same for `after', it could be
- ;; receive after Time -> X end, or
- ;; try after X end
- (erlang-push (list 'try token (current-column)) stack))
- ((looking-at "\\(of\\)[^_a-zA-Z0-9]")
- ;; Must handle separately, try X of -> catch
- (if (and stack (eq (car (car stack)) 'try))
- (let ((try-column (nth 2 (car stack)))
- (try-pos (nth 1 (car stack))))
- (erlang-pop stack)
- (erlang-push (list 'icr try-pos try-column) stack))))
-
- ((looking-at "\\(fun\\)[^_a-zA-Z0-9]")
- ;; Push a new layer if we are defining a `fun'
- ;; expression, not when we are refering an existing
- ;; function. 'fun's defines are only indented one level now.
- (if (save-excursion
- (goto-char (match-end 1))
- (erlang-skip-blank to)
- ;; Use erlang-variable-regexp here to look for an
- ;; optional variable name to match EEP37 named funs.
- (if (looking-at erlang-variable-regexp)
- (progn
- (goto-char (match-end 0))
- (erlang-skip-blank to)))
- (eq (following-char) ?\())
- (erlang-push (list 'fun token (current-column)) stack)))
- ((looking-at "\\(begin\\)[^_a-zA-Z0-9]")
- (erlang-push (list 'begin token (current-column)) stack))
- ;; Normal when case
- ;;((looking-at "when\\s ")
- ;;((looking-at "when\\s *\\($\\|%\\)")
- ((looking-at "when[^_a-zA-Z0-9]")
- (erlang-push (list 'when token (current-column)) stack))
- ((looking-at "catch\\b.*of")
- t)
- ((looking-at "catch\\b\\s *\\($\\|%\\|.*->\\)")
- (erlang-push (list 'icr token (current-column)) stack))
- ;;(erlang-push (list '-> token (current-column)) stack))
- ;;((looking-at "^of$")
- ;; (erlang-push (list 'icr token (current-column)) stack)
- ;;(erlang-push (list '-> token (current-column)) stack))
- )
+ ;; Must push a new icr (if/case/receive) layer.
+ (erlang-push (list 'icr token (current-column)) stack))
+ ((looking-at "\\(try\\|after\\)[^_a-zA-Z0-9]")
+ ;; Must handle separately, try catch or try X of -> catch
+ ;; same for `after', it could be
+ ;; receive after Time -> X end, or
+ ;; try after X end
+ (erlang-push (list 'try token (current-column)) stack))
+ ((looking-at "\\(of\\)[^_a-zA-Z0-9]")
+ ;; Must handle separately, try X of -> catch
+ (if (and stack (eq (car (car stack)) 'try))
+ (let ((try-column (nth 2 (car stack)))
+ (try-pos (nth 1 (car stack))))
+ (erlang-pop stack)
+ (erlang-push (list 'icr try-pos try-column) stack))))
+
+ ((looking-at "\\(fun\\)[^_a-zA-Z0-9]")
+ ;; Push a new layer if we are defining a `fun'
+ ;; expression, not when we are refering an existing
+ ;; function. 'fun's defines are only indented one level now.
+ (if (save-excursion
+ (goto-char (match-end 1))
+ (erlang-skip-blank to)
+ ;; Use erlang-variable-regexp here to look for an
+ ;; optional variable name to match EEP37 named funs.
+ (if (looking-at erlang-variable-regexp)
+ (progn
+ (goto-char (match-end 0))
+ (erlang-skip-blank to)))
+ (eq (following-char) ?\())
+ (erlang-push (list 'fun token (current-column)) stack)))
+ ((looking-at "\\(begin\\)[^_a-zA-Z0-9]")
+ (erlang-push (list 'begin token (current-column)) stack))
+ ;; Normal when case
+ ;;((looking-at "when\\s ")
+ ;;((looking-at "when\\s *\\($\\|%\\)")
+ ((looking-at "when[^_a-zA-Z0-9]")
+ (erlang-push (list 'when token (current-column)) stack))
+ ((looking-at "catch\\b.*of")
+ t)
+ ((looking-at "catch\\b\\s *\\($\\|%\\|.*->\\)")
+ (erlang-push (list 'icr token (current-column)) stack))
+ ;;(erlang-push (list '-> token (current-column)) stack))
+ ;;((looking-at "^of$")
+ ;; (erlang-push (list 'icr token (current-column)) stack)
+ ;;(erlang-push (list '-> token (current-column)) stack))
+ )
(forward-sexp 1))
- ;; String: Try to skip over it. (Catch error if not complete.)
- ((= cs ?\")
- (condition-case nil
- (progn
- (forward-sexp 1)
- (if (> (point) to)
- (progn
- (setq in-what 'string)
- (goto-char to))))
- (error
- (setq in-what 'string)
- (goto-char to))))
+ ;; String: Try to skip over it. (Catch error if not complete.)
+ ((= cs ?\")
+ (condition-case nil
+ (progn
+ (forward-sexp 1)
+ (if (> (point) to)
+ (progn
+ (setq in-what 'string)
+ (goto-char to))))
+ (error
+ (setq in-what 'string)
+ (goto-char to))))
;; Expression prefix e.i. $ or ^ (Note ^ can be in the character
;; literal $^ or part of string and $ outside of a string denotes
;; a character literal)
((= cs ?')
- (cond
+ (cond
((= (following-char) ?\") ;; $ or ^ was the last char in a string
- (forward-char 1))
+ (forward-char 1))
(t
- ;; Maybe a character literal, quote the next char to avoid
- ;; situations as $" being seen as the begining of a string.
- ;; Note the quoting something in the middle of a string is harmless.
- (quote (following-char))
- (forward-char 1))))
+ ;; Maybe a character literal, quote the next char to avoid
+ ;; situations as $" being seen as the begining of a string.
+ ;; Note the quoting something in the middle of a string is harmless.
+ (quote (following-char))
+ (forward-char 1))))
;; Symbol constituent or punctuation
-
+
((memq cs '(?. ?_))
- (cond
-
+ (cond
+
;; Clause end
((= (following-char) ?\;)
- (if (eq (car (car (last stack))) 'spec)
- (while (memq (car (car stack)) '(when ::))
- (erlang-pop stack)))
- (if (and stack (eq (car (car stack)) '->))
- (erlang-pop stack))
- (forward-char 1))
-
+ (if (eq (car (car (last stack))) 'spec)
+ (while (memq (car (car stack)) '(when ::))
+ (erlang-pop stack)))
+ (if (and stack (eq (car (car stack)) '->))
+ (erlang-pop stack))
+ (forward-char 1))
+
;; Parameter separator
((looking-at ",")
- (forward-char 1)
- (if (and stack (eq (car (car stack)) '::))
- ;; Type or spec
- (erlang-pop stack)))
+ (forward-char 1)
+ (if (and stack (eq (car (car stack)) '::))
+ ;; Type or spec
+ (erlang-pop stack)))
;; Function end
((looking-at "\\.\\(\\s \\|\n\\|\\s<\\)")
- (setq stack nil)
- (forward-char 1))
-
+ (setq stack nil)
+ (forward-char 1))
+
;; Function head
((looking-at "->")
- (if (and stack (eq (car (car stack)) 'when))
- (erlang-pop stack))
- (erlang-push (list '-> token (current-column)) stack)
- (forward-char 2))
-
+ (if (and stack (eq (car (car stack)) 'when))
+ (erlang-pop stack))
+ (erlang-push (list '-> token (current-column)) stack)
+ (forward-char 2))
+
;; List-comprehension divider
((looking-at "||")
- (erlang-push (list '|| token (current-column)) stack)
- (forward-char 2))
+ (erlang-push (list '|| token (current-column)) stack)
+ (forward-char 2))
;; Bit-syntax open. Note that map syntax allows "<<" to follow ":="
;; or "=>" without intervening whitespace, so handle that case here
((looking-at "\\(:=\\|=>\\)?<<")
- (erlang-push (list '<< token (current-column)) stack)
- (forward-char (- (match-end 0) (match-beginning 0))))
-
+ (erlang-push (list '<< token (current-column)) stack)
+ (forward-char (- (match-end 0) (match-beginning 0))))
+
;; Bit-syntax close
((looking-at ">>")
- (while (memq (car (car stack)) '(|| ->))
- (erlang-pop stack))
- (cond ((eq (car (car stack)) '<<)
- (erlang-pop stack))
- ((memq (car (car stack)) '(icr begin fun))
- (error "Missing `end'"))
- (t
- (error "Unbalanced parentheses")))
- (forward-char 2))
-
+ (while (memq (car (car stack)) '(|| ->))
+ (erlang-pop stack))
+ (cond ((eq (car (car stack)) '<<)
+ (erlang-pop stack))
+ ((memq (car (car stack)) '(icr begin fun))
+ (error "Missing `end'"))
+ (t
+ (error "Unbalanced parentheses")))
+ (forward-char 2))
+
;; Macro
((= (following-char) ??)
- ;; Skip over the ?
- (forward-char 1)
- )
+ ;; Skip over the ?
+ (forward-char 1)
+ )
;; Type spec's
((looking-at "-type\\s \\|-opaque\\s ")
- (if stack
- (forward-char 1)
- (erlang-push (list 'icr token (current-column)) stack)
- (forward-char 6)))
+ (if stack
+ (forward-char 1)
+ (erlang-push (list 'icr token (current-column)) stack)
+ (forward-char 6)))
((looking-at "-spec\\s ")
- (if stack
- (forward-char 1)
- (forward-char 6)
- (skip-chars-forward "^(\n")
- (erlang-push (list 'spec (point) (current-column)) stack)
- ))
+ (if stack
+ (forward-char 1)
+ (forward-char 6)
+ (skip-chars-forward "^(\n")
+ (erlang-push (list 'spec (point) (current-column)) stack)
+ ))
;; Type spec delimiter
((looking-at "::")
- (erlang-push (list ':: token (current-column)) stack)
- (forward-char 2))
-
- ;; Don't follow through in the clause below
- ;; '|' don't need spaces around it
+ (erlang-push (list ':: token (current-column)) stack)
+ (forward-char 2))
+
+ ;; Don't follow through in the clause below
+ ;; '|' don't need spaces around it
((looking-at "|")
- (forward-char 1))
-
+ (forward-char 1))
+
;; Other punctuation: Skip over it and any following punctuation
((= cs ?.)
- ;; Skip over all characters in the operand.
- (skip-syntax-forward "."))
-
+ ;; Skip over all characters in the operand.
+ (skip-syntax-forward "."))
+
;; Other char: Skip over it.
(t
- (forward-char 1))))
-
+ (forward-char 1))))
+
;; Open parenthesis
((= cs ?\()
(erlang-push (list '\( token (current-column)) stack)
(forward-char 1))
-
+
;; Close parenthesis
((= cs ?\))
(while (memq (car (car stack)) '(|| -> :: when))
- (erlang-pop stack))
+ (erlang-pop stack))
(cond ((eq (car (car stack)) '\()
- (erlang-pop stack)
- (if (and (eq (car (car stack)) 'fun)
- (or (eq (car (car (last stack))) 'spec)
- (eq (car (car (cdr stack))) '::))) ;; -type()
- ;; Inside fun type def ') closes fun definition
- (erlang-pop stack)))
- ((eq (car (car stack)) 'icr)
- (erlang-pop stack)
- ;; Normal catch not try-catch might have caused icr
- ;; and then incr should be removed and is not an error.
- (if (eq (car (car stack)) '\()
- (erlang-pop stack)
- (error "Missing `end'")
- ))
- ((eq (car (car stack)) 'begin)
- (error "Missing `end'"))
- (t
- (error "Unbalanced parenthesis"))
- )
- (forward-char 1))
-
+ (erlang-pop stack)
+ (if (and (eq (car (car stack)) 'fun)
+ (or (eq (car (car (last stack))) 'spec)
+ (eq (car (car (cdr stack))) '::))) ;; -type()
+ ;; Inside fun type def ') closes fun definition
+ (erlang-pop stack)))
+ ((eq (car (car stack)) 'icr)
+ (erlang-pop stack)
+ ;; Normal catch not try-catch might have caused icr
+ ;; and then incr should be removed and is not an error.
+ (if (eq (car (car stack)) '\()
+ (erlang-pop stack)
+ (error "Missing `end'")
+ ))
+ ((eq (car (car stack)) 'begin)
+ (error "Missing `end'"))
+ (t
+ (error "Unbalanced parenthesis"))
+ )
+ (forward-char 1))
+
;; Character quote: Skip it and the quoted char.
((= cs ?/)
(forward-char 2))
-
+
;; Character escape: Skip it and the escape sequence.
((= cs ?\\)
(forward-char 1)
@@ -2855,49 +2864,49 @@ Return nil if inside string, t if in a comment."
((eq (car stack-top) '\()
;; Element of list, tuple or part of an expression,
(cond ((null erlang-argument-indent)
- ;; indent to next column.
- (1+ (nth 2 stack-top)))
- ((= (char-syntax (following-char)) ?\))
- (goto-char (nth 1 stack-top))
- (cond ((looking-at "[({]\\s *\\($\\|%\\)")
- ;; Line ends with parenthesis.
- (let ((previous (erlang-indent-find-preceding-expr))
- (stack-pos (nth 2 stack-top)))
- (if (>= previous stack-pos) stack-pos
- (- (+ previous erlang-argument-indent) 1))))
- (t
- (nth 2 stack-top))))
- ((= (following-char) ?,)
- ;; a comma at the start of the line: line up with opening parenthesis.
- (nth 2 stack-top))
- (t
- (goto-char (nth 1 stack-top))
- (let ((base (cond ((looking-at "[({]\\s *\\($\\|%\\)")
- ;; Line ends with parenthesis.
- (erlang-indent-parenthesis (nth 2 stack-top)))
- (t
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (1+ (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column)))))
- (erlang-indent-standard indent-point token base 't)))))
- ;;
- ((eq (car stack-top) '<<)
- ;; Element of binary (possible comprehension) expression,
- (cond ((null erlang-argument-indent)
- ;; indent to next column.
- (+ 2 (nth 2 stack-top)))
- ((looking-at "\\(>>\\)[^_a-zA-Z0-9]")
- (nth 2 stack-top))
- (t
- (goto-char (nth 1 stack-top))
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (+ 2 (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column))))
-
+ ;; indent to next column.
+ (1+ (nth 2 stack-top)))
+ ((= (char-syntax (following-char)) ?\))
+ (goto-char (nth 1 stack-top))
+ (cond ((looking-at "[({]\\s *\\($\\|%\\)")
+ ;; Line ends with parenthesis.
+ (let ((previous (erlang-indent-find-preceding-expr))
+ (stack-pos (nth 2 stack-top)))
+ (if (>= previous stack-pos) stack-pos
+ (- (+ previous erlang-argument-indent) 1))))
+ (t
+ (nth 2 stack-top))))
+ ((= (following-char) ?,)
+ ;; a comma at the start of the line: line up with opening parenthesis.
+ (nth 2 stack-top))
+ (t
+ (goto-char (nth 1 stack-top))
+ (let ((base (cond ((looking-at "[({]\\s *\\($\\|%\\)")
+ ;; Line ends with parenthesis.
+ (erlang-indent-parenthesis (nth 2 stack-top)))
+ (t
+ ;; Indent to the same column as the first
+ ;; argument.
+ (goto-char (1+ (nth 1 stack-top)))
+ (skip-chars-forward " \t")
+ (current-column)))))
+ (erlang-indent-standard indent-point token base 't)))))
+ ;;
+ ((eq (car stack-top) '<<)
+ ;; Element of binary (possible comprehension) expression,
+ (cond ((null erlang-argument-indent)
+ ;; indent to next column.
+ (+ 2 (nth 2 stack-top)))
+ ((looking-at "\\(>>\\)[^_a-zA-Z0-9]")
+ (nth 2 stack-top))
+ (t
+ (goto-char (nth 1 stack-top))
+ ;; Indent to the same column as the first
+ ;; argument.
+ (goto-char (+ 2 (nth 1 stack-top)))
+ (skip-chars-forward " \t")
+ (current-column))))
+
((memq (car stack-top) '(icr fun spec))
;; The default indentation is the column of the option
;; directly following the keyword. (This does not apply to
@@ -2908,156 +2917,160 @@ Return nil if inside string, t if in a comment."
;; `after' should be indented to the same level as the
;; corresponding receive.
(cond ((looking-at "\\(after\\|of\\)\\($\\|[^_a-zA-Z0-9]\\)")
- (nth 2 stack-top))
- ((looking-at "when[^_a-zA-Z0-9]")
- ;; Handling one when part
- (+ (nth 2 stack-top) erlang-indent-level erlang-indent-guard))
- (t
- (save-excursion
- (goto-char (nth 1 stack-top))
- (if (looking-at "case[^_a-zA-Z0-9]")
- (+ (nth 2 stack-top) erlang-indent-level)
- (skip-chars-forward "a-z")
- (skip-chars-forward " \t")
- (if (memq (following-char) '(?% ?\n))
- (+ (nth 2 stack-top) erlang-indent-level)
- (current-column))))))
- )
- ((and (eq (car stack-top) '||) (looking-at "\\(]\\|>>\\)[^_a-zA-Z0-9]"))
- (nth 2 (car (cdr stack))))
+ (nth 2 stack-top))
+ ((looking-at "when[^_a-zA-Z0-9]")
+ ;; Handling one when part
+ (+ (nth 2 stack-top) erlang-indent-level erlang-indent-guard))
+ (t
+ (save-excursion
+ (goto-char (nth 1 stack-top))
+ (if (and erlang-icr-indent
+ (looking-at "\\(if\\|case\\|receive\\)[^_a-zA-Z0-9]"))
+ (+ (nth 2 stack-top) erlang-icr-indent)
+ (if (looking-at "\\(case\\|receive\\)[^_a-zA-Z0-9]")
+ (+ (nth 2 stack-top) erlang-indent-level)
+ (skip-chars-forward "a-z")
+ (skip-chars-forward " \t")
+ (if (memq (following-char) '(?% ?\n))
+ (+ (nth 2 stack-top) erlang-indent-level)
+ (current-column))))))))
+ ((and (eq (car stack-top) '||) (looking-at "\\(]\\|>>\\)[^_a-zA-Z0-9]"))
+ (nth 2 (car (cdr stack))))
;; Real indentation, where operators create extra indentation etc.
((memq (car stack-top) '(-> || try begin))
- (if (looking-at "\\(of\\)[^_a-zA-Z0-9]")
- (nth 2 stack-top)
- (goto-char (nth 1 stack-top))
- ;; Check if there is more code after the '->' on the
- ;; same line. If so use this indentation as base, else
- ;; use parent indentation + 2 * level as base.
- (let ((off erlang-indent-level)
- (skip 2))
- (cond ((null (cdr stack))) ; Top level in function.
- ((eq (car stack-top) 'begin)
- (setq skip 5))
- ((eq (car stack-top) 'try)
- (setq skip 5))
- ((eq (car stack-top) '->)
- ;; If in fun definition use standard indent level not double
- ;;(if (not (eq (car (car (cdr stack))) 'fun))
- ;; Removed it made multi clause fun's look to bad
- (setq off (* 2 erlang-indent-level)))) ;; )
- (let ((base (erlang-indent-find-base stack indent-point off skip)))
- ;; Special cases
- (goto-char indent-point)
- (cond ((looking-at "\\(end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
- (if (eq (car stack-top) '->)
- (erlang-pop stack))
- (if stack
- (erlang-caddr (car stack))
- 0))
- ((looking-at "catch\\b\\($\\|[^_a-zA-Z0-9]\\)")
- ;; Are we in a try
- (let ((start (if (eq (car stack-top) '->)
- (car (cdr stack))
- stack-top)))
- (if (null start) nil
- (goto-char (nth 1 start)))
- (cond ((looking-at "try\\($\\|[^_a-zA-Z0-9]\\)")
- (progn
- (if (eq (car stack-top) '->)
- (erlang-pop stack))
- (if stack
- (erlang-caddr (car stack))
- 0)))
- (t (erlang-indent-standard indent-point token base 'nil))))) ;; old catch
- (t
- (erlang-indent-standard indent-point token base 'nil)
- ))))
- ))
- ((eq (car stack-top) 'when)
- (goto-char (nth 1 stack-top))
- (if (looking-at "when\\s *\\($\\|%\\)")
- (progn
- (erlang-pop stack)
- (if (and stack (memq (nth 0 (car stack)) '(icr fun)))
- (progn
- (goto-char (nth 1 (car stack)))
- (+ (nth 2 (car stack)) erlang-indent-guard
- ;; receive XYZ or receive
- ;; XYZ
- ;; This if thing does not seem to be needed
- ;;(if (looking-at "[a-z]+\\s *\\($\\|%\\)")
- ;; erlang-indent-level
- ;; (* 2 erlang-indent-level))))
- (* 2 erlang-indent-level)))
- ;;erlang-indent-level))
- (+ erlang-indent-level erlang-indent-guard)))
+ (if (looking-at "\\(of\\)[^_a-zA-Z0-9]")
+ (nth 2 stack-top)
+ (goto-char (nth 1 stack-top))
+ ;; Check if there is more code after the '->' on the
+ ;; same line. If so use this indentation as base, else
+ ;; use parent indentation + 2 * level as base.
+ (let ((off erlang-indent-level)
+ (skip 2))
+ (cond ((null (cdr stack))) ; Top level in function.
+ ((eq (car stack-top) 'begin)
+ (setq skip 5))
+ ((eq (car stack-top) 'try)
+ (setq skip 5))
+ ((eq (car stack-top) '->)
+ ;; If in fun definition use standard indent level not double
+ ;;(if (not (eq (car (car (cdr stack))) 'fun))
+ ;; Removed it made multi clause fun's look to bad
+ (setq off (+ erlang-indent-level (if (not erlang-icr-indent)
+ erlang-indent-level
+ erlang-icr-indent)))))
+ (let ((base (erlang-indent-find-base stack indent-point off skip)))
+ ;; Special cases
+ (goto-char indent-point)
+ (cond ((looking-at "\\(end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
+ (if (eq (car stack-top) '->)
+ (erlang-pop stack))
+ (if stack
+ (erlang-caddr (car stack))
+ 0))
+ ((looking-at "catch\\b\\($\\|[^_a-zA-Z0-9]\\)")
+ ;; Are we in a try
+ (let ((start (if (eq (car stack-top) '->)
+ (car (cdr stack))
+ stack-top)))
+ (if (null start) nil
+ (goto-char (nth 1 start)))
+ (cond ((looking-at "try\\($\\|[^_a-zA-Z0-9]\\)")
+ (progn
+ (if (eq (car stack-top) '->)
+ (erlang-pop stack))
+ (if stack
+ (erlang-caddr (car stack))
+ 0)))
+ (t (erlang-indent-standard indent-point token base 'nil))))) ;; old catch
+ (t
+ (erlang-indent-standard indent-point token base 'nil)
+ ))))
+ ))
+ ((eq (car stack-top) 'when)
+ (goto-char (nth 1 stack-top))
+ (if (looking-at "when\\s *\\($\\|%\\)")
+ (progn
+ (erlang-pop stack)
+ (if (and stack (memq (nth 0 (car stack)) '(icr fun)))
+ (progn
+ (goto-char (nth 1 (car stack)))
+ (+ (nth 2 (car stack)) erlang-indent-guard
+ ;; receive XYZ or receive
+ ;; XYZ
+ ;; This if thing does not seem to be needed
+ ;;(if (looking-at "[a-z]+\\s *\\($\\|%\\)")
+ ;; erlang-indent-level
+ ;; (* 2 erlang-indent-level))))
+ (* 2 erlang-indent-level)))
+ ;;erlang-indent-level))
+ (+ erlang-indent-level erlang-indent-guard)))
;; "when" is followed by code, let's indent to the same
;; column.
(forward-char 4) ; Skip "when"
(skip-chars-forward " \t")
(current-column)))
- ;; Type and Spec indentation
- ((eq (car stack-top) '::)
- (if (looking-at "[},)]")
- ;; Closing function spec, record definition with types,
+ ;; Type and Spec indentation
+ ((eq (car stack-top) '::)
+ (if (looking-at "[},)]")
+ ;; Closing function spec, record definition with types,
;; or a comma at the start of the line
- ;; pop stack and recurse
- (erlang-calculate-stack-indent indent-point
- (cons (erlang-pop stack) (cdr state)))
- (cond ((null erlang-argument-indent)
- ;; indent to next column.
- (+ 2 (nth 2 stack-top)))
- ((looking-at "::[^_a-zA-Z0-9]")
- (nth 2 stack-top))
- (t
- (let ((start-alternativ (if (looking-at "|") 2 0)))
- (goto-char (nth 1 stack-top))
- (- (cond ((looking-at "::\\s *\\($\\|%\\)")
- ;; Line ends with ::
- (if (eq (car (car (last stack))) 'spec)
- (+ (erlang-indent-find-preceding-expr 1)
- erlang-argument-indent)
- (+ (erlang-indent-find-preceding-expr 2)
- erlang-argument-indent)))
- (t
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (+ 2 (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column))) start-alternativ))))))
- )))
+ ;; pop stack and recurse
+ (erlang-calculate-stack-indent indent-point
+ (cons (erlang-pop stack) (cdr state)))
+ (cond ((null erlang-argument-indent)
+ ;; indent to next column.
+ (+ 2 (nth 2 stack-top)))
+ ((looking-at "::[^_a-zA-Z0-9]")
+ (nth 2 stack-top))
+ (t
+ (let ((start-alternativ (if (looking-at "|") 2 0)))
+ (goto-char (nth 1 stack-top))
+ (- (cond ((looking-at "::\\s *\\($\\|%\\)")
+ ;; Line ends with ::
+ (if (eq (car (car (last stack))) 'spec)
+ (+ (erlang-indent-find-preceding-expr 1)
+ erlang-argument-indent)
+ (+ (erlang-indent-find-preceding-expr 2)
+ erlang-argument-indent)))
+ (t
+ ;; Indent to the same column as the first
+ ;; argument.
+ (goto-char (+ 2 (nth 1 stack-top)))
+ (skip-chars-forward " \t")
+ (current-column))) start-alternativ))))))
+ )))
(defun erlang-indent-standard (indent-point token base inside-parenthesis)
"Standard indent when in blocks or tuple or arguments.
Look at last thing to see in what state we are, move relative to the base."
- (goto-char token)
+ (goto-char token)
(cond ((looking-at "||\\|,\\|->\\||")
- base)
- ((erlang-at-keyword)
- (+ (current-column) erlang-indent-level))
- ((or (= (char-syntax (following-char)) ?.)
- (erlang-at-operator))
- (+ base erlang-indent-level))
- (t
- (goto-char indent-point)
- (cond ((memq (following-char) '(?\( ))
- ;; Function application.
- (+ (erlang-indent-find-preceding-expr)
- erlang-argument-indent))
- ;; Empty line, or end; treat it as the end of
- ;; the block. (Here we have a choice: should
- ;; the user be forced to reindent continued
- ;; lines, or should the "end" be reindented?)
-
- ;; Avoid treating comments a continued line.
- ((= (following-char) ?%)
- base)
- ;; Continued line (e.g. line beginning
- ;; with an operator.)
- (t
- (if (or (erlang-at-operator) (not inside-parenthesis))
- (+ base erlang-indent-level)
- base))))))
+ base)
+ ((erlang-at-keyword)
+ (+ (current-column) erlang-indent-level))
+ ((or (= (char-syntax (following-char)) ?.)
+ (erlang-at-operator))
+ (+ base erlang-indent-level))
+ (t
+ (goto-char indent-point)
+ (cond ((memq (following-char) '(?\( ))
+ ;; Function application.
+ (+ (erlang-indent-find-preceding-expr)
+ erlang-argument-indent))
+ ;; Empty line, or end; treat it as the end of
+ ;; the block. (Here we have a choice: should
+ ;; the user be forced to reindent continued
+ ;; lines, or should the "end" be reindented?)
+
+ ;; Avoid treating comments a continued line.
+ ((= (following-char) ?%)
+ base)
+ ;; Continued line (e.g. line beginning
+ ;; with an operator.)
+ (t
+ (if (or (erlang-at-operator) (not inside-parenthesis))
+ (+ base erlang-indent-level)
+ base))))))
(defun erlang-indent-find-base (stack indent-point &optional offset skip)
"Find the base column for current stack."
@@ -3067,21 +3080,21 @@ Return nil if inside string, t if in a comment."
(let* ((stack-top (car stack)))
(goto-char (nth 1 stack-top))
(if (< skip (- (point-max) (point)))
- (progn
- (forward-char skip)
- (if (looking-at "\\s *\\($\\|%\\)")
- (progn
- (if (memq (car stack-top) '(-> ||))
- (erlang-pop stack))
- ;; Take parent identation + offset,
- ;; else just erlang-indent-level if no parent
- (if stack
- (+ (erlang-caddr (car stack))
- offset)
- erlang-indent-level))
- (erlang-skip-blank indent-point)
- (current-column)))
- (+ (current-column) skip)))))
+ (progn
+ (forward-char skip)
+ (if (looking-at "\\s *\\($\\|%\\)")
+ (progn
+ (if (memq (car stack-top) '(-> ||))
+ (erlang-pop stack))
+ ;; Take parent identation + offset,
+ ;; else just erlang-indent-level if no parent
+ (if stack
+ (+ (erlang-caddr (car stack))
+ offset)
+ erlang-indent-level))
+ (erlang-skip-blank indent-point)
+ (current-column)))
+ (+ (current-column) skip)))))
;; Does not handle `begin' .. `end'.
@@ -3100,51 +3113,51 @@ This assumes that the preceding expression is either simple
;; where the call (forward-sexp -1) will fail when point is at the `#'.
(or
(ignore-errors
- ;; Needed to match the colon in "'foo':'bar'".
- (cond ((eq (preceding-char) ?:)
- (backward-char 1)
- (forward-sexp -1)
- (current-column))
- ((eq (preceding-char) ?#)
- ;; We may now be at:
- ;; - either a construction of a new record
- ;; - or update of a record, in which case we want
- ;; the column of the expression to be updated.
- ;;
- ;; To see which of the two cases we are at, we first
- ;; move an expression backwards, check for keywords,
- ;; then immediately an expression forwards. Moving
- ;; backwards skips past tokens like `,' or `->', but
- ;; when moving forwards again, we won't skip past such
- ;; tokens. We use this: if, after having moved
- ;; forwards, we're back where we started, then it was
- ;; a record update.
- ;; The check for keywords is to detect cases like:
- ;; case Something of #record_construction{...}
- (backward-char 1)
- (let ((record-start (point))
- (record-start-col (current-column)))
- (forward-sexp -1)
- (let ((preceding-expr-col (current-column))
- ;; white space definition according to erl_scan
- (white-space "\000-\040\200-\240"))
- (if (erlang-at-keyword)
- ;; The (forward-sexp -1) call moved past a keyword
- (1+ record-start-col)
- (forward-sexp 1)
- (skip-chars-forward white-space record-start)
- ;; Are we back where we started? If so, it was an update.
- (if (= (point) record-start)
- preceding-expr-col
- (goto-char record-start)
- (1+ (current-column)))))))
- (t col)))
+ ;; Needed to match the colon in "'foo':'bar'".
+ (cond ((eq (preceding-char) ?:)
+ (backward-char 1)
+ (forward-sexp -1)
+ (current-column))
+ ((eq (preceding-char) ?#)
+ ;; We may now be at:
+ ;; - either a construction of a new record
+ ;; - or update of a record, in which case we want
+ ;; the column of the expression to be updated.
+ ;;
+ ;; To see which of the two cases we are at, we first
+ ;; move an expression backwards, check for keywords,
+ ;; then immediately an expression forwards. Moving
+ ;; backwards skips past tokens like `,' or `->', but
+ ;; when moving forwards again, we won't skip past such
+ ;; tokens. We use this: if, after having moved
+ ;; forwards, we're back where we started, then it was
+ ;; a record update.
+ ;; The check for keywords is to detect cases like:
+ ;; case Something of #record_construction{...}
+ (backward-char 1)
+ (let ((record-start (point))
+ (record-start-col (current-column)))
+ (forward-sexp -1)
+ (let ((preceding-expr-col (current-column))
+ ;; white space definition according to erl_scan
+ (white-space "\000-\040\200-\240"))
+ (if (erlang-at-keyword)
+ ;; The (forward-sexp -1) call moved past a keyword
+ (1+ record-start-col)
+ (forward-sexp 1)
+ (skip-chars-forward white-space record-start)
+ ;; Are we back where we started? If so, it was an update.
+ (if (= (point) record-start)
+ preceding-expr-col
+ (goto-char record-start)
+ (1+ (current-column)))))))
+ (t col)))
col))))
-(defun erlang-indent-parenthesis (stack-position)
+(defun erlang-indent-parenthesis (stack-position)
(let ((previous (erlang-indent-find-preceding-expr)))
(if (> previous stack-position)
- (+ stack-position erlang-argument-indent)
+ (+ stack-position erlang-argument-indent)
(+ previous erlang-argument-indent))))
(defun erlang-skip-blank (&optional lim)
@@ -3153,20 +3166,20 @@ This assumes that the preceding expression is either simple
(let (stop)
(while (and (not stop) (< (point) lim))
(cond ((= (following-char) ?%)
- (skip-chars-forward "^\n" lim))
- ((= (following-char) ?\n)
- (skip-chars-forward "\n" lim))
- ((looking-at "\\s ")
- (if (re-search-forward "\\S " lim 'move)
- (forward-char -1)))
- (t
- (setq stop t))))
+ (skip-chars-forward "^\n" lim))
+ ((= (following-char) ?\n)
+ (skip-chars-forward "\n" lim))
+ ((looking-at "\\s ")
+ (if (re-search-forward "\\S " lim 'move)
+ (forward-char -1)))
+ (t
+ (setq stop t))))
stop))
(defun erlang-at-keyword ()
"Are we looking at an Erlang keyword which will increase indentation?"
(looking-at (concat "\\(when\\|if\\|fun\\|case\\|begin\\|"
- "of\\|receive\\|after\\|catch\\|try\\)\\b")))
+ "of\\|receive\\|after\\|catch\\|try\\)\\b")))
(defun erlang-at-operator ()
"Are we looking at an Erlang operator?"
@@ -3179,14 +3192,14 @@ This assumes that the preceding expression is either simple
Used both by `indent-for-comment' and the Erlang specific indentation
commands."
(cond ((looking-at "%%%") 0)
- ((looking-at "%%")
- (or (erlang-calculate-indent)
- (current-indentation)))
- (t
- (save-excursion
- (skip-chars-backward " \t")
- (max (if (bolp) 0 (1+ (current-column)))
- comment-column)))))
+ ((looking-at "%%")
+ (or (erlang-calculate-indent)
+ (current-indentation)))
+ (t
+ (save-excursion
+ (skip-chars-backward " \t")
+ (max (if (bolp) 0 (1+ (current-column)))
+ comment-column)))))
;;; Erlang movement commands
@@ -3214,18 +3227,18 @@ Return t unless search stops due to end of buffer."
;; that the regexp below includes the last character of the
;; previous line.
(if (bobp)
- (or (looking-at "\n")
- (forward-char 1))
- (forward-char -1)
- (if (looking-at "\\`\n")
- (forward-char 1))))
+ (or (looking-at "\n")
+ (forward-char 1))
+ (forward-char -1)
+ (if (looking-at "\\`\n")
+ (forward-char 1))))
;; The regexp matches a function header that isn't
;; included in a string.
(and (re-search-forward "\\(\\`\\|\\`\n\\|[^\\]\n\\)\\(-?[a-z]\\|'\\|-\\)"
- nil 'move (- arg))
+ nil 'move (- arg))
(let ((beg (match-beginning 2)))
- (and beg (goto-char beg))
- t)))
+ (and beg (goto-char beg))
+ t)))
(defun erlang-end-of-clause (&optional arg)
"Move to the end of the current clause.
@@ -3233,18 +3246,16 @@ With argument, do this that many times."
(interactive "p")
(or arg (setq arg 1))
(while (and (looking-at "[ \t]*[%\n]")
- (zerop (forward-line 1))))
+ (zerop (forward-line 1))))
;; Move to the next clause.
(erlang-beginning-of-clause (- arg))
(beginning-of-line);; Just to be sure...
(let ((continue t))
(while (and (not (bobp)) continue)
(forward-line -1)
- (skip-chars-forward " \t")
- (if (looking-at "[%\n]")
- nil
- (end-of-line)
- (setq continue nil)))))
+ (unless (looking-at "[ \t]*[%\n]")
+ (end-of-line)
+ (setq continue nil)))))
(defun erlang-mark-clause ()
"Put mark at end of clause, point at beginning."
@@ -3273,22 +3284,22 @@ Return t unless search stops due to end of buffer."
;; Search backward
((> arg 0)
(while (and (> arg 0)
- (and (erlang-beginning-of-clause 1)
- (let ((start (point))
- (name (erlang-name-of-function))
- (arity (erlang-get-function-arity)))
- ;; Note: "arity" is nil for e.g. "-import", hence
- ;; two "-import" clauses are not considered to
- ;; be part of the same function.
- (while (and (erlang-beginning-of-clause 1)
- (string-equal name
- (erlang-name-of-function))
- arity
- (equal arity
- (erlang-get-function-arity)))
- (setq start (point)))
- (goto-char start)
- t)))
+ (and (erlang-beginning-of-clause 1)
+ (let ((start (point))
+ (name (erlang-name-of-function))
+ (arity (erlang-get-function-arity)))
+ ;; Note: "arity" is nil for e.g. "-import", hence
+ ;; two "-import" clauses are not considered to
+ ;; be part of the same function.
+ (while (and (erlang-beginning-of-clause 1)
+ (string-equal name
+ (erlang-name-of-function))
+ arity
+ (equal arity
+ (erlang-get-function-arity)))
+ (setq start (point)))
+ (goto-char start)
+ t)))
(setq arg (1- arg))))
;; Search forward
((< arg 0)
@@ -3296,19 +3307,19 @@ Return t unless search stops due to end of buffer."
(erlang-beginning-of-clause 1)
;; Step -arg functions forward.
(while (and (< arg 0)
- ;; Step one function forward, or stop if the end of
- ;; the buffer was reached. Return t if we found the
- ;; function.
- (let ((name (erlang-name-of-function))
- (arity (erlang-get-function-arity))
- (found (erlang-beginning-of-clause -1)))
- (while (and found
- (string-equal name (erlang-name-of-function))
- arity
- (equal arity
- (erlang-get-function-arity)))
- (setq found (erlang-beginning-of-clause -1)))
- found))
+ ;; Step one function forward, or stop if the end of
+ ;; the buffer was reached. Return t if we found the
+ ;; function.
+ (let ((name (erlang-name-of-function))
+ (arity (erlang-get-function-arity))
+ (found (erlang-beginning-of-clause -1)))
+ (while (and found
+ (string-equal name (erlang-name-of-function))
+ arity
+ (equal arity
+ (erlang-get-function-arity)))
+ (setq found (erlang-beginning-of-clause -1)))
+ found))
(setq arg (1+ arg)))))
(zerop arg))
@@ -3324,35 +3335,35 @@ With negative argument go towards the beginning of the buffer."
;; Forward
(while (and (> arg 0) (< (point) (point-max)))
(let ((pos (point)))
- (while (progn
- (if (and first
- (progn
- (forward-char 1)
- (erlang-beginning-of-clause 1)))
- nil
- (or (bobp) (forward-char -1))
- (erlang-beginning-of-clause -1))
- (setq first nil)
- (erlang-pass-over-function)
- (skip-chars-forward " \t")
- (if (looking-at "[%\n]")
- (forward-line 1))
- (<= (point) pos))))
+ (while (progn
+ (if (and first
+ (progn
+ (forward-char 1)
+ (erlang-beginning-of-clause 1)))
+ nil
+ (or (bobp) (forward-char -1))
+ (erlang-beginning-of-clause -1))
+ (setq first nil)
+ (erlang-pass-over-function)
+ (skip-chars-forward " \t")
+ (if (looking-at "[%\n]")
+ (forward-line 1))
+ (<= (point) pos))))
(setq arg (1- arg)))
;; Backward
(while (< arg 0)
(let ((pos (point)))
- (erlang-beginning-of-clause 1)
- (erlang-pass-over-function)
- (forward-line 1)
- (if (>= (point) pos)
- (if (erlang-beginning-of-function 2)
- (progn
- (erlang-pass-over-function)
- (skip-chars-forward " \t")
- (if (looking-at "[%\n]")
- (forward-line 1)))
- (goto-char (point-min)))))
+ (erlang-beginning-of-clause 1)
+ (erlang-pass-over-function)
+ (forward-line 1)
+ (if (>= (point) pos)
+ (if (erlang-beginning-of-function 2)
+ (progn
+ (erlang-pass-over-function)
+ (skip-chars-forward " \t")
+ (if (looking-at "[%\n]")
+ (forward-line 1)))
+ (goto-char (point-min)))))
(setq arg (1+ arg)))))
(eval-and-compile
@@ -3366,18 +3377,18 @@ With negative argument go towards the beginning of the buffer."
;; Sets the region. In Emacs 19 and XEmacs, we want to activate
;; the region.
(condition-case nil
- (push-mark (point) nil t)
- (error (push-mark (point))))
+ (push-mark (point) nil t)
+ (error (push-mark (point))))
(erlang-beginning-of-function 1)
;; The above function deactivates the mark.
(if (boundp 'deactivate-mark)
- (funcall (symbol-function 'set) 'deactivate-mark nil)))))
+ (funcall (symbol-function 'set) 'deactivate-mark nil)))))
(defun erlang-pass-over-function ()
(while (progn
- (erlang-skip-blank)
- (and (not (looking-at "\\.\\(\\s \\|\n\\|\\s<\\)"))
- (not (eobp))))
+ (erlang-skip-blank)
+ (and (not (looking-at "\\.\\(\\s \\|\n\\|\\s<\\)"))
+ (not (eobp))))
(forward-sexp 1))
(if (not (eobp))
(forward-char 1)))
@@ -3386,7 +3397,7 @@ With negative argument go towards the beginning of the buffer."
(save-excursion
;; Skip over attribute leader.
(if (looking-at "-[ \t]*")
- (re-search-forward "-[ \t]*" nil 'move))
+ (re-search-forward "-[ \t]*" nil 'move))
(let ((start (point)))
(forward-sexp 1)
(buffer-substring start (point)))))
@@ -3401,54 +3412,54 @@ paragraph of it that point is in, preserving the comment's indentation
and initial `%':s."
(interactive "P")
(let ((has-comment nil)
- ;; If has-comment, the appropriate fill-prefix for the comment.
- comment-fill-prefix)
+ ;; If has-comment, the appropriate fill-prefix for the comment.
+ comment-fill-prefix)
;; Figure out what kind of comment we are looking at.
(save-excursion
(beginning-of-line)
(cond
;; Find the command prefix.
((looking-at (concat "\\s *" comment-start-skip))
- (setq has-comment t)
- (setq comment-fill-prefix (buffer-substring (match-beginning 0)
- (match-end 0))))
+ (setq has-comment t)
+ (setq comment-fill-prefix (buffer-substring (match-beginning 0)
+ (match-end 0))))
;; A line with some code, followed by a comment? Remember that the
;; % which starts the comment shouldn't be part of a string or
;; character.
((progn
- (while (not (looking-at "%\\|$"))
- (skip-chars-forward "^%\n\"\\\\")
- (cond
- ((eq (char-after (point)) ?\\) (forward-char 2))
- ((eq (char-after (point)) ?\") (forward-sexp 1))))
- (looking-at comment-start-skip))
- (setq has-comment t)
- (setq comment-fill-prefix
- (concat (make-string (current-column) ? )
- (buffer-substring (match-beginning 0) (match-end 0)))))))
+ (while (not (looking-at "%\\|$"))
+ (skip-chars-forward "^%\n\"\\\\")
+ (cond
+ ((eq (char-after (point)) ?\\) (forward-char 2))
+ ((eq (char-after (point)) ?\") (forward-sexp 1))))
+ (looking-at comment-start-skip))
+ (setq has-comment t)
+ (setq comment-fill-prefix
+ (concat (make-string (current-column) ? )
+ (buffer-substring (match-beginning 0) (match-end 0)))))))
(if (not has-comment)
- (fill-paragraph justify)
+ (fill-paragraph justify)
;; Narrow to include only the comment, and then fill the region.
(save-restriction
- (narrow-to-region
- ;; Find the first line we should include in the region to fill.
- (save-excursion
- (while (and (zerop (forward-line -1))
- (looking-at "^\\s *%")))
- ;; We may have gone to far. Go forward again.
- (or (looking-at "^\\s *%")
- (forward-line 1))
- (point))
- ;; Find the beginning of the first line past the region to fill.
- (save-excursion
- (while (progn (forward-line 1)
- (looking-at "^\\s *%")))
- (point)))
- ;; Lines with only % on them can be paragraph boundaries.
- (let ((paragraph-start (concat paragraph-start "\\|^[ \t%]*$"))
- (paragraph-separate (concat paragraph-start "\\|^[ \t%]*$"))
- (fill-prefix comment-fill-prefix))
- (fill-paragraph justify))))))
+ (narrow-to-region
+ ;; Find the first line we should include in the region to fill.
+ (save-excursion
+ (while (and (zerop (forward-line -1))
+ (looking-at "^\\s *%")))
+ ;; We may have gone to far. Go forward again.
+ (or (looking-at "^\\s *%")
+ (forward-line 1))
+ (point))
+ ;; Find the beginning of the first line past the region to fill.
+ (save-excursion
+ (while (progn (forward-line 1)
+ (looking-at "^\\s *%")))
+ (point)))
+ ;; Lines with only % on them can be paragraph boundaries.
+ (let ((paragraph-start (concat paragraph-start "\\|^[ \t%]*$"))
+ (paragraph-separate (concat paragraph-start "\\|^[ \t%]*$"))
+ (fill-prefix comment-fill-prefix))
+ (fill-paragraph justify))))))
(defun erlang-uncomment-region (beg end)
@@ -3467,22 +3478,22 @@ first parenthesis is preserved. The point is placed between
the parentheses."
(interactive)
(let ((name (save-excursion
- (and (erlang-beginning-of-clause)
- (erlang-get-function-name t))))
- (arrow (save-excursion
- (and (erlang-beginning-of-clause)
- (erlang-get-function-arrow)))))
+ (and (erlang-beginning-of-clause)
+ (erlang-get-function-name t))))
+ (arrow (save-excursion
+ (and (erlang-beginning-of-clause)
+ (erlang-get-function-arrow)))))
(if (or (null arrow) (null name))
- (error "Can't find name of current Erlang function"))
+ (error "Can't find name of current Erlang function"))
(if (and (bolp) (eolp))
- nil
+ nil
(end-of-line)
(newline))
(insert name)
(save-excursion
(insert ") " arrow))
(if erlang-new-clause-with-arguments
- (erlang-clone-arguments))))
+ (erlang-clone-arguments))))
(defun erlang-clone-arguments ()
@@ -3492,12 +3503,12 @@ The mark is set at the beginning of the inserted text, the point
at the end."
(interactive)
(let ((args (save-excursion
- (beginning-of-line)
- (and (erlang-beginning-of-clause)
- (erlang-get-function-arguments))))
- (p (point)))
+ (beginning-of-line)
+ (and (erlang-beginning-of-clause)
+ (erlang-get-function-arguments))))
+ (p (point)))
(if (null args)
- (error "Can't clone argument list"))
+ (error "Can't clone argument list"))
(insert args)
(set-mark p)))
@@ -3520,18 +3531,18 @@ Return nil if file contains no `-module' attribute."
(widen)
(goto-char (point-min))
(let ((md (match-data)))
- (unwind-protect
- (if (re-search-forward
- (eval-when-compile
- (concat "^-module\\s *(\\s *\\(\\("
- erlang-atom-regexp
- "\\)?\\)\\s *)\\s *\\."))
- (point-max) t)
- (erlang-remove-quotes
- (erlang-buffer-substring (match-beginning 1)
- (match-end 1)))
- nil)
- (store-match-data md))))))
+ (unwind-protect
+ (if (re-search-forward
+ (eval-when-compile
+ (concat "^-module\\s *(\\s *\\(\\("
+ erlang-atom-regexp
+ "\\)?\\)\\s *)\\s *\\."))
+ (point-max) t)
+ (erlang-remove-quotes
+ (erlang-buffer-substring (match-beginning 1)
+ (match-end 1)))
+ nil)
+ (store-match-data md))))))
(defun erlang-get-module-from-file-name (&optional file)
@@ -3551,7 +3562,7 @@ tags system could be used by files written in other languages."
nil
(setq file (file-name-nondirectory file))
(if (string-match erlang-file-name-extension-regexp file)
- (substring file 0 (match-beginning 0))
+ (substring file 0 (match-beginning 0))
nil)))
@@ -3572,30 +3583,30 @@ corresponds to the order of the parsed Erlang list."
(erlang-skip-blank)
(forward-char 1)
(if (not (eq (preceding-char) ?\[))
- '() ; Not looking at an Erlang list.
- (while ; Note: `while' has no body.
- (progn
- (erlang-skip-blank)
- (and (looking-at (eval-when-compile
- (concat erlang-atom-regexp "/\\([0-9]+\\)\\>")))
- (progn
- (setq res (cons
- (cons
- (erlang-remove-quotes
- (erlang-buffer-substring
- (match-beginning 1) (match-end 1)))
- (erlang-string-to-int
- (erlang-buffer-substring
- (match-beginning
- (+ 1 erlang-atom-regexp-matches))
- (match-end
- (+ 1 erlang-atom-regexp-matches)))))
- res))
- (goto-char (match-end 0))
- (erlang-skip-blank)
- (forward-char 1)
- ;; Test if there are more exported functions.
- (eq (preceding-char) ?,))))))
+ '() ; Not looking at an Erlang list.
+ (while ; Note: `while' has no body.
+ (progn
+ (erlang-skip-blank)
+ (and (looking-at (eval-when-compile
+ (concat erlang-atom-regexp "/\\([0-9]+\\)\\>")))
+ (progn
+ (setq res (cons
+ (cons
+ (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning 1) (match-end 1)))
+ (erlang-string-to-int
+ (erlang-buffer-substring
+ (match-beginning
+ (+ 1 erlang-atom-regexp-matches))
+ (match-end
+ (+ 1 erlang-atom-regexp-matches)))))
+ res))
+ (goto-char (match-end 0))
+ (erlang-skip-blank)
+ (forward-char 1)
+ ;; Test if there are more exported functions.
+ (eq (preceding-char) ?,))))))
(nreverse res)))
@@ -3607,14 +3618,14 @@ corresponds to the order of the parsed Erlang list."
(save-excursion
(goto-char (point-min))
(let ((md (match-data))
- (res '()))
+ (res '()))
(unwind-protect
- (progn
- (while (re-search-forward "^-export\\s *(" (point-max) t)
- (erlang-skip-blank)
- (setq res (nconc res (erlang-get-function-arity-list))))
- res)
- (store-match-data md)))))
+ (progn
+ (while (re-search-forward "^-export\\s *(" (point-max) t)
+ (erlang-skip-blank)
+ (setq res (nconc res (erlang-get-function-arity-list))))
+ res)
+ (store-match-data md)))))
(defun erlang-get-import ()
@@ -3625,30 +3636,30 @@ function and arity as cdr part."
(save-excursion
(goto-char (point-min))
(let ((md (match-data))
- (res '()))
+ (res '()))
(unwind-protect
- (progn
- (while (re-search-forward "^-import\\s *(" (point-max) t)
- (erlang-skip-blank)
- (if (looking-at erlang-atom-regexp)
- (let ((module (erlang-remove-quotes
- (erlang-buffer-substring
- (match-beginning 0)
- (match-end 0)))))
- (goto-char (match-end 0))
- (erlang-skip-blank)
- (if (eq (following-char) ?,)
- (progn
- (forward-char 1)
- (erlang-skip-blank)
- (let ((funcs (erlang-get-function-arity-list))
- (pair (assoc module res)))
- (if pair
- (setcdr pair (nconc (cdr pair) funcs))
- (setq res (cons (cons module funcs)
- res)))))))))
- (nreverse res))
- (store-match-data md)))))
+ (progn
+ (while (re-search-forward "^-import\\s *(" (point-max) t)
+ (erlang-skip-blank)
+ (if (looking-at erlang-atom-regexp)
+ (let ((module (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning 0)
+ (match-end 0)))))
+ (goto-char (match-end 0))
+ (erlang-skip-blank)
+ (if (eq (following-char) ?,)
+ (progn
+ (forward-char 1)
+ (erlang-skip-blank)
+ (let ((funcs (erlang-get-function-arity-list))
+ (pair (assoc module res)))
+ (if pair
+ (setcdr pair (nconc (cdr pair) funcs))
+ (setq res (cons (cons module funcs)
+ res)))))))))
+ (nreverse res))
+ (store-match-data md)))))
(defun erlang-get-function-name (&optional arg)
@@ -3660,12 +3671,12 @@ the first `(' is returned.
Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
(save-excursion
(if (not (eobp)) (forward-char 1))
- (and (erlang-beginning-of-clause)
- (erlang-get-function-name t)))"
+ (and (erlang-beginning-of-clause)
+ (erlang-get-function-name t)))"
(let ((n (if arg 0 1)))
(and (looking-at (eval-when-compile
- (concat "^" erlang-atom-regexp "\\s *(")))
- (erlang-buffer-substring (match-beginning n) (match-end n)))))
+ (concat "^" erlang-atom-regexp "\\s *(")))
+ (erlang-buffer-substring (match-beginning n) (match-end n)))))
(defun erlang-get-function-arrow ()
@@ -3674,9 +3685,9 @@ Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
(save-excursion
(if (not (eobp)) (forward-char 1))
- (and (erlang-beginning-of-clause)
- (erlang-get-function-arrow)))"
- (and
+ (and (erlang-beginning-of-clause)
+ (erlang-get-function-arrow)))"
+ (and
(save-excursion
(re-search-forward "->" (point-max) t)
(erlang-buffer-substring (- (point) 2) (+ (point) 1)))))
@@ -3684,33 +3695,33 @@ Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
(defun erlang-get-function-arity ()
"Return the number of arguments of function at point, or nil."
(and (looking-at (eval-when-compile
- (concat "^" erlang-atom-regexp "\\s *(")))
+ (concat "^" erlang-atom-regexp "\\s *(")))
(save-excursion
- (goto-char (match-end 0))
- (condition-case nil
- (let ((res 0)
- (cont t))
- (while cont
- (cond ((eobp)
- (setq res nil)
- (setq cont nil))
- ((looking-at "\\s *)")
- (setq cont nil))
- ((looking-at "\\s *\\($\\|%\\)")
- (forward-line 1))
- ((looking-at "\\s *<<[^>]*?>>")
- (when (zerop res)
- (setq res (+ 1 res)))
- (goto-char (match-end 0)))
- ((looking-at "\\s *,")
- (setq res (+ 1 res))
- (goto-char (match-end 0)))
- (t
- (when (zerop res)
- (setq res (+ 1 res)))
- (forward-sexp 1))))
- res)
- (error nil)))))
+ (goto-char (match-end 0))
+ (condition-case nil
+ (let ((res 0)
+ (cont t))
+ (while cont
+ (cond ((eobp)
+ (setq res nil)
+ (setq cont nil))
+ ((looking-at "\\s *)")
+ (setq cont nil))
+ ((looking-at "\\s *\\($\\|%\\)")
+ (forward-line 1))
+ ((looking-at "\\s *<<[^>]*?>>")
+ (when (zerop res)
+ (setq res (+ 1 res)))
+ (goto-char (match-end 0)))
+ ((looking-at "\\s *,")
+ (setq res (+ 1 res))
+ (goto-char (match-end 0)))
+ (t
+ (when (zerop res)
+ (setq res (+ 1 res)))
+ (forward-sexp 1))))
+ res)
+ (error nil)))))
(defun erlang-get-function-name-and-arity ()
"Return the name and arity of the function at point, or nil.
@@ -3722,15 +3733,15 @@ The return value is a string of the form \"foo/1\"."
(defun erlang-get-function-arguments ()
"Return arguments of current function, or nil."
(if (not (looking-at (eval-when-compile
- (concat "^" erlang-atom-regexp "\\s *("))))
+ (concat "^" erlang-atom-regexp "\\s *("))))
nil
(save-excursion
(condition-case nil
- (let ((start (match-end 0)))
- (goto-char (- start 1))
- (forward-sexp)
- (erlang-buffer-substring start (- (point) 1)))
- (error nil)))))
+ (let ((start (match-end 0)))
+ (goto-char (- start 1))
+ (forward-sexp)
+ (erlang-buffer-substring start (- (point) 1)))
+ (error nil)))))
(defun erlang-get-function-under-point ()
@@ -3747,37 +3758,37 @@ The following could be returned:
In the future the list may contain more elements."
(save-excursion
(let ((md (match-data))
- (res nil))
+ (res nil))
(if (eq (char-syntax (following-char)) ? )
- (skip-chars-backward " \t"))
+ (skip-chars-backward " \t"))
(skip-chars-backward "a-zA-Z0-9_:'")
(cond ((looking-at (eval-when-compile
- (concat erlang-atom-regexp ":" erlang-atom-regexp)))
- (setq res (list
- (erlang-remove-quotes
- (erlang-buffer-substring
- (match-beginning 1) (match-end 1)))
- (erlang-remove-quotes
- (erlang-buffer-substring
- (match-beginning (1+ erlang-atom-regexp-matches))
- (match-end (1+ erlang-atom-regexp-matches)))))))
- ((looking-at erlang-atom-regexp)
- (let ((fk (erlang-remove-quotes
- (erlang-buffer-substring
- (match-beginning 0) (match-end 0))))
- (mod nil)
- (imports (erlang-get-import)))
- (while (and imports (null mod))
- (if (assoc fk (cdr (car imports)))
- (setq mod (car (car imports)))
- (setq imports (cdr imports))))
- (cond ((eq (preceding-char) ?#)
- (setq fk (concat "-record(" fk)))
- ((eq (preceding-char) ??)
- (setq fk (concat "-define(" fk)))
- ((and (null mod) (not (member fk erlang-int-bifs)))
- (setq mod (erlang-get-module))))
- (setq res (list mod fk)))))
+ (concat erlang-atom-regexp ":" erlang-atom-regexp)))
+ (setq res (list
+ (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning 1) (match-end 1)))
+ (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning (1+ erlang-atom-regexp-matches))
+ (match-end (1+ erlang-atom-regexp-matches)))))))
+ ((looking-at erlang-atom-regexp)
+ (let ((fk (erlang-remove-quotes
+ (erlang-buffer-substring
+ (match-beginning 0) (match-end 0))))
+ (mod nil)
+ (imports (erlang-get-import)))
+ (while (and imports (null mod))
+ (if (assoc fk (cdr (car imports)))
+ (setq mod (car (car imports)))
+ (setq imports (cdr imports))))
+ (cond ((eq (preceding-char) ?#)
+ (setq fk (concat "-record(" fk)))
+ ((eq (preceding-char) ??)
+ (setq fk (concat "-define(" fk)))
+ ((and (null mod) (not (member fk erlang-int-bifs)))
+ (setq mod (erlang-get-module))))
+ (setq res (list mod fk)))))
(store-match-data md)
res)))
@@ -3788,11 +3799,11 @@ In the future the list may contain more elements."
"Return STR, possibly with quotes."
(let ((case-fold-search nil)) ; force string matching to be case sensitive
(if (and (stringp str)
- (not (string-match (eval-when-compile
- (concat "\\`" erlang-atom-regexp "\\'")) str)))
- (progn (if (fboundp 'replace-regexp-in-string)
- (setq str (replace-regexp-in-string "'" "\\'" str t t )))
- (concat "'" str "'"))
+ (not (string-match (eval-when-compile
+ (concat "\\`" erlang-atom-regexp "\\'")) str)))
+ (progn (if (fboundp 'replace-regexp-in-string)
+ (setq str (replace-regexp-in-string "'" "\\'" str t t )))
+ (concat "'" str "'"))
str)))
@@ -3800,9 +3811,9 @@ In the future the list may contain more elements."
"Return STR without quotes, if present."
(let ((md (match-data)))
(prog1
- (if (string-match "\\`'\\(.*\\)'\\'" str)
- (substring str 1 -1)
- str)
+ (if (string-match "\\`'\\(.*\\)'\\'" str)
+ (substring str 1 -1)
+ str)
(store-match-data md))))
(defun erlang-match-next-exported-function (max)
@@ -3872,30 +3883,30 @@ is prompted.
This function is normally placed in the hook `local-write-file-hooks'."
(if erlang-check-module-name
- (let ((mn (erlang-add-quotes-if-needed
- (erlang-get-module)))
- (fn (erlang-add-quotes-if-needed
- (erlang-get-module-from-file-name (buffer-file-name)))))
- (if (and (stringp mn) (stringp fn))
- (or (string-equal mn fn)
- (if (or (eq erlang-check-module-name t)
- (y-or-n-p
- "Module does not match file name. Modify source? "))
- (save-excursion
- (save-restriction
- (widen)
- (goto-char (point-min))
- (if (re-search-forward
- (eval-when-compile
- (concat "^-module\\s *(\\s *\\(\\("
- erlang-atom-regexp
- "\\)?\\)\\s *)\\s *\\."))
- (point-max) t)
- (progn
- (goto-char (match-beginning 1))
- (delete-region (match-beginning 1)
- (match-end 1))
- (insert fn))))))))))
+ (let ((mn (erlang-add-quotes-if-needed
+ (erlang-get-module)))
+ (fn (erlang-add-quotes-if-needed
+ (erlang-get-module-from-file-name (buffer-file-name)))))
+ (if (and (stringp mn) (stringp fn))
+ (or (string-equal mn fn)
+ (if (or (eq erlang-check-module-name t)
+ (y-or-n-p
+ "Module does not match file name. Modify source? "))
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (if (re-search-forward
+ (eval-when-compile
+ (concat "^-module\\s *(\\s *\\(\\("
+ erlang-atom-regexp
+ "\\)?\\)\\s *)\\s *\\."))
+ (point-max) t)
+ (progn
+ (goto-char (match-beginning 1))
+ (delete-region (match-beginning 1)
+ (match-end 1))
+ (insert fn))))))))))
;; Must return nil since it is added to `local-write-file-hook'.
nil)
@@ -3921,13 +3932,13 @@ non-whitespace characters following the point on the current line."
(interactive "P")
(self-insert-command (prefix-numeric-value arg))
(if (or arg
- (and (listp erlang-electric-commands)
- (not (memq 'erlang-electric-semicolon
- erlang-electric-commands)))
- (erlang-in-literal)
- (not (looking-at "\\s *\\(%.*\\)?$"))
- (null (erlang-test-criteria-list
- erlang-electric-semicolon-criteria)))
+ (and (listp erlang-electric-commands)
+ (not (memq 'erlang-electric-semicolon
+ erlang-electric-commands)))
+ (erlang-in-literal)
+ (not (looking-at "\\s *\\(%.*\\)?$"))
+ (null (erlang-test-criteria-list
+ erlang-electric-semicolon-criteria)))
(setq erlang-electric-newline-inhibit nil)
(setq erlang-electric-newline-inhibit t)
(undo-boundary)
@@ -3935,20 +3946,20 @@ non-whitespace characters following the point on the current line."
(end-of-line)
(newline)
(if (condition-case nil
- (progn (erlang-indent-line) t)
- (error (if (bolp) (delete-char -1))))
- (if (not (bolp))
- (save-excursion
- (insert " ->"))
- (condition-case nil
- (progn
- (erlang-generate-new-clause)
- (if erlang-electric-semicolon-insert-blank-lines
- (save-excursion
- (beginning-of-line)
- (newline
- erlang-electric-semicolon-insert-blank-lines))))
- (error (if (bolp) (delete-char -1))))))))
+ (progn (erlang-indent-line) t)
+ (error (if (bolp) (delete-char -1))))
+ (if (not (bolp))
+ (save-excursion
+ (insert " ->"))
+ (condition-case nil
+ (progn
+ (erlang-generate-new-clause)
+ (if erlang-electric-semicolon-insert-blank-lines
+ (save-excursion
+ (beginning-of-line)
+ (newline
+ erlang-electric-semicolon-insert-blank-lines))))
+ (error (if (bolp) (delete-char -1))))))))
(defun erlang-electric-comma (&optional arg)
@@ -3964,12 +3975,12 @@ non-whitespace characters following the point on the current line."
(self-insert-command (prefix-numeric-value arg))
(if (or arg
- (and (listp erlang-electric-commands)
- (not (memq 'erlang-electric-comma erlang-electric-commands)))
- (erlang-in-literal)
- (not (looking-at "\\s *\\(%.*\\)?$"))
- (null (erlang-test-criteria-list
- erlang-electric-comma-criteria)))
+ (and (listp erlang-electric-commands)
+ (not (memq 'erlang-electric-comma erlang-electric-commands)))
+ (erlang-in-literal)
+ (not (looking-at "\\s *\\(%.*\\)?$"))
+ (null (erlang-test-criteria-list
+ erlang-electric-comma-criteria)))
(setq erlang-electric-newline-inhibit nil)
(setq erlang-electric-newline-inhibit t)
(undo-boundary)
@@ -3977,12 +3988,12 @@ non-whitespace characters following the point on the current line."
(end-of-line)
(newline)
(condition-case nil
- (erlang-indent-line)
+ (erlang-indent-line)
(error (if (bolp) (delete-char -1))))))
(defun erlang-electric-lt (&optional arg)
"Insert a less-than sign, and optionally mark it as an open paren."
-
+
(interactive "p")
(self-insert-command arg)
@@ -3992,48 +4003,48 @@ non-whitespace characters following the point on the current line."
(save-excursion
(backward-char 2)
(when (and (eq (char-after (point)) ?<)
- (not (eq (get-text-property (point) 'category)
- 'bitsyntax-open-inner)))
- ;; Then mark the two chars...
- (put-text-property (point) (1+ (point))
- 'category 'bitsyntax-open-outer)
- (forward-char 1)
- (put-text-property (point) (1+ (point))
- 'category 'bitsyntax-open-inner)
- ;;...and unmark any subsequent less-than chars.
- (forward-char 1)
- (while (eq (char-after (point)) ?<)
- (remove-text-properties (point) (1+ (point))
- '(category nil))
- (forward-char 1))))))
+ (not (eq (get-text-property (point) 'category)
+ 'bitsyntax-open-inner)))
+ ;; Then mark the two chars...
+ (put-text-property (point) (1+ (point))
+ 'category 'bitsyntax-open-outer)
+ (forward-char 1)
+ (put-text-property (point) (1+ (point))
+ 'category 'bitsyntax-open-inner)
+ ;;...and unmark any subsequent less-than chars.
+ (forward-char 1)
+ (while (eq (char-after (point)) ?<)
+ (remove-text-properties (point) (1+ (point))
+ '(category nil))
+ (forward-char 1))))))
(defun erlang-after-bitsyntax-close ()
"Return t if point is immediately after a bit-syntax close parenthesis (`>>')."
(and (>= (point) 3)
(save-excursion
- (backward-char 2)
- (and (eq (char-after (point)) ?>)
- (not (eq (get-text-property (point) 'category)
- 'bitsyntax-close-outer))))))
-
+ (backward-char 2)
+ (and (eq (char-after (point)) ?>)
+ (not (eq (get-text-property (point) 'category)
+ 'bitsyntax-close-outer))))))
+
(defun erlang-after-arrow ()
"Return true if point is immediately after a function arrow (`->')."
(and (>= (point) 2)
- (and
- (save-excursion
- (backward-char)
- (eq (char-before (point)) ?-))
- (or (not (listp erlang-electric-commands))
- (memq 'erlang-electric-gt
- erlang-electric-commands))
- (not (erlang-in-literal))
- (looking-at "\\s *\\(%.*\\)?$")
- (erlang-test-criteria-list erlang-electric-arrow-criteria))))
+ (and
+ (save-excursion
+ (backward-char)
+ (eq (char-before (point)) ?-))
+ (or (not (listp erlang-electric-commands))
+ (memq 'erlang-electric-gt
+ erlang-electric-commands))
+ (not (erlang-in-literal))
+ (looking-at "\\s *\\(%.*\\)?$")
+ (erlang-test-criteria-list erlang-electric-arrow-criteria))))
(defun erlang-electric-gt (&optional arg)
"Insert a greater-than sign, and optionally mark it as a close paren."
-
+
(interactive "p")
(self-insert-command arg)
@@ -4044,17 +4055,17 @@ non-whitespace characters following the point on the current line."
(save-excursion
;; Then mark the two chars...
(backward-char 2)
- (put-text-property (point) (1+ (point))
- 'category 'bitsyntax-close-inner)
+ (put-text-property (point) (1+ (point))
+ 'category 'bitsyntax-close-inner)
(forward-char)
(put-text-property (point) (1+ (point))
- 'category 'bitsyntax-close-outer)
+ 'category 'bitsyntax-close-outer)
;;...and unmark any subsequent greater-than chars.
(forward-char)
(while (eq (char-after (point)) ?>)
- (remove-text-properties (point) (1+ (point))
- '(category nil))
- (forward-char))))
+ (remove-text-properties (point) (1+ (point))
+ '(category nil))
+ (forward-char))))
;; Did we just write a function arrow (`->')?
((erlang-after-arrow)
@@ -4063,13 +4074,13 @@ non-whitespace characters following the point on the current line."
(end-of-line)
(newline)
(condition-case nil
- (erlang-indent-line)
- (error (if (bolp) (delete-char -1))))))
+ (erlang-indent-line)
+ (error (if (bolp) (delete-char -1))))))
;; Then it's just a plain greater-than.
(t
nil)))
-
+
(defun erlang-electric-arrow\ off (&optional arg)
"Insert a '>'-sign and possibly a new indented line.
@@ -4089,22 +4100,22 @@ After being split/merged into `erlang-after-arrow' and
(let ((prec (preceding-char)))
(self-insert-command (prefix-numeric-value arg))
(if (or arg
- (and (listp erlang-electric-commands)
- (not (memq 'erlang-electric-arrow
- erlang-electric-commands)))
- (not (eq prec ?-))
- (erlang-in-literal)
- (not (looking-at "\\s *\\(%.*\\)?$"))
- (null (erlang-test-criteria-list
- erlang-electric-arrow-criteria)))
- (setq erlang-electric-newline-inhibit nil)
+ (and (listp erlang-electric-commands)
+ (not (memq 'erlang-electric-arrow
+ erlang-electric-commands)))
+ (not (eq prec ?-))
+ (erlang-in-literal)
+ (not (looking-at "\\s *\\(%.*\\)?$"))
+ (null (erlang-test-criteria-list
+ erlang-electric-arrow-criteria)))
+ (setq erlang-electric-newline-inhibit nil)
(setq erlang-electric-newline-inhibit t)
(undo-boundary)
(end-of-line)
(newline)
(condition-case nil
- (erlang-indent-line)
- (error (if (bolp) (delete-char -1)))))))
+ (erlang-indent-line)
+ (error (if (bolp) (delete-char -1)))))))
(defun erlang-electric-newline (&optional arg)
@@ -4119,30 +4130,30 @@ Should the previous command be another electric command we assume that
the user pressed newline out of old habit, hence we will do nothing."
(interactive "P")
(cond ((and (not arg)
- erlang-electric-newline-inhibit
- (memq last-command erlang-electric-newline-inhibit-list))
- ()) ; Do nothing!
- ((or arg
- (and (listp erlang-electric-commands)
- (not (memq 'erlang-electric-newline
- erlang-electric-commands)))
- (null (erlang-test-criteria-list
- erlang-electric-newline-criteria)))
- (newline (prefix-numeric-value arg)))
- (t
- (if (and comment-multi-line
- (save-excursion
- (beginning-of-line)
- (looking-at (concat "\\s *" comment-start-skip))))
- (let ((str (buffer-substring
- (or (match-end 1) (match-beginning 0))
- (min (match-end 0) (point)))))
- (newline)
- (undo-boundary)
- (insert str))
- (newline)
- (undo-boundary)
- (indent-according-to-mode)))))
+ erlang-electric-newline-inhibit
+ (memq last-command erlang-electric-newline-inhibit-list))
+ ()) ; Do nothing!
+ ((or arg
+ (and (listp erlang-electric-commands)
+ (not (memq 'erlang-electric-newline
+ erlang-electric-commands)))
+ (null (erlang-test-criteria-list
+ erlang-electric-newline-criteria)))
+ (newline (prefix-numeric-value arg)))
+ (t
+ (if (and comment-multi-line
+ (save-excursion
+ (beginning-of-line)
+ (looking-at (concat "\\s *" comment-start-skip))))
+ (let ((str (buffer-substring
+ (or (match-end 1) (match-beginning 0))
+ (min (match-end 0) (point)))))
+ (newline)
+ (undo-boundary)
+ (insert str))
+ (newline)
+ (undo-boundary)
+ (indent-according-to-mode)))))
(defun erlang-test-criteria-list (criteria)
@@ -4167,14 +4178,14 @@ Return t if criteria fulfilled, nil otherwise."
t
(save-excursion
(let ((answer nil))
- (while (and criteria (null answer))
- (if (eq (car criteria) t)
- (setq answer t)
- (setq answer (funcall (car criteria))))
- (setq criteria (cdr criteria)))
- (if (and answer (not (eq answer 'stop)))
- t
- nil)))))
+ (while (and criteria (null answer))
+ (if (eq (car criteria) t)
+ (setq answer t)
+ (setq answer (funcall (car criteria))))
+ (setq criteria (cdr criteria)))
+ (if (and answer (not (eq answer 'stop)))
+ t
+ nil)))))
(defun erlang-in-literal (&optional lim)
@@ -4185,11 +4196,11 @@ Should the point be inside none of the above mentioned types of
context, nil is returned."
(save-excursion
(let* ((lim (or lim (save-excursion
- (erlang-beginning-of-clause)
- (point))))
- (state (if (fboundp 'syntax-ppss) ; post Emacs 21.3
- (funcall (symbol-function 'syntax-ppss))
- (parse-partial-sexp lim (point)))))
+ (erlang-beginning-of-clause)
+ (point))))
+ (state (if (fboundp 'syntax-ppss) ; post Emacs 21.3
+ (funcall (symbol-function 'syntax-ppss))
+ (parse-partial-sexp lim (point)))))
(cond
((eq (nth 3 state) ?') 'atom)
((nth 3 state) 'string)
@@ -4203,7 +4214,7 @@ context, nil is returned."
This function is designed to be a member of a criteria list."
(eq (save-excursion (erlang-skip-blank) (point))
(save-excursion
- (erlang-beginning-of-function -1) (point))))
+ (erlang-beginning-of-function -1) (point))))
(defun erlang-at-end-of-clause-p ()
@@ -4212,7 +4223,7 @@ This function is designed to be a member of a criteria list."
This function is designed to be a member of a criteria list."
(eq (save-excursion (erlang-skip-blank) (point))
(save-excursion
- (erlang-beginning-of-clause -1) (point))))
+ (erlang-beginning-of-clause -1) (point))))
(defun erlang-stop-when-inside-argument-list ()
@@ -4224,22 +4235,22 @@ after `||', `stop' is not returned.
This function is designed to be a member of a criteria list."
(save-excursion
(condition-case nil
- (let ((orig-point (point))
- (state nil))
- (up-list -1)
- (if (not (eq (following-char) ?\[))
- 'stop
- ;; Do not return `stop' when inside a list comprehension
- ;; construction. (The point must be after `||').
- (while (< (point) orig-point)
+ (let ((orig-point (point))
+ (state nil))
+ (up-list -1)
+ (if (not (eq (following-char) ?\[))
+ 'stop
+ ;; Do not return `stop' when inside a list comprehension
+ ;; construction. (The point must be after `||').
+ (while (< (point) orig-point)
(let ((pt (point)))
(setq state (erlang-partial-parse pt orig-point state))
(if (= pt (point))
(error "Illegal syntax"))))
- (if (and (car state) (eq (car (car (car state))) '||))
- nil
- 'stop)))
- (error
+ (if (and (car state) (eq (car (car (car state))) '||))
+ nil
+ 'stop)))
+ (error
nil))))
@@ -4250,11 +4261,11 @@ This function is designed to be a member of a criteria list."
(save-excursion
(beginning-of-line)
(if (and (looking-at (eval-when-compile
- (concat "^" erlang-atom-regexp "\\s *(")))
- (not (looking-at
- (eval-when-compile
- (concat "^" erlang-atom-regexp ".*->")))))
- 'stop
+ (concat "^" erlang-atom-regexp "\\s *(")))
+ (not (looking-at
+ (eval-when-compile
+ (concat "^" erlang-atom-regexp ".*->")))))
+ 'stop
nil)))
@@ -4279,13 +4290,13 @@ A line containing only spaces and tabs is considered empty.
This function is designed to be a member of a criteria list."
(and erlang-next-lines-empty-threshold
(save-excursion
- (let ((left erlang-next-lines-empty-threshold)
- (cont t))
- (while (and cont (> left 0))
- (forward-line 1)
- (setq cont (looking-at "\\s *$"))
- (setq left (- left 1)))
- cont))))
+ (let ((left erlang-next-lines-empty-threshold)
+ (cont t))
+ (while (and cont (> left 0))
+ (forward-line 1)
+ (setq cont (looking-at "\\s *$"))
+ (setq left (- left 1)))
+ cont))))
(defun erlang-at-keyword-end-p ()
@@ -4304,9 +4315,9 @@ This function is designed to be a member of a criteria list."
(eval-when-compile
(if (or (featurep 'bytecomp)
- (featurep 'byte-compile))
+ (featurep 'byte-compile))
(progn
- (require 'etags))))
+ (require 'etags))))
;; Variables:
@@ -4352,26 +4363,26 @@ as on the old form `tag'.
In the completion list, `module:tag' and `module:' shows up.
-Call this function from an appropriate init file, or add it to
-Erlang mode hook with the commands:
- (add-hook 'erlang-mode-hook 'erlang-tags-init)
- (add-hook 'erlang-shell-mode-hook 'erlang-tags-init)
-
This function only works under Emacs 18 and Emacs 19. Currently, It
is not implemented under XEmacs. (Hint: The Emacs 19 etags module
works under XEmacs.)"
(interactive)
(cond ((= erlang-emacs-major-version 18)
- (require 'tags)
- (erlang-tags-define-keys (current-local-map))
- (setq erlang-tags-installed t))
- (t
- (require 'etags)
- ;; Test on a function available in the Emacs 19 version
- ;; of tags but not in the XEmacs version.
- (when (fboundp 'find-tag-noselect)
- (erlang-tags-define-keys (current-local-map))
- (setq erlang-tags-installed t)))))
+ (require 'tags)
+ (erlang-tags-define-keys (current-local-map))
+ (setq erlang-tags-installed t))
+ (t
+ (require 'etags)
+ (set (make-local-variable 'find-tag-default-function)
+ 'erlang-find-tag-for-completion)
+ (if (>= emacs-major-version 25)
+ (add-hook 'xref-backend-functions
+ #'erlang-etags--xref-backend nil t)
+ ;; Test on a function available in the Emacs 19 version
+ ;; of tags but not in the XEmacs version.
+ (when (fboundp 'find-tag-noselect)
+ (erlang-tags-define-keys (current-local-map))
+ (setq erlang-tags-installed t))))))
@@ -4386,11 +4397,11 @@ works under XEmacs.)"
(let ((alist erlang-tags-function-alist))
(while alist
(let* ((old (car (car alist)))
- (new (cdr (car alist)))
- (keys (append (where-is-internal old global-map))))
- (while keys
- (define-key map (car keys) new)
- (setq keys (cdr keys))))
+ (new (cdr (car alist)))
+ (keys (append (where-is-internal old global-map))))
+ (while keys
+ (define-key map (car keys) new)
+ (setq keys (cdr keys))))
(setq alist (cdr alist))))
;; Update the menu.
(erlang-menu-substitute erlang-menu-base-items erlang-tags-function-alist)
@@ -4403,11 +4414,11 @@ Search `-import' list of imported functions.
Single quotes are been stripped away."
(let ((mod-func (erlang-get-function-under-point)))
(cond ((null mod-func)
- nil)
- ((null (car mod-func))
- (nth 1 mod-func))
- (t
- (concat (car mod-func) ":" (nth 1 mod-func))))))
+ nil)
+ ((null (car mod-func))
+ (nth 1 mod-func))
+ (t
+ (concat (car mod-func) ":" (nth 1 mod-func))))))
;; Return `t' since it is used inside `tags-loop-form'.
@@ -4426,31 +4437,31 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(defun erlang-find-tag-other-window (tagname &optional next-p regexp-p)
"Like `find-tag-other-window' but aware of Erlang modules."
(interactive (erlang-tag-interactive
- "Find `module:tag' or `tag' other window: "))
+ "Find `module:tag' or `tag' other window: "))
;; This is to deal with the case where the tag is found in the
;; selected window's buffer; without this, point is moved in both
;; windows. To prevent this, we save the selected window's point
;; before doing find-tag-noselect, and restore it afterwards.
(let* ((window-point (window-point (selected-window)))
- (tagbuf (erlang-find-tag-noselect tagname next-p regexp-p))
- (tagpoint (progn (set-buffer tagbuf) (point))))
+ (tagbuf (erlang-find-tag-noselect tagname next-p regexp-p))
+ (tagpoint (progn (set-buffer tagbuf) (point))))
(set-window-point (prog1
- (selected-window)
- (switch-to-buffer-other-window tagbuf)
- ;; We have to set this new window's point; it
- ;; might already have been displaying a
- ;; different portion of tagbuf, in which case
- ;; switch-to-buffer-other-window doesn't set
- ;; the window's point from the buffer.
- (set-window-point (selected-window) tagpoint))
- window-point)))
+ (selected-window)
+ (switch-to-buffer-other-window tagbuf)
+ ;; We have to set this new window's point; it
+ ;; might already have been displaying a
+ ;; different portion of tagbuf, in which case
+ ;; switch-to-buffer-other-window doesn't set
+ ;; the window's point from the buffer.
+ (set-window-point (selected-window) tagpoint))
+ window-point)))
(defun erlang-find-tag-other-frame (tagname &optional next-p)
"Like `find-tag-other-frame' but aware of Erlang modules."
(interactive (erlang-tag-interactive
- "Find `module:tag' or `tag' other frame: "))
+ "Find `module:tag' or `tag' other frame: "))
(let ((pop-up-frames t))
(erlang-find-tag-other-window tagname next-p)))
@@ -4458,13 +4469,13 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(defun erlang-find-tag-regexp (regexp &optional next-p other-window)
"Like `find-tag-regexp' but aware of Erlang modules."
(interactive (if (fboundp 'find-tag-regexp)
- (erlang-tag-interactive
- "Find `module:regexp' or `regexp': ")
- (error "This version of Emacs can't find tags by regexps")))
+ (erlang-tag-interactive
+ "Find `module:regexp' or `regexp': ")
+ (error "This version of Emacs can't find tags by regexps")))
(funcall (if other-window
- 'erlang-find-tag-other-window
- 'erlang-find-tag)
- regexp next-p t))
+ 'erlang-find-tag-other-window
+ 'erlang-find-tag)
+ regexp next-p t))
;; Just like C-u M-. This could be added to the menu.
@@ -4473,7 +4484,7 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(interactive)
(let ((current-prefix-arg '(4)))
(if erlang-tags-installed
- (call-interactively 'erlang-find-tag)
+ (call-interactively 'erlang-find-tag)
(call-interactively 'find-tag))))
@@ -4485,9 +4496,9 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
;; without extension and directory matches the module.
;;
;; * `module:tag'
-;; Emacs 19: Replace test functions with functions aware of
-;; Erlang modules. Tricky because the etags system wasn't
-;; built for these kind of operations...
+;; Emacs 19: Replace test functions with functions aware of
+;; Erlang modules. Tricky because the etags system wasn't
+;; built for these kind of operations...
;;
;; Emacs 18: We loop over `find-tag' until we find a file
;; whose module matches the requested module. The
@@ -4506,78 +4517,78 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
;; know where to restart a tags command.
(if (boundp 'tags-loop-form)
(funcall (symbol-function 'set)
- 'tags-loop-form '(erlang-find-tag nil t)))
+ 'tags-loop-form '(erlang-find-tag nil t)))
(save-window-excursion
(cond
((string-match ":$" modtagname)
;; Only the module name was given. Read all files whose file name
;; match.
(let ((modname (substring modtagname 0 (match-beginning 0)))
- (file nil))
- (if (not next-p)
- (save-excursion
- (visit-tags-table-buffer)
- (setq erlang-tags-file-list
- (funcall (symbol-function 'tags-table-files)))))
- (while (null file)
- (or erlang-tags-file-list
- (save-excursion
- (if (and (featurep 'etags)
- (funcall
- (symbol-function 'visit-tags-table-buffer) 'same)
- (funcall
- (symbol-function 'visit-tags-table-buffer) t))
- (setq erlang-tags-file-list
- (funcall (symbol-function 'tags-table-files)))
- (error "No %stags containing %s" (if next-p "more " "")
- modtagname))))
- (if erlang-tags-file-list
- (let ((this-module (erlang-get-module-from-file-name
- (car erlang-tags-file-list))))
- (if (and (stringp this-module)
- (string= modname this-module))
- (setq file (car erlang-tags-file-list)))
- (setq erlang-tags-file-list (cdr erlang-tags-file-list)))))
- (set-buffer (or (get-file-buffer file)
- (find-file-noselect file)))))
+ (file nil))
+ (if (not next-p)
+ (save-excursion
+ (visit-tags-table-buffer)
+ (setq erlang-tags-file-list
+ (funcall (symbol-function 'tags-table-files)))))
+ (while (null file)
+ (or erlang-tags-file-list
+ (save-excursion
+ (if (and (featurep 'etags)
+ (funcall
+ (symbol-function 'visit-tags-table-buffer) 'same)
+ (funcall
+ (symbol-function 'visit-tags-table-buffer) t))
+ (setq erlang-tags-file-list
+ (funcall (symbol-function 'tags-table-files)))
+ (error "No %stags containing %s" (if next-p "more " "")
+ modtagname))))
+ (if erlang-tags-file-list
+ (let ((this-module (erlang-get-module-from-file-name
+ (car erlang-tags-file-list))))
+ (if (and (stringp this-module)
+ (string= modname this-module))
+ (setq file (car erlang-tags-file-list)))
+ (setq erlang-tags-file-list (cdr erlang-tags-file-list)))))
+ (set-buffer (or (get-file-buffer file)
+ (find-file-noselect file)))))
((string-match ":" modtagname)
(if (boundp 'find-tag-tag-order)
- ;; Method one: Add module-recognising functions to the
- ;; list of order functions. However, the tags system
- ;; from Emacs 18, and derives thereof (read: XEmacs)
- ;; hasn't got this feature.
- (progn
- (erlang-tags-install-module-check)
- (unwind-protect
- (funcall (symbol-function 'find-tag)
- modtagname next-p regexp-p)
- (erlang-tags-remove-module-check)))
- ;; Method two: Call the tags system until a file matching
- ;; the module is found. This could result in that many
- ;; files are read. (e.g. The tag "foo:file" will take a
- ;; while to process.)
- (let* ((modname (substring modtagname 0 (match-beginning 0)))
- (tagname (substring modtagname (match-end 0) nil))
- (last-tag tagname)
- file)
- (while
- (progn
- (funcall (symbol-function 'find-tag) tagname next-p regexp-p)
- (setq next-p t)
- ;; Determine the module form the file name. (The
- ;; alternative, to check `-module', would make this
- ;; code useless for non-Erlang programs.)
- (setq file (erlang-get-module-from-file-name buffer-file-name))
- (not (and (stringp file)
- (string= modname file))))))))
+ ;; Method one: Add module-recognising functions to the
+ ;; list of order functions. However, the tags system
+ ;; from Emacs 18, and derives thereof (read: XEmacs)
+ ;; hasn't got this feature.
+ (progn
+ (erlang-tags-install-module-check)
+ (unwind-protect
+ (funcall (symbol-function 'find-tag)
+ modtagname next-p regexp-p)
+ (erlang-tags-remove-module-check)))
+ ;; Method two: Call the tags system until a file matching
+ ;; the module is found. This could result in that many
+ ;; files are read. (e.g. The tag "foo:file" will take a
+ ;; while to process.)
+ (let* ((modname (substring modtagname 0 (match-beginning 0)))
+ (tagname (substring modtagname (match-end 0) nil))
+ (last-tag tagname)
+ file)
+ (while
+ (progn
+ (funcall (symbol-function 'find-tag) tagname next-p regexp-p)
+ (setq next-p t)
+ ;; Determine the module form the file name. (The
+ ;; alternative, to check `-module', would make this
+ ;; code useless for non-Erlang programs.)
+ (setq file (erlang-get-module-from-file-name buffer-file-name))
+ (not (and (stringp file)
+ (string= modname file))))))))
(t
(funcall (symbol-function 'find-tag) modtagname next-p regexp-p)))
- (current-buffer))) ; Return the new buffer.
+ (current-buffer))) ; Return the new buffer.
+
-
@@ -4594,18 +4605,18 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(require 'tags)))
(if current-prefix-arg
(list nil (if (< (prefix-numeric-value current-prefix-arg) 0)
- '-
- t))
+ '-
+ t))
(let* ((default (erlang-find-tag-default))
- (prompt (if default
- (format "%s(default %s) " prompt default)
- prompt))
- (spec (if (featurep 'etags)
- (completing-read prompt 'erlang-tags-complete-tag)
- (read-string prompt))))
+ (prompt (if default
+ (format "%s(default %s) " prompt default)
+ prompt))
+ (spec (if (featurep 'etags)
+ (completing-read prompt 'erlang-tags-complete-tag)
+ (read-string prompt))))
(list (if (equal spec "")
- (or default (error "There is no default tag"))
- spec)))))
+ (or default (error "There is no default tag"))
+ spec)))))
;; Search tag functions which are aware of Erlang modules. The tactic
@@ -4632,21 +4643,21 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(setq erlang-tags-orig-format-hooks
(symbol-value 'tags-table-format-hooks))
(funcall (symbol-function 'set) 'tags-table-format-hooks
- (cons 'erlang-tags-recognize-tags-table
- erlang-tags-orig-format-hooks))
+ (cons 'erlang-tags-recognize-tags-table
+ erlang-tags-orig-format-hooks))
(setq erlang-tags-buffer-list '())
- ))
-
+ ))
+
;; Install our functions in the TAGS files already resident.
(save-excursion
(let ((files (symbol-value 'tags-table-computed-list)))
(while files
- (if (stringp (car files))
- (if (get-file-buffer (car files))
- (progn
- (set-buffer (get-file-buffer (car files)))
- (erlang-tags-install-local))))
- (setq files (cdr files))))))
+ (if (stringp (car files))
+ (if (get-file-buffer (car files))
+ (progn
+ (set-buffer (get-file-buffer (car files)))
+ (erlang-tags-install-local))))
+ (setq files (cdr files))))))
(defun erlang-tags-install-local ()
@@ -4656,23 +4667,23 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
;; Mark this buffer as "installed" and record.
(set (make-local-variable 'erlang-tags-buffer-installed-p) t)
(setq erlang-tags-buffer-list
- (cons (current-buffer) erlang-tags-buffer-list))
+ (cons (current-buffer) erlang-tags-buffer-list))
;; Save the original values.
(set (make-local-variable 'erlang-tags-orig-tag-order)
- (symbol-value 'find-tag-tag-order))
+ (symbol-value 'find-tag-tag-order))
(set (make-local-variable 'erlang-tags-orig-regexp-tag-order)
- (symbol-value 'find-tag-regexp-tag-order))
+ (symbol-value 'find-tag-regexp-tag-order))
(set (make-local-variable 'erlang-tags-orig-search-function)
- (symbol-value 'find-tag-search-function))
+ (symbol-value 'find-tag-search-function))
(set (make-local-variable 'erlang-tags-orig-regexp-search-function)
- (symbol-value 'find-tag-regexp-search-function))
+ (symbol-value 'find-tag-regexp-search-function))
;; Install our own functions.
(set (make-local-variable 'find-tag-search-function)
- 'erlang-tags-search-forward)
+ 'erlang-tags-search-forward)
(set (make-local-variable 'find-tag-regexp-search-function)
- 'erlang-tags-regexp-search-forward)
+ 'erlang-tags-regexp-search-forward)
(set (make-local-variable 'find-tag-tag-order)
(mapcar #'erlang-make-order-function-aware-of-modules
erlang-tags-orig-tag-order))
@@ -4700,13 +4711,13 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(cond
((>= erlang-emacs-major-version 20)
(funcall (symbol-function 'set)
- 'tags-table-format-functions
- erlang-tags-orig-format-functions)
+ 'tags-table-format-functions
+ erlang-tags-orig-format-functions)
)
- (t
+ (t
(funcall (symbol-function 'set)
- 'tags-table-format-hooks
- erlang-tags-orig-format-hooks)
+ 'tags-table-format-hooks
+ erlang-tags-orig-format-hooks)
))
;; Remove our functions from the TAGS files. (Note that
@@ -4715,11 +4726,11 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
(save-excursion
(let ((buffers erlang-tags-buffer-list))
(while buffers
- (if (buffer-name (car buffers))
- (progn
- (set-buffer (car buffers))
- (erlang-tags-remove-local)))
- (setq buffers (cdr buffers))))))
+ (if (buffer-name (car buffers))
+ (progn
+ (set-buffer (car buffers))
+ (erlang-tags-remove-local)))
+ (setq buffers (cdr buffers))))))
(defun erlang-tags-remove-local ()
@@ -4728,14 +4739,14 @@ Tags can be given on the forms `tag', `module:', `module:tag'."
()
(funcall (symbol-function 'set) 'erlang-tags-buffer-installed-p nil)
(funcall (symbol-function 'set)
- 'find-tag-tag-order erlang-tags-orig-tag-order)
+ 'find-tag-tag-order erlang-tags-orig-tag-order)
(funcall (symbol-function 'set)
- 'find-tag-regexp-tag-order erlang-tags-orig-regexp-tag-order)
+ 'find-tag-regexp-tag-order erlang-tags-orig-regexp-tag-order)
(funcall (symbol-function 'set)
- 'find-tag-search-function erlang-tags-orig-search-function)
+ 'find-tag-search-function erlang-tags-orig-search-function)
(funcall (symbol-function 'set)
- 'find-tag-regexp-search-function
- erlang-tags-orig-regexp-search-function)))
+ 'find-tag-regexp-search-function
+ erlang-tags-orig-regexp-search-function)))
(defun erlang-tags-recognize-tags-table ()
@@ -4764,10 +4775,10 @@ for a tag on the form `module:tag'."
(if (string-match ":" tag)
(setq tag (substring tag (match-end 0) nil)))
(if (eq erlang-tags-orig-regexp-search-function
- 'erlang-tags-regexp-search-forward)
+ 'erlang-tags-regexp-search-forward)
(re-search-forward tag bound noerror count)
(funcall erlang-tags-orig-regexp-search-function
- tag bound noerror count)))
+ tag bound noerror count)))
;;; Tags completion, Emacs 19 `etags' specific.
;;;
@@ -4775,8 +4786,6 @@ for a tag on the form `module:tag'."
;;; completion-table' containing all normal tags plus tags on the form
;;; `module:tag' and `module:'.
-;; PENDING - Should probably make use of the
-;; `completion-at-point-functions' hook instead of this advice.
(when (and (locate-library "etags")
(require 'etags)
(fboundp 'etags-tags-completion-table)
@@ -4784,11 +4793,7 @@ for a tag on the form `module:tag'."
(if (fboundp 'advice-add)
;; Emacs 24.4+
(advice-add 'etags-tags-completion-table :around
- (lambda (oldfun)
- (if erlang-replace-etags-tags-completion-table
- (erlang-etags-tags-completion-table)
- (funcall oldfun)))
- (list :name 'erlang-replace-tags-table))
+ #'erlang-etags-tags-completion-table-advice)
;; Emacs 23.1-24.3
(defadvice etags-tags-completion-table (around
erlang-replace-tags-table
@@ -4797,6 +4802,11 @@ for a tag on the form `module:tag'."
(setq ad-return-value (erlang-etags-tags-completion-table))
ad-do-it))))
+(defun erlang-etags-tags-completion-table-advice (oldfun)
+ (if erlang-replace-etags-tags-completion-table
+ (erlang-etags-tags-completion-table)
+ (funcall oldfun)))
+
(defun erlang-complete-tag ()
"Perform tags completion on the text around point.
Completes to the set of names listed in the current tags table.
@@ -4807,26 +4817,25 @@ about Erlang modules."
(condition-case nil
(require 'etags)
(error nil))
- (cond ((and erlang-tags-installed
- (fboundp 'etags-tags-completion-table)
+ (cond ((and (fboundp 'etags-tags-completion-table)
(fboundp 'tags-lazy-completion-table)) ; Emacs 23.1+
(let ((erlang-replace-etags-tags-completion-table t))
(complete-tag)))
((and erlang-tags-installed
(fboundp 'complete-tag)
(fboundp 'tags-complete-tag)) ; Emacs 19-22
- (let ((orig-tags-complete-tag (symbol-function 'tags-complete-tag)))
- (fset 'tags-complete-tag
- (symbol-function 'erlang-tags-complete-tag))
- (unwind-protect
+ (let ((orig-tags-complete-tag (symbol-function 'tags-complete-tag)))
+ (fset 'tags-complete-tag
+ (symbol-function 'erlang-tags-complete-tag))
+ (unwind-protect
(complete-tag)
- (fset 'tags-complete-tag orig-tags-complete-tag))))
- ((fboundp 'complete-tag) ; Emacs 19
+ (fset 'tags-complete-tag orig-tags-complete-tag))))
+ ((fboundp 'complete-tag) ; Emacs 19
(complete-tag))
- ((fboundp 'tag-complete-symbol) ; XEmacs
- (funcall (symbol-function 'tag-complete-symbol)))
- (t
- (error "This version of Emacs can't complete tags"))))
+ ((fboundp 'tag-complete-symbol) ; XEmacs
+ (funcall (symbol-function 'tag-complete-symbol)))
+ (t
+ (error "This version of Emacs can't complete tags"))))
(defun erlang-find-tag-for-completion ()
@@ -4850,9 +4859,9 @@ about Erlang modules."
;; If we need to ask for the tag table, allow that.
(let ((enable-recursive-minibuffers t))
(visit-tags-table-buffer))
- (if (eq what t)
- (all-completions string (erlang-tags-completion-table) predicate)
- (try-completion string (erlang-tags-completion-table) predicate)))))
+ (if (eq what t)
+ (all-completions string (erlang-tags-completion-table) predicate)
+ (try-completion string (erlang-tags-completion-table) predicate)))))
;; `tags-completion-table' calls itself recursively, make it
@@ -4862,22 +4871,22 @@ about Erlang modules."
(defun erlang-tags-completion-table ()
"Build completion table. Tags on the form `tag' or `module:tag'."
(setq erlang-tags-orig-completion-table
- (symbol-function 'tags-completion-table))
+ (symbol-function 'tags-completion-table))
(fset 'tags-completion-table
- (symbol-function 'erlang-tags-completion-table-1))
+ (symbol-function 'erlang-tags-completion-table-1))
(unwind-protect
(erlang-tags-completion-table-1)
(fset 'tags-completion-table
- erlang-tags-orig-completion-table)))
+ erlang-tags-orig-completion-table)))
(defun erlang-tags-completion-table-1 ()
(make-local-variable 'erlang-tags-completion-table)
(or erlang-tags-completion-table
(let ((tags-completion-table nil)
- (tags-completion-table-function
- 'erlang-etags-tags-completion-table))
- (funcall erlang-tags-orig-completion-table)
- (setq erlang-tags-completion-table tags-completion-table))))
+ (tags-completion-table-function
+ 'erlang-etags-tags-completion-table))
+ (funcall erlang-tags-orig-completion-table)
+ (setq erlang-tags-completion-table tags-completion-table))))
@@ -4978,7 +4987,7 @@ about Erlang modules."
(cl-defmethod xref-backend-identifier-completion-table
((_backend (eql erlang-etags)))
(let ((erlang-replace-etags-tags-completion-table t))
- (tags-completion-table))))))
+ (tags-completion-table))))))
@@ -5213,44 +5222,36 @@ The following special commands are available:
(setq comint-input-ignoredups t)
(setq comint-scroll-show-maximum-output t)
(setq comint-scroll-to-bottom-on-output t)
- ;; In Emacs 19.30, `add-hook' has got a `local' flag, use it. If
- ;; the call fails, just call the normal `add-hook'.
- (condition-case nil
- (progn
- (add-hook 'comint-output-filter-functions
- 'inferior-erlang-strip-delete nil t)
- (add-hook 'comint-output-filter-functions
- 'inferior-erlang-strip-ctrl-m nil t))
- (error
- (funcall (symbol-function 'make-local-hook)
- 'comint-output-filter-functions) ; obsolete as of Emacs 21.1
- (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-delete)
- (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-ctrl-m)))
+ (add-hook 'comint-output-filter-functions
+ 'inferior-erlang-strip-delete nil t)
+ (add-hook 'comint-output-filter-functions
+ 'inferior-erlang-strip-ctrl-m nil t)
;; Some older versions of comint don't have an input ring.
(if (fboundp 'comint-read-input-ring)
(progn
- (setq comint-input-ring-file-name erlang-input-ring-file-name)
- (comint-read-input-ring t)
- (make-local-variable 'kill-buffer-hook)
- (add-hook 'kill-buffer-hook 'comint-write-input-ring)))
+ (setq comint-input-ring-file-name erlang-input-ring-file-name)
+ (comint-read-input-ring t)
+ (make-local-variable 'kill-buffer-hook)
+ (add-hook 'kill-buffer-hook 'comint-write-input-ring)))
;; At least in Emacs 21, we need to be in `compilation-minor-mode'
;; for `next-error' to work. We can avoid it clobbering the shell
;; keys thus.
(when inferior-erlang-use-cmm
(compilation-minor-mode 1)
(set (make-local-variable 'minor-mode-overriding-map-alist)
- `((compilation-minor-mode
- . ,(let ((map (make-sparse-keymap)))
- ;; It would be useful to put keymap properties on the
- ;; error lines so that we could use RET and mouse-2
- ;; on them directly.
- (when (boundp 'compilation-skip-threshold) ; new compile.el
- (define-key map [mouse-2] #'erlang-mouse-2-command)
- (define-key map "\C-m" #'erlang-RET-command))
- (if (boundp 'compilation-menu-map)
- (define-key map [menu-bar compilation]
- (cons "Errors" compilation-menu-map)))
- map)))))
+ `((compilation-minor-mode
+ . ,(let ((map (make-sparse-keymap)))
+ ;; It would be useful to put keymap properties on the
+ ;; error lines so that we could use RET and mouse-2
+ ;; on them directly.
+ (when (boundp 'compilation-skip-threshold) ; new compile.el
+ (define-key map [mouse-2] #'erlang-mouse-2-command)
+ (define-key map "\C-m" #'erlang-RET-command))
+ (if (boundp 'compilation-menu-map)
+ (define-key map [menu-bar compilation]
+ (cons "Errors" compilation-menu-map)))
+ map)))))
+ (erlang-tags-init)
(run-hooks 'erlang-shell-mode-hook))
@@ -5259,9 +5260,9 @@ The following special commands are available:
Selects Comint or Compilation mode command as appropriate."
(interactive "e")
(if (save-window-excursion
- (save-excursion
- (mouse-set-point event)
- (consp (get-text-property (line-beginning-position) 'message))))
+ (save-excursion
+ (mouse-set-point event)
+ (consp (get-text-property (line-beginning-position) 'message))))
(call-interactively (lookup-key compilation-mode-map [mouse-2]))
(call-interactively (lookup-key comint-mode-map [mouse-2]))))
@@ -5277,7 +5278,7 @@ Selects Comint or Compilation mode command as appropriate."
(define-key map "\M-\t" 'erlang-complete-tag)
(define-key map "\C-a" 'comint-bol) ; Normally the other way around.
(define-key map "\C-c\C-a" 'beginning-of-line)
- (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
+ (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
(define-key map "\M-\C-m" 'compile-goto-error)
(unless inferior-erlang-use-cmm
(define-key map "\C-x`" 'erlang-next-error)))
@@ -5351,8 +5352,8 @@ editing control characters:
(when current-prefix-arg
(list (if (fboundp 'read-shell-command)
;; `read-shell-command' is a new function in Emacs 23.
- (read-shell-command "Erlang command: ")
- (read-string "Erlang command: ")))))
+ (read-shell-command "Erlang command: ")
+ (read-string "Erlang command: ")))))
(require 'comint)
(let (cmd opts)
(if command
@@ -5379,16 +5380,16 @@ editing control characters:
(setq list-buffers-directory fake-file-name))))
(setq inferior-erlang-process
- (get-buffer-process inferior-erlang-buffer))
- (if (> 21 erlang-emacs-major-version) ; funcalls to avoid compiler warnings
- (funcall (symbol-function 'set-process-query-on-exit-flag)
- inferior-erlang-process nil)
+ (get-buffer-process inferior-erlang-buffer))
+ (if (> 21 erlang-emacs-major-version) ; funcalls to avoid compiler warnings
+ (funcall (symbol-function 'set-process-query-on-exit-flag)
+ inferior-erlang-process nil)
(funcall (symbol-function 'process-kill-without-query) inferior-erlang-process))
(if erlang-inferior-shell-split-window
(switch-to-buffer-other-window inferior-erlang-buffer)
- (switch-to-buffer inferior-erlang-buffer))
+ (switch-to-buffer inferior-erlang-buffer))
(if (and (not (eq system-type 'windows-nt))
- (eq inferior-erlang-shell-type 'newshell))
+ (eq inferior-erlang-shell-type 'newshell))
(setq comint-process-echoes t))
(erlang-shell-mode))
@@ -5420,22 +5421,22 @@ frame will become deselected before the next command."
(or (inferior-erlang-running-p)
(error "No inferior Erlang process is running"))
(let ((win (inferior-erlang-window
- inferior-erlang-display-buffer-any-frame))
- (frames-p (fboundp 'selected-frame)))
+ inferior-erlang-display-buffer-any-frame))
+ (frames-p (fboundp 'selected-frame)))
(if (null win)
- (let ((old-win (selected-window)))
- (save-excursion
- (switch-to-buffer-other-window inferior-erlang-buffer)
- (setq win (selected-window)))
- (select-window old-win))
+ (let ((old-win (selected-window)))
+ (save-excursion
+ (switch-to-buffer-other-window inferior-erlang-buffer)
+ (setq win (selected-window)))
+ (select-window old-win))
(if (and window-system
- frames-p
- (or select
- (eq inferior-erlang-display-buffer-any-frame 'raise))
- (not (eq (selected-frame) (window-frame win))))
- (raise-frame (window-frame win))))
+ frames-p
+ (or select
+ (eq inferior-erlang-display-buffer-any-frame 'raise))
+ (not (eq (selected-frame) (window-frame win))))
+ (raise-frame (window-frame win))))
(if select
- (select-window win))
+ (select-window win))
(sit-for 0)
win))
@@ -5452,8 +5453,8 @@ frame will become deselected before the next command."
"Return the window containing the inferior Erlang, or nil."
(and (inferior-erlang-running-p)
(if (and all-frames (>= erlang-emacs-major-version 19))
- (get-buffer-window inferior-erlang-buffer t)
- (get-buffer-window inferior-erlang-buffer))))
+ (get-buffer-window inferior-erlang-buffer t)
+ (get-buffer-window inferior-erlang-buffer))))
(defun inferior-erlang-wait-prompt ()
@@ -5461,21 +5462,21 @@ frame will become deselected before the next command."
(if (eq inferior-erlang-prompt-timeout t)
()
(or (inferior-erlang-running-p)
- (error "No inferior Erlang shell is running"))
+ (error "No inferior Erlang shell is running"))
(with-current-buffer inferior-erlang-buffer
(let ((msg nil))
- (while (save-excursion
- (goto-char (process-mark inferior-erlang-process))
- (forward-line 0)
- (not (looking-at comint-prompt-regexp)))
- (if msg
- ()
- (setq msg t)
- (message "Waiting for Erlang shell prompt (press C-g to abort)."))
- (or (accept-process-output inferior-erlang-process
- inferior-erlang-prompt-timeout)
- (error "No Erlang shell prompt before timeout")))
- (if msg (message ""))))))
+ (while (save-excursion
+ (goto-char (process-mark inferior-erlang-process))
+ (forward-line 0)
+ (not (looking-at comint-prompt-regexp)))
+ (if msg
+ ()
+ (setq msg t)
+ (message "Waiting for Erlang shell prompt (press C-g to abort)."))
+ (or (accept-process-output inferior-erlang-process
+ inferior-erlang-prompt-timeout)
+ (error "No Erlang shell prompt before timeout")))
+ (if msg (message ""))))))
(defun inferior-erlang-send-empty-cmd-unless-already-at-prompt ()
"If not already at a prompt, try to send an empty cmd to get a prompt.
@@ -5484,12 +5485,12 @@ situations, for instance if a crash or error report from sasl
has been printed after the last prompt."
(with-current-buffer inferior-erlang-buffer
(if (> (point-max) 1)
- ;; make sure we get a prompt if buffer contains data
- (if (save-excursion
- (goto-char (process-mark inferior-erlang-process))
- (forward-line 0)
- (not (looking-at comint-prompt-regexp)))
- (inferior-erlang-send-command "")))))
+ ;; make sure we get a prompt if buffer contains data
+ (if (save-excursion
+ (goto-char (process-mark inferior-erlang-process))
+ (forward-line 0)
+ (not (looking-at comint-prompt-regexp)))
+ (inferior-erlang-send-command "")))))
(autoload 'comint-send-input "comint")
@@ -5506,10 +5507,10 @@ Return the position after the newly inserted command."
(or (inferior-erlang-running-p)
(error "No inferior Erlang process is running"))
(let ((old-buffer (current-buffer))
- (insert-point (marker-position (process-mark inferior-erlang-process)))
- (insert-length (if comint-process-echoes
- 0
- (1+ (length cmd)))))
+ (insert-point (marker-position (process-mark inferior-erlang-process)))
+ (insert-length (if comint-process-echoes
+ 0
+ (1+ (length cmd)))))
(set-buffer inferior-erlang-buffer)
(goto-char insert-point)
(insert cmd)
@@ -5522,21 +5523,21 @@ Return the position after the newly inserted command."
;; This was previously cautioned against in the Lisp manual. It
;; has been sorted out in Emacs 21. -- fx
(let ((comint-eol-on-send nil)
- (comint-input-filter (if hist comint-input-filter 'ignore)))
+ (comint-input-filter (if hist comint-input-filter 'ignore)))
(if (and (not erlang-xemacs-p)
- (>= emacs-major-version 22))
- (comint-send-input nil t)
- (comint-send-input)))
+ (>= emacs-major-version 22))
+ (comint-send-input nil t)
+ (comint-send-input)))
;; Adjust all windows whose points are incorrect.
(if (null comint-process-echoes)
- (walk-windows
- (function
- (lambda (window)
- (if (and (eq (window-buffer window) inferior-erlang-buffer)
- (= (window-point window) insert-point))
- (set-window-point window
- (+ insert-point insert-length)))))
- nil t))
+ (walk-windows
+ (function
+ (lambda (window)
+ (if (and (eq (window-buffer window) inferior-erlang-buffer)
+ (= (window-point window) insert-point))
+ (set-window-point window
+ (+ insert-point insert-length)))))
+ nil t))
(set-buffer old-buffer)
(+ insert-point insert-length)))
@@ -5545,17 +5546,17 @@ Return the position after the newly inserted command."
"Remove `^H' (delete) and the characters it was supposed to remove."
(interactive)
(if (and (boundp 'comint-last-input-end)
- (boundp 'comint-last-output-start))
+ (boundp 'comint-last-output-start))
(save-excursion
- (goto-char
- (if (erlang-interactive-p)
- (symbol-value 'comint-last-input-end)
- (symbol-value 'comint-last-output-start)))
- (while (progn (skip-chars-forward "^\C-h")
- (not (eq (point) (point-max))))
- (delete-char 1)
- (or (bolp)
- (backward-delete-char 1))))))
+ (goto-char
+ (if (erlang-interactive-p)
+ (symbol-value 'comint-last-input-end)
+ (symbol-value 'comint-last-output-start)))
+ (while (progn (skip-chars-forward "^\C-h")
+ (not (eq (point) (point-max))))
+ (delete-char 1)
+ (or (bolp)
+ (backward-delete-char 1))))))
;; Basically `comint-strip-ctrl-m', with a few extra checks.
@@ -5563,15 +5564,15 @@ Return the position after the newly inserted command."
"Strip trailing `^M' characters from the current output group."
(interactive)
(if (and (boundp 'comint-last-input-end)
- (boundp 'comint-last-output-start))
+ (boundp 'comint-last-output-start))
(let ((pmark (process-mark (get-buffer-process (current-buffer)))))
- (save-excursion
- (goto-char
- (if (erlang-interactive-p)
- (symbol-value 'comint-last-input-end)
- (symbol-value 'comint-last-output-start)))
- (while (re-search-forward "\r+$" pmark t)
- (replace-match "" t t))))))
+ (save-excursion
+ (goto-char
+ (if (erlang-interactive-p)
+ (symbol-value 'comint-last-input-end)
+ (symbol-value 'comint-last-output-start)))
+ (while (re-search-forward "\r+$" pmark t)
+ (replace-match "" t t))))))
(defun inferior-erlang-compile (arg)
@@ -5594,18 +5595,18 @@ There exists two workarounds for this bug:
(save-some-buffers)
(inferior-erlang-prepare-for-input)
(let* ((dir (inferior-erlang-compile-outdir))
- (noext (substring (erlang-local-buffer-file-name) 0 -4))
- (opts (append (list (cons 'outdir dir))
- (if current-prefix-arg
- (list 'debug_info 'export_all))
- erlang-compile-extra-opts))
- end)
+ (noext (substring (erlang-local-buffer-file-name) 0 -4))
+ (opts (append (list (cons 'outdir dir))
+ (if current-prefix-arg
+ (list 'debug_info 'export_all))
+ erlang-compile-extra-opts))
+ end)
(with-current-buffer inferior-erlang-buffer
(when (fboundp 'compilation-forget-errors)
(compilation-forget-errors)))
(setq end (inferior-erlang-send-command
- (inferior-erlang-compute-compile-command noext opts)
- nil))
+ (inferior-erlang-compute-compile-command noext opts)
+ nil))
(sit-for 0)
(inferior-erlang-wait-prompt)
(with-current-buffer inferior-erlang-buffer
@@ -5619,7 +5620,7 @@ The buffer is displayed, according to `inferior-erlang-display-buffer'
unless the optional NO-DISPLAY is non-nil."
(or (inferior-erlang-running-p)
(save-excursion
- (inferior-erlang)))
+ (inferior-erlang)))
(or (inferior-erlang-running-p)
(error "Error starting inferior Erlang shell"))
(if (not no-display)
@@ -5631,96 +5632,96 @@ unless the optional NO-DISPLAY is non-nil."
(defun inferior-erlang-compile-outdir ()
"Return the directory to compile the current buffer into."
(let* ((buffer-dir (directory-file-name
- (file-name-directory (erlang-local-buffer-file-name))))
- (parent-dir (directory-file-name
- (file-name-directory buffer-dir)))
+ (file-name-directory (erlang-local-buffer-file-name))))
+ (parent-dir (directory-file-name
+ (file-name-directory buffer-dir)))
(ebin-dir (concat (file-name-as-directory parent-dir) "ebin"))
- (buffer-dir-base-name (file-name-nondirectory
- (expand-file-name
- (concat (file-name-as-directory buffer-dir)
- ".")))))
+ (buffer-dir-base-name (file-name-nondirectory
+ (expand-file-name
+ (concat (file-name-as-directory buffer-dir)
+ ".")))))
(if (and (string= buffer-dir-base-name "src")
- (file-directory-p ebin-dir))
- (file-name-as-directory ebin-dir)
+ (file-directory-p ebin-dir))
+ (file-name-as-directory ebin-dir)
(file-name-as-directory buffer-dir))))
(defun inferior-erlang-compute-compile-command (module-name opts)
(let ((ccfn erlang-compile-command-function-alist)
- (res (inferior-erlang-compute-erl-compile-command module-name opts))
- ccfn-entry
- done
+ (res (inferior-erlang-compute-erl-compile-command module-name opts))
+ ccfn-entry
+ done
result)
(if (not (null (erlang-local-buffer-file-name)))
- (while (and (not done) (not (null ccfn)))
- (setq ccfn-entry (car ccfn))
- (setq ccfn (cdr ccfn))
- (if (string-match (car ccfn-entry) (erlang-local-buffer-file-name))
- (let ((c-fn (cdr ccfn-entry)))
- (setq done t)
- (if (not (null c-fn))
- (setq result (funcall c-fn module-name opts)))))))
+ (while (and (not done) (not (null ccfn)))
+ (setq ccfn-entry (car ccfn))
+ (setq ccfn (cdr ccfn))
+ (if (string-match (car ccfn-entry) (erlang-local-buffer-file-name))
+ (let ((c-fn (cdr ccfn-entry)))
+ (setq done t)
+ (if (not (null c-fn))
+ (setq result (funcall c-fn module-name opts)))))))
result))
(defun inferior-erlang-compute-erl-compile-command (module-name opts)
(let* ((out-dir-opt (assoc 'outdir opts))
- (out-dir (cdr out-dir-opt)))
+ (out-dir (cdr out-dir-opt)))
(if erlang-compile-use-outdir
- (format "%s(\"%s\"%s)."
- erlang-compile-erlang-function
- module-name
- (inferior-erlang-format-comma-opts opts))
+ (format "%s(\"%s\"%s)."
+ erlang-compile-erlang-function
+ module-name
+ (inferior-erlang-format-comma-opts opts))
(let (;; Hopefully, noone else will ever use these...
- (tmpvar "Tmp7236")
- (tmpvar2 "Tmp8742"))
- (format
- (concat
- "f(%s), {ok, %s} = file:get_cwd(), "
- "file:set_cwd(\"%s\"), "
- "%s = %s(\"%s\"%s), file:set_cwd(%s), f(%s), %s.")
- tmpvar2 tmpvar
- out-dir
- tmpvar2
- erlang-compile-erlang-function
- module-name (inferior-erlang-format-comma-opts
- (remq out-dir-opt opts))
- tmpvar tmpvar tmpvar2)))))
+ (tmpvar "Tmp7236")
+ (tmpvar2 "Tmp8742"))
+ (format
+ (concat
+ "f(%s), {ok, %s} = file:get_cwd(), "
+ "file:set_cwd(\"%s\"), "
+ "%s = %s(\"%s\"%s), file:set_cwd(%s), f(%s), %s.")
+ tmpvar2 tmpvar
+ out-dir
+ tmpvar2
+ erlang-compile-erlang-function
+ module-name (inferior-erlang-format-comma-opts
+ (remq out-dir-opt opts))
+ tmpvar tmpvar tmpvar2)))))
(defun inferior-erlang-compute-leex-compile-command (module-name opts)
(let ((file-name (erlang-local-buffer-file-name))
- (erl-compile-expr (inferior-erlang-remove-any-trailing-dot
- (inferior-erlang-compute-erl-compile-command
- module-name opts))))
+ (erl-compile-expr (inferior-erlang-remove-any-trailing-dot
+ (inferior-erlang-compute-erl-compile-command
+ module-name opts))))
(format (concat "f(LErr1__), f(LErr2__), "
- "case case leex:file(\"%s\", [%s]) of"
- " ok -> ok;"
- " {ok,_} -> ok;"
- " {ok,_,_} -> ok;"
- " LErr1__ -> LErr1__ "
- "end of"
- " ok -> %s;"
- " LErr2__ -> LErr2__ "
- "end.")
- file-name
- (inferior-erlang-format-comma-opts erlang-leex-compile-opts)
- erl-compile-expr)))
+ "case case leex:file(\"%s\", [%s]) of"
+ " ok -> ok;"
+ " {ok,_} -> ok;"
+ " {ok,_,_} -> ok;"
+ " LErr1__ -> LErr1__ "
+ "end of"
+ " ok -> %s;"
+ " LErr2__ -> LErr2__ "
+ "end.")
+ file-name
+ (inferior-erlang-format-comma-opts erlang-leex-compile-opts)
+ erl-compile-expr)))
(defun inferior-erlang-compute-yecc-compile-command (module-name opts)
(let ((file-name (erlang-local-buffer-file-name))
- (erl-compile-expr (inferior-erlang-remove-any-trailing-dot
- (inferior-erlang-compute-erl-compile-command
- module-name opts))))
+ (erl-compile-expr (inferior-erlang-remove-any-trailing-dot
+ (inferior-erlang-compute-erl-compile-command
+ module-name opts))))
(format (concat "f(YErr1__), f(YErr2__), "
- "case case yecc:file(\"%s\", [%s]) of"
- " {ok,_} -> ok;"
- " {ok,_,_} -> ok;"
- " YErr1__ -> YErr1__ "
- "end of"
- " ok -> %s;"
- " YErr2__ -> YErr2__ "
- "end.")
- file-name
- (inferior-erlang-format-comma-opts erlang-yecc-compile-opts)
- erl-compile-expr)))
+ "case case yecc:file(\"%s\", [%s]) of"
+ " {ok,_} -> ok;"
+ " {ok,_,_} -> ok;"
+ " YErr1__ -> YErr1__ "
+ "end of"
+ " ok -> %s;"
+ " YErr2__ -> YErr2__ "
+ "end.")
+ file-name
+ (inferior-erlang-format-comma-opts erlang-yecc-compile-opts)
+ erl-compile-expr)))
(defun inferior-erlang-remove-any-trailing-dot (str)
(if (string= (substring str -1) ".")
@@ -5730,31 +5731,29 @@ unless the optional NO-DISPLAY is non-nil."
(defun inferior-erlang-format-comma-opts (opts)
(if (null opts)
""
- (concat ", " (inferior-erlang-format-opts opts))))
-
-(defun inferior-erlang-format-opts (opts)
- (concat "[" (inferior-erlang-string-join (mapcar 'inferior-erlang-format-opt
- opts)
- ", ")
- "]"))
+ (concat ", " (inferior-erlang-format-opt opts))))
(defun inferior-erlang-format-opt (opt)
(cond ((stringp opt) (concat "\"" opt "\""))
- ((atom opt) (format "%s" opt))
- ((consp opt) (concat "{" (inferior-erlang-string-join
- (mapcar 'inferior-erlang-format-opt
- (list (car opt) (cdr opt)))
- ", ")
- "}"))
- (t (error (format "Unexpected opt %s" opt)))))
-
-(defun inferior-erlang-string-join (strs sep)
- (let ((result (or (car strs) "")))
- (setq strs (cdr strs))
- (while strs
- (setq result (concat result sep (car strs)))
- (setq strs (cdr strs)))
- result))
+ ((vectorp opt) (inferior-erlang-tuple (append opt nil)))
+ ((atom opt) (format "%s" opt))
+ ((consp opt) (if (listp (cdr opt))
+ (inferior-erlang-list opt)
+ (inferior-erlang-tuple (list (car opt) (cdr opt)))))
+ (t (error "Unexpected erlang compile option %s" opt))))
+
+(defun inferior-erlang-tuple (opts)
+ (concat "{" (mapconcat 'inferior-erlang-format-opt
+ opts
+ ", ")
+ "}"))
+
+(defun inferior-erlang-list (opts)
+ (concat "[" (mapconcat 'inferior-erlang-format-opt
+ opts
+ ", ")
+ "]"))
+
(defun erlang-local-buffer-file-name ()
;; When editing a file remotely via tramp,
@@ -5768,11 +5767,11 @@ unless the optional NO-DISPLAY is non-nil."
;; the file name "/some/path/x.erl" without the
;; tramp-prefix "/ssh:host.example.com:".
(cond ((null (buffer-file-name))
- nil)
- ((erlang-tramp-remote-file-p)
- (erlang-tramp-get-localname))
- (t
- (buffer-file-name))))
+ nil)
+ ((erlang-tramp-remote-file-p)
+ (erlang-tramp-get-localname))
+ (t
+ (buffer-file-name))))
(defun erlang-tramp-remote-file-p ()
(and (fboundp 'tramp-tramp-file-p)
@@ -5798,22 +5797,22 @@ unless the optional NO-DISPLAY is non-nil."
Capable of finding error messages in an inferior Erlang buffer."
(interactive "P")
(let ((done nil)
- (buf (or (and (boundp 'next-error-last-buffer)
- next-error-last-buffer)
- (and (boundp 'compilation-last-buffer)
- compilation-last-buffer))))
+ (buf (or (and (boundp 'next-error-last-buffer)
+ next-error-last-buffer)
+ (and (boundp 'compilation-last-buffer)
+ compilation-last-buffer))))
(if (and (bufferp buf)
- (with-current-buffer buf
- (and (eq major-mode 'erlang-shell-mode)
- (setq major-mode 'compilation-mode))))
- (unwind-protect
- (progn
- (setq done t)
- (next-error argp))
- (with-current-buffer buf
- (setq major-mode 'erlang-shell-mode))))
+ (with-current-buffer buf
+ (and (eq major-mode 'erlang-shell-mode)
+ (setq major-mode 'compilation-mode))))
+ (unwind-protect
+ (progn
+ (setq done t)
+ (next-error argp))
+ (with-current-buffer buf
+ (setq major-mode 'erlang-shell-mode))))
(or done
- (next-error argp))))
+ (next-error argp))))
(defun inferior-erlang-change-directory (&optional dir)
@@ -5850,44 +5849,44 @@ sum([], Sum) -> Sum."
(interactive "r")
(save-excursion
(let (;; regexp for matching arrows. without a prefix argument,
- ;; the regexp matches function heads. With a prefix, it
- ;; matches any arrow.
- (re (if current-prefix-arg
- "^.*\\(\\)->"
- (eval-when-compile
- (concat "^" erlang-atom-regexp ".*\\(\\)->"))))
- ;; part of regexp matching directly before the arrow
- (arrow-match-pos (if current-prefix-arg
- 1
- (1+ erlang-atom-regexp-matches)))
- ;; accumulator for positions where arrows are found, ordered
- ;; by buffer position (from greatest to smallest)
- (arrow-positions '())
- ;; accumulator for longest distance from start of line to arrow
- (most-indent 0)
- ;; marker to track the end of the region we're aligning
- (end-marker (progn (goto-char end)
- (point-marker))))
+ ;; the regexp matches function heads. With a prefix, it
+ ;; matches any arrow.
+ (re (if current-prefix-arg
+ "^.*\\(\\)->"
+ (eval-when-compile
+ (concat "^" erlang-atom-regexp ".*\\(\\)->"))))
+ ;; part of regexp matching directly before the arrow
+ (arrow-match-pos (if current-prefix-arg
+ 1
+ (1+ erlang-atom-regexp-matches)))
+ ;; accumulator for positions where arrows are found, ordered
+ ;; by buffer position (from greatest to smallest)
+ (arrow-positions '())
+ ;; accumulator for longest distance from start of line to arrow
+ (most-indent 0)
+ ;; marker to track the end of the region we're aligning
+ (end-marker (progn (goto-char end)
+ (point-marker))))
;; Pass 1: Find the arrow positions, adjust the whitespace
;; before each arrow to one space, and find the greatest
;; indentation level.
(goto-char start)
(while (re-search-forward re end-marker t)
- (goto-char (match-beginning arrow-match-pos))
- (just-one-space) ; adjust whitespace
- (setq arrow-positions (cons (point) arrow-positions))
- (setq most-indent (max most-indent (erlang-column-number))))
- (set-marker end-marker nil) ; free the marker
+ (goto-char (match-beginning arrow-match-pos))
+ (just-one-space) ; adjust whitespace
+ (setq arrow-positions (cons (point) arrow-positions))
+ (setq most-indent (max most-indent (erlang-column-number))))
+ (set-marker end-marker nil) ; free the marker
;; Pass 2: Insert extra padding so that all arrow indentation is
;; equal. This is done last-to-first by buffer position, so that
;; inserting spaces before one arrow doesn't change the
;; positions of the next ones.
(mapc (lambda (arrow-pos)
- (goto-char arrow-pos)
- (let* ((pad (- most-indent (erlang-column-number))))
- (when (> pad 0)
- (insert-char ?\ pad))))
- arrow-positions))))
+ (goto-char arrow-pos)
+ (let* ((pad (- most-indent (erlang-column-number))))
+ (when (> pad 0)
+ (insert-char ?\ pad))))
+ arrow-positions))))
(defun erlang-column-number ()
"Return the column number of the current position in the buffer.
@@ -5899,7 +5898,7 @@ Tab characters are counted by their visual width."
(save-excursion
(erlang-beginning-of-function)
(if (looking-at "[a-z0-9_]+")
- (match-string 0))))
+ (match-string 0))))
;; Aliases for backward compatibility with older versions of Erlang Mode.
;;
@@ -5918,7 +5917,7 @@ it assumes that NEWDEF is loaded."
(erlang-obsolete 'calculate-erlang-indent 'erlang-calculate-indent)
(erlang-obsolete 'calculate-erlang-stack-indent
- 'erlang-calculate-stack-indent)
+ 'erlang-calculate-stack-indent)
(erlang-obsolete 'at-erlang-keyword 'erlang-at-keyword)
(erlang-obsolete 'at-erlang-operator 'erlang-at-operator)
(erlang-obsolete 'beginning-of-erlang-clause 'erlang-beginning-of-clause)
@@ -5933,12 +5932,12 @@ it assumes that NEWDEF is loaded."
(defconst erlang-unload-hook
(list (lambda ()
- (when (featurep 'advice)
- (ad-unadvise 'Man-notify-when-ready)
- (ad-unadvise 'set-visited-file-name)))))
+ (when (featurep 'advice)
+ (ad-unadvise 'Man-notify-when-ready)
+ (ad-unadvise 'set-visited-file-name)))))
-(defun erlang-string-to-int (string)
+(defun erlang-string-to-int (string)
(if (fboundp 'string-to-number)
(string-to-number string)
(funcall (symbol-function 'string-to-int) string)))
@@ -5951,6 +5950,7 @@ it assumes that NEWDEF is loaded."
;; Local variables:
;; coding: iso-8859-1
+;; indent-tabs-mode: nil
;; End:
;;; erlang.el ends here
diff --git a/lib/tools/emacs/erldoc.el b/lib/tools/emacs/erldoc.el
new file mode 100644
index 0000000000..cb355374d9
--- /dev/null
+++ b/lib/tools/emacs/erldoc.el
@@ -0,0 +1,508 @@
+;;; erldoc.el --- browse Erlang/OTP documentation -*- lexical-binding: t; -*-
+
+;; %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%
+
+;;; Commentary:
+
+;; Crawl Erlang/OTP HTML documentation and generate lookup tables.
+;;
+;; This package depends on `cl-lib', `pcase' and
+;; `libxml-parse-html-region'; emacs 24+ compiled with libxml2 should
+;; work. On emacs 24.1 and 24.2 do `M-x package-install RET cl-lib
+;; RET' to install `cl-lib'.
+;;
+;; Please customise `erldoc-man-index' to point to your local OTP
+;; documentation.
+;;
+;; To use:
+;;
+;; (define-key help-map "u" 'erldoc-browse)
+;; (define-key help-map "t" 'erldoc-browse-topic)
+;; (define-key help-map "a" 'erldoc-apropos)
+;;
+;; Note: these commands trigger indexing OTP documentation on first
+;; run with cache to disk which may take 1-2 minutes.
+
+
+;;; Examples:
+
+;; 1. `M-x erldoc-browse RET erlang:integer_to_binary/2 RET' opens the
+;; `erlang' manual anchored on the entry for `integer_to_binary/2'.
+;;
+;; 2. `M-x erldoc-apropos RET first RET' list all MFAs matching
+;; substring `first'.
+;;
+;; 3. `M-x erldoc-browse-topic RET efficiency_guide#Introduction RET'
+;; opens chapter `Introduction' of the `Efficiency Guide' in the
+;; browser.
+
+;;; History:
+
+;; Written in December 2013 as a temporary solution to help me browse
+;; the rich Erlang/OTP documentation. Three years on I find myself
+;; still using it every day. - Leo (2016)
+
+;;; Code:
+
+(eval-when-compile (require 'url-parse))
+(require 'cl-lib)
+(require 'erlang)
+
+(eval-and-compile ;for emacs < 24.3
+ (or (fboundp 'user-error) (defalias 'user-error 'error)))
+
+(defgroup erldoc nil
+ "Browse Erlang document."
+ :group 'help)
+
+(defcustom erldoc-man-index "http://www.erlang.org/doc/man_index.html"
+ "The URL to the man_index.html page.
+Note it is advisable to customise this to a local URL for example
+`file:///usr/local/19.1/lib/erlang/doc/man_index.html' to speed
+up the indexing."
+ :type 'string
+ :group 'erldoc)
+
+(defcustom erldoc-verify-man-path nil
+ "If non-nil verify man path existence for `file://'."
+ :type 'boolean
+ :group 'erldoc)
+
+(defcustom erldoc-output-file (locate-user-emacs-file "cache/erldoc")
+ "File to store the parsed results."
+ :type 'file
+ :group 'erldoc)
+
+(defun erldoc-strip-string (s)
+ (let* ((re "[ \t\n\r\f\v\u00a0]+")
+ (from (if (string-match (concat "\\`" re) s) (match-end 0) 0))
+ (to (and (string-match (concat re "\\'") s) (match-beginning 0))))
+ (substring s from (and to (max to from)))))
+
+;; Note: don't know how to get the BASE-URL to
+;; `libxml-parse-html-region' to work.
+(defun erldoc-expand-url (url base-url)
+ (if (url-type (url-generic-parse-url url))
+ url
+ (let* ((base (url-generic-parse-url base-url))
+ (dir (directory-file-name (file-name-directory (url-filename base)))))
+ (setf (url-filename base) (expand-file-name url dir))
+ (url-recreate-url base))))
+
+(defun erldoc-parse-html (url)
+ (with-temp-buffer
+ (url-insert-file-contents url)
+ (libxml-parse-html-region (point-min) (point-max))))
+
+(defalias 'erldoc-dom-text-node-p #'stringp)
+
+(defun erldoc-dom-attributes (dom)
+ (and (not (erldoc-dom-text-node-p dom)) (cadr dom)))
+
+(defun erldoc-dom-get-attribute (dom attrib-name)
+ (cdr (assq attrib-name (erldoc-dom-attributes dom))))
+
+(defun erldoc-dom-children (dom)
+ (and (not (erldoc-dom-text-node-p dom)) (cddr dom)))
+
+(defun erldoc-dom-get-text (dom)
+ (let ((text (car (last (erldoc-dom-children dom)))))
+ (and (erldoc-dom-text-node-p text) text)))
+
+(defvar erldoc-dom-walk-parent nil)
+(defvar erldoc-dom-walk-siblings nil)
+
+(defun erldoc-dom-walk (dom k)
+ (funcall k dom)
+ (let ((erldoc-dom-walk-parent dom)
+ (erldoc-dom-walk-siblings (unless (erldoc-dom-text-node-p dom)
+ (cddr dom))))
+ (dolist (child erldoc-dom-walk-siblings)
+ (erldoc-dom-walk child k))))
+
+(defun erldoc-dom-get-element (dom element-name)
+ (catch 'return
+ (erldoc-dom-walk dom (lambda (d)
+ (when (eq (car-safe d) element-name)
+ (throw 'return d))))))
+
+(defun erldoc-dom-get-element-by-id (dom id)
+ (catch 'return
+ (erldoc-dom-walk dom (lambda (d)
+ (when (equal (erldoc-dom-get-attribute d 'id) id)
+ (throw 'return d))))))
+
+(defun erldoc-dom-get-elements-by-id (dom id)
+ (let (result)
+ (erldoc-dom-walk dom (lambda (d)
+ (when (equal (erldoc-dom-get-attribute d 'id) id)
+ (push d result))))
+ (nreverse result)))
+
+(defun erldoc-fix-path (url)
+ (if (and erldoc-verify-man-path
+ ;; Could only verify local files
+ (equal (url-type (url-generic-parse-url url)) "file"))
+ (let* ((obj (url-generic-parse-url url))
+ (new (car (file-expand-wildcards
+ (replace-regexp-in-string
+ "-[0-9]+\\(?:[.][0-9]+\\)*" "*"
+ (url-filename obj))))))
+ (or new (error "File %s does not exist" (url-filename obj)))
+ (setf (url-filename obj) new)
+ (url-recreate-url obj))
+ url))
+
+(defun erldoc-parse-man-index (url)
+ (let ((table (erldoc-dom-get-element (erldoc-parse-html url) 'table))
+ (mans))
+ (erldoc-dom-walk
+ table
+ (lambda (d)
+ (when (eq (car-safe d) 'a)
+ (let ((href (erldoc-dom-get-attribute d 'href)))
+ (when (and href (not (string-match-p "index\\.html\\'" href)))
+ (with-demoted-errors "erldoc-parse-man-index: %S"
+ (push (cons (erldoc-dom-get-text d)
+ (erldoc-fix-path (erldoc-expand-url href url)))
+ mans)))))))
+ (nreverse mans)))
+
+(defun erldoc-parse-man (man)
+ (let ((dom (erldoc-parse-html (cdr man)))
+ (table (make-hash-table :test #'equal)))
+ (erldoc-dom-walk
+ (erldoc-dom-get-element-by-id dom "loadscrollpos")
+ (lambda (d)
+ (let ((href (erldoc-dom-get-attribute d 'href)))
+ (when (and href (string-match "#" href))
+ (puthash (substring href (match-end 0))
+ (list (concat (car man) ":" (erldoc-strip-string
+ (erldoc-dom-get-text d)))
+ (erldoc-expand-url href (cdr man)))
+ table)))))
+ (let ((span-content
+ (lambda (span)
+ (let ((texts))
+ (erldoc-dom-walk span
+ (lambda (d)
+ (and (erldoc-dom-text-node-p d)
+ (push (erldoc-strip-string d) texts))))
+ (and texts (mapconcat 'identity (nreverse texts) " ")))))
+ entries)
+ (erldoc-dom-walk
+ dom
+ (lambda (d)
+ ;; Get the full function signature.
+ (when (and (eq (car-safe d) 'a)
+ (gethash (erldoc-dom-get-attribute d 'name) table))
+ (push (append (gethash (erldoc-dom-get-attribute d 'name) table)
+ (list (funcall span-content
+ (or (erldoc-dom-get-element d 'span)
+ (cadr (memq d erldoc-dom-walk-siblings))))))
+ entries))
+ ;; Get data types
+ (when (and (eq (car-safe d) 'a)
+ (string-prefix-p "type-"
+ (or (erldoc-dom-get-attribute d 'name) "")))
+ (push (list (concat (car man) ":" (funcall span-content d))
+ (concat (cdr man) "#" (erldoc-dom-get-attribute d 'name))
+ (funcall span-content erldoc-dom-walk-parent))
+ entries))))
+ entries)))
+
+(defun erldoc-parse-all (man-index output &optional json)
+ (let* ((output (expand-file-name output))
+ (table (make-hash-table :size 11503 :test #'equal))
+ (mans (erldoc-parse-man-index man-index))
+ (progress 1)
+ (reporter (make-progress-reporter "Parsing Erlang/OTP documentation"
+ progress (length mans)))
+ fails all)
+ (dolist (man mans)
+ (condition-case err
+ (push (erldoc-parse-man man) all)
+ (error (push (error-message-string err) fails)))
+ (accept-process-output nil 0.01)
+ (progress-reporter-update reporter (cl-incf progress)))
+ (when fails
+ (display-warning 'erldoc-parse-all
+ (format "\n\n%s" (mapconcat #'identity fails "\n"))
+ :error))
+ (progress-reporter-done reporter)
+ (mapc (lambda (x) (puthash (car x) (cdr x) table))
+ (apply #'nconc (nreverse all)))
+ (with-temp-buffer
+ (if (not json)
+ (pp table (current-buffer))
+ (eval-and-compile (require 'json))
+ (let ((json-encoding-pretty-print t))
+ (insert (json-encode table))))
+ (unless (file-directory-p (file-name-directory output))
+ (make-directory (file-name-directory output) t))
+ (write-region nil nil output nil nil nil 'ask))))
+
+(defun erldoc-otp-release ()
+ "Get the otp release version (as string) or nil if not found."
+ (let ((otp (erldoc-dom-get-text
+ (erldoc-dom-get-element
+ (erldoc-parse-html
+ (erldoc-expand-url "index.html" erldoc-man-index))
+ 'title))))
+ (and (string-match "[0-9.]+\\'" otp) (match-string 0 otp))))
+
+(defvar erldoc-browse-history nil)
+(defvar erldoc-lookup-table nil)
+
+(defun erldoc-lookup-table ()
+ (or erldoc-lookup-table
+ (progn
+ (unless (file-exists-p erldoc-output-file)
+ (let ((of (pcase (erldoc-otp-release)
+ (`nil erldoc-output-file)
+ (ver (concat erldoc-output-file "-" ver)))))
+ (unless (file-exists-p of)
+ (erldoc-parse-all erldoc-man-index of))
+ (unless (string= erldoc-output-file of)
+ (make-symbolic-link of erldoc-output-file))))
+ (setq erldoc-lookup-table
+ (with-temp-buffer
+ (insert-file-contents erldoc-output-file)
+ (read (current-buffer)))))))
+
+(defun erldoc-best-matches (mfa)
+ (pcase mfa
+ ((and `(,m ,f) (let a (erlang-get-function-arity)))
+ (let ((mfa (format "%s:%s/%s" m f a)))
+ (cond ((gethash mfa (erldoc-lookup-table)) (list mfa))
+ (m (all-completions (concat m ":" f "/") (erldoc-lookup-table)))
+ (t (let* ((mod (erlang-get-module))
+ (mf1 (and mod (concat mod ":" f "/")))
+ (mf2 (concat "erlang:" f "/"))
+ (re (concat ":" (regexp-quote f) "/")))
+ (or (and mf1 (all-completions mf1 (erldoc-lookup-table)))
+ (all-completions mf2 (erldoc-lookup-table))
+ (cl-loop for k being the hash-keys of (erldoc-lookup-table)
+ when (string-match-p re k)
+ collect k)))))))))
+
+;;;###autoload
+(defun erldoc-browse (mfa)
+ (interactive
+ (let ((default
+ ;; `erlang-mode-syntax-table' is lazily initialised.
+ (with-syntax-table (or erlang-mode-syntax-table (standard-syntax-table))
+ (ignore-errors
+ (erldoc-best-matches
+ (or (erlang-get-function-under-point)
+ (save-excursion
+ (goto-char (or (cadr (syntax-ppss)) (point)))
+ (erlang-get-function-under-point))))))))
+ (list (completing-read (format (if default "Function {%d %s} (default %s): "
+ "Function: ")
+ (length default)
+ (if (= (length default) 1) "guess" "guesses")
+ (car default))
+ (erldoc-lookup-table)
+ nil t nil 'erldoc-browse-history default))))
+ (or (stringp mfa)
+ (signal 'wrong-type-argument (list 'string mfa 'mfa)))
+ (browse-url (or (car (gethash mfa (erldoc-lookup-table)))
+ (user-error "No documentation for %s" mfa))))
+
+;;;###autoload
+(defun erldoc-apropos (pattern)
+ (interactive "sPattern: ")
+ (with-help-window (help-buffer)
+ (with-current-buffer standard-output
+ (princ (concat "Erldoc apropos pattern: " pattern "\n\n"))
+ (maphash (lambda (k v)
+ (when (string-match-p pattern k)
+ (insert-text-button k :type 'help-url
+ 'help-args (list (car v)))
+ (insert "\n")))
+ (erldoc-lookup-table)))))
+
+(defun erldoc-tokenize-signature (sig)
+ ;; Divide SIG into (MF ARGLIST RETTYPE)
+ (let ((from (if (string-match "\\`.+?(" sig)
+ (1- (match-end 0))
+ 0))
+ (to (and (string-match "\\s-*->\\s-*.*?\\'" sig) (match-beginning 0))))
+ (list (erldoc-strip-string (substring sig 0 from))
+ (erldoc-strip-string (substring sig from (and to (max from to))))
+ (and to (erldoc-strip-string (substring sig to))))))
+
+(defun erldoc-format-signature (mod fn)
+ (when (and mod fn (or erldoc-lookup-table
+ (file-exists-p erldoc-output-file)))
+ (let ((re (concat "\\`" mod ":" fn "/\\([0-9]+\\)\\'"))
+ (sigs))
+ (maphash (lambda (k v)
+ (when (string-match re k)
+ (push (cons (string-to-number (match-string 1 k))
+ (cdr (erldoc-tokenize-signature (cadr v))))
+ sigs)))
+ (erldoc-lookup-table))
+ (when sigs
+ ;; Mostly single return type but there are exceptions such as
+ ;; `beam_lib:chunks/2,3'.
+ (let ((single-rettype
+ (cl-reduce (lambda (x1 x2) (and x1 x2 (equal x1 x2) x1))
+ sigs :key #'cl-caddr))
+ (sigs (sort sigs #'car-less-than-car)))
+ (if single-rettype
+ (concat mod ":" fn (mapconcat #'cadr sigs " | ") " " single-rettype)
+ (mapconcat (lambda (x) (concat mod ":" fn (nth 1 x) " " (nth 2 x)))
+ sigs "\n")))))))
+
+;;;###autoload
+(defun erldoc-eldoc-function ()
+ "A function suitable for `eldoc-documentation-function'."
+ (save-excursion
+ (pcase (erlang-get-function-under-point)
+ (`(,_ nil) )
+ (`(nil ,fn) (erldoc-format-signature "erlang" fn))
+ (`(,mod ,fn) (erldoc-format-signature mod fn)))))
+
+(defun erldoc-parse-eeps-index ()
+ (let* ((url "http://www.erlang.org/eeps/")
+ (table (catch 'return
+ (erldoc-dom-walk (erldoc-parse-html url)
+ (lambda (d)
+ (and (eq (car-safe d) 'table)
+ (equal (erldoc-dom-get-attribute d 'summary)
+ "Numerical Index of EEPs")
+ (throw 'return d))))))
+ (fix-title (lambda (title)
+ (replace-regexp-in-string
+ "`` *" "" (replace-regexp-in-string " *``, *" " by " title))))
+ (result))
+ (erldoc-dom-walk
+ table (lambda (d)
+ (when (eq (car-safe d) 'a)
+ (push (cons (funcall fix-title (erldoc-dom-get-attribute d 'title))
+ (erldoc-expand-url
+ (erldoc-dom-get-attribute d 'href)
+ url))
+ result))))
+ (nreverse result)))
+
+(defvar erldoc-user-guides nil)
+
+(defvar erldoc-missing-user-guides
+ '("compiler" "hipe" "kernel" "os_mon" "parsetools" "typer")
+ "List of standard Erlang applications with no user guides.")
+
+;; Search in `code:lib_dir/0' using find LIB_DIR -type f -name
+;; '*_app.html'.
+(defvar erldoc-app-manuals '("crypto" "diameter" "erl_docgen"
+ "kernel" "observer" "os_mon"
+ "runtime_tools" "sasl" "snmp"
+ "ssl" "test_server"
+ ("ssh" . "SSH") ("stdlib" . "STDLIB")
+ ("hipe" . "HiPE") ("typer" . "TypEr"))
+ "List of applications that come with a manual.")
+
+(defun erldoc-user-guide-chapters (user-guide)
+ (pcase-let ((`(,name . ,url) user-guide))
+ (unless (member name erldoc-missing-user-guides)
+ (let ((chaps (erldoc-dom-get-elements-by-id
+ (erldoc-dom-get-element-by-id (erldoc-parse-html url) "leftnav")
+ "no")))
+ (or chaps (warn "erldoc-user-guide-chapters no chapters found for `%s'"
+ (cdr user-guide)))
+ (mapcar (lambda (li)
+ (cons (concat name "#" (erldoc-dom-get-attribute li 'title))
+ (erldoc-expand-url (erldoc-dom-get-attribute
+ (erldoc-dom-get-element li 'a) 'href)
+ url)))
+ chaps)))))
+
+(defun erldoc-user-guides-1 ()
+ (let ((url (erldoc-expand-url "applications.html" erldoc-man-index))
+ app-guides app-mans)
+ (erldoc-dom-walk
+ (erldoc-parse-html url)
+ (lambda (d)
+ (when (and (eq (car-safe d) 'a)
+ (not (string-match-p "\\`[0-9.]+\\'" (erldoc-dom-get-text d))))
+ (with-demoted-errors "erldoc-user-guides-1: %S"
+ (let ((name (erldoc-strip-string (erldoc-dom-get-text d)))
+ (index-page (erldoc-fix-path (erldoc-expand-url
+ (erldoc-dom-get-attribute d 'href) url))))
+ (push (cons name (if (member name erldoc-missing-user-guides)
+ index-page
+ (erldoc-expand-url "users_guide.html" index-page)))
+ app-guides)
+ ;; Collect application manuals.
+ (pcase (assoc name (mapcar (lambda (x) (if (consp x) x (cons x x)))
+ erldoc-app-manuals))
+ (`(,_ . ,manual)
+ (push (cons name
+ (erldoc-expand-url (format "%s_app.html" manual)
+ index-page))
+ app-mans))))))))
+ (list (nreverse app-guides)
+ (nreverse app-mans))))
+
+(defun erldoc-user-guides ()
+ (or erldoc-user-guides
+ (let ((file (concat erldoc-output-file "-topics")))
+ (unless (file-exists-p file)
+ (unless (file-directory-p (file-name-directory file))
+ (make-directory (file-name-directory file) t))
+ (with-temp-buffer
+ (pcase-let ((`(,guides ,mans) (erldoc-user-guides-1)))
+ (pp (append (cl-mapcan #'erldoc-user-guide-chapters
+ (append (mapcar
+ (lambda (dir)
+ (cons dir (erldoc-expand-url
+ (concat dir "/users_guide.html")
+ erldoc-man-index)))
+ '("design_principles"
+ "efficiency_guide"
+ "embedded"
+ "getting_started"
+ "installation_guide"
+ "oam"
+ "programming_examples"
+ "reference_manual"
+ "system_architecture_intro"
+ "system_principles"
+ "tutorial"))
+ guides))
+ (mapcar (lambda (man)
+ (pcase-let ((`(,name . ,url) man))
+ (cons (concat name " (App)") url)))
+ mans)
+ (erldoc-parse-eeps-index))
+ (current-buffer)))
+ (write-region nil nil file nil nil nil 'ask)))
+ (setq erldoc-user-guides (with-temp-buffer (insert-file-contents file)
+ (read (current-buffer)))))))
+
+;;;###autoload
+(defun erldoc-browse-topic (topic)
+ (interactive
+ (list (completing-read "User guide: " (erldoc-user-guides) nil t)))
+ (browse-url (cdr (assoc topic (erldoc-user-guides)))))
+
+(provide 'erldoc)
+;;; erldoc.el ends here
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
index 7a1ff6a954..14a4eca7c3 100644
--- a/lib/tools/emacs/test.erl.indented
+++ b/lib/tools/emacs/test.erl.indented
@@ -1,4 +1,4 @@
-%% -*- erlang -*-
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
%%
%% %CopyrightBegin%
%%
@@ -27,7 +27,7 @@
%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
%%%-------------------------------------------------------------------
-%% Start off with syntax highlighting you have to verify this by looking here
+%% Start off with syntax highlighting you have to verify this by looking here
%% and see that the code looks alright
-module(test).
@@ -44,175 +44,175 @@ foo() ->
%% Module attributes should be highlighted
-export([t/1]).
--record(record1, {a,
- b,
- c
- }).
+-record(record1, {a,
+ b,
+ c
+ }).
-record(record2, {
- a,
- b
- }).
+ a,
+ b
+ }).
-record(record3, {a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
+ 8#4234,
+ b = 8#5432
+ bor 2#1010101
+ c = 123 +
+ 234,
d}).
-record(record4, {
- a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
+ a = 8#42423 bor
+ 8#4234,
+ b = 8#5432
+ bor 2#1010101
+ c = 123 +
+ 234,
+ d}).
-record(record5, { a = 1 :: integer()
- , b = foobar :: atom()
- }).
+ , b = foobar :: atom()
+ }).
-define(MACRO_1, macro).
-define(MACRO_2(_), macro).
-spec t(integer()) -> any().
--type ann() :: Var :: integer().
--type ann2() :: Var ::
- 'return'
- | 'return_white_spaces'
- | 'return_comments'
- | 'text' | ann().
--type paren() ::
- (ann2()).
--type t1() :: atom().
--type t2() :: [t1()].
--type t3(Atom) :: integer(Atom).
--type t4() :: t3(foobar).
--type t5() :: {t1(), t3(foo)}.
--type t6() :: 1 | 2 | 3 |
- 'foo' | 'bar'.
--type t7() :: [].
--type t71() :: [_].
+-type ann() :: Var :: integer().
+-type ann2() :: Var ::
+ 'return'
+ | 'return_white_spaces'
+ | 'return_comments'
+ | 'text' | ann().
+-type paren() ::
+ (ann2()).
+-type t1() :: atom().
+-type t2() :: [t1()].
+-type t3(Atom) :: integer(Atom).
+-type t4() :: t3(foobar).
+-type t5() :: {t1(), t3(foo)}.
+-type t6() :: 1 | 2 | 3 |
+ 'foo' | 'bar'.
+-type t7() :: [].
+-type t71() :: [_].
-type t8() :: {any(),none(),pid(),port(),
- reference(),float()}.
--type t9() :: [1|2|3|foo|bar] |
- list(a | b | c) | t71().
--type t10() :: {1|2|3|foo|t9()} | {}.
--type t11() :: 1..2.
--type t13() :: maybe_improper_list(integer(), t11()).
--type t14() :: [erl_scan:foo() |
- %% Should be highlighted
- term() |
- bool() |
- byte() |
- char() |
- non_neg_integer() | nonempty_list() |
- pos_integer() |
- neg_integer() |
- number() |
- list() |
- nonempty_improper_list() | nonempty_maybe_improper_list() |
- maybe_improper_list() | string() | iolist() | byte() |
- module() |
- mfa() |
- node() |
- timeout() |
- no_return() |
- %% Should not be highlighted
- nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+ reference(),float()}.
+-type t9() :: [1|2|3|foo|bar] |
+ list(a | b | c) | t71().
+-type t10() :: {1|2|3|foo|t9()} | {}.
+-type t11() :: 1..2.
+-type t13() :: maybe_improper_list(integer(), t11()).
+-type t14() :: [erl_scan:foo() |
+ %% Should be highlighted
+ term() |
+ bool() |
+ byte() |
+ char() |
+ non_neg_integer() | nonempty_list() |
+ pos_integer() |
+ neg_integer() |
+ number() |
+ list() |
+ nonempty_improper_list() | nonempty_maybe_improper_list() |
+ maybe_improper_list() | string() | iolist() | byte() |
+ module() |
+ mfa() |
+ node() |
+ timeout() |
+ no_return() |
+ %% Should not be highlighted
+ nonempty_() | nonlist() |
+ erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
<<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
- <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
- <<_:34>>|<<_:34>>|<<_:34>>].
--type t16() :: fun().
--type t17() :: fun((...) -> paren()).
--type t18() :: fun(() -> t17() | t16()).
+ <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
+ <<_:34>>|<<_:34>>|<<_:34>>].
+-type t16() :: fun().
+-type t17() :: fun((...) -> paren()).
+-type t18() :: fun(() -> t17() | t16()).
-type t19() :: fun((t18()) -> t16()) |
- fun((nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
- nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()).
--type t20() :: [t19(), ...].
--type t21() :: tuple().
--type t21(A) :: A.
--type t22() :: t21(integer()).
--type t23() :: #rec1{}.
--type t24() :: #rec2{a :: t23(), b :: [atom()]}.
--type t25() :: #rec3{f123 :: [t24() |
- 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())]}.
+ fun((nonempty_maybe_improper_list('integer', any())|
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
+ nonempty_maybe_improper_list('integer', any())|
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer()).
+-type t20() :: [t19(), ...].
+-type t21() :: tuple().
+-type t21(A) :: A.
+-type t22() :: t21(integer()).
+-type t23() :: #rec1{}.
+-type t24() :: #rec2{a :: t23(), b :: [atom()]}.
+-type t25() :: #rec3{f123 :: [t24() |
+ 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())]}.
-type t26() :: #rec4{ a :: integer()
- , b :: any()
- }.
+ , b :: any()
+ }.
-type t27() :: { integer()
- , atom()
- }.
+ , atom()
+ }.
-type t99() ::
- {t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
- t15(),t20(),t21(), t22(),t25()}.
+ {t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
+ t15(),t20(),t21(), t22(),t25()}.
-spec t1(FooBar :: t99()) -> t99();
- (t2()) -> t2();
+ (t2()) -> t2();
(t4()) -> t4() when is_subtype(t4(), t24);
- (t23()) -> t23() when is_subtype(t23(), atom()),
- is_subtype(t23(), t14());
- (t24()) -> t24() when is_subtype(t24(), atom()),
- is_subtype(t24(), t14()),
- is_subtype(t24(), t4()).
+ (t23()) -> t23() when is_subtype(t23(), atom()),
+ is_subtype(t23(), t14());
+ (t24()) -> t24() when is_subtype(t24(), atom()),
+ is_subtype(t24(), t14()),
+ is_subtype(t24(), t4()).
-spec over(I :: integer()) -> R1 :: foo:typen();
- (A :: atom()) -> R2 :: foo:atomen();
- (T :: tuple()) -> R3 :: bar:typen().
+ (A :: atom()) -> R2 :: foo:atomen();
+ (T :: tuple()) -> R3 :: bar:typen().
--spec mod:t2() -> any().
+-spec mod:t2() -> any().
--spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
+-spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
+ | {'del_member', name(), pid()},
+ #state{}) -> {'noreply', #state{}}.
--spec handle_cast(Cast ::
- {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
+-spec handle_cast(Cast ::
+ {'exchange', node(), [[name(),...]]}
+ | {'del_member', name(), pid()},
+ #state{}) -> {'noreply', #state{}}.
-spec all(fun((T) -> boolean()), List :: [T]) ->
- boolean() when is_subtype(T, term()). % (*)
+ boolean() when is_subtype(T, term()). % (*)
--spec get_closest_pid(term()) ->
- Return :: pid()
- | {'error', {'no_process', term()}
- | {'no_such_group', term()}}.
+-spec get_closest_pid(term()) ->
+ Return :: pid()
+ | {'error', {'no_process', term()}
+ | {'no_such_group', term()}}.
-spec add( X :: integer()
- , Y :: integer()
- ) -> integer().
+ , Y :: integer()
+ ) -> integer().
--opaque attributes_data() ::
- [{'column', column()} | {'line', info_line()} |
- {'text', string()}] | {line(),column()}.
+-opaque attributes_data() ::
+ [{'column', column()} | {'line', info_line()} |
+ {'text', string()}] | {line(),column()}.
-record(r,{
f1 :: attributes_data(),
- f222 = foo:bar(34, #rec3{}, 234234234423,
- aassdsfsdfsdf, 2234242323) ::
- [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
- f333 :: [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
- f3 = x:y(),
- f4 = x:z() :: t99(),
- f17 :: 'undefined',
- f18 :: 1 | 2 | 'undefined',
- f19 = 3 :: integer()|undefined,
- f5 = 3 :: undefined|integer()}).
+ f222 = foo:bar(34, #rec3{}, 234234234423,
+ aassdsfsdfsdf, 2234242323) ::
+ [t24() | 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())],
+ f333 :: [t24() | 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())],
+ f3 = x:y(),
+ f4 = x:z() :: t99(),
+ f17 :: 'undefined',
+ f18 :: 1 | 2 | 'undefined',
+ f19 = 3 :: integer()|undefined,
+ f5 = 3 :: undefined|integer()}).
-record(state, {
- sequence_number = 1 :: integer()
- }).
+ sequence_number = 1 :: integer()
+ }).
highlighting(X) % Function definitions should be highlighted
@@ -230,12 +230,12 @@ highlighting(X) % Function definitions should be highlighted
'#1',atom,
$", atom, % atom should be ok
- $', atom,
+ $', atom,
"string$", atom, "string$", atom, % currently buggy I know...
"string\$", atom, % workaround for bug above
- "char $in string", atom,
+ "char $in string", atom,
'atom$', atom, 'atom$', atom,
'atom\$', atom,
@@ -270,15 +270,15 @@ highlighting(X) % Function definitions should be highlighted
erlang:anything(lists),
%% Guards
is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
- is_function(Fun), is_pid(self()),
+ is_function(Fun), is_pid(self()),
not_a_guard:is_list([]),
%% Other Types
atom, % not (currently) hightlighted
- 234234,
+ 234234,
234.43,
- [list, are, not, higlighted],
+ [list, are, not, higlighted],
{nor, is, tuple},
ok.
@@ -290,35 +290,35 @@ highlighting(X) % Function definitions should be highlighted
%% Indented
- % Right
+ % Right
-indent_basics(X, Y, Z)
+indent_basics(X, Y, Z)
when X > 42,
Z < 13;
Y =:= 4711 ->
%% comments
- % right comments
- case lists:filter(fun(_, AlongName,
- B,
- C) ->
- true
- end,
- [a,v,b])
+ % right comments
+ case lists:filter(fun(_, AlongName,
+ B,
+ C) ->
+ true
+ end,
+ [a,v,b])
of
- [] ->
- Y = 5 * 43,
- ok;
- [_|_] ->
- Y = 5 * 43,
- ok
+ [] ->
+ Y = 5 * 43,
+ ok;
+ [_|_] ->
+ Y = 5 * 43,
+ ok
end,
Y,
%% List, tuples and binaries
- [a,
+ [a,
b, c
],
- [ a,
+ [ a,
b, c
],
@@ -326,10 +326,10 @@ indent_basics(X, Y, Z)
a,
b
],
- {a,
+ {a,
b,c
},
- { a,
+ { a,
b,c
},
@@ -366,16 +366,16 @@ indent_basics(X, Y, Z)
c
),
- call(2#42423 bor
- #4234,
- 2#5432,
- other_arg),
+ call(2#42423 bor
+ #4234,
+ 2#5432,
+ other_arg),
ok;
-indent_basics(Xlongname,
- #struct{a=Foo,
- b=Bar},
- [X|
- Y]) ->
+indent_basics(Xlongname,
+ #struct{a=Foo,
+ b=Bar},
+ [X|
+ Y]) ->
testing_next_clause,
ok;
indent_basics( % AD added clause
@@ -408,295 +408,295 @@ indent_nested() ->
indent_icr(Z) -> % icr = if case receive
%% If
if Z >= 0 ->
- X = 43 div 4,
- foo(X);
+ X = 43 div 4,
+ foo(X);
Z =< 10 ->
- X = 43 div 4,
- foo(X);
+ X = 43 div 4,
+ foo(X);
Z == 5 orelse
Z == 7 ->
- X = 43 div 4,
- foo(X);
+ X = 43 div 4,
+ foo(X);
true ->
- if_works
+ if_works
end,
%% Case
case {Z, foo, bar} of
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- {Z,_,_} when
- Z =:= 42 -> % AD line should be indented as a when
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when Z < 10 -> % AD when should be indented
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when % AD when should be indented
- Z < 10 % and the guards should follow when
- andalso % unsure about how though
- true ->
- X = 43 div 4,
- foo(X)
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_} when
+ Z =:= 42 -> % AD line should be indented as a when
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_}
+ when Z < 10 -> % AD when should be indented
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_}
+ when % AD when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
end,
%% begin
begin
- sune,
- X = 74234 + foo(8456) +
- 345 div 43,
- ok
+ sune,
+ X = 74234 + foo(8456) +
+ 345 div 43,
+ ok
end,
%% receive
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ Z ->
+ X = 43 div 4,
+ foo(X)
end,
receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z % AD added clause
- when Z =:= 1 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z when % AD added clause
- Z =:= 2 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ Z % AD added clause
+ when Z =:= 1 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z when % AD added clause
+ Z =:= 2 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z ->
+ X = 43 div 4,
+ foo(X)
after infinity ->
- foo(X),
- asd(X),
- 5*43
+ foo(X),
+ asd(X),
+ 5*43
end,
receive
- after 10 ->
- foo(X),
- asd(X),
- 5*43
+ after 10 ->
+ foo(X),
+ asd(X),
+ 5*43
end,
ok.
indent_fun() ->
%% Changed fun to one indention level
- Var = spawn(fun(X)
- when X == 2;
- X > 10 ->
- hello,
- case Hello() of
- true when is_atom(X) ->
- foo;
- false ->
- bar
- end;
- (Foo) when is_atom(Foo),
- is_integer(X) ->
- X = 6* 45,
- Y = true andalso
- kalle
- end),
+ Var = spawn(fun(X)
+ when X == 2;
+ X > 10 ->
+ hello,
+ case Hello() of
+ true when is_atom(X) ->
+ foo;
+ false ->
+ bar
+ end;
+ (Foo) when is_atom(Foo),
+ is_integer(X) ->
+ X = 6* 45,
+ Y = true andalso
+ kalle
+ end),
%% check EEP37 named funs
Fn1 = fun Fact(N) when N > 0 ->
- F = Fact(N-1),
- N * F;
- Fact(0) ->
- 1
- end,
+ F = Fact(N-1),
+ N * F;
+ Fact(0) ->
+ 1
+ end,
%% check anonymous funs too
Fn2 = fun(0) ->
- 1;
- (N) ->
- N
- end,
+ 1;
+ (N) ->
+ N
+ end,
ok.
indent_try_catch() ->
try
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R % AD added clause
- when R =:= 42 -> % when should be indented
- foo(R);
- error:R % AD added clause
- when % when should be indented
- R =:= 42 -> % but unsure about this (maybe 2 more)
- foo(R);
- error:R when % AD added clause
- R =:= foo -> % line should be 2 indented (works)
- foo(R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R % AD added clause
+ when R =:= 42 -> % when should be indented
+ foo(R);
+ error:R % AD added clause
+ when % when should be indented
+ R =:= 42 -> % but unsure about this (maybe 2 more)
+ foo(R);
+ error:R when % AD added clause
+ R =:= foo -> % line should be 2 indented (works)
+ foo(R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
after
- foo('after'),
- file:close(Xfile)
+ foo('after'),
+ file:close(Xfile)
end;
indent_try_catch() ->
try
- foo(bar)
+ foo(bar)
of
- X when true andalso
- kalle ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2);
- X % AD added clause
- when false andalso % when should be 2 indented
- bengt ->
- gurka();
- X when % AD added clause
- false andalso % line should be 2 indented
- not bengt ->
- gurka();
- X ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
+ X when true andalso
+ kalle ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2);
+ X % AD added clause
+ when false andalso % when should be 2 indented
+ bengt ->
+ gurka();
+ X when % AD added clause
+ false andalso % line should be 2 indented
+ not bengt ->
+ gurka();
+ X ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
after
- foo('after'),
- file:close(Xfile),
- bar(with_long_arg,
- with_second_arg)
+ foo('after'),
+ file:close(Xfile),
+ bar(with_long_arg,
+ with_second_arg)
end;
indent_try_catch() ->
try foo()
- after
- foo(),
- bar(with_long_arg,
- with_second_arg)
+ after
+ foo(),
+ bar(with_long_arg,
+ with_second_arg)
end.
indent_catch() ->
D = B +
- float(43.1),
+ float(43.1),
B = catch oskar(X),
- A = catch (baz +
- bax),
+ A = catch (baz +
+ bax),
catch foo(),
- C = catch B +
- float(43.1),
+ C = catch B +
+ float(43.1),
case catch foo(X) of
- A ->
- B
+ A ->
+ B
end,
case
- catch foo(X)
+ catch foo(X)
of
- A ->
- B
+ A ->
+ B
end,
case
- foo(X)
+ foo(X)
of
- A ->
- catch B,
- X
+ A ->
+ catch B,
+ X
end,
try sune of
- _ -> foo
+ _ -> foo
catch _:_ -> baf
end,
try
- sune
+ sune
of
- _ ->
- X = 5,
- (catch foo(X)),
- X + 10
+ _ ->
+ X = 5,
+ (catch foo(X)),
+ X + 10
catch _:_ -> baf
end,
try
- (catch sune)
+ (catch sune)
of
- _ ->
- catch foo() %% BUGBUG can't handle catch inside try without parentheses
+ _ ->
+ catch foo() %% BUGBUG can't handle catch inside try without parentheses
catch _:_ ->
- baf
+ baf
end,
try
- (catch exit())
+ (catch exit())
catch
- _ ->
- catch baf()
+ _ ->
+ catch baf()
end,
ok.
indent_binary() ->
X = lists:foldr(fun(M) ->
- <<Ma/binary, " ">>
- end, [], A),
+ <<Ma/binary, " ">>
+ end, [], A),
A = <<X/binary, 0:8>>,
B.
indent_comprehensions() ->
- %% I don't have a good idea how we want to handle this
+ %% I don't have a good idea how we want to handle this
%% but they are here to show how they are indented today.
- Result1 = [X ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- ],
+ Result1 = [X ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- ],
+ true = (X rem 2)
+ ],
- Binary1 = << <<X:8>> ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- >>,
+ Binary1 = << <<X:8>> ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ >>,
Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- >>,
+ true = (X rem 2)
+ >>,
ok.
%% This causes an error in earlier erlang-mode versions.
foo() ->
[#foo{
- foo = foo}].
+ foo = foo}].
%% Record indentation
some_function_with_a_very_long_name() ->
@@ -704,20 +704,20 @@ some_function_with_a_very_long_name() ->
field1=a,
field2=b},
case dummy_function_with_a_very_very_long_name(x) of
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- ok;
- Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b};
- #xyz{
- a=1,
- b=2} ->
- ok
+ #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b} ->
+ ok;
+ Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b} ->
+ Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b};
+ #xyz{
+ a=1,
+ b=2} ->
+ ok
end.
another_function_with_a_very_very_long_name() ->
@@ -726,45 +726,45 @@ another_function_with_a_very_very_long_name() ->
field2=1}.
some_function_name_xyz(xyzzy, #some_record{
- field1=Field1,
- field2=Field2}) ->
+ field1=Field1,
+ field2=Field2}) ->
SomeVariable = f(#'Some-long-record-name'{
- field_a = 1,
- 'inter-xyz-parameters' =
- #'Some-other-very-long-record-name'{
- field2 = Field1,
- field2 = Field2}}),
+ field_a = 1,
+ 'inter-xyz-parameters' =
+ #'Some-other-very-long-record-name'{
+ field2 = Field1,
+ field2 = Field2}}),
{ok, SomeVariable}.
commas_first() ->
{abc, [ {some_var, 1}
- , {some_other_var, 2}
- , {erlang_ftw, 9}
- , {erlang_cookie, 'cookie'}
- , {cmds,
- [ {one, "sudo ls"}
- , {one, "sudo ls"}
- , {two, "sudo ls"}
- , {three, "sudo ls"}
- , {four, "sudo ls"}
- , {three, "sudo ls"}
- ] }
- , {ssh_username, "yow"}
- , {cluster,
- [ {aaaa, [ {"10.198.55.12" , "" }
- , {"10.198.55.13" , "" }
- ] }
- , {bbbb, [ {"10.198.55.151", "" }
- , {"10.198.55.123", "" }
- , {"10.198.55.34" , "" }
- , {"10.198.55.85" , "" }
- , {"10.198.55.67" , "" }
- ] }
- , {cccc, [ {"10.198.55.68" , "" }
- , {"10.198.55.69" , "" }
- ] }
- ] }
- ]
+ , {some_other_var, 2}
+ , {erlang_ftw, 9}
+ , {erlang_cookie, 'cookie'}
+ , {cmds,
+ [ {one, "sudo ls"}
+ , {one, "sudo ls"}
+ , {two, "sudo ls"}
+ , {three, "sudo ls"}
+ , {four, "sudo ls"}
+ , {three, "sudo ls"}
+ ] }
+ , {ssh_username, "yow"}
+ , {cluster,
+ [ {aaaa, [ {"10.198.55.12" , "" }
+ , {"10.198.55.13" , "" }
+ ] }
+ , {bbbb, [ {"10.198.55.151", "" }
+ , {"10.198.55.123", "" }
+ , {"10.198.55.34" , "" }
+ , {"10.198.55.85" , "" }
+ , {"10.198.55.67" , "" }
+ ] }
+ , {cccc, [ {"10.198.55.68" , "" }
+ , {"10.198.55.69" , "" }
+ ] }
+ ] }
+ ]
}.
@@ -776,9 +776,9 @@ commas_first() ->
%% body, due to the function name being mistaken for a keyword
catcher(N) ->
try generate_exception(N) of
- Val -> {N, normal, Val}
+ Val -> {N, normal, Val}
catch
- throw:X -> {N, caught, thrown, X};
- exit:X -> {N, caught, exited, X};
- error:X -> {N, caught, error, X}
+ throw:X -> {N, caught, thrown, X};
+ exit:X -> {N, caught, exited, X};
+ error:X -> {N, caught, error, X}
end.
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
index 2552c71baf..c0cf1749b6 100644
--- a/lib/tools/emacs/test.erl.orig
+++ b/lib/tools/emacs/test.erl.orig
@@ -1,4 +1,4 @@
-%% -*- erlang -*-
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
%%
%% %CopyrightBegin%
%%
@@ -27,7 +27,7 @@
%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
%%%-------------------------------------------------------------------
-%% Start off with syntax highlighting you have to verify this by looking here
+%% Start off with syntax highlighting you have to verify this by looking here
%% and see that the code looks alright
-module(test).
@@ -44,18 +44,18 @@ foo() ->
%% Module attributes should be highlighted
-export([t/1]).
--record(record1, {a,
- b,
+-record(record1, {a,
+ b,
c
}).
-record(record2, {
a,
b
}).
-
+
-record(record3, {a = 8#42423 bor
8#4234,
- b = 8#5432
+ b = 8#5432
bor 2#1010101
c = 123 +
234,
@@ -64,7 +64,7 @@ foo() ->
-record(record4, {
a = 8#42423 bor
8#4234,
- b = 8#5432
+ b = 8#5432
bor 2#1010101
c = 123 +
234,
@@ -79,31 +79,31 @@ foo() ->
-spec t(integer()) -> any().
--type ann() :: Var :: integer().
--type ann2() :: Var ::
- 'return'
- | 'return_white_spaces'
+-type ann() :: Var :: integer().
+-type ann2() :: Var ::
+ 'return'
+ | 'return_white_spaces'
| 'return_comments'
- | 'text' | ann().
--type paren() ::
- (ann2()).
--type t1() :: atom().
--type t2() :: [t1()].
--type t3(Atom) :: integer(Atom).
--type t4() :: t3(foobar).
--type t5() :: {t1(), t3(foo)}.
--type t6() :: 1 | 2 | 3 |
- 'foo' | 'bar'.
--type t7() :: [].
--type t71() :: [_].
+ | 'text' | ann().
+-type paren() ::
+ (ann2()).
+-type t1() :: atom().
+-type t2() :: [t1()].
+-type t3(Atom) :: integer(Atom).
+-type t4() :: t3(foobar).
+-type t5() :: {t1(), t3(foo)}.
+-type t6() :: 1 | 2 | 3 |
+ 'foo' | 'bar'.
+-type t7() :: [].
+-type t71() :: [_].
-type t8() :: {any(),none(),pid(),port(),
- reference(),float()}.
--type t9() :: [1|2|3|foo|bar] |
- list(a | b | c) | t71().
--type t10() :: {1|2|3|foo|t9()} | {}.
--type t11() :: 1..2.
--type t13() :: maybe_improper_list(integer(), t11()).
--type t14() :: [erl_scan:foo() |
+ reference(),float()}.
+-type t9() :: [1|2|3|foo|bar] |
+ list(a | b | c) | t71().
+-type t10() :: {1|2|3|foo|t9()} | {}.
+-type t11() :: 1..2.
+-type t13() :: maybe_improper_list(integer(), t11()).
+-type t14() :: [erl_scan:foo() |
%% Should be highlighted
term() |
bool() |
@@ -122,31 +122,31 @@ foo() ->
timeout() |
no_return() |
%% Should not be highlighted
- nonempty_() | nonlist() |
+ nonempty_() | nonlist() |
erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
<<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
-<<_:34>>|<<_:34>>|<<_:34>>].
--type t16() :: fun().
--type t17() :: fun((...) -> paren()).
--type t18() :: fun(() -> t17() | t16()).
+<<_:34>>|<<_:34>>|<<_:34>>].
+-type t16() :: fun().
+-type t17() :: fun((...) -> paren()).
+-type t18() :: fun(() -> t17() | t16()).
-type t19() :: fun((t18()) -> t16()) |
fun((nonempty_maybe_improper_list('integer', any())|
1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
nonempty_maybe_improper_list('integer', any())|
-1|2|3|a|b|<<_:3,_:_*14>>|integer()).
--type t20() :: [t19(), ...].
--type t21() :: tuple().
--type t21(A) :: A.
--type t22() :: t21(integer()).
--type t23() :: #rec1{}.
--type t24() :: #rec2{a :: t23(), b :: [atom()]}.
--type t25() :: #rec3{f123 :: [t24() |
-1|2|3|4|a|b|c|d|
-nonempty_maybe_improper_list(integer, any())]}.
+1|2|3|a|b|<<_:3,_:_*14>>|integer()).
+-type t20() :: [t19(), ...].
+-type t21() :: tuple().
+-type t21(A) :: A.
+-type t22() :: t21(integer()).
+-type t23() :: #rec1{}.
+-type t24() :: #rec2{a :: t23(), b :: [atom()]}.
+-type t25() :: #rec3{f123 :: [t24() |
+1|2|3|4|a|b|c|d|
+nonempty_maybe_improper_list(integer, any())]}.
-type t26() :: #rec4{ a :: integer()
, b :: any()
}.
@@ -155,7 +155,7 @@ nonempty_maybe_improper_list(integer, any())]}.
}.
-type t99() ::
{t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
-t15(),t20(),t21(), t22(),t25()}.
+t15(),t20(),t21(), t22(),t25()}.
-spec t1(FooBar :: t99()) -> t99();
(t2()) -> t2();
(t4()) -> t4() when is_subtype(t4(), t24);
@@ -169,21 +169,21 @@ t15(),t20(),t21(), t22(),t25()}.
(A :: atom()) -> R2 :: foo:atomen();
(T :: tuple()) -> R3 :: bar:typen().
--spec mod:t2() -> any().
+-spec mod:t2() -> any().
--spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
+-spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
| {'del_member', name(), pid()},
#state{}) -> {'noreply', #state{}}.
--spec handle_cast(Cast ::
- {'exchange', node(), [[name(),...]]}
+-spec handle_cast(Cast ::
+ {'exchange', node(), [[name(),...]]}
| {'del_member', name(), pid()},
#state{}) -> {'noreply', #state{}}.
-spec all(fun((T) -> boolean()), List :: [T]) ->
boolean() when is_subtype(T, term()). % (*)
--spec get_closest_pid(term()) ->
+-spec get_closest_pid(term()) ->
Return :: pid()
| {'error', {'no_process', term()}
| {'no_such_group', term()}}.
@@ -192,23 +192,23 @@ t15(),t20(),t21(), t22(),t25()}.
, Y :: integer()
) -> integer().
--opaque attributes_data() ::
+-opaque attributes_data() ::
[{'column', column()} | {'line', info_line()} |
- {'text', string()}] | {line(),column()}.
+ {'text', string()}] | {line(),column()}.
-record(r,{
f1 :: attributes_data(),
-f222 = foo:bar(34, #rec3{}, 234234234423,
- aassdsfsdfsdf, 2234242323) ::
-[t24() | 1|2|3|4|a|b|c|d|
+f222 = foo:bar(34, #rec3{}, 234234234423,
+ aassdsfsdfsdf, 2234242323) ::
+[t24() | 1|2|3|4|a|b|c|d|
nonempty_maybe_improper_list(integer, any())],
-f333 :: [t24() | 1|2|3|4|a|b|c|d|
+f333 :: [t24() | 1|2|3|4|a|b|c|d|
nonempty_maybe_improper_list(integer, any())],
f3 = x:y(),
f4 = x:z() :: t99(),
f17 :: 'undefined',
f18 :: 1 | 2 | 'undefined',
f19 = 3 :: integer()|undefined,
-f5 = 3 :: undefined|integer()}).
+f5 = 3 :: undefined|integer()}).
-record(state, {
sequence_number = 1 :: integer()
@@ -230,12 +230,12 @@ highlighting(X) % Function definitions should be highlighted
'#1',atom,
$", atom, % atom should be ok
- $', atom,
-
+ $', atom,
+
"string$", atom, "string$", atom, % currently buggy I know...
"string\$", atom, % workaround for bug above
-
- "char $in string", atom,
+
+ "char $in string", atom,
'atom$', atom, 'atom$', atom,
'atom\$', atom,
@@ -270,18 +270,18 @@ highlighting(X) % Function definitions should be highlighted
erlang:anything(lists),
%% Guards
is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
- is_function(Fun), is_pid(self()),
+ is_function(Fun), is_pid(self()),
not_a_guard:is_list([]),
%% Other Types
atom, % not (currently) hightlighted
- 234234,
+ 234234,
234.43,
- [list, are, not, higlighted],
+ [list, are, not, higlighted],
{nor, is, tuple},
ok.
-
+
%%%
%%% Indentation
%%%
@@ -293,32 +293,32 @@ highlighting(X) % Function definitions should be highlighted
% Right
-indent_basics(X, Y, Z)
+indent_basics(X, Y, Z)
when X > 42,
Z < 13;
Y =:= 4711 ->
%% comments
% right comments
- case lists:filter(fun(_, AlongName,
- B,
+ case lists:filter(fun(_, AlongName,
+ B,
C) ->
true
- end,
+ end,
[a,v,b])
of
[] ->
Y = 5 * 43,
ok;
- [_|_] ->
+ [_|_] ->
Y = 5 * 43,
ok
end,
Y,
%% List, tuples and binaries
- [a,
+ [a,
b, c
],
- [ a,
+ [ a,
b, c
],
@@ -326,10 +326,10 @@ Y =:= 4711 ->
a,
b
],
- {a,
+ {a,
b,c
},
- { a,
+ { a,
b,c
},
@@ -337,7 +337,7 @@ Y =:= 4711 ->
a,
b
},
-
+
<<1:8,
2:8
>>,
@@ -359,21 +359,21 @@ Y =:= 4711 ->
c
),
-
+
(
a,
b,
c
),
- call(2#42423 bor
+ call(2#42423 bor
#4234,
2#5432,
other_arg),
ok;
-indent_basics(Xlongname,
+indent_basics(Xlongname,
#struct{a=Foo,
- b=Bar},
+ b=Bar},
[X|
Y]) ->
testing_next_clause,
@@ -451,13 +451,13 @@ indent_icr(Z) -> % icr = if case receive
%% receive
- receive
+ receive
{Z,_,_} ->
X = 43 div 4,
foo(X);
Z ->
X = 43 div 4,
- foo(X)
+ foo(X)
end,
receive
{Z,_,_} ->
@@ -473,33 +473,33 @@ indent_icr(Z) -> % icr = if case receive
foo(X);
Z ->
X = 43 div 4,
- foo(X)
+ foo(X)
after infinity ->
foo(X),
asd(X),
5*43
end,
receive
- after 10 ->
+ after 10 ->
foo(X),
asd(X),
5*43
end,
ok.
-
+
indent_fun() ->
%% Changed fun to one indention level
-Var = spawn(fun(X)
+Var = spawn(fun(X)
when X == 2;
- X > 10 ->
+ X > 10 ->
hello,
- case Hello() of
- true when is_atom(X) ->
+ case Hello() of
+ true when is_atom(X) ->
foo;
false ->
bar
end;
- (Foo) when is_atom(Foo),
+ (Foo) when is_atom(Foo),
is_integer(X) ->
X = 6* 45,
Y = true andalso
@@ -522,14 +522,14 @@ Fact(0) ->
indent_try_catch() ->
try
- io:format(stdout, "Parsing file ~s, ",
+ io:format(stdout, "Parsing file ~s, ",
[St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
+ {ok,Line3,REAs,Actions,St3} =
parse_rules(Xfile, Line2, Macs, St2)
catch
exit:{badarg,R} ->
foo(R),
- io:format(stdout,
+ io:format(stdout,
"ERROR reason ~p~n",
R);
error:R % AD added clause
@@ -544,7 +544,7 @@ indent_try_catch() ->
foo(R);
error:R ->
foo(R),
- io:format(stdout,
+ io:format(stdout,
"ERROR reason ~p~n",
R)
after
@@ -577,12 +577,12 @@ indent_try_catch() ->
catch
exit:{badarg,R} ->
foo(R),
- io:format(stdout,
+ io:format(stdout,
"ERROR reason ~p~n",
R);
error:R ->
foo(R),
- io:format(stdout,
+ io:format(stdout,
"ERROR reason ~p~n",
R)
after
@@ -593,7 +593,7 @@ indent_try_catch() ->
end;
indent_try_catch() ->
try foo()
- after
+ after
foo(),
bar(with_long_arg,
with_second_arg)
@@ -602,16 +602,16 @@ indent_try_catch() ->
indent_catch() ->
D = B +
float(43.1),
-
+
B = catch oskar(X),
-
- A = catch (baz +
+
+ A = catch (baz +
bax),
catch foo(),
- C = catch B +
+ C = catch B +
float(43.1),
-
+
case catch foo(X) of
A ->
B
@@ -673,9 +673,9 @@ indent_binary() ->
indent_comprehensions() ->
-%% I don't have a good idea how we want to handle this
+%% I don't have a good idea how we want to handle this
%% but they are here to show how they are indented today.
-Result1 = [X ||
+Result1 = [X ||
#record{a=X} <- lists:seq(1, 10),
true = (X rem 2)
],
@@ -683,7 +683,7 @@ Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
true = (X rem 2)
],
-Binary1 = << <<X:8>> ||
+Binary1 = << <<X:8>> ||
#record{a=X} <- lists:seq(1, 10),
true = (X rem 2)
>>,
diff --git a/lib/tools/examples/xref_examples.erl b/lib/tools/examples/xref_examples.erl
index 4c082195a2..f7e71c9708 100644
--- a/lib/tools/examples/xref_examples.erl
+++ b/lib/tools/examples/xref_examples.erl
@@ -7,7 +7,7 @@
%% ${HOME}/unused_locals.txt.
script() ->
Root = code:root_dir(),
- Dir = os:getenv("HOME"),
+ {ok,[[Dir]]} = init:get_argument(home),
Server = s,
xref:start(Server),
{ok, _Relname} = xref:add_release(Server, code:lib_dir(), {name,otp}),
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index 8db23dd151..d1a4624419 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -1636,6 +1636,11 @@ 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;
+
+trace_handler({trace_ts, Pid, gc_start, _Func, TS} = Trace, Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_gc_start(Table, Pid, TS),
+ TS;
%%
%% gc_end
@@ -1648,6 +1653,12 @@ 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;
+
+trace_handler({trace_ts, Pid, gc_end, _Func, TS} = Trace, Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_gc_end(Table, Pid, TS),
+ TS;
+
%%
%% link
trace_handler({trace_ts, Pid, link, _OtherPid, TS} = Trace,
@@ -1691,6 +1702,12 @@ trace_handler({trace_ts, Pid, send, _OtherPid, _Msg, TS} = Trace,
dump_stack(Dump, get(Pid), Trace),
TS;
%%
+%% send_to_non_existing_process
+trace_handler({trace_ts, Pid, send_to_non_existing_process, _OtherPid, _Msg, TS} = Trace,
+ _Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ TS;
+%%
%% 'receive'
trace_handler({trace_ts, Pid, 'receive', _Msg, TS} = Trace,
_Table, _, Dump) ->
diff --git a/lib/tools/src/make.erl b/lib/tools/src/make.erl
index 26378f28a0..37e67cbe34 100644
--- a/lib/tools/src/make.erl
+++ b/lib/tools/src/make.erl
@@ -317,5 +317,7 @@ check_includes2(Epp, File, ObjMTime) ->
epp:close(Epp),
false;
{error, _Error} ->
+ check_includes2(Epp, File, ObjMTime);
+ {warning, _Warning} ->
check_includes2(Epp, File, ObjMTime)
end.
diff --git a/lib/tools/src/tags.erl b/lib/tools/src/tags.erl
index b833d96c19..fff67eb0fd 100644
--- a/lib/tools/src/tags.erl
+++ b/lib/tools/src/tags.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.
diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src
index a00969eabe..4c7dd24006 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -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.
@@ -40,7 +40,7 @@
{env, [{file_util_search_methods,[{"", ""}, {"ebin", "esrc"}, {"ebin", "src"}]}
]
},
- {runtime_dependencies, ["stdlib-2.5","runtime_tools-1.8.14",
+ {runtime_dependencies, ["stdlib-3.1","runtime_tools-1.8.14",
"kernel-3.0","inets-5.10","erts-7.0",
"compiler-5.0"]}
]
diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl
index bb9815f9b0..f298a1ce81 100644
--- a/lib/tools/src/xref_base.erl
+++ b/lib/tools/src/xref_base.erl
@@ -746,7 +746,7 @@ read_a_module({Dir, BaseName}, AppName, Builtins, Verbose, Warnings, Mode) ->
message(Warnings, no_debug_info, [File]),
no;
{ok, M, Data, UnresCalls0} ->
- message(Verbose, done, [File]),
+ message(Verbose, done_file, [File]),
%% Remove duplicates. Identical unresolved calls on the
%% same line are counted as _one_ unresolved call.
UnresCalls = usort(UnresCalls0),
@@ -1842,6 +1842,8 @@ message(true, What, Arg) ->
set_up ->
io:format("Setting up...", Arg);
done ->
+ io:format("done~n", Arg);
+ done_file ->
io:format("done reading ~ts~n", Arg);
error ->
io:format("error~n", Arg);
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index b4c9264b30..90e113c178 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -1001,41 +1001,40 @@ otp_6115(Config) when is_list(Config) ->
%% Cover compile f1, but not f2
{ok, f1} = cover:compile(f1),
+ %% This test used to ensure that a process containing a
+ %% fun refering to cover compiled code was killed.
+ %% check_process_code may however ignore funs as of ERTS
+ %% version 8.1. The test has therefore been rewritten to
+ %% check that a process with a direct reference (in this
+ %% case a return address) to the code is killed.
+ %%
%% If f1 is cover compiled, a process P is started with a
- %% reference to the fun created in start_fail/0, and cover:stop() is
- %% called, then P should be killed.
- %% This is because (the fun held by P) references the cover
+ %% direct reference to the f1, and cover:stop() is called,
+ %% then P should be killed.
+ %% This is because of the return address to the cover
%% compiled code which should be *unloaded* when cover:stop() is
%% called -- running cover compiled code when there is no cover
%% server and thus no ets tables to bump counters in, makes no
%% sense.
- Pid1 = f1:start_a(),
- Pid2 = f1:start_b(),
+ Pid = spawn(fun () -> f1:non_tail_call_f2_wait() end),
%% Now stop cover
cover:stop(),
%% Ensure that f1 is loaded (and not cover compiled), and that
- %% both Pid1 and Pid2 are dead.
+ %% both Pid is dead.
case code:which(f1) of
Beam when is_list(Beam) ->
ok;
Other ->
ct:fail({"f1 is not reloaded", Other})
end,
- case process_info(Pid1) of
+ case process_info(Pid) of
undefined ->
ok;
- _PI1 ->
- RefToOldP1 = erlang:check_process_code(Pid1, f1),
- ct:fail({"Pid1 still alive", RefToOldP1})
- end,
- case process_info(Pid2) of
- undefined ->
- ok;
- _PI2 ->
- RefToOldP2 = erlang:check_process_code(Pid1, f2),
- ct:fail({"Pid2 still alive", RefToOldP2})
+ _PI ->
+ RefToOldP = erlang:check_process_code(Pid, f1),
+ ct:fail({"Pid still alive", RefToOldP})
end,
file:set_cwd(CWD),
diff --git a/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl b/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl
index 5399b33f19..fc4a62e70e 100644
--- a/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl
+++ b/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl
@@ -1,13 +1,6 @@
-module(f1).
--export([start_a/0, start_b/0]).
+-export([non_tail_call_f2_wait/0]).
-start_a() ->
- f2:start(fun() ->
- ok
- end).
-
-start_b() ->
- f2:start(fun fun1/0).
-
-fun1() ->
- ok.
+non_tail_call_f2_wait() ->
+ f2:wait(),
+ im_back.
diff --git a/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl b/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl
index 72a6a64c4d..4bc88035c7 100644
--- a/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl
+++ b/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl
@@ -1,13 +1,5 @@
-module(f2).
--export([start/1]).
+-export([wait/0]).
-start(Fun) ->
- spawn(fun() ->
- wait(Fun)
- end).
-
-wait(Fun) ->
- receive
- go ->
- Fun()
- end.
+wait() ->
+ receive after infinity -> ok end.
diff --git a/lib/tools/test/make_SUITE_data/test1.erl b/lib/tools/test/make_SUITE_data/test1.erl
index f4a133008e..9e21bdc767 100644
--- a/lib/tools/test/make_SUITE_data/test1.erl
+++ b/lib/tools/test/make_SUITE_data/test1.erl
@@ -3,6 +3,8 @@
-vsn('$Revision: /main/release/2 $').
-compile(export_all).
+-warning("a warning").
+
f1() ->
true.
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index ce30fb711a..01dbac6ecb 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -50,7 +50,7 @@
-export([analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]).
--export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1]).
+-export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1, otp_13708/1]).
-import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]).
@@ -82,7 +82,7 @@ groups() ->
fun_mfa_r14, fun_mfa_vars, qlc]},
{analyses, [],
[analyze, basic, md, q, variables, unused_locals]},
- {misc, [], [format_error, otp_7423, otp_7831, otp_10192]}].
+ {misc, [], [format_error, otp_7423, otp_7831, otp_10192, otp_13708]}].
init_per_suite(Conf) when is_list(Conf) ->
@@ -2393,6 +2393,19 @@ otp_10192(Conf) when is_list(Conf) ->
xref:stop(s),
ok.
+%% OTP-10192. Allow filenames with character codes greater than 126.
+otp_13708(Conf) when is_list(Conf) ->
+ {ok, _} = start(s),
+ ok = xref:set_default(s, [{verbose, true}]),
+ {ok, []} = xref:q(s,"E"),
+ xref:stop(s),
+
+ CopyDir = ?copydir,
+ Dir = fname(CopyDir,"lib_test"),
+ {ok, _} = start(s),
+ ok = xref:set_library_path(s, [Dir], [{verbose, true}]),
+ xref:stop(s).
+
%%%
%%% Utilities
%%%
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 8c889cbe4e..f60da27c44 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.8.4
+TOOLS_VSN = 2.9.1
diff --git a/lib/typer/doc/src/notes.xml b/lib/typer/doc/src/notes.xml
index 9ef5ca1c70..077219dcbc 100644
--- a/lib/typer/doc/src/notes.xml
+++ b/lib/typer/doc/src/notes.xml
@@ -31,6 +31,20 @@
</header>
<p>This document describes the changes made to TypEr.</p>
+<section><title>TypEr 0.9.12</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix a bug concerning parameterized opaque types. </p>
+ <p>
+ Own Id: OTP-14130</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>TypEr 0.9.11</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/typer/src/typer.app.src b/lib/typer/src/typer.app.src
index 974091b44c..e0be937cc3 100644
--- a/lib/typer/src/typer.app.src
+++ b/lib/typer/src/typer.app.src
@@ -7,5 +7,5 @@
{registered, []},
{applications, [compiler, dialyzer, hipe, kernel, stdlib]},
{env, []},
- {runtime_dependencies, ["stdlib-2.0","kernel-3.0","hipe-3.10.3","erts-6.0",
- "dialyzer-2.7","compiler-5.0"]}]}.
+ {runtime_dependencies, ["stdlib-3.0","kernel-5.0","hipe-3.15.4","erts-8.0",
+ "dialyzer-3.1","compiler-7.0"]}]}.
diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl
index 5c82750a21..4aad0b4347 100644
--- a/lib/typer/src/typer.erl
+++ b/lib/typer/src/typer.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. 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.
@@ -143,8 +143,9 @@ extract(#analysis{macros = Macros,
MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
CodeServer2 = dialyzer_codeserver:set_temp_records(MergedRecords, CodeServer1),
CodeServer3 = dialyzer_codeserver:finalize_exported_types(NewExpTypes, CodeServer2),
- CodeServer4 = dialyzer_utils:process_record_remote_types(CodeServer3),
- dialyzer_contracts:process_contract_remote_types(CodeServer4)
+ {CodeServer4, RecordDict} =
+ dialyzer_utils:process_record_remote_types(CodeServer3),
+ dialyzer_contracts:process_contract_remote_types(CodeServer4, RecordDict)
catch
throw:{error, ErrorMsg} ->
compile_error(ErrorMsg)
@@ -156,7 +157,7 @@ extract(#analysis{macros = Macros,
fun(Module, TmpPlt) ->
{ok, ModuleContracts} = dict:find(Module, Contracts),
SpecList = [{MFA, Contract}
- || {MFA, {_FileLine, Contract}} <- dict:to_list(ModuleContracts)],
+ || {MFA, {_FileLine, Contract}} <- maps:to_list(ModuleContracts)],
dialyzer_plt:insert_contract_list(TmpPlt, SpecList)
end,
NewTrustPLT = lists:foldl(FoldFun, TrustPLT, Modules),
@@ -172,8 +173,10 @@ get_type_info(#analysis{callgraph = CallGraph,
StrippedCallGraph = remove_external(CallGraph, TrustPLT),
%% io:format("--- Analyzing callgraph... "),
try
- NewPlt = dialyzer_succ_typings:analyze_callgraph(StrippedCallGraph,
- TrustPLT, CodeServer),
+ NewMiniPlt = dialyzer_succ_typings:analyze_callgraph(StrippedCallGraph,
+ TrustPLT,
+ CodeServer),
+ NewPlt = dialyzer_plt:restore_full_plt(NewMiniPlt),
Analysis#analysis{callgraph = StrippedCallGraph, trust_plt = NewPlt}
catch
error:What ->
@@ -224,7 +227,7 @@ get_external(Exts, Plt) ->
-type fa() :: {atom(), arity()}.
-type func_info() :: {line(), atom(), arity()}.
--record(info, {records = map__new() :: map_dict(),
+-record(info, {records = maps:new() :: erl_types:type_table(),
functions = [] :: [func_info()],
types = map__new() :: map_dict(),
edoc = false :: boolean()}).
@@ -267,7 +270,7 @@ write_inc_files(Inc) ->
Functions = [Key || {Key, _} <- Val],
Val1 = [{{F,A},Type} || {{_Line,F,A},Type} <- Val],
Info = #info{types = map__from_list(Val1),
- records = map__new(),
+ records = maps:new(),
%% Note we need to sort functions here!
functions = lists:keysort(1, Functions)},
%% io:format("Types ~p\n", [Info#info.types]),
@@ -849,8 +852,9 @@ collect_info(Analysis) ->
TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer),
TmpCServer2 =
dialyzer_codeserver:finalize_exported_types(MergedExpTypes, TmpCServer1),
- TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2),
- dialyzer_contracts:process_contract_remote_types(TmpCServer3)
+ {TmpCServer3, RecordDict} =
+ dialyzer_utils:process_record_remote_types(TmpCServer2),
+ dialyzer_contracts:process_contract_remote_types(TmpCServer3, RecordDict)
catch
throw:{error, ErrorMsg} ->
fatal_error(ErrorMsg)
diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk
index ed12e067c1..fe8f0446a5 100644
--- a/lib/typer/vsn.mk
+++ b/lib/typer/vsn.mk
@@ -1 +1 @@
-TYPER_VSN = 0.9.11
+TYPER_VSN = 0.9.12
diff --git a/lib/wx/api_gen/gl_gen.erl b/lib/wx/api_gen/gl_gen.erl
index 54635bdd2e..7e3766a43b 100644
--- a/lib/wx/api_gen/gl_gen.erl
+++ b/lib/wx/api_gen/gl_gen.erl
@@ -354,6 +354,7 @@ handle_arg_opt({single,Opt},P=#arg{type=T}) -> P#arg{type=T#type{single=Opt}};
handle_arg_opt({base,{Opt, Sz}}, P=#arg{type=T}) -> P#arg{type=T#type{base=Opt, size=Sz}};
handle_arg_opt({base,Opt}, P=#arg{type=T}) -> P#arg{type=T#type{base=Opt}};
handle_arg_opt({c_only,Opt},P) -> P#arg{where=c, alt=Opt};
+handle_arg_opt(list_binary, P) -> P#arg{alt=list_binary};
handle_arg_opt(string, P=#arg{type=T}) -> P#arg{type=T#type{base=string}};
handle_arg_opt({string,Max,Sz}, P=#arg{type=T}) ->
P#arg{type=T#type{base=string, size={Max,Sz}}}.
@@ -588,7 +589,7 @@ lookup(Name,[_|R],Def) ->
lookup(Name,R,Def);
lookup(_,[], Def) -> Def.
-setup_idx_binary(Name,Ext,_Opts) ->
+setup_idx_binary(Name,Ext, Opts) ->
FuncName = Name ++ Ext,
Func = #func{params=Args} = get(FuncName),
Id = next_id(function),
@@ -607,8 +608,7 @@ setup_idx_binary(Name,Ext,_Opts) ->
ok;
(_) -> ok
end, Args),
-
- case setup_idx_binary(Args, []) of
+ case setup_idx_binary_1(Args, []) of
ignore ->
put(FuncName, Func#func{id=Id}),
Name++Ext;
@@ -624,30 +624,41 @@ setup_idx_binary(Name,Ext,_Opts) ->
[FuncName,Extra]
end.
-setup_idx_binary([A=#arg{in=true,type=T=#type{base=idx_binary}}|R], Acc) ->
+setup_idx_binary_1([A=#arg{in=true,type=T=#type{base=idx_binary}}|R], Acc) ->
A1 = A#arg{type=T#type{base=guard_int,size=4}},
A2 = A#arg{type=T#type{base=binary}},
Head = reverse(Acc),
- case setup_idx_binary(R, []) of
+ case setup_idx_binary_1(R, []) of
ignore ->
{bin, Head ++ [A1|R], Head ++ [A2|R]};
{bin, R1,R2} ->
{bin, Head ++ [A1|R1], Head ++ [A2|R2]}
end;
-setup_idx_binary([A=#arg{in=true,type=T=#type{single={tuple,matrix}}}|R], Acc) ->
+setup_idx_binary_1([A=#arg{in=true,type=T=#type{base=int,size=4},alt=list_binary}|R], Acc) ->
+ A1 = A#arg{type=T#type{base=guard_int}},
+ A2 = A#arg{type=T#type{base=binary}},
+ Head = reverse(Acc),
+ case setup_idx_binary_1(R, []) of
+ ignore ->
+ {bin, Head ++ [A1|R], Head ++ [A2|R]};
+ {bin, R1,R2} ->
+ {bin, Head ++ [A1|R1], Head ++ [A2|R2]}
+ end;
+
+setup_idx_binary_1([A=#arg{in=true,type=T=#type{single={tuple,matrix}}}|R], Acc) ->
A1 = A#arg{type=T#type{single={tuple, matrix12}}},
A2 = A#arg{type=T#type{single={tuple, 16}}},
Head = reverse(Acc),
- case setup_idx_binary(R, []) of
+ case setup_idx_binary_1(R, []) of
ignore ->
{matrix, Head ++ [A1|R], Head ++ [A2|R]};
{matrix, R1,R2} ->
{matrix, Head ++ [A1|R1], Head ++ [A2|R2]}
end;
-setup_idx_binary([H|R],Acc) ->
- setup_idx_binary(R,[H|Acc]);
-setup_idx_binary([],_) -> ignore.
-
+setup_idx_binary_1([H|R],Acc) ->
+ setup_idx_binary_1(R,[H|Acc]);
+setup_idx_binary_1([],_) -> ignore.
+
is_equal(F1=#func{type=T1,params=A1},F2=#func{type=T2,params=A2}) ->
Equal = is_equal_type(T1,T2) andalso is_equal_args(A1,A2),
case Equal of
diff --git a/lib/wx/api_gen/gl_gen_c.erl b/lib/wx/api_gen/gl_gen_c.erl
index c6d4f3a520..53c194a315 100644
--- a/lib/wx/api_gen/gl_gen_c.erl
+++ b/lib/wx/api_gen/gl_gen_c.erl
@@ -123,20 +123,14 @@ declare_var(A=#arg{name=N,in=false,type=#type{name=T,base=B,single={list,Sz}}})
when is_number(Sz) ->
w(" ~s ~s[~p] = {~s};~n", [T,N,Sz,args(fun zero/1,",",lists:duplicate(Sz,B))]),
A;
-declare_var(A=#arg{name=N,in=false,type=#type{name=T,base=string,size={Max,_}, single=Single}}) ->
+declare_var(A=#arg{name=N,in=false,type=#type{name=T,base=string,size={Max,_}}}) ->
case is_integer(Max) of
- true ->
+ true ->
w(" ~s ~s[~p];~n", [T,N,Max]);
false ->
- %% w(" ~s ~s[*~s];~n", [T,N,Max]),
- w(" ~s *~s;~n", [T,N]),
+ w(" ~s *~s;~n", [T,N]),
w(" ~s = (~s *) driver_alloc(sizeof(~s) * *~s);~n", [N,T,T,Max]),
- store_free(N)
- %% case Single of
- %% {list, _, _} ->
- %% w(" ~s *~s_p = ~s;~n", [T,N,N]);
- %% _ -> ok
- %% end
+ store_free(N)
end,
A;
declare_var(A=#arg{name=N,in=false,type=#type{base=binary,size={MaxSz, _}}}) ->
@@ -163,9 +157,9 @@ declare_var(A=#arg{name=N,in=false,
type=#type{name=T,base=B,by_val=false,single=true}}) ->
w(" ~s ~s[1] = {~s};~n", [T,N,zero(B)]),
A;
-declare_var(A=#arg{where=c, type=#type{name=T}, alt={size,Var}}) ->
+declare_var(A=#arg{where=c, type=#type{name=T}, alt={size,Var}}) ->
w(" ~s ~s_size = bins_sz[~p];~n", [T, Var, get(bin_count)]),
- A;
+ A;
declare_var(A=#arg{where=_}) ->
A.
@@ -194,9 +188,16 @@ decode_arg(P=#arg{where=erl},A) -> {P,A};
decode_arg(P=#arg{where=c},A) -> {P,A};
decode_arg(P=#arg{in=false},A) -> {P,A};
-decode_arg(P=#arg{name=Name,type=#type{name=Type,base=binary}},A0) ->
+decode_arg(P=#arg{name=Name,alt=Alt,type=#type{name=Type,base=binary}},A0) ->
w(" ~s *~s = (~s *) bins[~p];~n", [Type,Name,Type,next_id(bin_count)]),
- {P, A0};
+ case Alt of
+ list_binary ->
+ A = align(4, A0),
+ w(" int * ~sLen = (int *) bp; bp += 4; (void) ~sLen;~n", [Name, Name]),
+ {P, A};
+ _ ->
+ {P, A0}
+ end;
decode_arg(P=#arg{name=Name,type=#type{name=Type,base=memory}},A0) ->
w(" ~s *~s = (~s *) bins[~p];~n", [Type,Name,Type,next_id(bin_count)]),
{P, A0};
@@ -217,7 +218,7 @@ decode_arg(P=#arg{name=Name,type=#type{size=Sz,single=list,name=Type}},A0) ->
A = align(max([Sz,4]),A0),
w(" int * ~sLen = (int *) bp; bp += ~p;~n", [Name, max([4,Sz])]),
w(" ~s * ~s = (~s *) bp; ", [Type,Name,Type]),
- w(" bp += (8-((*~sLen*~p+~p)%8))%8;~n", [Name,Sz,A]),
+ w(" bp += *~sLen*~p + (8-((*~sLen*~p+~p)%8))%8;~n", [Name,Sz,Name,Sz,A]),
{P, 0};
decode_arg(P=#arg{name=Name,type=#type{size=TSz,name=Type,single={tuple,undefined}}},A0) ->
A = align(TSz,A0),
diff --git a/lib/wx/api_gen/gl_gen_erl.erl b/lib/wx/api_gen/gl_gen_erl.erl
index 1be8d775be..3ad14825dd 100644
--- a/lib/wx/api_gen/gl_gen_erl.erl
+++ b/lib/wx/api_gen/gl_gen_erl.erl
@@ -116,7 +116,7 @@ gl_api(Fs) ->
w("call(Op, Args) ->~n", []),
w(" Port = get(opengl_port), ~n", []),
w(" _ = erlang:port_control(Port,Op,Args),~n", []),
- w(" rec().~n", []),
+ w(" rec(Op).~n", []),
w(" ~n", []),
w("%% @hidden~n", []),
w("cast(Op, Args) ->~n", []),
@@ -125,11 +125,15 @@ gl_api(Fs) ->
w(" ok.~n", []),
w(" ~n", []),
w("%% @hidden~n", []),
- w("rec() ->~n", []),
- w(" receive ~n", []),
+ w("rec(Op) ->~n", []),
+ w(" receive~n", []),
w(" {'_egl_result_', Res} -> Res;~n", []),
- w(" {'_egl_error_', Op, Res} -> error({error,Res,Op})~n", []),
- w(" end. ~n", []),
+ w(" {'_egl_error_', Op, Res} -> error({error,Res,Op});~n", []),
+ w(" {'_egl_error_', Other, Res} ->~n ", []),
+ w(" Err = io_lib:format(\"~~p in op: ~~p\", [Res, Other]),~n", []),
+ w(" error_logger:error_report([{gl, error}, {message, lists:flatten(Err)}]),~n", []),
+ w(" rec(Op)~n", []),
+ w(" end.~n", []),
w("~n", []),
w("%% @hidden~n", []),
w("send_bin(Bin) when is_binary(Bin) ->~n", []),
@@ -492,6 +496,8 @@ doc_arg_type2(#type{base=string, single=list}) ->
"iolist()";
doc_arg_type2(T=#type{single={tuple,Sz}}) ->
"{" ++ args(fun doc_arg_type3/1, ",", lists:duplicate(Sz,T)) ++ "}";
+doc_arg_type2(#type{base=guard_int, single=list}) ->
+ "[integer()]|mem()";
doc_arg_type2(T=#type{single=list}) ->
"[" ++ doc_arg_type3(T) ++ "]";
doc_arg_type2(T=#type{single={list, _Max}}) ->
@@ -512,7 +518,9 @@ doc_arg_type3(#type{base=binary}) -> "binary()";
doc_arg_type3(#type{base=memory}) -> "mem()".
guard_test(As) ->
- Str = args(fun(#arg{name=N,type=#type{base=guard_int}}) ->
+ Str = args(fun(#arg{name=N,type=#type{base=guard_int, single=list}}) ->
+ " is_list("++erl_arg_name(N)++")";
+ (#arg{name=N,type=#type{base=guard_int}}) ->
" is_integer("++erl_arg_name(N)++")";
(_) ->
skip
@@ -522,6 +530,13 @@ guard_test(As) ->
Other -> " when " ++ Other
end.
+pre_marshal([#arg{name=N,in=true, type=#type{base=binary, single=list}=T, alt=list_binary}=A|R]) ->
+ w(" send_bin(~s),~n", [erl_arg_name(N)]),
+ w(" ~sLen = byte_size(if is_binary(~s) -> ~s; is_tuple(~s) -> element(2, ~s) end) div 4,~n",
+ [erl_arg_name(N),erl_arg_name(N), erl_arg_name(N), erl_arg_name(N), erl_arg_name(N)]),
+ Type = T#type{base=int, by_val=true, single=true, ref=undefined},
+ Arg=A#arg{name=N++"Len", where=both, type=Type},
+ [Arg|pre_marshal(R)];
pre_marshal([#arg{name=N,in=true,type=#type{base=binary}}|R]) ->
w(" send_bin(~s),~n", [erl_arg_name(N)]),
pre_marshal(R);
@@ -530,8 +545,18 @@ pre_marshal([#arg{name=N,type=#type{base=memory}}|R]) ->
pre_marshal(R);
pre_marshal([A=#arg{name=N,type=#type{base=string,single=list}}|R]) ->
%% With null terminations
- w(" ~sTemp = list_to_binary([[Str|[0]] || Str <- ~s ]),~n",
+ w(" ~sTemp = list_to_binary([[Str|[0]] || Str <- ~s ]),~n",
[erl_arg_name(N), erl_arg_name(N)]),
+ w(" ~sLen = length(~s),~n",[erl_arg_name(N), erl_arg_name(N)]),
+ [A|pre_marshal(R)];
+pre_marshal([A=#arg{name=N,type=#type{base=string,single=true,ref={pointer,1}}}|R]) ->
+ w(" ~sLen = length(~s),~n",[erl_arg_name(N), erl_arg_name(N)]),
+ [A|pre_marshal(R)];
+pre_marshal([A=#arg{name=N,type=#type{single=list}}|R]) ->
+ w(" ~sLen = length(~s),~n",[erl_arg_name(N), erl_arg_name(N)]),
+ [A|pre_marshal(R)];
+pre_marshal([A=#arg{name=N,type=#type{single={tuple_list,_}}}|R]) ->
+ w(" ~sLen = length(~s),~n",[erl_arg_name(N), erl_arg_name(N)]),
[A|pre_marshal(R)];
pre_marshal([A|R]) ->
[A|pre_marshal(R)];
@@ -583,9 +608,9 @@ marshal_arg(#type{size=BSz,name=Type,single={tuple,matrix12}},Name,A0) ->
align(BSz,16,A0,All);
marshal_arg(#type{size=Sz,name=Type,base=Base,single=list},Name,A0)
- when Base =:= float; Base =:= int ->
+ when Base =:= float; Base =:= int; Base =:= guard_int ->
KeepA = case Sz of 8 -> "0:32,"; _ -> "" end,
- Str0 = "(length("++Name++")):?GLuint,"++KeepA++"\n"
+ Str0 = Name++"Len:?GLuint,"++KeepA++"\n"
" (<< <<C:?"++Type++">> || C <- "++Name++">>)/binary",
{Str,Align} = align(max([Sz,4]),A0,Str0),
align_after(Sz,Align,0,1,Name,Str);
@@ -606,7 +631,7 @@ marshal_arg(#type{base=string,single=true,ref={pointer,1}},Name,A0) ->
marshal_arg(#type{base=string,single=list,ref={pointer,2}},Name,A0) ->
Str0 =
- "(length("++Name++")):?GLuint,"
+ Name++"Len:?GLuint,"
"(size("++Name ++ "Temp)):?GLuint,"
"(" ++ Name ++ "Temp)/binary",
{Str,A} = align(4,A0,Str0),
@@ -620,7 +645,7 @@ marshal_arg(#type{size=Sz,name=Type,single={tuple_list,TSz}},Name,A0) ->
TBin = args(fun(ElName) -> ElName ++ ":?" ++ Type end, ",", Names),
KeepA = case Sz of 8 -> "0:32,"; 4 -> "" end,
- Str0 = "(length("++Name++")):?GLuint,"++KeepA++"\n"
+ Str0 = Name++"Len:?GLuint,"++KeepA++"\n"
" (<< <<"++TBin++">> || {"++TTup++"} <- "++Name++">>)/binary",
align(Sz,A0,Str0);
@@ -656,19 +681,19 @@ align(8,_,7,Str) -> {"0:8," ++Str, 0}.
align_after(8,0,_Add,_Multiplier,_Name,Str) -> {Str,0};
align_after(4,0,Add,Mult,Name,Str) ->
Extra = extra_align(Add,Mult),
- Align = ",0:(((length("++Name++")"++Extra++") rem 2)*32)",
+ Align = ",0:((("++Name++"Len"++Extra++") rem 2)*32)",
{Str ++ Align,0};
align_after(4,4,Add,Mult,Name,Str) ->
Extra = extra_align(Add,Mult),
- Align = ",0:(((1+length("++Name++")"++Extra++") rem 2)*32)",
+ Align = ",0:(((1+"++Name++"Len"++Extra++") rem 2)*32)",
{Str ++ Align,0};
align_after(2,A,Add,Mult,Name,Str) when (A rem 2) =:= 0 ->
Extra = extra_align(A+Add*2,Mult),
- Align = ",0:((8-((length("++Name++")*2"++Extra++") rem 8)) rem 8)",
+ Align = ",0:((8-(("++Name++"Len*2"++Extra++") rem 8)) rem 8)",
{Str ++ Align,0};
align_after(1,A,Add,Mult,Name,Str) ->
Extra = extra_align(A+Add,Mult),
- Align = ",0:((8-((length("++Name++")"++Extra++") rem 8)) rem 8)",
+ Align = ",0:((8-(("++Name++"Len"++Extra++") rem 8)) rem 8)",
{Str ++ Align,0};
align_after(Sz,A,Add,Mult,Name,Str) ->
io:format("~p ~p with ~p ~p ~s~n, ~p", [Sz,A,Add,Mult,Name,Str]),
diff --git a/lib/wx/api_gen/glapi.conf b/lib/wx/api_gen/glapi.conf
index 59fa8f7727..57f30a3f7e 100644
--- a/lib/wx/api_gen/glapi.conf
+++ b/lib/wx/api_gen/glapi.conf
@@ -181,9 +181,9 @@
{"glLoadTransposeMatrix", {"m", {single,{tuple,matrix}}}}.
{"glMultTransposeMatrix", {"m",{single,{tuple,matrix}}}}.
-{"glMultiDrawArrays", [{"first", [in,{single,list}]},
- {"count", [in,{single,list}]},
- {"primcount", {c_only, {length,"first"}}}]}.
+{"glMultiDrawArrays", [{"first", [in,{single,list}, list_binary]},
+ {"count", [in,{single,list}, list_binary]},
+ {"primcount", {c_only, {length,"count"}}}]}.
{"glGenQueries", {"ids", {single,{list, "n", "n"}}}}.
{"glGetQueryiv", {"params", {single, true}}}.
diff --git a/lib/wx/api_gen/wx_doxygen.conf b/lib/wx/api_gen/wx_doxygen.conf
index f4d3c99ec0..a96db00254 100644
--- a/lib/wx/api_gen/wx_doxygen.conf
+++ b/lib/wx/api_gen/wx_doxygen.conf
@@ -252,7 +252,7 @@ PREDEFINED = \
wxUSE_POPUPWIN=1 \
wxUSE_SYSTEM_OPTIONS=1 \
wxUSE_INTL=1 \
- wxABI_VERSION=20809 \
+ wxABI_VERSION=20812 \
__WXGTK24__=1 \
__WXGTK20__=1 \
__WXGTK__=1 \
diff --git a/lib/wx/api_gen/wx_extra/added_func.h b/lib/wx/api_gen/wx_extra/added_func.h
index 417188cc8a..bffe391140 100644
--- a/lib/wx/api_gen/wx_extra/added_func.h
+++ b/lib/wx/api_gen/wx_extra/added_func.h
@@ -34,8 +34,13 @@ class wxToolBar {
};
-class wxWindow {
+/* class wxWindow { */
+/* public: */
+/* bool IsDoubleBuffered(); */
+/* void SetDoubleBuffered(bool on); */
+/* }; */
+
+class wxWindowGTK {
public:
- bool IsDoubleBuffered();
- void SetDoubleBuffered(bool on);
+ double GetContentScaleFactor();
};
diff --git a/lib/wx/api_gen/wx_extra/wxListCtrl.erl b/lib/wx/api_gen/wx_extra/wxListCtrl.erl
index acdb69fdeb..355a4cdfd1 100644
--- a/lib/wx/api_gen/wx_extra/wxListCtrl.erl
+++ b/lib/wx/api_gen/wx_extra/wxListCtrl.erl
@@ -43,33 +43,36 @@ SortItems>>
<<EXPORT:wxListCtrl new/0, new/1, new/2 wxListCtrl:EXPORT>>
<<wxListCtrl_new_0
-%% @spec () -> wxListCtrl()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new() -> wxListCtrl().
new() ->
wxe_util:construct(~s, <<>>).
wxListCtrl_new_0>>
<<wxListCtrl_new_2
-%% @spec (Parent::wxWindow:wxWindow()) -> wxListCtrl()
-%% @equiv new(Parent, [])
+-spec new(Parent) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow().
new(Parent)
when is_record(Parent, wx_ref) ->
new(Parent, []).
-%% @spec (Parent::wxWindow:wxWindow(), [Option]) -> wxListCtrl()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
+%% @doc Creates a listctrl with optional callback functions:
%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
+%% OnGetItemText = (This, Item, Column) -> unicode:charlist()
+%% OnGetItemAttr = (This, Item) -> wxListItemAttr:wxListItemAttr()
%% OnGetItemColumnImage = (This, Item, Column) -> integer()
-%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+%%
+%% See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new(Parent, [Option]) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options)->
@@ -101,26 +104,27 @@ wxListCtrl_new_2>>
<<EXPORT:Create create/2, create/3 Create:EXPORT>>
<<Create
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow()) -> bool()
%% @equiv create(This,Parent, [])
+-spec create(This, Parent) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow().
create(This,Parent)
when is_record(This, wx_ref),is_record(Parent, wx_ref) ->
create(This,Parent, []).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow(), [Option]) -> bool()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
-%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
-%% OnGetItemColumnImage = (This, Item, Column) -> integer()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlcreate">external documentation</a>.
+-spec create(This, Parent, [Option]) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
+
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
diff --git a/lib/wx/api_gen/wx_extra/wxXmlResource.erl b/lib/wx/api_gen/wx_extra/wxXmlResource.erl
index 7700e2333e..b29ffba7c6 100644
--- a/lib/wx/api_gen/wx_extra/wxXmlResource.erl
+++ b/lib/wx/api_gen/wx_extra/wxXmlResource.erl
@@ -21,8 +21,6 @@
<<EXPORT:xrcctrl xrcctrl/3 xrcctrl:EXPORT>>
<<xrcctrl
-%% @spec (Window::wxWindow:wxWindow(),Name::string(), Type::atom()) -> wx:wxObject()
-
%% @doc Looks up a control with Name in a window created with XML
%% resources. You can use it to set/get values from controls.
%% The object is type casted to <b>Type</b>.
@@ -32,6 +30,10 @@
%% true = wxXmlResource:loadDialog(Xrc, Dlg, Frame, "controls_dialog"), <br />
%% LCtrl = xrcctrl(Dlg, "controls_listctrl", wxListCtrl), <br />
%% wxListCtrl:insertColumn(LCtrl, 0, "Name", [{width, 200}]), <br />
+-spec xrcctrl(Window, Name, Type) -> wx:wx_object() when
+ Window::wxWindow:wxWindow(),
+ Name::string(),
+ Type::atom().
xrcctrl(Window = #wx_ref{}, Name, Type) when is_list(Name), is_atom(Type) ->
%% Func Id ~s
diff --git a/lib/wx/api_gen/wx_gen.erl b/lib/wx/api_gen/wx_gen.erl
index cfae2197f8..6979a600f3 100644
--- a/lib/wx/api_gen/wx_gen.erl
+++ b/lib/wx/api_gen/wx_gen.erl
@@ -271,33 +271,31 @@ parse_attr1([{{attr,_}, #xmlElement{content=C, attributes=Attrs}}|R], AttrList0,
#param{where=nowhere} ->
parse_attr1(R,AttrList0,Opts,Res);
_ ->
- case keysearch(prot, #xmlAttribute.name, Attrs) of
- {value, #xmlAttribute{value = "public"}} ->
- {Acc,AttrList} = attr_acc(Param0, AttrList0),
- parse_attr1(R,AttrList,Opts,
- [Param0#param{in=false,prot=public,acc=Acc}|Res]);
- {value, #xmlAttribute{value = "protected"}} ->
- {Acc,AttrList} = attr_acc(Param0, AttrList0),
- parse_attr1(R,AttrList,Opts,
- [Param0#param{in=false,prot=protected,acc=Acc}|Res]);
- {value, #xmlAttribute{value = "private"}} ->
- {Acc,AttrList} = attr_acc(Param0, AttrList0),
- parse_attr1(R,AttrList,Opts,
- [Param0#param{in=false,prot=private,acc=Acc}|Res])
- end
+ {value, #xmlAttribute{value=Type}} =
+ keysearch(prot, #xmlAttribute.name, Attrs),
+ {Param,AttrList} = attr_acc(Param0, list_to_atom(Type), AttrList0),
+ parse_attr1(R,AttrList,Opts,[Param|Res])
end;
parse_attr1([{_Id,_}|R],AttrList,Info, Res) ->
parse_attr1(R,AttrList,Info, Res);
parse_attr1([],Left,_, Res) ->
{reverse(Res), Left}.
-attr_acc(#param{name=N}, List) ->
+attr_acc(#param{name=N}=P, Type, List) ->
Name = list_to_atom(N),
case get_value(Name, List, undefined) of
- undefined -> {undefined, List};
- Val -> {Val, lists:keydelete(Name,1,List)}
+ undefined -> {P#param{in=false,prot=Type,acc=undefined}, List};
+ Val when is_list(Val), is_integer(hd(Val)) ->
+ %% Function String
+ {P#param{in=false,prot=Type,acc=Val}, lists:keydelete(Name,1,List)};
+ OptList when is_list(OptList) ->
+ Param = foldl(fun handle_param_opt/2,P,OptList),
+ {Param#param{in=false,prot=Type,acc=undefined},
+ lists:keydelete(Name,1,List)};
+ Val ->
+ {P#param{in=false,prot=Type,acc=Val}, lists:keydelete(Name,1,List)}
end.
-
+
load_members(FileName, Class, Defs, Tab, Type,Opts) ->
File = filename:join(["wx_xml",FileName ++ ".xml"]),
put({loaded, FileName}, true),
diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl
index 84d3990786..d4b6db8153 100644
--- a/lib/wx/api_gen/wx_gen_cpp.erl
+++ b/lib/wx/api_gen/wx_gen_cpp.erl
@@ -1079,6 +1079,13 @@ build_ret(Name,_,#type{base=string,single=true}) ->
w(" rt.add(~s);~n",[Name]);
build_ret(Name,_,#type{name="wxArrayString", single=array}) ->
w(" rt.add(~s);~n", [Name]);
+build_ret(Name,_,#type{name="wxString", single={list,Variable}}) ->
+ Obj = case Name of
+ "ev->" ++ _ -> "ev";
+ _ -> "This"
+ end,
+ w(" wxArrayString tmpArrayStr(~s->~s, ~s);~n", [Obj,Variable,Name]),
+ w(" rt.add(tmpArrayStr);~n", []);
build_ret(Name,In,T) ->
?error({nyi, Name,In, T}).
@@ -1309,7 +1316,8 @@ encode_events(Evs) ->
w(" } else {~n"),
w(" send_res = rt.send();~n"),
w(" if(cb->skip) event->Skip();~n"),
- w(" if(app->recurse_level < 1) {~n"),
+ #class{id=MouseId} = lists:keyfind("wxMouseEvent", #class.name, Evs),
+ w(" if(app->recurse_level < 1 && Etype->cID != ~p) {~n", [MouseId]),
w(" app->recurse_level++;~n"),
w(" app->dispatch_cmds();~n"),
w(" app->recurse_level--;~n"),
diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl
index 794de25002..e272c08d90 100644
--- a/lib/wx/api_gen/wx_gen_erl.erl
+++ b/lib/wx/api_gen/wx_gen_erl.erl
@@ -649,7 +649,7 @@ guard_test(#param{def=Def}) when Def =/= none -> skip;
guard_test(#param{where=c}) -> skip;
guard_test(#param{in=In}) when In == false -> skip;
guard_test(#param{name=N, type=#type{base=string}}) ->
- "is_list(" ++ erl_arg_name(N) ++")";
+ "?is_chardata(" ++ erl_arg_name(N) ++")";
guard_test(#param{name=N, type=#type{name="wxArtClient"}}) ->
"is_list(" ++ erl_arg_name(N) ++")";
guard_test(#param{name=N, type=#type{name="wxArrayString"}}) ->
@@ -801,8 +801,13 @@ doc_arg_type(_, _) -> skip.
doc_arg_type2(T) ->
doc_arg_type2(T, in).
-doc_arg_type2(T=#type{single=Single}, Out) when Single =:= array; Single =:= list ->
- "[" ++ doc_arg_type3(T, Out) ++ "]";
+doc_arg_type2(T=#type{single=Single}, Out) ->
+ case Single of
+ array -> "[" ++ doc_arg_type3(T, Out) ++ "]";
+ list -> "[" ++ doc_arg_type3(T, Out) ++ "]";
+ {list, _} -> "[" ++ doc_arg_type3(T, Out) ++ "]";
+ true -> doc_arg_type3(T, Out)
+ end;
doc_arg_type2(T, Out) ->
doc_arg_type3(T, Out).
@@ -1207,7 +1212,7 @@ gen_event_recs() ->
w("-type wx() :: #wx{}. %% wx event record ~n",[]),
w("%% Here comes the definitions of all event records.~n"
"%% they contain the event type and possible some extra information.~n~n",[]),
- Events = [build_event_rec(C) || {_,C=#class{event=Evs}} <- get(), Evs =/= false],
+ Events = [build_event_rec(C) || {_,C=#class{event=Evs}} <- lists:sort(get()), Evs =/= false],
EventSubTypes = [Type || {_Rec, Type} <- Events],
EventRecs = [Rec || {Rec, _Type} <- Events],
w("-type event() :: ~s.~n",
diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf
index 786e536f93..a0dfa61dd1 100644
--- a/lib/wx/api_gen/wxapi.conf
+++ b/lib/wx/api_gen/wxapi.conf
@@ -104,7 +104,8 @@
'Center','CenterOnParent','Centre','CentreOnParent','ClearBackground',
{'ClientToScreen',[{"x",both},{"y",both}]}, 'Close',
'ConvertDialogToPixels','ConvertPixelsToDialog','Destroy','DestroyChildren',
- 'Disable',%%'DoGetBestSize','DoUpdateWindowUI', 'DragAcceptFiles',
+ 'Disable',%%'DoGetBestSize','DoUpdateWindowUI',
+ {'DragAcceptFiles', [{test_if, "wxCHECK_VERSION(2,8,10)"}]},
'Enable',
'FindFocus','FindWindow','FindWindowById','FindWindowByName','FindWindowByLabel',
'Fit','FitInside','Freeze', 'GetAcceleratorTable', % 'GetAccessible',
@@ -154,7 +155,8 @@
{'SetTransparent', [{test_if, "wxCHECK_VERSION(2,8,12)"}]},
{'CanSetTransparent', [{test_if, "wxCHECK_VERSION(2,8,12)"}]},
{'IsDoubleBuffered', [{test_if, "wxCHECK_VERSION(3,0,0)"}]},
- {'SetDoubleBuffered', [{test_if, "wxCHECK_VERSION(3,0,0) && !defined(__WXMAC__)"}]}
+ {'SetDoubleBuffered', [{test_if, "wxCHECK_VERSION(3,0,0) && !defined(__WXMAC__)"}]},
+ {'GetContentScaleFactor', [{test_if, "wxCHECK_VERSION(2,9,5)"}]}
]}.
{class, wxTopLevelWindowGTK, wxWindow,
@@ -1490,8 +1492,6 @@
['ButtonDown','ButtonIsDown','ButtonUp','GetButtonChange','GetButtonState',
'GetJoystick','GetPosition','GetZPosition','IsButton','IsMove','IsZMove']}.
-%% {class, wxDropFilesEvent, wxEvent, [{event,[wxEVT_DROP_FILES]}],[]}. %FIXME
-
{enum, wxUpdateUIMode, "wxUPDATE_UI_"}.
{class, wxUpdateUIEvent, wxCommandEvent,
@@ -1988,3 +1988,10 @@
{class, wxDCOverlay, root, [],
['wxDCOverlay', '~wxDCOverlay', 'Clear']}.
+
+{class, wxDropFilesEvent, wxEvent,
+ [{acc, [{m_files, [{single, {list, 'm_noFiles'}}]}]},
+ {event,[wxEVT_DROP_FILES]}],
+ ['GetPosition', 'GetNumberOfFiles',
+ {'GetFiles', [{return, [{single, {list, 'm_noFiles'}}]}]}
+ ]}.
diff --git a/lib/wx/c_src/gen/gl_funcs.cpp b/lib/wx/c_src/gen/gl_funcs.cpp
index 4dd3ad44ef..bc8d5bbdca 100644
--- a/lib/wx/c_src/gen/gl_funcs.cpp
+++ b/lib/wx/c_src/gen/gl_funcs.cpp
@@ -815,7 +815,7 @@ case 5107: { // glCallList
}; break;
case 5108: { // glCallLists
int * listsLen = (int *) bp; bp += 4;
- GLuint * lists = (GLuint *) bp; bp += (8-((*listsLen*4+4)%8))%8;
+ GLuint * lists = (GLuint *) bp; bp += *listsLen*4 + (8-((*listsLen*4+4)%8))%8;
weglCallLists(*listsLen,GL_UNSIGNED_INT,lists);
}; break;
case 5109: { // glListBase
@@ -1866,7 +1866,7 @@ case 5271: { // glGenTextures
}; break;
case 5272: { // glDeleteTextures
int * texturesLen = (int *) bp; bp += 4;
- GLuint * textures = (GLuint *) bp; bp += (8-((*texturesLen*4+4)%8))%8;
+ GLuint * textures = (GLuint *) bp; bp += *texturesLen*4 + (8-((*texturesLen*4+4)%8))%8;
weglDeleteTextures(*texturesLen,textures);
}; break;
case 5273: { // glBindTexture
@@ -1876,14 +1876,14 @@ case 5273: { // glBindTexture
}; break;
case 5274: { // glPrioritizeTextures
int * texturesLen = (int *) bp; bp += 4;
- GLuint * textures = (GLuint *) bp; bp += (8-((*texturesLen*4+4)%8))%8;
+ GLuint * textures = (GLuint *) bp; bp += *texturesLen*4 + (8-((*texturesLen*4+4)%8))%8;
int * prioritiesLen = (int *) bp; bp += 4;
- GLclampf * priorities = (GLclampf *) bp; bp += (8-((*prioritiesLen*4+4)%8))%8;
+ GLclampf * priorities = (GLclampf *) bp; bp += *prioritiesLen*4 + (8-((*prioritiesLen*4+4)%8))%8;
weglPrioritizeTextures(*texturesLen,textures,priorities);
}; break;
case 5275: { // glAreTexturesResident
int * texturesLen = (int *) bp; bp += 4;
- GLuint * textures = (GLuint *) bp; bp += (8-((*texturesLen*4+4)%8))%8;
+ GLuint * textures = (GLuint *) bp; bp += *texturesLen*4 + (8-((*texturesLen*4+4)%8))%8;
GLboolean *residences;
residences = (GLboolean *) driver_alloc(sizeof(GLboolean) * *texturesLen);
GLboolean result = weglAreTexturesResident(*texturesLen,textures,residences);
@@ -2921,132 +2921,140 @@ case 5394: { // glBlendFuncSeparate
case 5395: { // glMultiDrawArrays
GLenum *mode = (GLenum *) bp; bp += 4;
int * firstLen = (int *) bp; bp += 4;
- GLint * first = (GLint *) bp; bp += (8-((*firstLen*4+0)%8))%8;
+ GLint * first = (GLint *) bp; bp += *firstLen*4 + (8-((*firstLen*4+0)%8))%8;
int * countLen = (int *) bp; bp += 4;
- GLsizei * count = (GLsizei *) bp; bp += (8-((*countLen*4+4)%8))%8;
- weglMultiDrawArrays(*mode,first,count,*firstLen);
+ GLsizei * count = (GLsizei *) bp; bp += *countLen*4 + (8-((*countLen*4+4)%8))%8;
+ weglMultiDrawArrays(*mode,first,count,*countLen);
}; break;
-case 5396: { // glPointParameterf
+case 5396: { // glMultiDrawArrays
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLint *first = (GLint *) bins[0];
+ int * firstLen = (int *) bp; bp += 4; (void) firstLen;
+ GLsizei *count = (GLsizei *) bins[1];
+ int * countLen = (int *) bp; bp += 4; (void) countLen;
+ weglMultiDrawArrays(*mode,first,count,*countLen);
+}; break;
+case 5397: { // glPointParameterf
GLenum *pname = (GLenum *) bp; bp += 4;
GLfloat *param = (GLfloat *) bp; bp += 4;
weglPointParameterf(*pname,*param);
}; break;
-case 5397: { // glPointParameterfv
+case 5398: { // glPointParameterfv
GLenum *pname = (GLenum *) bp; bp += 4;
int *paramsLen = (int *) bp; bp += 4;
GLfloat *params = (GLfloat *) bp; bp += *paramsLen*4+((*paramsLen)+0)%2*4;
weglPointParameterfv(*pname,params);
}; break;
-case 5398: { // glPointParameteri
+case 5399: { // glPointParameteri
GLenum *pname = (GLenum *) bp; bp += 4;
GLint *param = (GLint *) bp; bp += 4;
weglPointParameteri(*pname,*param);
}; break;
-case 5399: { // glPointParameteriv
+case 5400: { // glPointParameteriv
GLenum *pname = (GLenum *) bp; bp += 4;
int *paramsLen = (int *) bp; bp += 4;
GLint *params = (GLint *) bp; bp += *paramsLen*4+((*paramsLen)+0)%2*4;
weglPointParameteriv(*pname,params);
}; break;
-case 5400: { // glFogCoordfv
+case 5401: { // glFogCoordfv
GLfloat *coord = (GLfloat *) bp; bp += 4;
weglFogCoordfv(coord);
}; break;
-case 5401: { // glFogCoorddv
+case 5402: { // glFogCoorddv
GLdouble *coord = (GLdouble *) bp; bp += 8;
weglFogCoorddv(coord);
}; break;
-case 5402: { // glFogCoordPointer
+case 5403: { // glFogCoordPointer
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
GLvoid *pointer = (GLvoid *) (ErlDrvSInt) * (int *) bp; bp += 4;
weglFogCoordPointer(*type,*stride,pointer);
}; break;
-case 5403: { // glFogCoordPointer
+case 5404: { // glFogCoordPointer
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
GLvoid *pointer = (GLvoid *) bins[0];
weglFogCoordPointer(*type,*stride,pointer);
}; break;
-case 5404: { // glSecondaryColor3bv
+case 5405: { // glSecondaryColor3bv
GLbyte *v = (GLbyte *) bp; bp += 1;
weglSecondaryColor3bv(v);
}; break;
-case 5405: { // glSecondaryColor3dv
+case 5406: { // glSecondaryColor3dv
GLdouble *v = (GLdouble *) bp; bp += 8;
weglSecondaryColor3dv(v);
}; break;
-case 5406: { // glSecondaryColor3fv
+case 5407: { // glSecondaryColor3fv
GLfloat *v = (GLfloat *) bp; bp += 4;
weglSecondaryColor3fv(v);
}; break;
-case 5407: { // glSecondaryColor3iv
+case 5408: { // glSecondaryColor3iv
GLint *v = (GLint *) bp; bp += 4;
weglSecondaryColor3iv(v);
}; break;
-case 5408: { // glSecondaryColor3sv
+case 5409: { // glSecondaryColor3sv
GLshort *v = (GLshort *) bp; bp += 2;
weglSecondaryColor3sv(v);
}; break;
-case 5409: { // glSecondaryColor3ubv
+case 5410: { // glSecondaryColor3ubv
GLubyte *v = (GLubyte *) bp; bp += 1;
weglSecondaryColor3ubv(v);
}; break;
-case 5410: { // glSecondaryColor3uiv
+case 5411: { // glSecondaryColor3uiv
GLuint *v = (GLuint *) bp; bp += 4;
weglSecondaryColor3uiv(v);
}; break;
-case 5411: { // glSecondaryColor3usv
+case 5412: { // glSecondaryColor3usv
GLushort *v = (GLushort *) bp; bp += 2;
weglSecondaryColor3usv(v);
}; break;
-case 5412: { // glSecondaryColorPointer
+case 5413: { // glSecondaryColorPointer
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
GLvoid *pointer = (GLvoid *) (ErlDrvSInt) * (int *) bp; bp += 4;
weglSecondaryColorPointer(*size,*type,*stride,pointer);
}; break;
-case 5413: { // glSecondaryColorPointer
+case 5414: { // glSecondaryColorPointer
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
GLvoid *pointer = (GLvoid *) bins[0];
weglSecondaryColorPointer(*size,*type,*stride,pointer);
}; break;
-case 5414: { // glWindowPos2dv
+case 5415: { // glWindowPos2dv
GLdouble *v = (GLdouble *) bp; bp += 8;
weglWindowPos2dv(v);
}; break;
-case 5415: { // glWindowPos2fv
+case 5416: { // glWindowPos2fv
GLfloat *v = (GLfloat *) bp; bp += 4;
weglWindowPos2fv(v);
}; break;
-case 5416: { // glWindowPos2iv
+case 5417: { // glWindowPos2iv
GLint *v = (GLint *) bp; bp += 4;
weglWindowPos2iv(v);
}; break;
-case 5417: { // glWindowPos2sv
+case 5418: { // glWindowPos2sv
GLshort *v = (GLshort *) bp; bp += 2;
weglWindowPos2sv(v);
}; break;
-case 5418: { // glWindowPos3dv
+case 5419: { // glWindowPos3dv
GLdouble *v = (GLdouble *) bp; bp += 8;
weglWindowPos3dv(v);
}; break;
-case 5419: { // glWindowPos3fv
+case 5420: { // glWindowPos3fv
GLfloat *v = (GLfloat *) bp; bp += 4;
weglWindowPos3fv(v);
}; break;
-case 5420: { // glWindowPos3iv
+case 5421: { // glWindowPos3iv
GLint *v = (GLint *) bp; bp += 4;
weglWindowPos3iv(v);
}; break;
-case 5421: { // glWindowPos3sv
+case 5422: { // glWindowPos3sv
GLshort *v = (GLshort *) bp; bp += 2;
weglWindowPos3sv(v);
}; break;
-case 5422: { // glGenQueries
+case 5423: { // glGenQueries
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *ids;
ids = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
@@ -3062,12 +3070,12 @@ case 5422: { // glGenQueries
driver_free(rt);
driver_free(ids);
}; break;
-case 5423: { // glDeleteQueries
+case 5424: { // glDeleteQueries
int * idsLen = (int *) bp; bp += 4;
- GLuint * ids = (GLuint *) bp; bp += (8-((*idsLen*4+4)%8))%8;
+ GLuint * ids = (GLuint *) bp; bp += *idsLen*4 + (8-((*idsLen*4+4)%8))%8;
weglDeleteQueries(*idsLen,ids);
}; break;
-case 5424: { // glIsQuery
+case 5425: { // glIsQuery
GLuint *id = (GLuint *) bp; bp += 4;
GLboolean result = weglIsQuery(*id);
int AP = 0; ErlDrvTermData rt[6];
@@ -3076,16 +3084,16 @@ case 5424: { // glIsQuery
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5425: { // glBeginQuery
+case 5426: { // glBeginQuery
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *id = (GLuint *) bp; bp += 4;
weglBeginQuery(*target,*id);
}; break;
-case 5426: { // glEndQuery
+case 5427: { // glEndQuery
GLenum *target = (GLenum *) bp; bp += 4;
weglEndQuery(*target);
}; break;
-case 5427: { // glGetQueryiv
+case 5428: { // glGetQueryiv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
@@ -3096,7 +3104,7 @@ case 5427: { // glGetQueryiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5428: { // glGetQueryObjectiv
+case 5429: { // glGetQueryObjectiv
GLuint *id = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
@@ -3107,7 +3115,7 @@ case 5428: { // glGetQueryObjectiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5429: { // glGetQueryObjectuiv
+case 5430: { // glGetQueryObjectuiv
GLuint *id = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLuint params[1] = {0};
@@ -3118,17 +3126,17 @@ case 5429: { // glGetQueryObjectuiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5430: { // glBindBuffer
+case 5431: { // glBindBuffer
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *buffer = (GLuint *) bp; bp += 4;
weglBindBuffer(*target,*buffer);
}; break;
-case 5431: { // glDeleteBuffers
+case 5432: { // glDeleteBuffers
int * buffersLen = (int *) bp; bp += 4;
- GLuint * buffers = (GLuint *) bp; bp += (8-((*buffersLen*4+4)%8))%8;
+ GLuint * buffers = (GLuint *) bp; bp += *buffersLen*4 + (8-((*buffersLen*4+4)%8))%8;
weglDeleteBuffers(*buffersLen,buffers);
}; break;
-case 5432: { // glGenBuffers
+case 5433: { // glGenBuffers
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *buffers;
buffers = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
@@ -3144,7 +3152,7 @@ case 5432: { // glGenBuffers
driver_free(rt);
driver_free(buffers);
}; break;
-case 5433: { // glIsBuffer
+case 5434: { // glIsBuffer
GLuint *buffer = (GLuint *) bp; bp += 4;
GLboolean result = weglIsBuffer(*buffer);
int AP = 0; ErlDrvTermData rt[6];
@@ -3153,7 +3161,7 @@ case 5433: { // glIsBuffer
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5434: { // glBufferData
+case 5435: { // glBufferData
GLenum *target = (GLenum *) bp; bp += 4;
bp += 4;
GLsizeiptr size = (GLsizeiptr) * (GLuint64EXT *) bp; bp += 8;
@@ -3161,7 +3169,7 @@ case 5434: { // glBufferData
GLenum *usage = (GLenum *) bp; bp += 4;
weglBufferData(*target,size,data,*usage);
}; break;
-case 5435: { // glBufferData
+case 5436: { // glBufferData
GLenum *target = (GLenum *) bp; bp += 4;
bp += 4;
GLsizeiptr size = (GLsizeiptr) * (GLuint64EXT *) bp; bp += 8;
@@ -3169,7 +3177,7 @@ case 5435: { // glBufferData
GLenum *usage = (GLenum *) bp; bp += 4;
weglBufferData(*target,size,data,*usage);
}; break;
-case 5436: { // glBufferSubData
+case 5437: { // glBufferSubData
GLenum *target = (GLenum *) bp; bp += 4;
bp += 4;
GLintptr offset = (GLintptr) * (GLuint64EXT *) bp; bp += 8;
@@ -3177,7 +3185,7 @@ case 5436: { // glBufferSubData
GLvoid *data = (GLvoid *) (ErlDrvSInt) * (int *) bp; bp += 4;
weglBufferSubData(*target,offset,size,data);
}; break;
-case 5437: { // glBufferSubData
+case 5438: { // glBufferSubData
GLenum *target = (GLenum *) bp; bp += 4;
bp += 4;
GLintptr offset = (GLintptr) * (GLuint64EXT *) bp; bp += 8;
@@ -3185,7 +3193,7 @@ case 5437: { // glBufferSubData
GLvoid *data = (GLvoid *) bins[0];
weglBufferSubData(*target,offset,size,data);
}; break;
-case 5438: { // glGetBufferSubData
+case 5439: { // glGetBufferSubData
GLenum *target = (GLenum *) bp; bp += 4;
bp += 4;
GLintptr offset = (GLintptr) * (GLuint64EXT *) bp; bp += 8;
@@ -3198,7 +3206,7 @@ case 5438: { // glGetBufferSubData
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5439: { // glGetBufferParameteriv
+case 5440: { // glGetBufferParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
@@ -3209,52 +3217,52 @@ case 5439: { // glGetBufferParameteriv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5440: { // glBlendEquationSeparate
+case 5441: { // glBlendEquationSeparate
GLenum *modeRGB = (GLenum *) bp; bp += 4;
GLenum *modeAlpha = (GLenum *) bp; bp += 4;
weglBlendEquationSeparate(*modeRGB,*modeAlpha);
}; break;
-case 5441: { // glDrawBuffers
+case 5442: { // glDrawBuffers
int * bufsLen = (int *) bp; bp += 4;
- GLenum * bufs = (GLenum *) bp; bp += (8-((*bufsLen*4+4)%8))%8;
+ GLenum * bufs = (GLenum *) bp; bp += *bufsLen*4 + (8-((*bufsLen*4+4)%8))%8;
weglDrawBuffers(*bufsLen,bufs);
}; break;
-case 5442: { // glStencilOpSeparate
+case 5443: { // glStencilOpSeparate
GLenum *face = (GLenum *) bp; bp += 4;
GLenum *sfail = (GLenum *) bp; bp += 4;
GLenum *dpfail = (GLenum *) bp; bp += 4;
GLenum *dppass = (GLenum *) bp; bp += 4;
weglStencilOpSeparate(*face,*sfail,*dpfail,*dppass);
}; break;
-case 5443: { // glStencilFuncSeparate
+case 5444: { // glStencilFuncSeparate
GLenum *face = (GLenum *) bp; bp += 4;
GLenum *func = (GLenum *) bp; bp += 4;
GLint *ref = (GLint *) bp; bp += 4;
GLuint *mask = (GLuint *) bp; bp += 4;
weglStencilFuncSeparate(*face,*func,*ref,*mask);
}; break;
-case 5444: { // glStencilMaskSeparate
+case 5445: { // glStencilMaskSeparate
GLenum *face = (GLenum *) bp; bp += 4;
GLuint *mask = (GLuint *) bp; bp += 4;
weglStencilMaskSeparate(*face,*mask);
}; break;
-case 5445: { // glAttachShader
+case 5446: { // glAttachShader
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *shader = (GLuint *) bp; bp += 4;
weglAttachShader(*program,*shader);
}; break;
-case 5446: { // glBindAttribLocation
+case 5447: { // glBindAttribLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
weglBindAttribLocation(*program,*index,name);
}; break;
-case 5447: { // glCompileShader
+case 5448: { // glCompileShader
GLuint *shader = (GLuint *) bp; bp += 4;
weglCompileShader(*shader);
}; break;
-case 5448: { // glCreateProgram
+case 5449: { // glCreateProgram
GLuint result = weglCreateProgram();
int AP = 0; ErlDrvTermData rt[6];
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
@@ -3262,7 +3270,7 @@ case 5448: { // glCreateProgram
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5449: { // glCreateShader
+case 5450: { // glCreateShader
GLenum *type = (GLenum *) bp; bp += 4;
GLuint result = weglCreateShader(*type);
int AP = 0; ErlDrvTermData rt[6];
@@ -3271,28 +3279,28 @@ case 5449: { // glCreateShader
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5450: { // glDeleteProgram
+case 5451: { // glDeleteProgram
GLuint *program = (GLuint *) bp; bp += 4;
weglDeleteProgram(*program);
}; break;
-case 5451: { // glDeleteShader
+case 5452: { // glDeleteShader
GLuint *shader = (GLuint *) bp; bp += 4;
weglDeleteShader(*shader);
}; break;
-case 5452: { // glDetachShader
+case 5453: { // glDetachShader
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *shader = (GLuint *) bp; bp += 4;
weglDetachShader(*program,*shader);
}; break;
-case 5453: { // glDisableVertexAttribArray
+case 5454: { // glDisableVertexAttribArray
GLuint *index = (GLuint *) bp; bp += 4;
weglDisableVertexAttribArray(*index);
}; break;
-case 5454: { // glEnableVertexAttribArray
+case 5455: { // glEnableVertexAttribArray
GLuint *index = (GLuint *) bp; bp += 4;
weglEnableVertexAttribArray(*index);
}; break;
-case 5455: { // glGetActiveAttrib
+case 5456: { // glGetActiveAttrib
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
@@ -3312,7 +3320,7 @@ case 5455: { // glGetActiveAttrib
driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
-case 5456: { // glGetActiveUniform
+case 5457: { // glGetActiveUniform
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
@@ -3332,7 +3340,7 @@ case 5456: { // glGetActiveUniform
driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
-case 5457: { // glGetAttachedShaders
+case 5458: { // glGetAttachedShaders
GLuint *program = (GLuint *) bp; bp += 4;
GLsizei *maxCount = (GLsizei *) bp; bp += 4;
GLsizei count[1] = {0};
@@ -3350,7 +3358,7 @@ case 5457: { // glGetAttachedShaders
driver_free(rt);
driver_free(obj);
}; break;
-case 5458: { // glGetAttribLocation
+case 5459: { // glGetAttribLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
@@ -3361,7 +3369,7 @@ case 5458: { // glGetAttribLocation
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5459: { // glGetProgramiv
+case 5460: { // glGetProgramiv
GLuint *program = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
@@ -3372,7 +3380,7 @@ case 5459: { // glGetProgramiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5460: { // glGetProgramInfoLog
+case 5461: { // glGetProgramInfoLog
GLuint *program = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
GLsizei length[1] = {0};
@@ -3386,7 +3394,7 @@ case 5460: { // glGetProgramInfoLog
driver_send_term(port,caller,rt,AP);
driver_free(infoLog);
}; break;
-case 5461: { // glGetShaderiv
+case 5462: { // glGetShaderiv
GLuint *shader = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
@@ -3397,7 +3405,7 @@ case 5461: { // glGetShaderiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5462: { // glGetShaderInfoLog
+case 5463: { // glGetShaderInfoLog
GLuint *shader = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
GLsizei length[1] = {0};
@@ -3411,7 +3419,7 @@ case 5462: { // glGetShaderInfoLog
driver_send_term(port,caller,rt,AP);
driver_free(infoLog);
}; break;
-case 5463: { // glGetShaderSource
+case 5464: { // glGetShaderSource
GLuint *shader = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
GLsizei length[1] = {0};
@@ -3425,7 +3433,7 @@ case 5463: { // glGetShaderSource
driver_send_term(port,caller,rt,AP);
driver_free(source);
}; break;
-case 5464: { // glGetUniformLocation
+case 5465: { // glGetUniformLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
@@ -3436,7 +3444,7 @@ case 5464: { // glGetUniformLocation
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5465: { // glGetUniformfv
+case 5466: { // glGetUniformfv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLfloat params[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
@@ -3465,7 +3473,7 @@ case 5465: { // glGetUniformfv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5466: { // glGetUniformiv
+case 5467: { // glGetUniformiv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -3493,7 +3501,7 @@ case 5466: { // glGetUniformiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5467: { // glGetVertexAttribdv
+case 5468: { // glGetVertexAttribdv
GLuint *index = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLdouble params[4] = {0.0,0.0,0.0,0.0};
@@ -3509,7 +3517,7 @@ case 5467: { // glGetVertexAttribdv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5468: { // glGetVertexAttribfv
+case 5469: { // glGetVertexAttribfv
GLuint *index = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLfloat params[4] = {0.0,0.0,0.0,0.0};
@@ -3526,7 +3534,7 @@ case 5468: { // glGetVertexAttribfv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5469: { // glGetVertexAttribiv
+case 5470: { // glGetVertexAttribiv
GLuint *index = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[4] = {0,0,0,0};
@@ -3542,7 +3550,7 @@ case 5469: { // glGetVertexAttribiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5470: { // glIsProgram
+case 5471: { // glIsProgram
GLuint *program = (GLuint *) bp; bp += 4;
GLboolean result = weglIsProgram(*program);
int AP = 0; ErlDrvTermData rt[6];
@@ -3551,7 +3559,7 @@ case 5470: { // glIsProgram
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5471: { // glIsShader
+case 5472: { // glIsShader
GLuint *shader = (GLuint *) bp; bp += 4;
GLboolean result = weglIsShader(*shader);
int AP = 0; ErlDrvTermData rt[6];
@@ -3560,11 +3568,11 @@ case 5471: { // glIsShader
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5472: { // glLinkProgram
+case 5473: { // glLinkProgram
GLuint *program = (GLuint *) bp; bp += 4;
weglLinkProgram(*program);
}; break;
-case 5473: { // glShaderSource
+case 5474: { // glShaderSource
GLuint *shader = (GLuint *) bp; bp += 4;
int * stringLen = (int *) bp; bp += 4;
int * stringTotSize = (int *) bp; bp += 4;
@@ -3576,29 +3584,29 @@ case 5473: { // glShaderSource
weglShaderSource(*shader,*stringLen,(const GLchar **) string,NULL);
driver_free(string);
}; break;
-case 5474: { // glUseProgram
+case 5475: { // glUseProgram
GLuint *program = (GLuint *) bp; bp += 4;
weglUseProgram(*program);
}; break;
-case 5475: { // glUniform1f
+case 5476: { // glUniform1f
GLint *location = (GLint *) bp; bp += 4;
GLfloat *v0 = (GLfloat *) bp; bp += 4;
weglUniform1f(*location,*v0);
}; break;
-case 5476: { // glUniform2f
+case 5477: { // glUniform2f
GLint *location = (GLint *) bp; bp += 4;
GLfloat *v0 = (GLfloat *) bp; bp += 4;
GLfloat *v1 = (GLfloat *) bp; bp += 4;
weglUniform2f(*location,*v0,*v1);
}; break;
-case 5477: { // glUniform3f
+case 5478: { // glUniform3f
GLint *location = (GLint *) bp; bp += 4;
GLfloat *v0 = (GLfloat *) bp; bp += 4;
GLfloat *v1 = (GLfloat *) bp; bp += 4;
GLfloat *v2 = (GLfloat *) bp; bp += 4;
weglUniform3f(*location,*v0,*v1,*v2);
}; break;
-case 5478: { // glUniform4f
+case 5479: { // glUniform4f
GLint *location = (GLint *) bp; bp += 4;
GLfloat *v0 = (GLfloat *) bp; bp += 4;
GLfloat *v1 = (GLfloat *) bp; bp += 4;
@@ -3606,25 +3614,25 @@ case 5478: { // glUniform4f
GLfloat *v3 = (GLfloat *) bp; bp += 4;
weglUniform4f(*location,*v0,*v1,*v2,*v3);
}; break;
-case 5479: { // glUniform1i
+case 5480: { // glUniform1i
GLint *location = (GLint *) bp; bp += 4;
GLint *v0 = (GLint *) bp; bp += 4;
weglUniform1i(*location,*v0);
}; break;
-case 5480: { // glUniform2i
+case 5481: { // glUniform2i
GLint *location = (GLint *) bp; bp += 4;
GLint *v0 = (GLint *) bp; bp += 4;
GLint *v1 = (GLint *) bp; bp += 4;
weglUniform2i(*location,*v0,*v1);
}; break;
-case 5481: { // glUniform3i
+case 5482: { // glUniform3i
GLint *location = (GLint *) bp; bp += 4;
GLint *v0 = (GLint *) bp; bp += 4;
GLint *v1 = (GLint *) bp; bp += 4;
GLint *v2 = (GLint *) bp; bp += 4;
weglUniform3i(*location,*v0,*v1,*v2);
}; break;
-case 5482: { // glUniform4i
+case 5483: { // glUniform4i
GLint *location = (GLint *) bp; bp += 4;
GLint *v0 = (GLint *) bp; bp += 4;
GLint *v1 = (GLint *) bp; bp += 4;
@@ -3632,55 +3640,55 @@ case 5482: { // glUniform4i
GLint *v3 = (GLint *) bp; bp += 4;
weglUniform4i(*location,*v0,*v1,*v2,*v3);
}; break;
-case 5483: { // glUniform1fv
+case 5484: { // glUniform1fv
GLint *location = (GLint *) bp; bp += 4;
int * valueLen = (int *) bp; bp += 4;
- GLfloat * value = (GLfloat *) bp; bp += (8-((*valueLen*4+0)%8))%8;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*4 + (8-((*valueLen*4+0)%8))%8;
weglUniform1fv(*location,*valueLen,value);
}; break;
-case 5484: { // glUniform2fv
+case 5485: { // glUniform2fv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLfloat * value = (GLfloat *) bp; bp += *valueLen*8;
weglUniform2fv(*location,*valueLen,value);
}; break;
-case 5485: { // glUniform3fv
+case 5486: { // glUniform3fv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLfloat * value = (GLfloat *) bp; bp += *valueLen*12;
weglUniform3fv(*location,*valueLen,value);
}; break;
-case 5486: { // glUniform4fv
+case 5487: { // glUniform4fv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLfloat * value = (GLfloat *) bp; bp += *valueLen*16;
weglUniform4fv(*location,*valueLen,value);
}; break;
-case 5487: { // glUniform1iv
+case 5488: { // glUniform1iv
GLint *location = (GLint *) bp; bp += 4;
int * valueLen = (int *) bp; bp += 4;
- GLint * value = (GLint *) bp; bp += (8-((*valueLen*4+0)%8))%8;
+ GLint * value = (GLint *) bp; bp += *valueLen*4 + (8-((*valueLen*4+0)%8))%8;
weglUniform1iv(*location,*valueLen,value);
}; break;
-case 5488: { // glUniform2iv
+case 5489: { // glUniform2iv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLint * value = (GLint *) bp; bp += *valueLen*8;
weglUniform2iv(*location,*valueLen,value);
}; break;
-case 5489: { // glUniform3iv
+case 5490: { // glUniform3iv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLint * value = (GLint *) bp; bp += *valueLen*12;
weglUniform3iv(*location,*valueLen,value);
}; break;
-case 5490: { // glUniform4iv
+case 5491: { // glUniform4iv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLint * value = (GLint *) bp; bp += *valueLen*16;
weglUniform4iv(*location,*valueLen,value);
}; break;
-case 5491: { // glUniformMatrix2fv
+case 5492: { // glUniformMatrix2fv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -3688,7 +3696,7 @@ case 5491: { // glUniformMatrix2fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*16;
weglUniformMatrix2fv(*location,*valueLen,*transpose,value);
}; break;
-case 5492: { // glUniformMatrix3fv
+case 5493: { // glUniformMatrix3fv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -3696,7 +3704,7 @@ case 5492: { // glUniformMatrix3fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*36;
weglUniformMatrix3fv(*location,*valueLen,*transpose,value);
}; break;
-case 5493: { // glUniformMatrix4fv
+case 5494: { // glUniformMatrix4fv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -3704,130 +3712,130 @@ case 5493: { // glUniformMatrix4fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*64;
weglUniformMatrix4fv(*location,*valueLen,*transpose,value);
}; break;
-case 5494: { // glValidateProgram
+case 5495: { // glValidateProgram
GLuint *program = (GLuint *) bp; bp += 4;
weglValidateProgram(*program);
}; break;
-case 5495: { // glVertexAttrib1dv
+case 5496: { // glVertexAttrib1dv
GLuint *index = (GLuint *) bp; bp += 4;
bp += 4;
GLdouble *v = (GLdouble *) bp; bp += 8;
weglVertexAttrib1dv(*index,v);
}; break;
-case 5496: { // glVertexAttrib1fv
+case 5497: { // glVertexAttrib1fv
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat *v = (GLfloat *) bp; bp += 4;
weglVertexAttrib1fv(*index,v);
}; break;
-case 5497: { // glVertexAttrib1sv
+case 5498: { // glVertexAttrib1sv
GLuint *index = (GLuint *) bp; bp += 4;
GLshort *v = (GLshort *) bp; bp += 2;
weglVertexAttrib1sv(*index,v);
}; break;
-case 5498: { // glVertexAttrib2dv
+case 5499: { // glVertexAttrib2dv
GLuint *index = (GLuint *) bp; bp += 4;
bp += 4;
GLdouble *v = (GLdouble *) bp; bp += 8;
weglVertexAttrib2dv(*index,v);
}; break;
-case 5499: { // glVertexAttrib2fv
+case 5500: { // glVertexAttrib2fv
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat *v = (GLfloat *) bp; bp += 4;
weglVertexAttrib2fv(*index,v);
}; break;
-case 5500: { // glVertexAttrib2sv
+case 5501: { // glVertexAttrib2sv
GLuint *index = (GLuint *) bp; bp += 4;
GLshort *v = (GLshort *) bp; bp += 2;
weglVertexAttrib2sv(*index,v);
}; break;
-case 5501: { // glVertexAttrib3dv
+case 5502: { // glVertexAttrib3dv
GLuint *index = (GLuint *) bp; bp += 4;
bp += 4;
GLdouble *v = (GLdouble *) bp; bp += 8;
weglVertexAttrib3dv(*index,v);
}; break;
-case 5502: { // glVertexAttrib3fv
+case 5503: { // glVertexAttrib3fv
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat *v = (GLfloat *) bp; bp += 4;
weglVertexAttrib3fv(*index,v);
}; break;
-case 5503: { // glVertexAttrib3sv
+case 5504: { // glVertexAttrib3sv
GLuint *index = (GLuint *) bp; bp += 4;
GLshort *v = (GLshort *) bp; bp += 2;
weglVertexAttrib3sv(*index,v);
}; break;
-case 5504: { // glVertexAttrib4Nbv
+case 5505: { // glVertexAttrib4Nbv
GLuint *index = (GLuint *) bp; bp += 4;
GLbyte * v = (GLbyte *) bp; bp += 4;
weglVertexAttrib4Nbv(*index,v);
}; break;
-case 5505: { // glVertexAttrib4Niv
+case 5506: { // glVertexAttrib4Niv
GLuint *index = (GLuint *) bp; bp += 4;
GLint * v = (GLint *) bp; bp += 16;
weglVertexAttrib4Niv(*index,v);
}; break;
-case 5506: { // glVertexAttrib4Nsv
+case 5507: { // glVertexAttrib4Nsv
GLuint *index = (GLuint *) bp; bp += 4;
GLshort * v = (GLshort *) bp; bp += 8;
weglVertexAttrib4Nsv(*index,v);
}; break;
-case 5507: { // glVertexAttrib4Nubv
+case 5508: { // glVertexAttrib4Nubv
GLuint *index = (GLuint *) bp; bp += 4;
GLubyte * v = (GLubyte *) bp; bp += 4;
weglVertexAttrib4Nubv(*index,v);
}; break;
-case 5508: { // glVertexAttrib4Nuiv
+case 5509: { // glVertexAttrib4Nuiv
GLuint *index = (GLuint *) bp; bp += 4;
GLuint * v = (GLuint *) bp; bp += 16;
weglVertexAttrib4Nuiv(*index,v);
}; break;
-case 5509: { // glVertexAttrib4Nusv
+case 5510: { // glVertexAttrib4Nusv
GLuint *index = (GLuint *) bp; bp += 4;
GLushort * v = (GLushort *) bp; bp += 8;
weglVertexAttrib4Nusv(*index,v);
}; break;
-case 5510: { // glVertexAttrib4bv
+case 5511: { // glVertexAttrib4bv
GLuint *index = (GLuint *) bp; bp += 4;
GLbyte * v = (GLbyte *) bp; bp += 4;
weglVertexAttrib4bv(*index,v);
}; break;
-case 5511: { // glVertexAttrib4dv
+case 5512: { // glVertexAttrib4dv
GLuint *index = (GLuint *) bp; bp += 4;
bp += 4;
GLdouble * v = (GLdouble *) bp; bp += 32;
weglVertexAttrib4dv(*index,v);
}; break;
-case 5512: { // glVertexAttrib4fv
+case 5513: { // glVertexAttrib4fv
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat * v = (GLfloat *) bp; bp += 16;
weglVertexAttrib4fv(*index,v);
}; break;
-case 5513: { // glVertexAttrib4iv
+case 5514: { // glVertexAttrib4iv
GLuint *index = (GLuint *) bp; bp += 4;
GLint * v = (GLint *) bp; bp += 16;
weglVertexAttrib4iv(*index,v);
}; break;
-case 5514: { // glVertexAttrib4sv
+case 5515: { // glVertexAttrib4sv
GLuint *index = (GLuint *) bp; bp += 4;
GLshort * v = (GLshort *) bp; bp += 8;
weglVertexAttrib4sv(*index,v);
}; break;
-case 5515: { // glVertexAttrib4ubv
+case 5516: { // glVertexAttrib4ubv
GLuint *index = (GLuint *) bp; bp += 4;
GLubyte * v = (GLubyte *) bp; bp += 4;
weglVertexAttrib4ubv(*index,v);
}; break;
-case 5516: { // glVertexAttrib4uiv
+case 5517: { // glVertexAttrib4uiv
GLuint *index = (GLuint *) bp; bp += 4;
GLuint * v = (GLuint *) bp; bp += 16;
weglVertexAttrib4uiv(*index,v);
}; break;
-case 5517: { // glVertexAttrib4usv
+case 5518: { // glVertexAttrib4usv
GLuint *index = (GLuint *) bp; bp += 4;
GLushort * v = (GLushort *) bp; bp += 8;
weglVertexAttrib4usv(*index,v);
}; break;
-case 5518: { // glVertexAttribPointer
+case 5519: { // glVertexAttribPointer
GLuint *index = (GLuint *) bp; bp += 4;
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -3837,7 +3845,7 @@ case 5518: { // glVertexAttribPointer
GLvoid *pointer = (GLvoid *) (ErlDrvSInt) * (int *) bp; bp += 4;
weglVertexAttribPointer(*index,*size,*type,*normalized,*stride,pointer);
}; break;
-case 5519: { // glVertexAttribPointer
+case 5520: { // glVertexAttribPointer
GLuint *index = (GLuint *) bp; bp += 4;
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -3847,7 +3855,7 @@ case 5519: { // glVertexAttribPointer
GLvoid *pointer = (GLvoid *) bins[0];
weglVertexAttribPointer(*index,*size,*type,*normalized,*stride,pointer);
}; break;
-case 5520: { // glUniformMatrix2x3fv
+case 5521: { // glUniformMatrix2x3fv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -3855,7 +3863,7 @@ case 5520: { // glUniformMatrix2x3fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*24;
weglUniformMatrix2x3fv(*location,*valueLen,*transpose,value);
}; break;
-case 5521: { // glUniformMatrix3x2fv
+case 5522: { // glUniformMatrix3x2fv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -3863,7 +3871,7 @@ case 5521: { // glUniformMatrix3x2fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*24;
weglUniformMatrix3x2fv(*location,*valueLen,*transpose,value);
}; break;
-case 5522: { // glUniformMatrix2x4fv
+case 5523: { // glUniformMatrix2x4fv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -3871,7 +3879,7 @@ case 5522: { // glUniformMatrix2x4fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*32;
weglUniformMatrix2x4fv(*location,*valueLen,*transpose,value);
}; break;
-case 5523: { // glUniformMatrix4x2fv
+case 5524: { // glUniformMatrix4x2fv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -3879,7 +3887,7 @@ case 5523: { // glUniformMatrix4x2fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*32;
weglUniformMatrix4x2fv(*location,*valueLen,*transpose,value);
}; break;
-case 5524: { // glUniformMatrix3x4fv
+case 5525: { // glUniformMatrix3x4fv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -3887,7 +3895,7 @@ case 5524: { // glUniformMatrix3x4fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*48;
weglUniformMatrix3x4fv(*location,*valueLen,*transpose,value);
}; break;
-case 5525: { // glUniformMatrix4x3fv
+case 5526: { // glUniformMatrix4x3fv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -3895,7 +3903,7 @@ case 5525: { // glUniformMatrix4x3fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*48;
weglUniformMatrix4x3fv(*location,*valueLen,*transpose,value);
}; break;
-case 5526: { // glColorMaski
+case 5527: { // glColorMaski
GLuint *index = (GLuint *) bp; bp += 4;
GLboolean *r = (GLboolean *) bp; bp += 1;
GLboolean *g = (GLboolean *) bp; bp += 1;
@@ -3903,7 +3911,7 @@ case 5526: { // glColorMaski
GLboolean *a = (GLboolean *) bp; bp += 1;
weglColorMaski(*index,*r,*g,*b,*a);
}; break;
-case 5527: { // glGetBooleani_v
+case 5528: { // glGetBooleani_v
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLboolean data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -3931,7 +3939,7 @@ case 5527: { // glGetBooleani_v
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5528: { // glGetIntegeri_v
+case 5529: { // glGetIntegeri_v
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLint data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -3959,17 +3967,17 @@ case 5528: { // glGetIntegeri_v
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5529: { // glEnablei
+case 5530: { // glEnablei
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
weglEnablei(*target,*index);
}; break;
-case 5530: { // glDisablei
+case 5531: { // glDisablei
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
weglDisablei(*target,*index);
}; break;
-case 5531: { // glIsEnabledi
+case 5532: { // glIsEnabledi
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLboolean result = weglIsEnabledi(*target,*index);
@@ -3979,14 +3987,14 @@ case 5531: { // glIsEnabledi
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5532: { // glBeginTransformFeedback
+case 5533: { // glBeginTransformFeedback
GLenum *primitiveMode = (GLenum *) bp; bp += 4;
weglBeginTransformFeedback(*primitiveMode);
}; break;
-case 5533: { // glEndTransformFeedback
+case 5534: { // glEndTransformFeedback
weglEndTransformFeedback();
}; break;
-case 5534: { // glBindBufferRange
+case 5535: { // glBindBufferRange
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLuint *buffer = (GLuint *) bp; bp += 4;
@@ -3995,13 +4003,13 @@ case 5534: { // glBindBufferRange
GLsizeiptr size = (GLsizeiptr) * (GLuint64EXT *) bp; bp += 8;
weglBindBufferRange(*target,*index,*buffer,offset,size);
}; break;
-case 5535: { // glBindBufferBase
+case 5536: { // glBindBufferBase
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLuint *buffer = (GLuint *) bp; bp += 4;
weglBindBufferBase(*target,*index,*buffer);
}; break;
-case 5536: { // glTransformFeedbackVaryings
+case 5537: { // glTransformFeedbackVaryings
GLuint *program = (GLuint *) bp; bp += 4;
int * varyingsLen = (int *) bp; bp += 4;
int * varyingsTotSize = (int *) bp; bp += 4;
@@ -4014,7 +4022,7 @@ case 5536: { // glTransformFeedbackVaryings
weglTransformFeedbackVaryings(*program,*varyingsLen,(const GLchar **) varyings,*bufferMode);
driver_free(varyings);
}; break;
-case 5537: { // glGetTransformFeedbackVarying
+case 5538: { // glGetTransformFeedbackVarying
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
@@ -4034,20 +4042,20 @@ case 5537: { // glGetTransformFeedbackVarying
driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
-case 5538: { // glClampColor
+case 5539: { // glClampColor
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *clamp = (GLenum *) bp; bp += 4;
weglClampColor(*target,*clamp);
}; break;
-case 5539: { // glBeginConditionalRender
+case 5540: { // glBeginConditionalRender
GLuint *id = (GLuint *) bp; bp += 4;
GLenum *mode = (GLenum *) bp; bp += 4;
weglBeginConditionalRender(*id,*mode);
}; break;
-case 5540: { // glEndConditionalRender
+case 5541: { // glEndConditionalRender
weglEndConditionalRender();
}; break;
-case 5541: { // glVertexAttribIPointer
+case 5542: { // glVertexAttribIPointer
GLuint *index = (GLuint *) bp; bp += 4;
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -4055,7 +4063,7 @@ case 5541: { // glVertexAttribIPointer
GLvoid *pointer = (GLvoid *) (ErlDrvSInt) * (int *) bp; bp += 4;
weglVertexAttribIPointer(*index,*size,*type,*stride,pointer);
}; break;
-case 5542: { // glVertexAttribIPointer
+case 5543: { // glVertexAttribIPointer
GLuint *index = (GLuint *) bp; bp += 4;
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -4063,7 +4071,7 @@ case 5542: { // glVertexAttribIPointer
GLvoid *pointer = (GLvoid *) bins[0];
weglVertexAttribIPointer(*index,*size,*type,*stride,pointer);
}; break;
-case 5543: { // glGetVertexAttribIiv
+case 5544: { // glGetVertexAttribIiv
GLuint *index = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[4] = {0,0,0,0};
@@ -4079,7 +4087,7 @@ case 5543: { // glGetVertexAttribIiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5544: { // glGetVertexAttribIuiv
+case 5545: { // glGetVertexAttribIuiv
GLuint *index = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLuint params[4] = {0,0,0,0};
@@ -4095,67 +4103,67 @@ case 5544: { // glGetVertexAttribIuiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5545: { // glVertexAttribI1iv
+case 5546: { // glVertexAttribI1iv
GLuint *index = (GLuint *) bp; bp += 4;
GLint *v = (GLint *) bp; bp += 4;
weglVertexAttribI1iv(*index,v);
}; break;
-case 5546: { // glVertexAttribI2iv
+case 5547: { // glVertexAttribI2iv
GLuint *index = (GLuint *) bp; bp += 4;
GLint *v = (GLint *) bp; bp += 4;
weglVertexAttribI2iv(*index,v);
}; break;
-case 5547: { // glVertexAttribI3iv
+case 5548: { // glVertexAttribI3iv
GLuint *index = (GLuint *) bp; bp += 4;
GLint *v = (GLint *) bp; bp += 4;
weglVertexAttribI3iv(*index,v);
}; break;
-case 5548: { // glVertexAttribI4iv
+case 5549: { // glVertexAttribI4iv
GLuint *index = (GLuint *) bp; bp += 4;
GLint * v = (GLint *) bp; bp += 16;
weglVertexAttribI4iv(*index,v);
}; break;
-case 5549: { // glVertexAttribI1uiv
+case 5550: { // glVertexAttribI1uiv
GLuint *index = (GLuint *) bp; bp += 4;
GLuint *v = (GLuint *) bp; bp += 4;
weglVertexAttribI1uiv(*index,v);
}; break;
-case 5550: { // glVertexAttribI2uiv
+case 5551: { // glVertexAttribI2uiv
GLuint *index = (GLuint *) bp; bp += 4;
GLuint *v = (GLuint *) bp; bp += 4;
weglVertexAttribI2uiv(*index,v);
}; break;
-case 5551: { // glVertexAttribI3uiv
+case 5552: { // glVertexAttribI3uiv
GLuint *index = (GLuint *) bp; bp += 4;
GLuint *v = (GLuint *) bp; bp += 4;
weglVertexAttribI3uiv(*index,v);
}; break;
-case 5552: { // glVertexAttribI4uiv
+case 5553: { // glVertexAttribI4uiv
GLuint *index = (GLuint *) bp; bp += 4;
GLuint * v = (GLuint *) bp; bp += 16;
weglVertexAttribI4uiv(*index,v);
}; break;
-case 5553: { // glVertexAttribI4bv
+case 5554: { // glVertexAttribI4bv
GLuint *index = (GLuint *) bp; bp += 4;
GLbyte * v = (GLbyte *) bp; bp += 4;
weglVertexAttribI4bv(*index,v);
}; break;
-case 5554: { // glVertexAttribI4sv
+case 5555: { // glVertexAttribI4sv
GLuint *index = (GLuint *) bp; bp += 4;
GLshort * v = (GLshort *) bp; bp += 8;
weglVertexAttribI4sv(*index,v);
}; break;
-case 5555: { // glVertexAttribI4ubv
+case 5556: { // glVertexAttribI4ubv
GLuint *index = (GLuint *) bp; bp += 4;
GLubyte * v = (GLubyte *) bp; bp += 4;
weglVertexAttribI4ubv(*index,v);
}; break;
-case 5556: { // glVertexAttribI4usv
+case 5557: { // glVertexAttribI4usv
GLuint *index = (GLuint *) bp; bp += 4;
GLushort * v = (GLushort *) bp; bp += 8;
weglVertexAttribI4usv(*index,v);
}; break;
-case 5557: { // glGetUniformuiv
+case 5558: { // glGetUniformuiv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLuint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -4183,14 +4191,14 @@ case 5557: { // glGetUniformuiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5558: { // glBindFragDataLocation
+case 5559: { // glBindFragDataLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *color = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
weglBindFragDataLocation(*program,*color,name);
}; break;
-case 5559: { // glGetFragDataLocation
+case 5560: { // glGetFragDataLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
@@ -4201,25 +4209,25 @@ case 5559: { // glGetFragDataLocation
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5560: { // glUniform1ui
+case 5561: { // glUniform1ui
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
weglUniform1ui(*location,*v0);
}; break;
-case 5561: { // glUniform2ui
+case 5562: { // glUniform2ui
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
GLuint *v1 = (GLuint *) bp; bp += 4;
weglUniform2ui(*location,*v0,*v1);
}; break;
-case 5562: { // glUniform3ui
+case 5563: { // glUniform3ui
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
GLuint *v1 = (GLuint *) bp; bp += 4;
GLuint *v2 = (GLuint *) bp; bp += 4;
weglUniform3ui(*location,*v0,*v1,*v2);
}; break;
-case 5563: { // glUniform4ui
+case 5564: { // glUniform4ui
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
GLuint *v1 = (GLuint *) bp; bp += 4;
@@ -4227,45 +4235,45 @@ case 5563: { // glUniform4ui
GLuint *v3 = (GLuint *) bp; bp += 4;
weglUniform4ui(*location,*v0,*v1,*v2,*v3);
}; break;
-case 5564: { // glUniform1uiv
+case 5565: { // glUniform1uiv
GLint *location = (GLint *) bp; bp += 4;
int * valueLen = (int *) bp; bp += 4;
- GLuint * value = (GLuint *) bp; bp += (8-((*valueLen*4+0)%8))%8;
+ GLuint * value = (GLuint *) bp; bp += *valueLen*4 + (8-((*valueLen*4+0)%8))%8;
weglUniform1uiv(*location,*valueLen,value);
}; break;
-case 5565: { // glUniform2uiv
+case 5566: { // glUniform2uiv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint * value = (GLuint *) bp; bp += *valueLen*8;
weglUniform2uiv(*location,*valueLen,value);
}; break;
-case 5566: { // glUniform3uiv
+case 5567: { // glUniform3uiv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint * value = (GLuint *) bp; bp += *valueLen*12;
weglUniform3uiv(*location,*valueLen,value);
}; break;
-case 5567: { // glUniform4uiv
+case 5568: { // glUniform4uiv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint * value = (GLuint *) bp; bp += *valueLen*16;
weglUniform4uiv(*location,*valueLen,value);
}; break;
-case 5568: { // glTexParameterIiv
+case 5569: { // glTexParameterIiv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
int *paramsLen = (int *) bp; bp += 4;
GLint *params = (GLint *) bp; bp += *paramsLen*4+((*paramsLen)+1)%2*4;
weglTexParameterIiv(*target,*pname,params);
}; break;
-case 5569: { // glTexParameterIuiv
+case 5570: { // glTexParameterIuiv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
int *paramsLen = (int *) bp; bp += 4;
GLuint *params = (GLuint *) bp; bp += *paramsLen*4+((*paramsLen)+1)%2*4;
weglTexParameterIuiv(*target,*pname,params);
}; break;
-case 5570: { // glGetTexParameterIiv
+case 5571: { // glGetTexParameterIiv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[4] = {0,0,0,0};
@@ -4281,7 +4289,7 @@ case 5570: { // glGetTexParameterIiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5571: { // glGetTexParameterIuiv
+case 5572: { // glGetTexParameterIuiv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLuint params[4] = {0,0,0,0};
@@ -4297,35 +4305,35 @@ case 5571: { // glGetTexParameterIuiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5572: { // glClearBufferiv
+case 5573: { // glClearBufferiv
GLenum *buffer = (GLenum *) bp; bp += 4;
GLint *drawbuffer = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLint *value = (GLint *) bp; bp += *valueLen*4+((*valueLen)+1)%2*4;
weglClearBufferiv(*buffer,*drawbuffer,value);
}; break;
-case 5573: { // glClearBufferuiv
+case 5574: { // glClearBufferuiv
GLenum *buffer = (GLenum *) bp; bp += 4;
GLint *drawbuffer = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint *value = (GLuint *) bp; bp += *valueLen*4+((*valueLen)+1)%2*4;
weglClearBufferuiv(*buffer,*drawbuffer,value);
}; break;
-case 5574: { // glClearBufferfv
+case 5575: { // glClearBufferfv
GLenum *buffer = (GLenum *) bp; bp += 4;
GLint *drawbuffer = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLfloat *value = (GLfloat *) bp; bp += *valueLen*4+((*valueLen)+1)%2*4;
weglClearBufferfv(*buffer,*drawbuffer,value);
}; break;
-case 5575: { // glClearBufferfi
+case 5576: { // glClearBufferfi
GLenum *buffer = (GLenum *) bp; bp += 4;
GLint *drawbuffer = (GLint *) bp; bp += 4;
GLfloat *depth = (GLfloat *) bp; bp += 4;
GLint *stencil = (GLint *) bp; bp += 4;
weglClearBufferfi(*buffer,*drawbuffer,*depth,*stencil);
}; break;
-case 5576: { // glGetStringi
+case 5577: { // glGetStringi
GLenum *name = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
const GLubyte * result = weglGetStringi(*name,*index);
@@ -4335,14 +4343,14 @@ case 5576: { // glGetStringi
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5577: { // glDrawArraysInstanced
+case 5578: { // glDrawArraysInstanced
GLenum *mode = (GLenum *) bp; bp += 4;
GLint *first = (GLint *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLsizei *primcount = (GLsizei *) bp; bp += 4;
weglDrawArraysInstanced(*mode,*first,*count,*primcount);
}; break;
-case 5578: { // glDrawElementsInstanced
+case 5579: { // glDrawElementsInstanced
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -4350,7 +4358,7 @@ case 5578: { // glDrawElementsInstanced
GLsizei *primcount = (GLsizei *) bp; bp += 4;
weglDrawElementsInstanced(*mode,*count,*type,indices,*primcount);
}; break;
-case 5579: { // glDrawElementsInstanced
+case 5580: { // glDrawElementsInstanced
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -4358,17 +4366,17 @@ case 5579: { // glDrawElementsInstanced
GLsizei *primcount = (GLsizei *) bp; bp += 4;
weglDrawElementsInstanced(*mode,*count,*type,indices,*primcount);
}; break;
-case 5580: { // glTexBuffer
+case 5581: { // glTexBuffer
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *internalformat = (GLenum *) bp; bp += 4;
GLuint *buffer = (GLuint *) bp; bp += 4;
weglTexBuffer(*target,*internalformat,*buffer);
}; break;
-case 5581: { // glPrimitiveRestartIndex
+case 5582: { // glPrimitiveRestartIndex
GLuint *index = (GLuint *) bp; bp += 4;
weglPrimitiveRestartIndex(*index);
}; break;
-case 5582: { // glGetInteger64i_v
+case 5583: { // glGetInteger64i_v
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLint64 data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -4396,7 +4404,7 @@ case 5582: { // glGetInteger64i_v
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5583: { // glGetBufferParameteri64v
+case 5584: { // glGetBufferParameteri64v
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint64 params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -4424,40 +4432,40 @@ case 5583: { // glGetBufferParameteri64v
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5584: { // glFramebufferTexture
+case 5585: { // glFramebufferTexture
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLuint *texture = (GLuint *) bp; bp += 4;
GLint *level = (GLint *) bp; bp += 4;
weglFramebufferTexture(*target,*attachment,*texture,*level);
}; break;
-case 5585: { // glVertexAttribDivisor
+case 5586: { // glVertexAttribDivisor
GLuint *index = (GLuint *) bp; bp += 4;
GLuint *divisor = (GLuint *) bp; bp += 4;
weglVertexAttribDivisor(*index,*divisor);
}; break;
-case 5586: { // glMinSampleShading
+case 5587: { // glMinSampleShading
GLclampf *value = (GLclampf *) bp; bp += 4;
weglMinSampleShading(*value);
}; break;
-case 5587: { // glBlendEquationi
+case 5588: { // glBlendEquationi
GLuint *buf = (GLuint *) bp; bp += 4;
GLenum *mode = (GLenum *) bp; bp += 4;
weglBlendEquationi(*buf,*mode);
}; break;
-case 5588: { // glBlendEquationSeparatei
+case 5589: { // glBlendEquationSeparatei
GLuint *buf = (GLuint *) bp; bp += 4;
GLenum *modeRGB = (GLenum *) bp; bp += 4;
GLenum *modeAlpha = (GLenum *) bp; bp += 4;
weglBlendEquationSeparatei(*buf,*modeRGB,*modeAlpha);
}; break;
-case 5589: { // glBlendFunci
+case 5590: { // glBlendFunci
GLuint *buf = (GLuint *) bp; bp += 4;
GLenum *src = (GLenum *) bp; bp += 4;
GLenum *dst = (GLenum *) bp; bp += 4;
weglBlendFunci(*buf,*src,*dst);
}; break;
-case 5590: { // glBlendFuncSeparatei
+case 5591: { // glBlendFuncSeparatei
GLuint *buf = (GLuint *) bp; bp += 4;
GLenum *srcRGB = (GLenum *) bp; bp += 4;
GLenum *dstRGB = (GLenum *) bp; bp += 4;
@@ -4465,103 +4473,103 @@ case 5590: { // glBlendFuncSeparatei
GLenum *dstAlpha = (GLenum *) bp; bp += 4;
weglBlendFuncSeparatei(*buf,*srcRGB,*dstRGB,*srcAlpha,*dstAlpha);
}; break;
-case 5591: { // glLoadTransposeMatrixfARB
+case 5592: { // glLoadTransposeMatrixfARB
GLfloat * m = (GLfloat *) bp; bp += 64;
weglLoadTransposeMatrixfARB(m);
}; break;
-case 5592: { // glLoadTransposeMatrixdARB
+case 5593: { // glLoadTransposeMatrixdARB
GLdouble * m = (GLdouble *) bp; bp += 128;
weglLoadTransposeMatrixdARB(m);
}; break;
-case 5593: { // glMultTransposeMatrixfARB
+case 5594: { // glMultTransposeMatrixfARB
GLfloat * m = (GLfloat *) bp; bp += 64;
weglMultTransposeMatrixfARB(m);
}; break;
-case 5594: { // glMultTransposeMatrixdARB
+case 5595: { // glMultTransposeMatrixdARB
GLdouble * m = (GLdouble *) bp; bp += 128;
weglMultTransposeMatrixdARB(m);
}; break;
-case 5595: { // glWeightbvARB
+case 5596: { // glWeightbvARB
int * weightsLen = (int *) bp; bp += 4;
- GLbyte * weights = (GLbyte *) bp; bp += (8-((*weightsLen*1+4)%8))%8;
+ GLbyte * weights = (GLbyte *) bp; bp += *weightsLen*1 + (8-((*weightsLen*1+4)%8))%8;
weglWeightbvARB(*weightsLen,weights);
}; break;
-case 5596: { // glWeightsvARB
+case 5597: { // glWeightsvARB
int * weightsLen = (int *) bp; bp += 4;
- GLshort * weights = (GLshort *) bp; bp += (8-((*weightsLen*2+4)%8))%8;
+ GLshort * weights = (GLshort *) bp; bp += *weightsLen*2 + (8-((*weightsLen*2+4)%8))%8;
weglWeightsvARB(*weightsLen,weights);
}; break;
-case 5597: { // glWeightivARB
+case 5598: { // glWeightivARB
int * weightsLen = (int *) bp; bp += 4;
- GLint * weights = (GLint *) bp; bp += (8-((*weightsLen*4+4)%8))%8;
+ GLint * weights = (GLint *) bp; bp += *weightsLen*4 + (8-((*weightsLen*4+4)%8))%8;
weglWeightivARB(*weightsLen,weights);
}; break;
-case 5598: { // glWeightfvARB
+case 5599: { // glWeightfvARB
int * weightsLen = (int *) bp; bp += 4;
- GLfloat * weights = (GLfloat *) bp; bp += (8-((*weightsLen*4+4)%8))%8;
+ GLfloat * weights = (GLfloat *) bp; bp += *weightsLen*4 + (8-((*weightsLen*4+4)%8))%8;
weglWeightfvARB(*weightsLen,weights);
}; break;
-case 5599: { // glWeightdvARB
+case 5600: { // glWeightdvARB
int * weightsLen = (int *) bp; bp += 8;
- GLdouble * weights = (GLdouble *) bp; bp += (8-((*weightsLen*8+0)%8))%8;
+ GLdouble * weights = (GLdouble *) bp; bp += *weightsLen*8 + (8-((*weightsLen*8+0)%8))%8;
weglWeightdvARB(*weightsLen,weights);
}; break;
-case 5600: { // glWeightubvARB
+case 5601: { // glWeightubvARB
int * weightsLen = (int *) bp; bp += 4;
- GLubyte * weights = (GLubyte *) bp; bp += (8-((*weightsLen*1+4)%8))%8;
+ GLubyte * weights = (GLubyte *) bp; bp += *weightsLen*1 + (8-((*weightsLen*1+4)%8))%8;
weglWeightubvARB(*weightsLen,weights);
}; break;
-case 5601: { // glWeightusvARB
+case 5602: { // glWeightusvARB
int * weightsLen = (int *) bp; bp += 4;
- GLushort * weights = (GLushort *) bp; bp += (8-((*weightsLen*2+4)%8))%8;
+ GLushort * weights = (GLushort *) bp; bp += *weightsLen*2 + (8-((*weightsLen*2+4)%8))%8;
weglWeightusvARB(*weightsLen,weights);
}; break;
-case 5602: { // glWeightuivARB
+case 5603: { // glWeightuivARB
int * weightsLen = (int *) bp; bp += 4;
- GLuint * weights = (GLuint *) bp; bp += (8-((*weightsLen*4+4)%8))%8;
+ GLuint * weights = (GLuint *) bp; bp += *weightsLen*4 + (8-((*weightsLen*4+4)%8))%8;
weglWeightuivARB(*weightsLen,weights);
}; break;
-case 5603: { // glVertexBlendARB
+case 5604: { // glVertexBlendARB
GLint *count = (GLint *) bp; bp += 4;
weglVertexBlendARB(*count);
}; break;
-case 5604: { // glCurrentPaletteMatrixARB
+case 5605: { // glCurrentPaletteMatrixARB
GLint *index = (GLint *) bp; bp += 4;
weglCurrentPaletteMatrixARB(*index);
}; break;
-case 5605: { // glMatrixIndexubvARB
+case 5606: { // glMatrixIndexubvARB
int * indicesLen = (int *) bp; bp += 4;
- GLubyte * indices = (GLubyte *) bp; bp += (8-((*indicesLen*1+4)%8))%8;
+ GLubyte * indices = (GLubyte *) bp; bp += *indicesLen*1 + (8-((*indicesLen*1+4)%8))%8;
weglMatrixIndexubvARB(*indicesLen,indices);
}; break;
-case 5606: { // glMatrixIndexusvARB
+case 5607: { // glMatrixIndexusvARB
int * indicesLen = (int *) bp; bp += 4;
- GLushort * indices = (GLushort *) bp; bp += (8-((*indicesLen*2+4)%8))%8;
+ GLushort * indices = (GLushort *) bp; bp += *indicesLen*2 + (8-((*indicesLen*2+4)%8))%8;
weglMatrixIndexusvARB(*indicesLen,indices);
}; break;
-case 5607: { // glMatrixIndexuivARB
+case 5608: { // glMatrixIndexuivARB
int * indicesLen = (int *) bp; bp += 4;
- GLuint * indices = (GLuint *) bp; bp += (8-((*indicesLen*4+4)%8))%8;
+ GLuint * indices = (GLuint *) bp; bp += *indicesLen*4 + (8-((*indicesLen*4+4)%8))%8;
weglMatrixIndexuivARB(*indicesLen,indices);
}; break;
-case 5608: { // glProgramStringARB
+case 5609: { // glProgramStringARB
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLvoid *string = (GLvoid *) bp;
int stringLen[1] = {(int)strlen((char *)string)}; bp += stringLen[0]+1+((8-((1+stringLen[0]+0)%8))%8);
weglProgramStringARB(*target,*format,*stringLen,string);
}; break;
-case 5609: { // glBindProgramARB
+case 5610: { // glBindProgramARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *program = (GLuint *) bp; bp += 4;
weglBindProgramARB(*target,*program);
}; break;
-case 5610: { // glDeleteProgramsARB
+case 5611: { // glDeleteProgramsARB
int * programsLen = (int *) bp; bp += 4;
- GLuint * programs = (GLuint *) bp; bp += (8-((*programsLen*4+4)%8))%8;
+ GLuint * programs = (GLuint *) bp; bp += *programsLen*4 + (8-((*programsLen*4+4)%8))%8;
weglDeleteProgramsARB(*programsLen,programs);
}; break;
-case 5611: { // glGenProgramsARB
+case 5612: { // glGenProgramsARB
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *programs;
programs = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
@@ -4577,7 +4585,7 @@ case 5611: { // glGenProgramsARB
driver_free(rt);
driver_free(programs);
}; break;
-case 5612: { // glProgramEnvParameter4dARB
+case 5613: { // glProgramEnvParameter4dARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble *x = (GLdouble *) bp; bp += 8;
@@ -4586,13 +4594,13 @@ case 5612: { // glProgramEnvParameter4dARB
GLdouble *w = (GLdouble *) bp; bp += 8;
weglProgramEnvParameter4dARB(*target,*index,*x,*y,*z,*w);
}; break;
-case 5613: { // glProgramEnvParameter4dvARB
+case 5614: { // glProgramEnvParameter4dvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble * params = (GLdouble *) bp; bp += 32;
weglProgramEnvParameter4dvARB(*target,*index,params);
}; break;
-case 5614: { // glProgramEnvParameter4fARB
+case 5615: { // glProgramEnvParameter4fARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat *x = (GLfloat *) bp; bp += 4;
@@ -4601,13 +4609,13 @@ case 5614: { // glProgramEnvParameter4fARB
GLfloat *w = (GLfloat *) bp; bp += 4;
weglProgramEnvParameter4fARB(*target,*index,*x,*y,*z,*w);
}; break;
-case 5615: { // glProgramEnvParameter4fvARB
+case 5616: { // glProgramEnvParameter4fvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat * params = (GLfloat *) bp; bp += 16;
weglProgramEnvParameter4fvARB(*target,*index,params);
}; break;
-case 5616: { // glProgramLocalParameter4dARB
+case 5617: { // glProgramLocalParameter4dARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble *x = (GLdouble *) bp; bp += 8;
@@ -4616,13 +4624,13 @@ case 5616: { // glProgramLocalParameter4dARB
GLdouble *w = (GLdouble *) bp; bp += 8;
weglProgramLocalParameter4dARB(*target,*index,*x,*y,*z,*w);
}; break;
-case 5617: { // glProgramLocalParameter4dvARB
+case 5618: { // glProgramLocalParameter4dvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble * params = (GLdouble *) bp; bp += 32;
weglProgramLocalParameter4dvARB(*target,*index,params);
}; break;
-case 5618: { // glProgramLocalParameter4fARB
+case 5619: { // glProgramLocalParameter4fARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat *x = (GLfloat *) bp; bp += 4;
@@ -4631,13 +4639,13 @@ case 5618: { // glProgramLocalParameter4fARB
GLfloat *w = (GLfloat *) bp; bp += 4;
weglProgramLocalParameter4fARB(*target,*index,*x,*y,*z,*w);
}; break;
-case 5619: { // glProgramLocalParameter4fvARB
+case 5620: { // glProgramLocalParameter4fvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat * params = (GLfloat *) bp; bp += 16;
weglProgramLocalParameter4fvARB(*target,*index,params);
}; break;
-case 5620: { // glGetProgramEnvParameterdvARB
+case 5621: { // glGetProgramEnvParameterdvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble params[4] = {0.0,0.0,0.0,0.0};
@@ -4653,7 +4661,7 @@ case 5620: { // glGetProgramEnvParameterdvARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5621: { // glGetProgramEnvParameterfvARB
+case 5622: { // glGetProgramEnvParameterfvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat params[4] = {0.0,0.0,0.0,0.0};
@@ -4670,7 +4678,7 @@ case 5621: { // glGetProgramEnvParameterfvARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5622: { // glGetProgramLocalParameterdvARB
+case 5623: { // glGetProgramLocalParameterdvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble params[4] = {0.0,0.0,0.0,0.0};
@@ -4686,7 +4694,7 @@ case 5622: { // glGetProgramLocalParameterdvARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5623: { // glGetProgramLocalParameterfvARB
+case 5624: { // glGetProgramLocalParameterfvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat params[4] = {0.0,0.0,0.0,0.0};
@@ -4703,7 +4711,7 @@ case 5623: { // glGetProgramLocalParameterfvARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5624: { // glGetProgramStringARB
+case 5625: { // glGetProgramStringARB
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLvoid *string = (GLvoid *) bins[0];
@@ -4714,7 +4722,7 @@ case 5624: { // glGetProgramStringARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5625: { // glGetBufferParameterivARB
+case 5626: { // glGetBufferParameterivARB
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -4742,11 +4750,11 @@ case 5625: { // glGetBufferParameterivARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5626: { // glDeleteObjectARB
+case 5627: { // glDeleteObjectARB
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglDeleteObjectARB(obj);
}; break;
-case 5627: { // glGetHandleARB
+case 5628: { // glGetHandleARB
GLenum *pname = (GLenum *) bp; bp += 4;
GLhandleARB result = weglGetHandleARB(*pname);
int AP = 0; ErlDrvTermData rt[6];
@@ -4755,12 +4763,12 @@ case 5627: { // glGetHandleARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5628: { // glDetachObjectARB
+case 5629: { // glDetachObjectARB
GLhandleARB containerObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLhandleARB attachedObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglDetachObjectARB(containerObj,attachedObj);
}; break;
-case 5629: { // glCreateShaderObjectARB
+case 5630: { // glCreateShaderObjectARB
GLenum *shaderType = (GLenum *) bp; bp += 4;
GLhandleARB result = weglCreateShaderObjectARB(*shaderType);
int AP = 0; ErlDrvTermData rt[6];
@@ -4769,7 +4777,7 @@ case 5629: { // glCreateShaderObjectARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5630: { // glShaderSourceARB
+case 5631: { // glShaderSourceARB
GLhandleARB shaderObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
int * stringLen = (int *) bp; bp += 4;
int * stringTotSize = (int *) bp; bp += 4;
@@ -4781,11 +4789,11 @@ case 5630: { // glShaderSourceARB
weglShaderSourceARB(shaderObj,*stringLen,(const GLchar **) string,NULL);
driver_free(string);
}; break;
-case 5631: { // glCompileShaderARB
+case 5632: { // glCompileShaderARB
GLhandleARB shaderObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglCompileShaderARB(shaderObj);
}; break;
-case 5632: { // glCreateProgramObjectARB
+case 5633: { // glCreateProgramObjectARB
GLhandleARB result = weglCreateProgramObjectARB();
int AP = 0; ErlDrvTermData rt[6];
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
@@ -4793,24 +4801,24 @@ case 5632: { // glCreateProgramObjectARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5633: { // glAttachObjectARB
+case 5634: { // glAttachObjectARB
GLhandleARB containerObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglAttachObjectARB(containerObj,obj);
}; break;
-case 5634: { // glLinkProgramARB
+case 5635: { // glLinkProgramARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglLinkProgramARB(programObj);
}; break;
-case 5635: { // glUseProgramObjectARB
+case 5636: { // glUseProgramObjectARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglUseProgramObjectARB(programObj);
}; break;
-case 5636: { // glValidateProgramARB
+case 5637: { // glValidateProgramARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglValidateProgramARB(programObj);
}; break;
-case 5637: { // glGetObjectParameterfvARB
+case 5638: { // glGetObjectParameterfvARB
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLenum *pname = (GLenum *) bp; bp += 4;
GLfloat params[1] = {0.0};
@@ -4822,7 +4830,7 @@ case 5637: { // glGetObjectParameterfvARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5638: { // glGetObjectParameterivARB
+case 5639: { // glGetObjectParameterivARB
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
@@ -4833,7 +4841,7 @@ case 5638: { // glGetObjectParameterivARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5639: { // glGetInfoLogARB
+case 5640: { // glGetInfoLogARB
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLsizei *maxLength = (GLsizei *) bp; bp += 4;
GLsizei length[1] = {0};
@@ -4847,7 +4855,7 @@ case 5639: { // glGetInfoLogARB
driver_send_term(port,caller,rt,AP);
driver_free(infoLog);
}; break;
-case 5640: { // glGetAttachedObjectsARB
+case 5641: { // glGetAttachedObjectsARB
GLhandleARB containerObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLsizei *maxCount = (GLsizei *) bp; bp += 4;
GLsizei count[1] = {0};
@@ -4865,7 +4873,7 @@ case 5640: { // glGetAttachedObjectsARB
driver_free(rt);
driver_free(obj);
}; break;
-case 5641: { // glGetUniformLocationARB
+case 5642: { // glGetUniformLocationARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
@@ -4876,7 +4884,7 @@ case 5641: { // glGetUniformLocationARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5642: { // glGetActiveUniformARB
+case 5643: { // glGetActiveUniformARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLuint *index = (GLuint *) bp; bp += 4;
GLsizei *maxLength = (GLsizei *) bp; bp += 4;
@@ -4896,7 +4904,7 @@ case 5642: { // glGetActiveUniformARB
driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
-case 5643: { // glGetUniformfvARB
+case 5644: { // glGetUniformfvARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLint *location = (GLint *) bp; bp += 4;
GLfloat params[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
@@ -4925,7 +4933,7 @@ case 5643: { // glGetUniformfvARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5644: { // glGetUniformivARB
+case 5645: { // glGetUniformivARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLint *location = (GLint *) bp; bp += 4;
GLint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -4953,7 +4961,7 @@ case 5644: { // glGetUniformivARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5645: { // glGetShaderSourceARB
+case 5646: { // glGetShaderSourceARB
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLsizei *maxLength = (GLsizei *) bp; bp += 4;
GLsizei length[1] = {0};
@@ -4967,14 +4975,14 @@ case 5645: { // glGetShaderSourceARB
driver_send_term(port,caller,rt,AP);
driver_free(source);
}; break;
-case 5646: { // glBindAttribLocationARB
+case 5647: { // glBindAttribLocationARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLuint *index = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
weglBindAttribLocationARB(programObj,*index,name);
}; break;
-case 5647: { // glGetActiveAttribARB
+case 5648: { // glGetActiveAttribARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLuint *index = (GLuint *) bp; bp += 4;
GLsizei *maxLength = (GLsizei *) bp; bp += 4;
@@ -4994,7 +5002,7 @@ case 5647: { // glGetActiveAttribARB
driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
-case 5648: { // glGetAttribLocationARB
+case 5649: { // glGetAttribLocationARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
@@ -5005,7 +5013,7 @@ case 5648: { // glGetAttribLocationARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5649: { // glIsRenderbuffer
+case 5650: { // glIsRenderbuffer
GLuint *renderbuffer = (GLuint *) bp; bp += 4;
GLboolean result = weglIsRenderbuffer(*renderbuffer);
int AP = 0; ErlDrvTermData rt[6];
@@ -5014,17 +5022,17 @@ case 5649: { // glIsRenderbuffer
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5650: { // glBindRenderbuffer
+case 5651: { // glBindRenderbuffer
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *renderbuffer = (GLuint *) bp; bp += 4;
weglBindRenderbuffer(*target,*renderbuffer);
}; break;
-case 5651: { // glDeleteRenderbuffers
+case 5652: { // glDeleteRenderbuffers
int * renderbuffersLen = (int *) bp; bp += 4;
- GLuint * renderbuffers = (GLuint *) bp; bp += (8-((*renderbuffersLen*4+4)%8))%8;
+ GLuint * renderbuffers = (GLuint *) bp; bp += *renderbuffersLen*4 + (8-((*renderbuffersLen*4+4)%8))%8;
weglDeleteRenderbuffers(*renderbuffersLen,renderbuffers);
}; break;
-case 5652: { // glGenRenderbuffers
+case 5653: { // glGenRenderbuffers
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *renderbuffers;
renderbuffers = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
@@ -5040,14 +5048,14 @@ case 5652: { // glGenRenderbuffers
driver_free(rt);
driver_free(renderbuffers);
}; break;
-case 5653: { // glRenderbufferStorage
+case 5654: { // glRenderbufferStorage
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *internalformat = (GLenum *) bp; bp += 4;
GLsizei *width = (GLsizei *) bp; bp += 4;
GLsizei *height = (GLsizei *) bp; bp += 4;
weglRenderbufferStorage(*target,*internalformat,*width,*height);
}; break;
-case 5654: { // glGetRenderbufferParameteriv
+case 5655: { // glGetRenderbufferParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
@@ -5058,7 +5066,7 @@ case 5654: { // glGetRenderbufferParameteriv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5655: { // glIsFramebuffer
+case 5656: { // glIsFramebuffer
GLuint *framebuffer = (GLuint *) bp; bp += 4;
GLboolean result = weglIsFramebuffer(*framebuffer);
int AP = 0; ErlDrvTermData rt[6];
@@ -5067,17 +5075,17 @@ case 5655: { // glIsFramebuffer
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5656: { // glBindFramebuffer
+case 5657: { // glBindFramebuffer
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *framebuffer = (GLuint *) bp; bp += 4;
weglBindFramebuffer(*target,*framebuffer);
}; break;
-case 5657: { // glDeleteFramebuffers
+case 5658: { // glDeleteFramebuffers
int * framebuffersLen = (int *) bp; bp += 4;
- GLuint * framebuffers = (GLuint *) bp; bp += (8-((*framebuffersLen*4+4)%8))%8;
+ GLuint * framebuffers = (GLuint *) bp; bp += *framebuffersLen*4 + (8-((*framebuffersLen*4+4)%8))%8;
weglDeleteFramebuffers(*framebuffersLen,framebuffers);
}; break;
-case 5658: { // glGenFramebuffers
+case 5659: { // glGenFramebuffers
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *framebuffers;
framebuffers = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
@@ -5093,7 +5101,7 @@ case 5658: { // glGenFramebuffers
driver_free(rt);
driver_free(framebuffers);
}; break;
-case 5659: { // glCheckFramebufferStatus
+case 5660: { // glCheckFramebufferStatus
GLenum *target = (GLenum *) bp; bp += 4;
GLenum result = weglCheckFramebufferStatus(*target);
int AP = 0; ErlDrvTermData rt[6];
@@ -5102,7 +5110,7 @@ case 5659: { // glCheckFramebufferStatus
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5660: { // glFramebufferTexture1D
+case 5661: { // glFramebufferTexture1D
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLenum *textarget = (GLenum *) bp; bp += 4;
@@ -5110,7 +5118,7 @@ case 5660: { // glFramebufferTexture1D
GLint *level = (GLint *) bp; bp += 4;
weglFramebufferTexture1D(*target,*attachment,*textarget,*texture,*level);
}; break;
-case 5661: { // glFramebufferTexture2D
+case 5662: { // glFramebufferTexture2D
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLenum *textarget = (GLenum *) bp; bp += 4;
@@ -5118,7 +5126,7 @@ case 5661: { // glFramebufferTexture2D
GLint *level = (GLint *) bp; bp += 4;
weglFramebufferTexture2D(*target,*attachment,*textarget,*texture,*level);
}; break;
-case 5662: { // glFramebufferTexture3D
+case 5663: { // glFramebufferTexture3D
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLenum *textarget = (GLenum *) bp; bp += 4;
@@ -5127,14 +5135,14 @@ case 5662: { // glFramebufferTexture3D
GLint *zoffset = (GLint *) bp; bp += 4;
weglFramebufferTexture3D(*target,*attachment,*textarget,*texture,*level,*zoffset);
}; break;
-case 5663: { // glFramebufferRenderbuffer
+case 5664: { // glFramebufferRenderbuffer
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLenum *renderbuffertarget = (GLenum *) bp; bp += 4;
GLuint *renderbuffer = (GLuint *) bp; bp += 4;
weglFramebufferRenderbuffer(*target,*attachment,*renderbuffertarget,*renderbuffer);
}; break;
-case 5664: { // glGetFramebufferAttachmentParameteriv
+case 5665: { // glGetFramebufferAttachmentParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
@@ -5146,11 +5154,11 @@ case 5664: { // glGetFramebufferAttachmentParameteriv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5665: { // glGenerateMipmap
+case 5666: { // glGenerateMipmap
GLenum *target = (GLenum *) bp; bp += 4;
weglGenerateMipmap(*target);
}; break;
-case 5666: { // glBlitFramebuffer
+case 5667: { // glBlitFramebuffer
GLint *srcX0 = (GLint *) bp; bp += 4;
GLint *srcY0 = (GLint *) bp; bp += 4;
GLint *srcX1 = (GLint *) bp; bp += 4;
@@ -5163,7 +5171,7 @@ case 5666: { // glBlitFramebuffer
GLenum *filter = (GLenum *) bp; bp += 4;
weglBlitFramebuffer(*srcX0,*srcY0,*srcX1,*srcY1,*dstX0,*dstY0,*dstX1,*dstY1,*mask,*filter);
}; break;
-case 5667: { // glRenderbufferStorageMultisample
+case 5668: { // glRenderbufferStorageMultisample
GLenum *target = (GLenum *) bp; bp += 4;
GLsizei *samples = (GLsizei *) bp; bp += 4;
GLenum *internalformat = (GLenum *) bp; bp += 4;
@@ -5171,7 +5179,7 @@ case 5667: { // glRenderbufferStorageMultisample
GLsizei *height = (GLsizei *) bp; bp += 4;
weglRenderbufferStorageMultisample(*target,*samples,*internalformat,*width,*height);
}; break;
-case 5668: { // glFramebufferTextureLayer
+case 5669: { // glFramebufferTextureLayer
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLuint *texture = (GLuint *) bp; bp += 4;
@@ -5179,7 +5187,7 @@ case 5668: { // glFramebufferTextureLayer
GLint *layer = (GLint *) bp; bp += 4;
weglFramebufferTextureLayer(*target,*attachment,*texture,*level,*layer);
}; break;
-case 5669: { // glFramebufferTextureFaceARB
+case 5670: { // glFramebufferTextureFaceARB
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLuint *texture = (GLuint *) bp; bp += 4;
@@ -5187,23 +5195,23 @@ case 5669: { // glFramebufferTextureFaceARB
GLenum *face = (GLenum *) bp; bp += 4;
weglFramebufferTextureFaceARB(*target,*attachment,*texture,*level,*face);
}; break;
-case 5670: { // glFlushMappedBufferRange
+case 5671: { // glFlushMappedBufferRange
GLenum *target = (GLenum *) bp; bp += 4;
bp += 4;
GLintptr offset = (GLintptr) * (GLuint64EXT *) bp; bp += 8;
GLsizeiptr length = (GLsizeiptr) * (GLuint64EXT *) bp; bp += 8;
weglFlushMappedBufferRange(*target,offset,length);
}; break;
-case 5671: { // glBindVertexArray
+case 5672: { // glBindVertexArray
GLuint *array = (GLuint *) bp; bp += 4;
weglBindVertexArray(*array);
}; break;
-case 5672: { // glDeleteVertexArrays
+case 5673: { // glDeleteVertexArrays
int * arraysLen = (int *) bp; bp += 4;
- GLuint * arrays = (GLuint *) bp; bp += (8-((*arraysLen*4+4)%8))%8;
+ GLuint * arrays = (GLuint *) bp; bp += *arraysLen*4 + (8-((*arraysLen*4+4)%8))%8;
weglDeleteVertexArrays(*arraysLen,arrays);
}; break;
-case 5673: { // glGenVertexArrays
+case 5674: { // glGenVertexArrays
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *arrays;
arrays = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
@@ -5219,7 +5227,7 @@ case 5673: { // glGenVertexArrays
driver_free(rt);
driver_free(arrays);
}; break;
-case 5674: { // glIsVertexArray
+case 5675: { // glIsVertexArray
GLuint *array = (GLuint *) bp; bp += 4;
GLboolean result = weglIsVertexArray(*array);
int AP = 0; ErlDrvTermData rt[6];
@@ -5228,7 +5236,7 @@ case 5674: { // glIsVertexArray
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5675: { // glGetUniformIndices
+case 5676: { // glGetUniformIndices
GLuint *program = (GLuint *) bp; bp += 4;
int * uniformNamesLen = (int *) bp; bp += 4;
int * uniformNamesTotSize = (int *) bp; bp += 4;
@@ -5252,10 +5260,10 @@ case 5675: { // glGetUniformIndices
driver_free(uniformIndices);
driver_free(uniformNames);
}; break;
-case 5676: { // glGetActiveUniformsiv
+case 5677: { // glGetActiveUniformsiv
GLuint *program = (GLuint *) bp; bp += 4;
int * uniformIndicesLen = (int *) bp; bp += 4;
- GLuint * uniformIndices = (GLuint *) bp; bp += (8-((*uniformIndicesLen*4+0)%8))%8;
+ GLuint * uniformIndices = (GLuint *) bp; bp += *uniformIndicesLen*4 + (8-((*uniformIndicesLen*4+0)%8))%8;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint *params;
params = (GLint *) driver_alloc(sizeof(GLint) * *uniformIndicesLen);
@@ -5271,7 +5279,7 @@ case 5676: { // glGetActiveUniformsiv
driver_free(rt);
driver_free(params);
}; break;
-case 5677: { // glGetActiveUniformName
+case 5678: { // glGetActiveUniformName
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *uniformIndex = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
@@ -5286,7 +5294,7 @@ case 5677: { // glGetActiveUniformName
driver_send_term(port,caller,rt,AP);
driver_free(uniformName);
}; break;
-case 5678: { // glGetUniformBlockIndex
+case 5679: { // glGetUniformBlockIndex
GLuint *program = (GLuint *) bp; bp += 4;
GLchar *uniformBlockName = (GLchar *) bp;
int uniformBlockNameLen[1] = {(int)strlen((char *)uniformBlockName)}; bp += uniformBlockNameLen[0]+1+((8-((1+uniformBlockNameLen[0]+4)%8))%8);
@@ -5297,7 +5305,7 @@ case 5678: { // glGetUniformBlockIndex
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5679: { // glGetActiveUniformBlockiv
+case 5680: { // glGetActiveUniformBlockiv
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *uniformBlockIndex = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
@@ -5309,7 +5317,7 @@ case 5679: { // glGetActiveUniformBlockiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5680: { // glGetActiveUniformBlockName
+case 5681: { // glGetActiveUniformBlockName
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *uniformBlockIndex = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
@@ -5324,13 +5332,13 @@ case 5680: { // glGetActiveUniformBlockName
driver_send_term(port,caller,rt,AP);
driver_free(uniformBlockName);
}; break;
-case 5681: { // glUniformBlockBinding
+case 5682: { // glUniformBlockBinding
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *uniformBlockIndex = (GLuint *) bp; bp += 4;
GLuint *uniformBlockBinding = (GLuint *) bp; bp += 4;
weglUniformBlockBinding(*program,*uniformBlockIndex,*uniformBlockBinding);
}; break;
-case 5682: { // glCopyBufferSubData
+case 5683: { // glCopyBufferSubData
GLenum *readTarget = (GLenum *) bp; bp += 4;
GLenum *writeTarget = (GLenum *) bp; bp += 4;
GLintptr readOffset = (GLintptr) * (GLuint64EXT *) bp; bp += 8;
@@ -5338,7 +5346,7 @@ case 5682: { // glCopyBufferSubData
GLsizeiptr size = (GLsizeiptr) * (GLuint64EXT *) bp; bp += 8;
weglCopyBufferSubData(*readTarget,*writeTarget,readOffset,writeOffset,size);
}; break;
-case 5683: { // glDrawElementsBaseVertex
+case 5684: { // glDrawElementsBaseVertex
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -5346,7 +5354,7 @@ case 5683: { // glDrawElementsBaseVertex
GLint *basevertex = (GLint *) bp; bp += 4;
weglDrawElementsBaseVertex(*mode,*count,*type,indices,*basevertex);
}; break;
-case 5684: { // glDrawElementsBaseVertex
+case 5685: { // glDrawElementsBaseVertex
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -5354,7 +5362,7 @@ case 5684: { // glDrawElementsBaseVertex
GLint *basevertex = (GLint *) bp; bp += 4;
weglDrawElementsBaseVertex(*mode,*count,*type,indices,*basevertex);
}; break;
-case 5685: { // glDrawRangeElementsBaseVertex
+case 5686: { // glDrawRangeElementsBaseVertex
GLenum *mode = (GLenum *) bp; bp += 4;
GLuint *start = (GLuint *) bp; bp += 4;
GLuint *end = (GLuint *) bp; bp += 4;
@@ -5364,7 +5372,7 @@ case 5685: { // glDrawRangeElementsBaseVertex
GLint *basevertex = (GLint *) bp; bp += 4;
weglDrawRangeElementsBaseVertex(*mode,*start,*end,*count,*type,indices,*basevertex);
}; break;
-case 5686: { // glDrawRangeElementsBaseVertex
+case 5687: { // glDrawRangeElementsBaseVertex
GLenum *mode = (GLenum *) bp; bp += 4;
GLuint *start = (GLuint *) bp; bp += 4;
GLuint *end = (GLuint *) bp; bp += 4;
@@ -5374,7 +5382,7 @@ case 5686: { // glDrawRangeElementsBaseVertex
GLint *basevertex = (GLint *) bp; bp += 4;
weglDrawRangeElementsBaseVertex(*mode,*start,*end,*count,*type,indices,*basevertex);
}; break;
-case 5687: { // glDrawElementsInstancedBaseVertex
+case 5688: { // glDrawElementsInstancedBaseVertex
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -5383,7 +5391,7 @@ case 5687: { // glDrawElementsInstancedBaseVertex
GLint *basevertex = (GLint *) bp; bp += 4;
weglDrawElementsInstancedBaseVertex(*mode,*count,*type,indices,*primcount,*basevertex);
}; break;
-case 5688: { // glDrawElementsInstancedBaseVertex
+case 5689: { // glDrawElementsInstancedBaseVertex
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -5392,11 +5400,11 @@ case 5688: { // glDrawElementsInstancedBaseVertex
GLint *basevertex = (GLint *) bp; bp += 4;
weglDrawElementsInstancedBaseVertex(*mode,*count,*type,indices,*primcount,*basevertex);
}; break;
-case 5689: { // glProvokingVertex
+case 5690: { // glProvokingVertex
GLenum *mode = (GLenum *) bp; bp += 4;
weglProvokingVertex(*mode);
}; break;
-case 5690: { // glFenceSync
+case 5691: { // glFenceSync
GLenum *condition = (GLenum *) bp; bp += 4;
GLbitfield *flags = (GLbitfield *) bp; bp += 4;
GLsync result = weglFenceSync(*condition,*flags);
@@ -5406,7 +5414,7 @@ case 5690: { // glFenceSync
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5691: { // glIsSync
+case 5692: { // glIsSync
GLsync sync = (GLsync) * (GLuint64EXT *) bp; bp += 8;
GLboolean result = weglIsSync(sync);
int AP = 0; ErlDrvTermData rt[6];
@@ -5415,11 +5423,11 @@ case 5691: { // glIsSync
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5692: { // glDeleteSync
+case 5693: { // glDeleteSync
GLsync sync = (GLsync) * (GLuint64EXT *) bp; bp += 8;
weglDeleteSync(sync);
}; break;
-case 5693: { // glClientWaitSync
+case 5694: { // glClientWaitSync
GLsync sync = (GLsync) * (GLuint64EXT *) bp; bp += 8;
GLbitfield *flags = (GLbitfield *) bp; bp += 4;
bp += 4;
@@ -5431,14 +5439,14 @@ case 5693: { // glClientWaitSync
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5694: { // glWaitSync
+case 5695: { // glWaitSync
GLsync sync = (GLsync) * (GLuint64EXT *) bp; bp += 8;
GLbitfield *flags = (GLbitfield *) bp; bp += 4;
bp += 4;
GLuint64 timeout = (GLuint64) * (GLuint64EXT *) bp; bp += 8;
weglWaitSync(sync,*flags,timeout);
}; break;
-case 5695: { // glGetInteger64v
+case 5696: { // glGetInteger64v
GLenum *pname = (GLenum *) bp; bp += 4;
GLint64 params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
weglGetInteger64v(*pname,params);
@@ -5465,7 +5473,7 @@ case 5695: { // glGetInteger64v
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5696: { // glGetSynciv
+case 5697: { // glGetSynciv
GLsync sync = (GLsync) * (GLuint64EXT *) bp; bp += 8;
GLenum *pname = (GLenum *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
@@ -5484,7 +5492,7 @@ case 5696: { // glGetSynciv
driver_free(rt);
driver_free(values);
}; break;
-case 5697: { // glTexImage2DMultisample
+case 5698: { // glTexImage2DMultisample
GLenum *target = (GLenum *) bp; bp += 4;
GLsizei *samples = (GLsizei *) bp; bp += 4;
GLint *internalformat = (GLint *) bp; bp += 4;
@@ -5493,7 +5501,7 @@ case 5697: { // glTexImage2DMultisample
GLboolean *fixedsamplelocations = (GLboolean *) bp; bp += 1;
weglTexImage2DMultisample(*target,*samples,*internalformat,*width,*height,*fixedsamplelocations);
}; break;
-case 5698: { // glTexImage3DMultisample
+case 5699: { // glTexImage3DMultisample
GLenum *target = (GLenum *) bp; bp += 4;
GLsizei *samples = (GLsizei *) bp; bp += 4;
GLint *internalformat = (GLint *) bp; bp += 4;
@@ -5503,7 +5511,7 @@ case 5698: { // glTexImage3DMultisample
GLboolean *fixedsamplelocations = (GLboolean *) bp; bp += 1;
weglTexImage3DMultisample(*target,*samples,*internalformat,*width,*height,*depth,*fixedsamplelocations);
}; break;
-case 5699: { // glGetMultisamplefv
+case 5700: { // glGetMultisamplefv
GLenum *pname = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat val[2] = {0.0,0.0};
@@ -5518,12 +5526,12 @@ case 5699: { // glGetMultisamplefv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5700: { // glSampleMaski
+case 5701: { // glSampleMaski
GLuint *index = (GLuint *) bp; bp += 4;
GLbitfield *mask = (GLbitfield *) bp; bp += 4;
weglSampleMaski(*index,*mask);
}; break;
-case 5701: { // glNamedStringARB
+case 5702: { // glNamedStringARB
GLenum *type = (GLenum *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
@@ -5531,12 +5539,12 @@ case 5701: { // glNamedStringARB
int stringLen[1] = {(int)strlen((char *)string)}; bp += stringLen[0]+1+((8-((1+stringLen[0]+0)%8))%8);
weglNamedStringARB(*type,*nameLen,name,*stringLen,string);
}; break;
-case 5702: { // glDeleteNamedStringARB
+case 5703: { // glDeleteNamedStringARB
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
weglDeleteNamedStringARB(*nameLen,name);
}; break;
-case 5703: { // glCompileShaderIncludeARB
+case 5704: { // glCompileShaderIncludeARB
GLuint *shader = (GLuint *) bp; bp += 4;
int * pathLen = (int *) bp; bp += 4;
int * pathTotSize = (int *) bp; bp += 4;
@@ -5548,7 +5556,7 @@ case 5703: { // glCompileShaderIncludeARB
weglCompileShaderIncludeARB(*shader,*pathLen,(const GLchar **) path,NULL);
driver_free(path);
}; break;
-case 5704: { // glIsNamedStringARB
+case 5705: { // glIsNamedStringARB
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
GLboolean result = weglIsNamedStringARB(*nameLen,name);
@@ -5558,7 +5566,7 @@ case 5704: { // glIsNamedStringARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5705: { // glGetNamedStringARB
+case 5706: { // glGetNamedStringARB
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
@@ -5573,7 +5581,7 @@ case 5705: { // glGetNamedStringARB
driver_send_term(port,caller,rt,AP);
driver_free(string);
}; break;
-case 5706: { // glGetNamedStringivARB
+case 5707: { // glGetNamedStringivARB
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
GLenum *pname = (GLenum *) bp; bp += 4;
@@ -5585,7 +5593,7 @@ case 5706: { // glGetNamedStringivARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5707: { // glBindFragDataLocationIndexed
+case 5708: { // glBindFragDataLocationIndexed
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *colorNumber = (GLuint *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
@@ -5593,7 +5601,7 @@ case 5707: { // glBindFragDataLocationIndexed
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
weglBindFragDataLocationIndexed(*program,*colorNumber,*index,name);
}; break;
-case 5708: { // glGetFragDataIndex
+case 5709: { // glGetFragDataIndex
GLuint *program = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
int nameLen[1] = {(int)strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
@@ -5604,7 +5612,7 @@ case 5708: { // glGetFragDataIndex
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5709: { // glGenSamplers
+case 5710: { // glGenSamplers
GLsizei *count = (GLsizei *) bp; bp += 4;
GLuint *samplers;
samplers = (GLuint *) driver_alloc(sizeof(GLuint) * *count);
@@ -5620,12 +5628,12 @@ case 5709: { // glGenSamplers
driver_free(rt);
driver_free(samplers);
}; break;
-case 5710: { // glDeleteSamplers
+case 5711: { // glDeleteSamplers
int * samplersLen = (int *) bp; bp += 4;
- GLuint * samplers = (GLuint *) bp; bp += (8-((*samplersLen*4+4)%8))%8;
+ GLuint * samplers = (GLuint *) bp; bp += *samplersLen*4 + (8-((*samplersLen*4+4)%8))%8;
weglDeleteSamplers(*samplersLen,samplers);
}; break;
-case 5711: { // glIsSampler
+case 5712: { // glIsSampler
GLuint *sampler = (GLuint *) bp; bp += 4;
GLboolean result = weglIsSampler(*sampler);
int AP = 0; ErlDrvTermData rt[6];
@@ -5634,52 +5642,52 @@ case 5711: { // glIsSampler
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5712: { // glBindSampler
+case 5713: { // glBindSampler
GLuint *unit = (GLuint *) bp; bp += 4;
GLuint *sampler = (GLuint *) bp; bp += 4;
weglBindSampler(*unit,*sampler);
}; break;
-case 5713: { // glSamplerParameteri
+case 5714: { // glSamplerParameteri
GLuint *sampler = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint *param = (GLint *) bp; bp += 4;
weglSamplerParameteri(*sampler,*pname,*param);
}; break;
-case 5714: { // glSamplerParameteriv
+case 5715: { // glSamplerParameteriv
GLuint *sampler = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
int * paramLen = (int *) bp; bp += 4;
- GLint * param = (GLint *) bp; bp += (8-((*paramLen*4+4)%8))%8;
+ GLint * param = (GLint *) bp; bp += *paramLen*4 + (8-((*paramLen*4+4)%8))%8;
weglSamplerParameteriv(*sampler,*pname,param);
}; break;
-case 5715: { // glSamplerParameterf
+case 5716: { // glSamplerParameterf
GLuint *sampler = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLfloat *param = (GLfloat *) bp; bp += 4;
weglSamplerParameterf(*sampler,*pname,*param);
}; break;
-case 5716: { // glSamplerParameterfv
+case 5717: { // glSamplerParameterfv
GLuint *sampler = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
int * paramLen = (int *) bp; bp += 4;
- GLfloat * param = (GLfloat *) bp; bp += (8-((*paramLen*4+4)%8))%8;
+ GLfloat * param = (GLfloat *) bp; bp += *paramLen*4 + (8-((*paramLen*4+4)%8))%8;
weglSamplerParameterfv(*sampler,*pname,param);
}; break;
-case 5717: { // glSamplerParameterIiv
+case 5718: { // glSamplerParameterIiv
GLuint *sampler = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
int * paramLen = (int *) bp; bp += 4;
- GLint * param = (GLint *) bp; bp += (8-((*paramLen*4+4)%8))%8;
+ GLint * param = (GLint *) bp; bp += *paramLen*4 + (8-((*paramLen*4+4)%8))%8;
weglSamplerParameterIiv(*sampler,*pname,param);
}; break;
-case 5718: { // glSamplerParameterIuiv
+case 5719: { // glSamplerParameterIuiv
GLuint *sampler = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
int * paramLen = (int *) bp; bp += 4;
- GLuint * param = (GLuint *) bp; bp += (8-((*paramLen*4+4)%8))%8;
+ GLuint * param = (GLuint *) bp; bp += *paramLen*4 + (8-((*paramLen*4+4)%8))%8;
weglSamplerParameterIuiv(*sampler,*pname,param);
}; break;
-case 5719: { // glGetSamplerParameteriv
+case 5720: { // glGetSamplerParameteriv
GLuint *sampler = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[4] = {0,0,0,0};
@@ -5695,7 +5703,7 @@ case 5719: { // glGetSamplerParameteriv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5720: { // glGetSamplerParameterIiv
+case 5721: { // glGetSamplerParameterIiv
GLuint *sampler = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[4] = {0,0,0,0};
@@ -5711,7 +5719,7 @@ case 5720: { // glGetSamplerParameterIiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5721: { // glGetSamplerParameterfv
+case 5722: { // glGetSamplerParameterfv
GLuint *sampler = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLfloat params[4] = {0.0,0.0,0.0,0.0};
@@ -5728,7 +5736,7 @@ case 5721: { // glGetSamplerParameterfv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5722: { // glGetSamplerParameterIuiv
+case 5723: { // glGetSamplerParameterIuiv
GLuint *sampler = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLuint params[4] = {0,0,0,0};
@@ -5744,12 +5752,12 @@ case 5722: { // glGetSamplerParameterIuiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5723: { // glQueryCounter
+case 5724: { // glQueryCounter
GLuint *id = (GLuint *) bp; bp += 4;
GLenum *target = (GLenum *) bp; bp += 4;
weglQueryCounter(*id,*target);
}; break;
-case 5724: { // glGetQueryObjecti64v
+case 5725: { // glGetQueryObjecti64v
GLuint *id = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint64 params[1] = {0};
@@ -5760,7 +5768,7 @@ case 5724: { // glGetQueryObjecti64v
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5725: { // glGetQueryObjectui64v
+case 5726: { // glGetQueryObjectui64v
GLuint *id = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLuint64 params[1] = {0};
@@ -5771,42 +5779,42 @@ case 5725: { // glGetQueryObjectui64v
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5726: { // glDrawArraysIndirect
+case 5727: { // glDrawArraysIndirect
GLenum *mode = (GLenum *) bp; bp += 4;
GLvoid *indirect = (GLvoid *) (ErlDrvSInt) * (int *) bp; bp += 4;
weglDrawArraysIndirect(*mode,indirect);
}; break;
-case 5727: { // glDrawArraysIndirect
+case 5728: { // glDrawArraysIndirect
GLenum *mode = (GLenum *) bp; bp += 4;
GLvoid *indirect = (GLvoid *) bins[0];
weglDrawArraysIndirect(*mode,indirect);
}; break;
-case 5728: { // glDrawElementsIndirect
+case 5729: { // glDrawElementsIndirect
GLenum *mode = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLvoid *indirect = (GLvoid *) (ErlDrvSInt) * (int *) bp; bp += 4;
weglDrawElementsIndirect(*mode,*type,indirect);
}; break;
-case 5729: { // glDrawElementsIndirect
+case 5730: { // glDrawElementsIndirect
GLenum *mode = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLvoid *indirect = (GLvoid *) bins[0];
weglDrawElementsIndirect(*mode,*type,indirect);
}; break;
-case 5730: { // glUniform1d
+case 5731: { // glUniform1d
GLint *location = (GLint *) bp; bp += 4;
bp += 4;
GLdouble *x = (GLdouble *) bp; bp += 8;
weglUniform1d(*location,*x);
}; break;
-case 5731: { // glUniform2d
+case 5732: { // glUniform2d
GLint *location = (GLint *) bp; bp += 4;
bp += 4;
GLdouble *x = (GLdouble *) bp; bp += 8;
GLdouble *y = (GLdouble *) bp; bp += 8;
weglUniform2d(*location,*x,*y);
}; break;
-case 5732: { // glUniform3d
+case 5733: { // glUniform3d
GLint *location = (GLint *) bp; bp += 4;
bp += 4;
GLdouble *x = (GLdouble *) bp; bp += 8;
@@ -5814,7 +5822,7 @@ case 5732: { // glUniform3d
GLdouble *z = (GLdouble *) bp; bp += 8;
weglUniform3d(*location,*x,*y,*z);
}; break;
-case 5733: { // glUniform4d
+case 5734: { // glUniform4d
GLint *location = (GLint *) bp; bp += 4;
bp += 4;
GLdouble *x = (GLdouble *) bp; bp += 8;
@@ -5823,35 +5831,35 @@ case 5733: { // glUniform4d
GLdouble *w = (GLdouble *) bp; bp += 8;
weglUniform4d(*location,*x,*y,*z,*w);
}; break;
-case 5734: { // glUniform1dv
+case 5735: { // glUniform1dv
GLint *location = (GLint *) bp; bp += 4;
bp += 4;
int * valueLen = (int *) bp; bp += 8;
- GLdouble * value = (GLdouble *) bp; bp += (8-((*valueLen*8+0)%8))%8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*8 + (8-((*valueLen*8+0)%8))%8;
weglUniform1dv(*location,*valueLen,value);
}; break;
-case 5735: { // glUniform2dv
+case 5736: { // glUniform2dv
GLint *location = (GLint *) bp; bp += 4;
bp += 4;
int *valueLen = (int *) bp; bp += 8;
GLdouble * value = (GLdouble *) bp; bp += *valueLen*16;
weglUniform2dv(*location,*valueLen,value);
}; break;
-case 5736: { // glUniform3dv
+case 5737: { // glUniform3dv
GLint *location = (GLint *) bp; bp += 4;
bp += 4;
int *valueLen = (int *) bp; bp += 8;
GLdouble * value = (GLdouble *) bp; bp += *valueLen*24;
weglUniform3dv(*location,*valueLen,value);
}; break;
-case 5737: { // glUniform4dv
+case 5738: { // glUniform4dv
GLint *location = (GLint *) bp; bp += 4;
bp += 4;
int *valueLen = (int *) bp; bp += 8;
GLdouble * value = (GLdouble *) bp; bp += *valueLen*32;
weglUniform4dv(*location,*valueLen,value);
}; break;
-case 5738: { // glUniformMatrix2dv
+case 5739: { // glUniformMatrix2dv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -5859,7 +5867,7 @@ case 5738: { // glUniformMatrix2dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*32;
weglUniformMatrix2dv(*location,*valueLen,*transpose,value);
}; break;
-case 5739: { // glUniformMatrix3dv
+case 5740: { // glUniformMatrix3dv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -5867,7 +5875,7 @@ case 5739: { // glUniformMatrix3dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*72;
weglUniformMatrix3dv(*location,*valueLen,*transpose,value);
}; break;
-case 5740: { // glUniformMatrix4dv
+case 5741: { // glUniformMatrix4dv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -5875,7 +5883,7 @@ case 5740: { // glUniformMatrix4dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*128;
weglUniformMatrix4dv(*location,*valueLen,*transpose,value);
}; break;
-case 5741: { // glUniformMatrix2x3dv
+case 5742: { // glUniformMatrix2x3dv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -5883,7 +5891,7 @@ case 5741: { // glUniformMatrix2x3dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*48;
weglUniformMatrix2x3dv(*location,*valueLen,*transpose,value);
}; break;
-case 5742: { // glUniformMatrix2x4dv
+case 5743: { // glUniformMatrix2x4dv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -5891,7 +5899,7 @@ case 5742: { // glUniformMatrix2x4dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*64;
weglUniformMatrix2x4dv(*location,*valueLen,*transpose,value);
}; break;
-case 5743: { // glUniformMatrix3x2dv
+case 5744: { // glUniformMatrix3x2dv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -5899,7 +5907,7 @@ case 5743: { // glUniformMatrix3x2dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*48;
weglUniformMatrix3x2dv(*location,*valueLen,*transpose,value);
}; break;
-case 5744: { // glUniformMatrix3x4dv
+case 5745: { // glUniformMatrix3x4dv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -5907,7 +5915,7 @@ case 5744: { // glUniformMatrix3x4dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*96;
weglUniformMatrix3x4dv(*location,*valueLen,*transpose,value);
}; break;
-case 5745: { // glUniformMatrix4x2dv
+case 5746: { // glUniformMatrix4x2dv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -5915,7 +5923,7 @@ case 5745: { // glUniformMatrix4x2dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*64;
weglUniformMatrix4x2dv(*location,*valueLen,*transpose,value);
}; break;
-case 5746: { // glUniformMatrix4x3dv
+case 5747: { // glUniformMatrix4x3dv
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
bp += 3;
@@ -5923,7 +5931,7 @@ case 5746: { // glUniformMatrix4x3dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*96;
weglUniformMatrix4x3dv(*location,*valueLen,*transpose,value);
}; break;
-case 5747: { // glGetUniformdv
+case 5748: { // glGetUniformdv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLdouble params[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
@@ -5951,7 +5959,7 @@ case 5747: { // glGetUniformdv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5748: { // glGetSubroutineUniformLocation
+case 5749: { // glGetSubroutineUniformLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLenum *shadertype = (GLenum *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
@@ -5963,7 +5971,7 @@ case 5748: { // glGetSubroutineUniformLocation
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5749: { // glGetSubroutineIndex
+case 5750: { // glGetSubroutineIndex
GLuint *program = (GLuint *) bp; bp += 4;
GLenum *shadertype = (GLenum *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
@@ -5975,7 +5983,7 @@ case 5749: { // glGetSubroutineIndex
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5750: { // glGetActiveSubroutineUniformName
+case 5751: { // glGetActiveSubroutineUniformName
GLuint *program = (GLuint *) bp; bp += 4;
GLenum *shadertype = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
@@ -5991,7 +5999,7 @@ case 5750: { // glGetActiveSubroutineUniformName
driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
-case 5751: { // glGetActiveSubroutineName
+case 5752: { // glGetActiveSubroutineName
GLuint *program = (GLuint *) bp; bp += 4;
GLenum *shadertype = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
@@ -6007,13 +6015,13 @@ case 5751: { // glGetActiveSubroutineName
driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
-case 5752: { // glUniformSubroutinesuiv
+case 5753: { // glUniformSubroutinesuiv
GLenum *shadertype = (GLenum *) bp; bp += 4;
int * indicesLen = (int *) bp; bp += 4;
- GLuint * indices = (GLuint *) bp; bp += (8-((*indicesLen*4+0)%8))%8;
+ GLuint * indices = (GLuint *) bp; bp += *indicesLen*4 + (8-((*indicesLen*4+0)%8))%8;
weglUniformSubroutinesuiv(*shadertype,*indicesLen,indices);
}; break;
-case 5753: { // glGetUniformSubroutineuiv
+case 5754: { // glGetUniformSubroutineuiv
GLenum *shadertype = (GLenum *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLuint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -6041,7 +6049,7 @@ case 5753: { // glGetUniformSubroutineuiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5754: { // glGetProgramStageiv
+case 5755: { // glGetProgramStageiv
GLuint *program = (GLuint *) bp; bp += 4;
GLenum *shadertype = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
@@ -6053,28 +6061,28 @@ case 5754: { // glGetProgramStageiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5755: { // glPatchParameteri
+case 5756: { // glPatchParameteri
GLenum *pname = (GLenum *) bp; bp += 4;
GLint *value = (GLint *) bp; bp += 4;
weglPatchParameteri(*pname,*value);
}; break;
-case 5756: { // glPatchParameterfv
+case 5757: { // glPatchParameterfv
GLenum *pname = (GLenum *) bp; bp += 4;
int * valuesLen = (int *) bp; bp += 4;
- GLfloat * values = (GLfloat *) bp; bp += (8-((*valuesLen*4+0)%8))%8;
+ GLfloat * values = (GLfloat *) bp; bp += *valuesLen*4 + (8-((*valuesLen*4+0)%8))%8;
weglPatchParameterfv(*pname,values);
}; break;
-case 5757: { // glBindTransformFeedback
+case 5758: { // glBindTransformFeedback
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *id = (GLuint *) bp; bp += 4;
weglBindTransformFeedback(*target,*id);
}; break;
-case 5758: { // glDeleteTransformFeedbacks
+case 5759: { // glDeleteTransformFeedbacks
int * idsLen = (int *) bp; bp += 4;
- GLuint * ids = (GLuint *) bp; bp += (8-((*idsLen*4+4)%8))%8;
+ GLuint * ids = (GLuint *) bp; bp += *idsLen*4 + (8-((*idsLen*4+4)%8))%8;
weglDeleteTransformFeedbacks(*idsLen,ids);
}; break;
-case 5759: { // glGenTransformFeedbacks
+case 5760: { // glGenTransformFeedbacks
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *ids;
ids = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
@@ -6090,7 +6098,7 @@ case 5759: { // glGenTransformFeedbacks
driver_free(rt);
driver_free(ids);
}; break;
-case 5760: { // glIsTransformFeedback
+case 5761: { // glIsTransformFeedback
GLuint *id = (GLuint *) bp; bp += 4;
GLboolean result = weglIsTransformFeedback(*id);
int AP = 0; ErlDrvTermData rt[6];
@@ -6099,35 +6107,35 @@ case 5760: { // glIsTransformFeedback
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5761: { // glPauseTransformFeedback
+case 5762: { // glPauseTransformFeedback
weglPauseTransformFeedback();
}; break;
-case 5762: { // glResumeTransformFeedback
+case 5763: { // glResumeTransformFeedback
weglResumeTransformFeedback();
}; break;
-case 5763: { // glDrawTransformFeedback
+case 5764: { // glDrawTransformFeedback
GLenum *mode = (GLenum *) bp; bp += 4;
GLuint *id = (GLuint *) bp; bp += 4;
weglDrawTransformFeedback(*mode,*id);
}; break;
-case 5764: { // glDrawTransformFeedbackStream
+case 5765: { // glDrawTransformFeedbackStream
GLenum *mode = (GLenum *) bp; bp += 4;
GLuint *id = (GLuint *) bp; bp += 4;
GLuint *stream = (GLuint *) bp; bp += 4;
weglDrawTransformFeedbackStream(*mode,*id,*stream);
}; break;
-case 5765: { // glBeginQueryIndexed
+case 5766: { // glBeginQueryIndexed
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLuint *id = (GLuint *) bp; bp += 4;
weglBeginQueryIndexed(*target,*index,*id);
}; break;
-case 5766: { // glEndQueryIndexed
+case 5767: { // glEndQueryIndexed
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
weglEndQueryIndexed(*target,*index);
}; break;
-case 5767: { // glGetQueryIndexediv
+case 5768: { // glGetQueryIndexediv
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
@@ -6139,18 +6147,18 @@ case 5767: { // glGetQueryIndexediv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5768: { // glReleaseShaderCompiler
+case 5769: { // glReleaseShaderCompiler
weglReleaseShaderCompiler();
}; break;
-case 5769: { // glShaderBinary
+case 5770: { // glShaderBinary
int * shadersLen = (int *) bp; bp += 4;
- GLuint * shaders = (GLuint *) bp; bp += (8-((*shadersLen*4+4)%8))%8;
+ GLuint * shaders = (GLuint *) bp; bp += *shadersLen*4 + (8-((*shadersLen*4+4)%8))%8;
GLenum *binaryformat = (GLenum *) bp; bp += 4;
GLvoid *binary = (GLvoid *) bins[0];
GLsizei binary_size = bins_sz[0];
weglShaderBinary(*shadersLen,shaders,*binaryformat,binary,binary_size);
}; break;
-case 5770: { // glGetShaderPrecisionFormat
+case 5771: { // glGetShaderPrecisionFormat
GLenum *shadertype = (GLenum *) bp; bp += 4;
GLenum *precisiontype = (GLenum *) bp; bp += 4;
GLint range[2] = {0,0};
@@ -6167,16 +6175,16 @@ case 5770: { // glGetShaderPrecisionFormat
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5771: { // glDepthRangef
+case 5772: { // glDepthRangef
GLclampf *n = (GLclampf *) bp; bp += 4;
GLclampf *f = (GLclampf *) bp; bp += 4;
weglDepthRangef(*n,*f);
}; break;
-case 5772: { // glClearDepthf
+case 5773: { // glClearDepthf
GLclampf *d = (GLclampf *) bp; bp += 4;
weglClearDepthf(*d);
}; break;
-case 5773: { // glGetProgramBinary
+case 5774: { // glGetProgramBinary
GLuint *program = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
GLsizei length[1] = {0};
@@ -6192,31 +6200,31 @@ case 5773: { // glGetProgramBinary
driver_send_term(port,caller,rt,AP);
driver_free_binary(binary);
}; break;
-case 5774: { // glProgramBinary
+case 5775: { // glProgramBinary
GLuint *program = (GLuint *) bp; bp += 4;
GLenum *binaryFormat = (GLenum *) bp; bp += 4;
GLvoid *binary = (GLvoid *) bins[0];
GLsizei binary_size = bins_sz[0];
weglProgramBinary(*program,*binaryFormat,binary,binary_size);
}; break;
-case 5775: { // glProgramParameteri
+case 5776: { // glProgramParameteri
GLuint *program = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint *value = (GLint *) bp; bp += 4;
weglProgramParameteri(*program,*pname,*value);
}; break;
-case 5776: { // glUseProgramStages
+case 5777: { // glUseProgramStages
GLuint *pipeline = (GLuint *) bp; bp += 4;
GLbitfield *stages = (GLbitfield *) bp; bp += 4;
GLuint *program = (GLuint *) bp; bp += 4;
weglUseProgramStages(*pipeline,*stages,*program);
}; break;
-case 5777: { // glActiveShaderProgram
+case 5778: { // glActiveShaderProgram
GLuint *pipeline = (GLuint *) bp; bp += 4;
GLuint *program = (GLuint *) bp; bp += 4;
weglActiveShaderProgram(*pipeline,*program);
}; break;
-case 5778: { // glCreateShaderProgramv
+case 5779: { // glCreateShaderProgramv
GLenum *type = (GLenum *) bp; bp += 4;
int * stringsLen = (int *) bp; bp += 4;
int * stringsTotSize = (int *) bp; bp += 4;
@@ -6233,16 +6241,16 @@ case 5778: { // glCreateShaderProgramv
driver_send_term(port,caller,rt,AP);
driver_free(strings);
}; break;
-case 5779: { // glBindProgramPipeline
+case 5780: { // glBindProgramPipeline
GLuint *pipeline = (GLuint *) bp; bp += 4;
weglBindProgramPipeline(*pipeline);
}; break;
-case 5780: { // glDeleteProgramPipelines
+case 5781: { // glDeleteProgramPipelines
int * pipelinesLen = (int *) bp; bp += 4;
- GLuint * pipelines = (GLuint *) bp; bp += (8-((*pipelinesLen*4+4)%8))%8;
+ GLuint * pipelines = (GLuint *) bp; bp += *pipelinesLen*4 + (8-((*pipelinesLen*4+4)%8))%8;
weglDeleteProgramPipelines(*pipelinesLen,pipelines);
}; break;
-case 5781: { // glGenProgramPipelines
+case 5782: { // glGenProgramPipelines
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *pipelines;
pipelines = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
@@ -6258,7 +6266,7 @@ case 5781: { // glGenProgramPipelines
driver_free(rt);
driver_free(pipelines);
}; break;
-case 5782: { // glIsProgramPipeline
+case 5783: { // glIsProgramPipeline
GLuint *pipeline = (GLuint *) bp; bp += 4;
GLboolean result = weglIsProgramPipeline(*pipeline);
int AP = 0; ErlDrvTermData rt[6];
@@ -6267,7 +6275,7 @@ case 5782: { // glIsProgramPipeline
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5783: { // glGetProgramPipelineiv
+case 5784: { // glGetProgramPipelineiv
GLuint *pipeline = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
@@ -6278,115 +6286,115 @@ case 5783: { // glGetProgramPipelineiv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5784: { // glProgramUniform1i
+case 5785: { // glProgramUniform1i
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLint *v0 = (GLint *) bp; bp += 4;
weglProgramUniform1i(*program,*location,*v0);
}; break;
-case 5785: { // glProgramUniform1iv
+case 5786: { // glProgramUniform1iv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int * valueLen = (int *) bp; bp += 4;
- GLint * value = (GLint *) bp; bp += (8-((*valueLen*4+4)%8))%8;
+ GLint * value = (GLint *) bp; bp += *valueLen*4 + (8-((*valueLen*4+4)%8))%8;
weglProgramUniform1iv(*program,*location,*valueLen,value);
}; break;
-case 5786: { // glProgramUniform1f
+case 5787: { // glProgramUniform1f
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLfloat *v0 = (GLfloat *) bp; bp += 4;
weglProgramUniform1f(*program,*location,*v0);
}; break;
-case 5787: { // glProgramUniform1fv
+case 5788: { // glProgramUniform1fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int * valueLen = (int *) bp; bp += 4;
- GLfloat * value = (GLfloat *) bp; bp += (8-((*valueLen*4+4)%8))%8;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*4 + (8-((*valueLen*4+4)%8))%8;
weglProgramUniform1fv(*program,*location,*valueLen,value);
}; break;
-case 5788: { // glProgramUniform1d
+case 5789: { // glProgramUniform1d
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLdouble *v0 = (GLdouble *) bp; bp += 8;
weglProgramUniform1d(*program,*location,*v0);
}; break;
-case 5789: { // glProgramUniform1dv
+case 5790: { // glProgramUniform1dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int * valueLen = (int *) bp; bp += 8;
- GLdouble * value = (GLdouble *) bp; bp += (8-((*valueLen*8+0)%8))%8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*8 + (8-((*valueLen*8+0)%8))%8;
weglProgramUniform1dv(*program,*location,*valueLen,value);
}; break;
-case 5790: { // glProgramUniform1ui
+case 5791: { // glProgramUniform1ui
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
weglProgramUniform1ui(*program,*location,*v0);
}; break;
-case 5791: { // glProgramUniform1uiv
+case 5792: { // glProgramUniform1uiv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int * valueLen = (int *) bp; bp += 4;
- GLuint * value = (GLuint *) bp; bp += (8-((*valueLen*4+4)%8))%8;
+ GLuint * value = (GLuint *) bp; bp += *valueLen*4 + (8-((*valueLen*4+4)%8))%8;
weglProgramUniform1uiv(*program,*location,*valueLen,value);
}; break;
-case 5792: { // glProgramUniform2i
+case 5793: { // glProgramUniform2i
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLint *v0 = (GLint *) bp; bp += 4;
GLint *v1 = (GLint *) bp; bp += 4;
weglProgramUniform2i(*program,*location,*v0,*v1);
}; break;
-case 5793: { // glProgramUniform2iv
+case 5794: { // glProgramUniform2iv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLint * value = (GLint *) bp; bp += *valueLen*8;
weglProgramUniform2iv(*program,*location,*valueLen,value);
}; break;
-case 5794: { // glProgramUniform2f
+case 5795: { // glProgramUniform2f
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLfloat *v0 = (GLfloat *) bp; bp += 4;
GLfloat *v1 = (GLfloat *) bp; bp += 4;
weglProgramUniform2f(*program,*location,*v0,*v1);
}; break;
-case 5795: { // glProgramUniform2fv
+case 5796: { // glProgramUniform2fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLfloat * value = (GLfloat *) bp; bp += *valueLen*8;
weglProgramUniform2fv(*program,*location,*valueLen,value);
}; break;
-case 5796: { // glProgramUniform2d
+case 5797: { // glProgramUniform2d
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLdouble *v0 = (GLdouble *) bp; bp += 8;
GLdouble *v1 = (GLdouble *) bp; bp += 8;
weglProgramUniform2d(*program,*location,*v0,*v1);
}; break;
-case 5797: { // glProgramUniform2dv
+case 5798: { // glProgramUniform2dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 8;
GLdouble * value = (GLdouble *) bp; bp += *valueLen*16;
weglProgramUniform2dv(*program,*location,*valueLen,value);
}; break;
-case 5798: { // glProgramUniform2ui
+case 5799: { // glProgramUniform2ui
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
GLuint *v1 = (GLuint *) bp; bp += 4;
weglProgramUniform2ui(*program,*location,*v0,*v1);
}; break;
-case 5799: { // glProgramUniform2uiv
+case 5800: { // glProgramUniform2uiv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint * value = (GLuint *) bp; bp += *valueLen*8;
weglProgramUniform2uiv(*program,*location,*valueLen,value);
}; break;
-case 5800: { // glProgramUniform3i
+case 5801: { // glProgramUniform3i
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLint *v0 = (GLint *) bp; bp += 4;
@@ -6394,14 +6402,14 @@ case 5800: { // glProgramUniform3i
GLint *v2 = (GLint *) bp; bp += 4;
weglProgramUniform3i(*program,*location,*v0,*v1,*v2);
}; break;
-case 5801: { // glProgramUniform3iv
+case 5802: { // glProgramUniform3iv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLint * value = (GLint *) bp; bp += *valueLen*12;
weglProgramUniform3iv(*program,*location,*valueLen,value);
}; break;
-case 5802: { // glProgramUniform3f
+case 5803: { // glProgramUniform3f
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLfloat *v0 = (GLfloat *) bp; bp += 4;
@@ -6409,14 +6417,14 @@ case 5802: { // glProgramUniform3f
GLfloat *v2 = (GLfloat *) bp; bp += 4;
weglProgramUniform3f(*program,*location,*v0,*v1,*v2);
}; break;
-case 5803: { // glProgramUniform3fv
+case 5804: { // glProgramUniform3fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLfloat * value = (GLfloat *) bp; bp += *valueLen*12;
weglProgramUniform3fv(*program,*location,*valueLen,value);
}; break;
-case 5804: { // glProgramUniform3d
+case 5805: { // glProgramUniform3d
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLdouble *v0 = (GLdouble *) bp; bp += 8;
@@ -6424,14 +6432,14 @@ case 5804: { // glProgramUniform3d
GLdouble *v2 = (GLdouble *) bp; bp += 8;
weglProgramUniform3d(*program,*location,*v0,*v1,*v2);
}; break;
-case 5805: { // glProgramUniform3dv
+case 5806: { // glProgramUniform3dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 8;
GLdouble * value = (GLdouble *) bp; bp += *valueLen*24;
weglProgramUniform3dv(*program,*location,*valueLen,value);
}; break;
-case 5806: { // glProgramUniform3ui
+case 5807: { // glProgramUniform3ui
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
@@ -6439,14 +6447,14 @@ case 5806: { // glProgramUniform3ui
GLuint *v2 = (GLuint *) bp; bp += 4;
weglProgramUniform3ui(*program,*location,*v0,*v1,*v2);
}; break;
-case 5807: { // glProgramUniform3uiv
+case 5808: { // glProgramUniform3uiv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint * value = (GLuint *) bp; bp += *valueLen*12;
weglProgramUniform3uiv(*program,*location,*valueLen,value);
}; break;
-case 5808: { // glProgramUniform4i
+case 5809: { // glProgramUniform4i
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLint *v0 = (GLint *) bp; bp += 4;
@@ -6455,14 +6463,14 @@ case 5808: { // glProgramUniform4i
GLint *v3 = (GLint *) bp; bp += 4;
weglProgramUniform4i(*program,*location,*v0,*v1,*v2,*v3);
}; break;
-case 5809: { // glProgramUniform4iv
+case 5810: { // glProgramUniform4iv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLint * value = (GLint *) bp; bp += *valueLen*16;
weglProgramUniform4iv(*program,*location,*valueLen,value);
}; break;
-case 5810: { // glProgramUniform4f
+case 5811: { // glProgramUniform4f
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLfloat *v0 = (GLfloat *) bp; bp += 4;
@@ -6471,14 +6479,14 @@ case 5810: { // glProgramUniform4f
GLfloat *v3 = (GLfloat *) bp; bp += 4;
weglProgramUniform4f(*program,*location,*v0,*v1,*v2,*v3);
}; break;
-case 5811: { // glProgramUniform4fv
+case 5812: { // glProgramUniform4fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLfloat * value = (GLfloat *) bp; bp += *valueLen*16;
weglProgramUniform4fv(*program,*location,*valueLen,value);
}; break;
-case 5812: { // glProgramUniform4d
+case 5813: { // glProgramUniform4d
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLdouble *v0 = (GLdouble *) bp; bp += 8;
@@ -6487,14 +6495,14 @@ case 5812: { // glProgramUniform4d
GLdouble *v3 = (GLdouble *) bp; bp += 8;
weglProgramUniform4d(*program,*location,*v0,*v1,*v2,*v3);
}; break;
-case 5813: { // glProgramUniform4dv
+case 5814: { // glProgramUniform4dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 8;
GLdouble * value = (GLdouble *) bp; bp += *valueLen*32;
weglProgramUniform4dv(*program,*location,*valueLen,value);
}; break;
-case 5814: { // glProgramUniform4ui
+case 5815: { // glProgramUniform4ui
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
@@ -6503,14 +6511,14 @@ case 5814: { // glProgramUniform4ui
GLuint *v3 = (GLuint *) bp; bp += 4;
weglProgramUniform4ui(*program,*location,*v0,*v1,*v2,*v3);
}; break;
-case 5815: { // glProgramUniform4uiv
+case 5816: { // glProgramUniform4uiv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint * value = (GLuint *) bp; bp += *valueLen*16;
weglProgramUniform4uiv(*program,*location,*valueLen,value);
}; break;
-case 5816: { // glProgramUniformMatrix2fv
+case 5817: { // glProgramUniformMatrix2fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6519,7 +6527,7 @@ case 5816: { // glProgramUniformMatrix2fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*16;
weglProgramUniformMatrix2fv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5817: { // glProgramUniformMatrix3fv
+case 5818: { // glProgramUniformMatrix3fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6528,7 +6536,7 @@ case 5817: { // glProgramUniformMatrix3fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*36;
weglProgramUniformMatrix3fv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5818: { // glProgramUniformMatrix4fv
+case 5819: { // glProgramUniformMatrix4fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6537,7 +6545,7 @@ case 5818: { // glProgramUniformMatrix4fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*64;
weglProgramUniformMatrix4fv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5819: { // glProgramUniformMatrix2dv
+case 5820: { // glProgramUniformMatrix2dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6546,7 +6554,7 @@ case 5819: { // glProgramUniformMatrix2dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*32;
weglProgramUniformMatrix2dv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5820: { // glProgramUniformMatrix3dv
+case 5821: { // glProgramUniformMatrix3dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6555,7 +6563,7 @@ case 5820: { // glProgramUniformMatrix3dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*72;
weglProgramUniformMatrix3dv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5821: { // glProgramUniformMatrix4dv
+case 5822: { // glProgramUniformMatrix4dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6564,7 +6572,7 @@ case 5821: { // glProgramUniformMatrix4dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*128;
weglProgramUniformMatrix4dv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5822: { // glProgramUniformMatrix2x3fv
+case 5823: { // glProgramUniformMatrix2x3fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6573,7 +6581,7 @@ case 5822: { // glProgramUniformMatrix2x3fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*24;
weglProgramUniformMatrix2x3fv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5823: { // glProgramUniformMatrix3x2fv
+case 5824: { // glProgramUniformMatrix3x2fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6582,7 +6590,7 @@ case 5823: { // glProgramUniformMatrix3x2fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*24;
weglProgramUniformMatrix3x2fv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5824: { // glProgramUniformMatrix2x4fv
+case 5825: { // glProgramUniformMatrix2x4fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6591,7 +6599,7 @@ case 5824: { // glProgramUniformMatrix2x4fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*32;
weglProgramUniformMatrix2x4fv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5825: { // glProgramUniformMatrix4x2fv
+case 5826: { // glProgramUniformMatrix4x2fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6600,7 +6608,7 @@ case 5825: { // glProgramUniformMatrix4x2fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*32;
weglProgramUniformMatrix4x2fv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5826: { // glProgramUniformMatrix3x4fv
+case 5827: { // glProgramUniformMatrix3x4fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6609,7 +6617,7 @@ case 5826: { // glProgramUniformMatrix3x4fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*48;
weglProgramUniformMatrix3x4fv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5827: { // glProgramUniformMatrix4x3fv
+case 5828: { // glProgramUniformMatrix4x3fv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6618,7 +6626,7 @@ case 5827: { // glProgramUniformMatrix4x3fv
GLfloat * value = (GLfloat *) bp; bp += *valueLen*48;
weglProgramUniformMatrix4x3fv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5828: { // glProgramUniformMatrix2x3dv
+case 5829: { // glProgramUniformMatrix2x3dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6627,7 +6635,7 @@ case 5828: { // glProgramUniformMatrix2x3dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*48;
weglProgramUniformMatrix2x3dv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5829: { // glProgramUniformMatrix3x2dv
+case 5830: { // glProgramUniformMatrix3x2dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6636,7 +6644,7 @@ case 5829: { // glProgramUniformMatrix3x2dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*48;
weglProgramUniformMatrix3x2dv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5830: { // glProgramUniformMatrix2x4dv
+case 5831: { // glProgramUniformMatrix2x4dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6645,7 +6653,7 @@ case 5830: { // glProgramUniformMatrix2x4dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*64;
weglProgramUniformMatrix2x4dv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5831: { // glProgramUniformMatrix4x2dv
+case 5832: { // glProgramUniformMatrix4x2dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6654,7 +6662,7 @@ case 5831: { // glProgramUniformMatrix4x2dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*64;
weglProgramUniformMatrix4x2dv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5832: { // glProgramUniformMatrix3x4dv
+case 5833: { // glProgramUniformMatrix3x4dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6663,7 +6671,7 @@ case 5832: { // glProgramUniformMatrix3x4dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*96;
weglProgramUniformMatrix3x4dv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5833: { // glProgramUniformMatrix4x3dv
+case 5834: { // glProgramUniformMatrix4x3dv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLboolean *transpose = (GLboolean *) bp; bp += 1;
@@ -6672,11 +6680,11 @@ case 5833: { // glProgramUniformMatrix4x3dv
GLdouble * value = (GLdouble *) bp; bp += *valueLen*96;
weglProgramUniformMatrix4x3dv(*program,*location,*valueLen,*transpose,value);
}; break;
-case 5834: { // glValidateProgramPipeline
+case 5835: { // glValidateProgramPipeline
GLuint *pipeline = (GLuint *) bp; bp += 4;
weglValidateProgramPipeline(*pipeline);
}; break;
-case 5835: { // glGetProgramPipelineInfoLog
+case 5836: { // glGetProgramPipelineInfoLog
GLuint *pipeline = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
GLsizei length[1] = {0};
@@ -6690,31 +6698,31 @@ case 5835: { // glGetProgramPipelineInfoLog
driver_send_term(port,caller,rt,AP);
driver_free(infoLog);
}; break;
-case 5836: { // glVertexAttribL1dv
+case 5837: { // glVertexAttribL1dv
GLuint *index = (GLuint *) bp; bp += 4;
bp += 4;
GLdouble *v = (GLdouble *) bp; bp += 8;
weglVertexAttribL1dv(*index,v);
}; break;
-case 5837: { // glVertexAttribL2dv
+case 5838: { // glVertexAttribL2dv
GLuint *index = (GLuint *) bp; bp += 4;
bp += 4;
GLdouble *v = (GLdouble *) bp; bp += 8;
weglVertexAttribL2dv(*index,v);
}; break;
-case 5838: { // glVertexAttribL3dv
+case 5839: { // glVertexAttribL3dv
GLuint *index = (GLuint *) bp; bp += 4;
bp += 4;
GLdouble *v = (GLdouble *) bp; bp += 8;
weglVertexAttribL3dv(*index,v);
}; break;
-case 5839: { // glVertexAttribL4dv
+case 5840: { // glVertexAttribL4dv
GLuint *index = (GLuint *) bp; bp += 4;
bp += 4;
GLdouble *v = (GLdouble *) bp; bp += 8;
weglVertexAttribL4dv(*index,v);
}; break;
-case 5840: { // glVertexAttribLPointer
+case 5841: { // glVertexAttribLPointer
GLuint *index = (GLuint *) bp; bp += 4;
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -6722,7 +6730,7 @@ case 5840: { // glVertexAttribLPointer
GLvoid *pointer = (GLvoid *) (ErlDrvSInt) * (int *) bp; bp += 4;
weglVertexAttribLPointer(*index,*size,*type,*stride,pointer);
}; break;
-case 5841: { // glVertexAttribLPointer
+case 5842: { // glVertexAttribLPointer
GLuint *index = (GLuint *) bp; bp += 4;
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -6730,7 +6738,7 @@ case 5841: { // glVertexAttribLPointer
GLvoid *pointer = (GLvoid *) bins[0];
weglVertexAttribLPointer(*index,*size,*type,*stride,pointer);
}; break;
-case 5842: { // glGetVertexAttribLdv
+case 5843: { // glGetVertexAttribLdv
GLuint *index = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLdouble params[4] = {0.0,0.0,0.0,0.0};
@@ -6746,13 +6754,13 @@ case 5842: { // glGetVertexAttribLdv
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5843: { // glViewportArrayv
+case 5844: { // glViewportArrayv
GLuint *first = (GLuint *) bp; bp += 4;
int *vLen = (int *) bp; bp += 4;
GLfloat * v = (GLfloat *) bp; bp += *vLen*16;
weglViewportArrayv(*first,*vLen,v);
}; break;
-case 5844: { // glViewportIndexedf
+case 5845: { // glViewportIndexedf
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat *x = (GLfloat *) bp; bp += 4;
GLfloat *y = (GLfloat *) bp; bp += 4;
@@ -6760,18 +6768,18 @@ case 5844: { // glViewportIndexedf
GLfloat *h = (GLfloat *) bp; bp += 4;
weglViewportIndexedf(*index,*x,*y,*w,*h);
}; break;
-case 5845: { // glViewportIndexedfv
+case 5846: { // glViewportIndexedfv
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat * v = (GLfloat *) bp; bp += 16;
weglViewportIndexedfv(*index,v);
}; break;
-case 5846: { // glScissorArrayv
+case 5847: { // glScissorArrayv
GLuint *first = (GLuint *) bp; bp += 4;
int *vLen = (int *) bp; bp += 4;
GLint * v = (GLint *) bp; bp += *vLen*16;
weglScissorArrayv(*first,*vLen,v);
}; break;
-case 5847: { // glScissorIndexed
+case 5848: { // glScissorIndexed
GLuint *index = (GLuint *) bp; bp += 4;
GLint *left = (GLint *) bp; bp += 4;
GLint *bottom = (GLint *) bp; bp += 4;
@@ -6779,26 +6787,26 @@ case 5847: { // glScissorIndexed
GLsizei *height = (GLsizei *) bp; bp += 4;
weglScissorIndexed(*index,*left,*bottom,*width,*height);
}; break;
-case 5848: { // glScissorIndexedv
+case 5849: { // glScissorIndexedv
GLuint *index = (GLuint *) bp; bp += 4;
GLint * v = (GLint *) bp; bp += 16;
weglScissorIndexedv(*index,v);
}; break;
-case 5849: { // glDepthRangeArrayv
+case 5850: { // glDepthRangeArrayv
GLuint *first = (GLuint *) bp; bp += 4;
bp += 4;
int *vLen = (int *) bp; bp += 8;
GLclampd * v = (GLclampd *) bp; bp += *vLen*16;
weglDepthRangeArrayv(*first,*vLen,v);
}; break;
-case 5850: { // glDepthRangeIndexed
+case 5851: { // glDepthRangeIndexed
GLuint *index = (GLuint *) bp; bp += 4;
bp += 4;
GLclampd *n = (GLclampd *) bp; bp += 8;
GLclampd *f = (GLclampd *) bp; bp += 8;
weglDepthRangeIndexed(*index,*n,*f);
}; break;
-case 5851: { // glGetFloati_v
+case 5852: { // glGetFloati_v
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat data[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
@@ -6827,7 +6835,7 @@ case 5851: { // glGetFloati_v
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5852: { // glGetDoublei_v
+case 5853: { // glGetDoublei_v
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble data[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
@@ -6855,16 +6863,16 @@ case 5852: { // glGetDoublei_v
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5853: { // glDebugMessageControlARB
+case 5854: { // glDebugMessageControlARB
GLenum *source = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLenum *severity = (GLenum *) bp; bp += 4;
int * idsLen = (int *) bp; bp += 4;
- GLuint * ids = (GLuint *) bp; bp += (8-((*idsLen*4+0)%8))%8;
+ GLuint * ids = (GLuint *) bp; bp += *idsLen*4 + (8-((*idsLen*4+0)%8))%8;
GLboolean *enabled = (GLboolean *) bp; bp += 1;
weglDebugMessageControlARB(*source,*type,*severity,*idsLen,ids,*enabled);
}; break;
-case 5854: { // glDebugMessageInsertARB
+case 5855: { // glDebugMessageInsertARB
GLenum *source = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLuint *id = (GLuint *) bp; bp += 4;
@@ -6873,7 +6881,7 @@ case 5854: { // glDebugMessageInsertARB
int bufLen[1] = {(int)strlen((char *)buf)}; bp += bufLen[0]+1+((8-((1+bufLen[0]+0)%8))%8);
weglDebugMessageInsertARB(*source,*type,*id,*severity,*bufLen,buf);
}; break;
-case 5855: { // glGetDebugMessageLogARB
+case 5856: { // glGetDebugMessageLogARB
GLuint *count = (GLuint *) bp; bp += 4;
GLsizei *bufsize = (GLsizei *) bp; bp += 4;
GLenum *sources;
@@ -6920,7 +6928,7 @@ case 5855: { // glGetDebugMessageLogARB
driver_free(types);
driver_free(sources);
}; break;
-case 5856: { // glGetGraphicsResetStatusARB
+case 5857: { // glGetGraphicsResetStatusARB
GLenum result = weglGetGraphicsResetStatusARB();
int AP = 0; ErlDrvTermData rt[6];
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
@@ -6928,7 +6936,7 @@ case 5856: { // glGetGraphicsResetStatusARB
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
driver_send_term(port,caller,rt,AP);
}; break;
-case 5857: { // glDrawArraysInstancedBaseInstance
+case 5858: { // glDrawArraysInstancedBaseInstance
GLenum *mode = (GLenum *) bp; bp += 4;
GLint *first = (GLint *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
@@ -6936,7 +6944,7 @@ case 5857: { // glDrawArraysInstancedBaseInstance
GLuint *baseinstance = (GLuint *) bp; bp += 4;
weglDrawArraysInstancedBaseInstance(*mode,*first,*count,*primcount,*baseinstance);
}; break;
-case 5858: { // glDrawElementsInstancedBaseInstance
+case 5859: { // glDrawElementsInstancedBaseInstance
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -6945,7 +6953,7 @@ case 5858: { // glDrawElementsInstancedBaseInstance
GLuint *baseinstance = (GLuint *) bp; bp += 4;
weglDrawElementsInstancedBaseInstance(*mode,*count,*type,indices,*primcount,*baseinstance);
}; break;
-case 5859: { // glDrawElementsInstancedBaseInstance
+case 5860: { // glDrawElementsInstancedBaseInstance
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -6954,7 +6962,7 @@ case 5859: { // glDrawElementsInstancedBaseInstance
GLuint *baseinstance = (GLuint *) bp; bp += 4;
weglDrawElementsInstancedBaseInstance(*mode,*count,*type,indices,*primcount,*baseinstance);
}; break;
-case 5860: { // glDrawElementsInstancedBaseVertexBaseInstance
+case 5861: { // glDrawElementsInstancedBaseVertexBaseInstance
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -6964,7 +6972,7 @@ case 5860: { // glDrawElementsInstancedBaseVertexBaseInstance
GLuint *baseinstance = (GLuint *) bp; bp += 4;
weglDrawElementsInstancedBaseVertexBaseInstance(*mode,*count,*type,indices,*primcount,*basevertex,*baseinstance);
}; break;
-case 5861: { // glDrawElementsInstancedBaseVertexBaseInstance
+case 5862: { // glDrawElementsInstancedBaseVertexBaseInstance
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
@@ -6974,20 +6982,20 @@ case 5861: { // glDrawElementsInstancedBaseVertexBaseInstance
GLuint *baseinstance = (GLuint *) bp; bp += 4;
weglDrawElementsInstancedBaseVertexBaseInstance(*mode,*count,*type,indices,*primcount,*basevertex,*baseinstance);
}; break;
-case 5862: { // glDrawTransformFeedbackInstanced
+case 5863: { // glDrawTransformFeedbackInstanced
GLenum *mode = (GLenum *) bp; bp += 4;
GLuint *id = (GLuint *) bp; bp += 4;
GLsizei *primcount = (GLsizei *) bp; bp += 4;
weglDrawTransformFeedbackInstanced(*mode,*id,*primcount);
}; break;
-case 5863: { // glDrawTransformFeedbackStreamInstanced
+case 5864: { // glDrawTransformFeedbackStreamInstanced
GLenum *mode = (GLenum *) bp; bp += 4;
GLuint *id = (GLuint *) bp; bp += 4;
GLuint *stream = (GLuint *) bp; bp += 4;
GLsizei *primcount = (GLsizei *) bp; bp += 4;
weglDrawTransformFeedbackStreamInstanced(*mode,*id,*stream,*primcount);
}; break;
-case 5864: { // glGetInternalformativ
+case 5865: { // glGetInternalformativ
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *internalformat = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
@@ -7006,7 +7014,7 @@ case 5864: { // glGetInternalformativ
driver_free(rt);
driver_free(params);
}; break;
-case 5865: { // glBindImageTexture
+case 5866: { // glBindImageTexture
GLuint *unit = (GLuint *) bp; bp += 4;
GLuint *texture = (GLuint *) bp; bp += 4;
GLint *level = (GLint *) bp; bp += 4;
@@ -7017,18 +7025,18 @@ case 5865: { // glBindImageTexture
GLenum *format = (GLenum *) bp; bp += 4;
weglBindImageTexture(*unit,*texture,*level,*layered,*layer,*access,*format);
}; break;
-case 5866: { // glMemoryBarrier
+case 5867: { // glMemoryBarrier
GLbitfield *barriers = (GLbitfield *) bp; bp += 4;
weglMemoryBarrier(*barriers);
}; break;
-case 5867: { // glTexStorage1D
+case 5868: { // glTexStorage1D
GLenum *target = (GLenum *) bp; bp += 4;
GLsizei *levels = (GLsizei *) bp; bp += 4;
GLenum *internalformat = (GLenum *) bp; bp += 4;
GLsizei *width = (GLsizei *) bp; bp += 4;
weglTexStorage1D(*target,*levels,*internalformat,*width);
}; break;
-case 5868: { // glTexStorage2D
+case 5869: { // glTexStorage2D
GLenum *target = (GLenum *) bp; bp += 4;
GLsizei *levels = (GLsizei *) bp; bp += 4;
GLenum *internalformat = (GLenum *) bp; bp += 4;
@@ -7036,7 +7044,7 @@ case 5868: { // glTexStorage2D
GLsizei *height = (GLsizei *) bp; bp += 4;
weglTexStorage2D(*target,*levels,*internalformat,*width,*height);
}; break;
-case 5869: { // glTexStorage3D
+case 5870: { // glTexStorage3D
GLenum *target = (GLenum *) bp; bp += 4;
GLsizei *levels = (GLsizei *) bp; bp += 4;
GLenum *internalformat = (GLenum *) bp; bp += 4;
@@ -7045,12 +7053,12 @@ case 5869: { // glTexStorage3D
GLsizei *depth = (GLsizei *) bp; bp += 4;
weglTexStorage3D(*target,*levels,*internalformat,*width,*height,*depth);
}; break;
-case 5870: { // glDepthBoundsEXT
+case 5871: { // glDepthBoundsEXT
GLclampd *zmin = (GLclampd *) bp; bp += 8;
GLclampd *zmax = (GLclampd *) bp; bp += 8;
weglDepthBoundsEXT(*zmin,*zmax);
}; break;
-case 5871: { // glStencilClearTagEXT
+case 5872: { // glStencilClearTagEXT
GLsizei *stencilTagBits = (GLsizei *) bp; bp += 4;
GLuint *stencilClearTag = (GLuint *) bp; bp += 4;
weglStencilClearTagEXT(*stencilTagBits,*stencilClearTag);
diff --git a/lib/wx/c_src/gen/wxe_events.cpp b/lib/wx/c_src/gen/wxe_events.cpp
index 4affe2ba53..01787c8a64 100644
--- a/lib/wx/c_src/gen/wxe_events.cpp
+++ b/lib/wx/c_src/gen/wxe_events.cpp
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2015. All Rights Reserved.
+ * Copyright Ericsson AB 2008-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.
@@ -306,6 +306,7 @@ void initEventTable()
{wxEVT_ACTIVATE_APP, 232, "activate_app"},
{wxEVT_HIBERNATE, 232, "hibernate"},
{wxEVT_MOUSE_CAPTURE_LOST, 235, "mouse_capture_lost"},
+ {wxEVT_DROP_FILES, 238, "drop_files"},
{-1, 0, }
};
for(int i=0; event_types[i].ev_type != -1; i++) {
@@ -881,6 +882,18 @@ case 235: {// wxMouseCaptureLostEvent
rt.addTupleCount(2);
break;
}
+case 238: {// wxDropFilesEvent
+ wxDropFilesEvent * ev = (wxDropFilesEvent *) event;
+ evClass = (char*)"wxDropFilesEvent";
+ rt.addAtom((char*)"wxDropFiles");
+ rt.addAtom(Etype->eName);
+ rt.addInt(ev->m_noFiles);
+ rt.add(ev->m_pos);
+ wxArrayString tmpArrayStr(ev->m_noFiles, ev->m_files);
+ rt.add(tmpArrayStr);
+ rt.addTupleCount(5);
+ break;
+}
}
rt.addTupleCount(5);
@@ -897,7 +910,7 @@ case 235: {// wxMouseCaptureLostEvent
} else {
send_res = rt.send();
if(cb->skip) event->Skip();
- if(app->recurse_level < 1) {
+ if(app->recurse_level < 1 && Etype->cID != 168) {
app->recurse_level++;
app->dispatch_cmds();
app->recurse_level--;
diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp
index 4243d8a35a..5425e9f3cb 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -315,6 +315,15 @@ case wxWindow_Disable: { // wxWindow::Disable
rt.addBool(Result);
break;
}
+#if wxCHECK_VERSION(2,8,10)
+case wxWindow_DragAcceptFiles: { // wxWindow::DragAcceptFiles
+ wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
+ bool * accept = (bool *) bp; bp += 4;
+ if(!This) throw wxe_badarg(0);
+ This->DragAcceptFiles(*accept);
+ break;
+}
+#endif
case wxWindow_Enable: { // wxWindow::Enable
bool enable=true;
wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
@@ -1707,6 +1716,15 @@ case wxWindow_SetDoubleBuffered: { // wxWindow::SetDoubleBuffered
break;
}
#endif
+#if wxCHECK_VERSION(2,9,5)
+case wxWindow_GetContentScaleFactor: { // wxWindow::GetContentScaleFactor
+ wxWindow *This = (wxWindow *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ double Result = This->GetContentScaleFactor();
+ rt.addFloat(Result);
+ break;
+}
+#endif
case wxTopLevelWindow_GetIcon: { // wxTopLevelWindow::GetIcon
wxTopLevelWindow *This = (wxTopLevelWindow *) getPtr(bp,memenv); bp += 4;
if(!This) throw wxe_badarg(0);
@@ -32049,6 +32067,28 @@ case wxDCOverlay_Clear: { // wxDCOverlay::Clear
This->Clear();
break;
}
+case wxDropFilesEvent_GetPosition: { // wxDropFilesEvent::GetPosition
+ wxDropFilesEvent *This = (wxDropFilesEvent *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxPoint Result = This->GetPosition();
+ rt.add(Result);
+ break;
+}
+case wxDropFilesEvent_GetNumberOfFiles: { // wxDropFilesEvent::GetNumberOfFiles
+ wxDropFilesEvent *This = (wxDropFilesEvent *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ int Result = This->GetNumberOfFiles();
+ rt.addInt(Result);
+ break;
+}
+case wxDropFilesEvent_GetFiles: { // wxDropFilesEvent::GetFiles
+ wxDropFilesEvent *This = (wxDropFilesEvent *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxString * Result = (wxString*)This->GetFiles();
+ wxArrayString tmpArrayStr(This->m_noFiles, Result);
+ rt.add(tmpArrayStr);
+ break;
+}
default: {
wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false); error.addAtom("_wxe_error_");
error.addInt((int) op);
diff --git a/lib/wx/c_src/gen/wxe_macros.h b/lib/wx/c_src/gen/wxe_macros.h
index 82b39b49cd..f44fa57053 100644
--- a/lib/wx/c_src/gen/wxe_macros.h
+++ b/lib/wx/c_src/gen/wxe_macros.h
@@ -94,3332 +94,3337 @@
#define wxWindow_Destroy 123
#define wxWindow_DestroyChildren 124
#define wxWindow_Disable 125
-#define wxWindow_Enable 126
-#define wxWindow_FindFocus 127
-#define wxWindow_FindWindow_1_0 128
-#define wxWindow_FindWindow_1_1 129
-#define wxWindow_FindWindowById 130
-#define wxWindow_FindWindowByName 131
-#define wxWindow_FindWindowByLabel 132
-#define wxWindow_Fit 133
-#define wxWindow_FitInside 134
-#define wxWindow_Freeze 135
-#define wxWindow_GetAcceleratorTable 136
-#define wxWindow_GetBackgroundColour 137
-#define wxWindow_GetBackgroundStyle 138
-#define wxWindow_GetBestSize 139
-#define wxWindow_GetCaret 141
-#define wxWindow_GetCapture 142
-#define wxWindow_GetCharHeight 143
-#define wxWindow_GetCharWidth 144
-#define wxWindow_GetChildren 145
-#define wxWindow_GetClientSize 148
-#define wxWindow_GetContainingSizer 149
-#define wxWindow_GetCursor 150
-#define wxWindow_GetDropTarget 151
-#define wxWindow_GetEventHandler 152
-#define wxWindow_GetExtraStyle 153
-#define wxWindow_GetFont 154
-#define wxWindow_GetForegroundColour 155
-#define wxWindow_GetGrandParent 156
-#define wxWindow_GetHandle 157
-#define wxWindow_GetHelpText 158
-#define wxWindow_GetId 159
-#define wxWindow_GetLabel 160
-#define wxWindow_GetMaxSize 161
-#define wxWindow_GetMinSize 162
-#define wxWindow_GetName 163
-#define wxWindow_GetParent 164
-#define wxWindow_GetPosition 166
-#define wxWindow_GetRect 167
-#define wxWindow_GetScreenPosition 169
-#define wxWindow_GetScreenRect 170
-#define wxWindow_GetScrollPos 171
-#define wxWindow_GetScrollRange 172
-#define wxWindow_GetScrollThumb 173
-#define wxWindow_GetSize 175
-#define wxWindow_GetSizer 176
-#define wxWindow_GetTextExtent 177
-#define wxWindow_GetToolTip 178
-#define wxWindow_GetUpdateRegion 179
-#define wxWindow_GetVirtualSize 181
-#define wxWindow_GetWindowStyleFlag 183
-#define wxWindow_GetWindowVariant 184
-#define wxWindow_HasCapture 185
-#define wxWindow_HasScrollbar 186
-#define wxWindow_HasTransparentBackground 187
-#define wxWindow_Hide 188
-#define wxWindow_InheritAttributes 189
-#define wxWindow_InitDialog 190
-#define wxWindow_InvalidateBestSize 191
-#define wxWindow_IsEnabled 192
-#define wxWindow_IsExposed_2 193
-#define wxWindow_IsExposed_4 194
-#define wxWindow_IsExposed_1_0 195
-#define wxWindow_IsExposed_1_1 196
-#define wxWindow_IsRetained 197
-#define wxWindow_IsShown 198
-#define wxWindow_IsTopLevel 199
-#define wxWindow_Layout 200
-#define wxWindow_LineDown 201
-#define wxWindow_LineUp 202
-#define wxWindow_Lower 203
-#define wxWindow_MakeModal 204
-#define wxWindow_Move_3 205
-#define wxWindow_Move_2 206
-#define wxWindow_MoveAfterInTabOrder 207
-#define wxWindow_MoveBeforeInTabOrder 208
-#define wxWindow_Navigate 209
-#define wxWindow_PageDown 210
-#define wxWindow_PageUp 211
-#define wxWindow_PopEventHandler 212
-#define wxWindow_PopupMenu_2 213
-#define wxWindow_PopupMenu_3 214
-#define wxWindow_Raise 215
-#define wxWindow_Refresh 216
-#define wxWindow_RefreshRect 217
-#define wxWindow_ReleaseMouse 218
-#define wxWindow_RemoveChild 219
-#define wxWindow_Reparent 220
-#define wxWindow_ScreenToClient_2 221
-#define wxWindow_ScreenToClient_1 222
-#define wxWindow_ScrollLines 224
-#define wxWindow_ScrollPages 226
-#define wxWindow_ScrollWindow 227
-#define wxWindow_SetAcceleratorTable 228
-#define wxWindow_SetAutoLayout 229
-#define wxWindow_SetBackgroundColour 230
-#define wxWindow_SetBackgroundStyle 231
-#define wxWindow_SetCaret 232
-#define wxWindow_SetClientSize_2 233
-#define wxWindow_SetClientSize_1_0 234
-#define wxWindow_SetClientSize_1_1 235
-#define wxWindow_SetContainingSizer 236
-#define wxWindow_SetCursor 237
-#define wxWindow_SetMaxSize 238
-#define wxWindow_SetMinSize 239
-#define wxWindow_SetOwnBackgroundColour 240
-#define wxWindow_SetOwnFont 241
-#define wxWindow_SetOwnForegroundColour 242
-#define wxWindow_SetDropTarget 243
-#define wxWindow_SetExtraStyle 244
-#define wxWindow_SetFocus 245
-#define wxWindow_SetFocusFromKbd 246
-#define wxWindow_SetFont 247
-#define wxWindow_SetForegroundColour 248
-#define wxWindow_SetHelpText 249
-#define wxWindow_SetId 250
-#define wxWindow_SetLabel 252
-#define wxWindow_SetName 253
-#define wxWindow_SetPalette 254
-#define wxWindow_SetScrollbar 255
-#define wxWindow_SetScrollPos 256
-#define wxWindow_SetSize_5 257
-#define wxWindow_SetSize_2_0 258
-#define wxWindow_SetSize_1 259
-#define wxWindow_SetSize_2_1 260
-#define wxWindow_SetSizeHints_3 261
-#define wxWindow_SetSizeHints_2 262
-#define wxWindow_SetSizer 263
-#define wxWindow_SetSizerAndFit 264
-#define wxWindow_SetThemeEnabled 265
-#define wxWindow_SetToolTip_1_0 266
-#define wxWindow_SetToolTip_1_1 267
-#define wxWindow_SetVirtualSize_1 268
-#define wxWindow_SetVirtualSize_2 269
-#define wxWindow_SetVirtualSizeHints_3 270
-#define wxWindow_SetVirtualSizeHints_2 271
-#define wxWindow_SetWindowStyle 272
-#define wxWindow_SetWindowStyleFlag 273
-#define wxWindow_SetWindowVariant 274
-#define wxWindow_ShouldInheritColours 275
-#define wxWindow_Show 276
-#define wxWindow_Thaw 277
-#define wxWindow_TransferDataFromWindow 278
-#define wxWindow_TransferDataToWindow 279
-#define wxWindow_Update 280
-#define wxWindow_UpdateWindowUI 281
-#define wxWindow_Validate 282
-#define wxWindow_WarpPointer 283
-#define wxWindow_SetTransparent 284
-#define wxWindow_CanSetTransparent 285
-#define wxWindow_IsDoubleBuffered 286
-#define wxWindow_SetDoubleBuffered 287
-#define wxTopLevelWindow_GetIcon 288
-#define wxTopLevelWindow_GetIcons 289
-#define wxTopLevelWindow_GetTitle 290
-#define wxTopLevelWindow_IsActive 291
-#define wxTopLevelWindow_Iconize 292
-#define wxTopLevelWindow_IsFullScreen 293
-#define wxTopLevelWindow_IsIconized 294
-#define wxTopLevelWindow_IsMaximized 295
-#define wxTopLevelWindow_Maximize 296
-#define wxTopLevelWindow_RequestUserAttention 297
-#define wxTopLevelWindow_SetIcon 298
-#define wxTopLevelWindow_SetIcons 299
-#define wxTopLevelWindow_CenterOnScreen 300
-#define wxTopLevelWindow_CentreOnScreen 301
-#define wxTopLevelWindow_SetShape 303
-#define wxTopLevelWindow_SetTitle 304
-#define wxTopLevelWindow_ShowFullScreen 305
-#define wxFrame_new_4 307
-#define wxFrame_new_0 308
-#define wxFrame_destruct 310
-#define wxFrame_Create 311
-#define wxFrame_CreateStatusBar 312
-#define wxFrame_CreateToolBar 313
-#define wxFrame_GetClientAreaOrigin 314
-#define wxFrame_GetMenuBar 315
-#define wxFrame_GetStatusBar 316
-#define wxFrame_GetStatusBarPane 317
-#define wxFrame_GetToolBar 318
-#define wxFrame_ProcessCommand 319
-#define wxFrame_SendSizeEvent 320
-#define wxFrame_SetMenuBar 321
-#define wxFrame_SetStatusBar 322
-#define wxFrame_SetStatusBarPane 323
-#define wxFrame_SetStatusText 324
-#define wxFrame_SetStatusWidths 325
-#define wxFrame_SetToolBar 326
-#define wxMiniFrame_new_0 327
-#define wxMiniFrame_new_4 328
-#define wxMiniFrame_Create 329
-#define wxMiniFrame_destroy 330
-#define wxSplashScreen_new_0 331
-#define wxSplashScreen_new_6 332
-#define wxSplashScreen_destruct 333
-#define wxSplashScreen_GetSplashStyle 334
-#define wxSplashScreen_GetTimeout 335
-#define wxPanel_new_0 336
-#define wxPanel_new_6 337
-#define wxPanel_new_2 338
-#define wxPanel_destruct 339
-#define wxPanel_InitDialog 340
-#define wxPanel_SetFocusIgnoringChildren 341
-#define wxScrolledWindow_new_0 342
-#define wxScrolledWindow_new_2 343
-#define wxScrolledWindow_destruct 344
-#define wxScrolledWindow_CalcScrolledPosition_4 345
-#define wxScrolledWindow_CalcScrolledPosition_1 346
-#define wxScrolledWindow_CalcUnscrolledPosition_4 347
-#define wxScrolledWindow_CalcUnscrolledPosition_1 348
-#define wxScrolledWindow_EnableScrolling 349
-#define wxScrolledWindow_GetScrollPixelsPerUnit 350
-#define wxScrolledWindow_GetViewStart 351
-#define wxScrolledWindow_DoPrepareDC 352
-#define wxScrolledWindow_PrepareDC 353
-#define wxScrolledWindow_Scroll 354
-#define wxScrolledWindow_SetScrollbars 355
-#define wxScrolledWindow_SetScrollRate 356
-#define wxScrolledWindow_SetTargetWindow 357
-#define wxSashWindow_new_0 358
-#define wxSashWindow_new_2 359
-#define wxSashWindow_destruct 360
-#define wxSashWindow_GetSashVisible 361
-#define wxSashWindow_GetMaximumSizeX 362
-#define wxSashWindow_GetMaximumSizeY 363
-#define wxSashWindow_GetMinimumSizeX 364
-#define wxSashWindow_GetMinimumSizeY 365
-#define wxSashWindow_SetMaximumSizeX 366
-#define wxSashWindow_SetMaximumSizeY 367
-#define wxSashWindow_SetMinimumSizeX 368
-#define wxSashWindow_SetMinimumSizeY 369
-#define wxSashWindow_SetSashVisible 370
-#define wxSashLayoutWindow_new_0 371
-#define wxSashLayoutWindow_new_2 372
-#define wxSashLayoutWindow_Create 373
-#define wxSashLayoutWindow_GetAlignment 374
-#define wxSashLayoutWindow_GetOrientation 375
-#define wxSashLayoutWindow_SetAlignment 376
-#define wxSashLayoutWindow_SetDefaultSize 377
-#define wxSashLayoutWindow_SetOrientation 378
-#define wxSashLayoutWindow_destroy 379
-#define wxGrid_new_0 380
-#define wxGrid_new_3 381
-#define wxGrid_new_4 382
-#define wxGrid_destruct 383
-#define wxGrid_AppendCols 384
-#define wxGrid_AppendRows 385
-#define wxGrid_AutoSize 386
-#define wxGrid_AutoSizeColumn 387
-#define wxGrid_AutoSizeColumns 388
-#define wxGrid_AutoSizeRow 389
-#define wxGrid_AutoSizeRows 390
-#define wxGrid_BeginBatch 391
-#define wxGrid_BlockToDeviceRect 392
-#define wxGrid_CanDragColSize 393
-#define wxGrid_CanDragRowSize 394
-#define wxGrid_CanDragGridSize 395
-#define wxGrid_CanEnableCellControl 396
-#define wxGrid_CellToRect_2 397
-#define wxGrid_CellToRect_1 398
-#define wxGrid_ClearGrid 399
-#define wxGrid_ClearSelection 400
-#define wxGrid_CreateGrid 401
-#define wxGrid_DeleteCols 402
-#define wxGrid_DeleteRows 403
-#define wxGrid_DisableCellEditControl 404
-#define wxGrid_DisableDragColSize 405
-#define wxGrid_DisableDragGridSize 406
-#define wxGrid_DisableDragRowSize 407
-#define wxGrid_EnableCellEditControl 408
-#define wxGrid_EnableDragColSize 409
-#define wxGrid_EnableDragGridSize 410
-#define wxGrid_EnableDragRowSize 411
-#define wxGrid_EnableEditing 412
-#define wxGrid_EnableGridLines 413
-#define wxGrid_EndBatch 414
-#define wxGrid_Fit 415
-#define wxGrid_ForceRefresh 416
-#define wxGrid_GetBatchCount 417
-#define wxGrid_GetCellAlignment 418
-#define wxGrid_GetCellBackgroundColour 419
-#define wxGrid_GetCellEditor 420
-#define wxGrid_GetCellFont 421
-#define wxGrid_GetCellRenderer 422
-#define wxGrid_GetCellTextColour 423
-#define wxGrid_GetCellValue_2 424
-#define wxGrid_GetCellValue_1 425
-#define wxGrid_GetColLabelAlignment 426
-#define wxGrid_GetColLabelSize 427
-#define wxGrid_GetColLabelValue 428
-#define wxGrid_GetColMinimalAcceptableWidth 429
-#define wxGrid_GetDefaultCellAlignment 430
-#define wxGrid_GetDefaultCellBackgroundColour 431
-#define wxGrid_GetDefaultCellFont 432
-#define wxGrid_GetDefaultCellTextColour 433
-#define wxGrid_GetDefaultColLabelSize 434
-#define wxGrid_GetDefaultColSize 435
-#define wxGrid_GetDefaultEditor 436
-#define wxGrid_GetDefaultEditorForCell_2 437
-#define wxGrid_GetDefaultEditorForCell_1 438
-#define wxGrid_GetDefaultEditorForType 439
-#define wxGrid_GetDefaultRenderer 440
-#define wxGrid_GetDefaultRendererForCell 441
-#define wxGrid_GetDefaultRendererForType 442
-#define wxGrid_GetDefaultRowLabelSize 443
-#define wxGrid_GetDefaultRowSize 444
-#define wxGrid_GetGridCursorCol 445
-#define wxGrid_GetGridCursorRow 446
-#define wxGrid_GetGridLineColour 447
-#define wxGrid_GridLinesEnabled 448
-#define wxGrid_GetLabelBackgroundColour 449
-#define wxGrid_GetLabelFont 450
-#define wxGrid_GetLabelTextColour 451
-#define wxGrid_GetNumberCols 452
-#define wxGrid_GetNumberRows 453
-#define wxGrid_GetOrCreateCellAttr 454
-#define wxGrid_GetRowMinimalAcceptableHeight 455
-#define wxGrid_GetRowLabelAlignment 456
-#define wxGrid_GetRowLabelSize 457
-#define wxGrid_GetRowLabelValue 458
-#define wxGrid_GetRowSize 459
-#define wxGrid_GetScrollLineX 460
-#define wxGrid_GetScrollLineY 461
-#define wxGrid_GetSelectedCells 462
-#define wxGrid_GetSelectedCols 463
-#define wxGrid_GetSelectedRows 464
-#define wxGrid_GetSelectionBackground 465
-#define wxGrid_GetSelectionBlockTopLeft 466
-#define wxGrid_GetSelectionBlockBottomRight 467
-#define wxGrid_GetSelectionForeground 468
-#define wxGrid_GetViewWidth 469
-#define wxGrid_GetGridWindow 470
-#define wxGrid_GetGridRowLabelWindow 471
-#define wxGrid_GetGridColLabelWindow 472
-#define wxGrid_GetGridCornerLabelWindow 473
-#define wxGrid_HideCellEditControl 474
-#define wxGrid_InsertCols 475
-#define wxGrid_InsertRows 476
-#define wxGrid_IsCellEditControlEnabled 477
-#define wxGrid_IsCurrentCellReadOnly 478
-#define wxGrid_IsEditable 479
-#define wxGrid_IsInSelection_2 480
-#define wxGrid_IsInSelection_1 481
-#define wxGrid_IsReadOnly 482
-#define wxGrid_IsSelection 483
-#define wxGrid_IsVisible_3 484
-#define wxGrid_IsVisible_2 485
-#define wxGrid_MakeCellVisible_2 486
-#define wxGrid_MakeCellVisible_1 487
-#define wxGrid_MoveCursorDown 488
-#define wxGrid_MoveCursorLeft 489
-#define wxGrid_MoveCursorRight 490
-#define wxGrid_MoveCursorUp 491
-#define wxGrid_MoveCursorDownBlock 492
-#define wxGrid_MoveCursorLeftBlock 493
-#define wxGrid_MoveCursorRightBlock 494
-#define wxGrid_MoveCursorUpBlock 495
-#define wxGrid_MovePageDown 496
-#define wxGrid_MovePageUp 497
-#define wxGrid_RegisterDataType 498
-#define wxGrid_SaveEditControlValue 499
-#define wxGrid_SelectAll 500
-#define wxGrid_SelectBlock_5 501
-#define wxGrid_SelectBlock_3 502
-#define wxGrid_SelectCol 503
-#define wxGrid_SelectRow 504
-#define wxGrid_SetCellAlignment_4 505
-#define wxGrid_SetCellAlignment_3 506
-#define wxGrid_SetCellAlignment_1 507
-#define wxGrid_SetCellBackgroundColour_3_0 508
-#define wxGrid_SetCellBackgroundColour_1 509
-#define wxGrid_SetCellBackgroundColour_3_1 510
-#define wxGrid_SetCellEditor 511
-#define wxGrid_SetCellFont 512
-#define wxGrid_SetCellRenderer 513
-#define wxGrid_SetCellTextColour_3_0 514
-#define wxGrid_SetCellTextColour_3_1 515
-#define wxGrid_SetCellTextColour_1 516
-#define wxGrid_SetCellValue_3_0 517
-#define wxGrid_SetCellValue_2 518
-#define wxGrid_SetCellValue_3_1 519
-#define wxGrid_SetColAttr 520
-#define wxGrid_SetColFormatBool 521
-#define wxGrid_SetColFormatNumber 522
-#define wxGrid_SetColFormatFloat 523
-#define wxGrid_SetColFormatCustom 524
-#define wxGrid_SetColLabelAlignment 525
-#define wxGrid_SetColLabelSize 526
-#define wxGrid_SetColLabelValue 527
-#define wxGrid_SetColMinimalWidth 528
-#define wxGrid_SetColMinimalAcceptableWidth 529
-#define wxGrid_SetColSize 530
-#define wxGrid_SetDefaultCellAlignment 531
-#define wxGrid_SetDefaultCellBackgroundColour 532
-#define wxGrid_SetDefaultCellFont 533
-#define wxGrid_SetDefaultCellTextColour 534
-#define wxGrid_SetDefaultEditor 535
-#define wxGrid_SetDefaultRenderer 536
-#define wxGrid_SetDefaultColSize 537
-#define wxGrid_SetDefaultRowSize 538
-#define wxGrid_SetGridCursor 539
-#define wxGrid_SetGridLineColour 540
-#define wxGrid_SetLabelBackgroundColour 541
-#define wxGrid_SetLabelFont 542
-#define wxGrid_SetLabelTextColour 543
-#define wxGrid_SetMargins 544
-#define wxGrid_SetReadOnly 545
-#define wxGrid_SetRowAttr 546
-#define wxGrid_SetRowLabelAlignment 547
-#define wxGrid_SetRowLabelSize 548
-#define wxGrid_SetRowLabelValue 549
-#define wxGrid_SetRowMinimalHeight 550
-#define wxGrid_SetRowMinimalAcceptableHeight 551
-#define wxGrid_SetRowSize 552
-#define wxGrid_SetScrollLineX 553
-#define wxGrid_SetScrollLineY 554
-#define wxGrid_SetSelectionBackground 555
-#define wxGrid_SetSelectionForeground 556
-#define wxGrid_SetSelectionMode 557
-#define wxGrid_ShowCellEditControl 558
-#define wxGrid_XToCol 559
-#define wxGrid_XToEdgeOfCol 560
-#define wxGrid_YToEdgeOfRow 561
-#define wxGrid_YToRow 562
-#define wxGridCellRenderer_Draw 563
-#define wxGridCellRenderer_GetBestSize 564
-#define wxGridCellEditor_Create 565
-#define wxGridCellEditor_IsCreated 566
-#define wxGridCellEditor_SetSize 567
-#define wxGridCellEditor_Show 568
-#define wxGridCellEditor_PaintBackground 569
-#define wxGridCellEditor_BeginEdit 570
-#define wxGridCellEditor_EndEdit 571
-#define wxGridCellEditor_Reset 572
-#define wxGridCellEditor_StartingKey 573
-#define wxGridCellEditor_StartingClick 574
-#define wxGridCellEditor_HandleReturn 575
-#define wxGridCellBoolRenderer_new 576
-#define wxGridCellBoolRenderer_destroy 577
-#define wxGridCellBoolEditor_new 578
-#define wxGridCellBoolEditor_IsTrueValue 579
-#define wxGridCellBoolEditor_UseStringValues 580
-#define wxGridCellBoolEditor_destroy 581
-#define wxGridCellFloatRenderer_new 582
-#define wxGridCellFloatRenderer_GetPrecision 583
-#define wxGridCellFloatRenderer_GetWidth 584
-#define wxGridCellFloatRenderer_SetParameters 585
-#define wxGridCellFloatRenderer_SetPrecision 586
-#define wxGridCellFloatRenderer_SetWidth 587
-#define wxGridCellFloatRenderer_destroy 588
-#define wxGridCellFloatEditor_new 589
-#define wxGridCellFloatEditor_SetParameters 590
-#define wxGridCellFloatEditor_destroy 591
-#define wxGridCellStringRenderer_new 592
-#define wxGridCellStringRenderer_destroy 593
-#define wxGridCellTextEditor_new 594
-#define wxGridCellTextEditor_SetParameters 595
-#define wxGridCellTextEditor_destroy 596
-#define wxGridCellChoiceEditor_new 598
-#define wxGridCellChoiceEditor_SetParameters 599
-#define wxGridCellChoiceEditor_destroy 600
-#define wxGridCellNumberRenderer_new 601
-#define wxGridCellNumberRenderer_destroy 602
-#define wxGridCellNumberEditor_new 603
-#define wxGridCellNumberEditor_GetValue 604
-#define wxGridCellNumberEditor_SetParameters 605
-#define wxGridCellNumberEditor_destroy 606
-#define wxGridCellAttr_SetTextColour 607
-#define wxGridCellAttr_SetBackgroundColour 608
-#define wxGridCellAttr_SetFont 609
-#define wxGridCellAttr_SetAlignment 610
-#define wxGridCellAttr_SetReadOnly 611
-#define wxGridCellAttr_SetRenderer 612
-#define wxGridCellAttr_SetEditor 613
-#define wxGridCellAttr_HasTextColour 614
-#define wxGridCellAttr_HasBackgroundColour 615
-#define wxGridCellAttr_HasFont 616
-#define wxGridCellAttr_HasAlignment 617
-#define wxGridCellAttr_HasRenderer 618
-#define wxGridCellAttr_HasEditor 619
-#define wxGridCellAttr_GetTextColour 620
-#define wxGridCellAttr_GetBackgroundColour 621
-#define wxGridCellAttr_GetFont 622
-#define wxGridCellAttr_GetAlignment 623
-#define wxGridCellAttr_GetRenderer 624
-#define wxGridCellAttr_GetEditor 625
-#define wxGridCellAttr_IsReadOnly 626
-#define wxGridCellAttr_SetDefAttr 627
-#define wxDC_Blit 628
-#define wxDC_CalcBoundingBox 629
-#define wxDC_Clear 630
-#define wxDC_ComputeScaleAndOrigin 631
-#define wxDC_CrossHair 632
-#define wxDC_DestroyClippingRegion 633
-#define wxDC_DeviceToLogicalX 634
-#define wxDC_DeviceToLogicalXRel 635
-#define wxDC_DeviceToLogicalY 636
-#define wxDC_DeviceToLogicalYRel 637
-#define wxDC_DrawArc 638
-#define wxDC_DrawBitmap 639
-#define wxDC_DrawCheckMark 640
-#define wxDC_DrawCircle 641
-#define wxDC_DrawEllipse_2 643
-#define wxDC_DrawEllipse_1 644
-#define wxDC_DrawEllipticArc 645
-#define wxDC_DrawIcon 646
-#define wxDC_DrawLabel 647
-#define wxDC_DrawLine 648
-#define wxDC_DrawLines 649
-#define wxDC_DrawPolygon 651
-#define wxDC_DrawPoint 653
-#define wxDC_DrawRectangle_2 655
-#define wxDC_DrawRectangle_1 656
-#define wxDC_DrawRotatedText 657
-#define wxDC_DrawRoundedRectangle_3 659
-#define wxDC_DrawRoundedRectangle_2 660
-#define wxDC_DrawText 661
-#define wxDC_EndDoc 662
-#define wxDC_EndPage 663
-#define wxDC_FloodFill 664
-#define wxDC_GetBackground 665
-#define wxDC_GetBackgroundMode 666
-#define wxDC_GetBrush 667
-#define wxDC_GetCharHeight 668
-#define wxDC_GetCharWidth 669
-#define wxDC_GetClippingBox 670
-#define wxDC_GetFont 672
-#define wxDC_GetLayoutDirection 673
-#define wxDC_GetLogicalFunction 674
-#define wxDC_GetMapMode 675
-#define wxDC_GetMultiLineTextExtent_4 676
-#define wxDC_GetMultiLineTextExtent_1 677
-#define wxDC_GetPartialTextExtents 678
-#define wxDC_GetPen 679
-#define wxDC_GetPixel 680
-#define wxDC_GetPPI 681
-#define wxDC_GetSize 683
-#define wxDC_GetSizeMM 685
-#define wxDC_GetTextBackground 686
-#define wxDC_GetTextExtent_4 687
-#define wxDC_GetTextExtent_1 688
-#define wxDC_GetTextForeground 690
-#define wxDC_GetUserScale 691
-#define wxDC_GradientFillConcentric_3 692
-#define wxDC_GradientFillConcentric_4 693
-#define wxDC_GradientFillLinear 694
-#define wxDC_LogicalToDeviceX 695
-#define wxDC_LogicalToDeviceXRel 696
-#define wxDC_LogicalToDeviceY 697
-#define wxDC_LogicalToDeviceYRel 698
-#define wxDC_MaxX 699
-#define wxDC_MaxY 700
-#define wxDC_MinX 701
-#define wxDC_MinY 702
-#define wxDC_IsOk 703
-#define wxDC_ResetBoundingBox 704
-#define wxDC_SetAxisOrientation 705
-#define wxDC_SetBackground 706
-#define wxDC_SetBackgroundMode 707
-#define wxDC_SetBrush 708
-#define wxDC_SetClippingRegion_2 710
-#define wxDC_SetClippingRegion_1_1 711
-#define wxDC_SetClippingRegion_1_0 712
-#define wxDC_SetDeviceOrigin 713
-#define wxDC_SetFont 714
-#define wxDC_SetLayoutDirection 715
-#define wxDC_SetLogicalFunction 716
-#define wxDC_SetMapMode 717
-#define wxDC_SetPalette 718
-#define wxDC_SetPen 719
-#define wxDC_SetTextBackground 720
-#define wxDC_SetTextForeground 721
-#define wxDC_SetUserScale 722
-#define wxDC_StartDoc 723
-#define wxDC_StartPage 724
-#define wxMirrorDC_new 725
-#define wxMirrorDC_destroy 726
-#define wxScreenDC_new 727
-#define wxScreenDC_destruct 728
-#define wxPostScriptDC_new_0 729
-#define wxPostScriptDC_new_1 730
-#define wxPostScriptDC_destruct 731
-#define wxPostScriptDC_SetResolution 732
-#define wxPostScriptDC_GetResolution 733
-#define wxWindowDC_new_0 734
-#define wxWindowDC_new_1 735
-#define wxWindowDC_destruct 736
-#define wxClientDC_new_0 737
-#define wxClientDC_new_1 738
-#define wxClientDC_destroy 739
-#define wxPaintDC_new_0 740
-#define wxPaintDC_new_1 741
-#define wxPaintDC_destroy 742
-#define wxMemoryDC_new_1_0 744
-#define wxMemoryDC_new_1_1 745
-#define wxMemoryDC_new_0 746
-#define wxMemoryDC_destruct 748
-#define wxMemoryDC_SelectObject 749
-#define wxMemoryDC_SelectObjectAsSource 750
-#define wxBufferedDC_new_0 751
-#define wxBufferedDC_new_2 752
-#define wxBufferedDC_new_3 753
-#define wxBufferedDC_destruct 754
-#define wxBufferedDC_Init_2 755
-#define wxBufferedDC_Init_3 756
-#define wxBufferedPaintDC_new_3 757
-#define wxBufferedPaintDC_new_2 758
-#define wxBufferedPaintDC_destruct 759
-#define wxGraphicsObject_destruct 760
-#define wxGraphicsObject_GetRenderer 761
-#define wxGraphicsObject_IsNull 762
-#define wxGraphicsContext_destruct 763
-#define wxGraphicsContext_Create_1_1 764
-#define wxGraphicsContext_Create_1_0 765
-#define wxGraphicsContext_Create_0 766
-#define wxGraphicsContext_CreatePen 767
-#define wxGraphicsContext_CreateBrush 768
-#define wxGraphicsContext_CreateRadialGradientBrush 769
-#define wxGraphicsContext_CreateLinearGradientBrush 770
-#define wxGraphicsContext_CreateFont 771
-#define wxGraphicsContext_CreateMatrix 772
-#define wxGraphicsContext_CreatePath 773
-#define wxGraphicsContext_Clip_1 774
-#define wxGraphicsContext_Clip_4 775
-#define wxGraphicsContext_ResetClip 776
-#define wxGraphicsContext_DrawBitmap 777
-#define wxGraphicsContext_DrawEllipse 778
-#define wxGraphicsContext_DrawIcon 779
-#define wxGraphicsContext_DrawLines 780
-#define wxGraphicsContext_DrawPath 781
-#define wxGraphicsContext_DrawRectangle 782
-#define wxGraphicsContext_DrawRoundedRectangle 783
-#define wxGraphicsContext_DrawText_3 784
-#define wxGraphicsContext_DrawText_4_0 785
-#define wxGraphicsContext_DrawText_4_1 786
-#define wxGraphicsContext_DrawText_5 787
-#define wxGraphicsContext_FillPath 788
-#define wxGraphicsContext_StrokePath 789
-#define wxGraphicsContext_GetPartialTextExtents 790
-#define wxGraphicsContext_GetTextExtent 791
-#define wxGraphicsContext_Rotate 792
-#define wxGraphicsContext_Scale 793
-#define wxGraphicsContext_Translate 794
-#define wxGraphicsContext_GetTransform 795
-#define wxGraphicsContext_SetTransform 796
-#define wxGraphicsContext_ConcatTransform 797
-#define wxGraphicsContext_SetBrush_1_1 798
-#define wxGraphicsContext_SetBrush_1_0 799
-#define wxGraphicsContext_SetFont_1 800
-#define wxGraphicsContext_SetFont_2 801
-#define wxGraphicsContext_SetPen_1_0 802
-#define wxGraphicsContext_SetPen_1_1 803
-#define wxGraphicsContext_StrokeLine 804
-#define wxGraphicsContext_StrokeLines 805
-#define wxGraphicsMatrix_Concat 807
-#define wxGraphicsMatrix_Get 809
-#define wxGraphicsMatrix_Invert 810
-#define wxGraphicsMatrix_IsEqual 811
-#define wxGraphicsMatrix_IsIdentity 813
-#define wxGraphicsMatrix_Rotate 814
-#define wxGraphicsMatrix_Scale 815
-#define wxGraphicsMatrix_Translate 816
-#define wxGraphicsMatrix_Set 817
-#define wxGraphicsMatrix_TransformPoint 818
-#define wxGraphicsMatrix_TransformDistance 819
-#define wxGraphicsPath_MoveToPoint_2 820
-#define wxGraphicsPath_MoveToPoint_1 821
-#define wxGraphicsPath_AddArc_6 822
-#define wxGraphicsPath_AddArc_5 823
-#define wxGraphicsPath_AddArcToPoint 824
-#define wxGraphicsPath_AddCircle 825
-#define wxGraphicsPath_AddCurveToPoint_6 826
-#define wxGraphicsPath_AddCurveToPoint_3 827
-#define wxGraphicsPath_AddEllipse 828
-#define wxGraphicsPath_AddLineToPoint_2 829
-#define wxGraphicsPath_AddLineToPoint_1 830
-#define wxGraphicsPath_AddPath 831
-#define wxGraphicsPath_AddQuadCurveToPoint 832
-#define wxGraphicsPath_AddRectangle 833
-#define wxGraphicsPath_AddRoundedRectangle 834
-#define wxGraphicsPath_CloseSubpath 835
-#define wxGraphicsPath_Contains_3 836
-#define wxGraphicsPath_Contains_2 837
-#define wxGraphicsPath_GetBox 839
-#define wxGraphicsPath_GetCurrentPoint 841
-#define wxGraphicsPath_Transform 842
-#define wxGraphicsRenderer_GetDefaultRenderer 843
-#define wxGraphicsRenderer_CreateContext_1_1 844
-#define wxGraphicsRenderer_CreateContext_1_0 845
-#define wxGraphicsRenderer_CreatePen 846
-#define wxGraphicsRenderer_CreateBrush 847
-#define wxGraphicsRenderer_CreateLinearGradientBrush 848
-#define wxGraphicsRenderer_CreateRadialGradientBrush 849
-#define wxGraphicsRenderer_CreateFont 850
-#define wxGraphicsRenderer_CreateMatrix 851
-#define wxGraphicsRenderer_CreatePath 852
-#define wxMenuBar_new_1 854
-#define wxMenuBar_new_0 856
-#define wxMenuBar_destruct 858
-#define wxMenuBar_Append 859
-#define wxMenuBar_Check 860
-#define wxMenuBar_Enable_2 861
-#define wxMenuBar_Enable_1 862
-#define wxMenuBar_EnableTop 863
-#define wxMenuBar_FindMenu 864
-#define wxMenuBar_FindMenuItem 865
-#define wxMenuBar_FindItem 866
-#define wxMenuBar_GetHelpString 867
-#define wxMenuBar_GetLabel_1 868
-#define wxMenuBar_GetLabel_0 869
-#define wxMenuBar_GetLabelTop 870
-#define wxMenuBar_GetMenu 871
-#define wxMenuBar_GetMenuCount 872
-#define wxMenuBar_Insert 873
-#define wxMenuBar_IsChecked 874
-#define wxMenuBar_IsEnabled_1 875
-#define wxMenuBar_IsEnabled_0 876
-#define wxMenuBar_Remove 877
-#define wxMenuBar_Replace 878
-#define wxMenuBar_SetHelpString 879
-#define wxMenuBar_SetLabel_2 880
-#define wxMenuBar_SetLabel_1 881
-#define wxMenuBar_SetLabelTop 882
-#define wxControl_GetLabel 883
-#define wxControl_SetLabel 884
-#define wxControlWithItems_Append_1 885
-#define wxControlWithItems_Append_2 886
-#define wxControlWithItems_appendStrings_1 887
-#define wxControlWithItems_Clear 888
-#define wxControlWithItems_Delete 889
-#define wxControlWithItems_FindString 890
-#define wxControlWithItems_getClientData 891
-#define wxControlWithItems_setClientData 892
-#define wxControlWithItems_GetCount 893
-#define wxControlWithItems_GetSelection 894
-#define wxControlWithItems_GetString 895
-#define wxControlWithItems_GetStringSelection 896
-#define wxControlWithItems_Insert_2 897
-#define wxControlWithItems_Insert_3 898
-#define wxControlWithItems_IsEmpty 899
-#define wxControlWithItems_Select 900
-#define wxControlWithItems_SetSelection 901
-#define wxControlWithItems_SetString 902
-#define wxControlWithItems_SetStringSelection 903
-#define wxMenu_new_2 906
-#define wxMenu_new_1 907
-#define wxMenu_destruct 909
-#define wxMenu_Append_3 910
-#define wxMenu_Append_1 911
-#define wxMenu_Append_4_0 912
-#define wxMenu_Append_4_1 913
-#define wxMenu_AppendCheckItem 914
-#define wxMenu_AppendRadioItem 915
-#define wxMenu_AppendSeparator 916
-#define wxMenu_Break 917
-#define wxMenu_Check 918
-#define wxMenu_Delete_1_0 919
-#define wxMenu_Delete_1_1 920
-#define wxMenu_Destroy_1_0 921
-#define wxMenu_Destroy_1_1 922
-#define wxMenu_Enable 923
-#define wxMenu_FindItem_1 924
-#define wxMenu_FindItem_2 925
-#define wxMenu_FindItemByPosition 926
-#define wxMenu_GetHelpString 927
-#define wxMenu_GetLabel 928
-#define wxMenu_GetMenuItemCount 929
-#define wxMenu_GetMenuItems 930
-#define wxMenu_GetTitle 932
-#define wxMenu_Insert_2 933
-#define wxMenu_Insert_3 934
-#define wxMenu_Insert_5_1 935
-#define wxMenu_Insert_5_0 936
-#define wxMenu_InsertCheckItem 937
-#define wxMenu_InsertRadioItem 938
-#define wxMenu_InsertSeparator 939
-#define wxMenu_IsChecked 940
-#define wxMenu_IsEnabled 941
-#define wxMenu_Prepend_1 942
-#define wxMenu_Prepend_2 943
-#define wxMenu_Prepend_4_1 944
-#define wxMenu_Prepend_4_0 945
-#define wxMenu_PrependCheckItem 946
-#define wxMenu_PrependRadioItem 947
-#define wxMenu_PrependSeparator 948
-#define wxMenu_Remove_1_0 949
-#define wxMenu_Remove_1_1 950
-#define wxMenu_SetHelpString 951
-#define wxMenu_SetLabel 952
-#define wxMenu_SetTitle 953
-#define wxMenuItem_new 954
-#define wxMenuItem_destruct 956
-#define wxMenuItem_Check 957
-#define wxMenuItem_Enable 958
-#define wxMenuItem_GetBitmap 959
-#define wxMenuItem_GetHelp 960
-#define wxMenuItem_GetId 961
-#define wxMenuItem_GetKind 962
-#define wxMenuItem_GetLabel 963
-#define wxMenuItem_GetLabelFromText 964
-#define wxMenuItem_GetMenu 965
-#define wxMenuItem_GetText 966
-#define wxMenuItem_GetSubMenu 967
-#define wxMenuItem_IsCheckable 968
-#define wxMenuItem_IsChecked 969
-#define wxMenuItem_IsEnabled 970
-#define wxMenuItem_IsSeparator 971
-#define wxMenuItem_IsSubMenu 972
-#define wxMenuItem_SetBitmap 973
-#define wxMenuItem_SetHelp 974
-#define wxMenuItem_SetMenu 975
-#define wxMenuItem_SetSubMenu 976
-#define wxMenuItem_SetText 977
-#define wxToolBar_AddControl 978
-#define wxToolBar_AddSeparator 979
-#define wxToolBar_AddTool_5 980
-#define wxToolBar_AddTool_4_0 981
-#define wxToolBar_AddTool_1 982
-#define wxToolBar_AddTool_4_1 983
-#define wxToolBar_AddTool_3 984
-#define wxToolBar_AddTool_6 985
-#define wxToolBar_AddCheckTool 986
-#define wxToolBar_AddRadioTool 987
-#define wxToolBar_AddStretchableSpace 988
-#define wxToolBar_InsertStretchableSpace 989
-#define wxToolBar_DeleteTool 990
-#define wxToolBar_DeleteToolByPos 991
-#define wxToolBar_EnableTool 992
-#define wxToolBar_FindById 993
-#define wxToolBar_FindControl 994
-#define wxToolBar_FindToolForPosition 995
-#define wxToolBar_GetToolSize 996
-#define wxToolBar_GetToolBitmapSize 997
-#define wxToolBar_GetMargins 998
-#define wxToolBar_GetToolEnabled 999
-#define wxToolBar_GetToolLongHelp 1000
-#define wxToolBar_GetToolPacking 1001
-#define wxToolBar_GetToolPos 1002
-#define wxToolBar_GetToolSeparation 1003
-#define wxToolBar_GetToolShortHelp 1004
-#define wxToolBar_GetToolState 1005
-#define wxToolBar_InsertControl 1006
-#define wxToolBar_InsertSeparator 1007
-#define wxToolBar_InsertTool_5 1008
-#define wxToolBar_InsertTool_2 1009
-#define wxToolBar_InsertTool_4 1010
-#define wxToolBar_Realize 1011
-#define wxToolBar_RemoveTool 1012
-#define wxToolBar_SetMargins 1013
-#define wxToolBar_SetToolBitmapSize 1014
-#define wxToolBar_SetToolLongHelp 1015
-#define wxToolBar_SetToolPacking 1016
-#define wxToolBar_SetToolShortHelp 1017
-#define wxToolBar_SetToolSeparation 1018
-#define wxToolBar_ToggleTool 1019
-#define wxStatusBar_new_0 1021
-#define wxStatusBar_new_2 1022
-#define wxStatusBar_destruct 1024
-#define wxStatusBar_Create 1025
-#define wxStatusBar_GetFieldRect 1026
-#define wxStatusBar_GetFieldsCount 1027
-#define wxStatusBar_GetStatusText 1028
-#define wxStatusBar_PopStatusText 1029
-#define wxStatusBar_PushStatusText 1030
-#define wxStatusBar_SetFieldsCount 1031
-#define wxStatusBar_SetMinHeight 1032
-#define wxStatusBar_SetStatusText 1033
-#define wxStatusBar_SetStatusWidths 1034
-#define wxStatusBar_SetStatusStyles 1035
-#define wxBitmap_new_0 1036
-#define wxBitmap_new_3 1037
-#define wxBitmap_new_4 1038
-#define wxBitmap_new_2_0 1039
-#define wxBitmap_new_2_1 1040
-#define wxBitmap_destruct 1041
-#define wxBitmap_ConvertToImage 1042
-#define wxBitmap_CopyFromIcon 1043
-#define wxBitmap_Create 1044
-#define wxBitmap_GetDepth 1045
-#define wxBitmap_GetHeight 1046
-#define wxBitmap_GetPalette 1047
-#define wxBitmap_GetMask 1048
-#define wxBitmap_GetWidth 1049
-#define wxBitmap_GetSubBitmap 1050
-#define wxBitmap_LoadFile 1051
-#define wxBitmap_Ok 1052
-#define wxBitmap_SaveFile 1053
-#define wxBitmap_SetDepth 1054
-#define wxBitmap_SetHeight 1055
-#define wxBitmap_SetMask 1056
-#define wxBitmap_SetPalette 1057
-#define wxBitmap_SetWidth 1058
-#define wxIcon_new_0 1059
-#define wxIcon_new_2 1060
-#define wxIcon_new_1 1061
-#define wxIcon_CopyFromBitmap 1062
-#define wxIcon_destroy 1063
-#define wxIconBundle_new_0 1064
-#define wxIconBundle_new_2 1065
-#define wxIconBundle_new_1_0 1066
-#define wxIconBundle_new_1_1 1067
-#define wxIconBundle_destruct 1068
-#define wxIconBundle_AddIcon_2 1069
-#define wxIconBundle_AddIcon_1 1070
-#define wxIconBundle_GetIcon_1_1 1071
-#define wxIconBundle_GetIcon_1_0 1072
-#define wxCursor_new_0 1073
-#define wxCursor_new_1_0 1074
-#define wxCursor_new_1_1 1075
-#define wxCursor_new_4 1076
-#define wxCursor_destruct 1077
-#define wxCursor_Ok 1078
-#define wxMask_new_0 1079
-#define wxMask_new_2_1 1080
-#define wxMask_new_2_0 1081
-#define wxMask_new_1 1082
-#define wxMask_destruct 1083
-#define wxMask_Create_2_1 1084
-#define wxMask_Create_2_0 1085
-#define wxMask_Create_1 1086
-#define wxImage_new_0 1087
-#define wxImage_new_3_0 1088
-#define wxImage_new_4 1089
-#define wxImage_new_5 1090
-#define wxImage_new_2 1091
-#define wxImage_new_3_1 1092
-#define wxImage_Blur 1093
-#define wxImage_BlurHorizontal 1094
-#define wxImage_BlurVertical 1095
-#define wxImage_ConvertAlphaToMask 1096
-#define wxImage_ConvertToGreyscale 1097
-#define wxImage_ConvertToMono 1098
-#define wxImage_Copy 1099
-#define wxImage_Create_3 1100
-#define wxImage_Create_4 1101
-#define wxImage_Create_5 1102
-#define wxImage_Destroy 1103
-#define wxImage_FindFirstUnusedColour 1104
-#define wxImage_GetImageExtWildcard 1105
-#define wxImage_GetAlpha_2 1106
-#define wxImage_GetAlpha_0 1107
-#define wxImage_GetBlue 1108
-#define wxImage_GetData 1109
-#define wxImage_GetGreen 1110
-#define wxImage_GetImageCount 1111
-#define wxImage_GetHeight 1112
-#define wxImage_GetMaskBlue 1113
-#define wxImage_GetMaskGreen 1114
-#define wxImage_GetMaskRed 1115
-#define wxImage_GetOrFindMaskColour 1116
-#define wxImage_GetPalette 1117
-#define wxImage_GetRed 1118
-#define wxImage_GetSubImage 1119
-#define wxImage_GetWidth 1120
-#define wxImage_HasAlpha 1121
-#define wxImage_HasMask 1122
-#define wxImage_GetOption 1123
-#define wxImage_GetOptionInt 1124
-#define wxImage_HasOption 1125
-#define wxImage_InitAlpha 1126
-#define wxImage_InitStandardHandlers 1127
-#define wxImage_IsTransparent 1128
-#define wxImage_LoadFile_2 1129
-#define wxImage_LoadFile_3 1130
-#define wxImage_Ok 1131
-#define wxImage_RemoveHandler 1132
-#define wxImage_Mirror 1133
-#define wxImage_Replace 1134
-#define wxImage_Rescale 1135
-#define wxImage_Resize 1136
-#define wxImage_Rotate 1137
-#define wxImage_RotateHue 1138
-#define wxImage_Rotate90 1139
-#define wxImage_SaveFile_1 1140
-#define wxImage_SaveFile_2_0 1141
-#define wxImage_SaveFile_2_1 1142
-#define wxImage_Scale 1143
-#define wxImage_Size 1144
-#define wxImage_SetAlpha_3 1145
-#define wxImage_SetAlpha_2 1146
-#define wxImage_SetData_2 1147
-#define wxImage_SetData_4 1148
-#define wxImage_SetMask 1149
-#define wxImage_SetMaskColour 1150
-#define wxImage_SetMaskFromImage 1151
-#define wxImage_SetOption_2_1 1152
-#define wxImage_SetOption_2_0 1153
-#define wxImage_SetPalette 1154
-#define wxImage_SetRGB_5 1155
-#define wxImage_SetRGB_4 1156
-#define wxImage_destroy 1157
-#define wxBrush_new_0 1158
-#define wxBrush_new_2 1159
-#define wxBrush_new_1 1160
-#define wxBrush_destruct 1162
-#define wxBrush_GetColour 1163
-#define wxBrush_GetStipple 1164
-#define wxBrush_GetStyle 1165
-#define wxBrush_IsHatch 1166
-#define wxBrush_IsOk 1167
-#define wxBrush_SetColour_1 1168
-#define wxBrush_SetColour_3 1169
-#define wxBrush_SetStipple 1170
-#define wxBrush_SetStyle 1171
-#define wxPen_new_0 1172
-#define wxPen_new_2 1173
-#define wxPen_destruct 1174
-#define wxPen_GetCap 1175
-#define wxPen_GetColour 1176
-#define wxPen_GetJoin 1177
-#define wxPen_GetStyle 1178
-#define wxPen_GetWidth 1179
-#define wxPen_IsOk 1180
-#define wxPen_SetCap 1181
-#define wxPen_SetColour_1 1182
-#define wxPen_SetColour_3 1183
-#define wxPen_SetJoin 1184
-#define wxPen_SetStyle 1185
-#define wxPen_SetWidth 1186
-#define wxRegion_new_0 1187
-#define wxRegion_new_4 1188
-#define wxRegion_new_2 1189
-#define wxRegion_new_1_1 1190
-#define wxRegion_new_1_0 1192
-#define wxRegion_destruct 1194
-#define wxRegion_Clear 1195
-#define wxRegion_Contains_2 1196
-#define wxRegion_Contains_1_0 1197
-#define wxRegion_Contains_4 1198
-#define wxRegion_Contains_1_1 1199
-#define wxRegion_ConvertToBitmap 1200
-#define wxRegion_GetBox 1201
-#define wxRegion_Intersect_4 1202
-#define wxRegion_Intersect_1_1 1203
-#define wxRegion_Intersect_1_0 1204
-#define wxRegion_IsEmpty 1205
-#define wxRegion_Subtract_4 1206
-#define wxRegion_Subtract_1_1 1207
-#define wxRegion_Subtract_1_0 1208
-#define wxRegion_Offset_2 1209
-#define wxRegion_Offset_1 1210
-#define wxRegion_Union_4 1211
-#define wxRegion_Union_1_2 1212
-#define wxRegion_Union_1_1 1213
-#define wxRegion_Union_1_0 1214
-#define wxRegion_Union_3 1215
-#define wxRegion_Xor_4 1216
-#define wxRegion_Xor_1_1 1217
-#define wxRegion_Xor_1_0 1218
-#define wxAcceleratorTable_new_0 1219
-#define wxAcceleratorTable_new_2 1220
-#define wxAcceleratorTable_destruct 1221
-#define wxAcceleratorTable_Ok 1222
-#define wxAcceleratorEntry_new_1_0 1223
-#define wxAcceleratorEntry_new_1_1 1224
-#define wxAcceleratorEntry_GetCommand 1225
-#define wxAcceleratorEntry_GetFlags 1226
-#define wxAcceleratorEntry_GetKeyCode 1227
-#define wxAcceleratorEntry_Set 1228
-#define wxAcceleratorEntry_destroy 1229
-#define wxCaret_new_3 1234
-#define wxCaret_new_2 1235
-#define wxCaret_destruct 1237
-#define wxCaret_Create_3 1238
-#define wxCaret_Create_2 1239
-#define wxCaret_GetBlinkTime 1240
-#define wxCaret_GetPosition 1242
-#define wxCaret_GetSize 1244
-#define wxCaret_GetWindow 1245
-#define wxCaret_Hide 1246
-#define wxCaret_IsOk 1247
-#define wxCaret_IsVisible 1248
-#define wxCaret_Move_2 1249
-#define wxCaret_Move_1 1250
-#define wxCaret_SetBlinkTime 1251
-#define wxCaret_SetSize_2 1252
-#define wxCaret_SetSize_1 1253
-#define wxCaret_Show 1254
-#define wxSizer_Add_2_1 1255
-#define wxSizer_Add_2_0 1256
-#define wxSizer_Add_3 1257
-#define wxSizer_Add_2_3 1258
-#define wxSizer_Add_2_2 1259
-#define wxSizer_AddSpacer 1260
-#define wxSizer_AddStretchSpacer 1261
-#define wxSizer_CalcMin 1262
-#define wxSizer_Clear 1263
-#define wxSizer_Detach_1_2 1264
-#define wxSizer_Detach_1_1 1265
-#define wxSizer_Detach_1_0 1266
-#define wxSizer_Fit 1267
-#define wxSizer_FitInside 1268
-#define wxSizer_GetChildren 1269
-#define wxSizer_GetItem_2_1 1270
-#define wxSizer_GetItem_2_0 1271
-#define wxSizer_GetItem_1 1272
-#define wxSizer_GetSize 1273
-#define wxSizer_GetPosition 1274
-#define wxSizer_GetMinSize 1275
-#define wxSizer_Hide_2_0 1276
-#define wxSizer_Hide_2_1 1277
-#define wxSizer_Hide_1 1278
-#define wxSizer_Insert_3_1 1279
-#define wxSizer_Insert_3_0 1280
-#define wxSizer_Insert_4 1281
-#define wxSizer_Insert_3_3 1282
-#define wxSizer_Insert_3_2 1283
-#define wxSizer_Insert_2 1284
-#define wxSizer_InsertSpacer 1285
-#define wxSizer_InsertStretchSpacer 1286
-#define wxSizer_IsShown_1_2 1287
-#define wxSizer_IsShown_1_1 1288
-#define wxSizer_IsShown_1_0 1289
-#define wxSizer_Layout 1290
-#define wxSizer_Prepend_2_1 1291
-#define wxSizer_Prepend_2_0 1292
-#define wxSizer_Prepend_3 1293
-#define wxSizer_Prepend_2_3 1294
-#define wxSizer_Prepend_2_2 1295
-#define wxSizer_Prepend_1 1296
-#define wxSizer_PrependSpacer 1297
-#define wxSizer_PrependStretchSpacer 1298
-#define wxSizer_RecalcSizes 1299
-#define wxSizer_Remove_1_1 1300
-#define wxSizer_Remove_1_0 1301
-#define wxSizer_Replace_3_1 1302
-#define wxSizer_Replace_3_0 1303
-#define wxSizer_Replace_2 1304
-#define wxSizer_SetDimension 1305
-#define wxSizer_SetMinSize_2 1306
-#define wxSizer_SetMinSize_1 1307
-#define wxSizer_SetItemMinSize_3_2 1308
-#define wxSizer_SetItemMinSize_2_2 1309
-#define wxSizer_SetItemMinSize_3_1 1310
-#define wxSizer_SetItemMinSize_2_1 1311
-#define wxSizer_SetItemMinSize_3_0 1312
-#define wxSizer_SetItemMinSize_2_0 1313
-#define wxSizer_SetSizeHints 1314
-#define wxSizer_SetVirtualSizeHints 1315
-#define wxSizer_Show_2_2 1316
-#define wxSizer_Show_2_1 1317
-#define wxSizer_Show_2_0 1318
-#define wxSizer_Show_1 1319
-#define wxSizerFlags_new 1320
-#define wxSizerFlags_Align 1321
-#define wxSizerFlags_Border_2 1322
-#define wxSizerFlags_Border_1 1323
-#define wxSizerFlags_Center 1324
-#define wxSizerFlags_Centre 1325
-#define wxSizerFlags_Expand 1326
-#define wxSizerFlags_Left 1327
-#define wxSizerFlags_Proportion 1328
-#define wxSizerFlags_Right 1329
-#define wxSizerFlags_destroy 1330
-#define wxSizerItem_new_5_1 1331
-#define wxSizerItem_new_2_1 1332
-#define wxSizerItem_new_5_0 1333
-#define wxSizerItem_new_2_0 1334
-#define wxSizerItem_new_6 1335
-#define wxSizerItem_new_3 1336
-#define wxSizerItem_new_0 1337
-#define wxSizerItem_destruct 1338
-#define wxSizerItem_CalcMin 1339
-#define wxSizerItem_DeleteWindows 1340
-#define wxSizerItem_DetachSizer 1341
-#define wxSizerItem_GetBorder 1342
-#define wxSizerItem_GetFlag 1343
-#define wxSizerItem_GetMinSize 1344
-#define wxSizerItem_GetPosition 1345
-#define wxSizerItem_GetProportion 1346
-#define wxSizerItem_GetRatio 1347
-#define wxSizerItem_GetRect 1348
-#define wxSizerItem_GetSize 1349
-#define wxSizerItem_GetSizer 1350
-#define wxSizerItem_GetSpacer 1351
-#define wxSizerItem_GetUserData 1352
-#define wxSizerItem_GetWindow 1353
-#define wxSizerItem_IsSizer 1354
-#define wxSizerItem_IsShown 1355
-#define wxSizerItem_IsSpacer 1356
-#define wxSizerItem_IsWindow 1357
-#define wxSizerItem_SetBorder 1358
-#define wxSizerItem_SetDimension 1359
-#define wxSizerItem_SetFlag 1360
-#define wxSizerItem_SetInitSize 1361
-#define wxSizerItem_SetMinSize_1 1362
-#define wxSizerItem_SetMinSize_2 1363
-#define wxSizerItem_SetProportion 1364
-#define wxSizerItem_SetRatio_2 1365
-#define wxSizerItem_SetRatio_1_1 1366
-#define wxSizerItem_SetRatio_1_0 1367
-#define wxSizerItem_SetSizer 1368
-#define wxSizerItem_SetSpacer_1 1369
-#define wxSizerItem_SetSpacer_2 1370
-#define wxSizerItem_SetWindow 1371
-#define wxSizerItem_Show 1372
-#define wxBoxSizer_new 1373
-#define wxBoxSizer_GetOrientation 1374
-#define wxBoxSizer_destroy 1375
-#define wxStaticBoxSizer_new_2 1376
-#define wxStaticBoxSizer_new_3 1377
-#define wxStaticBoxSizer_GetStaticBox 1378
-#define wxStaticBoxSizer_destroy 1379
-#define wxGridSizer_new_4 1380
-#define wxGridSizer_new_2 1381
-#define wxGridSizer_GetCols 1382
-#define wxGridSizer_GetHGap 1383
-#define wxGridSizer_GetRows 1384
-#define wxGridSizer_GetVGap 1385
-#define wxGridSizer_SetCols 1386
-#define wxGridSizer_SetHGap 1387
-#define wxGridSizer_SetRows 1388
-#define wxGridSizer_SetVGap 1389
-#define wxGridSizer_destroy 1390
-#define wxFlexGridSizer_new_4 1391
-#define wxFlexGridSizer_new_2 1392
-#define wxFlexGridSizer_AddGrowableCol 1393
-#define wxFlexGridSizer_AddGrowableRow 1394
-#define wxFlexGridSizer_GetFlexibleDirection 1395
-#define wxFlexGridSizer_GetNonFlexibleGrowMode 1396
-#define wxFlexGridSizer_RemoveGrowableCol 1397
-#define wxFlexGridSizer_RemoveGrowableRow 1398
-#define wxFlexGridSizer_SetFlexibleDirection 1399
-#define wxFlexGridSizer_SetNonFlexibleGrowMode 1400
-#define wxFlexGridSizer_destroy 1401
-#define wxGridBagSizer_new 1402
-#define wxGridBagSizer_Add_3_2 1403
-#define wxGridBagSizer_Add_3_1 1404
-#define wxGridBagSizer_Add_4 1405
-#define wxGridBagSizer_Add_1_0 1406
-#define wxGridBagSizer_Add_2_1 1407
-#define wxGridBagSizer_Add_2_0 1408
-#define wxGridBagSizer_Add_3_0 1409
-#define wxGridBagSizer_Add_1_1 1410
-#define wxGridBagSizer_CalcMin 1411
-#define wxGridBagSizer_CheckForIntersection_2 1412
-#define wxGridBagSizer_CheckForIntersection_3 1413
-#define wxGridBagSizer_FindItem_1_1 1414
-#define wxGridBagSizer_FindItem_1_0 1415
-#define wxGridBagSizer_FindItemAtPoint 1416
-#define wxGridBagSizer_FindItemAtPosition 1417
-#define wxGridBagSizer_FindItemWithData 1418
-#define wxGridBagSizer_GetCellSize 1419
-#define wxGridBagSizer_GetEmptyCellSize 1420
-#define wxGridBagSizer_GetItemPosition_1_2 1421
-#define wxGridBagSizer_GetItemPosition_1_1 1422
-#define wxGridBagSizer_GetItemPosition_1_0 1423
-#define wxGridBagSizer_GetItemSpan_1_2 1424
-#define wxGridBagSizer_GetItemSpan_1_1 1425
-#define wxGridBagSizer_GetItemSpan_1_0 1426
-#define wxGridBagSizer_SetEmptyCellSize 1427
-#define wxGridBagSizer_SetItemPosition_2_2 1428
-#define wxGridBagSizer_SetItemPosition_2_1 1429
-#define wxGridBagSizer_SetItemPosition_2_0 1430
-#define wxGridBagSizer_SetItemSpan_2_2 1431
-#define wxGridBagSizer_SetItemSpan_2_1 1432
-#define wxGridBagSizer_SetItemSpan_2_0 1433
-#define wxGridBagSizer_destroy 1434
-#define wxStdDialogButtonSizer_new 1435
-#define wxStdDialogButtonSizer_AddButton 1436
-#define wxStdDialogButtonSizer_Realize 1437
-#define wxStdDialogButtonSizer_SetAffirmativeButton 1438
-#define wxStdDialogButtonSizer_SetCancelButton 1439
-#define wxStdDialogButtonSizer_SetNegativeButton 1440
-#define wxStdDialogButtonSizer_destroy 1441
-#define wxFont_new_0 1442
-#define wxFont_new_1 1443
-#define wxFont_new_5 1444
-#define wxFont_destruct 1446
-#define wxFont_IsFixedWidth 1447
-#define wxFont_GetDefaultEncoding 1448
-#define wxFont_GetFaceName 1449
-#define wxFont_GetFamily 1450
-#define wxFont_GetNativeFontInfoDesc 1451
-#define wxFont_GetNativeFontInfoUserDesc 1452
-#define wxFont_GetPointSize 1453
-#define wxFont_GetStyle 1454
-#define wxFont_GetUnderlined 1455
-#define wxFont_GetWeight 1456
-#define wxFont_Ok 1457
-#define wxFont_SetDefaultEncoding 1458
-#define wxFont_SetFaceName 1459
-#define wxFont_SetFamily 1460
-#define wxFont_SetPointSize 1461
-#define wxFont_SetStyle 1462
-#define wxFont_SetUnderlined 1463
-#define wxFont_SetWeight 1464
-#define wxToolTip_Enable 1465
-#define wxToolTip_SetDelay 1466
-#define wxToolTip_new 1467
-#define wxToolTip_SetTip 1468
-#define wxToolTip_GetTip 1469
-#define wxToolTip_GetWindow 1470
-#define wxToolTip_destroy 1471
-#define wxButton_new_3 1473
-#define wxButton_new_0 1474
-#define wxButton_destruct 1475
-#define wxButton_Create 1476
-#define wxButton_GetDefaultSize 1477
-#define wxButton_SetDefault 1478
-#define wxButton_SetLabel 1479
-#define wxBitmapButton_new_4 1481
-#define wxBitmapButton_new_0 1482
-#define wxBitmapButton_Create 1483
-#define wxBitmapButton_GetBitmapDisabled 1484
-#define wxBitmapButton_GetBitmapFocus 1486
-#define wxBitmapButton_GetBitmapLabel 1488
-#define wxBitmapButton_GetBitmapSelected 1490
-#define wxBitmapButton_SetBitmapDisabled 1492
-#define wxBitmapButton_SetBitmapFocus 1493
-#define wxBitmapButton_SetBitmapLabel 1494
-#define wxBitmapButton_SetBitmapSelected 1495
-#define wxBitmapButton_destroy 1496
-#define wxToggleButton_new_0 1497
-#define wxToggleButton_new_4 1498
-#define wxToggleButton_Create 1499
-#define wxToggleButton_GetValue 1500
-#define wxToggleButton_SetValue 1501
-#define wxToggleButton_destroy 1502
-#define wxCalendarCtrl_new_0 1503
-#define wxCalendarCtrl_new_3 1504
-#define wxCalendarCtrl_Create 1505
-#define wxCalendarCtrl_destruct 1506
-#define wxCalendarCtrl_SetDate 1507
-#define wxCalendarCtrl_GetDate 1508
-#define wxCalendarCtrl_EnableYearChange 1509
-#define wxCalendarCtrl_EnableMonthChange 1510
-#define wxCalendarCtrl_EnableHolidayDisplay 1511
-#define wxCalendarCtrl_SetHeaderColours 1512
-#define wxCalendarCtrl_GetHeaderColourFg 1513
-#define wxCalendarCtrl_GetHeaderColourBg 1514
-#define wxCalendarCtrl_SetHighlightColours 1515
-#define wxCalendarCtrl_GetHighlightColourFg 1516
-#define wxCalendarCtrl_GetHighlightColourBg 1517
-#define wxCalendarCtrl_SetHolidayColours 1518
-#define wxCalendarCtrl_GetHolidayColourFg 1519
-#define wxCalendarCtrl_GetHolidayColourBg 1520
-#define wxCalendarCtrl_GetAttr 1521
-#define wxCalendarCtrl_SetAttr 1522
-#define wxCalendarCtrl_SetHoliday 1523
-#define wxCalendarCtrl_ResetAttr 1524
-#define wxCalendarCtrl_HitTest 1525
-#define wxCalendarDateAttr_new_0 1526
-#define wxCalendarDateAttr_new_2_1 1527
-#define wxCalendarDateAttr_new_2_0 1528
-#define wxCalendarDateAttr_SetTextColour 1529
-#define wxCalendarDateAttr_SetBackgroundColour 1530
-#define wxCalendarDateAttr_SetBorderColour 1531
-#define wxCalendarDateAttr_SetFont 1532
-#define wxCalendarDateAttr_SetBorder 1533
-#define wxCalendarDateAttr_SetHoliday 1534
-#define wxCalendarDateAttr_HasTextColour 1535
-#define wxCalendarDateAttr_HasBackgroundColour 1536
-#define wxCalendarDateAttr_HasBorderColour 1537
-#define wxCalendarDateAttr_HasFont 1538
-#define wxCalendarDateAttr_HasBorder 1539
-#define wxCalendarDateAttr_IsHoliday 1540
-#define wxCalendarDateAttr_GetTextColour 1541
-#define wxCalendarDateAttr_GetBackgroundColour 1542
-#define wxCalendarDateAttr_GetBorderColour 1543
-#define wxCalendarDateAttr_GetFont 1544
-#define wxCalendarDateAttr_GetBorder 1545
-#define wxCalendarDateAttr_destroy 1546
-#define wxCheckBox_new_4 1548
-#define wxCheckBox_new_0 1549
-#define wxCheckBox_Create 1550
-#define wxCheckBox_GetValue 1551
-#define wxCheckBox_Get3StateValue 1552
-#define wxCheckBox_Is3rdStateAllowedForUser 1553
-#define wxCheckBox_Is3State 1554
-#define wxCheckBox_IsChecked 1555
-#define wxCheckBox_SetValue 1556
-#define wxCheckBox_Set3StateValue 1557
-#define wxCheckBox_destroy 1558
-#define wxCheckListBox_new_0 1559
-#define wxCheckListBox_new_3 1561
-#define wxCheckListBox_Check 1562
-#define wxCheckListBox_IsChecked 1563
-#define wxCheckListBox_destroy 1564
-#define wxChoice_new_3 1567
-#define wxChoice_new_0 1568
-#define wxChoice_destruct 1570
-#define wxChoice_Create 1572
-#define wxChoice_Delete 1573
-#define wxChoice_GetColumns 1574
-#define wxChoice_SetColumns 1575
-#define wxComboBox_new_0 1576
-#define wxComboBox_new_3 1578
-#define wxComboBox_destruct 1579
-#define wxComboBox_Create 1581
-#define wxComboBox_CanCopy 1582
-#define wxComboBox_CanCut 1583
-#define wxComboBox_CanPaste 1584
-#define wxComboBox_CanRedo 1585
-#define wxComboBox_CanUndo 1586
-#define wxComboBox_Copy 1587
-#define wxComboBox_Cut 1588
-#define wxComboBox_GetInsertionPoint 1589
-#define wxComboBox_GetLastPosition 1590
-#define wxComboBox_GetValue 1591
-#define wxComboBox_Paste 1592
-#define wxComboBox_Redo 1593
-#define wxComboBox_Replace 1594
-#define wxComboBox_Remove 1595
-#define wxComboBox_SetInsertionPoint 1596
-#define wxComboBox_SetInsertionPointEnd 1597
-#define wxComboBox_SetSelection_1 1598
-#define wxComboBox_SetSelection_2 1599
-#define wxComboBox_SetValue 1600
-#define wxComboBox_Undo 1601
-#define wxGauge_new_0 1602
-#define wxGauge_new_4 1603
-#define wxGauge_Create 1604
-#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
+#define wxWindow_DragAcceptFiles 126
+#define wxWindow_Enable 127
+#define wxWindow_FindFocus 128
+#define wxWindow_FindWindow_1_0 129
+#define wxWindow_FindWindow_1_1 130
+#define wxWindow_FindWindowById 131
+#define wxWindow_FindWindowByName 132
+#define wxWindow_FindWindowByLabel 133
+#define wxWindow_Fit 134
+#define wxWindow_FitInside 135
+#define wxWindow_Freeze 136
+#define wxWindow_GetAcceleratorTable 137
+#define wxWindow_GetBackgroundColour 138
+#define wxWindow_GetBackgroundStyle 139
+#define wxWindow_GetBestSize 140
+#define wxWindow_GetCaret 142
+#define wxWindow_GetCapture 143
+#define wxWindow_GetCharHeight 144
+#define wxWindow_GetCharWidth 145
+#define wxWindow_GetChildren 146
+#define wxWindow_GetClientSize 149
+#define wxWindow_GetContainingSizer 150
+#define wxWindow_GetCursor 151
+#define wxWindow_GetDropTarget 152
+#define wxWindow_GetEventHandler 153
+#define wxWindow_GetExtraStyle 154
+#define wxWindow_GetFont 155
+#define wxWindow_GetForegroundColour 156
+#define wxWindow_GetGrandParent 157
+#define wxWindow_GetHandle 158
+#define wxWindow_GetHelpText 159
+#define wxWindow_GetId 160
+#define wxWindow_GetLabel 161
+#define wxWindow_GetMaxSize 162
+#define wxWindow_GetMinSize 163
+#define wxWindow_GetName 164
+#define wxWindow_GetParent 165
+#define wxWindow_GetPosition 167
+#define wxWindow_GetRect 168
+#define wxWindow_GetScreenPosition 170
+#define wxWindow_GetScreenRect 171
+#define wxWindow_GetScrollPos 172
+#define wxWindow_GetScrollRange 173
+#define wxWindow_GetScrollThumb 174
+#define wxWindow_GetSize 176
+#define wxWindow_GetSizer 177
+#define wxWindow_GetTextExtent 178
+#define wxWindow_GetToolTip 179
+#define wxWindow_GetUpdateRegion 180
+#define wxWindow_GetVirtualSize 182
+#define wxWindow_GetWindowStyleFlag 184
+#define wxWindow_GetWindowVariant 185
+#define wxWindow_HasCapture 186
+#define wxWindow_HasScrollbar 187
+#define wxWindow_HasTransparentBackground 188
+#define wxWindow_Hide 189
+#define wxWindow_InheritAttributes 190
+#define wxWindow_InitDialog 191
+#define wxWindow_InvalidateBestSize 192
+#define wxWindow_IsEnabled 193
+#define wxWindow_IsExposed_2 194
+#define wxWindow_IsExposed_4 195
+#define wxWindow_IsExposed_1_0 196
+#define wxWindow_IsExposed_1_1 197
+#define wxWindow_IsRetained 198
+#define wxWindow_IsShown 199
+#define wxWindow_IsTopLevel 200
+#define wxWindow_Layout 201
+#define wxWindow_LineDown 202
+#define wxWindow_LineUp 203
+#define wxWindow_Lower 204
+#define wxWindow_MakeModal 205
+#define wxWindow_Move_3 206
+#define wxWindow_Move_2 207
+#define wxWindow_MoveAfterInTabOrder 208
+#define wxWindow_MoveBeforeInTabOrder 209
+#define wxWindow_Navigate 210
+#define wxWindow_PageDown 211
+#define wxWindow_PageUp 212
+#define wxWindow_PopEventHandler 213
+#define wxWindow_PopupMenu_2 214
+#define wxWindow_PopupMenu_3 215
+#define wxWindow_Raise 216
+#define wxWindow_Refresh 217
+#define wxWindow_RefreshRect 218
+#define wxWindow_ReleaseMouse 219
+#define wxWindow_RemoveChild 220
+#define wxWindow_Reparent 221
+#define wxWindow_ScreenToClient_2 222
+#define wxWindow_ScreenToClient_1 223
+#define wxWindow_ScrollLines 225
+#define wxWindow_ScrollPages 227
+#define wxWindow_ScrollWindow 228
+#define wxWindow_SetAcceleratorTable 229
+#define wxWindow_SetAutoLayout 230
+#define wxWindow_SetBackgroundColour 231
+#define wxWindow_SetBackgroundStyle 232
+#define wxWindow_SetCaret 233
+#define wxWindow_SetClientSize_2 234
+#define wxWindow_SetClientSize_1_0 235
+#define wxWindow_SetClientSize_1_1 236
+#define wxWindow_SetContainingSizer 237
+#define wxWindow_SetCursor 238
+#define wxWindow_SetMaxSize 239
+#define wxWindow_SetMinSize 240
+#define wxWindow_SetOwnBackgroundColour 241
+#define wxWindow_SetOwnFont 242
+#define wxWindow_SetOwnForegroundColour 243
+#define wxWindow_SetDropTarget 244
+#define wxWindow_SetExtraStyle 245
+#define wxWindow_SetFocus 246
+#define wxWindow_SetFocusFromKbd 247
+#define wxWindow_SetFont 248
+#define wxWindow_SetForegroundColour 249
+#define wxWindow_SetHelpText 250
+#define wxWindow_SetId 251
+#define wxWindow_SetLabel 253
+#define wxWindow_SetName 254
+#define wxWindow_SetPalette 255
+#define wxWindow_SetScrollbar 256
+#define wxWindow_SetScrollPos 257
+#define wxWindow_SetSize_5 258
+#define wxWindow_SetSize_2_0 259
+#define wxWindow_SetSize_1 260
+#define wxWindow_SetSize_2_1 261
+#define wxWindow_SetSizeHints_3 262
+#define wxWindow_SetSizeHints_2 263
+#define wxWindow_SetSizer 264
+#define wxWindow_SetSizerAndFit 265
+#define wxWindow_SetThemeEnabled 266
+#define wxWindow_SetToolTip_1_0 267
+#define wxWindow_SetToolTip_1_1 268
+#define wxWindow_SetVirtualSize_1 269
+#define wxWindow_SetVirtualSize_2 270
+#define wxWindow_SetVirtualSizeHints_3 271
+#define wxWindow_SetVirtualSizeHints_2 272
+#define wxWindow_SetWindowStyle 273
+#define wxWindow_SetWindowStyleFlag 274
+#define wxWindow_SetWindowVariant 275
+#define wxWindow_ShouldInheritColours 276
+#define wxWindow_Show 277
+#define wxWindow_Thaw 278
+#define wxWindow_TransferDataFromWindow 279
+#define wxWindow_TransferDataToWindow 280
+#define wxWindow_Update 281
+#define wxWindow_UpdateWindowUI 282
+#define wxWindow_Validate 283
+#define wxWindow_WarpPointer 284
+#define wxWindow_SetTransparent 285
+#define wxWindow_CanSetTransparent 286
+#define wxWindow_IsDoubleBuffered 287
+#define wxWindow_SetDoubleBuffered 288
+#define wxWindow_GetContentScaleFactor 289
+#define wxTopLevelWindow_GetIcon 290
+#define wxTopLevelWindow_GetIcons 291
+#define wxTopLevelWindow_GetTitle 292
+#define wxTopLevelWindow_IsActive 293
+#define wxTopLevelWindow_Iconize 294
+#define wxTopLevelWindow_IsFullScreen 295
+#define wxTopLevelWindow_IsIconized 296
+#define wxTopLevelWindow_IsMaximized 297
+#define wxTopLevelWindow_Maximize 298
+#define wxTopLevelWindow_RequestUserAttention 299
+#define wxTopLevelWindow_SetIcon 300
+#define wxTopLevelWindow_SetIcons 301
+#define wxTopLevelWindow_CenterOnScreen 302
+#define wxTopLevelWindow_CentreOnScreen 303
+#define wxTopLevelWindow_SetShape 305
+#define wxTopLevelWindow_SetTitle 306
+#define wxTopLevelWindow_ShowFullScreen 307
+#define wxFrame_new_4 309
+#define wxFrame_new_0 310
+#define wxFrame_destruct 312
+#define wxFrame_Create 313
+#define wxFrame_CreateStatusBar 314
+#define wxFrame_CreateToolBar 315
+#define wxFrame_GetClientAreaOrigin 316
+#define wxFrame_GetMenuBar 317
+#define wxFrame_GetStatusBar 318
+#define wxFrame_GetStatusBarPane 319
+#define wxFrame_GetToolBar 320
+#define wxFrame_ProcessCommand 321
+#define wxFrame_SendSizeEvent 322
+#define wxFrame_SetMenuBar 323
+#define wxFrame_SetStatusBar 324
+#define wxFrame_SetStatusBarPane 325
+#define wxFrame_SetStatusText 326
+#define wxFrame_SetStatusWidths 327
+#define wxFrame_SetToolBar 328
+#define wxMiniFrame_new_0 329
+#define wxMiniFrame_new_4 330
+#define wxMiniFrame_Create 331
+#define wxMiniFrame_destroy 332
+#define wxSplashScreen_new_0 333
+#define wxSplashScreen_new_6 334
+#define wxSplashScreen_destruct 335
+#define wxSplashScreen_GetSplashStyle 336
+#define wxSplashScreen_GetTimeout 337
+#define wxPanel_new_0 338
+#define wxPanel_new_6 339
+#define wxPanel_new_2 340
+#define wxPanel_destruct 341
+#define wxPanel_InitDialog 342
+#define wxPanel_SetFocusIgnoringChildren 343
+#define wxScrolledWindow_new_0 344
+#define wxScrolledWindow_new_2 345
+#define wxScrolledWindow_destruct 346
+#define wxScrolledWindow_CalcScrolledPosition_4 347
+#define wxScrolledWindow_CalcScrolledPosition_1 348
+#define wxScrolledWindow_CalcUnscrolledPosition_4 349
+#define wxScrolledWindow_CalcUnscrolledPosition_1 350
+#define wxScrolledWindow_EnableScrolling 351
+#define wxScrolledWindow_GetScrollPixelsPerUnit 352
+#define wxScrolledWindow_GetViewStart 353
+#define wxScrolledWindow_DoPrepareDC 354
+#define wxScrolledWindow_PrepareDC 355
+#define wxScrolledWindow_Scroll 356
+#define wxScrolledWindow_SetScrollbars 357
+#define wxScrolledWindow_SetScrollRate 358
+#define wxScrolledWindow_SetTargetWindow 359
+#define wxSashWindow_new_0 360
+#define wxSashWindow_new_2 361
+#define wxSashWindow_destruct 362
+#define wxSashWindow_GetSashVisible 363
+#define wxSashWindow_GetMaximumSizeX 364
+#define wxSashWindow_GetMaximumSizeY 365
+#define wxSashWindow_GetMinimumSizeX 366
+#define wxSashWindow_GetMinimumSizeY 367
+#define wxSashWindow_SetMaximumSizeX 368
+#define wxSashWindow_SetMaximumSizeY 369
+#define wxSashWindow_SetMinimumSizeX 370
+#define wxSashWindow_SetMinimumSizeY 371
+#define wxSashWindow_SetSashVisible 372
+#define wxSashLayoutWindow_new_0 373
+#define wxSashLayoutWindow_new_2 374
+#define wxSashLayoutWindow_Create 375
+#define wxSashLayoutWindow_GetAlignment 376
+#define wxSashLayoutWindow_GetOrientation 377
+#define wxSashLayoutWindow_SetAlignment 378
+#define wxSashLayoutWindow_SetDefaultSize 379
+#define wxSashLayoutWindow_SetOrientation 380
+#define wxSashLayoutWindow_destroy 381
+#define wxGrid_new_0 382
+#define wxGrid_new_3 383
+#define wxGrid_new_4 384
+#define wxGrid_destruct 385
+#define wxGrid_AppendCols 386
+#define wxGrid_AppendRows 387
+#define wxGrid_AutoSize 388
+#define wxGrid_AutoSizeColumn 389
+#define wxGrid_AutoSizeColumns 390
+#define wxGrid_AutoSizeRow 391
+#define wxGrid_AutoSizeRows 392
+#define wxGrid_BeginBatch 393
+#define wxGrid_BlockToDeviceRect 394
+#define wxGrid_CanDragColSize 395
+#define wxGrid_CanDragRowSize 396
+#define wxGrid_CanDragGridSize 397
+#define wxGrid_CanEnableCellControl 398
+#define wxGrid_CellToRect_2 399
+#define wxGrid_CellToRect_1 400
+#define wxGrid_ClearGrid 401
+#define wxGrid_ClearSelection 402
+#define wxGrid_CreateGrid 403
+#define wxGrid_DeleteCols 404
+#define wxGrid_DeleteRows 405
+#define wxGrid_DisableCellEditControl 406
+#define wxGrid_DisableDragColSize 407
+#define wxGrid_DisableDragGridSize 408
+#define wxGrid_DisableDragRowSize 409
+#define wxGrid_EnableCellEditControl 410
+#define wxGrid_EnableDragColSize 411
+#define wxGrid_EnableDragGridSize 412
+#define wxGrid_EnableDragRowSize 413
+#define wxGrid_EnableEditing 414
+#define wxGrid_EnableGridLines 415
+#define wxGrid_EndBatch 416
+#define wxGrid_Fit 417
+#define wxGrid_ForceRefresh 418
+#define wxGrid_GetBatchCount 419
+#define wxGrid_GetCellAlignment 420
+#define wxGrid_GetCellBackgroundColour 421
+#define wxGrid_GetCellEditor 422
+#define wxGrid_GetCellFont 423
+#define wxGrid_GetCellRenderer 424
+#define wxGrid_GetCellTextColour 425
+#define wxGrid_GetCellValue_2 426
+#define wxGrid_GetCellValue_1 427
+#define wxGrid_GetColLabelAlignment 428
+#define wxGrid_GetColLabelSize 429
+#define wxGrid_GetColLabelValue 430
+#define wxGrid_GetColMinimalAcceptableWidth 431
+#define wxGrid_GetDefaultCellAlignment 432
+#define wxGrid_GetDefaultCellBackgroundColour 433
+#define wxGrid_GetDefaultCellFont 434
+#define wxGrid_GetDefaultCellTextColour 435
+#define wxGrid_GetDefaultColLabelSize 436
+#define wxGrid_GetDefaultColSize 437
+#define wxGrid_GetDefaultEditor 438
+#define wxGrid_GetDefaultEditorForCell_2 439
+#define wxGrid_GetDefaultEditorForCell_1 440
+#define wxGrid_GetDefaultEditorForType 441
+#define wxGrid_GetDefaultRenderer 442
+#define wxGrid_GetDefaultRendererForCell 443
+#define wxGrid_GetDefaultRendererForType 444
+#define wxGrid_GetDefaultRowLabelSize 445
+#define wxGrid_GetDefaultRowSize 446
+#define wxGrid_GetGridCursorCol 447
+#define wxGrid_GetGridCursorRow 448
+#define wxGrid_GetGridLineColour 449
+#define wxGrid_GridLinesEnabled 450
+#define wxGrid_GetLabelBackgroundColour 451
+#define wxGrid_GetLabelFont 452
+#define wxGrid_GetLabelTextColour 453
+#define wxGrid_GetNumberCols 454
+#define wxGrid_GetNumberRows 455
+#define wxGrid_GetOrCreateCellAttr 456
+#define wxGrid_GetRowMinimalAcceptableHeight 457
+#define wxGrid_GetRowLabelAlignment 458
+#define wxGrid_GetRowLabelSize 459
+#define wxGrid_GetRowLabelValue 460
+#define wxGrid_GetRowSize 461
+#define wxGrid_GetScrollLineX 462
+#define wxGrid_GetScrollLineY 463
+#define wxGrid_GetSelectedCells 464
+#define wxGrid_GetSelectedCols 465
+#define wxGrid_GetSelectedRows 466
+#define wxGrid_GetSelectionBackground 467
+#define wxGrid_GetSelectionBlockTopLeft 468
+#define wxGrid_GetSelectionBlockBottomRight 469
+#define wxGrid_GetSelectionForeground 470
+#define wxGrid_GetViewWidth 471
+#define wxGrid_GetGridWindow 472
+#define wxGrid_GetGridRowLabelWindow 473
+#define wxGrid_GetGridColLabelWindow 474
+#define wxGrid_GetGridCornerLabelWindow 475
+#define wxGrid_HideCellEditControl 476
+#define wxGrid_InsertCols 477
+#define wxGrid_InsertRows 478
+#define wxGrid_IsCellEditControlEnabled 479
+#define wxGrid_IsCurrentCellReadOnly 480
+#define wxGrid_IsEditable 481
+#define wxGrid_IsInSelection_2 482
+#define wxGrid_IsInSelection_1 483
+#define wxGrid_IsReadOnly 484
+#define wxGrid_IsSelection 485
+#define wxGrid_IsVisible_3 486
+#define wxGrid_IsVisible_2 487
+#define wxGrid_MakeCellVisible_2 488
+#define wxGrid_MakeCellVisible_1 489
+#define wxGrid_MoveCursorDown 490
+#define wxGrid_MoveCursorLeft 491
+#define wxGrid_MoveCursorRight 492
+#define wxGrid_MoveCursorUp 493
+#define wxGrid_MoveCursorDownBlock 494
+#define wxGrid_MoveCursorLeftBlock 495
+#define wxGrid_MoveCursorRightBlock 496
+#define wxGrid_MoveCursorUpBlock 497
+#define wxGrid_MovePageDown 498
+#define wxGrid_MovePageUp 499
+#define wxGrid_RegisterDataType 500
+#define wxGrid_SaveEditControlValue 501
+#define wxGrid_SelectAll 502
+#define wxGrid_SelectBlock_5 503
+#define wxGrid_SelectBlock_3 504
+#define wxGrid_SelectCol 505
+#define wxGrid_SelectRow 506
+#define wxGrid_SetCellAlignment_4 507
+#define wxGrid_SetCellAlignment_3 508
+#define wxGrid_SetCellAlignment_1 509
+#define wxGrid_SetCellBackgroundColour_3_0 510
+#define wxGrid_SetCellBackgroundColour_1 511
+#define wxGrid_SetCellBackgroundColour_3_1 512
+#define wxGrid_SetCellEditor 513
+#define wxGrid_SetCellFont 514
+#define wxGrid_SetCellRenderer 515
+#define wxGrid_SetCellTextColour_3_0 516
+#define wxGrid_SetCellTextColour_3_1 517
+#define wxGrid_SetCellTextColour_1 518
+#define wxGrid_SetCellValue_3_0 519
+#define wxGrid_SetCellValue_2 520
+#define wxGrid_SetCellValue_3_1 521
+#define wxGrid_SetColAttr 522
+#define wxGrid_SetColFormatBool 523
+#define wxGrid_SetColFormatNumber 524
+#define wxGrid_SetColFormatFloat 525
+#define wxGrid_SetColFormatCustom 526
+#define wxGrid_SetColLabelAlignment 527
+#define wxGrid_SetColLabelSize 528
+#define wxGrid_SetColLabelValue 529
+#define wxGrid_SetColMinimalWidth 530
+#define wxGrid_SetColMinimalAcceptableWidth 531
+#define wxGrid_SetColSize 532
+#define wxGrid_SetDefaultCellAlignment 533
+#define wxGrid_SetDefaultCellBackgroundColour 534
+#define wxGrid_SetDefaultCellFont 535
+#define wxGrid_SetDefaultCellTextColour 536
+#define wxGrid_SetDefaultEditor 537
+#define wxGrid_SetDefaultRenderer 538
+#define wxGrid_SetDefaultColSize 539
+#define wxGrid_SetDefaultRowSize 540
+#define wxGrid_SetGridCursor 541
+#define wxGrid_SetGridLineColour 542
+#define wxGrid_SetLabelBackgroundColour 543
+#define wxGrid_SetLabelFont 544
+#define wxGrid_SetLabelTextColour 545
+#define wxGrid_SetMargins 546
+#define wxGrid_SetReadOnly 547
+#define wxGrid_SetRowAttr 548
+#define wxGrid_SetRowLabelAlignment 549
+#define wxGrid_SetRowLabelSize 550
+#define wxGrid_SetRowLabelValue 551
+#define wxGrid_SetRowMinimalHeight 552
+#define wxGrid_SetRowMinimalAcceptableHeight 553
+#define wxGrid_SetRowSize 554
+#define wxGrid_SetScrollLineX 555
+#define wxGrid_SetScrollLineY 556
+#define wxGrid_SetSelectionBackground 557
+#define wxGrid_SetSelectionForeground 558
+#define wxGrid_SetSelectionMode 559
+#define wxGrid_ShowCellEditControl 560
+#define wxGrid_XToCol 561
+#define wxGrid_XToEdgeOfCol 562
+#define wxGrid_YToEdgeOfRow 563
+#define wxGrid_YToRow 564
+#define wxGridCellRenderer_Draw 565
+#define wxGridCellRenderer_GetBestSize 566
+#define wxGridCellEditor_Create 567
+#define wxGridCellEditor_IsCreated 568
+#define wxGridCellEditor_SetSize 569
+#define wxGridCellEditor_Show 570
+#define wxGridCellEditor_PaintBackground 571
+#define wxGridCellEditor_BeginEdit 572
+#define wxGridCellEditor_EndEdit 573
+#define wxGridCellEditor_Reset 574
+#define wxGridCellEditor_StartingKey 575
+#define wxGridCellEditor_StartingClick 576
+#define wxGridCellEditor_HandleReturn 577
+#define wxGridCellBoolRenderer_new 578
+#define wxGridCellBoolRenderer_destroy 579
+#define wxGridCellBoolEditor_new 580
+#define wxGridCellBoolEditor_IsTrueValue 581
+#define wxGridCellBoolEditor_UseStringValues 582
+#define wxGridCellBoolEditor_destroy 583
+#define wxGridCellFloatRenderer_new 584
+#define wxGridCellFloatRenderer_GetPrecision 585
+#define wxGridCellFloatRenderer_GetWidth 586
+#define wxGridCellFloatRenderer_SetParameters 587
+#define wxGridCellFloatRenderer_SetPrecision 588
+#define wxGridCellFloatRenderer_SetWidth 589
+#define wxGridCellFloatRenderer_destroy 590
+#define wxGridCellFloatEditor_new 591
+#define wxGridCellFloatEditor_SetParameters 592
+#define wxGridCellFloatEditor_destroy 593
+#define wxGridCellStringRenderer_new 594
+#define wxGridCellStringRenderer_destroy 595
+#define wxGridCellTextEditor_new 596
+#define wxGridCellTextEditor_SetParameters 597
+#define wxGridCellTextEditor_destroy 598
+#define wxGridCellChoiceEditor_new 600
+#define wxGridCellChoiceEditor_SetParameters 601
+#define wxGridCellChoiceEditor_destroy 602
+#define wxGridCellNumberRenderer_new 603
+#define wxGridCellNumberRenderer_destroy 604
+#define wxGridCellNumberEditor_new 605
+#define wxGridCellNumberEditor_GetValue 606
+#define wxGridCellNumberEditor_SetParameters 607
+#define wxGridCellNumberEditor_destroy 608
+#define wxGridCellAttr_SetTextColour 609
+#define wxGridCellAttr_SetBackgroundColour 610
+#define wxGridCellAttr_SetFont 611
+#define wxGridCellAttr_SetAlignment 612
+#define wxGridCellAttr_SetReadOnly 613
+#define wxGridCellAttr_SetRenderer 614
+#define wxGridCellAttr_SetEditor 615
+#define wxGridCellAttr_HasTextColour 616
+#define wxGridCellAttr_HasBackgroundColour 617
+#define wxGridCellAttr_HasFont 618
+#define wxGridCellAttr_HasAlignment 619
+#define wxGridCellAttr_HasRenderer 620
+#define wxGridCellAttr_HasEditor 621
+#define wxGridCellAttr_GetTextColour 622
+#define wxGridCellAttr_GetBackgroundColour 623
+#define wxGridCellAttr_GetFont 624
+#define wxGridCellAttr_GetAlignment 625
+#define wxGridCellAttr_GetRenderer 626
+#define wxGridCellAttr_GetEditor 627
+#define wxGridCellAttr_IsReadOnly 628
+#define wxGridCellAttr_SetDefAttr 629
+#define wxDC_Blit 630
+#define wxDC_CalcBoundingBox 631
+#define wxDC_Clear 632
+#define wxDC_ComputeScaleAndOrigin 633
+#define wxDC_CrossHair 634
+#define wxDC_DestroyClippingRegion 635
+#define wxDC_DeviceToLogicalX 636
+#define wxDC_DeviceToLogicalXRel 637
+#define wxDC_DeviceToLogicalY 638
+#define wxDC_DeviceToLogicalYRel 639
+#define wxDC_DrawArc 640
+#define wxDC_DrawBitmap 641
+#define wxDC_DrawCheckMark 642
+#define wxDC_DrawCircle 643
+#define wxDC_DrawEllipse_2 645
+#define wxDC_DrawEllipse_1 646
+#define wxDC_DrawEllipticArc 647
+#define wxDC_DrawIcon 648
+#define wxDC_DrawLabel 649
+#define wxDC_DrawLine 650
+#define wxDC_DrawLines 651
+#define wxDC_DrawPolygon 653
+#define wxDC_DrawPoint 655
+#define wxDC_DrawRectangle_2 657
+#define wxDC_DrawRectangle_1 658
+#define wxDC_DrawRotatedText 659
+#define wxDC_DrawRoundedRectangle_3 661
+#define wxDC_DrawRoundedRectangle_2 662
+#define wxDC_DrawText 663
+#define wxDC_EndDoc 664
+#define wxDC_EndPage 665
+#define wxDC_FloodFill 666
+#define wxDC_GetBackground 667
+#define wxDC_GetBackgroundMode 668
+#define wxDC_GetBrush 669
+#define wxDC_GetCharHeight 670
+#define wxDC_GetCharWidth 671
+#define wxDC_GetClippingBox 672
+#define wxDC_GetFont 674
+#define wxDC_GetLayoutDirection 675
+#define wxDC_GetLogicalFunction 676
+#define wxDC_GetMapMode 677
+#define wxDC_GetMultiLineTextExtent_4 678
+#define wxDC_GetMultiLineTextExtent_1 679
+#define wxDC_GetPartialTextExtents 680
+#define wxDC_GetPen 681
+#define wxDC_GetPixel 682
+#define wxDC_GetPPI 683
+#define wxDC_GetSize 685
+#define wxDC_GetSizeMM 687
+#define wxDC_GetTextBackground 688
+#define wxDC_GetTextExtent_4 689
+#define wxDC_GetTextExtent_1 690
+#define wxDC_GetTextForeground 692
+#define wxDC_GetUserScale 693
+#define wxDC_GradientFillConcentric_3 694
+#define wxDC_GradientFillConcentric_4 695
+#define wxDC_GradientFillLinear 696
+#define wxDC_LogicalToDeviceX 697
+#define wxDC_LogicalToDeviceXRel 698
+#define wxDC_LogicalToDeviceY 699
+#define wxDC_LogicalToDeviceYRel 700
+#define wxDC_MaxX 701
+#define wxDC_MaxY 702
+#define wxDC_MinX 703
+#define wxDC_MinY 704
+#define wxDC_IsOk 705
+#define wxDC_ResetBoundingBox 706
+#define wxDC_SetAxisOrientation 707
+#define wxDC_SetBackground 708
+#define wxDC_SetBackgroundMode 709
+#define wxDC_SetBrush 710
+#define wxDC_SetClippingRegion_2 712
+#define wxDC_SetClippingRegion_1_1 713
+#define wxDC_SetClippingRegion_1_0 714
+#define wxDC_SetDeviceOrigin 715
+#define wxDC_SetFont 716
+#define wxDC_SetLayoutDirection 717
+#define wxDC_SetLogicalFunction 718
+#define wxDC_SetMapMode 719
+#define wxDC_SetPalette 720
+#define wxDC_SetPen 721
+#define wxDC_SetTextBackground 722
+#define wxDC_SetTextForeground 723
+#define wxDC_SetUserScale 724
+#define wxDC_StartDoc 725
+#define wxDC_StartPage 726
+#define wxMirrorDC_new 727
+#define wxMirrorDC_destroy 728
+#define wxScreenDC_new 729
+#define wxScreenDC_destruct 730
+#define wxPostScriptDC_new_0 731
+#define wxPostScriptDC_new_1 732
+#define wxPostScriptDC_destruct 733
+#define wxPostScriptDC_SetResolution 734
+#define wxPostScriptDC_GetResolution 735
+#define wxWindowDC_new_0 736
+#define wxWindowDC_new_1 737
+#define wxWindowDC_destruct 738
+#define wxClientDC_new_0 739
+#define wxClientDC_new_1 740
+#define wxClientDC_destroy 741
+#define wxPaintDC_new_0 742
+#define wxPaintDC_new_1 743
+#define wxPaintDC_destroy 744
+#define wxMemoryDC_new_1_0 746
+#define wxMemoryDC_new_1_1 747
+#define wxMemoryDC_new_0 748
+#define wxMemoryDC_destruct 750
+#define wxMemoryDC_SelectObject 751
+#define wxMemoryDC_SelectObjectAsSource 752
+#define wxBufferedDC_new_0 753
+#define wxBufferedDC_new_2 754
+#define wxBufferedDC_new_3 755
+#define wxBufferedDC_destruct 756
+#define wxBufferedDC_Init_2 757
+#define wxBufferedDC_Init_3 758
+#define wxBufferedPaintDC_new_3 759
+#define wxBufferedPaintDC_new_2 760
+#define wxBufferedPaintDC_destruct 761
+#define wxGraphicsObject_destruct 762
+#define wxGraphicsObject_GetRenderer 763
+#define wxGraphicsObject_IsNull 764
+#define wxGraphicsContext_destruct 765
+#define wxGraphicsContext_Create_1_1 766
+#define wxGraphicsContext_Create_1_0 767
+#define wxGraphicsContext_Create_0 768
+#define wxGraphicsContext_CreatePen 769
+#define wxGraphicsContext_CreateBrush 770
+#define wxGraphicsContext_CreateRadialGradientBrush 771
+#define wxGraphicsContext_CreateLinearGradientBrush 772
+#define wxGraphicsContext_CreateFont 773
+#define wxGraphicsContext_CreateMatrix 774
+#define wxGraphicsContext_CreatePath 775
+#define wxGraphicsContext_Clip_1 776
+#define wxGraphicsContext_Clip_4 777
+#define wxGraphicsContext_ResetClip 778
+#define wxGraphicsContext_DrawBitmap 779
+#define wxGraphicsContext_DrawEllipse 780
+#define wxGraphicsContext_DrawIcon 781
+#define wxGraphicsContext_DrawLines 782
+#define wxGraphicsContext_DrawPath 783
+#define wxGraphicsContext_DrawRectangle 784
+#define wxGraphicsContext_DrawRoundedRectangle 785
+#define wxGraphicsContext_DrawText_3 786
+#define wxGraphicsContext_DrawText_4_0 787
+#define wxGraphicsContext_DrawText_4_1 788
+#define wxGraphicsContext_DrawText_5 789
+#define wxGraphicsContext_FillPath 790
+#define wxGraphicsContext_StrokePath 791
+#define wxGraphicsContext_GetPartialTextExtents 792
+#define wxGraphicsContext_GetTextExtent 793
+#define wxGraphicsContext_Rotate 794
+#define wxGraphicsContext_Scale 795
+#define wxGraphicsContext_Translate 796
+#define wxGraphicsContext_GetTransform 797
+#define wxGraphicsContext_SetTransform 798
+#define wxGraphicsContext_ConcatTransform 799
+#define wxGraphicsContext_SetBrush_1_1 800
+#define wxGraphicsContext_SetBrush_1_0 801
+#define wxGraphicsContext_SetFont_1 802
+#define wxGraphicsContext_SetFont_2 803
+#define wxGraphicsContext_SetPen_1_0 804
+#define wxGraphicsContext_SetPen_1_1 805
+#define wxGraphicsContext_StrokeLine 806
+#define wxGraphicsContext_StrokeLines 807
+#define wxGraphicsMatrix_Concat 809
+#define wxGraphicsMatrix_Get 811
+#define wxGraphicsMatrix_Invert 812
+#define wxGraphicsMatrix_IsEqual 813
+#define wxGraphicsMatrix_IsIdentity 815
+#define wxGraphicsMatrix_Rotate 816
+#define wxGraphicsMatrix_Scale 817
+#define wxGraphicsMatrix_Translate 818
+#define wxGraphicsMatrix_Set 819
+#define wxGraphicsMatrix_TransformPoint 820
+#define wxGraphicsMatrix_TransformDistance 821
+#define wxGraphicsPath_MoveToPoint_2 822
+#define wxGraphicsPath_MoveToPoint_1 823
+#define wxGraphicsPath_AddArc_6 824
+#define wxGraphicsPath_AddArc_5 825
+#define wxGraphicsPath_AddArcToPoint 826
+#define wxGraphicsPath_AddCircle 827
+#define wxGraphicsPath_AddCurveToPoint_6 828
+#define wxGraphicsPath_AddCurveToPoint_3 829
+#define wxGraphicsPath_AddEllipse 830
+#define wxGraphicsPath_AddLineToPoint_2 831
+#define wxGraphicsPath_AddLineToPoint_1 832
+#define wxGraphicsPath_AddPath 833
+#define wxGraphicsPath_AddQuadCurveToPoint 834
+#define wxGraphicsPath_AddRectangle 835
+#define wxGraphicsPath_AddRoundedRectangle 836
+#define wxGraphicsPath_CloseSubpath 837
+#define wxGraphicsPath_Contains_3 838
+#define wxGraphicsPath_Contains_2 839
+#define wxGraphicsPath_GetBox 841
+#define wxGraphicsPath_GetCurrentPoint 843
+#define wxGraphicsPath_Transform 844
+#define wxGraphicsRenderer_GetDefaultRenderer 845
+#define wxGraphicsRenderer_CreateContext_1_1 846
+#define wxGraphicsRenderer_CreateContext_1_0 847
+#define wxGraphicsRenderer_CreatePen 848
+#define wxGraphicsRenderer_CreateBrush 849
+#define wxGraphicsRenderer_CreateLinearGradientBrush 850
+#define wxGraphicsRenderer_CreateRadialGradientBrush 851
+#define wxGraphicsRenderer_CreateFont 852
+#define wxGraphicsRenderer_CreateMatrix 853
+#define wxGraphicsRenderer_CreatePath 854
+#define wxMenuBar_new_1 856
+#define wxMenuBar_new_0 858
+#define wxMenuBar_destruct 860
+#define wxMenuBar_Append 861
+#define wxMenuBar_Check 862
+#define wxMenuBar_Enable_2 863
+#define wxMenuBar_Enable_1 864
+#define wxMenuBar_EnableTop 865
+#define wxMenuBar_FindMenu 866
+#define wxMenuBar_FindMenuItem 867
+#define wxMenuBar_FindItem 868
+#define wxMenuBar_GetHelpString 869
+#define wxMenuBar_GetLabel_1 870
+#define wxMenuBar_GetLabel_0 871
+#define wxMenuBar_GetLabelTop 872
+#define wxMenuBar_GetMenu 873
+#define wxMenuBar_GetMenuCount 874
+#define wxMenuBar_Insert 875
+#define wxMenuBar_IsChecked 876
+#define wxMenuBar_IsEnabled_1 877
+#define wxMenuBar_IsEnabled_0 878
+#define wxMenuBar_Remove 879
+#define wxMenuBar_Replace 880
+#define wxMenuBar_SetHelpString 881
+#define wxMenuBar_SetLabel_2 882
+#define wxMenuBar_SetLabel_1 883
+#define wxMenuBar_SetLabelTop 884
+#define wxControl_GetLabel 885
+#define wxControl_SetLabel 886
+#define wxControlWithItems_Append_1 887
+#define wxControlWithItems_Append_2 888
+#define wxControlWithItems_appendStrings_1 889
+#define wxControlWithItems_Clear 890
+#define wxControlWithItems_Delete 891
+#define wxControlWithItems_FindString 892
+#define wxControlWithItems_getClientData 893
+#define wxControlWithItems_setClientData 894
+#define wxControlWithItems_GetCount 895
+#define wxControlWithItems_GetSelection 896
+#define wxControlWithItems_GetString 897
+#define wxControlWithItems_GetStringSelection 898
+#define wxControlWithItems_Insert_2 899
+#define wxControlWithItems_Insert_3 900
+#define wxControlWithItems_IsEmpty 901
+#define wxControlWithItems_Select 902
+#define wxControlWithItems_SetSelection 903
+#define wxControlWithItems_SetString 904
+#define wxControlWithItems_SetStringSelection 905
+#define wxMenu_new_2 908
+#define wxMenu_new_1 909
+#define wxMenu_destruct 911
+#define wxMenu_Append_3 912
+#define wxMenu_Append_1 913
+#define wxMenu_Append_4_0 914
+#define wxMenu_Append_4_1 915
+#define wxMenu_AppendCheckItem 916
+#define wxMenu_AppendRadioItem 917
+#define wxMenu_AppendSeparator 918
+#define wxMenu_Break 919
+#define wxMenu_Check 920
+#define wxMenu_Delete_1_0 921
+#define wxMenu_Delete_1_1 922
+#define wxMenu_Destroy_1_0 923
+#define wxMenu_Destroy_1_1 924
+#define wxMenu_Enable 925
+#define wxMenu_FindItem_1 926
+#define wxMenu_FindItem_2 927
+#define wxMenu_FindItemByPosition 928
+#define wxMenu_GetHelpString 929
+#define wxMenu_GetLabel 930
+#define wxMenu_GetMenuItemCount 931
+#define wxMenu_GetMenuItems 932
+#define wxMenu_GetTitle 934
+#define wxMenu_Insert_2 935
+#define wxMenu_Insert_3 936
+#define wxMenu_Insert_5_1 937
+#define wxMenu_Insert_5_0 938
+#define wxMenu_InsertCheckItem 939
+#define wxMenu_InsertRadioItem 940
+#define wxMenu_InsertSeparator 941
+#define wxMenu_IsChecked 942
+#define wxMenu_IsEnabled 943
+#define wxMenu_Prepend_1 944
+#define wxMenu_Prepend_2 945
+#define wxMenu_Prepend_4_1 946
+#define wxMenu_Prepend_4_0 947
+#define wxMenu_PrependCheckItem 948
+#define wxMenu_PrependRadioItem 949
+#define wxMenu_PrependSeparator 950
+#define wxMenu_Remove_1_0 951
+#define wxMenu_Remove_1_1 952
+#define wxMenu_SetHelpString 953
+#define wxMenu_SetLabel 954
+#define wxMenu_SetTitle 955
+#define wxMenuItem_new 956
+#define wxMenuItem_destruct 958
+#define wxMenuItem_Check 959
+#define wxMenuItem_Enable 960
+#define wxMenuItem_GetBitmap 961
+#define wxMenuItem_GetHelp 962
+#define wxMenuItem_GetId 963
+#define wxMenuItem_GetKind 964
+#define wxMenuItem_GetLabel 965
+#define wxMenuItem_GetLabelFromText 966
+#define wxMenuItem_GetMenu 967
+#define wxMenuItem_GetText 968
+#define wxMenuItem_GetSubMenu 969
+#define wxMenuItem_IsCheckable 970
+#define wxMenuItem_IsChecked 971
+#define wxMenuItem_IsEnabled 972
+#define wxMenuItem_IsSeparator 973
+#define wxMenuItem_IsSubMenu 974
+#define wxMenuItem_SetBitmap 975
+#define wxMenuItem_SetHelp 976
+#define wxMenuItem_SetMenu 977
+#define wxMenuItem_SetSubMenu 978
+#define wxMenuItem_SetText 979
+#define wxToolBar_AddControl 980
+#define wxToolBar_AddSeparator 981
+#define wxToolBar_AddTool_5 982
+#define wxToolBar_AddTool_4_0 983
+#define wxToolBar_AddTool_1 984
+#define wxToolBar_AddTool_4_1 985
+#define wxToolBar_AddTool_3 986
+#define wxToolBar_AddTool_6 987
+#define wxToolBar_AddCheckTool 988
+#define wxToolBar_AddRadioTool 989
+#define wxToolBar_AddStretchableSpace 990
+#define wxToolBar_InsertStretchableSpace 991
+#define wxToolBar_DeleteTool 992
+#define wxToolBar_DeleteToolByPos 993
+#define wxToolBar_EnableTool 994
+#define wxToolBar_FindById 995
+#define wxToolBar_FindControl 996
+#define wxToolBar_FindToolForPosition 997
+#define wxToolBar_GetToolSize 998
+#define wxToolBar_GetToolBitmapSize 999
+#define wxToolBar_GetMargins 1000
+#define wxToolBar_GetToolEnabled 1001
+#define wxToolBar_GetToolLongHelp 1002
+#define wxToolBar_GetToolPacking 1003
+#define wxToolBar_GetToolPos 1004
+#define wxToolBar_GetToolSeparation 1005
+#define wxToolBar_GetToolShortHelp 1006
+#define wxToolBar_GetToolState 1007
+#define wxToolBar_InsertControl 1008
+#define wxToolBar_InsertSeparator 1009
+#define wxToolBar_InsertTool_5 1010
+#define wxToolBar_InsertTool_2 1011
+#define wxToolBar_InsertTool_4 1012
+#define wxToolBar_Realize 1013
+#define wxToolBar_RemoveTool 1014
+#define wxToolBar_SetMargins 1015
+#define wxToolBar_SetToolBitmapSize 1016
+#define wxToolBar_SetToolLongHelp 1017
+#define wxToolBar_SetToolPacking 1018
+#define wxToolBar_SetToolShortHelp 1019
+#define wxToolBar_SetToolSeparation 1020
+#define wxToolBar_ToggleTool 1021
+#define wxStatusBar_new_0 1023
+#define wxStatusBar_new_2 1024
+#define wxStatusBar_destruct 1026
+#define wxStatusBar_Create 1027
+#define wxStatusBar_GetFieldRect 1028
+#define wxStatusBar_GetFieldsCount 1029
+#define wxStatusBar_GetStatusText 1030
+#define wxStatusBar_PopStatusText 1031
+#define wxStatusBar_PushStatusText 1032
+#define wxStatusBar_SetFieldsCount 1033
+#define wxStatusBar_SetMinHeight 1034
+#define wxStatusBar_SetStatusText 1035
+#define wxStatusBar_SetStatusWidths 1036
+#define wxStatusBar_SetStatusStyles 1037
+#define wxBitmap_new_0 1038
+#define wxBitmap_new_3 1039
+#define wxBitmap_new_4 1040
+#define wxBitmap_new_2_0 1041
+#define wxBitmap_new_2_1 1042
+#define wxBitmap_destruct 1043
+#define wxBitmap_ConvertToImage 1044
+#define wxBitmap_CopyFromIcon 1045
+#define wxBitmap_Create 1046
+#define wxBitmap_GetDepth 1047
+#define wxBitmap_GetHeight 1048
+#define wxBitmap_GetPalette 1049
+#define wxBitmap_GetMask 1050
+#define wxBitmap_GetWidth 1051
+#define wxBitmap_GetSubBitmap 1052
+#define wxBitmap_LoadFile 1053
+#define wxBitmap_Ok 1054
+#define wxBitmap_SaveFile 1055
+#define wxBitmap_SetDepth 1056
+#define wxBitmap_SetHeight 1057
+#define wxBitmap_SetMask 1058
+#define wxBitmap_SetPalette 1059
+#define wxBitmap_SetWidth 1060
+#define wxIcon_new_0 1061
+#define wxIcon_new_2 1062
+#define wxIcon_new_1 1063
+#define wxIcon_CopyFromBitmap 1064
+#define wxIcon_destroy 1065
+#define wxIconBundle_new_0 1066
+#define wxIconBundle_new_2 1067
+#define wxIconBundle_new_1_0 1068
+#define wxIconBundle_new_1_1 1069
+#define wxIconBundle_destruct 1070
+#define wxIconBundle_AddIcon_2 1071
+#define wxIconBundle_AddIcon_1 1072
+#define wxIconBundle_GetIcon_1_1 1073
+#define wxIconBundle_GetIcon_1_0 1074
+#define wxCursor_new_0 1075
+#define wxCursor_new_1_0 1076
+#define wxCursor_new_1_1 1077
+#define wxCursor_new_4 1078
+#define wxCursor_destruct 1079
+#define wxCursor_Ok 1080
+#define wxMask_new_0 1081
+#define wxMask_new_2_1 1082
+#define wxMask_new_2_0 1083
+#define wxMask_new_1 1084
+#define wxMask_destruct 1085
+#define wxMask_Create_2_1 1086
+#define wxMask_Create_2_0 1087
+#define wxMask_Create_1 1088
+#define wxImage_new_0 1089
+#define wxImage_new_3_0 1090
+#define wxImage_new_4 1091
+#define wxImage_new_5 1092
+#define wxImage_new_2 1093
+#define wxImage_new_3_1 1094
+#define wxImage_Blur 1095
+#define wxImage_BlurHorizontal 1096
+#define wxImage_BlurVertical 1097
+#define wxImage_ConvertAlphaToMask 1098
+#define wxImage_ConvertToGreyscale 1099
+#define wxImage_ConvertToMono 1100
+#define wxImage_Copy 1101
+#define wxImage_Create_3 1102
+#define wxImage_Create_4 1103
+#define wxImage_Create_5 1104
+#define wxImage_Destroy 1105
+#define wxImage_FindFirstUnusedColour 1106
+#define wxImage_GetImageExtWildcard 1107
+#define wxImage_GetAlpha_2 1108
+#define wxImage_GetAlpha_0 1109
+#define wxImage_GetBlue 1110
+#define wxImage_GetData 1111
+#define wxImage_GetGreen 1112
+#define wxImage_GetImageCount 1113
+#define wxImage_GetHeight 1114
+#define wxImage_GetMaskBlue 1115
+#define wxImage_GetMaskGreen 1116
+#define wxImage_GetMaskRed 1117
+#define wxImage_GetOrFindMaskColour 1118
+#define wxImage_GetPalette 1119
+#define wxImage_GetRed 1120
+#define wxImage_GetSubImage 1121
+#define wxImage_GetWidth 1122
+#define wxImage_HasAlpha 1123
+#define wxImage_HasMask 1124
+#define wxImage_GetOption 1125
+#define wxImage_GetOptionInt 1126
+#define wxImage_HasOption 1127
+#define wxImage_InitAlpha 1128
+#define wxImage_InitStandardHandlers 1129
+#define wxImage_IsTransparent 1130
+#define wxImage_LoadFile_2 1131
+#define wxImage_LoadFile_3 1132
+#define wxImage_Ok 1133
+#define wxImage_RemoveHandler 1134
+#define wxImage_Mirror 1135
+#define wxImage_Replace 1136
+#define wxImage_Rescale 1137
+#define wxImage_Resize 1138
+#define wxImage_Rotate 1139
+#define wxImage_RotateHue 1140
+#define wxImage_Rotate90 1141
+#define wxImage_SaveFile_1 1142
+#define wxImage_SaveFile_2_0 1143
+#define wxImage_SaveFile_2_1 1144
+#define wxImage_Scale 1145
+#define wxImage_Size 1146
+#define wxImage_SetAlpha_3 1147
+#define wxImage_SetAlpha_2 1148
+#define wxImage_SetData_2 1149
+#define wxImage_SetData_4 1150
+#define wxImage_SetMask 1151
+#define wxImage_SetMaskColour 1152
+#define wxImage_SetMaskFromImage 1153
+#define wxImage_SetOption_2_1 1154
+#define wxImage_SetOption_2_0 1155
+#define wxImage_SetPalette 1156
+#define wxImage_SetRGB_5 1157
+#define wxImage_SetRGB_4 1158
+#define wxImage_destroy 1159
+#define wxBrush_new_0 1160
+#define wxBrush_new_2 1161
+#define wxBrush_new_1 1162
+#define wxBrush_destruct 1164
+#define wxBrush_GetColour 1165
+#define wxBrush_GetStipple 1166
+#define wxBrush_GetStyle 1167
+#define wxBrush_IsHatch 1168
+#define wxBrush_IsOk 1169
+#define wxBrush_SetColour_1 1170
+#define wxBrush_SetColour_3 1171
+#define wxBrush_SetStipple 1172
+#define wxBrush_SetStyle 1173
+#define wxPen_new_0 1174
+#define wxPen_new_2 1175
+#define wxPen_destruct 1176
+#define wxPen_GetCap 1177
+#define wxPen_GetColour 1178
+#define wxPen_GetJoin 1179
+#define wxPen_GetStyle 1180
+#define wxPen_GetWidth 1181
+#define wxPen_IsOk 1182
+#define wxPen_SetCap 1183
+#define wxPen_SetColour_1 1184
+#define wxPen_SetColour_3 1185
+#define wxPen_SetJoin 1186
+#define wxPen_SetStyle 1187
+#define wxPen_SetWidth 1188
+#define wxRegion_new_0 1189
+#define wxRegion_new_4 1190
+#define wxRegion_new_2 1191
+#define wxRegion_new_1_1 1192
+#define wxRegion_new_1_0 1194
+#define wxRegion_destruct 1196
+#define wxRegion_Clear 1197
+#define wxRegion_Contains_2 1198
+#define wxRegion_Contains_1_0 1199
+#define wxRegion_Contains_4 1200
+#define wxRegion_Contains_1_1 1201
+#define wxRegion_ConvertToBitmap 1202
+#define wxRegion_GetBox 1203
+#define wxRegion_Intersect_4 1204
+#define wxRegion_Intersect_1_1 1205
+#define wxRegion_Intersect_1_0 1206
+#define wxRegion_IsEmpty 1207
+#define wxRegion_Subtract_4 1208
+#define wxRegion_Subtract_1_1 1209
+#define wxRegion_Subtract_1_0 1210
+#define wxRegion_Offset_2 1211
+#define wxRegion_Offset_1 1212
+#define wxRegion_Union_4 1213
+#define wxRegion_Union_1_2 1214
+#define wxRegion_Union_1_1 1215
+#define wxRegion_Union_1_0 1216
+#define wxRegion_Union_3 1217
+#define wxRegion_Xor_4 1218
+#define wxRegion_Xor_1_1 1219
+#define wxRegion_Xor_1_0 1220
+#define wxAcceleratorTable_new_0 1221
+#define wxAcceleratorTable_new_2 1222
+#define wxAcceleratorTable_destruct 1223
+#define wxAcceleratorTable_Ok 1224
+#define wxAcceleratorEntry_new_1_0 1225
+#define wxAcceleratorEntry_new_1_1 1226
+#define wxAcceleratorEntry_GetCommand 1227
+#define wxAcceleratorEntry_GetFlags 1228
+#define wxAcceleratorEntry_GetKeyCode 1229
+#define wxAcceleratorEntry_Set 1230
+#define wxAcceleratorEntry_destroy 1231
+#define wxCaret_new_3 1236
+#define wxCaret_new_2 1237
+#define wxCaret_destruct 1239
+#define wxCaret_Create_3 1240
+#define wxCaret_Create_2 1241
+#define wxCaret_GetBlinkTime 1242
+#define wxCaret_GetPosition 1244
+#define wxCaret_GetSize 1246
+#define wxCaret_GetWindow 1247
+#define wxCaret_Hide 1248
+#define wxCaret_IsOk 1249
+#define wxCaret_IsVisible 1250
+#define wxCaret_Move_2 1251
+#define wxCaret_Move_1 1252
+#define wxCaret_SetBlinkTime 1253
+#define wxCaret_SetSize_2 1254
+#define wxCaret_SetSize_1 1255
+#define wxCaret_Show 1256
+#define wxSizer_Add_2_1 1257
+#define wxSizer_Add_2_0 1258
+#define wxSizer_Add_3 1259
+#define wxSizer_Add_2_3 1260
+#define wxSizer_Add_2_2 1261
+#define wxSizer_AddSpacer 1262
+#define wxSizer_AddStretchSpacer 1263
+#define wxSizer_CalcMin 1264
+#define wxSizer_Clear 1265
+#define wxSizer_Detach_1_2 1266
+#define wxSizer_Detach_1_1 1267
+#define wxSizer_Detach_1_0 1268
+#define wxSizer_Fit 1269
+#define wxSizer_FitInside 1270
+#define wxSizer_GetChildren 1271
+#define wxSizer_GetItem_2_1 1272
+#define wxSizer_GetItem_2_0 1273
+#define wxSizer_GetItem_1 1274
+#define wxSizer_GetSize 1275
+#define wxSizer_GetPosition 1276
+#define wxSizer_GetMinSize 1277
+#define wxSizer_Hide_2_0 1278
+#define wxSizer_Hide_2_1 1279
+#define wxSizer_Hide_1 1280
+#define wxSizer_Insert_3_1 1281
+#define wxSizer_Insert_3_0 1282
+#define wxSizer_Insert_4 1283
+#define wxSizer_Insert_3_3 1284
+#define wxSizer_Insert_3_2 1285
+#define wxSizer_Insert_2 1286
+#define wxSizer_InsertSpacer 1287
+#define wxSizer_InsertStretchSpacer 1288
+#define wxSizer_IsShown_1_2 1289
+#define wxSizer_IsShown_1_1 1290
+#define wxSizer_IsShown_1_0 1291
+#define wxSizer_Layout 1292
+#define wxSizer_Prepend_2_1 1293
+#define wxSizer_Prepend_2_0 1294
+#define wxSizer_Prepend_3 1295
+#define wxSizer_Prepend_2_3 1296
+#define wxSizer_Prepend_2_2 1297
+#define wxSizer_Prepend_1 1298
+#define wxSizer_PrependSpacer 1299
+#define wxSizer_PrependStretchSpacer 1300
+#define wxSizer_RecalcSizes 1301
+#define wxSizer_Remove_1_1 1302
+#define wxSizer_Remove_1_0 1303
+#define wxSizer_Replace_3_1 1304
+#define wxSizer_Replace_3_0 1305
+#define wxSizer_Replace_2 1306
+#define wxSizer_SetDimension 1307
+#define wxSizer_SetMinSize_2 1308
+#define wxSizer_SetMinSize_1 1309
+#define wxSizer_SetItemMinSize_3_2 1310
+#define wxSizer_SetItemMinSize_2_2 1311
+#define wxSizer_SetItemMinSize_3_1 1312
+#define wxSizer_SetItemMinSize_2_1 1313
+#define wxSizer_SetItemMinSize_3_0 1314
+#define wxSizer_SetItemMinSize_2_0 1315
+#define wxSizer_SetSizeHints 1316
+#define wxSizer_SetVirtualSizeHints 1317
+#define wxSizer_Show_2_2 1318
+#define wxSizer_Show_2_1 1319
+#define wxSizer_Show_2_0 1320
+#define wxSizer_Show_1 1321
+#define wxSizerFlags_new 1322
+#define wxSizerFlags_Align 1323
+#define wxSizerFlags_Border_2 1324
+#define wxSizerFlags_Border_1 1325
+#define wxSizerFlags_Center 1326
+#define wxSizerFlags_Centre 1327
+#define wxSizerFlags_Expand 1328
+#define wxSizerFlags_Left 1329
+#define wxSizerFlags_Proportion 1330
+#define wxSizerFlags_Right 1331
+#define wxSizerFlags_destroy 1332
+#define wxSizerItem_new_5_1 1333
+#define wxSizerItem_new_2_1 1334
+#define wxSizerItem_new_5_0 1335
+#define wxSizerItem_new_2_0 1336
+#define wxSizerItem_new_6 1337
+#define wxSizerItem_new_3 1338
+#define wxSizerItem_new_0 1339
+#define wxSizerItem_destruct 1340
+#define wxSizerItem_CalcMin 1341
+#define wxSizerItem_DeleteWindows 1342
+#define wxSizerItem_DetachSizer 1343
+#define wxSizerItem_GetBorder 1344
+#define wxSizerItem_GetFlag 1345
+#define wxSizerItem_GetMinSize 1346
+#define wxSizerItem_GetPosition 1347
+#define wxSizerItem_GetProportion 1348
+#define wxSizerItem_GetRatio 1349
+#define wxSizerItem_GetRect 1350
+#define wxSizerItem_GetSize 1351
+#define wxSizerItem_GetSizer 1352
+#define wxSizerItem_GetSpacer 1353
+#define wxSizerItem_GetUserData 1354
+#define wxSizerItem_GetWindow 1355
+#define wxSizerItem_IsSizer 1356
+#define wxSizerItem_IsShown 1357
+#define wxSizerItem_IsSpacer 1358
+#define wxSizerItem_IsWindow 1359
+#define wxSizerItem_SetBorder 1360
+#define wxSizerItem_SetDimension 1361
+#define wxSizerItem_SetFlag 1362
+#define wxSizerItem_SetInitSize 1363
+#define wxSizerItem_SetMinSize_1 1364
+#define wxSizerItem_SetMinSize_2 1365
+#define wxSizerItem_SetProportion 1366
+#define wxSizerItem_SetRatio_2 1367
+#define wxSizerItem_SetRatio_1_1 1368
+#define wxSizerItem_SetRatio_1_0 1369
+#define wxSizerItem_SetSizer 1370
+#define wxSizerItem_SetSpacer_1 1371
+#define wxSizerItem_SetSpacer_2 1372
+#define wxSizerItem_SetWindow 1373
+#define wxSizerItem_Show 1374
+#define wxBoxSizer_new 1375
+#define wxBoxSizer_GetOrientation 1376
+#define wxBoxSizer_destroy 1377
+#define wxStaticBoxSizer_new_2 1378
+#define wxStaticBoxSizer_new_3 1379
+#define wxStaticBoxSizer_GetStaticBox 1380
+#define wxStaticBoxSizer_destroy 1381
+#define wxGridSizer_new_4 1382
+#define wxGridSizer_new_2 1383
+#define wxGridSizer_GetCols 1384
+#define wxGridSizer_GetHGap 1385
+#define wxGridSizer_GetRows 1386
+#define wxGridSizer_GetVGap 1387
+#define wxGridSizer_SetCols 1388
+#define wxGridSizer_SetHGap 1389
+#define wxGridSizer_SetRows 1390
+#define wxGridSizer_SetVGap 1391
+#define wxGridSizer_destroy 1392
+#define wxFlexGridSizer_new_4 1393
+#define wxFlexGridSizer_new_2 1394
+#define wxFlexGridSizer_AddGrowableCol 1395
+#define wxFlexGridSizer_AddGrowableRow 1396
+#define wxFlexGridSizer_GetFlexibleDirection 1397
+#define wxFlexGridSizer_GetNonFlexibleGrowMode 1398
+#define wxFlexGridSizer_RemoveGrowableCol 1399
+#define wxFlexGridSizer_RemoveGrowableRow 1400
+#define wxFlexGridSizer_SetFlexibleDirection 1401
+#define wxFlexGridSizer_SetNonFlexibleGrowMode 1402
+#define wxFlexGridSizer_destroy 1403
+#define wxGridBagSizer_new 1404
+#define wxGridBagSizer_Add_3_2 1405
+#define wxGridBagSizer_Add_3_1 1406
+#define wxGridBagSizer_Add_4 1407
+#define wxGridBagSizer_Add_1_0 1408
+#define wxGridBagSizer_Add_2_1 1409
+#define wxGridBagSizer_Add_2_0 1410
+#define wxGridBagSizer_Add_3_0 1411
+#define wxGridBagSizer_Add_1_1 1412
+#define wxGridBagSizer_CalcMin 1413
+#define wxGridBagSizer_CheckForIntersection_2 1414
+#define wxGridBagSizer_CheckForIntersection_3 1415
+#define wxGridBagSizer_FindItem_1_1 1416
+#define wxGridBagSizer_FindItem_1_0 1417
+#define wxGridBagSizer_FindItemAtPoint 1418
+#define wxGridBagSizer_FindItemAtPosition 1419
+#define wxGridBagSizer_FindItemWithData 1420
+#define wxGridBagSizer_GetCellSize 1421
+#define wxGridBagSizer_GetEmptyCellSize 1422
+#define wxGridBagSizer_GetItemPosition_1_2 1423
+#define wxGridBagSizer_GetItemPosition_1_1 1424
+#define wxGridBagSizer_GetItemPosition_1_0 1425
+#define wxGridBagSizer_GetItemSpan_1_2 1426
+#define wxGridBagSizer_GetItemSpan_1_1 1427
+#define wxGridBagSizer_GetItemSpan_1_0 1428
+#define wxGridBagSizer_SetEmptyCellSize 1429
+#define wxGridBagSizer_SetItemPosition_2_2 1430
+#define wxGridBagSizer_SetItemPosition_2_1 1431
+#define wxGridBagSizer_SetItemPosition_2_0 1432
+#define wxGridBagSizer_SetItemSpan_2_2 1433
+#define wxGridBagSizer_SetItemSpan_2_1 1434
+#define wxGridBagSizer_SetItemSpan_2_0 1435
+#define wxGridBagSizer_destroy 1436
+#define wxStdDialogButtonSizer_new 1437
+#define wxStdDialogButtonSizer_AddButton 1438
+#define wxStdDialogButtonSizer_Realize 1439
+#define wxStdDialogButtonSizer_SetAffirmativeButton 1440
+#define wxStdDialogButtonSizer_SetCancelButton 1441
+#define wxStdDialogButtonSizer_SetNegativeButton 1442
+#define wxStdDialogButtonSizer_destroy 1443
+#define wxFont_new_0 1444
+#define wxFont_new_1 1445
+#define wxFont_new_5 1446
+#define wxFont_destruct 1448
+#define wxFont_IsFixedWidth 1449
+#define wxFont_GetDefaultEncoding 1450
+#define wxFont_GetFaceName 1451
+#define wxFont_GetFamily 1452
+#define wxFont_GetNativeFontInfoDesc 1453
+#define wxFont_GetNativeFontInfoUserDesc 1454
+#define wxFont_GetPointSize 1455
+#define wxFont_GetStyle 1456
+#define wxFont_GetUnderlined 1457
+#define wxFont_GetWeight 1458
+#define wxFont_Ok 1459
+#define wxFont_SetDefaultEncoding 1460
+#define wxFont_SetFaceName 1461
+#define wxFont_SetFamily 1462
+#define wxFont_SetPointSize 1463
+#define wxFont_SetStyle 1464
+#define wxFont_SetUnderlined 1465
+#define wxFont_SetWeight 1466
+#define wxToolTip_Enable 1467
+#define wxToolTip_SetDelay 1468
+#define wxToolTip_new 1469
+#define wxToolTip_SetTip 1470
+#define wxToolTip_GetTip 1471
+#define wxToolTip_GetWindow 1472
+#define wxToolTip_destroy 1473
+#define wxButton_new_3 1475
+#define wxButton_new_0 1476
+#define wxButton_destruct 1477
+#define wxButton_Create 1478
+#define wxButton_GetDefaultSize 1479
+#define wxButton_SetDefault 1480
+#define wxButton_SetLabel 1481
+#define wxBitmapButton_new_4 1483
+#define wxBitmapButton_new_0 1484
+#define wxBitmapButton_Create 1485
+#define wxBitmapButton_GetBitmapDisabled 1486
+#define wxBitmapButton_GetBitmapFocus 1488
+#define wxBitmapButton_GetBitmapLabel 1490
+#define wxBitmapButton_GetBitmapSelected 1492
+#define wxBitmapButton_SetBitmapDisabled 1494
+#define wxBitmapButton_SetBitmapFocus 1495
+#define wxBitmapButton_SetBitmapLabel 1496
+#define wxBitmapButton_SetBitmapSelected 1497
+#define wxBitmapButton_destroy 1498
+#define wxToggleButton_new_0 1499
+#define wxToggleButton_new_4 1500
+#define wxToggleButton_Create 1501
+#define wxToggleButton_GetValue 1502
+#define wxToggleButton_SetValue 1503
+#define wxToggleButton_destroy 1504
+#define wxCalendarCtrl_new_0 1505
+#define wxCalendarCtrl_new_3 1506
+#define wxCalendarCtrl_Create 1507
+#define wxCalendarCtrl_destruct 1508
+#define wxCalendarCtrl_SetDate 1509
+#define wxCalendarCtrl_GetDate 1510
+#define wxCalendarCtrl_EnableYearChange 1511
+#define wxCalendarCtrl_EnableMonthChange 1512
+#define wxCalendarCtrl_EnableHolidayDisplay 1513
+#define wxCalendarCtrl_SetHeaderColours 1514
+#define wxCalendarCtrl_GetHeaderColourFg 1515
+#define wxCalendarCtrl_GetHeaderColourBg 1516
+#define wxCalendarCtrl_SetHighlightColours 1517
+#define wxCalendarCtrl_GetHighlightColourFg 1518
+#define wxCalendarCtrl_GetHighlightColourBg 1519
+#define wxCalendarCtrl_SetHolidayColours 1520
+#define wxCalendarCtrl_GetHolidayColourFg 1521
+#define wxCalendarCtrl_GetHolidayColourBg 1522
+#define wxCalendarCtrl_GetAttr 1523
+#define wxCalendarCtrl_SetAttr 1524
+#define wxCalendarCtrl_SetHoliday 1525
+#define wxCalendarCtrl_ResetAttr 1526
+#define wxCalendarCtrl_HitTest 1527
+#define wxCalendarDateAttr_new_0 1528
+#define wxCalendarDateAttr_new_2_1 1529
+#define wxCalendarDateAttr_new_2_0 1530
+#define wxCalendarDateAttr_SetTextColour 1531
+#define wxCalendarDateAttr_SetBackgroundColour 1532
+#define wxCalendarDateAttr_SetBorderColour 1533
+#define wxCalendarDateAttr_SetFont 1534
+#define wxCalendarDateAttr_SetBorder 1535
+#define wxCalendarDateAttr_SetHoliday 1536
+#define wxCalendarDateAttr_HasTextColour 1537
+#define wxCalendarDateAttr_HasBackgroundColour 1538
+#define wxCalendarDateAttr_HasBorderColour 1539
+#define wxCalendarDateAttr_HasFont 1540
+#define wxCalendarDateAttr_HasBorder 1541
+#define wxCalendarDateAttr_IsHoliday 1542
+#define wxCalendarDateAttr_GetTextColour 1543
+#define wxCalendarDateAttr_GetBackgroundColour 1544
+#define wxCalendarDateAttr_GetBorderColour 1545
+#define wxCalendarDateAttr_GetFont 1546
+#define wxCalendarDateAttr_GetBorder 1547
+#define wxCalendarDateAttr_destroy 1548
+#define wxCheckBox_new_4 1550
+#define wxCheckBox_new_0 1551
+#define wxCheckBox_Create 1552
+#define wxCheckBox_GetValue 1553
+#define wxCheckBox_Get3StateValue 1554
+#define wxCheckBox_Is3rdStateAllowedForUser 1555
+#define wxCheckBox_Is3State 1556
+#define wxCheckBox_IsChecked 1557
+#define wxCheckBox_SetValue 1558
+#define wxCheckBox_Set3StateValue 1559
+#define wxCheckBox_destroy 1560
+#define wxCheckListBox_new_0 1561
+#define wxCheckListBox_new_3 1563
+#define wxCheckListBox_Check 1564
+#define wxCheckListBox_IsChecked 1565
+#define wxCheckListBox_destroy 1566
+#define wxChoice_new_3 1569
+#define wxChoice_new_0 1570
+#define wxChoice_destruct 1572
+#define wxChoice_Create 1574
+#define wxChoice_Delete 1575
+#define wxChoice_GetColumns 1576
+#define wxChoice_SetColumns 1577
+#define wxComboBox_new_0 1578
+#define wxComboBox_new_3 1580
+#define wxComboBox_destruct 1581
+#define wxComboBox_Create 1583
+#define wxComboBox_CanCopy 1584
+#define wxComboBox_CanCut 1585
+#define wxComboBox_CanPaste 1586
+#define wxComboBox_CanRedo 1587
+#define wxComboBox_CanUndo 1588
+#define wxComboBox_Copy 1589
+#define wxComboBox_Cut 1590
+#define wxComboBox_GetInsertionPoint 1591
+#define wxComboBox_GetLastPosition 1592
+#define wxComboBox_GetValue 1593
+#define wxComboBox_Paste 1594
+#define wxComboBox_Redo 1595
+#define wxComboBox_Replace 1596
+#define wxComboBox_Remove 1597
+#define wxComboBox_SetInsertionPoint 1598
+#define wxComboBox_SetInsertionPointEnd 1599
+#define wxComboBox_SetSelection_1 1600
+#define wxComboBox_SetSelection_2 1601
+#define wxComboBox_SetValue 1602
+#define wxComboBox_Undo 1603
+#define wxGauge_new_0 1604
+#define wxGauge_new_4 1605
+#define wxGauge_Create 1606
+#define wxGauge_GetRange 1607
+#define wxGauge_GetValue 1608
+#define wxGauge_IsVertical 1609
+#define wxGauge_SetRange 1610
+#define wxGauge_SetValue 1611
+#define wxGauge_Pulse 1612
+#define wxGauge_destroy 1613
+#define wxGenericDirCtrl_new_0 1614
+#define wxGenericDirCtrl_new_2 1615
+#define wxGenericDirCtrl_destruct 1616
+#define wxGenericDirCtrl_Create 1617
+#define wxGenericDirCtrl_Init 1618
+#define wxGenericDirCtrl_CollapseTree 1619
+#define wxGenericDirCtrl_ExpandPath 1620
+#define wxGenericDirCtrl_GetDefaultPath 1621
+#define wxGenericDirCtrl_GetPath 1622
+#define wxGenericDirCtrl_GetFilePath 1623
+#define wxGenericDirCtrl_GetFilter 1624
+#define wxGenericDirCtrl_GetFilterIndex 1625
+#define wxGenericDirCtrl_GetRootId 1626
+#define wxGenericDirCtrl_GetTreeCtrl 1627
+#define wxGenericDirCtrl_ReCreateTree 1628
+#define wxGenericDirCtrl_SetDefaultPath 1629
+#define wxGenericDirCtrl_SetFilter 1630
+#define wxGenericDirCtrl_SetFilterIndex 1631
+#define wxGenericDirCtrl_SetPath 1632
+#define wxStaticBox_new_4 1634
+#define wxStaticBox_new_0 1635
+#define wxStaticBox_Create 1636
+#define wxStaticBox_destroy 1637
+#define wxStaticLine_new_2 1639
+#define wxStaticLine_new_0 1640
+#define wxStaticLine_Create 1641
+#define wxStaticLine_IsVertical 1642
+#define wxStaticLine_GetDefaultSize 1643
+#define wxStaticLine_destroy 1644
+#define wxListBox_new_3 1647
+#define wxListBox_new_0 1648
+#define wxListBox_destruct 1650
+#define wxListBox_Create 1652
+#define wxListBox_Deselect 1653
+#define wxListBox_GetSelections 1654
+#define wxListBox_InsertItems 1655
+#define wxListBox_IsSelected 1656
+#define wxListBox_Set 1657
+#define wxListBox_HitTest 1658
+#define wxListBox_SetFirstItem_1_0 1659
+#define wxListBox_SetFirstItem_1_1 1660
+#define wxListCtrl_new_0 1661
+#define wxListCtrl_new_2 1662
+#define wxListCtrl_Arrange 1663
+#define wxListCtrl_AssignImageList 1664
+#define wxListCtrl_ClearAll 1665
+#define wxListCtrl_Create 1666
+#define wxListCtrl_DeleteAllItems 1667
+#define wxListCtrl_DeleteColumn 1668
+#define wxListCtrl_DeleteItem 1669
+#define wxListCtrl_EditLabel 1670
+#define wxListCtrl_EnsureVisible 1671
+#define wxListCtrl_FindItem_3_0 1672
+#define wxListCtrl_FindItem_3_1 1673
+#define wxListCtrl_GetColumn 1674
+#define wxListCtrl_GetColumnCount 1675
+#define wxListCtrl_GetColumnWidth 1676
+#define wxListCtrl_GetCountPerPage 1677
+#define wxListCtrl_GetEditControl 1678
+#define wxListCtrl_GetImageList 1679
+#define wxListCtrl_GetItem 1680
+#define wxListCtrl_GetItemBackgroundColour 1681
+#define wxListCtrl_GetItemCount 1682
+#define wxListCtrl_GetItemData 1683
+#define wxListCtrl_GetItemFont 1684
+#define wxListCtrl_GetItemPosition 1685
+#define wxListCtrl_GetItemRect 1686
+#define wxListCtrl_GetItemSpacing 1687
+#define wxListCtrl_GetItemState 1688
+#define wxListCtrl_GetItemText 1689
+#define wxListCtrl_GetItemTextColour 1690
+#define wxListCtrl_GetNextItem 1691
+#define wxListCtrl_GetSelectedItemCount 1692
+#define wxListCtrl_GetTextColour 1693
+#define wxListCtrl_GetTopItem 1694
+#define wxListCtrl_GetViewRect 1695
+#define wxListCtrl_HitTest 1696
+#define wxListCtrl_InsertColumn_2 1697
+#define wxListCtrl_InsertColumn_3 1698
+#define wxListCtrl_InsertItem_1 1699
+#define wxListCtrl_InsertItem_2_1 1700
+#define wxListCtrl_InsertItem_2_0 1701
+#define wxListCtrl_InsertItem_3 1702
+#define wxListCtrl_RefreshItem 1703
+#define wxListCtrl_RefreshItems 1704
+#define wxListCtrl_ScrollList 1705
+#define wxListCtrl_SetBackgroundColour 1706
+#define wxListCtrl_SetColumn 1707
+#define wxListCtrl_SetColumnWidth 1708
+#define wxListCtrl_SetImageList 1709
+#define wxListCtrl_SetItem_1 1710
+#define wxListCtrl_SetItem_4 1711
+#define wxListCtrl_SetItemBackgroundColour 1712
+#define wxListCtrl_SetItemCount 1713
+#define wxListCtrl_SetItemData 1714
+#define wxListCtrl_SetItemFont 1715
+#define wxListCtrl_SetItemImage 1716
+#define wxListCtrl_SetItemColumnImage 1717
+#define wxListCtrl_SetItemPosition 1718
+#define wxListCtrl_SetItemState 1719
+#define wxListCtrl_SetItemText 1720
+#define wxListCtrl_SetItemTextColour 1721
+#define wxListCtrl_SetSingleStyle 1722
+#define wxListCtrl_SetTextColour 1723
+#define wxListCtrl_SetWindowStyleFlag 1724
+#define wxListCtrl_SortItems 1725
+#define wxListCtrl_destroy 1726
+#define wxListView_ClearColumnImage 1727
+#define wxListView_Focus 1728
+#define wxListView_GetFirstSelected 1729
+#define wxListView_GetFocusedItem 1730
+#define wxListView_GetNextSelected 1731
+#define wxListView_IsSelected 1732
+#define wxListView_Select 1733
+#define wxListView_SetColumnImage 1734
+#define wxListItem_new_0 1735
+#define wxListItem_new_1 1736
+#define wxListItem_destruct 1737
+#define wxListItem_Clear 1738
+#define wxListItem_GetAlign 1739
+#define wxListItem_GetBackgroundColour 1740
+#define wxListItem_GetColumn 1741
+#define wxListItem_GetFont 1742
+#define wxListItem_GetId 1743
+#define wxListItem_GetImage 1744
+#define wxListItem_GetMask 1745
+#define wxListItem_GetState 1746
+#define wxListItem_GetText 1747
+#define wxListItem_GetTextColour 1748
+#define wxListItem_GetWidth 1749
+#define wxListItem_SetAlign 1750
+#define wxListItem_SetBackgroundColour 1751
+#define wxListItem_SetColumn 1752
+#define wxListItem_SetFont 1753
+#define wxListItem_SetId 1754
+#define wxListItem_SetImage 1755
+#define wxListItem_SetMask 1756
+#define wxListItem_SetState 1757
+#define wxListItem_SetStateMask 1758
+#define wxListItem_SetText 1759
+#define wxListItem_SetTextColour 1760
+#define wxListItem_SetWidth 1761
+#define wxListItemAttr_new_0 1762
+#define wxListItemAttr_new_3 1763
+#define wxListItemAttr_GetBackgroundColour 1764
+#define wxListItemAttr_GetFont 1765
+#define wxListItemAttr_GetTextColour 1766
+#define wxListItemAttr_HasBackgroundColour 1767
+#define wxListItemAttr_HasFont 1768
+#define wxListItemAttr_HasTextColour 1769
+#define wxListItemAttr_SetBackgroundColour 1770
+#define wxListItemAttr_SetFont 1771
+#define wxListItemAttr_SetTextColour 1772
+#define wxListItemAttr_destroy 1773
+#define wxImageList_new_0 1774
+#define wxImageList_new_3 1775
+#define wxImageList_Add_1 1776
+#define wxImageList_Add_2_0 1777
+#define wxImageList_Add_2_1 1778
+#define wxImageList_Create 1779
+#define wxImageList_Draw 1781
+#define wxImageList_GetBitmap 1782
+#define wxImageList_GetIcon 1783
+#define wxImageList_GetImageCount 1784
+#define wxImageList_GetSize 1785
+#define wxImageList_Remove 1786
+#define wxImageList_RemoveAll 1787
+#define wxImageList_Replace_2 1788
+#define wxImageList_Replace_3 1789
+#define wxImageList_destroy 1790
+#define wxTextAttr_new_0 1791
+#define wxTextAttr_new_2 1792
+#define wxTextAttr_GetAlignment 1793
+#define wxTextAttr_GetBackgroundColour 1794
+#define wxTextAttr_GetFont 1795
+#define wxTextAttr_GetLeftIndent 1796
+#define wxTextAttr_GetLeftSubIndent 1797
+#define wxTextAttr_GetRightIndent 1798
+#define wxTextAttr_GetTabs 1799
+#define wxTextAttr_GetTextColour 1800
+#define wxTextAttr_HasBackgroundColour 1801
+#define wxTextAttr_HasFont 1802
+#define wxTextAttr_HasTextColour 1803
+#define wxTextAttr_GetFlags 1804
+#define wxTextAttr_IsDefault 1805
+#define wxTextAttr_SetAlignment 1806
+#define wxTextAttr_SetBackgroundColour 1807
+#define wxTextAttr_SetFlags 1808
+#define wxTextAttr_SetFont 1809
+#define wxTextAttr_SetLeftIndent 1810
+#define wxTextAttr_SetRightIndent 1811
+#define wxTextAttr_SetTabs 1812
+#define wxTextAttr_SetTextColour 1813
+#define wxTextAttr_destroy 1814
+#define wxTextCtrl_new_3 1816
+#define wxTextCtrl_new_0 1817
+#define wxTextCtrl_destruct 1819
+#define wxTextCtrl_AppendText 1820
+#define wxTextCtrl_CanCopy 1821
+#define wxTextCtrl_CanCut 1822
+#define wxTextCtrl_CanPaste 1823
+#define wxTextCtrl_CanRedo 1824
+#define wxTextCtrl_CanUndo 1825
+#define wxTextCtrl_Clear 1826
+#define wxTextCtrl_Copy 1827
+#define wxTextCtrl_Create 1828
+#define wxTextCtrl_Cut 1829
+#define wxTextCtrl_DiscardEdits 1830
+#define wxTextCtrl_ChangeValue 1831
+#define wxTextCtrl_EmulateKeyPress 1832
+#define wxTextCtrl_GetDefaultStyle 1833
+#define wxTextCtrl_GetInsertionPoint 1834
+#define wxTextCtrl_GetLastPosition 1835
+#define wxTextCtrl_GetLineLength 1836
+#define wxTextCtrl_GetLineText 1837
+#define wxTextCtrl_GetNumberOfLines 1838
+#define wxTextCtrl_GetRange 1839
+#define wxTextCtrl_GetSelection 1840
+#define wxTextCtrl_GetStringSelection 1841
+#define wxTextCtrl_GetStyle 1842
+#define wxTextCtrl_GetValue 1843
+#define wxTextCtrl_IsEditable 1844
+#define wxTextCtrl_IsModified 1845
+#define wxTextCtrl_IsMultiLine 1846
+#define wxTextCtrl_IsSingleLine 1847
+#define wxTextCtrl_LoadFile 1848
+#define wxTextCtrl_MarkDirty 1849
+#define wxTextCtrl_Paste 1850
+#define wxTextCtrl_PositionToXY 1851
+#define wxTextCtrl_Redo 1852
+#define wxTextCtrl_Remove 1853
+#define wxTextCtrl_Replace 1854
+#define wxTextCtrl_SaveFile 1855
+#define wxTextCtrl_SetDefaultStyle 1856
+#define wxTextCtrl_SetEditable 1857
+#define wxTextCtrl_SetInsertionPoint 1858
+#define wxTextCtrl_SetInsertionPointEnd 1859
+#define wxTextCtrl_SetMaxLength 1861
+#define wxTextCtrl_SetSelection 1862
+#define wxTextCtrl_SetStyle 1863
+#define wxTextCtrl_SetValue 1864
+#define wxTextCtrl_ShowPosition 1865
+#define wxTextCtrl_Undo 1866
+#define wxTextCtrl_WriteText 1867
+#define wxTextCtrl_XYToPosition 1868
+#define wxNotebook_new_0 1871
+#define wxNotebook_new_3 1872
+#define wxNotebook_destruct 1873
+#define wxNotebook_AddPage 1874
+#define wxNotebook_AdvanceSelection 1875
+#define wxNotebook_AssignImageList 1876
+#define wxNotebook_Create 1877
+#define wxNotebook_DeleteAllPages 1878
+#define wxNotebook_DeletePage 1879
+#define wxNotebook_RemovePage 1880
+#define wxNotebook_GetCurrentPage 1881
+#define wxNotebook_GetImageList 1882
+#define wxNotebook_GetPage 1884
+#define wxNotebook_GetPageCount 1885
+#define wxNotebook_GetPageImage 1886
+#define wxNotebook_GetPageText 1887
+#define wxNotebook_GetRowCount 1888
+#define wxNotebook_GetSelection 1889
+#define wxNotebook_GetThemeBackgroundColour 1890
+#define wxNotebook_HitTest 1892
+#define wxNotebook_InsertPage 1894
+#define wxNotebook_SetImageList 1895
+#define wxNotebook_SetPadding 1896
+#define wxNotebook_SetPageSize 1897
+#define wxNotebook_SetPageImage 1898
+#define wxNotebook_SetPageText 1899
+#define wxNotebook_SetSelection 1900
+#define wxNotebook_ChangeSelection 1901
+#define wxChoicebook_new_0 1902
+#define wxChoicebook_new_3 1903
+#define wxChoicebook_AddPage 1904
+#define wxChoicebook_AdvanceSelection 1905
+#define wxChoicebook_AssignImageList 1906
+#define wxChoicebook_Create 1907
+#define wxChoicebook_DeleteAllPages 1908
+#define wxChoicebook_DeletePage 1909
+#define wxChoicebook_RemovePage 1910
+#define wxChoicebook_GetCurrentPage 1911
+#define wxChoicebook_GetImageList 1912
+#define wxChoicebook_GetPage 1914
+#define wxChoicebook_GetPageCount 1915
+#define wxChoicebook_GetPageImage 1916
+#define wxChoicebook_GetPageText 1917
+#define wxChoicebook_GetSelection 1918
+#define wxChoicebook_HitTest 1919
+#define wxChoicebook_InsertPage 1920
+#define wxChoicebook_SetImageList 1921
+#define wxChoicebook_SetPageSize 1922
+#define wxChoicebook_SetPageImage 1923
+#define wxChoicebook_SetPageText 1924
+#define wxChoicebook_SetSelection 1925
+#define wxChoicebook_ChangeSelection 1926
+#define wxChoicebook_destroy 1927
+#define wxToolbook_new_0 1928
+#define wxToolbook_new_3 1929
+#define wxToolbook_AddPage 1930
+#define wxToolbook_AdvanceSelection 1931
+#define wxToolbook_AssignImageList 1932
+#define wxToolbook_Create 1933
+#define wxToolbook_DeleteAllPages 1934
+#define wxToolbook_DeletePage 1935
+#define wxToolbook_RemovePage 1936
+#define wxToolbook_GetCurrentPage 1937
+#define wxToolbook_GetImageList 1938
+#define wxToolbook_GetPage 1940
+#define wxToolbook_GetPageCount 1941
+#define wxToolbook_GetPageImage 1942
+#define wxToolbook_GetPageText 1943
+#define wxToolbook_GetSelection 1944
+#define wxToolbook_HitTest 1946
+#define wxToolbook_InsertPage 1947
+#define wxToolbook_SetImageList 1948
+#define wxToolbook_SetPageSize 1949
+#define wxToolbook_SetPageImage 1950
+#define wxToolbook_SetPageText 1951
+#define wxToolbook_SetSelection 1952
+#define wxToolbook_ChangeSelection 1953
+#define wxToolbook_destroy 1954
+#define wxListbook_new_0 1955
+#define wxListbook_new_3 1956
+#define wxListbook_AddPage 1957
+#define wxListbook_AdvanceSelection 1958
+#define wxListbook_AssignImageList 1959
+#define wxListbook_Create 1960
+#define wxListbook_DeleteAllPages 1961
+#define wxListbook_DeletePage 1962
+#define wxListbook_RemovePage 1963
+#define wxListbook_GetCurrentPage 1964
+#define wxListbook_GetImageList 1965
+#define wxListbook_GetPage 1967
+#define wxListbook_GetPageCount 1968
+#define wxListbook_GetPageImage 1969
+#define wxListbook_GetPageText 1970
+#define wxListbook_GetSelection 1971
+#define wxListbook_HitTest 1973
+#define wxListbook_InsertPage 1974
+#define wxListbook_SetImageList 1975
+#define wxListbook_SetPageSize 1976
+#define wxListbook_SetPageImage 1977
+#define wxListbook_SetPageText 1978
+#define wxListbook_SetSelection 1979
+#define wxListbook_ChangeSelection 1980
+#define wxListbook_destroy 1981
+#define wxTreebook_new_0 1982
+#define wxTreebook_new_3 1983
+#define wxTreebook_AddPage 1984
+#define wxTreebook_AdvanceSelection 1985
+#define wxTreebook_AssignImageList 1986
+#define wxTreebook_Create 1987
+#define wxTreebook_DeleteAllPages 1988
+#define wxTreebook_DeletePage 1989
+#define wxTreebook_RemovePage 1990
+#define wxTreebook_GetCurrentPage 1991
+#define wxTreebook_GetImageList 1992
+#define wxTreebook_GetPage 1994
+#define wxTreebook_GetPageCount 1995
+#define wxTreebook_GetPageImage 1996
+#define wxTreebook_GetPageText 1997
+#define wxTreebook_GetSelection 1998
+#define wxTreebook_ExpandNode 1999
+#define wxTreebook_IsNodeExpanded 2000
+#define wxTreebook_HitTest 2002
+#define wxTreebook_InsertPage 2003
+#define wxTreebook_InsertSubPage 2004
+#define wxTreebook_SetImageList 2005
+#define wxTreebook_SetPageSize 2006
+#define wxTreebook_SetPageImage 2007
+#define wxTreebook_SetPageText 2008
+#define wxTreebook_SetSelection 2009
+#define wxTreebook_ChangeSelection 2010
+#define wxTreebook_destroy 2011
+#define wxTreeCtrl_new_2 2014
+#define wxTreeCtrl_new_0 2015
+#define wxTreeCtrl_destruct 2017
+#define wxTreeCtrl_AddRoot 2018
+#define wxTreeCtrl_AppendItem 2019
+#define wxTreeCtrl_AssignImageList 2020
+#define wxTreeCtrl_AssignStateImageList 2021
+#define wxTreeCtrl_Collapse 2022
+#define wxTreeCtrl_CollapseAndReset 2023
+#define wxTreeCtrl_Create 2024
+#define wxTreeCtrl_Delete 2025
+#define wxTreeCtrl_DeleteAllItems 2026
+#define wxTreeCtrl_DeleteChildren 2027
+#define wxTreeCtrl_EditLabel 2028
+#define wxTreeCtrl_EnsureVisible 2029
+#define wxTreeCtrl_Expand 2030
+#define wxTreeCtrl_GetBoundingRect 2031
+#define wxTreeCtrl_GetChildrenCount 2033
+#define wxTreeCtrl_GetCount 2034
+#define wxTreeCtrl_GetEditControl 2035
+#define wxTreeCtrl_GetFirstChild 2036
+#define wxTreeCtrl_GetNextChild 2037
+#define wxTreeCtrl_GetFirstVisibleItem 2038
+#define wxTreeCtrl_GetImageList 2039
+#define wxTreeCtrl_GetIndent 2040
+#define wxTreeCtrl_GetItemBackgroundColour 2041
+#define wxTreeCtrl_GetItemData 2042
+#define wxTreeCtrl_GetItemFont 2043
+#define wxTreeCtrl_GetItemImage_1 2044
+#define wxTreeCtrl_GetItemImage_2 2045
+#define wxTreeCtrl_GetItemText 2046
+#define wxTreeCtrl_GetItemTextColour 2047
+#define wxTreeCtrl_GetLastChild 2048
+#define wxTreeCtrl_GetNextSibling 2049
+#define wxTreeCtrl_GetNextVisible 2050
+#define wxTreeCtrl_GetItemParent 2051
+#define wxTreeCtrl_GetPrevSibling 2052
+#define wxTreeCtrl_GetPrevVisible 2053
+#define wxTreeCtrl_GetRootItem 2054
+#define wxTreeCtrl_GetSelection 2055
+#define wxTreeCtrl_GetSelections 2056
+#define wxTreeCtrl_GetStateImageList 2057
+#define wxTreeCtrl_HitTest 2058
+#define wxTreeCtrl_InsertItem 2060
+#define wxTreeCtrl_IsBold 2061
+#define wxTreeCtrl_IsExpanded 2062
+#define wxTreeCtrl_IsSelected 2063
+#define wxTreeCtrl_IsVisible 2064
+#define wxTreeCtrl_ItemHasChildren 2065
+#define wxTreeCtrl_IsTreeItemIdOk 2066
+#define wxTreeCtrl_PrependItem 2067
+#define wxTreeCtrl_ScrollTo 2068
+#define wxTreeCtrl_SelectItem_1 2069
+#define wxTreeCtrl_SelectItem_2 2070
+#define wxTreeCtrl_SetIndent 2071
+#define wxTreeCtrl_SetImageList 2072
+#define wxTreeCtrl_SetItemBackgroundColour 2073
+#define wxTreeCtrl_SetItemBold 2074
+#define wxTreeCtrl_SetItemData 2075
+#define wxTreeCtrl_SetItemDropHighlight 2076
+#define wxTreeCtrl_SetItemFont 2077
+#define wxTreeCtrl_SetItemHasChildren 2078
+#define wxTreeCtrl_SetItemImage_2 2079
+#define wxTreeCtrl_SetItemImage_3 2080
+#define wxTreeCtrl_SetItemText 2081
+#define wxTreeCtrl_SetItemTextColour 2082
+#define wxTreeCtrl_SetStateImageList 2083
+#define wxTreeCtrl_SetWindowStyle 2084
+#define wxTreeCtrl_SortChildren 2085
+#define wxTreeCtrl_Toggle 2086
+#define wxTreeCtrl_ToggleItemSelection 2087
+#define wxTreeCtrl_Unselect 2088
+#define wxTreeCtrl_UnselectAll 2089
+#define wxTreeCtrl_UnselectItem 2090
+#define wxScrollBar_new_0 2091
+#define wxScrollBar_new_3 2092
+#define wxScrollBar_destruct 2093
+#define wxScrollBar_Create 2094
+#define wxScrollBar_GetRange 2095
+#define wxScrollBar_GetPageSize 2096
+#define wxScrollBar_GetThumbPosition 2097
+#define wxScrollBar_GetThumbSize 2098
+#define wxScrollBar_SetThumbPosition 2099
+#define wxScrollBar_SetScrollbar 2100
+#define wxSpinButton_new_2 2102
+#define wxSpinButton_new_0 2103
+#define wxSpinButton_Create 2104
+#define wxSpinButton_GetMax 2105
+#define wxSpinButton_GetMin 2106
+#define wxSpinButton_GetValue 2107
+#define wxSpinButton_SetRange 2108
+#define wxSpinButton_SetValue 2109
+#define wxSpinButton_destroy 2110
+#define wxSpinCtrl_new_0 2111
+#define wxSpinCtrl_new_2 2112
+#define wxSpinCtrl_Create 2114
+#define wxSpinCtrl_SetValue_1_1 2117
+#define wxSpinCtrl_SetValue_1_0 2118
+#define wxSpinCtrl_GetValue 2120
+#define wxSpinCtrl_SetRange 2122
+#define wxSpinCtrl_SetSelection 2123
+#define wxSpinCtrl_GetMin 2125
+#define wxSpinCtrl_GetMax 2127
+#define wxSpinCtrl_destroy 2128
+#define wxStaticText_new_0 2129
+#define wxStaticText_new_4 2130
+#define wxStaticText_Create 2131
+#define wxStaticText_GetLabel 2132
+#define wxStaticText_SetLabel 2133
+#define wxStaticText_Wrap 2134
+#define wxStaticText_destroy 2135
+#define wxStaticBitmap_new_0 2136
+#define wxStaticBitmap_new_4 2137
+#define wxStaticBitmap_Create 2138
+#define wxStaticBitmap_GetBitmap 2139
+#define wxStaticBitmap_SetBitmap 2140
+#define wxStaticBitmap_destroy 2141
+#define wxRadioBox_new 2142
+#define wxRadioBox_destruct 2144
+#define wxRadioBox_Create 2145
+#define wxRadioBox_Enable_2 2146
+#define wxRadioBox_Enable_1 2147
+#define wxRadioBox_GetSelection 2148
+#define wxRadioBox_GetString 2149
+#define wxRadioBox_SetSelection 2150
+#define wxRadioBox_Show_2 2151
+#define wxRadioBox_Show_1 2152
+#define wxRadioBox_GetColumnCount 2153
+#define wxRadioBox_GetItemHelpText 2154
+#define wxRadioBox_GetItemToolTip 2155
+#define wxRadioBox_GetItemFromPoint 2157
+#define wxRadioBox_GetRowCount 2158
+#define wxRadioBox_IsItemEnabled 2159
+#define wxRadioBox_IsItemShown 2160
+#define wxRadioBox_SetItemHelpText 2161
+#define wxRadioBox_SetItemToolTip 2162
+#define wxRadioButton_new_0 2163
+#define wxRadioButton_new_4 2164
+#define wxRadioButton_Create 2165
+#define wxRadioButton_GetValue 2166
+#define wxRadioButton_SetValue 2167
+#define wxRadioButton_destroy 2168
+#define wxSlider_new_6 2170
+#define wxSlider_new_0 2171
+#define wxSlider_Create 2172
+#define wxSlider_GetLineSize 2173
+#define wxSlider_GetMax 2174
+#define wxSlider_GetMin 2175
+#define wxSlider_GetPageSize 2176
+#define wxSlider_GetThumbLength 2177
+#define wxSlider_GetValue 2178
+#define wxSlider_SetLineSize 2179
+#define wxSlider_SetPageSize 2180
+#define wxSlider_SetRange 2181
+#define wxSlider_SetThumbLength 2182
+#define wxSlider_SetValue 2183
+#define wxSlider_destroy 2184
+#define wxDialog_new_4 2186
+#define wxDialog_new_0 2187
+#define wxDialog_destruct 2189
+#define wxDialog_Create 2190
+#define wxDialog_CreateButtonSizer 2191
+#define wxDialog_CreateStdDialogButtonSizer 2192
+#define wxDialog_EndModal 2193
+#define wxDialog_GetAffirmativeId 2194
+#define wxDialog_GetReturnCode 2195
+#define wxDialog_IsModal 2196
+#define wxDialog_SetAffirmativeId 2197
+#define wxDialog_SetReturnCode 2198
+#define wxDialog_Show 2199
+#define wxDialog_ShowModal 2200
+#define wxColourDialog_new_0 2201
+#define wxColourDialog_new_2 2202
+#define wxColourDialog_destruct 2203
+#define wxColourDialog_Create 2204
+#define wxColourDialog_GetColourData 2205
+#define wxColourData_new_0 2206
+#define wxColourData_new_1 2207
+#define wxColourData_destruct 2208
+#define wxColourData_GetChooseFull 2209
+#define wxColourData_GetColour 2210
+#define wxColourData_GetCustomColour 2212
+#define wxColourData_SetChooseFull 2213
+#define wxColourData_SetColour 2214
+#define wxColourData_SetCustomColour 2215
+#define wxPalette_new_0 2216
+#define wxPalette_new_4 2217
+#define wxPalette_destruct 2219
+#define wxPalette_Create 2220
+#define wxPalette_GetColoursCount 2221
+#define wxPalette_GetPixel 2222
+#define wxPalette_GetRGB 2223
+#define wxPalette_IsOk 2224
+#define wxDirDialog_new 2228
+#define wxDirDialog_destruct 2229
+#define wxDirDialog_GetPath 2230
+#define wxDirDialog_GetMessage 2231
+#define wxDirDialog_SetMessage 2232
+#define wxDirDialog_SetPath 2233
+#define wxFileDialog_new 2237
+#define wxFileDialog_destruct 2238
+#define wxFileDialog_GetDirectory 2239
+#define wxFileDialog_GetFilename 2240
+#define wxFileDialog_GetFilenames 2241
+#define wxFileDialog_GetFilterIndex 2242
+#define wxFileDialog_GetMessage 2243
+#define wxFileDialog_GetPath 2244
+#define wxFileDialog_GetPaths 2245
+#define wxFileDialog_GetWildcard 2246
+#define wxFileDialog_SetDirectory 2247
+#define wxFileDialog_SetFilename 2248
+#define wxFileDialog_SetFilterIndex 2249
+#define wxFileDialog_SetMessage 2250
+#define wxFileDialog_SetPath 2251
+#define wxFileDialog_SetWildcard 2252
+#define wxPickerBase_SetInternalMargin 2253
+#define wxPickerBase_GetInternalMargin 2254
+#define wxPickerBase_SetTextCtrlProportion 2255
+#define wxPickerBase_SetPickerCtrlProportion 2256
+#define wxPickerBase_GetTextCtrlProportion 2257
+#define wxPickerBase_GetPickerCtrlProportion 2258
+#define wxPickerBase_HasTextCtrl 2259
+#define wxPickerBase_GetTextCtrl 2260
+#define wxPickerBase_IsTextCtrlGrowable 2261
+#define wxPickerBase_SetPickerCtrlGrowable 2262
+#define wxPickerBase_SetTextCtrlGrowable 2263
+#define wxPickerBase_IsPickerCtrlGrowable 2264
+#define wxFilePickerCtrl_new_0 2265
+#define wxFilePickerCtrl_new_3 2266
+#define wxFilePickerCtrl_Create 2267
+#define wxFilePickerCtrl_GetPath 2268
+#define wxFilePickerCtrl_SetPath 2269
+#define wxFilePickerCtrl_destroy 2270
+#define wxDirPickerCtrl_new_0 2271
+#define wxDirPickerCtrl_new_3 2272
+#define wxDirPickerCtrl_Create 2273
+#define wxDirPickerCtrl_GetPath 2274
+#define wxDirPickerCtrl_SetPath 2275
+#define wxDirPickerCtrl_destroy 2276
+#define wxColourPickerCtrl_new_0 2277
+#define wxColourPickerCtrl_new_3 2278
+#define wxColourPickerCtrl_Create 2279
+#define wxColourPickerCtrl_GetColour 2280
+#define wxColourPickerCtrl_SetColour_1_1 2281
+#define wxColourPickerCtrl_SetColour_1_0 2282
+#define wxColourPickerCtrl_destroy 2283
+#define wxDatePickerCtrl_new_0 2284
+#define wxDatePickerCtrl_new_3 2285
+#define wxDatePickerCtrl_GetRange 2286
+#define wxDatePickerCtrl_GetValue 2287
+#define wxDatePickerCtrl_SetRange 2288
+#define wxDatePickerCtrl_SetValue 2289
+#define wxDatePickerCtrl_destroy 2290
+#define wxFontPickerCtrl_new_0 2291
+#define wxFontPickerCtrl_new_3 2292
+#define wxFontPickerCtrl_Create 2293
+#define wxFontPickerCtrl_GetSelectedFont 2294
+#define wxFontPickerCtrl_SetSelectedFont 2295
+#define wxFontPickerCtrl_GetMaxPointSize 2296
+#define wxFontPickerCtrl_SetMaxPointSize 2297
+#define wxFontPickerCtrl_destroy 2298
+#define wxFindReplaceDialog_new_0 2301
+#define wxFindReplaceDialog_new_4 2302
+#define wxFindReplaceDialog_destruct 2303
+#define wxFindReplaceDialog_Create 2304
+#define wxFindReplaceDialog_GetData 2305
+#define wxFindReplaceData_new_0 2306
+#define wxFindReplaceData_new_1 2307
+#define wxFindReplaceData_GetFindString 2308
+#define wxFindReplaceData_GetReplaceString 2309
+#define wxFindReplaceData_GetFlags 2310
+#define wxFindReplaceData_SetFlags 2311
+#define wxFindReplaceData_SetFindString 2312
+#define wxFindReplaceData_SetReplaceString 2313
+#define wxFindReplaceData_destroy 2314
+#define wxMultiChoiceDialog_new_0 2315
+#define wxMultiChoiceDialog_new_5 2317
+#define wxMultiChoiceDialog_GetSelections 2318
+#define wxMultiChoiceDialog_SetSelections 2319
+#define wxMultiChoiceDialog_destroy 2320
+#define wxSingleChoiceDialog_new_0 2321
+#define wxSingleChoiceDialog_new_5 2323
+#define wxSingleChoiceDialog_GetSelection 2324
+#define wxSingleChoiceDialog_GetStringSelection 2325
+#define wxSingleChoiceDialog_SetSelection 2326
+#define wxSingleChoiceDialog_destroy 2327
+#define wxTextEntryDialog_new 2328
+#define wxTextEntryDialog_GetValue 2329
+#define wxTextEntryDialog_SetValue 2330
+#define wxTextEntryDialog_destroy 2331
+#define wxPasswordEntryDialog_new 2332
+#define wxPasswordEntryDialog_destroy 2333
+#define wxFontData_new_0 2334
+#define wxFontData_new_1 2335
+#define wxFontData_destruct 2336
+#define wxFontData_EnableEffects 2337
+#define wxFontData_GetAllowSymbols 2338
+#define wxFontData_GetColour 2339
+#define wxFontData_GetChosenFont 2340
+#define wxFontData_GetEnableEffects 2341
+#define wxFontData_GetInitialFont 2342
+#define wxFontData_GetShowHelp 2343
+#define wxFontData_SetAllowSymbols 2344
+#define wxFontData_SetChosenFont 2345
+#define wxFontData_SetColour 2346
+#define wxFontData_SetInitialFont 2347
+#define wxFontData_SetRange 2348
+#define wxFontData_SetShowHelp 2349
+#define wxFontDialog_new_0 2353
+#define wxFontDialog_new_2 2355
+#define wxFontDialog_Create 2357
+#define wxFontDialog_GetFontData 2358
+#define wxFontDialog_destroy 2360
+#define wxProgressDialog_new 2361
+#define wxProgressDialog_destruct 2362
+#define wxProgressDialog_Resume 2363
+#define wxProgressDialog_Update_2 2364
+#define wxProgressDialog_Update_0 2365
+#define wxMessageDialog_new 2366
+#define wxMessageDialog_destruct 2367
+#define wxPageSetupDialog_new 2368
+#define wxPageSetupDialog_destruct 2369
+#define wxPageSetupDialog_GetPageSetupData 2370
+#define wxPageSetupDialog_ShowModal 2371
+#define wxPageSetupDialogData_new_0 2372
+#define wxPageSetupDialogData_new_1_0 2373
+#define wxPageSetupDialogData_new_1_1 2374
+#define wxPageSetupDialogData_destruct 2375
+#define wxPageSetupDialogData_EnableHelp 2376
+#define wxPageSetupDialogData_EnableMargins 2377
+#define wxPageSetupDialogData_EnableOrientation 2378
+#define wxPageSetupDialogData_EnablePaper 2379
+#define wxPageSetupDialogData_EnablePrinter 2380
+#define wxPageSetupDialogData_GetDefaultMinMargins 2381
+#define wxPageSetupDialogData_GetEnableMargins 2382
+#define wxPageSetupDialogData_GetEnableOrientation 2383
+#define wxPageSetupDialogData_GetEnablePaper 2384
+#define wxPageSetupDialogData_GetEnablePrinter 2385
+#define wxPageSetupDialogData_GetEnableHelp 2386
+#define wxPageSetupDialogData_GetDefaultInfo 2387
+#define wxPageSetupDialogData_GetMarginTopLeft 2388
+#define wxPageSetupDialogData_GetMarginBottomRight 2389
+#define wxPageSetupDialogData_GetMinMarginTopLeft 2390
+#define wxPageSetupDialogData_GetMinMarginBottomRight 2391
+#define wxPageSetupDialogData_GetPaperId 2392
+#define wxPageSetupDialogData_GetPaperSize 2393
+#define wxPageSetupDialogData_GetPrintData 2395
+#define wxPageSetupDialogData_IsOk 2396
+#define wxPageSetupDialogData_SetDefaultInfo 2397
+#define wxPageSetupDialogData_SetDefaultMinMargins 2398
+#define wxPageSetupDialogData_SetMarginTopLeft 2399
+#define wxPageSetupDialogData_SetMarginBottomRight 2400
+#define wxPageSetupDialogData_SetMinMarginTopLeft 2401
+#define wxPageSetupDialogData_SetMinMarginBottomRight 2402
+#define wxPageSetupDialogData_SetPaperId 2403
+#define wxPageSetupDialogData_SetPaperSize_1_1 2404
+#define wxPageSetupDialogData_SetPaperSize_1_0 2405
+#define wxPageSetupDialogData_SetPrintData 2406
+#define wxPrintDialog_new_2_0 2407
+#define wxPrintDialog_new_2_1 2408
+#define wxPrintDialog_destruct 2409
+#define wxPrintDialog_GetPrintDialogData 2410
+#define wxPrintDialog_GetPrintDC 2411
+#define wxPrintDialogData_new_0 2412
+#define wxPrintDialogData_new_1_1 2413
+#define wxPrintDialogData_new_1_0 2414
+#define wxPrintDialogData_destruct 2415
+#define wxPrintDialogData_EnableHelp 2416
+#define wxPrintDialogData_EnablePageNumbers 2417
+#define wxPrintDialogData_EnablePrintToFile 2418
+#define wxPrintDialogData_EnableSelection 2419
+#define wxPrintDialogData_GetAllPages 2420
+#define wxPrintDialogData_GetCollate 2421
+#define wxPrintDialogData_GetFromPage 2422
+#define wxPrintDialogData_GetMaxPage 2423
+#define wxPrintDialogData_GetMinPage 2424
+#define wxPrintDialogData_GetNoCopies 2425
+#define wxPrintDialogData_GetPrintData 2426
+#define wxPrintDialogData_GetPrintToFile 2427
+#define wxPrintDialogData_GetSelection 2428
+#define wxPrintDialogData_GetToPage 2429
+#define wxPrintDialogData_IsOk 2430
+#define wxPrintDialogData_SetCollate 2431
+#define wxPrintDialogData_SetFromPage 2432
+#define wxPrintDialogData_SetMaxPage 2433
+#define wxPrintDialogData_SetMinPage 2434
+#define wxPrintDialogData_SetNoCopies 2435
+#define wxPrintDialogData_SetPrintData 2436
+#define wxPrintDialogData_SetPrintToFile 2437
+#define wxPrintDialogData_SetSelection 2438
+#define wxPrintDialogData_SetToPage 2439
+#define wxPrintData_new_0 2440
+#define wxPrintData_new_1 2441
+#define wxPrintData_destruct 2442
+#define wxPrintData_GetCollate 2443
+#define wxPrintData_GetBin 2444
+#define wxPrintData_GetColour 2445
+#define wxPrintData_GetDuplex 2446
+#define wxPrintData_GetNoCopies 2447
+#define wxPrintData_GetOrientation 2448
+#define wxPrintData_GetPaperId 2449
+#define wxPrintData_GetPrinterName 2450
+#define wxPrintData_GetQuality 2451
+#define wxPrintData_IsOk 2452
+#define wxPrintData_SetBin 2453
+#define wxPrintData_SetCollate 2454
+#define wxPrintData_SetColour 2455
+#define wxPrintData_SetDuplex 2456
+#define wxPrintData_SetNoCopies 2457
+#define wxPrintData_SetOrientation 2458
+#define wxPrintData_SetPaperId 2459
+#define wxPrintData_SetPrinterName 2460
+#define wxPrintData_SetQuality 2461
+#define wxPrintPreview_new_2 2464
+#define wxPrintPreview_new_3 2465
+#define wxPrintPreview_destruct 2467
+#define wxPrintPreview_GetCanvas 2468
+#define wxPrintPreview_GetCurrentPage 2469
+#define wxPrintPreview_GetFrame 2470
+#define wxPrintPreview_GetMaxPage 2471
+#define wxPrintPreview_GetMinPage 2472
+#define wxPrintPreview_GetPrintout 2473
+#define wxPrintPreview_GetPrintoutForPrinting 2474
+#define wxPrintPreview_IsOk 2475
+#define wxPrintPreview_PaintPage 2476
+#define wxPrintPreview_Print 2477
+#define wxPrintPreview_RenderPage 2478
+#define wxPrintPreview_SetCanvas 2479
+#define wxPrintPreview_SetCurrentPage 2480
+#define wxPrintPreview_SetFrame 2481
+#define wxPrintPreview_SetPrintout 2482
+#define wxPrintPreview_SetZoom 2483
+#define wxPreviewFrame_new 2484
+#define wxPreviewFrame_destruct 2485
+#define wxPreviewFrame_CreateControlBar 2486
+#define wxPreviewFrame_CreateCanvas 2487
+#define wxPreviewFrame_Initialize 2488
+#define wxPreviewFrame_OnCloseWindow 2489
+#define wxPreviewControlBar_new 2490
+#define wxPreviewControlBar_destruct 2491
+#define wxPreviewControlBar_CreateButtons 2492
+#define wxPreviewControlBar_GetPrintPreview 2493
+#define wxPreviewControlBar_GetZoomControl 2494
+#define wxPreviewControlBar_SetZoomControl 2495
+#define wxPrinter_new 2497
+#define wxPrinter_CreateAbortWindow 2498
+#define wxPrinter_GetAbort 2499
+#define wxPrinter_GetLastError 2500
+#define wxPrinter_GetPrintDialogData 2501
+#define wxPrinter_Print 2502
+#define wxPrinter_PrintDialog 2503
+#define wxPrinter_ReportError 2504
+#define wxPrinter_Setup 2505
+#define wxPrinter_destroy 2506
+#define wxXmlResource_new_1 2507
+#define wxXmlResource_new_2 2508
+#define wxXmlResource_destruct 2509
+#define wxXmlResource_AttachUnknownControl 2510
+#define wxXmlResource_ClearHandlers 2511
+#define wxXmlResource_CompareVersion 2512
+#define wxXmlResource_Get 2513
+#define wxXmlResource_GetFlags 2514
+#define wxXmlResource_GetVersion 2515
+#define wxXmlResource_GetXRCID 2516
+#define wxXmlResource_InitAllHandlers 2517
+#define wxXmlResource_Load 2518
+#define wxXmlResource_LoadBitmap 2519
+#define wxXmlResource_LoadDialog_2 2520
+#define wxXmlResource_LoadDialog_3 2521
+#define wxXmlResource_LoadFrame_2 2522
+#define wxXmlResource_LoadFrame_3 2523
+#define wxXmlResource_LoadIcon 2524
+#define wxXmlResource_LoadMenu 2525
+#define wxXmlResource_LoadMenuBar_2 2526
+#define wxXmlResource_LoadMenuBar_1 2527
+#define wxXmlResource_LoadPanel_2 2528
+#define wxXmlResource_LoadPanel_3 2529
+#define wxXmlResource_LoadToolBar 2530
+#define wxXmlResource_Set 2531
+#define wxXmlResource_SetFlags 2532
+#define wxXmlResource_Unload 2533
+#define wxXmlResource_xrcctrl 2534
+#define wxHtmlEasyPrinting_new 2535
+#define wxHtmlEasyPrinting_destruct 2536
+#define wxHtmlEasyPrinting_GetPrintData 2537
+#define wxHtmlEasyPrinting_GetPageSetupData 2538
+#define wxHtmlEasyPrinting_PreviewFile 2539
+#define wxHtmlEasyPrinting_PreviewText 2540
+#define wxHtmlEasyPrinting_PrintFile 2541
+#define wxHtmlEasyPrinting_PrintText 2542
+#define wxHtmlEasyPrinting_PageSetup 2543
+#define wxHtmlEasyPrinting_SetFonts 2544
+#define wxHtmlEasyPrinting_SetHeader 2545
+#define wxHtmlEasyPrinting_SetFooter 2546
+#define wxGLCanvas_new_2 2548
+#define wxGLCanvas_new_3_1 2549
+#define wxGLCanvas_new_3_0 2550
+#define wxGLCanvas_GetContext 2551
+#define wxGLCanvas_SetCurrent 2553
+#define wxGLCanvas_SwapBuffers 2554
+#define wxGLCanvas_destroy 2555
+#define wxAuiManager_new 2556
+#define wxAuiManager_destruct 2557
+#define wxAuiManager_AddPane_2_1 2558
+#define wxAuiManager_AddPane_3 2559
+#define wxAuiManager_AddPane_2_0 2560
+#define wxAuiManager_DetachPane 2561
+#define wxAuiManager_GetAllPanes 2562
+#define wxAuiManager_GetArtProvider 2563
+#define wxAuiManager_GetDockSizeConstraint 2564
+#define wxAuiManager_GetFlags 2565
+#define wxAuiManager_GetManagedWindow 2566
+#define wxAuiManager_GetManager 2567
+#define wxAuiManager_GetPane_1_1 2568
+#define wxAuiManager_GetPane_1_0 2569
+#define wxAuiManager_HideHint 2570
+#define wxAuiManager_InsertPane 2571
+#define wxAuiManager_LoadPaneInfo 2572
+#define wxAuiManager_LoadPerspective 2573
+#define wxAuiManager_SavePaneInfo 2574
+#define wxAuiManager_SavePerspective 2575
+#define wxAuiManager_SetArtProvider 2576
+#define wxAuiManager_SetDockSizeConstraint 2577
+#define wxAuiManager_SetFlags 2578
+#define wxAuiManager_SetManagedWindow 2579
+#define wxAuiManager_ShowHint 2580
+#define wxAuiManager_UnInit 2581
+#define wxAuiManager_Update 2582
+#define wxAuiPaneInfo_new_0 2583
+#define wxAuiPaneInfo_new_1 2584
+#define wxAuiPaneInfo_destruct 2585
+#define wxAuiPaneInfo_BestSize_1 2586
+#define wxAuiPaneInfo_BestSize_2 2587
+#define wxAuiPaneInfo_Bottom 2588
+#define wxAuiPaneInfo_BottomDockable 2589
+#define wxAuiPaneInfo_Caption 2590
+#define wxAuiPaneInfo_CaptionVisible 2591
+#define wxAuiPaneInfo_Centre 2592
+#define wxAuiPaneInfo_CentrePane 2593
+#define wxAuiPaneInfo_CloseButton 2594
+#define wxAuiPaneInfo_DefaultPane 2595
+#define wxAuiPaneInfo_DestroyOnClose 2596
+#define wxAuiPaneInfo_Direction 2597
+#define wxAuiPaneInfo_Dock 2598
+#define wxAuiPaneInfo_Dockable 2599
+#define wxAuiPaneInfo_Fixed 2600
+#define wxAuiPaneInfo_Float 2601
+#define wxAuiPaneInfo_Floatable 2602
+#define wxAuiPaneInfo_FloatingPosition_1 2603
+#define wxAuiPaneInfo_FloatingPosition_2 2604
+#define wxAuiPaneInfo_FloatingSize_1 2605
+#define wxAuiPaneInfo_FloatingSize_2 2606
+#define wxAuiPaneInfo_Gripper 2607
+#define wxAuiPaneInfo_GripperTop 2608
+#define wxAuiPaneInfo_HasBorder 2609
+#define wxAuiPaneInfo_HasCaption 2610
+#define wxAuiPaneInfo_HasCloseButton 2611
+#define wxAuiPaneInfo_HasFlag 2612
+#define wxAuiPaneInfo_HasGripper 2613
+#define wxAuiPaneInfo_HasGripperTop 2614
+#define wxAuiPaneInfo_HasMaximizeButton 2615
+#define wxAuiPaneInfo_HasMinimizeButton 2616
+#define wxAuiPaneInfo_HasPinButton 2617
+#define wxAuiPaneInfo_Hide 2618
+#define wxAuiPaneInfo_IsBottomDockable 2619
+#define wxAuiPaneInfo_IsDocked 2620
+#define wxAuiPaneInfo_IsFixed 2621
+#define wxAuiPaneInfo_IsFloatable 2622
+#define wxAuiPaneInfo_IsFloating 2623
+#define wxAuiPaneInfo_IsLeftDockable 2624
+#define wxAuiPaneInfo_IsMovable 2625
+#define wxAuiPaneInfo_IsOk 2626
+#define wxAuiPaneInfo_IsResizable 2627
+#define wxAuiPaneInfo_IsRightDockable 2628
+#define wxAuiPaneInfo_IsShown 2629
+#define wxAuiPaneInfo_IsToolbar 2630
+#define wxAuiPaneInfo_IsTopDockable 2631
+#define wxAuiPaneInfo_Layer 2632
+#define wxAuiPaneInfo_Left 2633
+#define wxAuiPaneInfo_LeftDockable 2634
+#define wxAuiPaneInfo_MaxSize_1 2635
+#define wxAuiPaneInfo_MaxSize_2 2636
+#define wxAuiPaneInfo_MaximizeButton 2637
+#define wxAuiPaneInfo_MinSize_1 2638
+#define wxAuiPaneInfo_MinSize_2 2639
+#define wxAuiPaneInfo_MinimizeButton 2640
+#define wxAuiPaneInfo_Movable 2641
+#define wxAuiPaneInfo_Name 2642
+#define wxAuiPaneInfo_PaneBorder 2643
+#define wxAuiPaneInfo_PinButton 2644
+#define wxAuiPaneInfo_Position 2645
+#define wxAuiPaneInfo_Resizable 2646
+#define wxAuiPaneInfo_Right 2647
+#define wxAuiPaneInfo_RightDockable 2648
+#define wxAuiPaneInfo_Row 2649
+#define wxAuiPaneInfo_SafeSet 2650
+#define wxAuiPaneInfo_SetFlag 2651
+#define wxAuiPaneInfo_Show 2652
+#define wxAuiPaneInfo_ToolbarPane 2653
+#define wxAuiPaneInfo_Top 2654
+#define wxAuiPaneInfo_TopDockable 2655
+#define wxAuiPaneInfo_Window 2656
+#define wxAuiPaneInfo_GetWindow 2657
+#define wxAuiPaneInfo_GetFrame 2658
+#define wxAuiPaneInfo_GetDirection 2659
+#define wxAuiPaneInfo_GetLayer 2660
+#define wxAuiPaneInfo_GetRow 2661
+#define wxAuiPaneInfo_GetPosition 2662
+#define wxAuiPaneInfo_GetFloatingPosition 2663
+#define wxAuiPaneInfo_GetFloatingSize 2664
+#define wxAuiNotebook_new_0 2665
+#define wxAuiNotebook_new_2 2666
+#define wxAuiNotebook_AddPage 2667
+#define wxAuiNotebook_Create 2668
+#define wxAuiNotebook_DeletePage 2669
+#define wxAuiNotebook_GetArtProvider 2670
+#define wxAuiNotebook_GetPage 2671
+#define wxAuiNotebook_GetPageBitmap 2672
+#define wxAuiNotebook_GetPageCount 2673
+#define wxAuiNotebook_GetPageIndex 2674
+#define wxAuiNotebook_GetPageText 2675
+#define wxAuiNotebook_GetSelection 2676
+#define wxAuiNotebook_InsertPage 2677
+#define wxAuiNotebook_RemovePage 2678
+#define wxAuiNotebook_SetArtProvider 2679
+#define wxAuiNotebook_SetFont 2680
+#define wxAuiNotebook_SetPageBitmap 2681
+#define wxAuiNotebook_SetPageText 2682
+#define wxAuiNotebook_SetSelection 2683
+#define wxAuiNotebook_SetTabCtrlHeight 2684
+#define wxAuiNotebook_SetUniformBitmapSize 2685
+#define wxAuiNotebook_destroy 2686
+#define wxAuiTabArt_SetFlags 2687
+#define wxAuiTabArt_SetMeasuringFont 2688
+#define wxAuiTabArt_SetNormalFont 2689
+#define wxAuiTabArt_SetSelectedFont 2690
+#define wxAuiTabArt_SetColour 2691
+#define wxAuiTabArt_SetActiveColour 2692
+#define wxAuiDockArt_GetColour 2693
+#define wxAuiDockArt_GetFont 2694
+#define wxAuiDockArt_GetMetric 2695
+#define wxAuiDockArt_SetColour 2696
+#define wxAuiDockArt_SetFont 2697
+#define wxAuiDockArt_SetMetric 2698
+#define wxAuiSimpleTabArt_new 2699
+#define wxAuiSimpleTabArt_destroy 2700
+#define wxMDIParentFrame_new_0 2701
+#define wxMDIParentFrame_new_4 2702
+#define wxMDIParentFrame_destruct 2703
+#define wxMDIParentFrame_ActivateNext 2704
+#define wxMDIParentFrame_ActivatePrevious 2705
+#define wxMDIParentFrame_ArrangeIcons 2706
+#define wxMDIParentFrame_Cascade 2707
+#define wxMDIParentFrame_Create 2708
+#define wxMDIParentFrame_GetActiveChild 2709
+#define wxMDIParentFrame_GetClientWindow 2710
+#define wxMDIParentFrame_Tile 2711
+#define wxMDIChildFrame_new_0 2712
+#define wxMDIChildFrame_new_4 2713
+#define wxMDIChildFrame_destruct 2714
+#define wxMDIChildFrame_Activate 2715
+#define wxMDIChildFrame_Create 2716
+#define wxMDIChildFrame_Maximize 2717
+#define wxMDIChildFrame_Restore 2718
+#define wxMDIClientWindow_new_0 2719
+#define wxMDIClientWindow_new_2 2720
+#define wxMDIClientWindow_destruct 2721
+#define wxMDIClientWindow_CreateClient 2722
+#define wxLayoutAlgorithm_new 2723
+#define wxLayoutAlgorithm_LayoutFrame 2724
+#define wxLayoutAlgorithm_LayoutMDIFrame 2725
+#define wxLayoutAlgorithm_LayoutWindow 2726
+#define wxLayoutAlgorithm_destroy 2727
+#define wxEvent_GetId 2728
+#define wxEvent_GetSkipped 2729
+#define wxEvent_GetTimestamp 2730
+#define wxEvent_IsCommandEvent 2731
+#define wxEvent_ResumePropagation 2732
+#define wxEvent_ShouldPropagate 2733
+#define wxEvent_Skip 2734
+#define wxEvent_StopPropagation 2735
+#define wxCommandEvent_getClientData 2736
+#define wxCommandEvent_GetExtraLong 2737
+#define wxCommandEvent_GetInt 2738
+#define wxCommandEvent_GetSelection 2739
+#define wxCommandEvent_GetString 2740
+#define wxCommandEvent_IsChecked 2741
+#define wxCommandEvent_IsSelection 2742
+#define wxCommandEvent_SetInt 2743
+#define wxCommandEvent_SetString 2744
+#define wxScrollEvent_GetOrientation 2745
+#define wxScrollEvent_GetPosition 2746
+#define wxScrollWinEvent_GetOrientation 2747
+#define wxScrollWinEvent_GetPosition 2748
+#define wxMouseEvent_AltDown 2749
+#define wxMouseEvent_Button 2750
+#define wxMouseEvent_ButtonDClick 2751
+#define wxMouseEvent_ButtonDown 2752
+#define wxMouseEvent_ButtonUp 2753
+#define wxMouseEvent_CmdDown 2754
+#define wxMouseEvent_ControlDown 2755
+#define wxMouseEvent_Dragging 2756
+#define wxMouseEvent_Entering 2757
+#define wxMouseEvent_GetButton 2758
+#define wxMouseEvent_GetPosition 2761
+#define wxMouseEvent_GetLogicalPosition 2762
+#define wxMouseEvent_GetLinesPerAction 2763
+#define wxMouseEvent_GetWheelRotation 2764
+#define wxMouseEvent_GetWheelDelta 2765
+#define wxMouseEvent_GetX 2766
+#define wxMouseEvent_GetY 2767
+#define wxMouseEvent_IsButton 2768
+#define wxMouseEvent_IsPageScroll 2769
+#define wxMouseEvent_Leaving 2770
+#define wxMouseEvent_LeftDClick 2771
+#define wxMouseEvent_LeftDown 2772
+#define wxMouseEvent_LeftIsDown 2773
+#define wxMouseEvent_LeftUp 2774
+#define wxMouseEvent_MetaDown 2775
+#define wxMouseEvent_MiddleDClick 2776
+#define wxMouseEvent_MiddleDown 2777
+#define wxMouseEvent_MiddleIsDown 2778
+#define wxMouseEvent_MiddleUp 2779
+#define wxMouseEvent_Moving 2780
+#define wxMouseEvent_RightDClick 2781
+#define wxMouseEvent_RightDown 2782
+#define wxMouseEvent_RightIsDown 2783
+#define wxMouseEvent_RightUp 2784
+#define wxMouseEvent_ShiftDown 2785
+#define wxSetCursorEvent_GetCursor 2786
+#define wxSetCursorEvent_GetX 2787
+#define wxSetCursorEvent_GetY 2788
+#define wxSetCursorEvent_HasCursor 2789
+#define wxSetCursorEvent_SetCursor 2790
+#define wxKeyEvent_AltDown 2791
+#define wxKeyEvent_CmdDown 2792
+#define wxKeyEvent_ControlDown 2793
+#define wxKeyEvent_GetKeyCode 2794
+#define wxKeyEvent_GetModifiers 2795
+#define wxKeyEvent_GetPosition 2798
+#define wxKeyEvent_GetRawKeyCode 2799
+#define wxKeyEvent_GetRawKeyFlags 2800
+#define wxKeyEvent_GetUnicodeKey 2801
+#define wxKeyEvent_GetX 2802
+#define wxKeyEvent_GetY 2803
+#define wxKeyEvent_HasModifiers 2804
+#define wxKeyEvent_MetaDown 2805
+#define wxKeyEvent_ShiftDown 2806
+#define wxSizeEvent_GetSize 2807
+#define wxMoveEvent_GetPosition 2808
+#define wxEraseEvent_GetDC 2809
+#define wxFocusEvent_GetWindow 2810
+#define wxChildFocusEvent_GetWindow 2811
+#define wxMenuEvent_GetMenu 2812
+#define wxMenuEvent_GetMenuId 2813
+#define wxMenuEvent_IsPopup 2814
+#define wxCloseEvent_CanVeto 2815
+#define wxCloseEvent_GetLoggingOff 2816
+#define wxCloseEvent_SetCanVeto 2817
+#define wxCloseEvent_SetLoggingOff 2818
+#define wxCloseEvent_Veto 2819
+#define wxShowEvent_SetShow 2820
+#define wxShowEvent_GetShow 2821
+#define wxIconizeEvent_Iconized 2822
+#define wxJoystickEvent_ButtonDown 2823
+#define wxJoystickEvent_ButtonIsDown 2824
+#define wxJoystickEvent_ButtonUp 2825
+#define wxJoystickEvent_GetButtonChange 2826
+#define wxJoystickEvent_GetButtonState 2827
+#define wxJoystickEvent_GetJoystick 2828
+#define wxJoystickEvent_GetPosition 2829
+#define wxJoystickEvent_GetZPosition 2830
+#define wxJoystickEvent_IsButton 2831
+#define wxJoystickEvent_IsMove 2832
+#define wxJoystickEvent_IsZMove 2833
+#define wxUpdateUIEvent_CanUpdate 2834
+#define wxUpdateUIEvent_Check 2835
+#define wxUpdateUIEvent_Enable 2836
+#define wxUpdateUIEvent_Show 2837
+#define wxUpdateUIEvent_GetChecked 2838
+#define wxUpdateUIEvent_GetEnabled 2839
+#define wxUpdateUIEvent_GetShown 2840
+#define wxUpdateUIEvent_GetSetChecked 2841
+#define wxUpdateUIEvent_GetSetEnabled 2842
+#define wxUpdateUIEvent_GetSetShown 2843
+#define wxUpdateUIEvent_GetSetText 2844
+#define wxUpdateUIEvent_GetText 2845
+#define wxUpdateUIEvent_GetMode 2846
+#define wxUpdateUIEvent_GetUpdateInterval 2847
+#define wxUpdateUIEvent_ResetUpdateTime 2848
+#define wxUpdateUIEvent_SetMode 2849
+#define wxUpdateUIEvent_SetText 2850
+#define wxUpdateUIEvent_SetUpdateInterval 2851
+#define wxMouseCaptureChangedEvent_GetCapturedWindow 2852
+#define wxPaletteChangedEvent_SetChangedWindow 2853
+#define wxPaletteChangedEvent_GetChangedWindow 2854
+#define wxQueryNewPaletteEvent_SetPaletteRealized 2855
+#define wxQueryNewPaletteEvent_GetPaletteRealized 2856
+#define wxNavigationKeyEvent_GetDirection 2857
+#define wxNavigationKeyEvent_SetDirection 2858
+#define wxNavigationKeyEvent_IsWindowChange 2859
+#define wxNavigationKeyEvent_SetWindowChange 2860
+#define wxNavigationKeyEvent_IsFromTab 2861
+#define wxNavigationKeyEvent_SetFromTab 2862
+#define wxNavigationKeyEvent_GetCurrentFocus 2863
+#define wxNavigationKeyEvent_SetCurrentFocus 2864
+#define wxHelpEvent_GetOrigin 2865
+#define wxHelpEvent_GetPosition 2866
+#define wxHelpEvent_SetOrigin 2867
+#define wxHelpEvent_SetPosition 2868
+#define wxContextMenuEvent_GetPosition 2869
+#define wxContextMenuEvent_SetPosition 2870
+#define wxIdleEvent_CanSend 2871
+#define wxIdleEvent_GetMode 2872
+#define wxIdleEvent_RequestMore 2873
+#define wxIdleEvent_MoreRequested 2874
+#define wxIdleEvent_SetMode 2875
+#define wxGridEvent_AltDown 2876
+#define wxGridEvent_ControlDown 2877
+#define wxGridEvent_GetCol 2878
+#define wxGridEvent_GetPosition 2879
+#define wxGridEvent_GetRow 2880
+#define wxGridEvent_MetaDown 2881
+#define wxGridEvent_Selecting 2882
+#define wxGridEvent_ShiftDown 2883
+#define wxNotifyEvent_Allow 2884
+#define wxNotifyEvent_IsAllowed 2885
+#define wxNotifyEvent_Veto 2886
+#define wxSashEvent_GetEdge 2887
+#define wxSashEvent_GetDragRect 2888
+#define wxSashEvent_GetDragStatus 2889
+#define wxListEvent_GetCacheFrom 2890
+#define wxListEvent_GetCacheTo 2891
+#define wxListEvent_GetKeyCode 2892
+#define wxListEvent_GetIndex 2893
+#define wxListEvent_GetColumn 2894
+#define wxListEvent_GetPoint 2895
+#define wxListEvent_GetLabel 2896
+#define wxListEvent_GetText 2897
+#define wxListEvent_GetImage 2898
+#define wxListEvent_GetData 2899
+#define wxListEvent_GetMask 2900
+#define wxListEvent_GetItem 2901
+#define wxListEvent_IsEditCancelled 2902
+#define wxDateEvent_GetDate 2903
+#define wxCalendarEvent_GetWeekDay 2904
+#define wxFileDirPickerEvent_GetPath 2905
+#define wxColourPickerEvent_GetColour 2906
+#define wxFontPickerEvent_GetFont 2907
+#define wxStyledTextEvent_GetPosition 2908
+#define wxStyledTextEvent_GetKey 2909
+#define wxStyledTextEvent_GetModifiers 2910
+#define wxStyledTextEvent_GetModificationType 2911
+#define wxStyledTextEvent_GetText 2912
+#define wxStyledTextEvent_GetLength 2913
+#define wxStyledTextEvent_GetLinesAdded 2914
+#define wxStyledTextEvent_GetLine 2915
+#define wxStyledTextEvent_GetFoldLevelNow 2916
+#define wxStyledTextEvent_GetFoldLevelPrev 2917
+#define wxStyledTextEvent_GetMargin 2918
+#define wxStyledTextEvent_GetMessage 2919
+#define wxStyledTextEvent_GetWParam 2920
+#define wxStyledTextEvent_GetLParam 2921
+#define wxStyledTextEvent_GetListType 2922
+#define wxStyledTextEvent_GetX 2923
+#define wxStyledTextEvent_GetY 2924
+#define wxStyledTextEvent_GetDragText 2925
+#define wxStyledTextEvent_GetDragAllowMove 2926
+#define wxStyledTextEvent_GetDragResult 2927
+#define wxStyledTextEvent_GetShift 2928
+#define wxStyledTextEvent_GetControl 2929
+#define wxStyledTextEvent_GetAlt 2930
+#define utils_wxGetKeyState 2931
+#define utils_wxGetMousePosition 2932
+#define utils_wxGetMouseState 2933
+#define utils_wxSetDetectableAutoRepeat 2934
+#define utils_wxBell 2935
+#define utils_wxFindMenuItemId 2936
+#define utils_wxGenericFindWindowAtPoint 2937
+#define utils_wxFindWindowAtPoint 2938
+#define utils_wxBeginBusyCursor 2939
+#define utils_wxEndBusyCursor 2940
+#define utils_wxIsBusy 2941
+#define utils_wxShutdown 2942
+#define utils_wxShell 2943
+#define utils_wxLaunchDefaultBrowser 2944
+#define utils_wxGetEmailAddress 2945
+#define utils_wxGetUserId 2946
+#define utils_wxGetHomeDir 2947
+#define utils_wxNewId 2948
+#define utils_wxRegisterId 2949
+#define utils_wxGetCurrentId 2950
+#define utils_wxGetOsDescription 2951
+#define utils_wxIsPlatformLittleEndian 2952
+#define utils_wxIsPlatform64Bit 2953
+#define gdicmn_wxDisplaySize 2954
+#define gdicmn_wxSetCursor 2955
+#define wxPrintout_new 2956
+#define wxPrintout_destruct 2957
+#define wxPrintout_GetDC 2958
+#define wxPrintout_GetPageSizeMM 2959
+#define wxPrintout_GetPageSizePixels 2960
+#define wxPrintout_GetPaperRectPixels 2961
+#define wxPrintout_GetPPIPrinter 2962
+#define wxPrintout_GetPPIScreen 2963
+#define wxPrintout_GetTitle 2964
+#define wxPrintout_IsPreview 2965
+#define wxPrintout_FitThisSizeToPaper 2966
+#define wxPrintout_FitThisSizeToPage 2967
+#define wxPrintout_FitThisSizeToPageMargins 2968
+#define wxPrintout_MapScreenSizeToPaper 2969
+#define wxPrintout_MapScreenSizeToPage 2970
+#define wxPrintout_MapScreenSizeToPageMargins 2971
+#define wxPrintout_MapScreenSizeToDevice 2972
+#define wxPrintout_GetLogicalPaperRect 2973
+#define wxPrintout_GetLogicalPageRect 2974
+#define wxPrintout_GetLogicalPageMarginsRect 2975
+#define wxPrintout_SetLogicalOrigin 2976
+#define wxPrintout_OffsetLogicalOrigin 2977
+#define wxStyledTextCtrl_new_2 2978
+#define wxStyledTextCtrl_new_0 2979
+#define wxStyledTextCtrl_destruct 2980
+#define wxStyledTextCtrl_Create 2981
+#define wxStyledTextCtrl_AddText 2982
+#define wxStyledTextCtrl_AddStyledText 2983
+#define wxStyledTextCtrl_InsertText 2984
+#define wxStyledTextCtrl_ClearAll 2985
+#define wxStyledTextCtrl_ClearDocumentStyle 2986
+#define wxStyledTextCtrl_GetLength 2987
+#define wxStyledTextCtrl_GetCharAt 2988
+#define wxStyledTextCtrl_GetCurrentPos 2989
+#define wxStyledTextCtrl_GetAnchor 2990
+#define wxStyledTextCtrl_GetStyleAt 2991
+#define wxStyledTextCtrl_Redo 2992
+#define wxStyledTextCtrl_SetUndoCollection 2993
+#define wxStyledTextCtrl_SelectAll 2994
+#define wxStyledTextCtrl_SetSavePoint 2995
+#define wxStyledTextCtrl_GetStyledText 2996
+#define wxStyledTextCtrl_CanRedo 2997
+#define wxStyledTextCtrl_MarkerLineFromHandle 2998
+#define wxStyledTextCtrl_MarkerDeleteHandle 2999
+#define wxStyledTextCtrl_GetUndoCollection 3000
+#define wxStyledTextCtrl_GetViewWhiteSpace 3001
+#define wxStyledTextCtrl_SetViewWhiteSpace 3002
+#define wxStyledTextCtrl_PositionFromPoint 3003
+#define wxStyledTextCtrl_PositionFromPointClose 3004
+#define wxStyledTextCtrl_GotoLine 3005
+#define wxStyledTextCtrl_GotoPos 3006
+#define wxStyledTextCtrl_SetAnchor 3007
+#define wxStyledTextCtrl_GetCurLine 3008
+#define wxStyledTextCtrl_GetEndStyled 3009
+#define wxStyledTextCtrl_ConvertEOLs 3010
+#define wxStyledTextCtrl_GetEOLMode 3011
+#define wxStyledTextCtrl_SetEOLMode 3012
+#define wxStyledTextCtrl_StartStyling 3013
+#define wxStyledTextCtrl_SetStyling 3014
+#define wxStyledTextCtrl_GetBufferedDraw 3015
+#define wxStyledTextCtrl_SetBufferedDraw 3016
+#define wxStyledTextCtrl_SetTabWidth 3017
+#define wxStyledTextCtrl_GetTabWidth 3018
+#define wxStyledTextCtrl_SetCodePage 3019
+#define wxStyledTextCtrl_MarkerDefine 3020
+#define wxStyledTextCtrl_MarkerSetForeground 3021
+#define wxStyledTextCtrl_MarkerSetBackground 3022
+#define wxStyledTextCtrl_MarkerAdd 3023
+#define wxStyledTextCtrl_MarkerDelete 3024
+#define wxStyledTextCtrl_MarkerDeleteAll 3025
+#define wxStyledTextCtrl_MarkerGet 3026
+#define wxStyledTextCtrl_MarkerNext 3027
+#define wxStyledTextCtrl_MarkerPrevious 3028
+#define wxStyledTextCtrl_MarkerDefineBitmap 3029
+#define wxStyledTextCtrl_MarkerAddSet 3030
+#define wxStyledTextCtrl_MarkerSetAlpha 3031
+#define wxStyledTextCtrl_SetMarginType 3032
+#define wxStyledTextCtrl_GetMarginType 3033
+#define wxStyledTextCtrl_SetMarginWidth 3034
+#define wxStyledTextCtrl_GetMarginWidth 3035
+#define wxStyledTextCtrl_SetMarginMask 3036
+#define wxStyledTextCtrl_GetMarginMask 3037
+#define wxStyledTextCtrl_SetMarginSensitive 3038
+#define wxStyledTextCtrl_GetMarginSensitive 3039
+#define wxStyledTextCtrl_StyleClearAll 3040
+#define wxStyledTextCtrl_StyleSetForeground 3041
+#define wxStyledTextCtrl_StyleSetBackground 3042
+#define wxStyledTextCtrl_StyleSetBold 3043
+#define wxStyledTextCtrl_StyleSetItalic 3044
+#define wxStyledTextCtrl_StyleSetSize 3045
+#define wxStyledTextCtrl_StyleSetFaceName 3046
+#define wxStyledTextCtrl_StyleSetEOLFilled 3047
+#define wxStyledTextCtrl_StyleResetDefault 3048
+#define wxStyledTextCtrl_StyleSetUnderline 3049
+#define wxStyledTextCtrl_StyleSetCase 3050
+#define wxStyledTextCtrl_StyleSetHotSpot 3051
+#define wxStyledTextCtrl_SetSelForeground 3052
+#define wxStyledTextCtrl_SetSelBackground 3053
+#define wxStyledTextCtrl_GetSelAlpha 3054
+#define wxStyledTextCtrl_SetSelAlpha 3055
+#define wxStyledTextCtrl_SetCaretForeground 3056
+#define wxStyledTextCtrl_CmdKeyAssign 3057
+#define wxStyledTextCtrl_CmdKeyClear 3058
+#define wxStyledTextCtrl_CmdKeyClearAll 3059
+#define wxStyledTextCtrl_SetStyleBytes 3060
+#define wxStyledTextCtrl_StyleSetVisible 3061
+#define wxStyledTextCtrl_GetCaretPeriod 3062
+#define wxStyledTextCtrl_SetCaretPeriod 3063
+#define wxStyledTextCtrl_SetWordChars 3064
+#define wxStyledTextCtrl_BeginUndoAction 3065
+#define wxStyledTextCtrl_EndUndoAction 3066
+#define wxStyledTextCtrl_IndicatorSetStyle 3067
+#define wxStyledTextCtrl_IndicatorGetStyle 3068
+#define wxStyledTextCtrl_IndicatorSetForeground 3069
+#define wxStyledTextCtrl_IndicatorGetForeground 3070
+#define wxStyledTextCtrl_SetWhitespaceForeground 3071
+#define wxStyledTextCtrl_SetWhitespaceBackground 3072
+#define wxStyledTextCtrl_GetStyleBits 3073
+#define wxStyledTextCtrl_SetLineState 3074
+#define wxStyledTextCtrl_GetLineState 3075
+#define wxStyledTextCtrl_GetMaxLineState 3076
+#define wxStyledTextCtrl_GetCaretLineVisible 3077
+#define wxStyledTextCtrl_SetCaretLineVisible 3078
+#define wxStyledTextCtrl_GetCaretLineBackground 3079
+#define wxStyledTextCtrl_SetCaretLineBackground 3080
+#define wxStyledTextCtrl_AutoCompShow 3081
+#define wxStyledTextCtrl_AutoCompCancel 3082
+#define wxStyledTextCtrl_AutoCompActive 3083
+#define wxStyledTextCtrl_AutoCompPosStart 3084
+#define wxStyledTextCtrl_AutoCompComplete 3085
+#define wxStyledTextCtrl_AutoCompStops 3086
+#define wxStyledTextCtrl_AutoCompSetSeparator 3087
+#define wxStyledTextCtrl_AutoCompGetSeparator 3088
+#define wxStyledTextCtrl_AutoCompSelect 3089
+#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3090
+#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3091
+#define wxStyledTextCtrl_AutoCompSetFillUps 3092
+#define wxStyledTextCtrl_AutoCompSetChooseSingle 3093
+#define wxStyledTextCtrl_AutoCompGetChooseSingle 3094
+#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3095
+#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3096
+#define wxStyledTextCtrl_UserListShow 3097
+#define wxStyledTextCtrl_AutoCompSetAutoHide 3098
+#define wxStyledTextCtrl_AutoCompGetAutoHide 3099
+#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3100
+#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3101
+#define wxStyledTextCtrl_RegisterImage 3102
+#define wxStyledTextCtrl_ClearRegisteredImages 3103
+#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3104
+#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3105
+#define wxStyledTextCtrl_AutoCompSetMaxWidth 3106
+#define wxStyledTextCtrl_AutoCompGetMaxWidth 3107
+#define wxStyledTextCtrl_AutoCompSetMaxHeight 3108
+#define wxStyledTextCtrl_AutoCompGetMaxHeight 3109
+#define wxStyledTextCtrl_SetIndent 3110
+#define wxStyledTextCtrl_GetIndent 3111
+#define wxStyledTextCtrl_SetUseTabs 3112
+#define wxStyledTextCtrl_GetUseTabs 3113
+#define wxStyledTextCtrl_SetLineIndentation 3114
+#define wxStyledTextCtrl_GetLineIndentation 3115
+#define wxStyledTextCtrl_GetLineIndentPosition 3116
+#define wxStyledTextCtrl_GetColumn 3117
+#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3118
+#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3119
+#define wxStyledTextCtrl_SetIndentationGuides 3120
+#define wxStyledTextCtrl_GetIndentationGuides 3121
+#define wxStyledTextCtrl_SetHighlightGuide 3122
+#define wxStyledTextCtrl_GetHighlightGuide 3123
+#define wxStyledTextCtrl_GetLineEndPosition 3124
+#define wxStyledTextCtrl_GetCodePage 3125
+#define wxStyledTextCtrl_GetCaretForeground 3126
+#define wxStyledTextCtrl_GetReadOnly 3127
+#define wxStyledTextCtrl_SetCurrentPos 3128
+#define wxStyledTextCtrl_SetSelectionStart 3129
+#define wxStyledTextCtrl_GetSelectionStart 3130
+#define wxStyledTextCtrl_SetSelectionEnd 3131
+#define wxStyledTextCtrl_GetSelectionEnd 3132
+#define wxStyledTextCtrl_SetPrintMagnification 3133
+#define wxStyledTextCtrl_GetPrintMagnification 3134
+#define wxStyledTextCtrl_SetPrintColourMode 3135
+#define wxStyledTextCtrl_GetPrintColourMode 3136
+#define wxStyledTextCtrl_FindText 3137
+#define wxStyledTextCtrl_FormatRange 3138
+#define wxStyledTextCtrl_GetFirstVisibleLine 3139
+#define wxStyledTextCtrl_GetLine 3140
+#define wxStyledTextCtrl_GetLineCount 3141
+#define wxStyledTextCtrl_SetMarginLeft 3142
+#define wxStyledTextCtrl_GetMarginLeft 3143
+#define wxStyledTextCtrl_SetMarginRight 3144
+#define wxStyledTextCtrl_GetMarginRight 3145
+#define wxStyledTextCtrl_GetModify 3146
+#define wxStyledTextCtrl_SetSelection 3147
+#define wxStyledTextCtrl_GetSelectedText 3148
+#define wxStyledTextCtrl_GetTextRange 3149
+#define wxStyledTextCtrl_HideSelection 3150
+#define wxStyledTextCtrl_LineFromPosition 3151
+#define wxStyledTextCtrl_PositionFromLine 3152
+#define wxStyledTextCtrl_LineScroll 3153
+#define wxStyledTextCtrl_EnsureCaretVisible 3154
+#define wxStyledTextCtrl_ReplaceSelection 3155
+#define wxStyledTextCtrl_SetReadOnly 3156
+#define wxStyledTextCtrl_CanPaste 3157
+#define wxStyledTextCtrl_CanUndo 3158
+#define wxStyledTextCtrl_EmptyUndoBuffer 3159
+#define wxStyledTextCtrl_Undo 3160
+#define wxStyledTextCtrl_Cut 3161
+#define wxStyledTextCtrl_Copy 3162
+#define wxStyledTextCtrl_Paste 3163
+#define wxStyledTextCtrl_Clear 3164
+#define wxStyledTextCtrl_SetText 3165
+#define wxStyledTextCtrl_GetText 3166
+#define wxStyledTextCtrl_GetTextLength 3167
+#define wxStyledTextCtrl_GetOvertype 3168
+#define wxStyledTextCtrl_SetCaretWidth 3169
+#define wxStyledTextCtrl_GetCaretWidth 3170
+#define wxStyledTextCtrl_SetTargetStart 3171
+#define wxStyledTextCtrl_GetTargetStart 3172
+#define wxStyledTextCtrl_SetTargetEnd 3173
+#define wxStyledTextCtrl_GetTargetEnd 3174
+#define wxStyledTextCtrl_ReplaceTarget 3175
+#define wxStyledTextCtrl_SearchInTarget 3176
+#define wxStyledTextCtrl_SetSearchFlags 3177
+#define wxStyledTextCtrl_GetSearchFlags 3178
+#define wxStyledTextCtrl_CallTipShow 3179
+#define wxStyledTextCtrl_CallTipCancel 3180
+#define wxStyledTextCtrl_CallTipActive 3181
+#define wxStyledTextCtrl_CallTipPosAtStart 3182
+#define wxStyledTextCtrl_CallTipSetHighlight 3183
+#define wxStyledTextCtrl_CallTipSetBackground 3184
+#define wxStyledTextCtrl_CallTipSetForeground 3185
+#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3186
+#define wxStyledTextCtrl_CallTipUseStyle 3187
+#define wxStyledTextCtrl_VisibleFromDocLine 3188
+#define wxStyledTextCtrl_DocLineFromVisible 3189
+#define wxStyledTextCtrl_WrapCount 3190
+#define wxStyledTextCtrl_SetFoldLevel 3191
+#define wxStyledTextCtrl_GetFoldLevel 3192
+#define wxStyledTextCtrl_GetLastChild 3193
+#define wxStyledTextCtrl_GetFoldParent 3194
+#define wxStyledTextCtrl_ShowLines 3195
+#define wxStyledTextCtrl_HideLines 3196
+#define wxStyledTextCtrl_GetLineVisible 3197
+#define wxStyledTextCtrl_SetFoldExpanded 3198
+#define wxStyledTextCtrl_GetFoldExpanded 3199
+#define wxStyledTextCtrl_ToggleFold 3200
+#define wxStyledTextCtrl_EnsureVisible 3201
+#define wxStyledTextCtrl_SetFoldFlags 3202
+#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3203
+#define wxStyledTextCtrl_SetTabIndents 3204
+#define wxStyledTextCtrl_GetTabIndents 3205
+#define wxStyledTextCtrl_SetBackSpaceUnIndents 3206
+#define wxStyledTextCtrl_GetBackSpaceUnIndents 3207
+#define wxStyledTextCtrl_SetMouseDwellTime 3208
+#define wxStyledTextCtrl_GetMouseDwellTime 3209
+#define wxStyledTextCtrl_WordStartPosition 3210
+#define wxStyledTextCtrl_WordEndPosition 3211
+#define wxStyledTextCtrl_SetWrapMode 3212
+#define wxStyledTextCtrl_GetWrapMode 3213
+#define wxStyledTextCtrl_SetWrapVisualFlags 3214
+#define wxStyledTextCtrl_GetWrapVisualFlags 3215
+#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3216
+#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3217
+#define wxStyledTextCtrl_SetWrapStartIndent 3218
+#define wxStyledTextCtrl_GetWrapStartIndent 3219
+#define wxStyledTextCtrl_SetLayoutCache 3220
+#define wxStyledTextCtrl_GetLayoutCache 3221
+#define wxStyledTextCtrl_SetScrollWidth 3222
+#define wxStyledTextCtrl_GetScrollWidth 3223
+#define wxStyledTextCtrl_TextWidth 3224
+#define wxStyledTextCtrl_GetEndAtLastLine 3225
+#define wxStyledTextCtrl_TextHeight 3226
+#define wxStyledTextCtrl_SetUseVerticalScrollBar 3227
+#define wxStyledTextCtrl_GetUseVerticalScrollBar 3228
+#define wxStyledTextCtrl_AppendText 3229
+#define wxStyledTextCtrl_GetTwoPhaseDraw 3230
+#define wxStyledTextCtrl_SetTwoPhaseDraw 3231
+#define wxStyledTextCtrl_TargetFromSelection 3232
+#define wxStyledTextCtrl_LinesJoin 3233
+#define wxStyledTextCtrl_LinesSplit 3234
+#define wxStyledTextCtrl_SetFoldMarginColour 3235
+#define wxStyledTextCtrl_SetFoldMarginHiColour 3236
+#define wxStyledTextCtrl_LineDown 3237
+#define wxStyledTextCtrl_LineDownExtend 3238
+#define wxStyledTextCtrl_LineUp 3239
+#define wxStyledTextCtrl_LineUpExtend 3240
+#define wxStyledTextCtrl_CharLeft 3241
+#define wxStyledTextCtrl_CharLeftExtend 3242
+#define wxStyledTextCtrl_CharRight 3243
+#define wxStyledTextCtrl_CharRightExtend 3244
+#define wxStyledTextCtrl_WordLeft 3245
+#define wxStyledTextCtrl_WordLeftExtend 3246
+#define wxStyledTextCtrl_WordRight 3247
+#define wxStyledTextCtrl_WordRightExtend 3248
+#define wxStyledTextCtrl_Home 3249
+#define wxStyledTextCtrl_HomeExtend 3250
+#define wxStyledTextCtrl_LineEnd 3251
+#define wxStyledTextCtrl_LineEndExtend 3252
+#define wxStyledTextCtrl_DocumentStart 3253
+#define wxStyledTextCtrl_DocumentStartExtend 3254
+#define wxStyledTextCtrl_DocumentEnd 3255
+#define wxStyledTextCtrl_DocumentEndExtend 3256
+#define wxStyledTextCtrl_PageUp 3257
+#define wxStyledTextCtrl_PageUpExtend 3258
+#define wxStyledTextCtrl_PageDown 3259
+#define wxStyledTextCtrl_PageDownExtend 3260
+#define wxStyledTextCtrl_EditToggleOvertype 3261
+#define wxStyledTextCtrl_Cancel 3262
+#define wxStyledTextCtrl_DeleteBack 3263
+#define wxStyledTextCtrl_Tab 3264
+#define wxStyledTextCtrl_BackTab 3265
+#define wxStyledTextCtrl_NewLine 3266
+#define wxStyledTextCtrl_FormFeed 3267
+#define wxStyledTextCtrl_VCHome 3268
+#define wxStyledTextCtrl_VCHomeExtend 3269
+#define wxStyledTextCtrl_ZoomIn 3270
+#define wxStyledTextCtrl_ZoomOut 3271
+#define wxStyledTextCtrl_DelWordLeft 3272
+#define wxStyledTextCtrl_DelWordRight 3273
+#define wxStyledTextCtrl_LineCut 3274
+#define wxStyledTextCtrl_LineDelete 3275
+#define wxStyledTextCtrl_LineTranspose 3276
+#define wxStyledTextCtrl_LineDuplicate 3277
+#define wxStyledTextCtrl_LowerCase 3278
+#define wxStyledTextCtrl_UpperCase 3279
+#define wxStyledTextCtrl_LineScrollDown 3280
+#define wxStyledTextCtrl_LineScrollUp 3281
+#define wxStyledTextCtrl_DeleteBackNotLine 3282
+#define wxStyledTextCtrl_HomeDisplay 3283
+#define wxStyledTextCtrl_HomeDisplayExtend 3284
+#define wxStyledTextCtrl_LineEndDisplay 3285
+#define wxStyledTextCtrl_LineEndDisplayExtend 3286
+#define wxStyledTextCtrl_HomeWrapExtend 3287
+#define wxStyledTextCtrl_LineEndWrap 3288
+#define wxStyledTextCtrl_LineEndWrapExtend 3289
+#define wxStyledTextCtrl_VCHomeWrap 3290
+#define wxStyledTextCtrl_VCHomeWrapExtend 3291
+#define wxStyledTextCtrl_LineCopy 3292
+#define wxStyledTextCtrl_MoveCaretInsideView 3293
+#define wxStyledTextCtrl_LineLength 3294
+#define wxStyledTextCtrl_BraceHighlight 3295
+#define wxStyledTextCtrl_BraceBadLight 3296
+#define wxStyledTextCtrl_BraceMatch 3297
+#define wxStyledTextCtrl_GetViewEOL 3298
+#define wxStyledTextCtrl_SetViewEOL 3299
+#define wxStyledTextCtrl_SetModEventMask 3300
+#define wxStyledTextCtrl_GetEdgeColumn 3301
+#define wxStyledTextCtrl_SetEdgeColumn 3302
+#define wxStyledTextCtrl_SetEdgeMode 3303
+#define wxStyledTextCtrl_GetEdgeMode 3304
+#define wxStyledTextCtrl_GetEdgeColour 3305
+#define wxStyledTextCtrl_SetEdgeColour 3306
+#define wxStyledTextCtrl_SearchAnchor 3307
+#define wxStyledTextCtrl_SearchNext 3308
+#define wxStyledTextCtrl_SearchPrev 3309
+#define wxStyledTextCtrl_LinesOnScreen 3310
+#define wxStyledTextCtrl_UsePopUp 3311
+#define wxStyledTextCtrl_SelectionIsRectangle 3312
+#define wxStyledTextCtrl_SetZoom 3313
+#define wxStyledTextCtrl_GetZoom 3314
+#define wxStyledTextCtrl_GetModEventMask 3315
+#define wxStyledTextCtrl_SetSTCFocus 3316
+#define wxStyledTextCtrl_GetSTCFocus 3317
+#define wxStyledTextCtrl_SetStatus 3318
+#define wxStyledTextCtrl_GetStatus 3319
+#define wxStyledTextCtrl_SetMouseDownCaptures 3320
+#define wxStyledTextCtrl_GetMouseDownCaptures 3321
+#define wxStyledTextCtrl_SetSTCCursor 3322
+#define wxStyledTextCtrl_GetSTCCursor 3323
+#define wxStyledTextCtrl_SetControlCharSymbol 3324
+#define wxStyledTextCtrl_GetControlCharSymbol 3325
+#define wxStyledTextCtrl_WordPartLeft 3326
+#define wxStyledTextCtrl_WordPartLeftExtend 3327
+#define wxStyledTextCtrl_WordPartRight 3328
+#define wxStyledTextCtrl_WordPartRightExtend 3329
+#define wxStyledTextCtrl_SetVisiblePolicy 3330
+#define wxStyledTextCtrl_DelLineLeft 3331
+#define wxStyledTextCtrl_DelLineRight 3332
+#define wxStyledTextCtrl_GetXOffset 3333
+#define wxStyledTextCtrl_ChooseCaretX 3334
+#define wxStyledTextCtrl_SetXCaretPolicy 3335
+#define wxStyledTextCtrl_SetYCaretPolicy 3336
+#define wxStyledTextCtrl_GetPrintWrapMode 3337
+#define wxStyledTextCtrl_SetHotspotActiveForeground 3338
+#define wxStyledTextCtrl_SetHotspotActiveBackground 3339
+#define wxStyledTextCtrl_SetHotspotActiveUnderline 3340
+#define wxStyledTextCtrl_SetHotspotSingleLine 3341
+#define wxStyledTextCtrl_ParaDownExtend 3342
+#define wxStyledTextCtrl_ParaUp 3343
+#define wxStyledTextCtrl_ParaUpExtend 3344
+#define wxStyledTextCtrl_PositionBefore 3345
+#define wxStyledTextCtrl_PositionAfter 3346
+#define wxStyledTextCtrl_CopyRange 3347
+#define wxStyledTextCtrl_CopyText 3348
+#define wxStyledTextCtrl_SetSelectionMode 3349
+#define wxStyledTextCtrl_GetSelectionMode 3350
+#define wxStyledTextCtrl_LineDownRectExtend 3351
+#define wxStyledTextCtrl_LineUpRectExtend 3352
+#define wxStyledTextCtrl_CharLeftRectExtend 3353
+#define wxStyledTextCtrl_CharRightRectExtend 3354
+#define wxStyledTextCtrl_HomeRectExtend 3355
+#define wxStyledTextCtrl_VCHomeRectExtend 3356
+#define wxStyledTextCtrl_LineEndRectExtend 3357
+#define wxStyledTextCtrl_PageUpRectExtend 3358
+#define wxStyledTextCtrl_PageDownRectExtend 3359
+#define wxStyledTextCtrl_StutteredPageUp 3360
+#define wxStyledTextCtrl_StutteredPageUpExtend 3361
+#define wxStyledTextCtrl_StutteredPageDown 3362
+#define wxStyledTextCtrl_StutteredPageDownExtend 3363
+#define wxStyledTextCtrl_WordLeftEnd 3364
+#define wxStyledTextCtrl_WordLeftEndExtend 3365
+#define wxStyledTextCtrl_WordRightEnd 3366
+#define wxStyledTextCtrl_WordRightEndExtend 3367
+#define wxStyledTextCtrl_SetWhitespaceChars 3368
+#define wxStyledTextCtrl_SetCharsDefault 3369
+#define wxStyledTextCtrl_AutoCompGetCurrent 3370
+#define wxStyledTextCtrl_Allocate 3371
+#define wxStyledTextCtrl_FindColumn 3372
+#define wxStyledTextCtrl_GetCaretSticky 3373
+#define wxStyledTextCtrl_SetCaretSticky 3374
+#define wxStyledTextCtrl_ToggleCaretSticky 3375
+#define wxStyledTextCtrl_SetPasteConvertEndings 3376
+#define wxStyledTextCtrl_GetPasteConvertEndings 3377
+#define wxStyledTextCtrl_SelectionDuplicate 3378
+#define wxStyledTextCtrl_SetCaretLineBackAlpha 3379
+#define wxStyledTextCtrl_GetCaretLineBackAlpha 3380
+#define wxStyledTextCtrl_StartRecord 3381
+#define wxStyledTextCtrl_StopRecord 3382
+#define wxStyledTextCtrl_SetLexer 3383
+#define wxStyledTextCtrl_GetLexer 3384
+#define wxStyledTextCtrl_Colourise 3385
+#define wxStyledTextCtrl_SetProperty 3386
+#define wxStyledTextCtrl_SetKeyWords 3387
+#define wxStyledTextCtrl_SetLexerLanguage 3388
+#define wxStyledTextCtrl_GetProperty 3389
+#define wxStyledTextCtrl_GetStyleBitsNeeded 3390
+#define wxStyledTextCtrl_GetCurrentLine 3391
+#define wxStyledTextCtrl_StyleSetSpec 3392
+#define wxStyledTextCtrl_StyleSetFont 3393
+#define wxStyledTextCtrl_StyleSetFontAttr 3394
+#define wxStyledTextCtrl_StyleSetCharacterSet 3395
+#define wxStyledTextCtrl_StyleSetFontEncoding 3396
+#define wxStyledTextCtrl_CmdKeyExecute 3397
+#define wxStyledTextCtrl_SetMargins 3398
+#define wxStyledTextCtrl_GetSelection 3399
+#define wxStyledTextCtrl_PointFromPosition 3400
+#define wxStyledTextCtrl_ScrollToLine 3401
+#define wxStyledTextCtrl_ScrollToColumn 3402
+#define wxStyledTextCtrl_SetVScrollBar 3403
+#define wxStyledTextCtrl_SetHScrollBar 3404
+#define wxStyledTextCtrl_GetLastKeydownProcessed 3405
+#define wxStyledTextCtrl_SetLastKeydownProcessed 3406
+#define wxStyledTextCtrl_SaveFile 3407
+#define wxStyledTextCtrl_LoadFile 3408
+#define wxStyledTextCtrl_DoDragOver 3409
+#define wxStyledTextCtrl_DoDropText 3410
+#define wxStyledTextCtrl_GetUseAntiAliasing 3411
+#define wxStyledTextCtrl_AddTextRaw 3412
+#define wxStyledTextCtrl_InsertTextRaw 3413
+#define wxStyledTextCtrl_GetCurLineRaw 3414
+#define wxStyledTextCtrl_GetLineRaw 3415
+#define wxStyledTextCtrl_GetSelectedTextRaw 3416
+#define wxStyledTextCtrl_GetTextRangeRaw 3417
+#define wxStyledTextCtrl_SetTextRaw 3418
+#define wxStyledTextCtrl_GetTextRaw 3419
+#define wxStyledTextCtrl_AppendTextRaw 3420
+#define wxArtProvider_GetBitmap 3421
+#define wxArtProvider_GetIcon 3422
+#define wxTreeEvent_GetKeyCode 3423
+#define wxTreeEvent_GetItem 3424
+#define wxTreeEvent_GetKeyEvent 3425
+#define wxTreeEvent_GetLabel 3426
+#define wxTreeEvent_GetOldItem 3427
+#define wxTreeEvent_GetPoint 3428
+#define wxTreeEvent_IsEditCancelled 3429
+#define wxTreeEvent_SetToolTip 3430
+#define wxNotebookEvent_GetOldSelection 3431
+#define wxNotebookEvent_GetSelection 3432
+#define wxNotebookEvent_SetOldSelection 3433
+#define wxNotebookEvent_SetSelection 3434
+#define wxFileDataObject_new 3435
+#define wxFileDataObject_AddFile 3436
+#define wxFileDataObject_GetFilenames 3437
+#define wxFileDataObject_destroy 3438
+#define wxTextDataObject_new 3439
+#define wxTextDataObject_GetTextLength 3440
+#define wxTextDataObject_GetText 3441
+#define wxTextDataObject_SetText 3442
+#define wxTextDataObject_destroy 3443
+#define wxBitmapDataObject_new_1_1 3444
+#define wxBitmapDataObject_new_1_0 3445
+#define wxBitmapDataObject_GetBitmap 3446
+#define wxBitmapDataObject_SetBitmap 3447
+#define wxBitmapDataObject_destroy 3448
+#define wxClipboard_new 3450
+#define wxClipboard_destruct 3451
+#define wxClipboard_AddData 3452
+#define wxClipboard_Clear 3453
+#define wxClipboard_Close 3454
+#define wxClipboard_Flush 3455
+#define wxClipboard_GetData 3456
+#define wxClipboard_IsOpened 3457
+#define wxClipboard_Open 3458
+#define wxClipboard_SetData 3459
+#define wxClipboard_UsePrimarySelection 3461
+#define wxClipboard_IsSupported 3462
+#define wxClipboard_Get 3463
+#define wxSpinEvent_GetPosition 3464
+#define wxSpinEvent_SetPosition 3465
+#define wxSplitterWindow_new_0 3466
+#define wxSplitterWindow_new_2 3467
+#define wxSplitterWindow_destruct 3468
+#define wxSplitterWindow_Create 3469
+#define wxSplitterWindow_GetMinimumPaneSize 3470
+#define wxSplitterWindow_GetSashGravity 3471
+#define wxSplitterWindow_GetSashPosition 3472
+#define wxSplitterWindow_GetSplitMode 3473
+#define wxSplitterWindow_GetWindow1 3474
+#define wxSplitterWindow_GetWindow2 3475
+#define wxSplitterWindow_Initialize 3476
+#define wxSplitterWindow_IsSplit 3477
+#define wxSplitterWindow_ReplaceWindow 3478
+#define wxSplitterWindow_SetSashGravity 3479
+#define wxSplitterWindow_SetSashPosition 3480
+#define wxSplitterWindow_SetSashSize 3481
+#define wxSplitterWindow_SetMinimumPaneSize 3482
+#define wxSplitterWindow_SetSplitMode 3483
+#define wxSplitterWindow_SplitHorizontally 3484
+#define wxSplitterWindow_SplitVertically 3485
+#define wxSplitterWindow_Unsplit 3486
+#define wxSplitterWindow_UpdateSize 3487
+#define wxSplitterEvent_GetSashPosition 3488
+#define wxSplitterEvent_GetX 3489
+#define wxSplitterEvent_GetY 3490
+#define wxSplitterEvent_GetWindowBeingRemoved 3491
+#define wxSplitterEvent_SetSashPosition 3492
+#define wxHtmlWindow_new_0 3493
+#define wxHtmlWindow_new_2 3494
+#define wxHtmlWindow_AppendToPage 3495
+#define wxHtmlWindow_GetOpenedAnchor 3496
+#define wxHtmlWindow_GetOpenedPage 3497
+#define wxHtmlWindow_GetOpenedPageTitle 3498
+#define wxHtmlWindow_GetRelatedFrame 3499
+#define wxHtmlWindow_HistoryBack 3500
+#define wxHtmlWindow_HistoryCanBack 3501
+#define wxHtmlWindow_HistoryCanForward 3502
+#define wxHtmlWindow_HistoryClear 3503
+#define wxHtmlWindow_HistoryForward 3504
+#define wxHtmlWindow_LoadFile 3505
+#define wxHtmlWindow_LoadPage 3506
+#define wxHtmlWindow_SelectAll 3507
+#define wxHtmlWindow_SelectionToText 3508
+#define wxHtmlWindow_SelectLine 3509
+#define wxHtmlWindow_SelectWord 3510
+#define wxHtmlWindow_SetBorders 3511
+#define wxHtmlWindow_SetFonts 3512
+#define wxHtmlWindow_SetPage 3513
+#define wxHtmlWindow_SetRelatedFrame 3514
+#define wxHtmlWindow_SetRelatedStatusBar 3515
+#define wxHtmlWindow_ToText 3516
+#define wxHtmlWindow_destroy 3517
+#define wxHtmlLinkEvent_GetLinkInfo 3518
+#define wxSystemSettings_GetColour 3519
+#define wxSystemSettings_GetFont 3520
+#define wxSystemSettings_GetMetric 3521
+#define wxSystemSettings_GetScreenType 3522
+#define wxSystemOptions_GetOption 3523
+#define wxSystemOptions_GetOptionInt 3524
+#define wxSystemOptions_HasOption 3525
+#define wxSystemOptions_IsFalse 3526
+#define wxSystemOptions_SetOption_2_1 3527
+#define wxSystemOptions_SetOption_2_0 3528
+#define wxAuiNotebookEvent_SetSelection 3529
+#define wxAuiNotebookEvent_GetSelection 3530
+#define wxAuiNotebookEvent_SetOldSelection 3531
+#define wxAuiNotebookEvent_GetOldSelection 3532
+#define wxAuiNotebookEvent_SetDragSource 3533
+#define wxAuiNotebookEvent_GetDragSource 3534
+#define wxAuiManagerEvent_SetManager 3535
+#define wxAuiManagerEvent_GetManager 3536
+#define wxAuiManagerEvent_SetPane 3537
+#define wxAuiManagerEvent_GetPane 3538
+#define wxAuiManagerEvent_SetButton 3539
+#define wxAuiManagerEvent_GetButton 3540
+#define wxAuiManagerEvent_SetDC 3541
+#define wxAuiManagerEvent_GetDC 3542
+#define wxAuiManagerEvent_Veto 3543
+#define wxAuiManagerEvent_GetVeto 3544
+#define wxAuiManagerEvent_SetCanVeto 3545
+#define wxAuiManagerEvent_CanVeto 3546
+#define wxLogNull_new 3547
+#define wxLogNull_destroy 3548
+#define wxTaskBarIcon_new 3549
+#define wxTaskBarIcon_destruct 3550
+#define wxTaskBarIcon_PopupMenu 3551
+#define wxTaskBarIcon_RemoveIcon 3552
+#define wxTaskBarIcon_SetIcon 3553
+#define wxLocale_new_0 3554
+#define wxLocale_new_2 3556
+#define wxLocale_destruct 3557
+#define wxLocale_Init 3559
+#define wxLocale_AddCatalog_1 3560
+#define wxLocale_AddCatalog_3 3561
+#define wxLocale_AddCatalogLookupPathPrefix 3562
+#define wxLocale_GetCanonicalName 3563
+#define wxLocale_GetLanguage 3564
+#define wxLocale_GetLanguageName 3565
+#define wxLocale_GetLocale 3566
+#define wxLocale_GetName 3567
+#define wxLocale_GetString_2 3568
+#define wxLocale_GetString_4 3569
+#define wxLocale_GetHeaderValue 3570
+#define wxLocale_GetSysName 3571
+#define wxLocale_GetSystemEncoding 3572
+#define wxLocale_GetSystemEncodingName 3573
+#define wxLocale_GetSystemLanguage 3574
+#define wxLocale_IsLoaded 3575
+#define wxLocale_IsOk 3576
+#define wxActivateEvent_GetActive 3577
+#define wxPopupWindow_new_2 3579
+#define wxPopupWindow_new_0 3580
+#define wxPopupWindow_destruct 3582
+#define wxPopupWindow_Create 3583
+#define wxPopupWindow_Position 3584
+#define wxPopupTransientWindow_new_0 3585
+#define wxPopupTransientWindow_new_2 3586
+#define wxPopupTransientWindow_destruct 3587
+#define wxPopupTransientWindow_Popup 3588
+#define wxPopupTransientWindow_Dismiss 3589
+#define wxOverlay_new 3590
+#define wxOverlay_destruct 3591
+#define wxOverlay_Reset 3592
+#define wxDCOverlay_new_6 3593
+#define wxDCOverlay_new_2 3594
+#define wxDCOverlay_destruct 3595
+#define wxDCOverlay_Clear 3596
+#define wxDropFilesEvent_GetPosition 3597
+#define wxDropFilesEvent_GetNumberOfFiles 3598
+#define wxDropFilesEvent_GetFiles 3599
diff --git a/lib/wx/c_src/wxe_main.cpp b/lib/wx/c_src/wxe_main.cpp
index 6fcde42eb5..c7565e33bd 100644
--- a/lib/wx/c_src/wxe_main.cpp
+++ b/lib/wx/c_src/wxe_main.cpp
@@ -67,6 +67,7 @@ int load_native_gui()
int start_native_gui(wxe_data *sd)
{
int res;
+ ErlDrvThreadOpts *opts = NULL;
wxe_status_m = erl_drv_mutex_create((char *) "wxe_status_m");
wxe_status_c = erl_drv_cond_create((char *)"wxe_status_c");
@@ -78,8 +79,11 @@ int start_native_gui(wxe_data *sd)
res = erl_drv_steal_main_thread((char *)"wxwidgets",
&wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
#else
+ opts = erl_drv_thread_opts_create((char *)"wx thread");
+ opts->suggested_stack_size = 8192;
res = erl_drv_thread_create((char *)"wxwidgets",
- &wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
+ &wxe_thread,wxe_main_loop,(void *) sd->pdl,opts);
+ erl_drv_thread_opts_destroy(opts);
#endif
if(res == 0) {
erl_drv_mutex_lock(wxe_status_m);
diff --git a/lib/wx/doc/specs/.gitignore b/lib/wx/doc/specs/.gitignore
new file mode 100644
index 0000000000..322eebcb06
--- /dev/null
+++ b/lib/wx/doc/specs/.gitignore
@@ -0,0 +1 @@
+specs_*.xml
diff --git a/lib/wx/doc/src/Makefile b/lib/wx/doc/src/Makefile
index cae2f9fe4e..23890f47f0 100644
--- a/lib/wx/doc/src/Makefile
+++ b/lib/wx/doc/src/Makefile
@@ -63,10 +63,15 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+
+TOP_SPECS_FILE = specs.xml
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
XML_FLAGS +=
+SPECS_FLAGS = -I../../include -I../../src
DVIPS_FLAGS +=
# ----------------------------------------------------
@@ -111,6 +116,7 @@ clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f $(SPECDIR)/*
rm -f errs core *~ ../html/edoc-info
rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 4f0e166924..9086117c81 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -32,6 +32,52 @@
<p>This document describes the changes made to the wxErlang
application.</p>
+<section><title>Wx 1.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Allow string arguments to be binaries as specified, i.e.
+ unicode:chardata().</p>
+ <p>
+ Own Id: OTP-13934 Aux Id: ERL-270 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add wxWindow:dragAcceptFiles/2 and wxDropFilesEvent to
+ support simple drag and drop from file browser.</p>
+ <p>
+ Own Id: OTP-13933</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Wx 1.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Increased the stacksize for the wx thread. The default
+ stacksize on Windows is 1MB which is not enough if the
+ user created many nested dialogs.</p>
+ <p>
+ Own Id: OTP-13816</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Wx 1.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/wx/doc/src/specs.xml b/lib/wx/doc/src/specs.xml
new file mode 100644
index 0000000000..2e19baafc4
--- /dev/null
+++ b/lib/wx/doc/src/specs.xml
@@ -0,0 +1,232 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<specs xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="../specs/specs_wx.xml"/>
+ <xi:include href="../specs/specs_wx_object.xml"/>
+ <xi:include href="../specs/specs_wxAcceleratorEntry.xml"/>
+ <xi:include href="../specs/specs_wxAcceleratorTable.xml"/>
+ <xi:include href="../specs/specs_wxActivateEvent.xml"/>
+ <xi:include href="../specs/specs_wxArtProvider.xml"/>
+ <xi:include href="../specs/specs_wxAuiDockArt.xml"/>
+ <xi:include href="../specs/specs_wxAuiManager.xml"/>
+ <xi:include href="../specs/specs_wxAuiManagerEvent.xml"/>
+ <xi:include href="../specs/specs_wxAuiNotebook.xml"/>
+ <xi:include href="../specs/specs_wxAuiNotebookEvent.xml"/>
+ <xi:include href="../specs/specs_wxAuiPaneInfo.xml"/>
+ <xi:include href="../specs/specs_wxAuiSimpleTabArt.xml"/>
+ <xi:include href="../specs/specs_wxAuiTabArt.xml"/>
+ <xi:include href="../specs/specs_wxBitmapButton.xml"/>
+ <xi:include href="../specs/specs_wxBitmapDataObject.xml"/>
+ <xi:include href="../specs/specs_wxBitmap.xml"/>
+ <xi:include href="../specs/specs_wxBoxSizer.xml"/>
+ <xi:include href="../specs/specs_wxBrush.xml"/>
+ <xi:include href="../specs/specs_wxBufferedDC.xml"/>
+ <xi:include href="../specs/specs_wxBufferedPaintDC.xml"/>
+ <xi:include href="../specs/specs_wxButton.xml"/>
+ <xi:include href="../specs/specs_wxCalendarCtrl.xml"/>
+ <xi:include href="../specs/specs_wxCalendarDateAttr.xml"/>
+ <xi:include href="../specs/specs_wxCalendarEvent.xml"/>
+ <xi:include href="../specs/specs_wxCaret.xml"/>
+ <xi:include href="../specs/specs_wxCheckBox.xml"/>
+ <xi:include href="../specs/specs_wxCheckListBox.xml"/>
+ <xi:include href="../specs/specs_wxChildFocusEvent.xml"/>
+ <xi:include href="../specs/specs_wxChoicebook.xml"/>
+ <xi:include href="../specs/specs_wxChoice.xml"/>
+ <xi:include href="../specs/specs_wxClientDC.xml"/>
+ <xi:include href="../specs/specs_wxClipboard.xml"/>
+ <xi:include href="../specs/specs_wxClipboardTextEvent.xml"/>
+ <xi:include href="../specs/specs_wxCloseEvent.xml"/>
+ <xi:include href="../specs/specs_wxColourData.xml"/>
+ <xi:include href="../specs/specs_wxColourDialog.xml"/>
+ <xi:include href="../specs/specs_wxColourPickerCtrl.xml"/>
+ <xi:include href="../specs/specs_wxColourPickerEvent.xml"/>
+ <xi:include href="../specs/specs_wxComboBox.xml"/>
+ <xi:include href="../specs/specs_wxCommandEvent.xml"/>
+ <xi:include href="../specs/specs_wxContextMenuEvent.xml"/>
+ <xi:include href="../specs/specs_wxControl.xml"/>
+ <xi:include href="../specs/specs_wxControlWithItems.xml"/>
+ <xi:include href="../specs/specs_wxCursor.xml"/>
+ <xi:include href="../specs/specs_wxDataObject.xml"/>
+ <xi:include href="../specs/specs_wxDateEvent.xml"/>
+ <xi:include href="../specs/specs_wxDatePickerCtrl.xml"/>
+ <xi:include href="../specs/specs_wxDC.xml"/>
+ <xi:include href="../specs/specs_wxDCOverlay.xml"/>
+ <xi:include href="../specs/specs_wxDialog.xml"/>
+ <xi:include href="../specs/specs_wxDirDialog.xml"/>
+ <xi:include href="../specs/specs_wxDirPickerCtrl.xml"/>
+ <xi:include href="../specs/specs_wxDisplayChangedEvent.xml"/>
+ <xi:include href="../specs/specs_wxEraseEvent.xml"/>
+ <xi:include href="../specs/specs_wxEvent.xml"/>
+ <xi:include href="../specs/specs_wxEvtHandler.xml"/>
+ <xi:include href="../specs/specs_wxFileDataObject.xml"/>
+ <xi:include href="../specs/specs_wxFileDialog.xml"/>
+ <xi:include href="../specs/specs_wxFileDirPickerEvent.xml"/>
+ <xi:include href="../specs/specs_wxFilePickerCtrl.xml"/>
+ <xi:include href="../specs/specs_wxFindReplaceData.xml"/>
+ <xi:include href="../specs/specs_wxFindReplaceDialog.xml"/>
+ <xi:include href="../specs/specs_wxFlexGridSizer.xml"/>
+ <xi:include href="../specs/specs_wxFocusEvent.xml"/>
+ <xi:include href="../specs/specs_wxFontData.xml"/>
+ <xi:include href="../specs/specs_wxFontDialog.xml"/>
+ <xi:include href="../specs/specs_wxFont.xml"/>
+ <xi:include href="../specs/specs_wxFontPickerCtrl.xml"/>
+ <xi:include href="../specs/specs_wxFontPickerEvent.xml"/>
+ <xi:include href="../specs/specs_wxFrame.xml"/>
+ <xi:include href="../specs/specs_wxGauge.xml"/>
+ <xi:include href="../specs/specs_wxGBSizerItem.xml"/>
+ <xi:include href="../specs/specs_wxGenericDirCtrl.xml"/>
+ <xi:include href="../specs/specs_wxGLCanvas.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsBrush.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsContext.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsFont.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsMatrix.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsObject.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsPath.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsPen.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridBagSizer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellAttr.xml"/>
+ <xi:include href="../specs/specs_wxGridCellBoolEditor.xml"/>
+ <xi:include href="../specs/specs_wxGridCellBoolRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellChoiceEditor.xml"/>
+ <xi:include href="../specs/specs_wxGridCellEditor.xml"/>
+ <xi:include href="../specs/specs_wxGridCellFloatEditor.xml"/>
+ <xi:include href="../specs/specs_wxGridCellFloatRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellNumberEditor.xml"/>
+ <xi:include href="../specs/specs_wxGridCellNumberRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellStringRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellTextEditor.xml"/>
+ <xi:include href="../specs/specs_wxGrid.xml"/>
+ <xi:include href="../specs/specs_wxGridEvent.xml"/>
+ <xi:include href="../specs/specs_wxGridSizer.xml"/>
+ <xi:include href="../specs/specs_wxHelpEvent.xml"/>
+ <xi:include href="../specs/specs_wxHtmlEasyPrinting.xml"/>
+ <xi:include href="../specs/specs_wxHtmlLinkEvent.xml"/>
+ <xi:include href="../specs/specs_wxHtmlWindow.xml"/>
+ <xi:include href="../specs/specs_wxIconBundle.xml"/>
+ <xi:include href="../specs/specs_wxIcon.xml"/>
+ <xi:include href="../specs/specs_wxIconizeEvent.xml"/>
+ <xi:include href="../specs/specs_wxIdleEvent.xml"/>
+ <xi:include href="../specs/specs_wxImage.xml"/>
+ <xi:include href="../specs/specs_wxImageList.xml"/>
+ <xi:include href="../specs/specs_wxInitDialogEvent.xml"/>
+ <xi:include href="../specs/specs_wxJoystickEvent.xml"/>
+ <xi:include href="../specs/specs_wxKeyEvent.xml"/>
+ <xi:include href="../specs/specs_wxLayoutAlgorithm.xml"/>
+ <xi:include href="../specs/specs_wxListbook.xml"/>
+ <xi:include href="../specs/specs_wxListBox.xml"/>
+ <xi:include href="../specs/specs_wxListCtrl.xml"/>
+ <xi:include href="../specs/specs_wxListEvent.xml"/>
+ <xi:include href="../specs/specs_wxListItemAttr.xml"/>
+ <xi:include href="../specs/specs_wxListItem.xml"/>
+ <xi:include href="../specs/specs_wxListView.xml"/>
+ <xi:include href="../specs/specs_wxLocale.xml"/>
+ <xi:include href="../specs/specs_wxLogNull.xml"/>
+ <xi:include href="../specs/specs_wxMask.xml"/>
+ <xi:include href="../specs/specs_wxMaximizeEvent.xml"/>
+ <xi:include href="../specs/specs_wxMDIChildFrame.xml"/>
+ <xi:include href="../specs/specs_wxMDIClientWindow.xml"/>
+ <xi:include href="../specs/specs_wxMDIParentFrame.xml"/>
+ <xi:include href="../specs/specs_wxMemoryDC.xml"/>
+ <xi:include href="../specs/specs_wxMenuBar.xml"/>
+ <xi:include href="../specs/specs_wxMenu.xml"/>
+ <xi:include href="../specs/specs_wxMenuEvent.xml"/>
+ <xi:include href="../specs/specs_wxMenuItem.xml"/>
+ <xi:include href="../specs/specs_wxMessageDialog.xml"/>
+ <xi:include href="../specs/specs_wxMiniFrame.xml"/>
+ <xi:include href="../specs/specs_wxMirrorDC.xml"/>
+ <xi:include href="../specs/specs_wxMouseCaptureChangedEvent.xml"/>
+ <xi:include href="../specs/specs_wxMouseCaptureLostEvent.xml"/>
+ <xi:include href="../specs/specs_wxMouseEvent.xml"/>
+ <xi:include href="../specs/specs_wxMoveEvent.xml"/>
+ <xi:include href="../specs/specs_wxMultiChoiceDialog.xml"/>
+ <xi:include href="../specs/specs_wxNavigationKeyEvent.xml"/>
+ <xi:include href="../specs/specs_wxNotebook.xml"/>
+ <xi:include href="../specs/specs_wxNotebookEvent.xml"/>
+ <xi:include href="../specs/specs_wxNotifyEvent.xml"/>
+ <xi:include href="../specs/specs_wxOverlay.xml"/>
+ <xi:include href="../specs/specs_wxPageSetupDialogData.xml"/>
+ <xi:include href="../specs/specs_wxPageSetupDialog.xml"/>
+ <xi:include href="../specs/specs_wxPaintDC.xml"/>
+ <xi:include href="../specs/specs_wxPaintEvent.xml"/>
+ <xi:include href="../specs/specs_wxPaletteChangedEvent.xml"/>
+ <xi:include href="../specs/specs_wxPalette.xml"/>
+ <xi:include href="../specs/specs_wxPanel.xml"/>
+ <xi:include href="../specs/specs_wxPasswordEntryDialog.xml"/>
+ <xi:include href="../specs/specs_wxPen.xml"/>
+ <xi:include href="../specs/specs_wxPickerBase.xml"/>
+ <xi:include href="../specs/specs_wxPopupTransientWindow.xml"/>
+ <xi:include href="../specs/specs_wxPopupWindow.xml"/>
+ <xi:include href="../specs/specs_wxPostScriptDC.xml"/>
+ <xi:include href="../specs/specs_wxPreviewCanvas.xml"/>
+ <xi:include href="../specs/specs_wxPreviewControlBar.xml"/>
+ <xi:include href="../specs/specs_wxPreviewFrame.xml"/>
+ <xi:include href="../specs/specs_wxPrintData.xml"/>
+ <xi:include href="../specs/specs_wxPrintDialogData.xml"/>
+ <xi:include href="../specs/specs_wxPrintDialog.xml"/>
+ <xi:include href="../specs/specs_wxPrinter.xml"/>
+ <xi:include href="../specs/specs_wxPrintout.xml"/>
+ <xi:include href="../specs/specs_wxPrintPreview.xml"/>
+ <xi:include href="../specs/specs_wxProgressDialog.xml"/>
+ <xi:include href="../specs/specs_wxQueryNewPaletteEvent.xml"/>
+ <xi:include href="../specs/specs_wxRadioBox.xml"/>
+ <xi:include href="../specs/specs_wxRadioButton.xml"/>
+ <xi:include href="../specs/specs_wxRegion.xml"/>
+ <xi:include href="../specs/specs_wxSashEvent.xml"/>
+ <xi:include href="../specs/specs_wxSashLayoutWindow.xml"/>
+ <xi:include href="../specs/specs_wxSashWindow.xml"/>
+ <xi:include href="../specs/specs_wxScreenDC.xml"/>
+ <xi:include href="../specs/specs_wxScrollBar.xml"/>
+ <xi:include href="../specs/specs_wxScrolledWindow.xml"/>
+ <xi:include href="../specs/specs_wxScrollEvent.xml"/>
+ <xi:include href="../specs/specs_wxScrollWinEvent.xml"/>
+ <xi:include href="../specs/specs_wxSetCursorEvent.xml"/>
+ <xi:include href="../specs/specs_wxShowEvent.xml"/>
+ <xi:include href="../specs/specs_wxSingleChoiceDialog.xml"/>
+ <xi:include href="../specs/specs_wxSizeEvent.xml"/>
+ <xi:include href="../specs/specs_wxSizer.xml"/>
+ <xi:include href="../specs/specs_wxSizerFlags.xml"/>
+ <xi:include href="../specs/specs_wxSizerItem.xml"/>
+ <xi:include href="../specs/specs_wxSlider.xml"/>
+ <xi:include href="../specs/specs_wxSpinButton.xml"/>
+ <xi:include href="../specs/specs_wxSpinCtrl.xml"/>
+ <xi:include href="../specs/specs_wxSpinEvent.xml"/>
+ <xi:include href="../specs/specs_wxSplashScreen.xml"/>
+ <xi:include href="../specs/specs_wxSplitterEvent.xml"/>
+ <xi:include href="../specs/specs_wxSplitterWindow.xml"/>
+ <xi:include href="../specs/specs_wxStaticBitmap.xml"/>
+ <xi:include href="../specs/specs_wxStaticBox.xml"/>
+ <xi:include href="../specs/specs_wxStaticBoxSizer.xml"/>
+ <xi:include href="../specs/specs_wxStaticLine.xml"/>
+ <xi:include href="../specs/specs_wxStaticText.xml"/>
+ <xi:include href="../specs/specs_wxStatusBar.xml"/>
+ <xi:include href="../specs/specs_wxStdDialogButtonSizer.xml"/>
+ <xi:include href="../specs/specs_wxStyledTextCtrl.xml"/>
+ <xi:include href="../specs/specs_wxStyledTextEvent.xml"/>
+ <xi:include href="../specs/specs_wxSysColourChangedEvent.xml"/>
+ <xi:include href="../specs/specs_wxSystemOptions.xml"/>
+ <xi:include href="../specs/specs_wxSystemSettings.xml"/>
+ <xi:include href="../specs/specs_wxTaskBarIcon.xml"/>
+ <xi:include href="../specs/specs_wxTaskBarIconEvent.xml"/>
+ <xi:include href="../specs/specs_wxTextAttr.xml"/>
+ <xi:include href="../specs/specs_wxTextCtrl.xml"/>
+ <xi:include href="../specs/specs_wxTextDataObject.xml"/>
+ <xi:include href="../specs/specs_wxTextEntryDialog.xml"/>
+ <xi:include href="../specs/specs_wxToggleButton.xml"/>
+ <xi:include href="../specs/specs_wxToolBar.xml"/>
+ <xi:include href="../specs/specs_wxToolbook.xml"/>
+ <xi:include href="../specs/specs_wxToolTip.xml"/>
+ <xi:include href="../specs/specs_wxTopLevelWindow.xml"/>
+ <xi:include href="../specs/specs_wxTreebook.xml"/>
+ <xi:include href="../specs/specs_wxTreeCtrl.xml"/>
+ <xi:include href="../specs/specs_wxTreeEvent.xml"/>
+ <xi:include href="../specs/specs_wxUpdateUIEvent.xml"/>
+ <xi:include href="../specs/specs_wxWindowCreateEvent.xml"/>
+ <xi:include href="../specs/specs_wxWindowDC.xml"/>
+ <xi:include href="../specs/specs_wxWindowDestroyEvent.xml"/>
+ <xi:include href="../specs/specs_wxWindow.xml"/>
+ <xi:include href="../specs/specs_wxXmlResource.xml"/>
+ <xi:include href="../specs/specs_wx_misc.xml"/>
+ <xi:include href="../specs/specs_glu.xml"/>
+ <xi:include href="../specs/specs_gl.xml"/>
+</specs>
diff --git a/lib/wx/examples/demo/demo.erl b/lib/wx/examples/demo/demo.erl
index 99c28b3177..8b7412017a 100644
--- a/lib/wx/examples/demo/demo.erl
+++ b/lib/wx/examples/demo/demo.erl
@@ -60,11 +60,15 @@ start_link() ->
start_link(Debug) ->
wx_object:start_link(?MODULE, Debug, []).
+format(#state{log=Log}, Str, Args) ->
+ wxTextCtrl:appendText(Log, io_lib:format(Str, Args)),
+ ok;
format(Config,Str,Args) ->
Log = proplists:get_value(log, Config),
wxTextCtrl:appendText(Log, io_lib:format(Str, Args)),
ok.
+
-define(DEBUG_NONE, 101).
-define(DEBUG_VERBOSE, 102).
-define(DEBUG_TRACE, 103).
@@ -97,7 +101,11 @@ init(Options) ->
wxFrame:connect(Frame, close_window),
_SB = wxFrame:createStatusBar(Frame,[]),
-
+
+ %% Setup on toplevel because stc seems to steal this on linux
+ wxFrame:dragAcceptFiles(Frame, true),
+ wxFrame:connect(Frame, drop_files),
+
%% T Uppersplitter
%% O Left | Right
%% P Widgets|Code | Demo
@@ -201,15 +209,15 @@ handle_info({'EXIT',_, shutdown}, State) ->
handle_info({'EXIT',_, normal}, State) ->
{noreply,State};
handle_info(Msg, State) ->
- io:format("Got Info ~p~n",[Msg]),
+ format(State, "Got Info ~p~n",[Msg]),
{noreply,State}.
handle_call(Msg, _From, State) ->
- io:format("Got Call ~p~n",[Msg]),
+ format(State, "Got Call ~p~n",[Msg]),
{reply,ok,State}.
handle_cast(Msg, State) ->
- io:format("Got cast ~p~n",[Msg]),
+ format(State, "Got cast ~p~n",[Msg]),
{noreply,State}.
%% Async Events are handled in handle_event as in handle_info
@@ -286,7 +294,7 @@ handle_event(#wx{event=#wxClose{}}, State = #state{win=Frame}) ->
ok = wxFrame:setStatusText(Frame, "Closing...",[]),
{stop, normal, State};
handle_event(Ev,State) ->
- io:format("~p Got event ~p ~n",[?MODULE, Ev]),
+ format(State, "~p Got event ~p ~n",[?MODULE, Ev]),
{noreply, State}.
code_change(_, _, State) ->
@@ -364,6 +372,7 @@ code_area(Parent) ->
?stc:setVisiblePolicy(Ed, Policy, 3),
%% ?stc:connect(Ed, stc_doubleclick),
+ %% ?stc:connect(Ed, std_do_drop, fun(Ev, Obj) -> io:format("Ev ~p ~p~n",[Ev,Obj]) end),
?stc:setReadOnly(Ed, true),
Ed.
diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl
index af7cca7ed2..a14cc89cee 100644
--- a/lib/wx/include/wx.hrl
+++ b/lib/wx/include/wx.hrl
@@ -33,164 +33,157 @@
%% Here comes the definitions of all event records.
%% they contain the event type and possible some extra information.
--record(wxInitDialog, {type :: wxInitDialogEventType()}). %% Callback event: {@link wxInitDialogEvent}
--type wxInitDialogEventType() :: 'init_dialog'.
--type wxInitDialog() :: #wxInitDialog{}. %% Callback event: {@link wxInitDialogEvent}
+-record(wxActivate,{type :: wxActivateEventType(), %% Callback event: {@link wxActivateEvent}
+ active :: boolean()}).
+-type wxActivateEventType() :: 'activate' | 'activate_app' | 'hibernate'.
+-type wxActivate() :: #wxActivate{}. %% Callback event: {@link wxActivateEvent}
--record(wxClose, {type :: wxCloseEventType()}). %% Callback event: {@link wxCloseEvent}
--type wxCloseEventType() :: 'close_window' | 'end_session' | 'query_end_session'.
--type wxClose() :: #wxClose{}. %% Callback event: {@link wxCloseEvent}
+-record(wxAuiManager,{type :: wxAuiManagerEventType(), %% Callback event: {@link wxAuiManagerEvent}
+ manager :: wxAuiManager:wxAuiManager(),
+ pane :: wxAuiPaneInfo:wxAuiPaneInfo(),
+ button :: integer(),
+ veto_flag :: boolean(),
+ canveto_flag :: boolean(),
+ dc :: wxDC:wxDC()}).
+-type wxAuiManagerEventType() :: 'aui_pane_button' | 'aui_pane_close' | 'aui_pane_maximize' | 'aui_pane_restore' | 'aui_pane_activated' | 'aui_render' | 'aui_find_manager'.
+-type wxAuiManager() :: #wxAuiManager{}. %% Callback event: {@link wxAuiManagerEvent}
--record(wxStyledText,{type :: wxStyledTextEventType(), %% Callback event: {@link wxStyledTextEvent}
- position :: integer(),
- key :: integer(),
- modifiers :: integer(),
- modificationType :: integer(),
- text :: unicode:chardata(),
- length :: integer(),
- linesAdded :: integer(),
- line :: integer(),
- foldLevelNow :: integer(),
- foldLevelPrev :: integer(),
- margin :: integer(),
- message :: integer(),
- wParam :: integer(),
- lParam :: integer(),
- listType :: integer(),
- x :: integer(),
- y :: integer(),
- dragText :: unicode:chardata(),
- dragAllowMove :: boolean(),
- dragResult :: wx:wx_enum()}).
--type wxStyledTextEventType() :: 'stc_change' | 'stc_styleneeded' | 'stc_charadded' | 'stc_savepointreached' | 'stc_savepointleft' | 'stc_romodifyattempt' | 'stc_key' | 'stc_doubleclick' | 'stc_updateui' | 'stc_modified' | 'stc_macrorecord' | 'stc_marginclick' | 'stc_needshown' | 'stc_painted' | 'stc_userlistselection' | 'stc_uridropped' | 'stc_dwellstart' | 'stc_dwellend' | 'stc_start_drag' | 'stc_drag_over' | 'stc_do_drop' | 'stc_zoom' | 'stc_hotspot_click' | 'stc_hotspot_dclick' | 'stc_calltip_click' | 'stc_autocomp_selection'.
--type wxStyledText() :: #wxStyledText{}. %% Callback event: {@link wxStyledTextEvent}
+-record(wxAuiNotebook,{type :: wxAuiNotebookEventType(), %% Callback event: {@link wxAuiNotebookEvent}
+ old_selection :: integer(),
+ selection :: integer(),
+ drag_source :: wxAuiNotebook:wxAuiNotebook()}).
+-type wxAuiNotebookEventType() :: 'command_auinotebook_page_close' | 'command_auinotebook_page_changed' | 'command_auinotebook_page_changing' | 'command_auinotebook_button' | 'command_auinotebook_begin_drag' | 'command_auinotebook_end_drag' | 'command_auinotebook_drag_motion' | 'command_auinotebook_allow_dnd' | 'command_auinotebook_tab_middle_down' | 'command_auinotebook_tab_middle_up' | 'command_auinotebook_tab_right_down' | 'command_auinotebook_tab_right_up' | 'command_auinotebook_page_closed' | 'command_auinotebook_drag_done' | 'command_auinotebook_bg_dclick'.
+-type wxAuiNotebook() :: #wxAuiNotebook{}. %% Callback event: {@link wxAuiNotebookEvent}
--record(wxFileDirPicker,{type :: wxFileDirPickerEventType(), %% Callback event: {@link wxFileDirPickerEvent}
- path :: unicode:chardata()}).
--type wxFileDirPickerEventType() :: 'command_filepicker_changed' | 'command_dirpicker_changed'.
--type wxFileDirPicker() :: #wxFileDirPicker{}. %% Callback event: {@link wxFileDirPickerEvent}
+-record(wxCalendar,{type :: wxCalendarEventType(), %% Callback event: {@link wxCalendarEvent}
+ wday :: wx:wx_enum(),
+ date :: wx:wx_datetime()}).
+-type wxCalendarEventType() :: 'calendar_sel_changed' | 'calendar_day_changed' | 'calendar_month_changed' | 'calendar_year_changed' | 'calendar_doubleclicked' | 'calendar_weekday_clicked'.
+-type wxCalendar() :: #wxCalendar{}. %% Callback event: {@link wxCalendarEvent}
--record(wxNotebook,{type :: wxNotebookEventType(), %% Callback event: {@link wxNotebookEvent}
- nSel :: integer(),
- nOldSel :: integer()}).
--type wxNotebookEventType() :: 'command_notebook_page_changed' | 'command_notebook_page_changing'.
--type wxNotebook() :: #wxNotebook{}. %% Callback event: {@link wxNotebookEvent}
+-record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent}
+-type wxChildFocusEventType() :: 'child_focus'.
+-type wxChildFocus() :: #wxChildFocus{}. %% Callback event: {@link wxChildFocusEvent}
--record(wxIdle, {type :: wxIdleEventType()}). %% Callback event: {@link wxIdleEvent}
--type wxIdleEventType() :: 'idle'.
--type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent}
+-record(wxClipboardText, {type :: wxClipboardTextEventType()}). %% Callback event: {@link wxClipboardTextEvent}
+-type wxClipboardTextEventType() :: 'command_text_copy' | 'command_text_cut' | 'command_text_paste'.
+-type wxClipboardText() :: #wxClipboardText{}. %% Callback event: {@link wxClipboardTextEvent}
+
+-record(wxClose, {type :: wxCloseEventType()}). %% Callback event: {@link wxCloseEvent}
+-type wxCloseEventType() :: 'close_window' | 'end_session' | 'query_end_session'.
+-type wxClose() :: #wxClose{}. %% Callback event: {@link wxCloseEvent}
-record(wxColourPicker,{type :: wxColourPickerEventType(), %% Callback event: {@link wxColourPickerEvent}
colour :: wx:wx_colour()}).
-type wxColourPickerEventType() :: 'command_colourpicker_changed'.
-type wxColourPicker() :: #wxColourPicker{}. %% Callback event: {@link wxColourPickerEvent}
--record(wxSplitter, {type :: wxSplitterEventType()}). %% Callback event: {@link wxSplitterEvent}
--type wxSplitterEventType() :: 'command_splitter_sash_pos_changed' | 'command_splitter_sash_pos_changing' | 'command_splitter_doubleclicked' | 'command_splitter_unsplit'.
--type wxSplitter() :: #wxSplitter{}. %% Callback event: {@link wxSplitterEvent}
+-record(wxCommand,{type :: wxCommandEventType(), %% Callback event: {@link wxCommandEvent}
+ cmdString :: unicode:chardata(),
+ commandInt :: integer(),
+ extraLong :: integer()}).
+-type wxCommandEventType() :: 'command_button_clicked' | 'command_checkbox_clicked' | 'command_choice_selected' | 'command_listbox_selected' | 'command_listbox_doubleclicked' | 'command_text_updated' | 'command_text_enter' | 'command_menu_selected' | 'command_slider_updated' | 'command_radiobox_selected' | 'command_radiobutton_selected' | 'command_scrollbar_updated' | 'command_vlbox_selected' | 'command_combobox_selected' | 'command_tool_rclicked' | 'command_tool_enter' | 'command_checklistbox_toggled' | 'command_togglebutton_clicked' | 'command_left_click' | 'command_left_dclick' | 'command_right_click' | 'command_set_focus' | 'command_kill_focus' | 'command_enter'.
+-type wxCommand() :: #wxCommand{}. %% Callback event: {@link wxCommandEvent}
--record(wxSash,{type :: wxSashEventType(), %% Callback event: {@link wxSashEvent}
- edge :: wx:wx_enum(),
- dragRect :: {X::integer(), Y::integer(), W::integer(), H::integer()},
- dragStatus :: wx:wx_enum()}).
--type wxSashEventType() :: 'sash_dragged'.
--type wxSash() :: #wxSash{}. %% Callback event: {@link wxSashEvent}
+-record(wxContextMenu,{type :: wxContextMenuEventType(), %% Callback event: {@link wxContextMenuEvent}
+ pos :: {X::integer(), Y::integer()}}).
+-type wxContextMenuEventType() :: 'context_menu'.
+-type wxContextMenu() :: #wxContextMenu{}. %% Callback event: {@link wxContextMenuEvent}
--record(wxHelp, {type :: wxHelpEventType()}). %% Callback event: {@link wxHelpEvent}
--type wxHelpEventType() :: 'help' | 'detailed_help'.
--type wxHelp() :: #wxHelp{}. %% Callback event: {@link wxHelpEvent}
+-record(wxDate,{type :: wxDateEventType(), %% Callback event: {@link wxDateEvent}
+ date :: wx:wx_datetime()}).
+-type wxDateEventType() :: 'date_changed'.
+-type wxDate() :: #wxDate{}. %% Callback event: {@link wxDateEvent}
-record(wxDisplayChanged, {type :: wxDisplayChangedEventType()}). %% Callback event: {@link wxDisplayChangedEvent}
-type wxDisplayChangedEventType() :: 'display_changed'.
-type wxDisplayChanged() :: #wxDisplayChanged{}. %% Callback event: {@link wxDisplayChangedEvent}
--record(wxMouseCaptureLost, {type :: wxMouseCaptureLostEventType()}). %% Callback event: {@link wxMouseCaptureLostEvent}
--type wxMouseCaptureLostEventType() :: 'mouse_capture_lost'.
--type wxMouseCaptureLost() :: #wxMouseCaptureLost{}. %% Callback event: {@link wxMouseCaptureLostEvent}
+-record(wxDropFiles,{type :: wxDropFilesEventType(), %% Callback event: {@link wxDropFilesEvent}
+ noFiles :: integer(),
+ pos :: {X::integer(), Y::integer()},
+ files :: [unicode:chardata()]}).
+-type wxDropFilesEventType() :: 'drop_files'.
+-type wxDropFiles() :: #wxDropFiles{}. %% Callback event: {@link wxDropFilesEvent}
--record(wxFontPicker,{type :: wxFontPickerEventType(), %% Callback event: {@link wxFontPickerEvent}
- font :: wxFont:wxFont()}).
--type wxFontPickerEventType() :: 'command_fontpicker_changed'.
--type wxFontPicker() :: #wxFontPicker{}. %% Callback event: {@link wxFontPickerEvent}
+-record(wxErase,{type :: wxEraseEventType(), %% Callback event: {@link wxEraseEvent}
+ dc :: wxDC:wxDC()}).
+-type wxEraseEventType() :: 'erase_background'.
+-type wxErase() :: #wxErase{}. %% Callback event: {@link wxEraseEvent}
+
+-record(wxFileDirPicker,{type :: wxFileDirPickerEventType(), %% Callback event: {@link wxFileDirPickerEvent}
+ path :: unicode:chardata()}).
+-type wxFileDirPickerEventType() :: 'command_filepicker_changed' | 'command_dirpicker_changed'.
+-type wxFileDirPicker() :: #wxFileDirPicker{}. %% Callback event: {@link wxFileDirPickerEvent}
-record(wxFocus,{type :: wxFocusEventType(), %% Callback event: {@link wxFocusEvent}
win :: wxWindow:wxWindow()}).
-type wxFocusEventType() :: 'set_focus' | 'kill_focus'.
-type wxFocus() :: #wxFocus{}. %% Callback event: {@link wxFocusEvent}
--record(wxPaletteChanged, {type :: wxPaletteChangedEventType()}). %% Callback event: {@link wxPaletteChangedEvent}
--type wxPaletteChangedEventType() :: 'palette_changed'.
--type wxPaletteChanged() :: #wxPaletteChanged{}. %% Callback event: {@link wxPaletteChangedEvent}
-
--record(wxScroll,{type :: wxScrollEventType(), %% Callback event: {@link wxScrollEvent}
- commandInt :: integer(),
- extraLong :: integer()}).
--type wxScrollEventType() :: 'scroll_top' | 'scroll_bottom' | 'scroll_lineup' | 'scroll_linedown' | 'scroll_pageup' | 'scroll_pagedown' | 'scroll_thumbtrack' | 'scroll_thumbrelease' | 'scroll_changed'.
--type wxScroll() :: #wxScroll{}. %% Callback event: {@link wxScrollEvent}
-
--record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent}
--type wxChildFocusEventType() :: 'child_focus'.
--type wxChildFocus() :: #wxChildFocus{}. %% Callback event: {@link wxChildFocusEvent}
-
--record(wxAuiNotebook,{type :: wxAuiNotebookEventType(), %% Callback event: {@link wxAuiNotebookEvent}
- old_selection :: integer(),
- selection :: integer(),
- drag_source :: wxAuiNotebook:wxAuiNotebook()}).
--type wxAuiNotebookEventType() :: 'command_auinotebook_page_close' | 'command_auinotebook_page_changed' | 'command_auinotebook_page_changing' | 'command_auinotebook_button' | 'command_auinotebook_begin_drag' | 'command_auinotebook_end_drag' | 'command_auinotebook_drag_motion' | 'command_auinotebook_allow_dnd' | 'command_auinotebook_tab_middle_down' | 'command_auinotebook_tab_middle_up' | 'command_auinotebook_tab_right_down' | 'command_auinotebook_tab_right_up' | 'command_auinotebook_page_closed' | 'command_auinotebook_drag_done' | 'command_auinotebook_bg_dclick'.
--type wxAuiNotebook() :: #wxAuiNotebook{}. %% Callback event: {@link wxAuiNotebookEvent}
-
--record(wxSize,{type :: wxSizeEventType(), %% Callback event: {@link wxSizeEvent}
- size :: {W::integer(), H::integer()},
- rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
--type wxSizeEventType() :: 'size'.
--type wxSize() :: #wxSize{}. %% Callback event: {@link wxSizeEvent}
-
--record(wxCommand,{type :: wxCommandEventType(), %% Callback event: {@link wxCommandEvent}
- cmdString :: unicode:chardata(),
- commandInt :: integer(),
- extraLong :: integer()}).
--type wxCommandEventType() :: 'command_button_clicked' | 'command_checkbox_clicked' | 'command_choice_selected' | 'command_listbox_selected' | 'command_listbox_doubleclicked' | 'command_text_updated' | 'command_text_enter' | 'command_menu_selected' | 'command_slider_updated' | 'command_radiobox_selected' | 'command_radiobutton_selected' | 'command_scrollbar_updated' | 'command_vlbox_selected' | 'command_combobox_selected' | 'command_tool_rclicked' | 'command_tool_enter' | 'command_checklistbox_toggled' | 'command_togglebutton_clicked' | 'command_left_click' | 'command_left_dclick' | 'command_right_click' | 'command_set_focus' | 'command_kill_focus' | 'command_enter'.
--type wxCommand() :: #wxCommand{}. %% Callback event: {@link wxCommandEvent}
-
--record(wxMaximize, {type :: wxMaximizeEventType()}). %% Callback event: {@link wxMaximizeEvent}
--type wxMaximizeEventType() :: 'maximize'.
--type wxMaximize() :: #wxMaximize{}. %% Callback event: {@link wxMaximizeEvent}
+-record(wxFontPicker,{type :: wxFontPickerEventType(), %% Callback event: {@link wxFontPickerEvent}
+ font :: wxFont:wxFont()}).
+-type wxFontPickerEventType() :: 'command_fontpicker_changed'.
+-type wxFontPicker() :: #wxFontPicker{}. %% Callback event: {@link wxFontPickerEvent}
--record(wxSpin,{type :: wxSpinEventType(), %% Callback event: {@link wxSpinEvent}
- commandInt :: integer()}).
--type wxSpinEventType() :: 'command_spinctrl_updated' | 'spin_up' | 'spin_down' | 'spin'.
--type wxSpin() :: #wxSpin{}. %% Callback event: {@link wxSpinEvent}
+-record(wxGrid,{type :: wxGridEventType(), %% Callback event: {@link wxGridEvent}
+ row :: integer(),
+ col :: integer(),
+ x :: integer(),
+ y :: integer(),
+ selecting :: boolean(),
+ control :: boolean(),
+ meta :: boolean(),
+ shift :: boolean(),
+ alt :: boolean()}).
+-type wxGridEventType() :: 'grid_cell_left_click' | 'grid_cell_right_click' | 'grid_cell_left_dclick' | 'grid_cell_right_dclick' | 'grid_label_left_click' | 'grid_label_right_click' | 'grid_label_left_dclick' | 'grid_label_right_dclick' | 'grid_row_size' | 'grid_col_size' | 'grid_range_select' | 'grid_cell_change' | 'grid_select_cell' | 'grid_editor_shown' | 'grid_editor_hidden' | 'grid_editor_created' | 'grid_cell_begin_drag'.
+-type wxGrid() :: #wxGrid{}. %% Callback event: {@link wxGridEvent}
--record(wxMenu,{type :: wxMenuEventType(), %% Callback event: {@link wxMenuEvent}
- menuId :: integer(),
- menu :: wxMenu:wxMenu()}).
--type wxMenuEventType() :: 'menu_open' | 'menu_close' | 'menu_highlight'.
--type wxMenu() :: #wxMenu{}. %% Callback event: {@link wxMenuEvent}
+-record(wxHelp, {type :: wxHelpEventType()}). %% Callback event: {@link wxHelpEvent}
+-type wxHelpEventType() :: 'help' | 'detailed_help'.
+-type wxHelp() :: #wxHelp{}. %% Callback event: {@link wxHelpEvent}
--record(wxShow,{type :: wxShowEventType(), %% Callback event: {@link wxShowEvent}
- show :: boolean()}).
--type wxShowEventType() :: 'show'.
--type wxShow() :: #wxShow{}. %% Callback event: {@link wxShowEvent}
+-record(wxHtmlLink,{type :: wxHtmlLinkEventType(), %% Callback event: {@link wxHtmlLinkEvent}
+ linkInfo :: wx:wx_wxHtmlLinkInfo()}).
+-type wxHtmlLinkEventType() :: 'command_html_link_clicked'.
+-type wxHtmlLink() :: #wxHtmlLink{}. %% Callback event: {@link wxHtmlLinkEvent}
--record(wxWindowDestroy, {type :: wxWindowDestroyEventType()}). %% Callback event: {@link wxWindowDestroyEvent}
--type wxWindowDestroyEventType() :: 'destroy'.
--type wxWindowDestroy() :: #wxWindowDestroy{}. %% Callback event: {@link wxWindowDestroyEvent}
+-record(wxIconize,{type :: wxIconizeEventType(), %% Callback event: {@link wxIconizeEvent}
+ iconized :: boolean()}).
+-type wxIconizeEventType() :: 'iconize'.
+-type wxIconize() :: #wxIconize{}. %% Callback event: {@link wxIconizeEvent}
--record(wxContextMenu,{type :: wxContextMenuEventType(), %% Callback event: {@link wxContextMenuEvent}
- pos :: {X::integer(), Y::integer()}}).
--type wxContextMenuEventType() :: 'context_menu'.
--type wxContextMenu() :: #wxContextMenu{}. %% Callback event: {@link wxContextMenuEvent}
+-record(wxIdle, {type :: wxIdleEventType()}). %% Callback event: {@link wxIdleEvent}
+-type wxIdleEventType() :: 'idle'.
+-type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent}
--record(wxActivate,{type :: wxActivateEventType(), %% Callback event: {@link wxActivateEvent}
- active :: boolean()}).
--type wxActivateEventType() :: 'activate' | 'activate_app' | 'hibernate'.
--type wxActivate() :: #wxActivate{}. %% Callback event: {@link wxActivateEvent}
+-record(wxInitDialog, {type :: wxInitDialogEventType()}). %% Callback event: {@link wxInitDialogEvent}
+-type wxInitDialogEventType() :: 'init_dialog'.
+-type wxInitDialog() :: #wxInitDialog{}. %% Callback event: {@link wxInitDialogEvent}
--record(wxMove,{type :: wxMoveEventType(), %% Callback event: {@link wxMoveEvent}
+-record(wxJoystick,{type :: wxJoystickEventType(), %% Callback event: {@link wxJoystickEvent}
pos :: {X::integer(), Y::integer()},
- rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
--type wxMoveEventType() :: 'move'.
--type wxMove() :: #wxMove{}. %% Callback event: {@link wxMoveEvent}
+ zPosition :: integer(),
+ buttonChange :: integer(),
+ buttonState :: integer(),
+ joyStick :: integer()}).
+-type wxJoystickEventType() :: 'joy_button_down' | 'joy_button_up' | 'joy_move' | 'joy_zmove'.
+-type wxJoystick() :: #wxJoystick{}. %% Callback event: {@link wxJoystickEvent}
+
+-record(wxKey,{type :: wxKeyEventType(), %% Callback event: {@link wxKeyEvent}
+ x :: integer(),
+ y :: integer(),
+ keyCode :: integer(),
+ controlDown :: boolean(),
+ shiftDown :: boolean(),
+ altDown :: boolean(),
+ metaDown :: boolean(),
+ scanCode :: boolean(),
+ uniChar :: integer(),
+ rawCode :: integer(),
+ rawFlags :: integer()}).
+-type wxKeyEventType() :: 'char' | 'char_hook' | 'key_down' | 'key_up'.
+-type wxKey() :: #wxKey{}. %% Callback event: {@link wxKeyEvent}
-record(wxList,{type :: wxListEventType(), %% Callback event: {@link wxListEvent}
code :: integer(),
@@ -201,24 +194,23 @@
-type wxListEventType() :: 'command_list_begin_drag' | 'command_list_begin_rdrag' | 'command_list_begin_label_edit' | 'command_list_end_label_edit' | 'command_list_delete_item' | 'command_list_delete_all_items' | 'command_list_key_down' | 'command_list_insert_item' | 'command_list_col_click' | 'command_list_col_right_click' | 'command_list_col_begin_drag' | 'command_list_col_dragging' | 'command_list_col_end_drag' | 'command_list_item_selected' | 'command_list_item_deselected' | 'command_list_item_right_click' | 'command_list_item_middle_click' | 'command_list_item_activated' | 'command_list_item_focused' | 'command_list_cache_hint'.
-type wxList() :: #wxList{}. %% Callback event: {@link wxListEvent}
--record(wxClipboardText, {type :: wxClipboardTextEventType()}). %% Callback event: {@link wxClipboardTextEvent}
--type wxClipboardTextEventType() :: 'command_text_copy' | 'command_text_cut' | 'command_text_paste'.
--type wxClipboardText() :: #wxClipboardText{}. %% Callback event: {@link wxClipboardTextEvent}
+-record(wxMaximize, {type :: wxMaximizeEventType()}). %% Callback event: {@link wxMaximizeEvent}
+-type wxMaximizeEventType() :: 'maximize'.
+-type wxMaximize() :: #wxMaximize{}. %% Callback event: {@link wxMaximizeEvent}
--record(wxScrollWin,{type :: wxScrollWinEventType(), %% Callback event: {@link wxScrollWinEvent}
- commandInt :: integer(),
- extraLong :: integer()}).
--type wxScrollWinEventType() :: 'scrollwin_top' | 'scrollwin_bottom' | 'scrollwin_lineup' | 'scrollwin_linedown' | 'scrollwin_pageup' | 'scrollwin_pagedown' | 'scrollwin_thumbtrack' | 'scrollwin_thumbrelease'.
--type wxScrollWin() :: #wxScrollWin{}. %% Callback event: {@link wxScrollWinEvent}
+-record(wxMenu,{type :: wxMenuEventType(), %% Callback event: {@link wxMenuEvent}
+ menuId :: integer(),
+ menu :: wxMenu:wxMenu()}).
+-type wxMenuEventType() :: 'menu_open' | 'menu_close' | 'menu_highlight'.
+-type wxMenu() :: #wxMenu{}. %% Callback event: {@link wxMenuEvent}
--record(wxIconize,{type :: wxIconizeEventType(), %% Callback event: {@link wxIconizeEvent}
- iconized :: boolean()}).
--type wxIconizeEventType() :: 'iconize'.
--type wxIconize() :: #wxIconize{}. %% Callback event: {@link wxIconizeEvent}
+-record(wxMouseCaptureChanged, {type :: wxMouseCaptureChangedEventType()}). %% Callback event: {@link wxMouseCaptureChangedEvent}
+-type wxMouseCaptureChangedEventType() :: 'mouse_capture_changed'.
+-type wxMouseCaptureChanged() :: #wxMouseCaptureChanged{}. %% Callback event: {@link wxMouseCaptureChangedEvent}
--record(wxUpdateUI, {type :: wxUpdateUIEventType()}). %% Callback event: {@link wxUpdateUIEvent}
--type wxUpdateUIEventType() :: 'update_ui'.
--type wxUpdateUI() :: #wxUpdateUI{}. %% Callback event: {@link wxUpdateUIEvent}
+-record(wxMouseCaptureLost, {type :: wxMouseCaptureLostEventType()}). %% Callback event: {@link wxMouseCaptureLostEvent}
+-type wxMouseCaptureLostEventType() :: 'mouse_capture_lost'.
+-type wxMouseCaptureLost() :: #wxMouseCaptureLost{}. %% Callback event: {@link wxMouseCaptureLostEvent}
-record(wxMouse,{type :: wxMouseEventType(), %% Callback event: {@link wxMouseEvent}
x :: integer(),
@@ -236,16 +228,11 @@
-type wxMouseEventType() :: 'left_down' | 'left_up' | 'middle_down' | 'middle_up' | 'right_down' | 'right_up' | 'motion' | 'enter_window' | 'leave_window' | 'left_dclick' | 'middle_dclick' | 'right_dclick' | 'mousewheel'.
-type wxMouse() :: #wxMouse{}. %% Callback event: {@link wxMouseEvent}
--record(wxTree,{type :: wxTreeEventType(), %% Callback event: {@link wxTreeEvent}
- item :: integer(),
- itemOld :: integer(),
- pointDrag :: {X::integer(), Y::integer()}}).
--type wxTreeEventType() :: 'command_tree_begin_drag' | 'command_tree_begin_rdrag' | 'command_tree_begin_label_edit' | 'command_tree_end_label_edit' | 'command_tree_delete_item' | 'command_tree_get_info' | 'command_tree_set_info' | 'command_tree_item_expanded' | 'command_tree_item_expanding' | 'command_tree_item_collapsed' | 'command_tree_item_collapsing' | 'command_tree_sel_changed' | 'command_tree_sel_changing' | 'command_tree_key_down' | 'command_tree_item_activated' | 'command_tree_item_right_click' | 'command_tree_item_middle_click' | 'command_tree_end_drag' | 'command_tree_state_image_click' | 'command_tree_item_gettooltip' | 'command_tree_item_menu'.
--type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent}
-
--record(wxSysColourChanged, {type :: wxSysColourChangedEventType()}). %% Callback event: {@link wxSysColourChangedEvent}
--type wxSysColourChangedEventType() :: 'sys_colour_changed'.
--type wxSysColourChanged() :: #wxSysColourChanged{}. %% Callback event: {@link wxSysColourChangedEvent}
+-record(wxMove,{type :: wxMoveEventType(), %% Callback event: {@link wxMoveEvent}
+ pos :: {X::integer(), Y::integer()},
+ rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
+-type wxMoveEventType() :: 'move'.
+-type wxMove() :: #wxMove{}. %% Callback event: {@link wxMoveEvent}
-record(wxNavigationKey,{type :: wxNavigationKeyEventType(), %% Callback event: {@link wxNavigationKeyEvent}
flags :: integer(),
@@ -253,103 +240,123 @@
-type wxNavigationKeyEventType() :: 'navigation_key'.
-type wxNavigationKey() :: #wxNavigationKey{}. %% Callback event: {@link wxNavigationKeyEvent}
+-record(wxNotebook,{type :: wxNotebookEventType(), %% Callback event: {@link wxNotebookEvent}
+ nSel :: integer(),
+ nOldSel :: integer()}).
+-type wxNotebookEventType() :: 'command_notebook_page_changed' | 'command_notebook_page_changing'.
+-type wxNotebook() :: #wxNotebook{}. %% Callback event: {@link wxNotebookEvent}
+
+-record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent}
+-type wxPaintEventType() :: 'paint'.
+-type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent}
+
+-record(wxPaletteChanged, {type :: wxPaletteChangedEventType()}). %% Callback event: {@link wxPaletteChangedEvent}
+-type wxPaletteChangedEventType() :: 'palette_changed'.
+-type wxPaletteChanged() :: #wxPaletteChanged{}. %% Callback event: {@link wxPaletteChangedEvent}
+
-record(wxQueryNewPalette, {type :: wxQueryNewPaletteEventType()}). %% Callback event: {@link wxQueryNewPaletteEvent}
-type wxQueryNewPaletteEventType() :: 'query_new_palette'.
-type wxQueryNewPalette() :: #wxQueryNewPalette{}. %% Callback event: {@link wxQueryNewPaletteEvent}
--record(wxMouseCaptureChanged, {type :: wxMouseCaptureChangedEventType()}). %% Callback event: {@link wxMouseCaptureChangedEvent}
--type wxMouseCaptureChangedEventType() :: 'mouse_capture_changed'.
--type wxMouseCaptureChanged() :: #wxMouseCaptureChanged{}. %% Callback event: {@link wxMouseCaptureChangedEvent}
+-record(wxSash,{type :: wxSashEventType(), %% Callback event: {@link wxSashEvent}
+ edge :: wx:wx_enum(),
+ dragRect :: {X::integer(), Y::integer(), W::integer(), H::integer()},
+ dragStatus :: wx:wx_enum()}).
+-type wxSashEventType() :: 'sash_dragged'.
+-type wxSash() :: #wxSash{}. %% Callback event: {@link wxSashEvent}
--record(wxHtmlLink,{type :: wxHtmlLinkEventType(), %% Callback event: {@link wxHtmlLinkEvent}
- linkInfo :: wx:wx_wxHtmlLinkInfo()}).
--type wxHtmlLinkEventType() :: 'command_html_link_clicked'.
--type wxHtmlLink() :: #wxHtmlLink{}. %% Callback event: {@link wxHtmlLinkEvent}
+-record(wxScroll,{type :: wxScrollEventType(), %% Callback event: {@link wxScrollEvent}
+ commandInt :: integer(),
+ extraLong :: integer()}).
+-type wxScrollEventType() :: 'scroll_top' | 'scroll_bottom' | 'scroll_lineup' | 'scroll_linedown' | 'scroll_pageup' | 'scroll_pagedown' | 'scroll_thumbtrack' | 'scroll_thumbrelease' | 'scroll_changed'.
+-type wxScroll() :: #wxScroll{}. %% Callback event: {@link wxScrollEvent}
--record(wxKey,{type :: wxKeyEventType(), %% Callback event: {@link wxKeyEvent}
+-record(wxScrollWin,{type :: wxScrollWinEventType(), %% Callback event: {@link wxScrollWinEvent}
+ commandInt :: integer(),
+ extraLong :: integer()}).
+-type wxScrollWinEventType() :: 'scrollwin_top' | 'scrollwin_bottom' | 'scrollwin_lineup' | 'scrollwin_linedown' | 'scrollwin_pageup' | 'scrollwin_pagedown' | 'scrollwin_thumbtrack' | 'scrollwin_thumbrelease'.
+-type wxScrollWin() :: #wxScrollWin{}. %% Callback event: {@link wxScrollWinEvent}
+
+-record(wxSetCursor,{type :: wxSetCursorEventType(), %% Callback event: {@link wxSetCursorEvent}
x :: integer(),
y :: integer(),
- keyCode :: integer(),
- controlDown :: boolean(),
- shiftDown :: boolean(),
- altDown :: boolean(),
- metaDown :: boolean(),
- scanCode :: boolean(),
- uniChar :: integer(),
- rawCode :: integer(),
- rawFlags :: integer()}).
--type wxKeyEventType() :: 'char' | 'char_hook' | 'key_down' | 'key_up'.
--type wxKey() :: #wxKey{}. %% Callback event: {@link wxKeyEvent}
+ cursor :: wxCursor:wxCursor()}).
+-type wxSetCursorEventType() :: 'set_cursor'.
+-type wxSetCursor() :: #wxSetCursor{}. %% Callback event: {@link wxSetCursorEvent}
+
+-record(wxShow,{type :: wxShowEventType(), %% Callback event: {@link wxShowEvent}
+ show :: boolean()}).
+-type wxShowEventType() :: 'show'.
+-type wxShow() :: #wxShow{}. %% Callback event: {@link wxShowEvent}
+
+-record(wxSize,{type :: wxSizeEventType(), %% Callback event: {@link wxSizeEvent}
+ size :: {W::integer(), H::integer()},
+ rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
+-type wxSizeEventType() :: 'size'.
+-type wxSize() :: #wxSize{}. %% Callback event: {@link wxSizeEvent}
+
+-record(wxSpin,{type :: wxSpinEventType(), %% Callback event: {@link wxSpinEvent}
+ commandInt :: integer()}).
+-type wxSpinEventType() :: 'command_spinctrl_updated' | 'spin_up' | 'spin_down' | 'spin'.
+-type wxSpin() :: #wxSpin{}. %% Callback event: {@link wxSpinEvent}
+
+-record(wxSplitter, {type :: wxSplitterEventType()}). %% Callback event: {@link wxSplitterEvent}
+-type wxSplitterEventType() :: 'command_splitter_sash_pos_changed' | 'command_splitter_sash_pos_changing' | 'command_splitter_doubleclicked' | 'command_splitter_unsplit'.
+-type wxSplitter() :: #wxSplitter{}. %% Callback event: {@link wxSplitterEvent}
+
+-record(wxStyledText,{type :: wxStyledTextEventType(), %% Callback event: {@link wxStyledTextEvent}
+ position :: integer(),
+ key :: integer(),
+ modifiers :: integer(),
+ modificationType :: integer(),
+ text :: unicode:chardata(),
+ length :: integer(),
+ linesAdded :: integer(),
+ line :: integer(),
+ foldLevelNow :: integer(),
+ foldLevelPrev :: integer(),
+ margin :: integer(),
+ message :: integer(),
+ wParam :: integer(),
+ lParam :: integer(),
+ listType :: integer(),
+ x :: integer(),
+ y :: integer(),
+ dragText :: unicode:chardata(),
+ dragAllowMove :: boolean(),
+ dragResult :: wx:wx_enum()}).
+-type wxStyledTextEventType() :: 'stc_change' | 'stc_styleneeded' | 'stc_charadded' | 'stc_savepointreached' | 'stc_savepointleft' | 'stc_romodifyattempt' | 'stc_key' | 'stc_doubleclick' | 'stc_updateui' | 'stc_modified' | 'stc_macrorecord' | 'stc_marginclick' | 'stc_needshown' | 'stc_painted' | 'stc_userlistselection' | 'stc_uridropped' | 'stc_dwellstart' | 'stc_dwellend' | 'stc_start_drag' | 'stc_drag_over' | 'stc_do_drop' | 'stc_zoom' | 'stc_hotspot_click' | 'stc_hotspot_dclick' | 'stc_calltip_click' | 'stc_autocomp_selection'.
+-type wxStyledText() :: #wxStyledText{}. %% Callback event: {@link wxStyledTextEvent}
+
+-record(wxSysColourChanged, {type :: wxSysColourChangedEventType()}). %% Callback event: {@link wxSysColourChangedEvent}
+-type wxSysColourChangedEventType() :: 'sys_colour_changed'.
+-type wxSysColourChanged() :: #wxSysColourChanged{}. %% Callback event: {@link wxSysColourChangedEvent}
-record(wxTaskBarIcon, {type :: wxTaskBarIconEventType()}). %% Callback event: {@link wxTaskBarIconEvent}
-type wxTaskBarIconEventType() :: 'taskbar_move' | 'taskbar_left_down' | 'taskbar_left_up' | 'taskbar_right_down' | 'taskbar_right_up' | 'taskbar_left_dclick' | 'taskbar_right_dclick'.
-type wxTaskBarIcon() :: #wxTaskBarIcon{}. %% Callback event: {@link wxTaskBarIconEvent}
--record(wxGrid,{type :: wxGridEventType(), %% Callback event: {@link wxGridEvent}
- row :: integer(),
- col :: integer(),
- x :: integer(),
- y :: integer(),
- selecting :: boolean(),
- control :: boolean(),
- meta :: boolean(),
- shift :: boolean(),
- alt :: boolean()}).
--type wxGridEventType() :: 'grid_cell_left_click' | 'grid_cell_right_click' | 'grid_cell_left_dclick' | 'grid_cell_right_dclick' | 'grid_label_left_click' | 'grid_label_right_click' | 'grid_label_left_dclick' | 'grid_label_right_dclick' | 'grid_row_size' | 'grid_col_size' | 'grid_range_select' | 'grid_cell_change' | 'grid_select_cell' | 'grid_editor_shown' | 'grid_editor_hidden' | 'grid_editor_created' | 'grid_cell_begin_drag'.
--type wxGrid() :: #wxGrid{}. %% Callback event: {@link wxGridEvent}
+-record(wxTree,{type :: wxTreeEventType(), %% Callback event: {@link wxTreeEvent}
+ item :: integer(),
+ itemOld :: integer(),
+ pointDrag :: {X::integer(), Y::integer()}}).
+-type wxTreeEventType() :: 'command_tree_begin_drag' | 'command_tree_begin_rdrag' | 'command_tree_begin_label_edit' | 'command_tree_end_label_edit' | 'command_tree_delete_item' | 'command_tree_get_info' | 'command_tree_set_info' | 'command_tree_item_expanded' | 'command_tree_item_expanding' | 'command_tree_item_collapsed' | 'command_tree_item_collapsing' | 'command_tree_sel_changed' | 'command_tree_sel_changing' | 'command_tree_key_down' | 'command_tree_item_activated' | 'command_tree_item_right_click' | 'command_tree_item_middle_click' | 'command_tree_end_drag' | 'command_tree_state_image_click' | 'command_tree_item_gettooltip' | 'command_tree_item_menu'.
+-type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent}
--record(wxCalendar,{type :: wxCalendarEventType(), %% Callback event: {@link wxCalendarEvent}
- wday :: wx:wx_enum(),
- date :: wx:wx_datetime()}).
--type wxCalendarEventType() :: 'calendar_sel_changed' | 'calendar_day_changed' | 'calendar_month_changed' | 'calendar_year_changed' | 'calendar_doubleclicked' | 'calendar_weekday_clicked'.
--type wxCalendar() :: #wxCalendar{}. %% Callback event: {@link wxCalendarEvent}
+-record(wxUpdateUI, {type :: wxUpdateUIEventType()}). %% Callback event: {@link wxUpdateUIEvent}
+-type wxUpdateUIEventType() :: 'update_ui'.
+-type wxUpdateUI() :: #wxUpdateUI{}. %% Callback event: {@link wxUpdateUIEvent}
-record(wxWindowCreate, {type :: wxWindowCreateEventType()}). %% Callback event: {@link wxWindowCreateEvent}
-type wxWindowCreateEventType() :: 'create'.
-type wxWindowCreate() :: #wxWindowCreate{}. %% Callback event: {@link wxWindowCreateEvent}
--record(wxDate,{type :: wxDateEventType(), %% Callback event: {@link wxDateEvent}
- date :: wx:wx_datetime()}).
--type wxDateEventType() :: 'date_changed'.
--type wxDate() :: #wxDate{}. %% Callback event: {@link wxDateEvent}
-
--record(wxAuiManager,{type :: wxAuiManagerEventType(), %% Callback event: {@link wxAuiManagerEvent}
- manager :: wxAuiManager:wxAuiManager(),
- pane :: wxAuiPaneInfo:wxAuiPaneInfo(),
- button :: integer(),
- veto_flag :: boolean(),
- canveto_flag :: boolean(),
- dc :: wxDC:wxDC()}).
--type wxAuiManagerEventType() :: 'aui_pane_button' | 'aui_pane_close' | 'aui_pane_maximize' | 'aui_pane_restore' | 'aui_pane_activated' | 'aui_render' | 'aui_find_manager'.
--type wxAuiManager() :: #wxAuiManager{}. %% Callback event: {@link wxAuiManagerEvent}
-
--record(wxJoystick,{type :: wxJoystickEventType(), %% Callback event: {@link wxJoystickEvent}
- pos :: {X::integer(), Y::integer()},
- zPosition :: integer(),
- buttonChange :: integer(),
- buttonState :: integer(),
- joyStick :: integer()}).
--type wxJoystickEventType() :: 'joy_button_down' | 'joy_button_up' | 'joy_move' | 'joy_zmove'.
--type wxJoystick() :: #wxJoystick{}. %% Callback event: {@link wxJoystickEvent}
-
--record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent}
--type wxPaintEventType() :: 'paint'.
--type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent}
-
--record(wxErase,{type :: wxEraseEventType(), %% Callback event: {@link wxEraseEvent}
- dc :: wxDC:wxDC()}).
--type wxEraseEventType() :: 'erase_background'.
--type wxErase() :: #wxErase{}. %% Callback event: {@link wxEraseEvent}
-
--record(wxSetCursor,{type :: wxSetCursorEventType(), %% Callback event: {@link wxSetCursorEvent}
- x :: integer(),
- y :: integer(),
- cursor :: wxCursor:wxCursor()}).
--type wxSetCursorEventType() :: 'set_cursor'.
--type wxSetCursor() :: #wxSetCursor{}. %% Callback event: {@link wxSetCursorEvent}
+-record(wxWindowDestroy, {type :: wxWindowDestroyEventType()}). %% Callback event: {@link wxWindowDestroyEvent}
+-type wxWindowDestroyEventType() :: 'destroy'.
+-type wxWindowDestroy() :: #wxWindowDestroy{}. %% Callback event: {@link wxWindowDestroyEvent}
--type event() :: wxActivate() | wxAuiManager() | wxAuiNotebook() | wxCalendar() | wxChildFocus() | wxClipboardText() | wxClose() | wxColourPicker() | wxCommand() | wxContextMenu() | wxDate() | wxDisplayChanged() | wxErase() | wxFileDirPicker() | wxFocus() | wxFontPicker() | wxGrid() | wxHelp() | wxHtmlLink() | wxIconize() | wxIdle() | wxInitDialog() | wxJoystick() | wxKey() | wxList() | wxMaximize() | wxMenu() | wxMouse() | wxMouseCaptureChanged() | wxMouseCaptureLost() | wxMove() | wxNavigationKey() | wxNotebook() | wxPaint() | wxPaletteChanged() | wxQueryNewPalette() | wxSash() | wxScroll() | wxScrollWin() | wxSetCursor() | wxShow() | wxSize() | wxSpin() | wxSplitter() | wxStyledText() | wxSysColourChanged() | wxTaskBarIcon() | wxTree() | wxUpdateUI() | wxWindowCreate() | wxWindowDestroy().
--type wxEventType() :: wxActivateEventType() | wxAuiManagerEventType() | wxAuiNotebookEventType() | wxCalendarEventType() | wxChildFocusEventType() | wxClipboardTextEventType() | wxCloseEventType() | wxColourPickerEventType() | wxCommandEventType() | wxContextMenuEventType() | wxDateEventType() | wxDisplayChangedEventType() | wxEraseEventType() | wxFileDirPickerEventType() | wxFocusEventType() | wxFontPickerEventType() | wxGridEventType() | wxHelpEventType() | wxHtmlLinkEventType() | wxIconizeEventType() | wxIdleEventType() | wxInitDialogEventType() | wxJoystickEventType() | wxKeyEventType() | wxListEventType() | wxMaximizeEventType() | wxMenuEventType() | wxMouseCaptureChangedEventType() | wxMouseCaptureLostEventType() | wxMouseEventType() | wxMoveEventType() | wxNavigationKeyEventType() | wxNotebookEventType() | wxPaintEventType() | wxPaletteChangedEventType() | wxQueryNewPaletteEventType() | wxSashEventType() | wxScrollEventType() | wxScrollWinEventType() | wxSetCursorEventType() | wxShowEventType() | wxSizeEventType() | wxSpinEventType() | wxSplitterEventType() | wxStyledTextEventType() | wxSysColourChangedEventType() | wxTaskBarIconEventType() | wxTreeEventType() | wxUpdateUIEventType() | wxWindowCreateEventType() | wxWindowDestroyEventType().
+-type event() :: wxActivate() | wxAuiManager() | wxAuiNotebook() | wxCalendar() | wxChildFocus() | wxClipboardText() | wxClose() | wxColourPicker() | wxCommand() | wxContextMenu() | wxDate() | wxDisplayChanged() | wxDropFiles() | wxErase() | wxFileDirPicker() | wxFocus() | wxFontPicker() | wxGrid() | wxHelp() | wxHtmlLink() | wxIconize() | wxIdle() | wxInitDialog() | wxJoystick() | wxKey() | wxList() | wxMaximize() | wxMenu() | wxMouse() | wxMouseCaptureChanged() | wxMouseCaptureLost() | wxMove() | wxNavigationKey() | wxNotebook() | wxPaint() | wxPaletteChanged() | wxQueryNewPalette() | wxSash() | wxScroll() | wxScrollWin() | wxSetCursor() | wxShow() | wxSize() | wxSpin() | wxSplitter() | wxStyledText() | wxSysColourChanged() | wxTaskBarIcon() | wxTree() | wxUpdateUI() | wxWindowCreate() | wxWindowDestroy().
+-type wxEventType() :: wxActivateEventType() | wxAuiManagerEventType() | wxAuiNotebookEventType() | wxCalendarEventType() | wxChildFocusEventType() | wxClipboardTextEventType() | wxCloseEventType() | wxColourPickerEventType() | wxCommandEventType() | wxContextMenuEventType() | wxDateEventType() | wxDisplayChangedEventType() | wxDropFilesEventType() | wxEraseEventType() | wxFileDirPickerEventType() | wxFocusEventType() | wxFontPickerEventType() | wxGridEventType() | wxHelpEventType() | wxHtmlLinkEventType() | wxIconizeEventType() | wxIdleEventType() | wxInitDialogEventType() | wxJoystickEventType() | wxKeyEventType() | wxListEventType() | wxMaximizeEventType() | wxMenuEventType() | wxMouseCaptureChangedEventType() | wxMouseCaptureLostEventType() | wxMouseEventType() | wxMoveEventType() | wxNavigationKeyEventType() | wxNotebookEventType() | wxPaintEventType() | wxPaletteChangedEventType() | wxQueryNewPaletteEventType() | wxSashEventType() | wxScrollEventType() | wxScrollWinEventType() | wxSetCursorEventType() | wxShowEventType() | wxSizeEventType() | wxSpinEventType() | wxSplitterEventType() | wxStyledTextEventType() | wxSysColourChangedEventType() | wxTaskBarIconEventType() | wxTreeEventType() | wxUpdateUIEventType() | wxWindowCreateEventType() | wxWindowDestroyEventType().
%% Hardcoded Records
-record(wxMouseState, {x :: integer(), y :: integer(),
@@ -4085,3 +4092,7 @@
-define(wxWINDOW_VARIANT_MINI, 2).
-define(wxWINDOW_VARIANT_LARGE, 3).
-define(wxWINDOW_VARIANT_MAX, 4).
+% From "xmlres.h": wxXmlResourceFlags
+-define(wxXRC_USE_LOCALE, 1).
+-define(wxXRC_NO_SUBCLASSING, 2).
+-define(wxXRC_NO_RELOADING, 4).
diff --git a/lib/wx/src/gen/gl.erl b/lib/wx/src/gen/gl.erl
index e10b99b10a..4a178ea1e4 100644
--- a/lib/wx/src/gen/gl.erl
+++ b/lib/wx/src/gen/gl.erl
@@ -283,7 +283,7 @@
call(Op, Args) ->
Port = get(opengl_port),
_ = erlang:port_control(Port,Op,Args),
- rec().
+ rec(Op).
%% @hidden
cast(Op, Args) ->
@@ -292,11 +292,15 @@ cast(Op, Args) ->
ok.
%% @hidden
-rec() ->
- receive
+rec(Op) ->
+ receive
{'_egl_result_', Res} -> Res;
- {'_egl_error_', Op, Res} -> error({error,Res,Op})
- end.
+ {'_egl_error_', Op, Res} -> error({error,Res,Op});
+ {'_egl_error_', Other, Res} ->
+ Err = io_lib:format("~p in op: ~p", [Res, Other]),
+ error_logger:error_report([{gl, error}, {message, lists:flatten(Err)}]),
+ rec(Op)
+ end.
%% @hidden
send_bin(Bin) when is_binary(Bin) ->
@@ -2949,8 +2953,9 @@ callList(List) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCallLists.xml">external</a> documentation.
-spec callLists(Lists) -> 'ok' when Lists :: [integer()].
callLists(Lists) ->
- cast(5108, <<(length(Lists)):?GLuint,
- (<< <<C:?GLuint>> || C <- Lists>>)/binary,0:(((1+length(Lists)) rem 2)*32)>>).
+ ListsLen = length(Lists),
+ cast(5108, <<ListsLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Lists>>)/binary,0:(((1+ListsLen) rem 2)*32)>>).
%% @doc set the display-list base for
%%
@@ -6945,8 +6950,9 @@ genTextures(N) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteTextures.xml">external</a> documentation.
-spec deleteTextures(Textures) -> 'ok' when Textures :: [integer()].
deleteTextures(Textures) ->
- cast(5272, <<(length(Textures)):?GLuint,
- (<< <<C:?GLuint>> || C <- Textures>>)/binary,0:(((1+length(Textures)) rem 2)*32)>>).
+ TexturesLen = length(Textures),
+ cast(5272, <<TexturesLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Textures>>)/binary,0:(((1+TexturesLen) rem 2)*32)>>).
%% @doc Bind a named texture to a texturing target
%%
@@ -7026,9 +7032,11 @@ bindTexture(Target,Texture) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPrioritizeTextures.xml">external</a> documentation.
-spec prioritizeTextures(Textures, Priorities) -> 'ok' when Textures :: [integer()],Priorities :: [clamp()].
prioritizeTextures(Textures,Priorities) ->
- cast(5274, <<(length(Textures)):?GLuint,
- (<< <<C:?GLuint>> || C <- Textures>>)/binary,0:(((1+length(Textures)) rem 2)*32),(length(Priorities)):?GLuint,
- (<< <<C:?GLclampf>> || C <- Priorities>>)/binary,0:(((1+length(Priorities)) rem 2)*32)>>).
+ TexturesLen = length(Textures),
+ PrioritiesLen = length(Priorities),
+ cast(5274, <<TexturesLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Textures>>)/binary,0:(((1+TexturesLen) rem 2)*32),PrioritiesLen:?GLuint,
+ (<< <<C:?GLclampf>> || C <- Priorities>>)/binary,0:(((1+PrioritiesLen) rem 2)*32)>>).
%% @doc Determine if textures are loaded in texture memory
%%
@@ -7052,8 +7060,9 @@ prioritizeTextures(Textures,Priorities) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glAreTexturesResident.xml">external</a> documentation.
-spec areTexturesResident(Textures) -> {0|1,Residences :: [0|1]} when Textures :: [integer()].
areTexturesResident(Textures) ->
- call(5275, <<(length(Textures)):?GLuint,
- (<< <<C:?GLuint>> || C <- Textures>>)/binary,0:(((1+length(Textures)) rem 2)*32)>>).
+ TexturesLen = length(Textures),
+ call(5275, <<TexturesLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Textures>>)/binary,0:(((1+TexturesLen) rem 2)*32)>>).
%% @doc Determine if a name corresponds to a texture
%%
@@ -9650,11 +9659,19 @@ blendFuncSeparate(SfactorRGB,DfactorRGB,SfactorAlpha,DfactorAlpha) ->
%%
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiDrawArrays.xml">external</a> documentation.
--spec multiDrawArrays(Mode, First, Count) -> 'ok' when Mode :: enum(),First :: [integer()],Count :: [integer()].
+-spec multiDrawArrays(Mode, First, Count) -> 'ok' when Mode :: enum(),First :: [integer()]|mem(),Count :: [integer()]|mem().
+multiDrawArrays(Mode,First,Count) when is_list(First), is_list(Count) ->
+ FirstLen = length(First),
+ CountLen = length(Count),
+ cast(5395, <<Mode:?GLenum,FirstLen:?GLuint,
+ (<< <<C:?GLint>> || C <- First>>)/binary,0:(((FirstLen) rem 2)*32),CountLen:?GLuint,
+ (<< <<C:?GLsizei>> || C <- Count>>)/binary,0:(((1+CountLen) rem 2)*32)>>);
multiDrawArrays(Mode,First,Count) ->
- cast(5395, <<Mode:?GLenum,(length(First)):?GLuint,
- (<< <<C:?GLint>> || C <- First>>)/binary,0:(((length(First)) rem 2)*32),(length(Count)):?GLuint,
- (<< <<C:?GLsizei>> || C <- Count>>)/binary,0:(((1+length(Count)) rem 2)*32)>>).
+ send_bin(First),
+ FirstLen = byte_size(if is_binary(First) -> First; is_tuple(First) -> element(2, First) end) div 4,
+ send_bin(Count),
+ CountLen = byte_size(if is_binary(Count) -> Count; is_tuple(Count) -> element(2, Count) end) div 4,
+ cast(5396, <<Mode:?GLenum,FirstLen:?GLint,CountLen:?GLsizei>>).
%% @doc Specify point parameters
%%
@@ -9671,26 +9688,26 @@ multiDrawArrays(Mode,First,Count) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPointParameter.xml">external</a> documentation.
-spec pointParameterf(Pname, Param) -> 'ok' when Pname :: enum(),Param :: float().
pointParameterf(Pname,Param) ->
- cast(5396, <<Pname:?GLenum,Param:?GLfloat>>).
+ cast(5397, <<Pname:?GLenum,Param:?GLfloat>>).
%% @doc
%% See {@link pointParameterf/2}
-spec pointParameterfv(Pname, Params) -> 'ok' when Pname :: enum(),Params :: tuple().
pointParameterfv(Pname,Params) ->
- cast(5397, <<Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5398, <<Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>).
%% @doc
%% See {@link pointParameterf/2}
-spec pointParameteri(Pname, Param) -> 'ok' when Pname :: enum(),Param :: integer().
pointParameteri(Pname,Param) ->
- cast(5398, <<Pname:?GLenum,Param:?GLint>>).
+ cast(5399, <<Pname:?GLenum,Param:?GLint>>).
%% @doc
%% See {@link pointParameterf/2}
-spec pointParameteriv(Pname, Params) -> 'ok' when Pname :: enum(),Params :: tuple().
pointParameteriv(Pname,Params) ->
- cast(5399, <<Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5400, <<Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>).
%% @doc Set the current fog coordinates
@@ -9702,7 +9719,7 @@ pointParameteriv(Pname,Params) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFogCoord.xml">external</a> documentation.
-spec fogCoordf(Coord) -> 'ok' when Coord :: float().
fogCoordf(Coord) ->
- cast(5400, <<Coord:?GLfloat>>).
+ cast(5401, <<Coord:?GLfloat>>).
%% @equiv fogCoordf(Coord)
-spec fogCoordfv(Coord) -> 'ok' when Coord :: {Coord :: float()}.
@@ -9712,7 +9729,7 @@ fogCoordfv({Coord}) -> fogCoordf(Coord).
%% See {@link fogCoordf/1}
-spec fogCoordd(Coord) -> 'ok' when Coord :: float().
fogCoordd(Coord) ->
- cast(5401, <<Coord:?GLdouble>>).
+ cast(5402, <<Coord:?GLdouble>>).
%% @equiv fogCoordd(Coord)
-spec fogCoorddv(Coord) -> 'ok' when Coord :: {Coord :: float()}.
@@ -9743,10 +9760,10 @@ fogCoorddv({Coord}) -> fogCoordd(Coord).
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFogCoordPointer.xml">external</a> documentation.
-spec fogCoordPointer(Type, Stride, Pointer) -> 'ok' when Type :: enum(),Stride :: integer(),Pointer :: offset()|mem().
fogCoordPointer(Type,Stride,Pointer) when is_integer(Pointer) ->
- cast(5402, <<Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5403, <<Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
fogCoordPointer(Type,Stride,Pointer) ->
send_bin(Pointer),
- cast(5403, <<Type:?GLenum,Stride:?GLsizei>>).
+ cast(5404, <<Type:?GLenum,Stride:?GLsizei>>).
%% @doc Set the current secondary color
%%
@@ -9780,7 +9797,7 @@ fogCoordPointer(Type,Stride,Pointer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColor.xml">external</a> documentation.
-spec secondaryColor3b(Red, Green, Blue) -> 'ok' when Red :: integer(),Green :: integer(),Blue :: integer().
secondaryColor3b(Red,Green,Blue) ->
- cast(5404, <<Red:?GLbyte,Green:?GLbyte,Blue:?GLbyte>>).
+ cast(5405, <<Red:?GLbyte,Green:?GLbyte,Blue:?GLbyte>>).
%% @equiv secondaryColor3b(Red,Green,Blue)
-spec secondaryColor3bv(V) -> 'ok' when V :: {Red :: integer(),Green :: integer(),Blue :: integer()}.
@@ -9790,7 +9807,7 @@ secondaryColor3bv({Red,Green,Blue}) -> secondaryColor3b(Red,Green,Blue).
%% See {@link secondaryColor3b/3}
-spec secondaryColor3d(Red, Green, Blue) -> 'ok' when Red :: float(),Green :: float(),Blue :: float().
secondaryColor3d(Red,Green,Blue) ->
- cast(5405, <<Red:?GLdouble,Green:?GLdouble,Blue:?GLdouble>>).
+ cast(5406, <<Red:?GLdouble,Green:?GLdouble,Blue:?GLdouble>>).
%% @equiv secondaryColor3d(Red,Green,Blue)
-spec secondaryColor3dv(V) -> 'ok' when V :: {Red :: float(),Green :: float(),Blue :: float()}.
@@ -9800,7 +9817,7 @@ secondaryColor3dv({Red,Green,Blue}) -> secondaryColor3d(Red,Green,Blue).
%% See {@link secondaryColor3b/3}
-spec secondaryColor3f(Red, Green, Blue) -> 'ok' when Red :: float(),Green :: float(),Blue :: float().
secondaryColor3f(Red,Green,Blue) ->
- cast(5406, <<Red:?GLfloat,Green:?GLfloat,Blue:?GLfloat>>).
+ cast(5407, <<Red:?GLfloat,Green:?GLfloat,Blue:?GLfloat>>).
%% @equiv secondaryColor3f(Red,Green,Blue)
-spec secondaryColor3fv(V) -> 'ok' when V :: {Red :: float(),Green :: float(),Blue :: float()}.
@@ -9810,7 +9827,7 @@ secondaryColor3fv({Red,Green,Blue}) -> secondaryColor3f(Red,Green,Blue).
%% See {@link secondaryColor3b/3}
-spec secondaryColor3i(Red, Green, Blue) -> 'ok' when Red :: integer(),Green :: integer(),Blue :: integer().
secondaryColor3i(Red,Green,Blue) ->
- cast(5407, <<Red:?GLint,Green:?GLint,Blue:?GLint>>).
+ cast(5408, <<Red:?GLint,Green:?GLint,Blue:?GLint>>).
%% @equiv secondaryColor3i(Red,Green,Blue)
-spec secondaryColor3iv(V) -> 'ok' when V :: {Red :: integer(),Green :: integer(),Blue :: integer()}.
@@ -9820,7 +9837,7 @@ secondaryColor3iv({Red,Green,Blue}) -> secondaryColor3i(Red,Green,Blue).
%% See {@link secondaryColor3b/3}
-spec secondaryColor3s(Red, Green, Blue) -> 'ok' when Red :: integer(),Green :: integer(),Blue :: integer().
secondaryColor3s(Red,Green,Blue) ->
- cast(5408, <<Red:?GLshort,Green:?GLshort,Blue:?GLshort>>).
+ cast(5409, <<Red:?GLshort,Green:?GLshort,Blue:?GLshort>>).
%% @equiv secondaryColor3s(Red,Green,Blue)
-spec secondaryColor3sv(V) -> 'ok' when V :: {Red :: integer(),Green :: integer(),Blue :: integer()}.
@@ -9830,7 +9847,7 @@ secondaryColor3sv({Red,Green,Blue}) -> secondaryColor3s(Red,Green,Blue).
%% See {@link secondaryColor3b/3}
-spec secondaryColor3ub(Red, Green, Blue) -> 'ok' when Red :: integer(),Green :: integer(),Blue :: integer().
secondaryColor3ub(Red,Green,Blue) ->
- cast(5409, <<Red:?GLubyte,Green:?GLubyte,Blue:?GLubyte>>).
+ cast(5410, <<Red:?GLubyte,Green:?GLubyte,Blue:?GLubyte>>).
%% @equiv secondaryColor3ub(Red,Green,Blue)
-spec secondaryColor3ubv(V) -> 'ok' when V :: {Red :: integer(),Green :: integer(),Blue :: integer()}.
@@ -9840,7 +9857,7 @@ secondaryColor3ubv({Red,Green,Blue}) -> secondaryColor3ub(Red,Green,Blue).
%% See {@link secondaryColor3b/3}
-spec secondaryColor3ui(Red, Green, Blue) -> 'ok' when Red :: integer(),Green :: integer(),Blue :: integer().
secondaryColor3ui(Red,Green,Blue) ->
- cast(5410, <<Red:?GLuint,Green:?GLuint,Blue:?GLuint>>).
+ cast(5411, <<Red:?GLuint,Green:?GLuint,Blue:?GLuint>>).
%% @equiv secondaryColor3ui(Red,Green,Blue)
-spec secondaryColor3uiv(V) -> 'ok' when V :: {Red :: integer(),Green :: integer(),Blue :: integer()}.
@@ -9850,7 +9867,7 @@ secondaryColor3uiv({Red,Green,Blue}) -> secondaryColor3ui(Red,Green,Blue).
%% See {@link secondaryColor3b/3}
-spec secondaryColor3us(Red, Green, Blue) -> 'ok' when Red :: integer(),Green :: integer(),Blue :: integer().
secondaryColor3us(Red,Green,Blue) ->
- cast(5411, <<Red:?GLushort,Green:?GLushort,Blue:?GLushort>>).
+ cast(5412, <<Red:?GLushort,Green:?GLushort,Blue:?GLushort>>).
%% @equiv secondaryColor3us(Red,Green,Blue)
-spec secondaryColor3usv(V) -> 'ok' when V :: {Red :: integer(),Green :: integer(),Blue :: integer()}.
@@ -9883,10 +9900,10 @@ secondaryColor3usv({Red,Green,Blue}) -> secondaryColor3us(Red,Green,Blue).
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColorPointer.xml">external</a> documentation.
-spec secondaryColorPointer(Size, Type, Stride, Pointer) -> 'ok' when Size :: integer(),Type :: enum(),Stride :: integer(),Pointer :: offset()|mem().
secondaryColorPointer(Size,Type,Stride,Pointer) when is_integer(Pointer) ->
- cast(5412, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5413, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
secondaryColorPointer(Size,Type,Stride,Pointer) ->
send_bin(Pointer),
- cast(5413, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
+ cast(5414, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
%% @doc Specify the raster position in window coordinates for pixel operations
%%
@@ -9929,7 +9946,7 @@ secondaryColorPointer(Size,Type,Stride,Pointer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos.xml">external</a> documentation.
-spec windowPos2d(X, Y) -> 'ok' when X :: float(),Y :: float().
windowPos2d(X,Y) ->
- cast(5414, <<X:?GLdouble,Y:?GLdouble>>).
+ cast(5415, <<X:?GLdouble,Y:?GLdouble>>).
%% @equiv windowPos2d(X,Y)
-spec windowPos2dv(V) -> 'ok' when V :: {X :: float(),Y :: float()}.
@@ -9939,7 +9956,7 @@ windowPos2dv({X,Y}) -> windowPos2d(X,Y).
%% See {@link windowPos2d/2}
-spec windowPos2f(X, Y) -> 'ok' when X :: float(),Y :: float().
windowPos2f(X,Y) ->
- cast(5415, <<X:?GLfloat,Y:?GLfloat>>).
+ cast(5416, <<X:?GLfloat,Y:?GLfloat>>).
%% @equiv windowPos2f(X,Y)
-spec windowPos2fv(V) -> 'ok' when V :: {X :: float(),Y :: float()}.
@@ -9949,7 +9966,7 @@ windowPos2fv({X,Y}) -> windowPos2f(X,Y).
%% See {@link windowPos2d/2}
-spec windowPos2i(X, Y) -> 'ok' when X :: integer(),Y :: integer().
windowPos2i(X,Y) ->
- cast(5416, <<X:?GLint,Y:?GLint>>).
+ cast(5417, <<X:?GLint,Y:?GLint>>).
%% @equiv windowPos2i(X,Y)
-spec windowPos2iv(V) -> 'ok' when V :: {X :: integer(),Y :: integer()}.
@@ -9959,7 +9976,7 @@ windowPos2iv({X,Y}) -> windowPos2i(X,Y).
%% See {@link windowPos2d/2}
-spec windowPos2s(X, Y) -> 'ok' when X :: integer(),Y :: integer().
windowPos2s(X,Y) ->
- cast(5417, <<X:?GLshort,Y:?GLshort>>).
+ cast(5418, <<X:?GLshort,Y:?GLshort>>).
%% @equiv windowPos2s(X,Y)
-spec windowPos2sv(V) -> 'ok' when V :: {X :: integer(),Y :: integer()}.
@@ -9969,7 +9986,7 @@ windowPos2sv({X,Y}) -> windowPos2s(X,Y).
%% See {@link windowPos2d/2}
-spec windowPos3d(X, Y, Z) -> 'ok' when X :: float(),Y :: float(),Z :: float().
windowPos3d(X,Y,Z) ->
- cast(5418, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5419, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @equiv windowPos3d(X,Y,Z)
-spec windowPos3dv(V) -> 'ok' when V :: {X :: float(),Y :: float(),Z :: float()}.
@@ -9979,7 +9996,7 @@ windowPos3dv({X,Y,Z}) -> windowPos3d(X,Y,Z).
%% See {@link windowPos2d/2}
-spec windowPos3f(X, Y, Z) -> 'ok' when X :: float(),Y :: float(),Z :: float().
windowPos3f(X,Y,Z) ->
- cast(5419, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
+ cast(5420, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
%% @equiv windowPos3f(X,Y,Z)
-spec windowPos3fv(V) -> 'ok' when V :: {X :: float(),Y :: float(),Z :: float()}.
@@ -9989,7 +10006,7 @@ windowPos3fv({X,Y,Z}) -> windowPos3f(X,Y,Z).
%% See {@link windowPos2d/2}
-spec windowPos3i(X, Y, Z) -> 'ok' when X :: integer(),Y :: integer(),Z :: integer().
windowPos3i(X,Y,Z) ->
- cast(5420, <<X:?GLint,Y:?GLint,Z:?GLint>>).
+ cast(5421, <<X:?GLint,Y:?GLint,Z:?GLint>>).
%% @equiv windowPos3i(X,Y,Z)
-spec windowPos3iv(V) -> 'ok' when V :: {X :: integer(),Y :: integer(),Z :: integer()}.
@@ -9999,7 +10016,7 @@ windowPos3iv({X,Y,Z}) -> windowPos3i(X,Y,Z).
%% See {@link windowPos2d/2}
-spec windowPos3s(X, Y, Z) -> 'ok' when X :: integer(),Y :: integer(),Z :: integer().
windowPos3s(X,Y,Z) ->
- cast(5421, <<X:?GLshort,Y:?GLshort,Z:?GLshort>>).
+ cast(5422, <<X:?GLshort,Y:?GLshort,Z:?GLshort>>).
%% @equiv windowPos3s(X,Y,Z)
-spec windowPos3sv(V) -> 'ok' when V :: {X :: integer(),Y :: integer(),Z :: integer()}.
@@ -10020,7 +10037,7 @@ windowPos3sv({X,Y,Z}) -> windowPos3s(X,Y,Z).
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenQueries.xml">external</a> documentation.
-spec genQueries(N) -> [integer()] when N :: integer().
genQueries(N) ->
- call(5422, <<N:?GLsizei>>).
+ call(5423, <<N:?GLsizei>>).
%% @doc Delete named query objects
%%
@@ -10034,8 +10051,9 @@ genQueries(N) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteQueries.xml">external</a> documentation.
-spec deleteQueries(Ids) -> 'ok' when Ids :: [integer()].
deleteQueries(Ids) ->
- cast(5423, <<(length(Ids)):?GLuint,
- (<< <<C:?GLuint>> || C <- Ids>>)/binary,0:(((1+length(Ids)) rem 2)*32)>>).
+ IdsLen = length(Ids),
+ cast(5424, <<IdsLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Ids>>)/binary,0:(((1+IdsLen) rem 2)*32)>>).
%% @doc Determine if a name corresponds to a query object
%%
@@ -10049,7 +10067,7 @@ deleteQueries(Ids) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsQuery.xml">external</a> documentation.
-spec isQuery(Id) -> 0|1 when Id :: integer().
isQuery(Id) ->
- call(5424, <<Id:?GLuint>>).
+ call(5425, <<Id:?GLuint>>).
%% @doc Delimit the boundaries of a query object
%%
@@ -10116,20 +10134,20 @@ isQuery(Id) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBeginQuery.xml">external</a> documentation.
-spec beginQuery(Target, Id) -> 'ok' when Target :: enum(),Id :: integer().
beginQuery(Target,Id) ->
- cast(5425, <<Target:?GLenum,Id:?GLuint>>).
+ cast(5426, <<Target:?GLenum,Id:?GLuint>>).
%% @doc
%% See {@link beginQuery/2}
-spec endQuery(Target) -> 'ok' when Target :: enum().
endQuery(Target) ->
- cast(5426, <<Target:?GLenum>>).
+ cast(5427, <<Target:?GLenum>>).
%% @doc glGetQuery
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQuery.xml">external</a> documentation.
-spec getQueryiv(Target, Pname) -> integer() when Target :: enum(),Pname :: enum().
getQueryiv(Target,Pname) ->
- call(5427, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5428, <<Target:?GLenum,Pname:?GLenum>>).
%% @doc Return parameters of a query object
%%
@@ -10149,13 +10167,13 @@ getQueryiv(Target,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQueryObject.xml">external</a> documentation.
-spec getQueryObjectiv(Id, Pname) -> integer() when Id :: integer(),Pname :: enum().
getQueryObjectiv(Id,Pname) ->
- call(5428, <<Id:?GLuint,Pname:?GLenum>>).
+ call(5429, <<Id:?GLuint,Pname:?GLenum>>).
%% @doc
%% See {@link getQueryObjectiv/2}
-spec getQueryObjectuiv(Id, Pname) -> integer() when Id :: integer(),Pname :: enum().
getQueryObjectuiv(Id,Pname) ->
- call(5429, <<Id:?GLuint,Pname:?GLenum>>).
+ call(5430, <<Id:?GLuint,Pname:?GLenum>>).
%% @doc Bind a named buffer object
%%
@@ -10238,7 +10256,7 @@ getQueryObjectuiv(Id,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindBuffer.xml">external</a> documentation.
-spec bindBuffer(Target, Buffer) -> 'ok' when Target :: enum(),Buffer :: integer().
bindBuffer(Target,Buffer) ->
- cast(5430, <<Target:?GLenum,Buffer:?GLuint>>).
+ cast(5431, <<Target:?GLenum,Buffer:?GLuint>>).
%% @doc Delete named buffer objects
%%
@@ -10253,8 +10271,9 @@ bindBuffer(Target,Buffer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteBuffers.xml">external</a> documentation.
-spec deleteBuffers(Buffers) -> 'ok' when Buffers :: [integer()].
deleteBuffers(Buffers) ->
- cast(5431, <<(length(Buffers)):?GLuint,
- (<< <<C:?GLuint>> || C <- Buffers>>)/binary,0:(((1+length(Buffers)) rem 2)*32)>>).
+ BuffersLen = length(Buffers),
+ cast(5432, <<BuffersLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Buffers>>)/binary,0:(((1+BuffersLen) rem 2)*32)>>).
%% @doc Generate buffer object names
%%
@@ -10272,7 +10291,7 @@ deleteBuffers(Buffers) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenBuffers.xml">external</a> documentation.
-spec genBuffers(N) -> [integer()] when N :: integer().
genBuffers(N) ->
- call(5432, <<N:?GLsizei>>).
+ call(5433, <<N:?GLsizei>>).
%% @doc Determine if a name corresponds to a buffer object
%%
@@ -10287,7 +10306,7 @@ genBuffers(N) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsBuffer.xml">external</a> documentation.
-spec isBuffer(Buffer) -> 0|1 when Buffer :: integer().
isBuffer(Buffer) ->
- call(5433, <<Buffer:?GLuint>>).
+ call(5434, <<Buffer:?GLuint>>).
%% @doc Creates and initializes a buffer object's data store
%%
@@ -10325,10 +10344,10 @@ isBuffer(Buffer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBufferData.xml">external</a> documentation.
-spec bufferData(Target, Size, Data, Usage) -> 'ok' when Target :: enum(),Size :: integer(),Data :: offset()|mem(),Usage :: enum().
bufferData(Target,Size,Data,Usage) when is_integer(Data) ->
- cast(5434, <<Target:?GLenum,0:32,Size:?GLsizeiptr,Data:?GLuint,Usage:?GLenum>>);
+ cast(5435, <<Target:?GLenum,0:32,Size:?GLsizeiptr,Data:?GLuint,Usage:?GLenum>>);
bufferData(Target,Size,Data,Usage) ->
send_bin(Data),
- cast(5435, <<Target:?GLenum,0:32,Size:?GLsizeiptr,Usage:?GLenum>>).
+ cast(5436, <<Target:?GLenum,0:32,Size:?GLsizeiptr,Usage:?GLenum>>).
%% @doc Updates a subset of a buffer object's data store
%%
@@ -10341,10 +10360,10 @@ bufferData(Target,Size,Data,Usage) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBufferSubData.xml">external</a> documentation.
-spec bufferSubData(Target, Offset, Size, Data) -> 'ok' when Target :: enum(),Offset :: integer(),Size :: integer(),Data :: offset()|mem().
bufferSubData(Target,Offset,Size,Data) when is_integer(Data) ->
- cast(5436, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr,Data:?GLuint>>);
+ cast(5437, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr,Data:?GLuint>>);
bufferSubData(Target,Offset,Size,Data) ->
send_bin(Data),
- cast(5437, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
+ cast(5438, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
%% @doc Returns a subset of a buffer object's data store
%%
@@ -10358,7 +10377,7 @@ bufferSubData(Target,Offset,Size,Data) ->
-spec getBufferSubData(Target, Offset, Size, Data) -> 'ok' when Target :: enum(),Offset :: integer(),Size :: integer(),Data :: mem().
getBufferSubData(Target,Offset,Size,Data) ->
send_bin(Data),
- call(5438, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
+ call(5439, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
%% @doc Return parameters of a buffer object
%%
@@ -10382,7 +10401,7 @@ getBufferSubData(Target,Offset,Size,Data) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetBufferParameteriv.xml">external</a> documentation.
-spec getBufferParameteriv(Target, Pname) -> integer() when Target :: enum(),Pname :: enum().
getBufferParameteriv(Target,Pname) ->
- call(5439, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5440, <<Target:?GLenum,Pname:?GLenum>>).
%% @doc Set the RGB blend equation and the alpha blend equation separately
%%
@@ -10424,7 +10443,7 @@ getBufferParameteriv(Target,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendEquationSeparate.xml">external</a> documentation.
-spec blendEquationSeparate(ModeRGB, ModeAlpha) -> 'ok' when ModeRGB :: enum(),ModeAlpha :: enum().
blendEquationSeparate(ModeRGB,ModeAlpha) ->
- cast(5440, <<ModeRGB:?GLenum,ModeAlpha:?GLenum>>).
+ cast(5441, <<ModeRGB:?GLenum,ModeAlpha:?GLenum>>).
%% @doc Specifies a list of color buffers to be drawn into
%%
@@ -10464,8 +10483,9 @@ blendEquationSeparate(ModeRGB,ModeAlpha) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawBuffers.xml">external</a> documentation.
-spec drawBuffers(Bufs) -> 'ok' when Bufs :: [enum()].
drawBuffers(Bufs) ->
- cast(5441, <<(length(Bufs)):?GLuint,
- (<< <<C:?GLenum>> || C <- Bufs>>)/binary,0:(((1+length(Bufs)) rem 2)*32)>>).
+ BufsLen = length(Bufs),
+ cast(5442, <<BufsLen:?GLuint,
+ (<< <<C:?GLenum>> || C <- Bufs>>)/binary,0:(((1+BufsLen) rem 2)*32)>>).
%% @doc Set front and/or back stencil test actions
%%
@@ -10526,7 +10546,7 @@ drawBuffers(Bufs) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilOpSeparate.xml">external</a> documentation.
-spec stencilOpSeparate(Face, Sfail, Dpfail, Dppass) -> 'ok' when Face :: enum(),Sfail :: enum(),Dpfail :: enum(),Dppass :: enum().
stencilOpSeparate(Face,Sfail,Dpfail,Dppass) ->
- cast(5442, <<Face:?GLenum,Sfail:?GLenum,Dpfail:?GLenum,Dppass:?GLenum>>).
+ cast(5443, <<Face:?GLenum,Sfail:?GLenum,Dpfail:?GLenum,Dppass:?GLenum>>).
%% @doc Set front and/or back function and reference value for stencil testing
%%
@@ -10589,7 +10609,7 @@ stencilOpSeparate(Face,Sfail,Dpfail,Dppass) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilFuncSeparate.xml">external</a> documentation.
-spec stencilFuncSeparate(Face, Func, Ref, Mask) -> 'ok' when Face :: enum(),Func :: enum(),Ref :: integer(),Mask :: integer().
stencilFuncSeparate(Face,Func,Ref,Mask) ->
- cast(5443, <<Face:?GLenum,Func:?GLenum,Ref:?GLint,Mask:?GLuint>>).
+ cast(5444, <<Face:?GLenum,Func:?GLenum,Ref:?GLint,Mask:?GLuint>>).
%% @doc Control the front and/or back writing of individual bits in the stencil planes
%%
@@ -10607,7 +10627,7 @@ stencilFuncSeparate(Face,Func,Ref,Mask) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilMaskSeparate.xml">external</a> documentation.
-spec stencilMaskSeparate(Face, Mask) -> 'ok' when Face :: enum(),Mask :: integer().
stencilMaskSeparate(Face,Mask) ->
- cast(5444, <<Face:?GLenum,Mask:?GLuint>>).
+ cast(5445, <<Face:?GLenum,Mask:?GLuint>>).
%% @doc Attaches a shader object to a program object
%%
@@ -10631,7 +10651,7 @@ stencilMaskSeparate(Face,Mask) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glAttachShader.xml">external</a> documentation.
-spec attachShader(Program, Shader) -> 'ok' when Program :: integer(),Shader :: integer().
attachShader(Program,Shader) ->
- cast(5445, <<Program:?GLuint,Shader:?GLuint>>).
+ cast(5446, <<Program:?GLuint,Shader:?GLuint>>).
%% @doc Associates a generic vertex attribute index with a named attribute variable
%%
@@ -10668,7 +10688,8 @@ attachShader(Program,Shader) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindAttribLocation.xml">external</a> documentation.
-spec bindAttribLocation(Program, Index, Name) -> 'ok' when Program :: integer(),Index :: integer(),Name :: string().
bindAttribLocation(Program,Index,Name) ->
- cast(5446, <<Program:?GLuint,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ cast(5447, <<Program:?GLuint,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 1) rem 8)) rem 8)>>).
%% @doc Compiles a shader object
%%
@@ -10688,7 +10709,7 @@ bindAttribLocation(Program,Index,Name) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompileShader.xml">external</a> documentation.
-spec compileShader(Shader) -> 'ok' when Shader :: integer().
compileShader(Shader) ->
- cast(5447, <<Shader:?GLuint>>).
+ cast(5448, <<Shader:?GLuint>>).
%% @doc Creates a program object
%%
@@ -10711,7 +10732,7 @@ compileShader(Shader) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateProgram.xml">external</a> documentation.
-spec createProgram() -> integer().
createProgram() ->
- call(5448, <<>>).
+ call(5449, <<>>).
%% @doc Creates a shader object
%%
@@ -10734,7 +10755,7 @@ createProgram() ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateShader.xml">external</a> documentation.
-spec createShader(Type) -> integer() when Type :: enum().
createShader(Type) ->
- call(5449, <<Type:?GLenum>>).
+ call(5450, <<Type:?GLenum>>).
%% @doc Deletes a program object
%%
@@ -10755,7 +10776,7 @@ createShader(Type) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteProgram.xml">external</a> documentation.
-spec deleteProgram(Program) -> 'ok' when Program :: integer().
deleteProgram(Program) ->
- cast(5450, <<Program:?GLuint>>).
+ cast(5451, <<Program:?GLuint>>).
%% @doc Deletes a shader object
%%
@@ -10774,7 +10795,7 @@ deleteProgram(Program) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteShader.xml">external</a> documentation.
-spec deleteShader(Shader) -> 'ok' when Shader :: integer().
deleteShader(Shader) ->
- cast(5451, <<Shader:?GLuint>>).
+ cast(5452, <<Shader:?GLuint>>).
%% @doc Detaches a shader object from a program object to which it is attached
%%
@@ -10789,7 +10810,7 @@ deleteShader(Shader) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDetachShader.xml">external</a> documentation.
-spec detachShader(Program, Shader) -> 'ok' when Program :: integer(),Shader :: integer().
detachShader(Program,Shader) ->
- cast(5452, <<Program:?GLuint,Shader:?GLuint>>).
+ cast(5453, <<Program:?GLuint,Shader:?GLuint>>).
%% @doc Enable or disable a generic vertex attribute array
%%
@@ -10804,13 +10825,13 @@ detachShader(Program,Shader) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEnableVertexAttribArray.xml">external</a> documentation.
-spec disableVertexAttribArray(Index) -> 'ok' when Index :: integer().
disableVertexAttribArray(Index) ->
- cast(5453, <<Index:?GLuint>>).
+ cast(5454, <<Index:?GLuint>>).
%% @doc
%% See {@link disableVertexAttribArray/1}
-spec enableVertexAttribArray(Index) -> 'ok' when Index :: integer().
enableVertexAttribArray(Index) ->
- cast(5454, <<Index:?GLuint>>).
+ cast(5455, <<Index:?GLuint>>).
%% @doc Returns information about an active attribute variable for the specified program object
%%
@@ -10868,7 +10889,7 @@ enableVertexAttribArray(Index) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveAttrib.xml">external</a> documentation.
-spec getActiveAttrib(Program, Index, BufSize) -> {Size :: integer(),Type :: enum(),Name :: string()} when Program :: integer(),Index :: integer(),BufSize :: integer().
getActiveAttrib(Program,Index,BufSize) ->
- call(5455, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
+ call(5456, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
%% @doc Returns information about an active uniform variable for the specified program object
%%
@@ -11015,7 +11036,7 @@ getActiveAttrib(Program,Index,BufSize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniform.xml">external</a> documentation.
-spec getActiveUniform(Program, Index, BufSize) -> {Size :: integer(),Type :: enum(),Name :: string()} when Program :: integer(),Index :: integer(),BufSize :: integer().
getActiveUniform(Program,Index,BufSize) ->
- call(5456, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
+ call(5457, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
%% @doc Returns the handles of the shader objects attached to a program object
%%
@@ -11035,7 +11056,7 @@ getActiveUniform(Program,Index,BufSize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetAttachedShaders.xml">external</a> documentation.
-spec getAttachedShaders(Program, MaxCount) -> [integer()] when Program :: integer(),MaxCount :: integer().
getAttachedShaders(Program,MaxCount) ->
- call(5457, <<Program:?GLuint,MaxCount:?GLsizei>>).
+ call(5458, <<Program:?GLuint,MaxCount:?GLsizei>>).
%% @doc Returns the location of an attribute variable
%%
@@ -11059,7 +11080,8 @@ getAttachedShaders(Program,MaxCount) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetAttribLocation.xml">external</a> documentation.
-spec getAttribLocation(Program, Name) -> integer() when Program :: integer(),Name :: string().
getAttribLocation(Program,Name) ->
- call(5458, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ call(5459, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 5) rem 8)) rem 8)>>).
%% @doc Returns a parameter from a program object
%%
@@ -11130,7 +11152,7 @@ getAttribLocation(Program,Name) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgram.xml">external</a> documentation.
-spec getProgramiv(Program, Pname) -> integer() when Program :: integer(),Pname :: enum().
getProgramiv(Program,Pname) ->
- call(5459, <<Program:?GLuint,Pname:?GLenum>>).
+ call(5460, <<Program:?GLuint,Pname:?GLenum>>).
%% @doc Returns the information log for a program object
%%
@@ -11155,7 +11177,7 @@ getProgramiv(Program,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramInfoLog.xml">external</a> documentation.
-spec getProgramInfoLog(Program, BufSize) -> string() when Program :: integer(),BufSize :: integer().
getProgramInfoLog(Program,BufSize) ->
- call(5460, <<Program:?GLuint,BufSize:?GLsizei>>).
+ call(5461, <<Program:?GLuint,BufSize:?GLsizei>>).
%% @doc Returns a parameter from a shader object
%%
@@ -11186,7 +11208,7 @@ getProgramInfoLog(Program,BufSize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetShader.xml">external</a> documentation.
-spec getShaderiv(Shader, Pname) -> integer() when Shader :: integer(),Pname :: enum().
getShaderiv(Shader,Pname) ->
- call(5461, <<Shader:?GLuint,Pname:?GLenum>>).
+ call(5462, <<Shader:?GLuint,Pname:?GLenum>>).
%% @doc Returns the information log for a shader object
%%
@@ -11209,7 +11231,7 @@ getShaderiv(Shader,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetShaderInfoLog.xml">external</a> documentation.
-spec getShaderInfoLog(Shader, BufSize) -> string() when Shader :: integer(),BufSize :: integer().
getShaderInfoLog(Shader,BufSize) ->
- call(5462, <<Shader:?GLuint,BufSize:?GLsizei>>).
+ call(5463, <<Shader:?GLuint,BufSize:?GLsizei>>).
%% @doc Returns the source code string from a shader object
%%
@@ -11229,7 +11251,7 @@ getShaderInfoLog(Shader,BufSize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetShaderSource.xml">external</a> documentation.
-spec getShaderSource(Shader, BufSize) -> string() when Shader :: integer(),BufSize :: integer().
getShaderSource(Shader,BufSize) ->
- call(5463, <<Shader:?GLuint,BufSize:?GLsizei>>).
+ call(5464, <<Shader:?GLuint,BufSize:?GLsizei>>).
%% @doc Returns the location of a uniform variable
%%
@@ -11262,7 +11284,8 @@ getShaderSource(Shader,BufSize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformLocation.xml">external</a> documentation.
-spec getUniformLocation(Program, Name) -> integer() when Program :: integer(),Name :: string().
getUniformLocation(Program,Name) ->
- call(5464, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ call(5465, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 5) rem 8)) rem 8)>>).
%% @doc Returns the value of a uniform variable
%%
@@ -11288,13 +11311,13 @@ getUniformLocation(Program,Name) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniform.xml">external</a> documentation.
-spec getUniformfv(Program, Location) -> matrix() when Program :: integer(),Location :: integer().
getUniformfv(Program,Location) ->
- call(5465, <<Program:?GLuint,Location:?GLint>>).
+ call(5466, <<Program:?GLuint,Location:?GLint>>).
%% @doc
%% See {@link getUniformfv/2}
-spec getUniformiv(Program, Location) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()} when Program :: integer(),Location :: integer().
getUniformiv(Program,Location) ->
- call(5466, <<Program:?GLuint,Location:?GLint>>).
+ call(5467, <<Program:?GLuint,Location:?GLint>>).
%% @doc Return a generic vertex attribute parameter
%%
@@ -11361,19 +11384,19 @@ getUniformiv(Program,Location) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetVertexAttrib.xml">external</a> documentation.
-spec getVertexAttribdv(Index, Pname) -> {float(),float(),float(),float()} when Index :: integer(),Pname :: enum().
getVertexAttribdv(Index,Pname) ->
- call(5467, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5468, <<Index:?GLuint,Pname:?GLenum>>).
%% @doc
%% See {@link getVertexAttribdv/2}
-spec getVertexAttribfv(Index, Pname) -> {float(),float(),float(),float()} when Index :: integer(),Pname :: enum().
getVertexAttribfv(Index,Pname) ->
- call(5468, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5469, <<Index:?GLuint,Pname:?GLenum>>).
%% @doc
%% See {@link getVertexAttribdv/2}
-spec getVertexAttribiv(Index, Pname) -> {integer(),integer(),integer(),integer()} when Index :: integer(),Pname :: enum().
getVertexAttribiv(Index,Pname) ->
- call(5469, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5470, <<Index:?GLuint,Pname:?GLenum>>).
%% @doc Determines if a name corresponds to a program object
%%
@@ -11385,7 +11408,7 @@ getVertexAttribiv(Index,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsProgram.xml">external</a> documentation.
-spec isProgram(Program) -> 0|1 when Program :: integer().
isProgram(Program) ->
- call(5470, <<Program:?GLuint>>).
+ call(5471, <<Program:?GLuint>>).
%% @doc Determines if a name corresponds to a shader object
%%
@@ -11397,7 +11420,7 @@ isProgram(Program) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsShader.xml">external</a> documentation.
-spec isShader(Shader) -> 0|1 when Shader :: integer().
isShader(Shader) ->
- call(5471, <<Shader:?GLuint>>).
+ call(5472, <<Shader:?GLuint>>).
%% @doc Links a program object
%%
@@ -11517,7 +11540,7 @@ isShader(Shader) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLinkProgram.xml">external</a> documentation.
-spec linkProgram(Program) -> 'ok' when Program :: integer().
linkProgram(Program) ->
- cast(5472, <<Program:?GLuint>>).
+ cast(5473, <<Program:?GLuint>>).
%% @doc Replaces the source code in a shader object
%%
@@ -11535,8 +11558,9 @@ linkProgram(Program) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glShaderSource.xml">external</a> documentation.
-spec shaderSource(Shader, String) -> 'ok' when Shader :: integer(),String :: iolist().
shaderSource(Shader,String) ->
- StringTemp = list_to_binary([[Str|[0]] || Str <- String ]),
- cast(5473, <<Shader:?GLuint,(length(String)):?GLuint,(size(StringTemp)):?GLuint,(StringTemp)/binary,0:((8-((size(StringTemp)+0) rem 8)) rem 8)>>).
+ StringTemp = list_to_binary([[Str|[0]] || Str <- String ]),
+ StringLen = length(String),
+ cast(5474, <<Shader:?GLuint,StringLen:?GLuint,(size(StringTemp)):?GLuint,(StringTemp)/binary,0:((8-((size(StringTemp)+0) rem 8)) rem 8)>>).
%% @doc Installs a program object as part of current rendering state
%%
@@ -11577,7 +11601,7 @@ shaderSource(Shader,String) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUseProgram.xml">external</a> documentation.
-spec useProgram(Program) -> 'ok' when Program :: integer().
useProgram(Program) ->
- cast(5474, <<Program:?GLuint>>).
+ cast(5475, <<Program:?GLuint>>).
%% @doc Specify the value of a uniform variable for the current program object
%%
@@ -11645,125 +11669,136 @@ useProgram(Program) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
-spec uniform1f(Location, V0) -> 'ok' when Location :: integer(),V0 :: float().
uniform1f(Location,V0) ->
- cast(5475, <<Location:?GLint,V0:?GLfloat>>).
+ cast(5476, <<Location:?GLint,V0:?GLfloat>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform2f(Location, V0, V1) -> 'ok' when Location :: integer(),V0 :: float(),V1 :: float().
uniform2f(Location,V0,V1) ->
- cast(5476, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat>>).
+ cast(5477, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform3f(Location, V0, V1, V2) -> 'ok' when Location :: integer(),V0 :: float(),V1 :: float(),V2 :: float().
uniform3f(Location,V0,V1,V2) ->
- cast(5477, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat>>).
+ cast(5478, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform4f(Location, V0, V1, V2, V3) -> 'ok' when Location :: integer(),V0 :: float(),V1 :: float(),V2 :: float(),V3 :: float().
uniform4f(Location,V0,V1,V2,V3) ->
- cast(5478, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>>).
+ cast(5479, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform1i(Location, V0) -> 'ok' when Location :: integer(),V0 :: integer().
uniform1i(Location,V0) ->
- cast(5479, <<Location:?GLint,V0:?GLint>>).
+ cast(5480, <<Location:?GLint,V0:?GLint>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform2i(Location, V0, V1) -> 'ok' when Location :: integer(),V0 :: integer(),V1 :: integer().
uniform2i(Location,V0,V1) ->
- cast(5480, <<Location:?GLint,V0:?GLint,V1:?GLint>>).
+ cast(5481, <<Location:?GLint,V0:?GLint,V1:?GLint>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform3i(Location, V0, V1, V2) -> 'ok' when Location :: integer(),V0 :: integer(),V1 :: integer(),V2 :: integer().
uniform3i(Location,V0,V1,V2) ->
- cast(5481, <<Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint>>).
+ cast(5482, <<Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform4i(Location, V0, V1, V2, V3) -> 'ok' when Location :: integer(),V0 :: integer(),V1 :: integer(),V2 :: integer(),V3 :: integer().
uniform4i(Location,V0,V1,V2,V3) ->
- cast(5482, <<Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint,V3:?GLint>>).
+ cast(5483, <<Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint,V3:?GLint>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform1fv(Location, Value) -> 'ok' when Location :: integer(),Value :: [float()].
uniform1fv(Location,Value) ->
- cast(5483, <<Location:?GLint,(length(Value)):?GLuint,
- (<< <<C:?GLfloat>> || C <- Value>>)/binary,0:(((length(Value)) rem 2)*32)>>).
+ ValueLen = length(Value),
+ cast(5484, <<Location:?GLint,ValueLen:?GLuint,
+ (<< <<C:?GLfloat>> || C <- Value>>)/binary,0:(((ValueLen) rem 2)*32)>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform2fv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{float(),float()}].
uniform2fv(Location,Value) ->
- cast(5484, <<Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5485, <<Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat>> || {V1,V2} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform3fv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{float(),float(),float()}].
uniform3fv(Location,Value) ->
- cast(5485, <<Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5486, <<Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>> || {V1,V2,V3} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform4fv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{float(),float(),float(),float()}].
uniform4fv(Location,Value) ->
- cast(5486, <<Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5487, <<Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform1iv(Location, Value) -> 'ok' when Location :: integer(),Value :: [integer()].
uniform1iv(Location,Value) ->
- cast(5487, <<Location:?GLint,(length(Value)):?GLuint,
- (<< <<C:?GLint>> || C <- Value>>)/binary,0:(((length(Value)) rem 2)*32)>>).
+ ValueLen = length(Value),
+ cast(5488, <<Location:?GLint,ValueLen:?GLuint,
+ (<< <<C:?GLint>> || C <- Value>>)/binary,0:(((ValueLen) rem 2)*32)>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform2iv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{integer(),integer()}].
uniform2iv(Location,Value) ->
- cast(5488, <<Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5489, <<Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLint,V2:?GLint>> || {V1,V2} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform3iv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{integer(),integer(),integer()}].
uniform3iv(Location,Value) ->
- cast(5489, <<Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5490, <<Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLint,V2:?GLint,V3:?GLint>> || {V1,V2,V3} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform4iv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{integer(),integer(),integer(),integer()}].
uniform4iv(Location,Value) ->
- cast(5490, <<Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5491, <<Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix2fv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float()}].
uniformMatrix2fv(Location,Transpose,Value) ->
- cast(5491, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5492, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix3fv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix3fv(Location,Transpose,Value) ->
- cast(5492, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5493, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix4fv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix4fv(Location,Transpose,Value) ->
- cast(5493, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5494, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat,V13:?GLfloat,V14:?GLfloat,V15:?GLfloat,V16:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12,V13,V14,V15,V16} <- Value>>)/binary>>).
%% @doc Validates a program object
@@ -11791,7 +11826,7 @@ uniformMatrix4fv(Location,Transpose,Value) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glValidateProgram.xml">external</a> documentation.
-spec validateProgram(Program) -> 'ok' when Program :: integer().
validateProgram(Program) ->
- cast(5494, <<Program:?GLuint>>).
+ cast(5495, <<Program:?GLuint>>).
%% @doc Specifies the value of a generic vertex attribute
%%
@@ -11866,7 +11901,7 @@ validateProgram(Program) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
-spec vertexAttrib1d(Index, X) -> 'ok' when Index :: integer(),X :: float().
vertexAttrib1d(Index,X) ->
- cast(5495, <<Index:?GLuint,0:32,X:?GLdouble>>).
+ cast(5496, <<Index:?GLuint,0:32,X:?GLdouble>>).
%% @equiv vertexAttrib1d(Index,X)
-spec vertexAttrib1dv(Index :: integer(),V) -> 'ok' when V :: {X :: float()}.
@@ -11876,7 +11911,7 @@ vertexAttrib1dv(Index,{X}) -> vertexAttrib1d(Index,X).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib1f(Index, X) -> 'ok' when Index :: integer(),X :: float().
vertexAttrib1f(Index,X) ->
- cast(5496, <<Index:?GLuint,X:?GLfloat>>).
+ cast(5497, <<Index:?GLuint,X:?GLfloat>>).
%% @equiv vertexAttrib1f(Index,X)
-spec vertexAttrib1fv(Index :: integer(),V) -> 'ok' when V :: {X :: float()}.
@@ -11886,7 +11921,7 @@ vertexAttrib1fv(Index,{X}) -> vertexAttrib1f(Index,X).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib1s(Index, X) -> 'ok' when Index :: integer(),X :: integer().
vertexAttrib1s(Index,X) ->
- cast(5497, <<Index:?GLuint,X:?GLshort>>).
+ cast(5498, <<Index:?GLuint,X:?GLshort>>).
%% @equiv vertexAttrib1s(Index,X)
-spec vertexAttrib1sv(Index :: integer(),V) -> 'ok' when V :: {X :: integer()}.
@@ -11896,7 +11931,7 @@ vertexAttrib1sv(Index,{X}) -> vertexAttrib1s(Index,X).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib2d(Index, X, Y) -> 'ok' when Index :: integer(),X :: float(),Y :: float().
vertexAttrib2d(Index,X,Y) ->
- cast(5498, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble>>).
+ cast(5499, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble>>).
%% @equiv vertexAttrib2d(Index,X,Y)
-spec vertexAttrib2dv(Index :: integer(),V) -> 'ok' when V :: {X :: float(),Y :: float()}.
@@ -11906,7 +11941,7 @@ vertexAttrib2dv(Index,{X,Y}) -> vertexAttrib2d(Index,X,Y).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib2f(Index, X, Y) -> 'ok' when Index :: integer(),X :: float(),Y :: float().
vertexAttrib2f(Index,X,Y) ->
- cast(5499, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat>>).
+ cast(5500, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat>>).
%% @equiv vertexAttrib2f(Index,X,Y)
-spec vertexAttrib2fv(Index :: integer(),V) -> 'ok' when V :: {X :: float(),Y :: float()}.
@@ -11916,7 +11951,7 @@ vertexAttrib2fv(Index,{X,Y}) -> vertexAttrib2f(Index,X,Y).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib2s(Index, X, Y) -> 'ok' when Index :: integer(),X :: integer(),Y :: integer().
vertexAttrib2s(Index,X,Y) ->
- cast(5500, <<Index:?GLuint,X:?GLshort,Y:?GLshort>>).
+ cast(5501, <<Index:?GLuint,X:?GLshort,Y:?GLshort>>).
%% @equiv vertexAttrib2s(Index,X,Y)
-spec vertexAttrib2sv(Index :: integer(),V) -> 'ok' when V :: {X :: integer(),Y :: integer()}.
@@ -11926,7 +11961,7 @@ vertexAttrib2sv(Index,{X,Y}) -> vertexAttrib2s(Index,X,Y).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib3d(Index, X, Y, Z) -> 'ok' when Index :: integer(),X :: float(),Y :: float(),Z :: float().
vertexAttrib3d(Index,X,Y,Z) ->
- cast(5501, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5502, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @equiv vertexAttrib3d(Index,X,Y,Z)
-spec vertexAttrib3dv(Index :: integer(),V) -> 'ok' when V :: {X :: float(),Y :: float(),Z :: float()}.
@@ -11936,7 +11971,7 @@ vertexAttrib3dv(Index,{X,Y,Z}) -> vertexAttrib3d(Index,X,Y,Z).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib3f(Index, X, Y, Z) -> 'ok' when Index :: integer(),X :: float(),Y :: float(),Z :: float().
vertexAttrib3f(Index,X,Y,Z) ->
- cast(5502, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
+ cast(5503, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
%% @equiv vertexAttrib3f(Index,X,Y,Z)
-spec vertexAttrib3fv(Index :: integer(),V) -> 'ok' when V :: {X :: float(),Y :: float(),Z :: float()}.
@@ -11946,7 +11981,7 @@ vertexAttrib3fv(Index,{X,Y,Z}) -> vertexAttrib3f(Index,X,Y,Z).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib3s(Index, X, Y, Z) -> 'ok' when Index :: integer(),X :: integer(),Y :: integer(),Z :: integer().
vertexAttrib3s(Index,X,Y,Z) ->
- cast(5503, <<Index:?GLuint,X:?GLshort,Y:?GLshort,Z:?GLshort>>).
+ cast(5504, <<Index:?GLuint,X:?GLshort,Y:?GLshort,Z:?GLshort>>).
%% @equiv vertexAttrib3s(Index,X,Y,Z)
-spec vertexAttrib3sv(Index :: integer(),V) -> 'ok' when V :: {X :: integer(),Y :: integer(),Z :: integer()}.
@@ -11956,25 +11991,25 @@ vertexAttrib3sv(Index,{X,Y,Z}) -> vertexAttrib3s(Index,X,Y,Z).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4Nbv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttrib4Nbv(Index,{V1,V2,V3,V4}) ->
- cast(5504, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
+ cast(5505, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4Niv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttrib4Niv(Index,{V1,V2,V3,V4}) ->
- cast(5505, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
+ cast(5506, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4Nsv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttrib4Nsv(Index,{V1,V2,V3,V4}) ->
- cast(5506, <<Index:?GLuint,V1:?GLshort,V2:?GLshort,V3:?GLshort,V4:?GLshort>>).
+ cast(5507, <<Index:?GLuint,V1:?GLshort,V2:?GLshort,V3:?GLshort,V4:?GLshort>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4Nub(Index, X, Y, Z, W) -> 'ok' when Index :: integer(),X :: integer(),Y :: integer(),Z :: integer(),W :: integer().
vertexAttrib4Nub(Index,X,Y,Z,W) ->
- cast(5507, <<Index:?GLuint,X:?GLubyte,Y:?GLubyte,Z:?GLubyte,W:?GLubyte>>).
+ cast(5508, <<Index:?GLuint,X:?GLubyte,Y:?GLubyte,Z:?GLubyte,W:?GLubyte>>).
%% @equiv vertexAttrib4Nub(Index,X,Y,Z,W)
-spec vertexAttrib4Nubv(Index :: integer(),V) -> 'ok' when V :: {X :: integer(),Y :: integer(),Z :: integer(),W :: integer()}.
@@ -11984,25 +12019,25 @@ vertexAttrib4Nubv(Index,{X,Y,Z,W}) -> vertexAttrib4Nub(Index,X,Y,Z,W).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4Nuiv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttrib4Nuiv(Index,{V1,V2,V3,V4}) ->
- cast(5508, <<Index:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>>).
+ cast(5509, <<Index:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4Nusv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttrib4Nusv(Index,{V1,V2,V3,V4}) ->
- cast(5509, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
+ cast(5510, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4bv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttrib4bv(Index,{V1,V2,V3,V4}) ->
- cast(5510, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
+ cast(5511, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4d(Index, X, Y, Z, W) -> 'ok' when Index :: integer(),X :: float(),Y :: float(),Z :: float(),W :: float().
vertexAttrib4d(Index,X,Y,Z,W) ->
- cast(5511, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5512, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
%% @equiv vertexAttrib4d(Index,X,Y,Z,W)
-spec vertexAttrib4dv(Index :: integer(),V) -> 'ok' when V :: {X :: float(),Y :: float(),Z :: float(),W :: float()}.
@@ -12012,7 +12047,7 @@ vertexAttrib4dv(Index,{X,Y,Z,W}) -> vertexAttrib4d(Index,X,Y,Z,W).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4f(Index, X, Y, Z, W) -> 'ok' when Index :: integer(),X :: float(),Y :: float(),Z :: float(),W :: float().
vertexAttrib4f(Index,X,Y,Z,W) ->
- cast(5512, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
+ cast(5513, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
%% @equiv vertexAttrib4f(Index,X,Y,Z,W)
-spec vertexAttrib4fv(Index :: integer(),V) -> 'ok' when V :: {X :: float(),Y :: float(),Z :: float(),W :: float()}.
@@ -12022,13 +12057,13 @@ vertexAttrib4fv(Index,{X,Y,Z,W}) -> vertexAttrib4f(Index,X,Y,Z,W).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4iv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttrib4iv(Index,{V1,V2,V3,V4}) ->
- cast(5513, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
+ cast(5514, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4s(Index, X, Y, Z, W) -> 'ok' when Index :: integer(),X :: integer(),Y :: integer(),Z :: integer(),W :: integer().
vertexAttrib4s(Index,X,Y,Z,W) ->
- cast(5514, <<Index:?GLuint,X:?GLshort,Y:?GLshort,Z:?GLshort,W:?GLshort>>).
+ cast(5515, <<Index:?GLuint,X:?GLshort,Y:?GLshort,Z:?GLshort,W:?GLshort>>).
%% @equiv vertexAttrib4s(Index,X,Y,Z,W)
-spec vertexAttrib4sv(Index :: integer(),V) -> 'ok' when V :: {X :: integer(),Y :: integer(),Z :: integer(),W :: integer()}.
@@ -12038,19 +12073,19 @@ vertexAttrib4sv(Index,{X,Y,Z,W}) -> vertexAttrib4s(Index,X,Y,Z,W).
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4ubv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttrib4ubv(Index,{V1,V2,V3,V4}) ->
- cast(5515, <<Index:?GLuint,V1:?GLubyte,V2:?GLubyte,V3:?GLubyte,V4:?GLubyte>>).
+ cast(5516, <<Index:?GLuint,V1:?GLubyte,V2:?GLubyte,V3:?GLubyte,V4:?GLubyte>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4uiv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttrib4uiv(Index,{V1,V2,V3,V4}) ->
- cast(5516, <<Index:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>>).
+ cast(5517, <<Index:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttrib4usv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttrib4usv(Index,{V1,V2,V3,V4}) ->
- cast(5517, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
+ cast(5518, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
%% @doc Define an array of generic vertex attribute data
%%
@@ -12094,51 +12129,57 @@ vertexAttrib4usv(Index,{V1,V2,V3,V4}) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribPointer.xml">external</a> documentation.
-spec vertexAttribPointer(Index, Size, Type, Normalized, Stride, Pointer) -> 'ok' when Index :: integer(),Size :: integer(),Type :: enum(),Normalized :: 0|1,Stride :: integer(),Pointer :: offset()|mem().
vertexAttribPointer(Index,Size,Type,Normalized,Stride,Pointer) when is_integer(Pointer) ->
- cast(5518, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Normalized:?GLboolean,0:24,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5519, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Normalized:?GLboolean,0:24,Stride:?GLsizei,Pointer:?GLuint>>);
vertexAttribPointer(Index,Size,Type,Normalized,Stride,Pointer) ->
send_bin(Pointer),
- cast(5519, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Normalized:?GLboolean,0:24,Stride:?GLsizei>>).
+ cast(5520, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Normalized:?GLboolean,0:24,Stride:?GLsizei>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix2x3fv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float()}].
uniformMatrix2x3fv(Location,Transpose,Value) ->
- cast(5520, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5521, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix3x2fv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float()}].
uniformMatrix3x2fv(Location,Transpose,Value) ->
- cast(5521, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5522, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix2x4fv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix2x4fv(Location,Transpose,Value) ->
- cast(5522, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5523, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix4x2fv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix4x2fv(Location,Transpose,Value) ->
- cast(5523, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5524, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix3x4fv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix3x4fv(Location,Transpose,Value) ->
- cast(5524, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5525, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix4x3fv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix4x3fv(Location,Transpose,Value) ->
- cast(5525, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5526, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
%% @doc glColorMaski
@@ -12146,39 +12187,39 @@ uniformMatrix4x3fv(Location,Transpose,Value) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColorMaski.xml">external</a> documentation.
-spec colorMaski(Index, R, G, B, A) -> 'ok' when Index :: integer(),R :: 0|1,G :: 0|1,B :: 0|1,A :: 0|1.
colorMaski(Index,R,G,B,A) ->
- cast(5526, <<Index:?GLuint,R:?GLboolean,G:?GLboolean,B:?GLboolean,A:?GLboolean>>).
+ cast(5527, <<Index:?GLuint,R:?GLboolean,G:?GLboolean,B:?GLboolean,A:?GLboolean>>).
%% @doc
%% See {@link getBooleanv/1}
-spec getBooleani_v(Target, Index) -> [0|1] when Target :: enum(),Index :: integer().
getBooleani_v(Target,Index) ->
- call(5527, <<Target:?GLenum,Index:?GLuint>>).
+ call(5528, <<Target:?GLenum,Index:?GLuint>>).
%% @doc
%% See {@link getBooleanv/1}
-spec getIntegeri_v(Target, Index) -> [integer()] when Target :: enum(),Index :: integer().
getIntegeri_v(Target,Index) ->
- call(5528, <<Target:?GLenum,Index:?GLuint>>).
+ call(5529, <<Target:?GLenum,Index:?GLuint>>).
%% @doc
%% See {@link enable/1}
-spec enablei(Target, Index) -> 'ok' when Target :: enum(),Index :: integer().
enablei(Target,Index) ->
- cast(5529, <<Target:?GLenum,Index:?GLuint>>).
+ cast(5530, <<Target:?GLenum,Index:?GLuint>>).
%% @doc glEnablei
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEnablei.xml">external</a> documentation.
-spec disablei(Target, Index) -> 'ok' when Target :: enum(),Index :: integer().
disablei(Target,Index) ->
- cast(5530, <<Target:?GLenum,Index:?GLuint>>).
+ cast(5531, <<Target:?GLenum,Index:?GLuint>>).
%% @doc glIsEnabledi
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsEnabledi.xml">external</a> documentation.
-spec isEnabledi(Target, Index) -> 0|1 when Target :: enum(),Index :: integer().
isEnabledi(Target,Index) ->
- call(5531, <<Target:?GLenum,Index:?GLuint>>).
+ call(5532, <<Target:?GLenum,Index:?GLuint>>).
%% @doc Start transform feedback operation
%%
@@ -12206,13 +12247,13 @@ isEnabledi(Target,Index) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBeginTransformFeedback.xml">external</a> documentation.
-spec beginTransformFeedback(PrimitiveMode) -> 'ok' when PrimitiveMode :: enum().
beginTransformFeedback(PrimitiveMode) ->
- cast(5532, <<PrimitiveMode:?GLenum>>).
+ cast(5533, <<PrimitiveMode:?GLenum>>).
%% @doc
%% See {@link beginTransformFeedback/1}
-spec endTransformFeedback() -> 'ok'.
endTransformFeedback() ->
- cast(5533, <<>>).
+ cast(5534, <<>>).
%% @doc Bind a range within a buffer object to an indexed buffer target
%%
@@ -12231,7 +12272,7 @@ endTransformFeedback() ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindBufferRange.xml">external</a> documentation.
-spec bindBufferRange(Target, Index, Buffer, Offset, Size) -> 'ok' when Target :: enum(),Index :: integer(),Buffer :: integer(),Offset :: integer(),Size :: integer().
bindBufferRange(Target,Index,Buffer,Offset,Size) ->
- cast(5534, <<Target:?GLenum,Index:?GLuint,Buffer:?GLuint,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
+ cast(5535, <<Target:?GLenum,Index:?GLuint,Buffer:?GLuint,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
%% @doc Bind a buffer object to an indexed buffer target
%%
@@ -12246,7 +12287,7 @@ bindBufferRange(Target,Index,Buffer,Offset,Size) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindBufferBase.xml">external</a> documentation.
-spec bindBufferBase(Target, Index, Buffer) -> 'ok' when Target :: enum(),Index :: integer(),Buffer :: integer().
bindBufferBase(Target,Index,Buffer) ->
- cast(5535, <<Target:?GLenum,Index:?GLuint,Buffer:?GLuint>>).
+ cast(5536, <<Target:?GLenum,Index:?GLuint,Buffer:?GLuint>>).
%% @doc Specify values to record in transform feedback buffers
%%
@@ -12284,8 +12325,9 @@ bindBufferBase(Target,Index,Buffer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTransformFeedbackVaryings.xml">external</a> documentation.
-spec transformFeedbackVaryings(Program, Varyings, BufferMode) -> 'ok' when Program :: integer(),Varyings :: iolist(),BufferMode :: enum().
transformFeedbackVaryings(Program,Varyings,BufferMode) ->
- VaryingsTemp = list_to_binary([[Str|[0]] || Str <- Varyings ]),
- cast(5536, <<Program:?GLuint,(length(Varyings)):?GLuint,(size(VaryingsTemp)):?GLuint,(VaryingsTemp)/binary,0:((8-((size(VaryingsTemp)+0) rem 8)) rem 8),BufferMode:?GLenum>>).
+ VaryingsTemp = list_to_binary([[Str|[0]] || Str <- Varyings ]),
+ VaryingsLen = length(Varyings),
+ cast(5537, <<Program:?GLuint,VaryingsLen:?GLuint,(size(VaryingsTemp)):?GLuint,(VaryingsTemp)/binary,0:((8-((size(VaryingsTemp)+0) rem 8)) rem 8),BufferMode:?GLenum>>).
%% @doc Retrieve information about varying variables selected for transform feedback
%%
@@ -12318,7 +12360,7 @@ transformFeedbackVaryings(Program,Varyings,BufferMode) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTransformFeedbackVarying.xml">external</a> documentation.
-spec getTransformFeedbackVarying(Program, Index, BufSize) -> {Size :: integer(),Type :: enum(),Name :: string()} when Program :: integer(),Index :: integer(),BufSize :: integer().
getTransformFeedbackVarying(Program,Index,BufSize) ->
- call(5537, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
+ call(5538, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
%% @doc specify whether data read via
%%
@@ -12333,7 +12375,7 @@ getTransformFeedbackVarying(Program,Index,BufSize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClampColor.xml">external</a> documentation.
-spec clampColor(Target, Clamp) -> 'ok' when Target :: enum(),Clamp :: enum().
clampColor(Target,Clamp) ->
- cast(5538, <<Target:?GLenum,Clamp:?GLenum>>).
+ cast(5539, <<Target:?GLenum,Clamp:?GLenum>>).
%% @doc Start conditional rendering
%%
@@ -12365,84 +12407,84 @@ clampColor(Target,Clamp) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBeginConditionalRender.xml">external</a> documentation.
-spec beginConditionalRender(Id, Mode) -> 'ok' when Id :: integer(),Mode :: enum().
beginConditionalRender(Id,Mode) ->
- cast(5539, <<Id:?GLuint,Mode:?GLenum>>).
+ cast(5540, <<Id:?GLuint,Mode:?GLenum>>).
%% @doc
%% See {@link beginConditionalRender/2}
-spec endConditionalRender() -> 'ok'.
endConditionalRender() ->
- cast(5540, <<>>).
+ cast(5541, <<>>).
%% @doc glVertexAttribIPointer
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribIPointer.xml">external</a> documentation.
-spec vertexAttribIPointer(Index, Size, Type, Stride, Pointer) -> 'ok' when Index :: integer(),Size :: integer(),Type :: enum(),Stride :: integer(),Pointer :: offset()|mem().
vertexAttribIPointer(Index,Size,Type,Stride,Pointer) when is_integer(Pointer) ->
- cast(5541, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5542, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
vertexAttribIPointer(Index,Size,Type,Stride,Pointer) ->
send_bin(Pointer),
- cast(5542, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
+ cast(5543, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
%% @doc
%% See {@link getVertexAttribdv/2}
-spec getVertexAttribIiv(Index, Pname) -> {integer(),integer(),integer(),integer()} when Index :: integer(),Pname :: enum().
getVertexAttribIiv(Index,Pname) ->
- call(5543, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5544, <<Index:?GLuint,Pname:?GLenum>>).
%% @doc glGetVertexAttribI
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetVertexAttribI.xml">external</a> documentation.
-spec getVertexAttribIuiv(Index, Pname) -> {integer(),integer(),integer(),integer()} when Index :: integer(),Pname :: enum().
getVertexAttribIuiv(Index,Pname) ->
- call(5544, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5545, <<Index:?GLuint,Pname:?GLenum>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI1i(Index, X) -> 'ok' when Index :: integer(),X :: integer().
vertexAttribI1i(Index,X) ->
- cast(5545, <<Index:?GLuint,X:?GLint>>).
+ cast(5546, <<Index:?GLuint,X:?GLint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI2i(Index, X, Y) -> 'ok' when Index :: integer(),X :: integer(),Y :: integer().
vertexAttribI2i(Index,X,Y) ->
- cast(5546, <<Index:?GLuint,X:?GLint,Y:?GLint>>).
+ cast(5547, <<Index:?GLuint,X:?GLint,Y:?GLint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI3i(Index, X, Y, Z) -> 'ok' when Index :: integer(),X :: integer(),Y :: integer(),Z :: integer().
vertexAttribI3i(Index,X,Y,Z) ->
- cast(5547, <<Index:?GLuint,X:?GLint,Y:?GLint,Z:?GLint>>).
+ cast(5548, <<Index:?GLuint,X:?GLint,Y:?GLint,Z:?GLint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI4i(Index, X, Y, Z, W) -> 'ok' when Index :: integer(),X :: integer(),Y :: integer(),Z :: integer(),W :: integer().
vertexAttribI4i(Index,X,Y,Z,W) ->
- cast(5548, <<Index:?GLuint,X:?GLint,Y:?GLint,Z:?GLint,W:?GLint>>).
+ cast(5549, <<Index:?GLuint,X:?GLint,Y:?GLint,Z:?GLint,W:?GLint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI1ui(Index, X) -> 'ok' when Index :: integer(),X :: integer().
vertexAttribI1ui(Index,X) ->
- cast(5549, <<Index:?GLuint,X:?GLuint>>).
+ cast(5550, <<Index:?GLuint,X:?GLuint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI2ui(Index, X, Y) -> 'ok' when Index :: integer(),X :: integer(),Y :: integer().
vertexAttribI2ui(Index,X,Y) ->
- cast(5550, <<Index:?GLuint,X:?GLuint,Y:?GLuint>>).
+ cast(5551, <<Index:?GLuint,X:?GLuint,Y:?GLuint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI3ui(Index, X, Y, Z) -> 'ok' when Index :: integer(),X :: integer(),Y :: integer(),Z :: integer().
vertexAttribI3ui(Index,X,Y,Z) ->
- cast(5551, <<Index:?GLuint,X:?GLuint,Y:?GLuint,Z:?GLuint>>).
+ cast(5552, <<Index:?GLuint,X:?GLuint,Y:?GLuint,Z:?GLuint>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI4ui(Index, X, Y, Z, W) -> 'ok' when Index :: integer(),X :: integer(),Y :: integer(),Z :: integer(),W :: integer().
vertexAttribI4ui(Index,X,Y,Z,W) ->
- cast(5552, <<Index:?GLuint,X:?GLuint,Y:?GLuint,Z:?GLuint,W:?GLuint>>).
+ cast(5553, <<Index:?GLuint,X:?GLuint,Y:?GLuint,Z:?GLuint,W:?GLuint>>).
%% @equiv vertexAttribI1i(Index,X)
-spec vertexAttribI1iv(Index :: integer(),V) -> 'ok' when V :: {X :: integer()}.
@@ -12480,31 +12522,31 @@ vertexAttribI4uiv(Index,{X,Y,Z,W}) -> vertexAttribI4ui(Index,X,Y,Z,W).
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI4bv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttribI4bv(Index,{V1,V2,V3,V4}) ->
- cast(5553, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
+ cast(5554, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI4sv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttribI4sv(Index,{V1,V2,V3,V4}) ->
- cast(5554, <<Index:?GLuint,V1:?GLshort,V2:?GLshort,V3:?GLshort,V4:?GLshort>>).
+ cast(5555, <<Index:?GLuint,V1:?GLshort,V2:?GLshort,V3:?GLshort,V4:?GLshort>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI4ubv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttribI4ubv(Index,{V1,V2,V3,V4}) ->
- cast(5555, <<Index:?GLuint,V1:?GLubyte,V2:?GLubyte,V3:?GLubyte,V4:?GLubyte>>).
+ cast(5556, <<Index:?GLuint,V1:?GLubyte,V2:?GLubyte,V3:?GLubyte,V4:?GLubyte>>).
%% @doc
%% See {@link vertexAttrib1d/2}
-spec vertexAttribI4usv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
vertexAttribI4usv(Index,{V1,V2,V3,V4}) ->
- cast(5556, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
+ cast(5557, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
%% @doc
%% See {@link getUniformfv/2}
-spec getUniformuiv(Program, Location) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()} when Program :: integer(),Location :: integer().
getUniformuiv(Program,Location) ->
- call(5557, <<Program:?GLuint,Location:?GLint>>).
+ call(5558, <<Program:?GLuint,Location:?GLint>>).
%% @doc Bind a user-defined varying out variable to a fragment shader color number
%%
@@ -12531,7 +12573,8 @@ getUniformuiv(Program,Location) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindFragDataLocation.xml">external</a> documentation.
-spec bindFragDataLocation(Program, Color, Name) -> 'ok' when Program :: integer(),Color :: integer(),Name :: string().
bindFragDataLocation(Program,Color,Name) ->
- cast(5558, <<Program:?GLuint,Color:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ cast(5559, <<Program:?GLuint,Color:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 1) rem 8)) rem 8)>>).
%% @doc Query the bindings of color numbers to user-defined varying out variables
%%
@@ -12544,65 +12587,70 @@ bindFragDataLocation(Program,Color,Name) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetFragDataLocation.xml">external</a> documentation.
-spec getFragDataLocation(Program, Name) -> integer() when Program :: integer(),Name :: string().
getFragDataLocation(Program,Name) ->
- call(5559, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ call(5560, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 5) rem 8)) rem 8)>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform1ui(Location, V0) -> 'ok' when Location :: integer(),V0 :: integer().
uniform1ui(Location,V0) ->
- cast(5560, <<Location:?GLint,V0:?GLuint>>).
+ cast(5561, <<Location:?GLint,V0:?GLuint>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform2ui(Location, V0, V1) -> 'ok' when Location :: integer(),V0 :: integer(),V1 :: integer().
uniform2ui(Location,V0,V1) ->
- cast(5561, <<Location:?GLint,V0:?GLuint,V1:?GLuint>>).
+ cast(5562, <<Location:?GLint,V0:?GLuint,V1:?GLuint>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform3ui(Location, V0, V1, V2) -> 'ok' when Location :: integer(),V0 :: integer(),V1 :: integer(),V2 :: integer().
uniform3ui(Location,V0,V1,V2) ->
- cast(5562, <<Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint>>).
+ cast(5563, <<Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform4ui(Location, V0, V1, V2, V3) -> 'ok' when Location :: integer(),V0 :: integer(),V1 :: integer(),V2 :: integer(),V3 :: integer().
uniform4ui(Location,V0,V1,V2,V3) ->
- cast(5563, <<Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint>>).
+ cast(5564, <<Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform1uiv(Location, Value) -> 'ok' when Location :: integer(),Value :: [integer()].
uniform1uiv(Location,Value) ->
- cast(5564, <<Location:?GLint,(length(Value)):?GLuint,
- (<< <<C:?GLuint>> || C <- Value>>)/binary,0:(((length(Value)) rem 2)*32)>>).
+ ValueLen = length(Value),
+ cast(5565, <<Location:?GLint,ValueLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Value>>)/binary,0:(((ValueLen) rem 2)*32)>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform2uiv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{integer(),integer()}].
uniform2uiv(Location,Value) ->
- cast(5565, <<Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5566, <<Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLuint,V2:?GLuint>> || {V1,V2} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform3uiv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{integer(),integer(),integer()}].
uniform3uiv(Location,Value) ->
- cast(5566, <<Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5567, <<Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLuint,V2:?GLuint,V3:?GLuint>> || {V1,V2,V3} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform4uiv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{integer(),integer(),integer(),integer()}].
uniform4uiv(Location,Value) ->
- cast(5567, <<Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5568, <<Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link texParameterf/3}
-spec texParameterIiv(Target, Pname, Params) -> 'ok' when Target :: enum(),Pname :: enum(),Params :: tuple().
texParameterIiv(Target,Pname,Params) ->
- cast(5568, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5569, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @doc glTexParameterI
@@ -12610,21 +12658,21 @@ texParameterIiv(Target,Pname,Params) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexParameterI.xml">external</a> documentation.
-spec texParameterIuiv(Target, Pname, Params) -> 'ok' when Target :: enum(),Pname :: enum(),Params :: tuple().
texParameterIuiv(Target,Pname,Params) ->
- cast(5569, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5570, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLuint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @doc
%% See {@link getTexParameterfv/2}
-spec getTexParameterIiv(Target, Pname) -> {integer(),integer(),integer(),integer()} when Target :: enum(),Pname :: enum().
getTexParameterIiv(Target,Pname) ->
- call(5570, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5571, <<Target:?GLenum,Pname:?GLenum>>).
%% @doc glGetTexParameterI
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexParameterI.xml">external</a> documentation.
-spec getTexParameterIuiv(Target, Pname) -> {integer(),integer(),integer(),integer()} when Target :: enum(),Pname :: enum().
getTexParameterIuiv(Target,Pname) ->
- call(5571, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5572, <<Target:?GLenum,Pname:?GLenum>>).
%% @doc Clear individual buffers of the currently bound draw framebuffer
%%
@@ -12657,21 +12705,21 @@ getTexParameterIuiv(Target,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearBuffer.xml">external</a> documentation.
-spec clearBufferiv(Buffer, Drawbuffer, Value) -> 'ok' when Buffer :: enum(),Drawbuffer :: integer(),Value :: tuple().
clearBufferiv(Buffer,Drawbuffer,Value) ->
- cast(5572, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
+ cast(5573, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Value)>>)/binary,0:(((1+size(Value)) rem 2)*32)>>).
%% @doc
%% See {@link clearBufferiv/3}
-spec clearBufferuiv(Buffer, Drawbuffer, Value) -> 'ok' when Buffer :: enum(),Drawbuffer :: integer(),Value :: tuple().
clearBufferuiv(Buffer,Drawbuffer,Value) ->
- cast(5573, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
+ cast(5574, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
(<< <<C:?GLuint>> ||C <- tuple_to_list(Value)>>)/binary,0:(((1+size(Value)) rem 2)*32)>>).
%% @doc
%% See {@link clearBufferiv/3}
-spec clearBufferfv(Buffer, Drawbuffer, Value) -> 'ok' when Buffer :: enum(),Drawbuffer :: integer(),Value :: tuple().
clearBufferfv(Buffer,Drawbuffer,Value) ->
- cast(5574, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
+ cast(5575, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Value)>>)/binary,0:(((1+size(Value)) rem 2)*32)>>).
%% @doc glClearBufferfi
@@ -12679,30 +12727,30 @@ clearBufferfv(Buffer,Drawbuffer,Value) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearBufferfi.xml">external</a> documentation.
-spec clearBufferfi(Buffer, Drawbuffer, Depth, Stencil) -> 'ok' when Buffer :: enum(),Drawbuffer :: integer(),Depth :: float(),Stencil :: integer().
clearBufferfi(Buffer,Drawbuffer,Depth,Stencil) ->
- cast(5575, <<Buffer:?GLenum,Drawbuffer:?GLint,Depth:?GLfloat,Stencil:?GLint>>).
+ cast(5576, <<Buffer:?GLenum,Drawbuffer:?GLint,Depth:?GLfloat,Stencil:?GLint>>).
%% @doc
%% See {@link getString/1}
-spec getStringi(Name, Index) -> string() when Name :: enum(),Index :: integer().
getStringi(Name,Index) ->
- call(5576, <<Name:?GLenum,Index:?GLuint>>).
+ call(5577, <<Name:?GLenum,Index:?GLuint>>).
%% @doc glDrawArraysInstance
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawArraysInstance.xml">external</a> documentation.
-spec drawArraysInstanced(Mode, First, Count, Primcount) -> 'ok' when Mode :: enum(),First :: integer(),Count :: integer(),Primcount :: integer().
drawArraysInstanced(Mode,First,Count,Primcount) ->
- cast(5577, <<Mode:?GLenum,First:?GLint,Count:?GLsizei,Primcount:?GLsizei>>).
+ cast(5578, <<Mode:?GLenum,First:?GLint,Count:?GLsizei,Primcount:?GLsizei>>).
%% @doc glDrawElementsInstance
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElementsInstance.xml">external</a> documentation.
-spec drawElementsInstanced(Mode, Count, Type, Indices, Primcount) -> 'ok' when Mode :: enum(),Count :: integer(),Type :: enum(),Indices :: offset()|mem(),Primcount :: integer().
drawElementsInstanced(Mode,Count,Type,Indices,Primcount) when is_integer(Indices) ->
- cast(5578, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei>>);
+ cast(5579, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei>>);
drawElementsInstanced(Mode,Count,Type,Indices,Primcount) ->
send_bin(Indices),
- cast(5579, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei>>).
+ cast(5580, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei>>).
%% @doc Attach the storage for a buffer object to the active buffer texture
%%
@@ -12770,7 +12818,7 @@ drawElementsInstanced(Mode,Count,Type,Indices,Primcount) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexBuffer.xml">external</a> documentation.
-spec texBuffer(Target, Internalformat, Buffer) -> 'ok' when Target :: enum(),Internalformat :: enum(),Buffer :: integer().
texBuffer(Target,Internalformat,Buffer) ->
- cast(5580, <<Target:?GLenum,Internalformat:?GLenum,Buffer:?GLuint>>).
+ cast(5581, <<Target:?GLenum,Internalformat:?GLenum,Buffer:?GLuint>>).
%% @doc Specify the primitive restart index
%%
@@ -12792,20 +12840,20 @@ texBuffer(Target,Internalformat,Buffer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPrimitiveRestartIndex.xml">external</a> documentation.
-spec primitiveRestartIndex(Index) -> 'ok' when Index :: integer().
primitiveRestartIndex(Index) ->
- cast(5581, <<Index:?GLuint>>).
+ cast(5582, <<Index:?GLuint>>).
%% @doc
%% See {@link getBooleanv/1}
-spec getInteger64i_v(Target, Index) -> [integer()] when Target :: enum(),Index :: integer().
getInteger64i_v(Target,Index) ->
- call(5582, <<Target:?GLenum,Index:?GLuint>>).
+ call(5583, <<Target:?GLenum,Index:?GLuint>>).
%% @doc glGetBufferParameteri64v
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetBufferParameteri64v.xml">external</a> documentation.
-spec getBufferParameteri64v(Target, Pname) -> [integer()] when Target :: enum(),Pname :: enum().
getBufferParameteri64v(Target,Pname) ->
- call(5583, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5584, <<Target:?GLenum,Pname:?GLenum>>).
%% @doc Attach a level of a texture object as a logical buffer to the currently bound framebuffer object
%%
@@ -12862,7 +12910,7 @@ getBufferParameteri64v(Target,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFramebufferTexture.xml">external</a> documentation.
-spec framebufferTexture(Target, Attachment, Texture, Level) -> 'ok' when Target :: enum(),Attachment :: enum(),Texture :: integer(),Level :: integer().
framebufferTexture(Target,Attachment,Texture,Level) ->
- cast(5584, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint>>).
+ cast(5585, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint>>).
%% @doc Modify the rate at which generic vertex attributes advance during instanced rendering
%%
@@ -12878,7 +12926,7 @@ framebufferTexture(Target,Attachment,Texture,Level) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribDivisor.xml">external</a> documentation.
-spec vertexAttribDivisor(Index, Divisor) -> 'ok' when Index :: integer(),Divisor :: integer().
vertexAttribDivisor(Index,Divisor) ->
- cast(5585, <<Index:?GLuint,Divisor:?GLuint>>).
+ cast(5586, <<Index:?GLuint,Divisor:?GLuint>>).
%% @doc Specifies minimum rate at which sample shaing takes place
%%
@@ -12899,107 +12947,112 @@ vertexAttribDivisor(Index,Divisor) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMinSampleShading.xml">external</a> documentation.
-spec minSampleShading(Value) -> 'ok' when Value :: clamp().
minSampleShading(Value) ->
- cast(5586, <<Value:?GLclampf>>).
+ cast(5587, <<Value:?GLclampf>>).
%% @doc
%% See {@link blendEquation/1}
-spec blendEquationi(Buf, Mode) -> 'ok' when Buf :: integer(),Mode :: enum().
blendEquationi(Buf,Mode) ->
- cast(5587, <<Buf:?GLuint,Mode:?GLenum>>).
+ cast(5588, <<Buf:?GLuint,Mode:?GLenum>>).
%% @doc
%% See {@link blendEquationSeparate/2}
-spec blendEquationSeparatei(Buf, ModeRGB, ModeAlpha) -> 'ok' when Buf :: integer(),ModeRGB :: enum(),ModeAlpha :: enum().
blendEquationSeparatei(Buf,ModeRGB,ModeAlpha) ->
- cast(5588, <<Buf:?GLuint,ModeRGB:?GLenum,ModeAlpha:?GLenum>>).
+ cast(5589, <<Buf:?GLuint,ModeRGB:?GLenum,ModeAlpha:?GLenum>>).
%% @doc glBlendFunci
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendFunci.xml">external</a> documentation.
-spec blendFunci(Buf, Src, Dst) -> 'ok' when Buf :: integer(),Src :: enum(),Dst :: enum().
blendFunci(Buf,Src,Dst) ->
- cast(5589, <<Buf:?GLuint,Src:?GLenum,Dst:?GLenum>>).
+ cast(5590, <<Buf:?GLuint,Src:?GLenum,Dst:?GLenum>>).
%% @doc
%% See {@link blendFuncSeparate/4}
-spec blendFuncSeparatei(Buf, SrcRGB, DstRGB, SrcAlpha, DstAlpha) -> 'ok' when Buf :: integer(),SrcRGB :: enum(),DstRGB :: enum(),SrcAlpha :: enum(),DstAlpha :: enum().
blendFuncSeparatei(Buf,SrcRGB,DstRGB,SrcAlpha,DstAlpha) ->
- cast(5590, <<Buf:?GLuint,SrcRGB:?GLenum,DstRGB:?GLenum,SrcAlpha:?GLenum,DstAlpha:?GLenum>>).
+ cast(5591, <<Buf:?GLuint,SrcRGB:?GLenum,DstRGB:?GLenum,SrcAlpha:?GLenum,DstAlpha:?GLenum>>).
%% @doc glLoadTransposeMatrixARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadTransposeMatrixARB.xml">external</a> documentation.
-spec loadTransposeMatrixfARB(M) -> 'ok' when M :: matrix().
loadTransposeMatrixfARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- cast(5591, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
+ cast(5592, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
loadTransposeMatrixfARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- cast(5591, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
+ cast(5592, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
%% @doc glLoadTransposeMatrixARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadTransposeMatrixARB.xml">external</a> documentation.
-spec loadTransposeMatrixdARB(M) -> 'ok' when M :: matrix().
loadTransposeMatrixdARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- cast(5592, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
+ cast(5593, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
loadTransposeMatrixdARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- cast(5592, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
+ cast(5593, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
%% @doc glMultTransposeMatrixARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultTransposeMatrixARB.xml">external</a> documentation.
-spec multTransposeMatrixfARB(M) -> 'ok' when M :: matrix().
multTransposeMatrixfARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- cast(5593, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
+ cast(5594, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
multTransposeMatrixfARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- cast(5593, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
+ cast(5594, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
%% @doc glMultTransposeMatrixARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultTransposeMatrixARB.xml">external</a> documentation.
-spec multTransposeMatrixdARB(M) -> 'ok' when M :: matrix().
multTransposeMatrixdARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- cast(5594, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
+ cast(5595, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
multTransposeMatrixdARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- cast(5594, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
+ cast(5595, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
%% @doc glWeightARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
-spec weightbvARB(Weights) -> 'ok' when Weights :: [integer()].
weightbvARB(Weights) ->
- cast(5595, <<(length(Weights)):?GLuint,
- (<< <<C:?GLbyte>> || C <- Weights>>)/binary,0:((8-((length(Weights)+ 4) rem 8)) rem 8)>>).
+ WeightsLen = length(Weights),
+ cast(5596, <<WeightsLen:?GLuint,
+ (<< <<C:?GLbyte>> || C <- Weights>>)/binary,0:((8-((WeightsLen+ 4) rem 8)) rem 8)>>).
%% @doc glWeightARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
-spec weightsvARB(Weights) -> 'ok' when Weights :: [integer()].
weightsvARB(Weights) ->
- cast(5596, <<(length(Weights)):?GLuint,
- (<< <<C:?GLshort>> || C <- Weights>>)/binary,0:((8-((length(Weights)*2+ 4) rem 8)) rem 8)>>).
+ WeightsLen = length(Weights),
+ cast(5597, <<WeightsLen:?GLuint,
+ (<< <<C:?GLshort>> || C <- Weights>>)/binary,0:((8-((WeightsLen*2+ 4) rem 8)) rem 8)>>).
%% @doc glWeightARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
-spec weightivARB(Weights) -> 'ok' when Weights :: [integer()].
weightivARB(Weights) ->
- cast(5597, <<(length(Weights)):?GLuint,
- (<< <<C:?GLint>> || C <- Weights>>)/binary,0:(((1+length(Weights)) rem 2)*32)>>).
+ WeightsLen = length(Weights),
+ cast(5598, <<WeightsLen:?GLuint,
+ (<< <<C:?GLint>> || C <- Weights>>)/binary,0:(((1+WeightsLen) rem 2)*32)>>).
%% @doc glWeightARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
-spec weightfvARB(Weights) -> 'ok' when Weights :: [float()].
weightfvARB(Weights) ->
- cast(5598, <<(length(Weights)):?GLuint,
- (<< <<C:?GLfloat>> || C <- Weights>>)/binary,0:(((1+length(Weights)) rem 2)*32)>>).
+ WeightsLen = length(Weights),
+ cast(5599, <<WeightsLen:?GLuint,
+ (<< <<C:?GLfloat>> || C <- Weights>>)/binary,0:(((1+WeightsLen) rem 2)*32)>>).
%% @doc glWeightARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
-spec weightdvARB(Weights) -> 'ok' when Weights :: [float()].
weightdvARB(Weights) ->
- cast(5599, <<(length(Weights)):?GLuint,0:32,
+ WeightsLen = length(Weights),
+ cast(5600, <<WeightsLen:?GLuint,0:32,
(<< <<C:?GLdouble>> || C <- Weights>>)/binary>>).
%% @doc glWeightARB
@@ -13007,175 +13060,183 @@ weightdvARB(Weights) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
-spec weightubvARB(Weights) -> 'ok' when Weights :: [integer()].
weightubvARB(Weights) ->
- cast(5600, <<(length(Weights)):?GLuint,
- (<< <<C:?GLubyte>> || C <- Weights>>)/binary,0:((8-((length(Weights)+ 4) rem 8)) rem 8)>>).
+ WeightsLen = length(Weights),
+ cast(5601, <<WeightsLen:?GLuint,
+ (<< <<C:?GLubyte>> || C <- Weights>>)/binary,0:((8-((WeightsLen+ 4) rem 8)) rem 8)>>).
%% @doc glWeightARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
-spec weightusvARB(Weights) -> 'ok' when Weights :: [integer()].
weightusvARB(Weights) ->
- cast(5601, <<(length(Weights)):?GLuint,
- (<< <<C:?GLushort>> || C <- Weights>>)/binary,0:((8-((length(Weights)*2+ 4) rem 8)) rem 8)>>).
+ WeightsLen = length(Weights),
+ cast(5602, <<WeightsLen:?GLuint,
+ (<< <<C:?GLushort>> || C <- Weights>>)/binary,0:((8-((WeightsLen*2+ 4) rem 8)) rem 8)>>).
%% @doc glWeightARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
-spec weightuivARB(Weights) -> 'ok' when Weights :: [integer()].
weightuivARB(Weights) ->
- cast(5602, <<(length(Weights)):?GLuint,
- (<< <<C:?GLuint>> || C <- Weights>>)/binary,0:(((1+length(Weights)) rem 2)*32)>>).
+ WeightsLen = length(Weights),
+ cast(5603, <<WeightsLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Weights>>)/binary,0:(((1+WeightsLen) rem 2)*32)>>).
%% @doc glVertexBlenARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexBlenARB.xml">external</a> documentation.
-spec vertexBlendARB(Count) -> 'ok' when Count :: integer().
vertexBlendARB(Count) ->
- cast(5603, <<Count:?GLint>>).
+ cast(5604, <<Count:?GLint>>).
%% @doc glCurrentPaletteMatrixARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCurrentPaletteMatrixARB.xml">external</a> documentation.
-spec currentPaletteMatrixARB(Index) -> 'ok' when Index :: integer().
currentPaletteMatrixARB(Index) ->
- cast(5604, <<Index:?GLint>>).
+ cast(5605, <<Index:?GLint>>).
%% @doc glMatrixIndexARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMatrixIndexARB.xml">external</a> documentation.
-spec matrixIndexubvARB(Indices) -> 'ok' when Indices :: [integer()].
matrixIndexubvARB(Indices) ->
- cast(5605, <<(length(Indices)):?GLuint,
- (<< <<C:?GLubyte>> || C <- Indices>>)/binary,0:((8-((length(Indices)+ 4) rem 8)) rem 8)>>).
+ IndicesLen = length(Indices),
+ cast(5606, <<IndicesLen:?GLuint,
+ (<< <<C:?GLubyte>> || C <- Indices>>)/binary,0:((8-((IndicesLen+ 4) rem 8)) rem 8)>>).
%% @doc glMatrixIndexARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMatrixIndexARB.xml">external</a> documentation.
-spec matrixIndexusvARB(Indices) -> 'ok' when Indices :: [integer()].
matrixIndexusvARB(Indices) ->
- cast(5606, <<(length(Indices)):?GLuint,
- (<< <<C:?GLushort>> || C <- Indices>>)/binary,0:((8-((length(Indices)*2+ 4) rem 8)) rem 8)>>).
+ IndicesLen = length(Indices),
+ cast(5607, <<IndicesLen:?GLuint,
+ (<< <<C:?GLushort>> || C <- Indices>>)/binary,0:((8-((IndicesLen*2+ 4) rem 8)) rem 8)>>).
%% @doc glMatrixIndexARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMatrixIndexARB.xml">external</a> documentation.
-spec matrixIndexuivARB(Indices) -> 'ok' when Indices :: [integer()].
matrixIndexuivARB(Indices) ->
- cast(5607, <<(length(Indices)):?GLuint,
- (<< <<C:?GLuint>> || C <- Indices>>)/binary,0:(((1+length(Indices)) rem 2)*32)>>).
+ IndicesLen = length(Indices),
+ cast(5608, <<IndicesLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Indices>>)/binary,0:(((1+IndicesLen) rem 2)*32)>>).
%% @doc glProgramStringARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramStringARB.xml">external</a> documentation.
-spec programStringARB(Target, Format, String) -> 'ok' when Target :: enum(),Format :: enum(),String :: string().
programStringARB(Target,Format,String) ->
- cast(5608, <<Target:?GLenum,Format:?GLenum,(list_to_binary([String|[0]]))/binary,0:((8-((length(String)+ 1) rem 8)) rem 8)>>).
+ StringLen = length(String),
+ cast(5609, <<Target:?GLenum,Format:?GLenum,(list_to_binary([String|[0]]))/binary,0:((8-((StringLen+ 1) rem 8)) rem 8)>>).
%% @doc glBindProgramARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindProgramARB.xml">external</a> documentation.
-spec bindProgramARB(Target, Program) -> 'ok' when Target :: enum(),Program :: integer().
bindProgramARB(Target,Program) ->
- cast(5609, <<Target:?GLenum,Program:?GLuint>>).
+ cast(5610, <<Target:?GLenum,Program:?GLuint>>).
%% @doc glDeleteProgramsARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteProgramsARB.xml">external</a> documentation.
-spec deleteProgramsARB(Programs) -> 'ok' when Programs :: [integer()].
deleteProgramsARB(Programs) ->
- cast(5610, <<(length(Programs)):?GLuint,
- (<< <<C:?GLuint>> || C <- Programs>>)/binary,0:(((1+length(Programs)) rem 2)*32)>>).
+ ProgramsLen = length(Programs),
+ cast(5611, <<ProgramsLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Programs>>)/binary,0:(((1+ProgramsLen) rem 2)*32)>>).
%% @doc glGenProgramsARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenProgramsARB.xml">external</a> documentation.
-spec genProgramsARB(N) -> [integer()] when N :: integer().
genProgramsARB(N) ->
- call(5611, <<N:?GLsizei>>).
+ call(5612, <<N:?GLsizei>>).
%% @doc glProgramEnvParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramEnvParameterARB.xml">external</a> documentation.
-spec programEnvParameter4dARB(Target, Index, X, Y, Z, W) -> 'ok' when Target :: enum(),Index :: integer(),X :: float(),Y :: float(),Z :: float(),W :: float().
programEnvParameter4dARB(Target,Index,X,Y,Z,W) ->
- cast(5612, <<Target:?GLenum,Index:?GLuint,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5613, <<Target:?GLenum,Index:?GLuint,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
%% @doc glProgramEnvParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramEnvParameterARB.xml">external</a> documentation.
-spec programEnvParameter4dvARB(Target, Index, Params) -> 'ok' when Target :: enum(),Index :: integer(),Params :: {float(),float(),float(),float()}.
programEnvParameter4dvARB(Target,Index,{P1,P2,P3,P4}) ->
- cast(5613, <<Target:?GLenum,Index:?GLuint,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble>>).
+ cast(5614, <<Target:?GLenum,Index:?GLuint,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble>>).
%% @doc glProgramEnvParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramEnvParameterARB.xml">external</a> documentation.
-spec programEnvParameter4fARB(Target, Index, X, Y, Z, W) -> 'ok' when Target :: enum(),Index :: integer(),X :: float(),Y :: float(),Z :: float(),W :: float().
programEnvParameter4fARB(Target,Index,X,Y,Z,W) ->
- cast(5614, <<Target:?GLenum,Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
+ cast(5615, <<Target:?GLenum,Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
%% @doc glProgramEnvParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramEnvParameterARB.xml">external</a> documentation.
-spec programEnvParameter4fvARB(Target, Index, Params) -> 'ok' when Target :: enum(),Index :: integer(),Params :: {float(),float(),float(),float()}.
programEnvParameter4fvARB(Target,Index,{P1,P2,P3,P4}) ->
- cast(5615, <<Target:?GLenum,Index:?GLuint,P1:?GLfloat,P2:?GLfloat,P3:?GLfloat,P4:?GLfloat>>).
+ cast(5616, <<Target:?GLenum,Index:?GLuint,P1:?GLfloat,P2:?GLfloat,P3:?GLfloat,P4:?GLfloat>>).
%% @doc glProgramLocalParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramLocalParameterARB.xml">external</a> documentation.
-spec programLocalParameter4dARB(Target, Index, X, Y, Z, W) -> 'ok' when Target :: enum(),Index :: integer(),X :: float(),Y :: float(),Z :: float(),W :: float().
programLocalParameter4dARB(Target,Index,X,Y,Z,W) ->
- cast(5616, <<Target:?GLenum,Index:?GLuint,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5617, <<Target:?GLenum,Index:?GLuint,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
%% @doc glProgramLocalParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramLocalParameterARB.xml">external</a> documentation.
-spec programLocalParameter4dvARB(Target, Index, Params) -> 'ok' when Target :: enum(),Index :: integer(),Params :: {float(),float(),float(),float()}.
programLocalParameter4dvARB(Target,Index,{P1,P2,P3,P4}) ->
- cast(5617, <<Target:?GLenum,Index:?GLuint,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble>>).
+ cast(5618, <<Target:?GLenum,Index:?GLuint,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble>>).
%% @doc glProgramLocalParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramLocalParameterARB.xml">external</a> documentation.
-spec programLocalParameter4fARB(Target, Index, X, Y, Z, W) -> 'ok' when Target :: enum(),Index :: integer(),X :: float(),Y :: float(),Z :: float(),W :: float().
programLocalParameter4fARB(Target,Index,X,Y,Z,W) ->
- cast(5618, <<Target:?GLenum,Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
+ cast(5619, <<Target:?GLenum,Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
%% @doc glProgramLocalParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramLocalParameterARB.xml">external</a> documentation.
-spec programLocalParameter4fvARB(Target, Index, Params) -> 'ok' when Target :: enum(),Index :: integer(),Params :: {float(),float(),float(),float()}.
programLocalParameter4fvARB(Target,Index,{P1,P2,P3,P4}) ->
- cast(5619, <<Target:?GLenum,Index:?GLuint,P1:?GLfloat,P2:?GLfloat,P3:?GLfloat,P4:?GLfloat>>).
+ cast(5620, <<Target:?GLenum,Index:?GLuint,P1:?GLfloat,P2:?GLfloat,P3:?GLfloat,P4:?GLfloat>>).
%% @doc glGetProgramEnvParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramEnvParameterARB.xml">external</a> documentation.
-spec getProgramEnvParameterdvARB(Target, Index) -> {float(),float(),float(),float()} when Target :: enum(),Index :: integer().
getProgramEnvParameterdvARB(Target,Index) ->
- call(5620, <<Target:?GLenum,Index:?GLuint>>).
+ call(5621, <<Target:?GLenum,Index:?GLuint>>).
%% @doc glGetProgramEnvParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramEnvParameterARB.xml">external</a> documentation.
-spec getProgramEnvParameterfvARB(Target, Index) -> {float(),float(),float(),float()} when Target :: enum(),Index :: integer().
getProgramEnvParameterfvARB(Target,Index) ->
- call(5621, <<Target:?GLenum,Index:?GLuint>>).
+ call(5622, <<Target:?GLenum,Index:?GLuint>>).
%% @doc glGetProgramLocalParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramLocalParameterARB.xml">external</a> documentation.
-spec getProgramLocalParameterdvARB(Target, Index) -> {float(),float(),float(),float()} when Target :: enum(),Index :: integer().
getProgramLocalParameterdvARB(Target,Index) ->
- call(5622, <<Target:?GLenum,Index:?GLuint>>).
+ call(5623, <<Target:?GLenum,Index:?GLuint>>).
%% @doc glGetProgramLocalParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramLocalParameterARB.xml">external</a> documentation.
-spec getProgramLocalParameterfvARB(Target, Index) -> {float(),float(),float(),float()} when Target :: enum(),Index :: integer().
getProgramLocalParameterfvARB(Target,Index) ->
- call(5623, <<Target:?GLenum,Index:?GLuint>>).
+ call(5624, <<Target:?GLenum,Index:?GLuint>>).
%% @doc glGetProgramStringARB
%%
@@ -13183,176 +13244,180 @@ getProgramLocalParameterfvARB(Target,Index) ->
-spec getProgramStringARB(Target, Pname, String) -> 'ok' when Target :: enum(),Pname :: enum(),String :: mem().
getProgramStringARB(Target,Pname,String) ->
send_bin(String),
- call(5624, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5625, <<Target:?GLenum,Pname:?GLenum>>).
%% @doc glGetBufferParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetBufferParameterARB.xml">external</a> documentation.
-spec getBufferParameterivARB(Target, Pname) -> [integer()] when Target :: enum(),Pname :: enum().
getBufferParameterivARB(Target,Pname) ->
- call(5625, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5626, <<Target:?GLenum,Pname:?GLenum>>).
%% @doc glDeleteObjectARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteObjectARB.xml">external</a> documentation.
-spec deleteObjectARB(Obj) -> 'ok' when Obj :: integer().
deleteObjectARB(Obj) ->
- cast(5626, <<Obj:?GLhandleARB>>).
+ cast(5627, <<Obj:?GLhandleARB>>).
%% @doc glGetHandleARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetHandleARB.xml">external</a> documentation.
-spec getHandleARB(Pname) -> integer() when Pname :: enum().
getHandleARB(Pname) ->
- call(5627, <<Pname:?GLenum>>).
+ call(5628, <<Pname:?GLenum>>).
%% @doc glDetachObjectARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDetachObjectARB.xml">external</a> documentation.
-spec detachObjectARB(ContainerObj, AttachedObj) -> 'ok' when ContainerObj :: integer(),AttachedObj :: integer().
detachObjectARB(ContainerObj,AttachedObj) ->
- cast(5628, <<ContainerObj:?GLhandleARB,AttachedObj:?GLhandleARB>>).
+ cast(5629, <<ContainerObj:?GLhandleARB,AttachedObj:?GLhandleARB>>).
%% @doc glCreateShaderObjectARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateShaderObjectARB.xml">external</a> documentation.
-spec createShaderObjectARB(ShaderType) -> integer() when ShaderType :: enum().
createShaderObjectARB(ShaderType) ->
- call(5629, <<ShaderType:?GLenum>>).
+ call(5630, <<ShaderType:?GLenum>>).
%% @doc glShaderSourceARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glShaderSourceARB.xml">external</a> documentation.
-spec shaderSourceARB(ShaderObj, String) -> 'ok' when ShaderObj :: integer(),String :: iolist().
shaderSourceARB(ShaderObj,String) ->
- StringTemp = list_to_binary([[Str|[0]] || Str <- String ]),
- cast(5630, <<ShaderObj:?GLhandleARB,(length(String)):?GLuint,(size(StringTemp)):?GLuint,(StringTemp)/binary,0:((8-((size(StringTemp)+4) rem 8)) rem 8)>>).
+ StringTemp = list_to_binary([[Str|[0]] || Str <- String ]),
+ StringLen = length(String),
+ cast(5631, <<ShaderObj:?GLhandleARB,StringLen:?GLuint,(size(StringTemp)):?GLuint,(StringTemp)/binary,0:((8-((size(StringTemp)+4) rem 8)) rem 8)>>).
%% @doc glCompileShaderARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompileShaderARB.xml">external</a> documentation.
-spec compileShaderARB(ShaderObj) -> 'ok' when ShaderObj :: integer().
compileShaderARB(ShaderObj) ->
- cast(5631, <<ShaderObj:?GLhandleARB>>).
+ cast(5632, <<ShaderObj:?GLhandleARB>>).
%% @doc glCreateProgramObjectARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateProgramObjectARB.xml">external</a> documentation.
-spec createProgramObjectARB() -> integer().
createProgramObjectARB() ->
- call(5632, <<>>).
+ call(5633, <<>>).
%% @doc glAttachObjectARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glAttachObjectARB.xml">external</a> documentation.
-spec attachObjectARB(ContainerObj, Obj) -> 'ok' when ContainerObj :: integer(),Obj :: integer().
attachObjectARB(ContainerObj,Obj) ->
- cast(5633, <<ContainerObj:?GLhandleARB,Obj:?GLhandleARB>>).
+ cast(5634, <<ContainerObj:?GLhandleARB,Obj:?GLhandleARB>>).
%% @doc glLinkProgramARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLinkProgramARB.xml">external</a> documentation.
-spec linkProgramARB(ProgramObj) -> 'ok' when ProgramObj :: integer().
linkProgramARB(ProgramObj) ->
- cast(5634, <<ProgramObj:?GLhandleARB>>).
+ cast(5635, <<ProgramObj:?GLhandleARB>>).
%% @doc glUseProgramObjectARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUseProgramObjectARB.xml">external</a> documentation.
-spec useProgramObjectARB(ProgramObj) -> 'ok' when ProgramObj :: integer().
useProgramObjectARB(ProgramObj) ->
- cast(5635, <<ProgramObj:?GLhandleARB>>).
+ cast(5636, <<ProgramObj:?GLhandleARB>>).
%% @doc glValidateProgramARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glValidateProgramARB.xml">external</a> documentation.
-spec validateProgramARB(ProgramObj) -> 'ok' when ProgramObj :: integer().
validateProgramARB(ProgramObj) ->
- cast(5636, <<ProgramObj:?GLhandleARB>>).
+ cast(5637, <<ProgramObj:?GLhandleARB>>).
%% @doc glGetObjectParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetObjectParameterARB.xml">external</a> documentation.
-spec getObjectParameterfvARB(Obj, Pname) -> float() when Obj :: integer(),Pname :: enum().
getObjectParameterfvARB(Obj,Pname) ->
- call(5637, <<Obj:?GLhandleARB,Pname:?GLenum>>).
+ call(5638, <<Obj:?GLhandleARB,Pname:?GLenum>>).
%% @doc glGetObjectParameterARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetObjectParameterARB.xml">external</a> documentation.
-spec getObjectParameterivARB(Obj, Pname) -> integer() when Obj :: integer(),Pname :: enum().
getObjectParameterivARB(Obj,Pname) ->
- call(5638, <<Obj:?GLhandleARB,Pname:?GLenum>>).
+ call(5639, <<Obj:?GLhandleARB,Pname:?GLenum>>).
%% @doc glGetInfoLogARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetInfoLogARB.xml">external</a> documentation.
-spec getInfoLogARB(Obj, MaxLength) -> string() when Obj :: integer(),MaxLength :: integer().
getInfoLogARB(Obj,MaxLength) ->
- call(5639, <<Obj:?GLhandleARB,MaxLength:?GLsizei>>).
+ call(5640, <<Obj:?GLhandleARB,MaxLength:?GLsizei>>).
%% @doc glGetAttachedObjectsARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetAttachedObjectsARB.xml">external</a> documentation.
-spec getAttachedObjectsARB(ContainerObj, MaxCount) -> [integer()] when ContainerObj :: integer(),MaxCount :: integer().
getAttachedObjectsARB(ContainerObj,MaxCount) ->
- call(5640, <<ContainerObj:?GLhandleARB,MaxCount:?GLsizei>>).
+ call(5641, <<ContainerObj:?GLhandleARB,MaxCount:?GLsizei>>).
%% @doc glGetUniformLocationARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformLocationARB.xml">external</a> documentation.
-spec getUniformLocationARB(ProgramObj, Name) -> integer() when ProgramObj :: integer(),Name :: string().
getUniformLocationARB(ProgramObj,Name) ->
- call(5641, <<ProgramObj:?GLhandleARB,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ call(5642, <<ProgramObj:?GLhandleARB,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 1) rem 8)) rem 8)>>).
%% @doc glGetActiveUniformARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniformARB.xml">external</a> documentation.
-spec getActiveUniformARB(ProgramObj, Index, MaxLength) -> {Size :: integer(),Type :: enum(),Name :: string()} when ProgramObj :: integer(),Index :: integer(),MaxLength :: integer().
getActiveUniformARB(ProgramObj,Index,MaxLength) ->
- call(5642, <<ProgramObj:?GLhandleARB,Index:?GLuint,MaxLength:?GLsizei>>).
+ call(5643, <<ProgramObj:?GLhandleARB,Index:?GLuint,MaxLength:?GLsizei>>).
%% @doc glGetUniformARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformARB.xml">external</a> documentation.
-spec getUniformfvARB(ProgramObj, Location) -> matrix() when ProgramObj :: integer(),Location :: integer().
getUniformfvARB(ProgramObj,Location) ->
- call(5643, <<ProgramObj:?GLhandleARB,Location:?GLint>>).
+ call(5644, <<ProgramObj:?GLhandleARB,Location:?GLint>>).
%% @doc glGetUniformARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformARB.xml">external</a> documentation.
-spec getUniformivARB(ProgramObj, Location) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()} when ProgramObj :: integer(),Location :: integer().
getUniformivARB(ProgramObj,Location) ->
- call(5644, <<ProgramObj:?GLhandleARB,Location:?GLint>>).
+ call(5645, <<ProgramObj:?GLhandleARB,Location:?GLint>>).
%% @doc glGetShaderSourceARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetShaderSourceARB.xml">external</a> documentation.
-spec getShaderSourceARB(Obj, MaxLength) -> string() when Obj :: integer(),MaxLength :: integer().
getShaderSourceARB(Obj,MaxLength) ->
- call(5645, <<Obj:?GLhandleARB,MaxLength:?GLsizei>>).
+ call(5646, <<Obj:?GLhandleARB,MaxLength:?GLsizei>>).
%% @doc glBindAttribLocationARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindAttribLocationARB.xml">external</a> documentation.
-spec bindAttribLocationARB(ProgramObj, Index, Name) -> 'ok' when ProgramObj :: integer(),Index :: integer(),Name :: string().
bindAttribLocationARB(ProgramObj,Index,Name) ->
- cast(5646, <<ProgramObj:?GLhandleARB,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ cast(5647, <<ProgramObj:?GLhandleARB,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 5) rem 8)) rem 8)>>).
%% @doc glGetActiveAttribARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveAttribARB.xml">external</a> documentation.
-spec getActiveAttribARB(ProgramObj, Index, MaxLength) -> {Size :: integer(),Type :: enum(),Name :: string()} when ProgramObj :: integer(),Index :: integer(),MaxLength :: integer().
getActiveAttribARB(ProgramObj,Index,MaxLength) ->
- call(5647, <<ProgramObj:?GLhandleARB,Index:?GLuint,MaxLength:?GLsizei>>).
+ call(5648, <<ProgramObj:?GLhandleARB,Index:?GLuint,MaxLength:?GLsizei>>).
%% @doc glGetAttribLocationARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetAttribLocationARB.xml">external</a> documentation.
-spec getAttribLocationARB(ProgramObj, Name) -> integer() when ProgramObj :: integer(),Name :: string().
getAttribLocationARB(ProgramObj,Name) ->
- call(5648, <<ProgramObj:?GLhandleARB,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ call(5649, <<ProgramObj:?GLhandleARB,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 1) rem 8)) rem 8)>>).
%% @doc Determine if a name corresponds to a renderbuffer object
%%
@@ -13367,7 +13432,7 @@ getAttribLocationARB(ProgramObj,Name) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsRenderbuffer.xml">external</a> documentation.
-spec isRenderbuffer(Renderbuffer) -> 0|1 when Renderbuffer :: integer().
isRenderbuffer(Renderbuffer) ->
- call(5649, <<Renderbuffer:?GLuint>>).
+ call(5650, <<Renderbuffer:?GLuint>>).
%% @doc Bind a renderbuffer to a renderbuffer target
%%
@@ -13380,7 +13445,7 @@ isRenderbuffer(Renderbuffer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindRenderbuffer.xml">external</a> documentation.
-spec bindRenderbuffer(Target, Renderbuffer) -> 'ok' when Target :: enum(),Renderbuffer :: integer().
bindRenderbuffer(Target,Renderbuffer) ->
- cast(5650, <<Target:?GLenum,Renderbuffer:?GLuint>>).
+ cast(5651, <<Target:?GLenum,Renderbuffer:?GLuint>>).
%% @doc Delete renderbuffer objects
%%
@@ -13402,8 +13467,9 @@ bindRenderbuffer(Target,Renderbuffer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteRenderbuffers.xml">external</a> documentation.
-spec deleteRenderbuffers(Renderbuffers) -> 'ok' when Renderbuffers :: [integer()].
deleteRenderbuffers(Renderbuffers) ->
- cast(5651, <<(length(Renderbuffers)):?GLuint,
- (<< <<C:?GLuint>> || C <- Renderbuffers>>)/binary,0:(((1+length(Renderbuffers)) rem 2)*32)>>).
+ RenderbuffersLen = length(Renderbuffers),
+ cast(5652, <<RenderbuffersLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Renderbuffers>>)/binary,0:(((1+RenderbuffersLen) rem 2)*32)>>).
%% @doc Generate renderbuffer object names
%%
@@ -13421,7 +13487,7 @@ deleteRenderbuffers(Renderbuffers) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenRenderbuffers.xml">external</a> documentation.
-spec genRenderbuffers(N) -> [integer()] when N :: integer().
genRenderbuffers(N) ->
- call(5652, <<N:?GLsizei>>).
+ call(5653, <<N:?GLsizei>>).
%% @doc Establish data storage, format and dimensions of a renderbuffer object's image
%%
@@ -13442,7 +13508,7 @@ genRenderbuffers(N) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRenderbufferStorage.xml">external</a> documentation.
-spec renderbufferStorage(Target, Internalformat, Width, Height) -> 'ok' when Target :: enum(),Internalformat :: enum(),Width :: integer(),Height :: integer().
renderbufferStorage(Target,Internalformat,Width,Height) ->
- cast(5653, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5654, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei>>).
%% @doc Retrieve information about a bound renderbuffer object
%%
@@ -13470,7 +13536,7 @@ renderbufferStorage(Target,Internalformat,Width,Height) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetRenderbufferParameter.xml">external</a> documentation.
-spec getRenderbufferParameteriv(Target, Pname) -> integer() when Target :: enum(),Pname :: enum().
getRenderbufferParameteriv(Target,Pname) ->
- call(5654, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5655, <<Target:?GLenum,Pname:?GLenum>>).
%% @doc Determine if a name corresponds to a framebuffer object
%%
@@ -13484,7 +13550,7 @@ getRenderbufferParameteriv(Target,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsFramebuffer.xml">external</a> documentation.
-spec isFramebuffer(Framebuffer) -> 0|1 when Framebuffer :: integer().
isFramebuffer(Framebuffer) ->
- call(5655, <<Framebuffer:?GLuint>>).
+ call(5656, <<Framebuffer:?GLuint>>).
%% @doc Bind a framebuffer to a framebuffer target
%%
@@ -13502,7 +13568,7 @@ isFramebuffer(Framebuffer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindFramebuffer.xml">external</a> documentation.
-spec bindFramebuffer(Target, Framebuffer) -> 'ok' when Target :: enum(),Framebuffer :: integer().
bindFramebuffer(Target,Framebuffer) ->
- cast(5656, <<Target:?GLenum,Framebuffer:?GLuint>>).
+ cast(5657, <<Target:?GLenum,Framebuffer:?GLuint>>).
%% @doc Delete framebuffer objects
%%
@@ -13517,8 +13583,9 @@ bindFramebuffer(Target,Framebuffer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteFramebuffers.xml">external</a> documentation.
-spec deleteFramebuffers(Framebuffers) -> 'ok' when Framebuffers :: [integer()].
deleteFramebuffers(Framebuffers) ->
- cast(5657, <<(length(Framebuffers)):?GLuint,
- (<< <<C:?GLuint>> || C <- Framebuffers>>)/binary,0:(((1+length(Framebuffers)) rem 2)*32)>>).
+ FramebuffersLen = length(Framebuffers),
+ cast(5658, <<FramebuffersLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Framebuffers>>)/binary,0:(((1+FramebuffersLen) rem 2)*32)>>).
%% @doc Generate framebuffer object names
%%
@@ -13536,7 +13603,7 @@ deleteFramebuffers(Framebuffers) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenFramebuffers.xml">external</a> documentation.
-spec genFramebuffers(N) -> [integer()] when N :: integer().
genFramebuffers(N) ->
- call(5658, <<N:?GLsizei>>).
+ call(5659, <<N:?GLsizei>>).
%% @doc Check the completeness status of a framebuffer
%%
@@ -13587,25 +13654,25 @@ genFramebuffers(N) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCheckFramebufferStatus.xml">external</a> documentation.
-spec checkFramebufferStatus(Target) -> enum() when Target :: enum().
checkFramebufferStatus(Target) ->
- call(5659, <<Target:?GLenum>>).
+ call(5660, <<Target:?GLenum>>).
%% @doc
%% See {@link framebufferTexture/4}
-spec framebufferTexture1D(Target, Attachment, Textarget, Texture, Level) -> 'ok' when Target :: enum(),Attachment :: enum(),Textarget :: enum(),Texture :: integer(),Level :: integer().
framebufferTexture1D(Target,Attachment,Textarget,Texture,Level) ->
- cast(5660, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint>>).
+ cast(5661, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint>>).
%% @doc
%% See {@link framebufferTexture/4}
-spec framebufferTexture2D(Target, Attachment, Textarget, Texture, Level) -> 'ok' when Target :: enum(),Attachment :: enum(),Textarget :: enum(),Texture :: integer(),Level :: integer().
framebufferTexture2D(Target,Attachment,Textarget,Texture,Level) ->
- cast(5661, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint>>).
+ cast(5662, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint>>).
%% @doc
%% See {@link framebufferTexture/4}
-spec framebufferTexture3D(Target, Attachment, Textarget, Texture, Level, Zoffset) -> 'ok' when Target :: enum(),Attachment :: enum(),Textarget :: enum(),Texture :: integer(),Level :: integer(),Zoffset :: integer().
framebufferTexture3D(Target,Attachment,Textarget,Texture,Level,Zoffset) ->
- cast(5662, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint,Zoffset:?GLint>>).
+ cast(5663, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint,Zoffset:?GLint>>).
%% @doc Attach a renderbuffer as a logical buffer to the currently bound framebuffer object
%%
@@ -13637,7 +13704,7 @@ framebufferTexture3D(Target,Attachment,Textarget,Texture,Level,Zoffset) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFramebufferRenderbuffer.xml">external</a> documentation.
-spec framebufferRenderbuffer(Target, Attachment, Renderbuffertarget, Renderbuffer) -> 'ok' when Target :: enum(),Attachment :: enum(),Renderbuffertarget :: enum(),Renderbuffer :: integer().
framebufferRenderbuffer(Target,Attachment,Renderbuffertarget,Renderbuffer) ->
- cast(5663, <<Target:?GLenum,Attachment:?GLenum,Renderbuffertarget:?GLenum,Renderbuffer:?GLuint>>).
+ cast(5664, <<Target:?GLenum,Attachment:?GLenum,Renderbuffertarget:?GLenum,Renderbuffer:?GLuint>>).
%% @doc Retrieve information about attachments of a bound framebuffer object
%%
@@ -13733,7 +13800,7 @@ framebufferRenderbuffer(Target,Attachment,Renderbuffertarget,Renderbuffer) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetFramebufferAttachmentParameter.xml">external</a> documentation.
-spec getFramebufferAttachmentParameteriv(Target, Attachment, Pname) -> integer() when Target :: enum(),Attachment :: enum(),Pname :: enum().
getFramebufferAttachmentParameteriv(Target,Attachment,Pname) ->
- call(5664, <<Target:?GLenum,Attachment:?GLenum,Pname:?GLenum>>).
+ call(5665, <<Target:?GLenum,Attachment:?GLenum,Pname:?GLenum>>).
%% @doc Generate mipmaps for a specified texture target
%%
@@ -13753,7 +13820,7 @@ getFramebufferAttachmentParameteriv(Target,Attachment,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenerateMipmap.xml">external</a> documentation.
-spec generateMipmap(Target) -> 'ok' when Target :: enum().
generateMipmap(Target) ->
- cast(5665, <<Target:?GLenum>>).
+ cast(5666, <<Target:?GLenum>>).
%% @doc Copy a block of pixels from the read framebuffer to the draw framebuffer
%%
@@ -13795,7 +13862,7 @@ generateMipmap(Target) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlitFramebuffer.xml">external</a> documentation.
-spec blitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter) -> 'ok' when SrcX0 :: integer(),SrcY0 :: integer(),SrcX1 :: integer(),SrcY1 :: integer(),DstX0 :: integer(),DstY0 :: integer(),DstX1 :: integer(),DstY1 :: integer(),Mask :: integer(),Filter :: enum().
blitFramebuffer(SrcX0,SrcY0,SrcX1,SrcY1,DstX0,DstY0,DstX1,DstY1,Mask,Filter) ->
- cast(5666, <<SrcX0:?GLint,SrcY0:?GLint,SrcX1:?GLint,SrcY1:?GLint,DstX0:?GLint,DstY0:?GLint,DstX1:?GLint,DstY1:?GLint,Mask:?GLbitfield,Filter:?GLenum>>).
+ cast(5667, <<SrcX0:?GLint,SrcY0:?GLint,SrcX1:?GLint,SrcY1:?GLint,DstX0:?GLint,DstY0:?GLint,DstX1:?GLint,DstY1:?GLint,Mask:?GLbitfield,Filter:?GLenum>>).
%% @doc Establish data storage, format, dimensions and sample count of a renderbuffer object's image
%%
@@ -13819,19 +13886,19 @@ blitFramebuffer(SrcX0,SrcY0,SrcX1,SrcY1,DstX0,DstY0,DstX1,DstY1,Mask,Filter) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRenderbufferStorageMultisample.xml">external</a> documentation.
-spec renderbufferStorageMultisample(Target, Samples, Internalformat, Width, Height) -> 'ok' when Target :: enum(),Samples :: integer(),Internalformat :: enum(),Width :: integer(),Height :: integer().
renderbufferStorageMultisample(Target,Samples,Internalformat,Width,Height) ->
- cast(5667, <<Target:?GLenum,Samples:?GLsizei,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5668, <<Target:?GLenum,Samples:?GLsizei,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei>>).
%% @doc
%% See {@link framebufferTexture/4}
-spec framebufferTextureLayer(Target, Attachment, Texture, Level, Layer) -> 'ok' when Target :: enum(),Attachment :: enum(),Texture :: integer(),Level :: integer(),Layer :: integer().
framebufferTextureLayer(Target,Attachment,Texture,Level,Layer) ->
- cast(5668, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint,Layer:?GLint>>).
+ cast(5669, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint,Layer:?GLint>>).
%% @doc
%% See {@link framebufferTexture/4}
-spec framebufferTextureFaceARB(Target, Attachment, Texture, Level, Face) -> 'ok' when Target :: enum(),Attachment :: enum(),Texture :: integer(),Level :: integer(),Face :: enum().
framebufferTextureFaceARB(Target,Attachment,Texture,Level,Face) ->
- cast(5669, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint,Face:?GLenum>>).
+ cast(5670, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint,Face:?GLenum>>).
%% @doc Indicate modifications to a range of a mapped buffer
%%
@@ -13845,7 +13912,7 @@ framebufferTextureFaceARB(Target,Attachment,Texture,Level,Face) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFlushMappedBufferRange.xml">external</a> documentation.
-spec flushMappedBufferRange(Target, Offset, Length) -> 'ok' when Target :: enum(),Offset :: integer(),Length :: integer().
flushMappedBufferRange(Target,Offset,Length) ->
- cast(5670, <<Target:?GLenum,0:32,Offset:?GLintptr,Length:?GLsizeiptr>>).
+ cast(5671, <<Target:?GLenum,0:32,Offset:?GLintptr,Length:?GLsizeiptr>>).
%% @doc Bind a vertex array object
%%
@@ -13860,7 +13927,7 @@ flushMappedBufferRange(Target,Offset,Length) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindVertexArray.xml">external</a> documentation.
-spec bindVertexArray(Array) -> 'ok' when Array :: integer().
bindVertexArray(Array) ->
- cast(5671, <<Array:?GLuint>>).
+ cast(5672, <<Array:?GLuint>>).
%% @doc Delete vertex array objects
%%
@@ -13873,8 +13940,9 @@ bindVertexArray(Array) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteVertexArrays.xml">external</a> documentation.
-spec deleteVertexArrays(Arrays) -> 'ok' when Arrays :: [integer()].
deleteVertexArrays(Arrays) ->
- cast(5672, <<(length(Arrays)):?GLuint,
- (<< <<C:?GLuint>> || C <- Arrays>>)/binary,0:(((1+length(Arrays)) rem 2)*32)>>).
+ ArraysLen = length(Arrays),
+ cast(5673, <<ArraysLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Arrays>>)/binary,0:(((1+ArraysLen) rem 2)*32)>>).
%% @doc Generate vertex array object names
%%
@@ -13892,7 +13960,7 @@ deleteVertexArrays(Arrays) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenVertexArrays.xml">external</a> documentation.
-spec genVertexArrays(N) -> [integer()] when N :: integer().
genVertexArrays(N) ->
- call(5673, <<N:?GLsizei>>).
+ call(5674, <<N:?GLsizei>>).
%% @doc Determine if a name corresponds to a vertex array object
%%
@@ -13906,7 +13974,7 @@ genVertexArrays(N) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsVertexArray.xml">external</a> documentation.
-spec isVertexArray(Array) -> 0|1 when Array :: integer().
isVertexArray(Array) ->
- call(5674, <<Array:?GLuint>>).
+ call(5675, <<Array:?GLuint>>).
%% @doc Retrieve the index of a named uniform block
%%
@@ -13933,16 +14001,18 @@ isVertexArray(Array) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformIndices.xml">external</a> documentation.
-spec getUniformIndices(Program, UniformNames) -> [integer()] when Program :: integer(),UniformNames :: iolist().
getUniformIndices(Program,UniformNames) ->
- UniformNamesTemp = list_to_binary([[Str|[0]] || Str <- UniformNames ]),
- call(5675, <<Program:?GLuint,(length(UniformNames)):?GLuint,(size(UniformNamesTemp)):?GLuint,(UniformNamesTemp)/binary,0:((8-((size(UniformNamesTemp)+0) rem 8)) rem 8)>>).
+ UniformNamesTemp = list_to_binary([[Str|[0]] || Str <- UniformNames ]),
+ UniformNamesLen = length(UniformNames),
+ call(5676, <<Program:?GLuint,UniformNamesLen:?GLuint,(size(UniformNamesTemp)):?GLuint,(UniformNamesTemp)/binary,0:((8-((size(UniformNamesTemp)+0) rem 8)) rem 8)>>).
%% @doc glGetActiveUniforms
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniforms.xml">external</a> documentation.
-spec getActiveUniformsiv(Program, UniformIndices, Pname) -> [integer()] when Program :: integer(),UniformIndices :: [integer()],Pname :: enum().
getActiveUniformsiv(Program,UniformIndices,Pname) ->
- call(5676, <<Program:?GLuint,(length(UniformIndices)):?GLuint,
- (<< <<C:?GLuint>> || C <- UniformIndices>>)/binary,0:(((length(UniformIndices)) rem 2)*32),Pname:?GLenum>>).
+ UniformIndicesLen = length(UniformIndices),
+ call(5677, <<Program:?GLuint,UniformIndicesLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- UniformIndices>>)/binary,0:(((UniformIndicesLen) rem 2)*32),Pname:?GLenum>>).
%% @doc Query the name of an active uniform
%%
@@ -13971,7 +14041,7 @@ getActiveUniformsiv(Program,UniformIndices,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniformName.xml">external</a> documentation.
-spec getActiveUniformName(Program, UniformIndex, BufSize) -> string() when Program :: integer(),UniformIndex :: integer(),BufSize :: integer().
getActiveUniformName(Program,UniformIndex,BufSize) ->
- call(5677, <<Program:?GLuint,UniformIndex:?GLuint,BufSize:?GLsizei>>).
+ call(5678, <<Program:?GLuint,UniformIndex:?GLuint,BufSize:?GLsizei>>).
%% @doc Retrieve the index of a named uniform block
%%
@@ -13995,7 +14065,8 @@ getActiveUniformName(Program,UniformIndex,BufSize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformBlockIndex.xml">external</a> documentation.
-spec getUniformBlockIndex(Program, UniformBlockName) -> integer() when Program :: integer(),UniformBlockName :: string().
getUniformBlockIndex(Program,UniformBlockName) ->
- call(5678, <<Program:?GLuint,(list_to_binary([UniformBlockName|[0]]))/binary,0:((8-((length(UniformBlockName)+ 5) rem 8)) rem 8)>>).
+ UniformBlockNameLen = length(UniformBlockName),
+ call(5679, <<Program:?GLuint,(list_to_binary([UniformBlockName|[0]]))/binary,0:((8-((UniformBlockNameLen+ 5) rem 8)) rem 8)>>).
%% @doc Query information about an active uniform block
%%
@@ -14048,7 +14119,7 @@ getUniformBlockIndex(Program,UniformBlockName) ->
-spec getActiveUniformBlockiv(Program, UniformBlockIndex, Pname, Params) -> 'ok' when Program :: integer(),UniformBlockIndex :: integer(),Pname :: enum(),Params :: mem().
getActiveUniformBlockiv(Program,UniformBlockIndex,Pname,Params) ->
send_bin(Params),
- call(5679, <<Program:?GLuint,UniformBlockIndex:?GLuint,Pname:?GLenum>>).
+ call(5680, <<Program:?GLuint,UniformBlockIndex:?GLuint,Pname:?GLenum>>).
%% @doc Retrieve the name of an active uniform block
%%
@@ -14077,7 +14148,7 @@ getActiveUniformBlockiv(Program,UniformBlockIndex,Pname,Params) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniformBlockName.xml">external</a> documentation.
-spec getActiveUniformBlockName(Program, UniformBlockIndex, BufSize) -> string() when Program :: integer(),UniformBlockIndex :: integer(),BufSize :: integer().
getActiveUniformBlockName(Program,UniformBlockIndex,BufSize) ->
- call(5680, <<Program:?GLuint,UniformBlockIndex:?GLuint,BufSize:?GLsizei>>).
+ call(5681, <<Program:?GLuint,UniformBlockIndex:?GLuint,BufSize:?GLsizei>>).
%% @doc Assign a binding point to an active uniform block
%%
@@ -14097,7 +14168,7 @@ getActiveUniformBlockName(Program,UniformBlockIndex,BufSize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformBlockBinding.xml">external</a> documentation.
-spec uniformBlockBinding(Program, UniformBlockIndex, UniformBlockBinding) -> 'ok' when Program :: integer(),UniformBlockIndex :: integer(),UniformBlockBinding :: integer().
uniformBlockBinding(Program,UniformBlockIndex,UniformBlockBinding) ->
- cast(5681, <<Program:?GLuint,UniformBlockIndex:?GLuint,UniformBlockBinding:?GLuint>>).
+ cast(5682, <<Program:?GLuint,UniformBlockIndex:?GLuint,UniformBlockBinding:?GLuint>>).
%% @doc Copy part of the data store of a buffer object to the data store of another buffer object
%%
@@ -14123,7 +14194,7 @@ uniformBlockBinding(Program,UniformBlockIndex,UniformBlockBinding) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyBufferSubData.xml">external</a> documentation.
-spec copyBufferSubData(ReadTarget, WriteTarget, ReadOffset, WriteOffset, Size) -> 'ok' when ReadTarget :: enum(),WriteTarget :: enum(),ReadOffset :: integer(),WriteOffset :: integer(),Size :: integer().
copyBufferSubData(ReadTarget,WriteTarget,ReadOffset,WriteOffset,Size) ->
- cast(5682, <<ReadTarget:?GLenum,WriteTarget:?GLenum,ReadOffset:?GLintptr,WriteOffset:?GLintptr,Size:?GLsizeiptr>>).
+ cast(5683, <<ReadTarget:?GLenum,WriteTarget:?GLenum,ReadOffset:?GLintptr,WriteOffset:?GLintptr,Size:?GLsizeiptr>>).
%% @doc Render primitives from array data with a per-element offset
%%
@@ -14137,10 +14208,10 @@ copyBufferSubData(ReadTarget,WriteTarget,ReadOffset,WriteOffset,Size) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElementsBaseVertex.xml">external</a> documentation.
-spec drawElementsBaseVertex(Mode, Count, Type, Indices, Basevertex) -> 'ok' when Mode :: enum(),Count :: integer(),Type :: enum(),Indices :: offset()|mem(),Basevertex :: integer().
drawElementsBaseVertex(Mode,Count,Type,Indices,Basevertex) when is_integer(Indices) ->
- cast(5683, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Basevertex:?GLint>>);
+ cast(5684, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Basevertex:?GLint>>);
drawElementsBaseVertex(Mode,Count,Type,Indices,Basevertex) ->
send_bin(Indices),
- cast(5684, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Basevertex:?GLint>>).
+ cast(5685, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Basevertex:?GLint>>).
%% @doc Render primitives from array data with a per-element offset
%%
@@ -14159,10 +14230,10 @@ drawElementsBaseVertex(Mode,Count,Type,Indices,Basevertex) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawRangeElementsBaseVertex.xml">external</a> documentation.
-spec drawRangeElementsBaseVertex(Mode, Start, End, Count, Type, Indices, Basevertex) -> 'ok' when Mode :: enum(),Start :: integer(),End :: integer(),Count :: integer(),Type :: enum(),Indices :: offset()|mem(),Basevertex :: integer().
drawRangeElementsBaseVertex(Mode,Start,End,Count,Type,Indices,Basevertex) when is_integer(Indices) ->
- cast(5685, <<Mode:?GLenum,Start:?GLuint,End:?GLuint,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Basevertex:?GLint>>);
+ cast(5686, <<Mode:?GLenum,Start:?GLuint,End:?GLuint,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Basevertex:?GLint>>);
drawRangeElementsBaseVertex(Mode,Start,End,Count,Type,Indices,Basevertex) ->
send_bin(Indices),
- cast(5686, <<Mode:?GLenum,Start:?GLuint,End:?GLuint,Count:?GLsizei,Type:?GLenum,Basevertex:?GLint>>).
+ cast(5687, <<Mode:?GLenum,Start:?GLuint,End:?GLuint,Count:?GLsizei,Type:?GLenum,Basevertex:?GLint>>).
%% @doc Render multiple instances of a set of primitives from array data with a per-element offset
%%
@@ -14176,10 +14247,10 @@ drawRangeElementsBaseVertex(Mode,Start,End,Count,Type,Indices,Basevertex) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElementsInstancedBaseVertex.xml">external</a> documentation.
-spec drawElementsInstancedBaseVertex(Mode, Count, Type, Indices, Primcount, Basevertex) -> 'ok' when Mode :: enum(),Count :: integer(),Type :: enum(),Indices :: offset()|mem(),Primcount :: integer(),Basevertex :: integer().
drawElementsInstancedBaseVertex(Mode,Count,Type,Indices,Primcount,Basevertex) when is_integer(Indices) ->
- cast(5687, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei,Basevertex:?GLint>>);
+ cast(5688, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei,Basevertex:?GLint>>);
drawElementsInstancedBaseVertex(Mode,Count,Type,Indices,Primcount,Basevertex) ->
send_bin(Indices),
- cast(5688, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei,Basevertex:?GLint>>).
+ cast(5689, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei,Basevertex:?GLint>>).
%% @doc Specifiy the vertex to be used as the source of data for flat shaded varyings
%%
@@ -14213,7 +14284,7 @@ drawElementsInstancedBaseVertex(Mode,Count,Type,Indices,Primcount,Basevertex) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProvokingVertex.xml">external</a> documentation.
-spec provokingVertex(Mode) -> 'ok' when Mode :: enum().
provokingVertex(Mode) ->
- cast(5689, <<Mode:?GLenum>>).
+ cast(5690, <<Mode:?GLenum>>).
%% @doc Create a new sync object and insert it into the GL command stream
%%
@@ -14237,7 +14308,7 @@ provokingVertex(Mode) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFenceSync.xml">external</a> documentation.
-spec fenceSync(Condition, Flags) -> integer() when Condition :: enum(),Flags :: integer().
fenceSync(Condition,Flags) ->
- call(5690, <<Condition:?GLenum,Flags:?GLbitfield>>).
+ call(5691, <<Condition:?GLenum,Flags:?GLbitfield>>).
%% @doc Determine if a name corresponds to a sync object
%%
@@ -14248,7 +14319,7 @@ fenceSync(Condition,Flags) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsSync.xml">external</a> documentation.
-spec isSync(Sync) -> 0|1 when Sync :: integer().
isSync(Sync) ->
- call(5691, <<Sync:?GLsync>>).
+ call(5692, <<Sync:?GLsync>>).
%% @doc Delete a sync object
%%
@@ -14265,7 +14336,7 @@ isSync(Sync) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteSync.xml">external</a> documentation.
-spec deleteSync(Sync) -> 'ok' when Sync :: integer().
deleteSync(Sync) ->
- cast(5692, <<Sync:?GLsync>>).
+ cast(5693, <<Sync:?GLsync>>).
%% @doc Block and wait for a sync object to become signaled
%%
@@ -14291,7 +14362,7 @@ deleteSync(Sync) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClientWaitSync.xml">external</a> documentation.
-spec clientWaitSync(Sync, Flags, Timeout) -> enum() when Sync :: integer(),Flags :: integer(),Timeout :: integer().
clientWaitSync(Sync,Flags,Timeout) ->
- call(5693, <<Sync:?GLsync,Flags:?GLbitfield,0:32,Timeout:?GLuint64>>).
+ call(5694, <<Sync:?GLsync,Flags:?GLbitfield,0:32,Timeout:?GLuint64>>).
%% @doc Instruct the GL server to block until the specified sync object becomes signaled
%%
@@ -14313,13 +14384,13 @@ clientWaitSync(Sync,Flags,Timeout) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWaitSync.xml">external</a> documentation.
-spec waitSync(Sync, Flags, Timeout) -> 'ok' when Sync :: integer(),Flags :: integer(),Timeout :: integer().
waitSync(Sync,Flags,Timeout) ->
- cast(5694, <<Sync:?GLsync,Flags:?GLbitfield,0:32,Timeout:?GLuint64>>).
+ cast(5695, <<Sync:?GLsync,Flags:?GLbitfield,0:32,Timeout:?GLuint64>>).
%% @doc
%% See {@link getBooleanv/1}
-spec getInteger64v(Pname) -> [integer()] when Pname :: enum().
getInteger64v(Pname) ->
- call(5695, <<Pname:?GLenum>>).
+ call(5696, <<Pname:?GLenum>>).
%% @doc Query the properties of a sync object
%%
@@ -14354,7 +14425,7 @@ getInteger64v(Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSync.xml">external</a> documentation.
-spec getSynciv(Sync, Pname, BufSize) -> [integer()] when Sync :: integer(),Pname :: enum(),BufSize :: integer().
getSynciv(Sync,Pname,BufSize) ->
- call(5696, <<Sync:?GLsync,Pname:?GLenum,BufSize:?GLsizei>>).
+ call(5697, <<Sync:?GLsync,Pname:?GLenum,BufSize:?GLsizei>>).
%% @doc Establish the data storage, format, dimensions, and number of samples of a multisample texture's image
%%
@@ -14382,7 +14453,7 @@ getSynciv(Sync,Pname,BufSize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2DMultisample.xml">external</a> documentation.
-spec texImage2DMultisample(Target, Samples, Internalformat, Width, Height, Fixedsamplelocations) -> 'ok' when Target :: enum(),Samples :: integer(),Internalformat :: integer(),Width :: integer(),Height :: integer(),Fixedsamplelocations :: 0|1.
texImage2DMultisample(Target,Samples,Internalformat,Width,Height,Fixedsamplelocations) ->
- cast(5697, <<Target:?GLenum,Samples:?GLsizei,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Fixedsamplelocations:?GLboolean>>).
+ cast(5698, <<Target:?GLenum,Samples:?GLsizei,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Fixedsamplelocations:?GLboolean>>).
%% @doc Establish the data storage, format, dimensions, and number of samples of a multisample texture's image
%%
@@ -14410,7 +14481,7 @@ texImage2DMultisample(Target,Samples,Internalformat,Width,Height,Fixedsampleloca
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexImage3DMultisample.xml">external</a> documentation.
-spec texImage3DMultisample(Target, Samples, Internalformat, Width, Height, Depth, Fixedsamplelocations) -> 'ok' when Target :: enum(),Samples :: integer(),Internalformat :: integer(),Width :: integer(),Height :: integer(),Depth :: integer(),Fixedsamplelocations :: 0|1.
texImage3DMultisample(Target,Samples,Internalformat,Width,Height,Depth,Fixedsamplelocations) ->
- cast(5698, <<Target:?GLenum,Samples:?GLsizei,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Fixedsamplelocations:?GLboolean>>).
+ cast(5699, <<Target:?GLenum,Samples:?GLsizei,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Fixedsamplelocations:?GLboolean>>).
%% @doc Retrieve the location of a sample
%%
@@ -14428,7 +14499,7 @@ texImage3DMultisample(Target,Samples,Internalformat,Width,Height,Depth,Fixedsamp
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetMultisample.xml">external</a> documentation.
-spec getMultisamplefv(Pname, Index) -> {float(),float()} when Pname :: enum(),Index :: integer().
getMultisamplefv(Pname,Index) ->
- call(5699, <<Pname:?GLenum,Index:?GLuint>>).
+ call(5700, <<Pname:?GLenum,Index:?GLuint>>).
%% @doc Set the value of a sub-word of the sample mask
%%
@@ -14443,57 +14514,65 @@ getMultisamplefv(Pname,Index) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSampleMaski.xml">external</a> documentation.
-spec sampleMaski(Index, Mask) -> 'ok' when Index :: integer(),Mask :: integer().
sampleMaski(Index,Mask) ->
- cast(5700, <<Index:?GLuint,Mask:?GLbitfield>>).
+ cast(5701, <<Index:?GLuint,Mask:?GLbitfield>>).
%% @doc glNamedStringARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glNamedStringARB.xml">external</a> documentation.
-spec namedStringARB(Type, Name, String) -> 'ok' when Type :: enum(),Name :: string(),String :: string().
namedStringARB(Type,Name,String) ->
- cast(5701, <<Type:?GLenum,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8),(list_to_binary([String|[0]]))/binary,0:((8-((length(String)+ 1) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ StringLen = length(String),
+ cast(5702, <<Type:?GLenum,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 5) rem 8)) rem 8),(list_to_binary([String|[0]]))/binary,0:((8-((StringLen+ 1) rem 8)) rem 8)>>).
%% @doc glDeleteNamedStringARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteNamedStringARB.xml">external</a> documentation.
-spec deleteNamedStringARB(Name) -> 'ok' when Name :: string().
deleteNamedStringARB(Name) ->
- cast(5702, <<(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ cast(5703, <<(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 1) rem 8)) rem 8)>>).
%% @doc glCompileShaderIncludeARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompileShaderIncludeARB.xml">external</a> documentation.
-spec compileShaderIncludeARB(Shader, Path) -> 'ok' when Shader :: integer(),Path :: iolist().
compileShaderIncludeARB(Shader,Path) ->
- PathTemp = list_to_binary([[Str|[0]] || Str <- Path ]),
- cast(5703, <<Shader:?GLuint,(length(Path)):?GLuint,(size(PathTemp)):?GLuint,(PathTemp)/binary,0:((8-((size(PathTemp)+0) rem 8)) rem 8)>>).
+ PathTemp = list_to_binary([[Str|[0]] || Str <- Path ]),
+ PathLen = length(Path),
+ cast(5704, <<Shader:?GLuint,PathLen:?GLuint,(size(PathTemp)):?GLuint,(PathTemp)/binary,0:((8-((size(PathTemp)+0) rem 8)) rem 8)>>).
%% @doc glIsNamedStringARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsNamedStringARB.xml">external</a> documentation.
-spec isNamedStringARB(Name) -> 0|1 when Name :: string().
isNamedStringARB(Name) ->
- call(5704, <<(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ call(5705, <<(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 1) rem 8)) rem 8)>>).
%% @doc glGetNamedStringARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetNamedStringARB.xml">external</a> documentation.
-spec getNamedStringARB(Name, BufSize) -> string() when Name :: string(),BufSize :: integer().
getNamedStringARB(Name,BufSize) ->
- call(5705, <<(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8),BufSize:?GLsizei>>).
+ NameLen = length(Name),
+ call(5706, <<(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 1) rem 8)) rem 8),BufSize:?GLsizei>>).
%% @doc glGetNamedStringARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetNamedStringARB.xml">external</a> documentation.
-spec getNamedStringivARB(Name, Pname) -> integer() when Name :: string(),Pname :: enum().
getNamedStringivARB(Name,Pname) ->
- call(5706, <<(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8),Pname:?GLenum>>).
+ NameLen = length(Name),
+ call(5707, <<(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 1) rem 8)) rem 8),Pname:?GLenum>>).
%% @doc glBindFragDataLocationIndexe
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindFragDataLocationIndexe.xml">external</a> documentation.
-spec bindFragDataLocationIndexed(Program, ColorNumber, Index, Name) -> 'ok' when Program :: integer(),ColorNumber :: integer(),Index :: integer(),Name :: string().
bindFragDataLocationIndexed(Program,ColorNumber,Index,Name) ->
- cast(5707, <<Program:?GLuint,ColorNumber:?GLuint,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ cast(5708, <<Program:?GLuint,ColorNumber:?GLuint,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 5) rem 8)) rem 8)>>).
%% @doc Query the bindings of color indices to user-defined varying out variables
%%
@@ -14504,7 +14583,8 @@ bindFragDataLocationIndexed(Program,ColorNumber,Index,Name) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetFragDataIndex.xml">external</a> documentation.
-spec getFragDataIndex(Program, Name) -> integer() when Program :: integer(),Name :: string().
getFragDataIndex(Program,Name) ->
- call(5708, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ call(5709, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 5) rem 8)) rem 8)>>).
%% @doc Generate sampler object names
%%
@@ -14522,7 +14602,7 @@ getFragDataIndex(Program,Name) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenSamplers.xml">external</a> documentation.
-spec genSamplers(Count) -> [integer()] when Count :: integer().
genSamplers(Count) ->
- call(5709, <<Count:?GLsizei>>).
+ call(5710, <<Count:?GLsizei>>).
%% @doc Delete named sampler objects
%%
@@ -14535,8 +14615,9 @@ genSamplers(Count) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteSamplers.xml">external</a> documentation.
-spec deleteSamplers(Samplers) -> 'ok' when Samplers :: [integer()].
deleteSamplers(Samplers) ->
- cast(5710, <<(length(Samplers)):?GLuint,
- (<< <<C:?GLuint>> || C <- Samplers>>)/binary,0:(((1+length(Samplers)) rem 2)*32)>>).
+ SamplersLen = length(Samplers),
+ cast(5711, <<SamplersLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Samplers>>)/binary,0:(((1+SamplersLen) rem 2)*32)>>).
%% @doc Determine if a name corresponds to a sampler object
%%
@@ -14549,7 +14630,7 @@ deleteSamplers(Samplers) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsSampler.xml">external</a> documentation.
-spec isSampler(Sampler) -> 0|1 when Sampler :: integer().
isSampler(Sampler) ->
- call(5711, <<Sampler:?GLuint>>).
+ call(5712, <<Sampler:?GLuint>>).
%% @doc Bind a named sampler to a texturing target
%%
@@ -14566,7 +14647,7 @@ isSampler(Sampler) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindSampler.xml">external</a> documentation.
-spec bindSampler(Unit, Sampler) -> 'ok' when Unit :: integer(),Sampler :: integer().
bindSampler(Unit,Sampler) ->
- cast(5712, <<Unit:?GLuint,Sampler:?GLuint>>).
+ cast(5713, <<Unit:?GLuint,Sampler:?GLuint>>).
%% @doc Set sampler parameters
%%
@@ -14712,42 +14793,46 @@ bindSampler(Unit,Sampler) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSamplerParameter.xml">external</a> documentation.
-spec samplerParameteri(Sampler, Pname, Param) -> 'ok' when Sampler :: integer(),Pname :: enum(),Param :: integer().
samplerParameteri(Sampler,Pname,Param) ->
- cast(5713, <<Sampler:?GLuint,Pname:?GLenum,Param:?GLint>>).
+ cast(5714, <<Sampler:?GLuint,Pname:?GLenum,Param:?GLint>>).
%% @doc
%% See {@link samplerParameteri/3}
-spec samplerParameteriv(Sampler, Pname, Param) -> 'ok' when Sampler :: integer(),Pname :: enum(),Param :: [integer()].
samplerParameteriv(Sampler,Pname,Param) ->
- cast(5714, <<Sampler:?GLuint,Pname:?GLenum,(length(Param)):?GLuint,
- (<< <<C:?GLint>> || C <- Param>>)/binary,0:(((1+length(Param)) rem 2)*32)>>).
+ ParamLen = length(Param),
+ cast(5715, <<Sampler:?GLuint,Pname:?GLenum,ParamLen:?GLuint,
+ (<< <<C:?GLint>> || C <- Param>>)/binary,0:(((1+ParamLen) rem 2)*32)>>).
%% @doc
%% See {@link samplerParameteri/3}
-spec samplerParameterf(Sampler, Pname, Param) -> 'ok' when Sampler :: integer(),Pname :: enum(),Param :: float().
samplerParameterf(Sampler,Pname,Param) ->
- cast(5715, <<Sampler:?GLuint,Pname:?GLenum,Param:?GLfloat>>).
+ cast(5716, <<Sampler:?GLuint,Pname:?GLenum,Param:?GLfloat>>).
%% @doc
%% See {@link samplerParameteri/3}
-spec samplerParameterfv(Sampler, Pname, Param) -> 'ok' when Sampler :: integer(),Pname :: enum(),Param :: [float()].
samplerParameterfv(Sampler,Pname,Param) ->
- cast(5716, <<Sampler:?GLuint,Pname:?GLenum,(length(Param)):?GLuint,
- (<< <<C:?GLfloat>> || C <- Param>>)/binary,0:(((1+length(Param)) rem 2)*32)>>).
+ ParamLen = length(Param),
+ cast(5717, <<Sampler:?GLuint,Pname:?GLenum,ParamLen:?GLuint,
+ (<< <<C:?GLfloat>> || C <- Param>>)/binary,0:(((1+ParamLen) rem 2)*32)>>).
%% @doc
%% See {@link samplerParameteri/3}
-spec samplerParameterIiv(Sampler, Pname, Param) -> 'ok' when Sampler :: integer(),Pname :: enum(),Param :: [integer()].
samplerParameterIiv(Sampler,Pname,Param) ->
- cast(5717, <<Sampler:?GLuint,Pname:?GLenum,(length(Param)):?GLuint,
- (<< <<C:?GLint>> || C <- Param>>)/binary,0:(((1+length(Param)) rem 2)*32)>>).
+ ParamLen = length(Param),
+ cast(5718, <<Sampler:?GLuint,Pname:?GLenum,ParamLen:?GLuint,
+ (<< <<C:?GLint>> || C <- Param>>)/binary,0:(((1+ParamLen) rem 2)*32)>>).
%% @doc glSamplerParameterI
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSamplerParameterI.xml">external</a> documentation.
-spec samplerParameterIuiv(Sampler, Pname, Param) -> 'ok' when Sampler :: integer(),Pname :: enum(),Param :: [integer()].
samplerParameterIuiv(Sampler,Pname,Param) ->
- cast(5718, <<Sampler:?GLuint,Pname:?GLenum,(length(Param)):?GLuint,
- (<< <<C:?GLuint>> || C <- Param>>)/binary,0:(((1+length(Param)) rem 2)*32)>>).
+ ParamLen = length(Param),
+ cast(5719, <<Sampler:?GLuint,Pname:?GLenum,ParamLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Param>>)/binary,0:(((1+ParamLen) rem 2)*32)>>).
%% @doc Return sampler parameter values
%%
@@ -14794,26 +14879,26 @@ samplerParameterIuiv(Sampler,Pname,Param) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSamplerParameter.xml">external</a> documentation.
-spec getSamplerParameteriv(Sampler, Pname) -> [integer()] when Sampler :: integer(),Pname :: enum().
getSamplerParameteriv(Sampler,Pname) ->
- call(5719, <<Sampler:?GLuint,Pname:?GLenum>>).
+ call(5720, <<Sampler:?GLuint,Pname:?GLenum>>).
%% @doc
%% See {@link getSamplerParameteriv/2}
-spec getSamplerParameterIiv(Sampler, Pname) -> [integer()] when Sampler :: integer(),Pname :: enum().
getSamplerParameterIiv(Sampler,Pname) ->
- call(5720, <<Sampler:?GLuint,Pname:?GLenum>>).
+ call(5721, <<Sampler:?GLuint,Pname:?GLenum>>).
%% @doc
%% See {@link getSamplerParameteriv/2}
-spec getSamplerParameterfv(Sampler, Pname) -> [float()] when Sampler :: integer(),Pname :: enum().
getSamplerParameterfv(Sampler,Pname) ->
- call(5721, <<Sampler:?GLuint,Pname:?GLenum>>).
+ call(5722, <<Sampler:?GLuint,Pname:?GLenum>>).
%% @doc glGetSamplerParameterI
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSamplerParameterI.xml">external</a> documentation.
-spec getSamplerParameterIuiv(Sampler, Pname) -> [integer()] when Sampler :: integer(),Pname :: enum().
getSamplerParameterIuiv(Sampler,Pname) ->
- call(5722, <<Sampler:?GLuint,Pname:?GLenum>>).
+ call(5723, <<Sampler:?GLuint,Pname:?GLenum>>).
%% @doc Record the GL time into a query object after all previous commands have reached the GL server but have not yet necessarily executed.
%%
@@ -14828,21 +14913,21 @@ getSamplerParameterIuiv(Sampler,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glQueryCounter.xml">external</a> documentation.
-spec queryCounter(Id, Target) -> 'ok' when Id :: integer(),Target :: enum().
queryCounter(Id,Target) ->
- cast(5723, <<Id:?GLuint,Target:?GLenum>>).
+ cast(5724, <<Id:?GLuint,Target:?GLenum>>).
%% @doc glGetQueryObjecti64v
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQueryObjecti64v.xml">external</a> documentation.
-spec getQueryObjecti64v(Id, Pname) -> integer() when Id :: integer(),Pname :: enum().
getQueryObjecti64v(Id,Pname) ->
- call(5724, <<Id:?GLuint,Pname:?GLenum>>).
+ call(5725, <<Id:?GLuint,Pname:?GLenum>>).
%% @doc glGetQueryObjectui64v
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQueryObjectui64v.xml">external</a> documentation.
-spec getQueryObjectui64v(Id, Pname) -> integer() when Id :: integer(),Pname :: enum().
getQueryObjectui64v(Id,Pname) ->
- call(5725, <<Id:?GLuint,Pname:?GLenum>>).
+ call(5726, <<Id:?GLuint,Pname:?GLenum>>).
%% @doc Render primitives from array data, taking parameters from memory
%%
@@ -14872,10 +14957,10 @@ getQueryObjectui64v(Id,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawArraysIndirect.xml">external</a> documentation.
-spec drawArraysIndirect(Mode, Indirect) -> 'ok' when Mode :: enum(),Indirect :: offset()|mem().
drawArraysIndirect(Mode,Indirect) when is_integer(Indirect) ->
- cast(5726, <<Mode:?GLenum,Indirect:?GLuint>>);
+ cast(5727, <<Mode:?GLenum,Indirect:?GLuint>>);
drawArraysIndirect(Mode,Indirect) ->
send_bin(Indirect),
- cast(5727, <<Mode:?GLenum>>).
+ cast(5728, <<Mode:?GLenum>>).
%% @doc Render indexed primitives from array data, taking parameters from memory
%%
@@ -14912,131 +14997,144 @@ drawArraysIndirect(Mode,Indirect) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElementsIndirect.xml">external</a> documentation.
-spec drawElementsIndirect(Mode, Type, Indirect) -> 'ok' when Mode :: enum(),Type :: enum(),Indirect :: offset()|mem().
drawElementsIndirect(Mode,Type,Indirect) when is_integer(Indirect) ->
- cast(5728, <<Mode:?GLenum,Type:?GLenum,Indirect:?GLuint>>);
+ cast(5729, <<Mode:?GLenum,Type:?GLenum,Indirect:?GLuint>>);
drawElementsIndirect(Mode,Type,Indirect) ->
send_bin(Indirect),
- cast(5729, <<Mode:?GLenum,Type:?GLenum>>).
+ cast(5730, <<Mode:?GLenum,Type:?GLenum>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform1d(Location, X) -> 'ok' when Location :: integer(),X :: float().
uniform1d(Location,X) ->
- cast(5730, <<Location:?GLint,0:32,X:?GLdouble>>).
+ cast(5731, <<Location:?GLint,0:32,X:?GLdouble>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform2d(Location, X, Y) -> 'ok' when Location :: integer(),X :: float(),Y :: float().
uniform2d(Location,X,Y) ->
- cast(5731, <<Location:?GLint,0:32,X:?GLdouble,Y:?GLdouble>>).
+ cast(5732, <<Location:?GLint,0:32,X:?GLdouble,Y:?GLdouble>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform3d(Location, X, Y, Z) -> 'ok' when Location :: integer(),X :: float(),Y :: float(),Z :: float().
uniform3d(Location,X,Y,Z) ->
- cast(5732, <<Location:?GLint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5733, <<Location:?GLint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform4d(Location, X, Y, Z, W) -> 'ok' when Location :: integer(),X :: float(),Y :: float(),Z :: float(),W :: float().
uniform4d(Location,X,Y,Z,W) ->
- cast(5733, <<Location:?GLint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5734, <<Location:?GLint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform1dv(Location, Value) -> 'ok' when Location :: integer(),Value :: [float()].
uniform1dv(Location,Value) ->
- cast(5734, <<Location:?GLint,0:32,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5735, <<Location:?GLint,0:32,ValueLen:?GLuint,0:32,
(<< <<C:?GLdouble>> || C <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform2dv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{float(),float()}].
uniform2dv(Location,Value) ->
- cast(5735, <<Location:?GLint,0:32,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5736, <<Location:?GLint,0:32,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble>> || {V1,V2} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform3dv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{float(),float(),float()}].
uniform3dv(Location,Value) ->
- cast(5736, <<Location:?GLint,0:32,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5737, <<Location:?GLint,0:32,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble>> || {V1,V2,V3} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniform4dv(Location, Value) -> 'ok' when Location :: integer(),Value :: [{float(),float(),float(),float()}].
uniform4dv(Location,Value) ->
- cast(5737, <<Location:?GLint,0:32,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5738, <<Location:?GLint,0:32,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix2dv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float()}].
uniformMatrix2dv(Location,Transpose,Value) ->
- cast(5738, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5739, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix3dv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix3dv(Location,Transpose,Value) ->
- cast(5739, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5740, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix4dv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix4dv(Location,Transpose,Value) ->
- cast(5740, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5741, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble,V13:?GLdouble,V14:?GLdouble,V15:?GLdouble,V16:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12,V13,V14,V15,V16} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix2x3dv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float()}].
uniformMatrix2x3dv(Location,Transpose,Value) ->
- cast(5741, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5742, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix2x4dv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix2x4dv(Location,Transpose,Value) ->
- cast(5742, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5743, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix3x2dv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float()}].
uniformMatrix3x2dv(Location,Transpose,Value) ->
- cast(5743, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5744, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix3x4dv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix3x4dv(Location,Transpose,Value) ->
- cast(5744, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5745, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix4x2dv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix4x2dv(Location,Transpose,Value) ->
- cast(5745, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5746, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
%% @doc
%% See {@link uniform1f/2}
-spec uniformMatrix4x3dv(Location, Transpose, Value) -> 'ok' when Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
uniformMatrix4x3dv(Location,Transpose,Value) ->
- cast(5746, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5747, <<Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
%% @doc
%% See {@link getUniformfv/2}
-spec getUniformdv(Program, Location) -> matrix() when Program :: integer(),Location :: integer().
getUniformdv(Program,Location) ->
- call(5747, <<Program:?GLuint,Location:?GLint>>).
+ call(5748, <<Program:?GLuint,Location:?GLint>>).
%% @doc Retrieve the location of a subroutine uniform of a given shader stage within a program
%%
@@ -15055,7 +15153,8 @@ getUniformdv(Program,Location) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSubroutineUniformLocation.xml">external</a> documentation.
-spec getSubroutineUniformLocation(Program, Shadertype, Name) -> integer() when Program :: integer(),Shadertype :: enum(),Name :: string().
getSubroutineUniformLocation(Program,Shadertype,Name) ->
- call(5748, <<Program:?GLuint,Shadertype:?GLenum,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ call(5749, <<Program:?GLuint,Shadertype:?GLenum,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 1) rem 8)) rem 8)>>).
%% @doc Retrieve the index of a subroutine uniform of a given shader stage within a program
%%
@@ -15075,7 +15174,8 @@ getSubroutineUniformLocation(Program,Shadertype,Name) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSubroutineIndex.xml">external</a> documentation.
-spec getSubroutineIndex(Program, Shadertype, Name) -> integer() when Program :: integer(),Shadertype :: enum(),Name :: string().
getSubroutineIndex(Program,Shadertype,Name) ->
- call(5749, <<Program:?GLuint,Shadertype:?GLenum,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ NameLen = length(Name),
+ call(5750, <<Program:?GLuint,Shadertype:?GLenum,(list_to_binary([Name|[0]]))/binary,0:((8-((NameLen+ 1) rem 8)) rem 8)>>).
%% @doc Query the name of an active shader subroutine uniform
%%
@@ -15096,7 +15196,7 @@ getSubroutineIndex(Program,Shadertype,Name) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveSubroutineUniformName.xml">external</a> documentation.
-spec getActiveSubroutineUniformName(Program, Shadertype, Index, Bufsize) -> string() when Program :: integer(),Shadertype :: enum(),Index :: integer(),Bufsize :: integer().
getActiveSubroutineUniformName(Program,Shadertype,Index,Bufsize) ->
- call(5750, <<Program:?GLuint,Shadertype:?GLenum,Index:?GLuint,Bufsize:?GLsizei>>).
+ call(5751, <<Program:?GLuint,Shadertype:?GLenum,Index:?GLuint,Bufsize:?GLsizei>>).
%% @doc Query the name of an active shader subroutine
%%
@@ -15114,7 +15214,7 @@ getActiveSubroutineUniformName(Program,Shadertype,Index,Bufsize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveSubroutineName.xml">external</a> documentation.
-spec getActiveSubroutineName(Program, Shadertype, Index, Bufsize) -> string() when Program :: integer(),Shadertype :: enum(),Index :: integer(),Bufsize :: integer().
getActiveSubroutineName(Program,Shadertype,Index,Bufsize) ->
- call(5751, <<Program:?GLuint,Shadertype:?GLenum,Index:?GLuint,Bufsize:?GLsizei>>).
+ call(5752, <<Program:?GLuint,Shadertype:?GLenum,Index:?GLuint,Bufsize:?GLsizei>>).
%% @doc Load active subroutine uniforms
%%
@@ -15128,8 +15228,9 @@ getActiveSubroutineName(Program,Shadertype,Index,Bufsize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformSubroutines.xml">external</a> documentation.
-spec uniformSubroutinesuiv(Shadertype, Indices) -> 'ok' when Shadertype :: enum(),Indices :: [integer()].
uniformSubroutinesuiv(Shadertype,Indices) ->
- cast(5752, <<Shadertype:?GLenum,(length(Indices)):?GLuint,
- (<< <<C:?GLuint>> || C <- Indices>>)/binary,0:(((length(Indices)) rem 2)*32)>>).
+ IndicesLen = length(Indices),
+ cast(5753, <<Shadertype:?GLenum,IndicesLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Indices>>)/binary,0:(((IndicesLen) rem 2)*32)>>).
%% @doc Retrieve the value of a subroutine uniform of a given shader stage of the current program
%%
@@ -15142,7 +15243,7 @@ uniformSubroutinesuiv(Shadertype,Indices) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformSubroutine.xml">external</a> documentation.
-spec getUniformSubroutineuiv(Shadertype, Location) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()} when Shadertype :: enum(),Location :: integer().
getUniformSubroutineuiv(Shadertype,Location) ->
- call(5753, <<Shadertype:?GLenum,Location:?GLint>>).
+ call(5754, <<Shadertype:?GLenum,Location:?GLint>>).
%% @doc Retrieve properties of a program object corresponding to a specified shader stage
%%
@@ -15174,7 +15275,7 @@ getUniformSubroutineuiv(Shadertype,Location) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramStage.xml">external</a> documentation.
-spec getProgramStageiv(Program, Shadertype, Pname) -> integer() when Program :: integer(),Shadertype :: enum(),Pname :: enum().
getProgramStageiv(Program,Shadertype,Pname) ->
- call(5754, <<Program:?GLuint,Shadertype:?GLenum,Pname:?GLenum>>).
+ call(5755, <<Program:?GLuint,Shadertype:?GLenum,Pname:?GLenum>>).
%% @doc Specifies the parameters for patch primitives
%%
@@ -15201,14 +15302,15 @@ getProgramStageiv(Program,Shadertype,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPatchParameter.xml">external</a> documentation.
-spec patchParameteri(Pname, Value) -> 'ok' when Pname :: enum(),Value :: integer().
patchParameteri(Pname,Value) ->
- cast(5755, <<Pname:?GLenum,Value:?GLint>>).
+ cast(5756, <<Pname:?GLenum,Value:?GLint>>).
%% @doc
%% See {@link patchParameteri/2}
-spec patchParameterfv(Pname, Values) -> 'ok' when Pname :: enum(),Values :: [float()].
patchParameterfv(Pname,Values) ->
- cast(5756, <<Pname:?GLenum,(length(Values)):?GLuint,
- (<< <<C:?GLfloat>> || C <- Values>>)/binary,0:(((length(Values)) rem 2)*32)>>).
+ ValuesLen = length(Values),
+ cast(5757, <<Pname:?GLenum,ValuesLen:?GLuint,
+ (<< <<C:?GLfloat>> || C <- Values>>)/binary,0:(((ValuesLen) rem 2)*32)>>).
%% @doc Bind a transform feedback object
%%
@@ -15231,7 +15333,7 @@ patchParameterfv(Pname,Values) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindTransformFeedback.xml">external</a> documentation.
-spec bindTransformFeedback(Target, Id) -> 'ok' when Target :: enum(),Id :: integer().
bindTransformFeedback(Target,Id) ->
- cast(5757, <<Target:?GLenum,Id:?GLuint>>).
+ cast(5758, <<Target:?GLenum,Id:?GLuint>>).
%% @doc Delete transform feedback objects
%%
@@ -15244,8 +15346,9 @@ bindTransformFeedback(Target,Id) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteTransformFeedbacks.xml">external</a> documentation.
-spec deleteTransformFeedbacks(Ids) -> 'ok' when Ids :: [integer()].
deleteTransformFeedbacks(Ids) ->
- cast(5758, <<(length(Ids)):?GLuint,
- (<< <<C:?GLuint>> || C <- Ids>>)/binary,0:(((1+length(Ids)) rem 2)*32)>>).
+ IdsLen = length(Ids),
+ cast(5759, <<IdsLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Ids>>)/binary,0:(((1+IdsLen) rem 2)*32)>>).
%% @doc Reserve transform feedback object names
%%
@@ -15256,7 +15359,7 @@ deleteTransformFeedbacks(Ids) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenTransformFeedbacks.xml">external</a> documentation.
-spec genTransformFeedbacks(N) -> [integer()] when N :: integer().
genTransformFeedbacks(N) ->
- call(5759, <<N:?GLsizei>>).
+ call(5760, <<N:?GLsizei>>).
%% @doc Determine if a name corresponds to a transform feedback object
%%
@@ -15271,7 +15374,7 @@ genTransformFeedbacks(N) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsTransformFeedback.xml">external</a> documentation.
-spec isTransformFeedback(Id) -> 0|1 when Id :: integer().
isTransformFeedback(Id) ->
- call(5760, <<Id:?GLuint>>).
+ call(5761, <<Id:?GLuint>>).
%% @doc Pause transform feedback operations
%%
@@ -15284,7 +15387,7 @@ isTransformFeedback(Id) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPauseTransformFeedback.xml">external</a> documentation.
-spec pauseTransformFeedback() -> 'ok'.
pauseTransformFeedback() ->
- cast(5761, <<>>).
+ cast(5762, <<>>).
%% @doc Resume transform feedback operations
%%
@@ -15297,7 +15400,7 @@ pauseTransformFeedback() ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glResumeTransformFeedback.xml">external</a> documentation.
-spec resumeTransformFeedback() -> 'ok'.
resumeTransformFeedback() ->
- cast(5762, <<>>).
+ cast(5763, <<>>).
%% @doc Render primitives using a count derived from a transform feedback object
%%
@@ -15311,7 +15414,7 @@ resumeTransformFeedback() ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawTransformFeedback.xml">external</a> documentation.
-spec drawTransformFeedback(Mode, Id) -> 'ok' when Mode :: enum(),Id :: integer().
drawTransformFeedback(Mode,Id) ->
- cast(5763, <<Mode:?GLenum,Id:?GLuint>>).
+ cast(5764, <<Mode:?GLenum,Id:?GLuint>>).
%% @doc Render primitives using a count derived from a specifed stream of a transform feedback object
%%
@@ -15329,14 +15432,14 @@ drawTransformFeedback(Mode,Id) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawTransformFeedbackStream.xml">external</a> documentation.
-spec drawTransformFeedbackStream(Mode, Id, Stream) -> 'ok' when Mode :: enum(),Id :: integer(),Stream :: integer().
drawTransformFeedbackStream(Mode,Id,Stream) ->
- cast(5764, <<Mode:?GLenum,Id:?GLuint,Stream:?GLuint>>).
+ cast(5765, <<Mode:?GLenum,Id:?GLuint,Stream:?GLuint>>).
%% @doc glBeginQueryIndexe
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBeginQueryIndexe.xml">external</a> documentation.
-spec beginQueryIndexed(Target, Index, Id) -> 'ok' when Target :: enum(),Index :: integer(),Id :: integer().
beginQueryIndexed(Target,Index,Id) ->
- cast(5765, <<Target:?GLenum,Index:?GLuint,Id:?GLuint>>).
+ cast(5766, <<Target:?GLenum,Index:?GLuint,Id:?GLuint>>).
%% @doc Delimit the boundaries of a query object on an indexed target
%%
@@ -15413,7 +15516,7 @@ beginQueryIndexed(Target,Index,Id) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBeginQueryIndexed.xml">external</a> documentation.
-spec endQueryIndexed(Target, Index) -> 'ok' when Target :: enum(),Index :: integer().
endQueryIndexed(Target,Index) ->
- cast(5766, <<Target:?GLenum,Index:?GLuint>>).
+ cast(5767, <<Target:?GLenum,Index:?GLuint>>).
%% @doc Return parameters of an indexed query object target
%%
@@ -15431,7 +15534,7 @@ endQueryIndexed(Target,Index) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQueryIndexed.xml">external</a> documentation.
-spec getQueryIndexediv(Target, Index, Pname) -> integer() when Target :: enum(),Index :: integer(),Pname :: enum().
getQueryIndexediv(Target,Index,Pname) ->
- call(5767, <<Target:?GLenum,Index:?GLuint,Pname:?GLenum>>).
+ call(5768, <<Target:?GLenum,Index:?GLuint,Pname:?GLenum>>).
%% @doc Release resources consumed by the implementation's shader compiler
%%
@@ -15443,7 +15546,7 @@ getQueryIndexediv(Target,Index,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glReleaseShaderCompiler.xml">external</a> documentation.
-spec releaseShaderCompiler() -> 'ok'.
releaseShaderCompiler() ->
- cast(5768, <<>>).
+ cast(5769, <<>>).
%% @doc Load pre-compiled shader binaries
%%
@@ -15465,9 +15568,10 @@ releaseShaderCompiler() ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glShaderBinary.xml">external</a> documentation.
-spec shaderBinary(Shaders, Binaryformat, Binary) -> 'ok' when Shaders :: [integer()],Binaryformat :: enum(),Binary :: binary().
shaderBinary(Shaders,Binaryformat,Binary) ->
+ ShadersLen = length(Shaders),
send_bin(Binary),
- cast(5769, <<(length(Shaders)):?GLuint,
- (<< <<C:?GLuint>> || C <- Shaders>>)/binary,0:(((1+length(Shaders)) rem 2)*32),Binaryformat:?GLenum>>).
+ cast(5770, <<ShadersLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Shaders>>)/binary,0:(((1+ShadersLen) rem 2)*32),Binaryformat:?GLenum>>).
%% @doc Retrieve the range and precision for numeric formats supported by the shader compiler
%%
@@ -15492,20 +15596,20 @@ shaderBinary(Shaders,Binaryformat,Binary) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetShaderPrecisionFormat.xml">external</a> documentation.
-spec getShaderPrecisionFormat(Shadertype, Precisiontype) -> {Range :: {integer(),integer()},Precision :: integer()} when Shadertype :: enum(),Precisiontype :: enum().
getShaderPrecisionFormat(Shadertype,Precisiontype) ->
- call(5770, <<Shadertype:?GLenum,Precisiontype:?GLenum>>).
+ call(5771, <<Shadertype:?GLenum,Precisiontype:?GLenum>>).
%% @doc
%% See {@link depthRange/2}
-spec depthRangef(N, F) -> 'ok' when N :: clamp(),F :: clamp().
depthRangef(N,F) ->
- cast(5771, <<N:?GLclampf,F:?GLclampf>>).
+ cast(5772, <<N:?GLclampf,F:?GLclampf>>).
%% @doc glClearDepthf
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearDepthf.xml">external</a> documentation.
-spec clearDepthf(D) -> 'ok' when D :: clamp().
clearDepthf(D) ->
- cast(5772, <<D:?GLclampf>>).
+ cast(5773, <<D:?GLclampf>>).
%% @doc Return a binary representation of a program object's compiled and linked executable source
%%
@@ -15526,7 +15630,7 @@ clearDepthf(D) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramBinary.xml">external</a> documentation.
-spec getProgramBinary(Program, BufSize) -> {BinaryFormat :: enum(),Binary :: binary()} when Program :: integer(),BufSize :: integer().
getProgramBinary(Program,BufSize) ->
- call(5773, <<Program:?GLuint,BufSize:?GLsizei>>).
+ call(5774, <<Program:?GLuint,BufSize:?GLsizei>>).
%% @doc Load a program object with a program binary
%%
@@ -15554,7 +15658,7 @@ getProgramBinary(Program,BufSize) ->
-spec programBinary(Program, BinaryFormat, Binary) -> 'ok' when Program :: integer(),BinaryFormat :: enum(),Binary :: binary().
programBinary(Program,BinaryFormat,Binary) ->
send_bin(Binary),
- cast(5774, <<Program:?GLuint,BinaryFormat:?GLenum>>).
+ cast(5775, <<Program:?GLuint,BinaryFormat:?GLenum>>).
%% @doc Specify a parameter for a program object
%%
@@ -15579,7 +15683,7 @@ programBinary(Program,BinaryFormat,Binary) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramParameter.xml">external</a> documentation.
-spec programParameteri(Program, Pname, Value) -> 'ok' when Program :: integer(),Pname :: enum(),Value :: integer().
programParameteri(Program,Pname,Value) ->
- cast(5775, <<Program:?GLuint,Pname:?GLenum,Value:?GLint>>).
+ cast(5776, <<Program:?GLuint,Pname:?GLenum,Value:?GLint>>).
%% @doc Bind stages of a program object to a program pipeline
%%
@@ -15604,7 +15708,7 @@ programParameteri(Program,Pname,Value) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUseProgramStages.xml">external</a> documentation.
-spec useProgramStages(Pipeline, Stages, Program) -> 'ok' when Pipeline :: integer(),Stages :: integer(),Program :: integer().
useProgramStages(Pipeline,Stages,Program) ->
- cast(5776, <<Pipeline:?GLuint,Stages:?GLbitfield,Program:?GLuint>>).
+ cast(5777, <<Pipeline:?GLuint,Stages:?GLbitfield,Program:?GLuint>>).
%% @doc Set the active program object for a program pipeline object
%%
@@ -15616,15 +15720,16 @@ useProgramStages(Pipeline,Stages,Program) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glActiveShaderProgram.xml">external</a> documentation.
-spec activeShaderProgram(Pipeline, Program) -> 'ok' when Pipeline :: integer(),Program :: integer().
activeShaderProgram(Pipeline,Program) ->
- cast(5777, <<Pipeline:?GLuint,Program:?GLuint>>).
+ cast(5778, <<Pipeline:?GLuint,Program:?GLuint>>).
%% @doc glCreateShaderProgramv
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateShaderProgramv.xml">external</a> documentation.
-spec createShaderProgramv(Type, Strings) -> integer() when Type :: enum(),Strings :: iolist().
createShaderProgramv(Type,Strings) ->
- StringsTemp = list_to_binary([[Str|[0]] || Str <- Strings ]),
- call(5778, <<Type:?GLenum,(length(Strings)):?GLuint,(size(StringsTemp)):?GLuint,(StringsTemp)/binary,0:((8-((size(StringsTemp)+0) rem 8)) rem 8)>>).
+ StringsTemp = list_to_binary([[Str|[0]] || Str <- Strings ]),
+ StringsLen = length(Strings),
+ call(5779, <<Type:?GLenum,StringsLen:?GLuint,(size(StringsTemp)):?GLuint,(StringsTemp)/binary,0:((8-((size(StringsTemp)+0) rem 8)) rem 8)>>).
%% @doc Bind a program pipeline to the current context
%%
@@ -15646,7 +15751,7 @@ createShaderProgramv(Type,Strings) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindProgramPipeline.xml">external</a> documentation.
-spec bindProgramPipeline(Pipeline) -> 'ok' when Pipeline :: integer().
bindProgramPipeline(Pipeline) ->
- cast(5779, <<Pipeline:?GLuint>>).
+ cast(5780, <<Pipeline:?GLuint>>).
%% @doc Delete program pipeline objects
%%
@@ -15660,8 +15765,9 @@ bindProgramPipeline(Pipeline) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteProgramPipelines.xml">external</a> documentation.
-spec deleteProgramPipelines(Pipelines) -> 'ok' when Pipelines :: [integer()].
deleteProgramPipelines(Pipelines) ->
- cast(5780, <<(length(Pipelines)):?GLuint,
- (<< <<C:?GLuint>> || C <- Pipelines>>)/binary,0:(((1+length(Pipelines)) rem 2)*32)>>).
+ PipelinesLen = length(Pipelines),
+ cast(5781, <<PipelinesLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Pipelines>>)/binary,0:(((1+PipelinesLen) rem 2)*32)>>).
%% @doc Reserve program pipeline object names
%%
@@ -15672,7 +15778,7 @@ deleteProgramPipelines(Pipelines) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenProgramPipelines.xml">external</a> documentation.
-spec genProgramPipelines(N) -> [integer()] when N :: integer().
genProgramPipelines(N) ->
- call(5781, <<N:?GLsizei>>).
+ call(5782, <<N:?GLsizei>>).
%% @doc Determine if a name corresponds to a program pipeline object
%%
@@ -15687,7 +15793,7 @@ genProgramPipelines(N) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsProgramPipeline.xml">external</a> documentation.
-spec isProgramPipeline(Pipeline) -> 0|1 when Pipeline :: integer().
isProgramPipeline(Pipeline) ->
- call(5782, <<Pipeline:?GLuint>>).
+ call(5783, <<Pipeline:?GLuint>>).
%% @doc Retrieve properties of a program pipeline object
%%
@@ -15725,7 +15831,7 @@ isProgramPipeline(Pipeline) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramPipeline.xml">external</a> documentation.
-spec getProgramPipelineiv(Pipeline, Pname) -> integer() when Pipeline :: integer(),Pname :: enum().
getProgramPipelineiv(Pipeline,Pname) ->
- call(5783, <<Pipeline:?GLuint,Pname:?GLenum>>).
+ call(5784, <<Pipeline:?GLuint,Pname:?GLenum>>).
%% @doc Specify the value of a uniform variable for a specified program object
%%
@@ -15792,334 +15898,368 @@ getProgramPipelineiv(Pipeline,Pname) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
-spec programUniform1i(Program, Location, V0) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: integer().
programUniform1i(Program,Location,V0) ->
- cast(5784, <<Program:?GLuint,Location:?GLint,V0:?GLint>>).
+ cast(5785, <<Program:?GLuint,Location:?GLint,V0:?GLint>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform1iv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [integer()].
programUniform1iv(Program,Location,Value) ->
- cast(5785, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
- (<< <<C:?GLint>> || C <- Value>>)/binary,0:(((1+length(Value)) rem 2)*32)>>).
+ ValueLen = length(Value),
+ cast(5786, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
+ (<< <<C:?GLint>> || C <- Value>>)/binary,0:(((1+ValueLen) rem 2)*32)>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform1f(Program, Location, V0) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: float().
programUniform1f(Program,Location,V0) ->
- cast(5786, <<Program:?GLuint,Location:?GLint,V0:?GLfloat>>).
+ cast(5787, <<Program:?GLuint,Location:?GLint,V0:?GLfloat>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform1fv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [float()].
programUniform1fv(Program,Location,Value) ->
- cast(5787, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
- (<< <<C:?GLfloat>> || C <- Value>>)/binary,0:(((1+length(Value)) rem 2)*32)>>).
+ ValueLen = length(Value),
+ cast(5788, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
+ (<< <<C:?GLfloat>> || C <- Value>>)/binary,0:(((1+ValueLen) rem 2)*32)>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform1d(Program, Location, V0) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: float().
programUniform1d(Program,Location,V0) ->
- cast(5788, <<Program:?GLuint,Location:?GLint,V0:?GLdouble>>).
+ cast(5789, <<Program:?GLuint,Location:?GLint,V0:?GLdouble>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform1dv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [float()].
programUniform1dv(Program,Location,Value) ->
- cast(5789, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5790, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,0:32,
(<< <<C:?GLdouble>> || C <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform1ui(Program, Location, V0) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: integer().
programUniform1ui(Program,Location,V0) ->
- cast(5790, <<Program:?GLuint,Location:?GLint,V0:?GLuint>>).
+ cast(5791, <<Program:?GLuint,Location:?GLint,V0:?GLuint>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform1uiv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [integer()].
programUniform1uiv(Program,Location,Value) ->
- cast(5791, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
- (<< <<C:?GLuint>> || C <- Value>>)/binary,0:(((1+length(Value)) rem 2)*32)>>).
+ ValueLen = length(Value),
+ cast(5792, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Value>>)/binary,0:(((1+ValueLen) rem 2)*32)>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform2i(Program, Location, V0, V1) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: integer(),V1 :: integer().
programUniform2i(Program,Location,V0,V1) ->
- cast(5792, <<Program:?GLuint,Location:?GLint,V0:?GLint,V1:?GLint>>).
+ cast(5793, <<Program:?GLuint,Location:?GLint,V0:?GLint,V1:?GLint>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform2iv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{integer(),integer()}].
programUniform2iv(Program,Location,Value) ->
- cast(5793, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5794, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLint,V2:?GLint>> || {V1,V2} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform2f(Program, Location, V0, V1) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: float(),V1 :: float().
programUniform2f(Program,Location,V0,V1) ->
- cast(5794, <<Program:?GLuint,Location:?GLint,V0:?GLfloat,V1:?GLfloat>>).
+ cast(5795, <<Program:?GLuint,Location:?GLint,V0:?GLfloat,V1:?GLfloat>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform2fv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{float(),float()}].
programUniform2fv(Program,Location,Value) ->
- cast(5795, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5796, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat>> || {V1,V2} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform2d(Program, Location, V0, V1) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: float(),V1 :: float().
programUniform2d(Program,Location,V0,V1) ->
- cast(5796, <<Program:?GLuint,Location:?GLint,V0:?GLdouble,V1:?GLdouble>>).
+ cast(5797, <<Program:?GLuint,Location:?GLint,V0:?GLdouble,V1:?GLdouble>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform2dv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{float(),float()}].
programUniform2dv(Program,Location,Value) ->
- cast(5797, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5798, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble>> || {V1,V2} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform2ui(Program, Location, V0, V1) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: integer(),V1 :: integer().
programUniform2ui(Program,Location,V0,V1) ->
- cast(5798, <<Program:?GLuint,Location:?GLint,V0:?GLuint,V1:?GLuint>>).
+ cast(5799, <<Program:?GLuint,Location:?GLint,V0:?GLuint,V1:?GLuint>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform2uiv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{integer(),integer()}].
programUniform2uiv(Program,Location,Value) ->
- cast(5799, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5800, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLuint,V2:?GLuint>> || {V1,V2} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform3i(Program, Location, V0, V1, V2) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: integer(),V1 :: integer(),V2 :: integer().
programUniform3i(Program,Location,V0,V1,V2) ->
- cast(5800, <<Program:?GLuint,Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint>>).
+ cast(5801, <<Program:?GLuint,Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform3iv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{integer(),integer(),integer()}].
programUniform3iv(Program,Location,Value) ->
- cast(5801, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5802, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLint,V2:?GLint,V3:?GLint>> || {V1,V2,V3} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform3f(Program, Location, V0, V1, V2) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: float(),V1 :: float(),V2 :: float().
programUniform3f(Program,Location,V0,V1,V2) ->
- cast(5802, <<Program:?GLuint,Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat>>).
+ cast(5803, <<Program:?GLuint,Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform3fv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{float(),float(),float()}].
programUniform3fv(Program,Location,Value) ->
- cast(5803, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5804, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>> || {V1,V2,V3} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform3d(Program, Location, V0, V1, V2) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: float(),V1 :: float(),V2 :: float().
programUniform3d(Program,Location,V0,V1,V2) ->
- cast(5804, <<Program:?GLuint,Location:?GLint,V0:?GLdouble,V1:?GLdouble,V2:?GLdouble>>).
+ cast(5805, <<Program:?GLuint,Location:?GLint,V0:?GLdouble,V1:?GLdouble,V2:?GLdouble>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform3dv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{float(),float(),float()}].
programUniform3dv(Program,Location,Value) ->
- cast(5805, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5806, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble>> || {V1,V2,V3} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform3ui(Program, Location, V0, V1, V2) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: integer(),V1 :: integer(),V2 :: integer().
programUniform3ui(Program,Location,V0,V1,V2) ->
- cast(5806, <<Program:?GLuint,Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint>>).
+ cast(5807, <<Program:?GLuint,Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform3uiv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{integer(),integer(),integer()}].
programUniform3uiv(Program,Location,Value) ->
- cast(5807, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5808, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLuint,V2:?GLuint,V3:?GLuint>> || {V1,V2,V3} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform4i(Program, Location, V0, V1, V2, V3) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: integer(),V1 :: integer(),V2 :: integer(),V3 :: integer().
programUniform4i(Program,Location,V0,V1,V2,V3) ->
- cast(5808, <<Program:?GLuint,Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint,V3:?GLint>>).
+ cast(5809, <<Program:?GLuint,Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint,V3:?GLint>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform4iv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{integer(),integer(),integer(),integer()}].
programUniform4iv(Program,Location,Value) ->
- cast(5809, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5810, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform4f(Program, Location, V0, V1, V2, V3) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: float(),V1 :: float(),V2 :: float(),V3 :: float().
programUniform4f(Program,Location,V0,V1,V2,V3) ->
- cast(5810, <<Program:?GLuint,Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>>).
+ cast(5811, <<Program:?GLuint,Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform4fv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{float(),float(),float(),float()}].
programUniform4fv(Program,Location,Value) ->
- cast(5811, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5812, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform4d(Program, Location, V0, V1, V2, V3) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: float(),V1 :: float(),V2 :: float(),V3 :: float().
programUniform4d(Program,Location,V0,V1,V2,V3) ->
- cast(5812, <<Program:?GLuint,Location:?GLint,V0:?GLdouble,V1:?GLdouble,V2:?GLdouble,V3:?GLdouble>>).
+ cast(5813, <<Program:?GLuint,Location:?GLint,V0:?GLdouble,V1:?GLdouble,V2:?GLdouble,V3:?GLdouble>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform4dv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{float(),float(),float(),float()}].
programUniform4dv(Program,Location,Value) ->
- cast(5813, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5814, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform4ui(Program, Location, V0, V1, V2, V3) -> 'ok' when Program :: integer(),Location :: integer(),V0 :: integer(),V1 :: integer(),V2 :: integer(),V3 :: integer().
programUniform4ui(Program,Location,V0,V1,V2,V3) ->
- cast(5814, <<Program:?GLuint,Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint>>).
+ cast(5815, <<Program:?GLuint,Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniform4uiv(Program, Location, Value) -> 'ok' when Program :: integer(),Location :: integer(),Value :: [{integer(),integer(),integer(),integer()}].
programUniform4uiv(Program,Location,Value) ->
- cast(5815, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5816, <<Program:?GLuint,Location:?GLint,ValueLen:?GLuint,
(<< <<V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix2fv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float()}].
programUniformMatrix2fv(Program,Location,Transpose,Value) ->
- cast(5816, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5817, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix3fv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix3fv(Program,Location,Transpose,Value) ->
- cast(5817, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5818, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix4fv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix4fv(Program,Location,Transpose,Value) ->
- cast(5818, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5819, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat,V13:?GLfloat,V14:?GLfloat,V15:?GLfloat,V16:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12,V13,V14,V15,V16} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix2dv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float()}].
programUniformMatrix2dv(Program,Location,Transpose,Value) ->
- cast(5819, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5820, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix3dv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix3dv(Program,Location,Transpose,Value) ->
- cast(5820, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5821, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix4dv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix4dv(Program,Location,Transpose,Value) ->
- cast(5821, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5822, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble,V13:?GLdouble,V14:?GLdouble,V15:?GLdouble,V16:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12,V13,V14,V15,V16} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix2x3fv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float()}].
programUniformMatrix2x3fv(Program,Location,Transpose,Value) ->
- cast(5822, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5823, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix3x2fv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float()}].
programUniformMatrix3x2fv(Program,Location,Transpose,Value) ->
- cast(5823, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5824, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix2x4fv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix2x4fv(Program,Location,Transpose,Value) ->
- cast(5824, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5825, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix4x2fv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix4x2fv(Program,Location,Transpose,Value) ->
- cast(5825, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5826, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix3x4fv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix3x4fv(Program,Location,Transpose,Value) ->
- cast(5826, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5827, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix4x3fv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix4x3fv(Program,Location,Transpose,Value) ->
- cast(5827, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ ValueLen = length(Value),
+ cast(5828, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,ValueLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix2x3dv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float()}].
programUniformMatrix2x3dv(Program,Location,Transpose,Value) ->
- cast(5828, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5829, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix3x2dv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float()}].
programUniformMatrix3x2dv(Program,Location,Transpose,Value) ->
- cast(5829, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5830, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix2x4dv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix2x4dv(Program,Location,Transpose,Value) ->
- cast(5830, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5831, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix4x2dv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix4x2dv(Program,Location,Transpose,Value) ->
- cast(5831, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5832, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix3x4dv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix3x4dv(Program,Location,Transpose,Value) ->
- cast(5832, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5833, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
%% @doc
%% See {@link programUniform1i/3}
-spec programUniformMatrix4x3dv(Program, Location, Transpose, Value) -> 'ok' when Program :: integer(),Location :: integer(),Transpose :: 0|1,Value :: [{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}].
programUniformMatrix4x3dv(Program,Location,Transpose,Value) ->
- cast(5833, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ ValueLen = length(Value),
+ cast(5834, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,ValueLen:?GLuint,0:32,
(<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
%% @doc Validate a program pipeline object against current GL state
@@ -16140,7 +16280,7 @@ programUniformMatrix4x3dv(Program,Location,Transpose,Value) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glValidateProgramPipeline.xml">external</a> documentation.
-spec validateProgramPipeline(Pipeline) -> 'ok' when Pipeline :: integer().
validateProgramPipeline(Pipeline) ->
- cast(5834, <<Pipeline:?GLuint>>).
+ cast(5835, <<Pipeline:?GLuint>>).
%% @doc Retrieve the info log string from a program pipeline object
%%
@@ -16157,35 +16297,35 @@ validateProgramPipeline(Pipeline) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramPipelineInfoLog.xml">external</a> documentation.
-spec getProgramPipelineInfoLog(Pipeline, BufSize) -> string() when Pipeline :: integer(),BufSize :: integer().
getProgramPipelineInfoLog(Pipeline,BufSize) ->
- call(5835, <<Pipeline:?GLuint,BufSize:?GLsizei>>).
+ call(5836, <<Pipeline:?GLuint,BufSize:?GLsizei>>).
%% @doc glVertexAttribL
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribL.xml">external</a> documentation.
-spec vertexAttribL1d(Index, X) -> 'ok' when Index :: integer(),X :: float().
vertexAttribL1d(Index,X) ->
- cast(5836, <<Index:?GLuint,0:32,X:?GLdouble>>).
+ cast(5837, <<Index:?GLuint,0:32,X:?GLdouble>>).
%% @doc glVertexAttribL
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribL.xml">external</a> documentation.
-spec vertexAttribL2d(Index, X, Y) -> 'ok' when Index :: integer(),X :: float(),Y :: float().
vertexAttribL2d(Index,X,Y) ->
- cast(5837, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble>>).
+ cast(5838, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble>>).
%% @doc glVertexAttribL
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribL.xml">external</a> documentation.
-spec vertexAttribL3d(Index, X, Y, Z) -> 'ok' when Index :: integer(),X :: float(),Y :: float(),Z :: float().
vertexAttribL3d(Index,X,Y,Z) ->
- cast(5838, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5839, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @doc glVertexAttribL
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribL.xml">external</a> documentation.
-spec vertexAttribL4d(Index, X, Y, Z, W) -> 'ok' when Index :: integer(),X :: float(),Y :: float(),Z :: float(),W :: float().
vertexAttribL4d(Index,X,Y,Z,W) ->
- cast(5839, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5840, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
%% @equiv vertexAttribL1d(Index,X)
-spec vertexAttribL1dv(Index :: integer(),V) -> 'ok' when V :: {X :: float()}.
@@ -16208,24 +16348,25 @@ vertexAttribL4dv(Index,{X,Y,Z,W}) -> vertexAttribL4d(Index,X,Y,Z,W).
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribLPointer.xml">external</a> documentation.
-spec vertexAttribLPointer(Index, Size, Type, Stride, Pointer) -> 'ok' when Index :: integer(),Size :: integer(),Type :: enum(),Stride :: integer(),Pointer :: offset()|mem().
vertexAttribLPointer(Index,Size,Type,Stride,Pointer) when is_integer(Pointer) ->
- cast(5840, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5841, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
vertexAttribLPointer(Index,Size,Type,Stride,Pointer) ->
send_bin(Pointer),
- cast(5841, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
+ cast(5842, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
%% @doc glGetVertexAttribL
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetVertexAttribL.xml">external</a> documentation.
-spec getVertexAttribLdv(Index, Pname) -> {float(),float(),float(),float()} when Index :: integer(),Pname :: enum().
getVertexAttribLdv(Index,Pname) ->
- call(5842, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5843, <<Index:?GLuint,Pname:?GLenum>>).
%% @doc glViewportArrayv
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glViewportArrayv.xml">external</a> documentation.
-spec viewportArrayv(First, V) -> 'ok' when First :: integer(),V :: [{float(),float(),float(),float()}].
viewportArrayv(First,V) ->
- cast(5843, <<First:?GLuint,(length(V)):?GLuint,
+ VLen = length(V),
+ cast(5844, <<First:?GLuint,VLen:?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>> || {V1,V2,V3,V4} <- V>>)/binary>>).
%% @doc Set a specified viewport
@@ -16265,20 +16406,21 @@ viewportArrayv(First,V) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glViewportIndexed.xml">external</a> documentation.
-spec viewportIndexedf(Index, X, Y, W, H) -> 'ok' when Index :: integer(),X :: float(),Y :: float(),W :: float(),H :: float().
viewportIndexedf(Index,X,Y,W,H) ->
- cast(5844, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,W:?GLfloat,H:?GLfloat>>).
+ cast(5845, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,W:?GLfloat,H:?GLfloat>>).
%% @doc
%% See {@link viewportIndexedf/5}
-spec viewportIndexedfv(Index, V) -> 'ok' when Index :: integer(),V :: {float(),float(),float(),float()}.
viewportIndexedfv(Index,{V1,V2,V3,V4}) ->
- cast(5845, <<Index:?GLuint,V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>>).
+ cast(5846, <<Index:?GLuint,V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>>).
%% @doc glScissorArrayv
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glScissorArrayv.xml">external</a> documentation.
-spec scissorArrayv(First, V) -> 'ok' when First :: integer(),V :: [{integer(),integer(),integer(),integer()}].
scissorArrayv(First,V) ->
- cast(5846, <<First:?GLuint,(length(V)):?GLuint,
+ VLen = length(V),
+ cast(5847, <<First:?GLuint,VLen:?GLuint,
(<< <<V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>> || {V1,V2,V3,V4} <- V>>)/binary>>).
%% @doc glScissorIndexe
@@ -16286,21 +16428,22 @@ scissorArrayv(First,V) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glScissorIndexe.xml">external</a> documentation.
-spec scissorIndexed(Index, Left, Bottom, Width, Height) -> 'ok' when Index :: integer(),Left :: integer(),Bottom :: integer(),Width :: integer(),Height :: integer().
scissorIndexed(Index,Left,Bottom,Width,Height) ->
- cast(5847, <<Index:?GLuint,Left:?GLint,Bottom:?GLint,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5848, <<Index:?GLuint,Left:?GLint,Bottom:?GLint,Width:?GLsizei,Height:?GLsizei>>).
%% @doc glScissorIndexe
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glScissorIndexe.xml">external</a> documentation.
-spec scissorIndexedv(Index, V) -> 'ok' when Index :: integer(),V :: {integer(),integer(),integer(),integer()}.
scissorIndexedv(Index,{V1,V2,V3,V4}) ->
- cast(5848, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
+ cast(5849, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
%% @doc glDepthRangeArrayv
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDepthRangeArrayv.xml">external</a> documentation.
-spec depthRangeArrayv(First, V) -> 'ok' when First :: integer(),V :: [{clamp(),clamp()}].
depthRangeArrayv(First,V) ->
- cast(5849, <<First:?GLuint,0:32,(length(V)):?GLuint,0:32,
+ VLen = length(V),
+ cast(5850, <<First:?GLuint,0:32,VLen:?GLuint,0:32,
(<< <<V1:?GLclampd,V2:?GLclampd>> || {V1,V2} <- V>>)/binary>>).
%% @doc glDepthRangeIndexe
@@ -16308,48 +16451,50 @@ depthRangeArrayv(First,V) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDepthRangeIndexe.xml">external</a> documentation.
-spec depthRangeIndexed(Index, N, F) -> 'ok' when Index :: integer(),N :: clamp(),F :: clamp().
depthRangeIndexed(Index,N,F) ->
- cast(5850, <<Index:?GLuint,0:32,N:?GLclampd,F:?GLclampd>>).
+ cast(5851, <<Index:?GLuint,0:32,N:?GLclampd,F:?GLclampd>>).
%% @doc
%% See {@link getBooleanv/1}
-spec getFloati_v(Target, Index) -> [float()] when Target :: enum(),Index :: integer().
getFloati_v(Target,Index) ->
- call(5851, <<Target:?GLenum,Index:?GLuint>>).
+ call(5852, <<Target:?GLenum,Index:?GLuint>>).
%% @doc
%% See {@link getBooleanv/1}
-spec getDoublei_v(Target, Index) -> [float()] when Target :: enum(),Index :: integer().
getDoublei_v(Target,Index) ->
- call(5852, <<Target:?GLenum,Index:?GLuint>>).
+ call(5853, <<Target:?GLenum,Index:?GLuint>>).
%% @doc glDebugMessageControlARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDebugMessageControlARB.xml">external</a> documentation.
-spec debugMessageControlARB(Source, Type, Severity, Ids, Enabled) -> 'ok' when Source :: enum(),Type :: enum(),Severity :: enum(),Ids :: [integer()],Enabled :: 0|1.
debugMessageControlARB(Source,Type,Severity,Ids,Enabled) ->
- cast(5853, <<Source:?GLenum,Type:?GLenum,Severity:?GLenum,(length(Ids)):?GLuint,
- (<< <<C:?GLuint>> || C <- Ids>>)/binary,0:(((length(Ids)) rem 2)*32),Enabled:?GLboolean>>).
+ IdsLen = length(Ids),
+ cast(5854, <<Source:?GLenum,Type:?GLenum,Severity:?GLenum,IdsLen:?GLuint,
+ (<< <<C:?GLuint>> || C <- Ids>>)/binary,0:(((IdsLen) rem 2)*32),Enabled:?GLboolean>>).
%% @doc glDebugMessageInsertARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDebugMessageInsertARB.xml">external</a> documentation.
-spec debugMessageInsertARB(Source, Type, Id, Severity, Buf) -> 'ok' when Source :: enum(),Type :: enum(),Id :: integer(),Severity :: enum(),Buf :: string().
debugMessageInsertARB(Source,Type,Id,Severity,Buf) ->
- cast(5854, <<Source:?GLenum,Type:?GLenum,Id:?GLuint,Severity:?GLenum,(list_to_binary([Buf|[0]]))/binary,0:((8-((length(Buf)+ 1) rem 8)) rem 8)>>).
+ BufLen = length(Buf),
+ cast(5855, <<Source:?GLenum,Type:?GLenum,Id:?GLuint,Severity:?GLenum,(list_to_binary([Buf|[0]]))/binary,0:((8-((BufLen+ 1) rem 8)) rem 8)>>).
%% @doc glGetDebugMessageLogARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetDebugMessageLogARB.xml">external</a> documentation.
-spec getDebugMessageLogARB(Count, Bufsize) -> {integer(),Sources :: [enum()],Types :: [enum()],Ids :: [integer()],Severities :: [enum()],MessageLog :: [string()]} when Count :: integer(),Bufsize :: integer().
getDebugMessageLogARB(Count,Bufsize) ->
- call(5855, <<Count:?GLuint,Bufsize:?GLsizei>>).
+ call(5856, <<Count:?GLuint,Bufsize:?GLsizei>>).
%% @doc glGetGraphicsResetStatusARB
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetGraphicsResetStatusARB.xml">external</a> documentation.
-spec getGraphicsResetStatusARB() -> enum().
getGraphicsResetStatusARB() ->
- call(5856, <<>>).
+ call(5857, <<>>).
%% @doc Draw multiple instances of a range of elements with offset applied to instanced attributes
%%
@@ -16372,7 +16517,7 @@ getGraphicsResetStatusARB() ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawArraysInstancedBaseInstance.xml">external</a> documentation.
-spec drawArraysInstancedBaseInstance(Mode, First, Count, Primcount, Baseinstance) -> 'ok' when Mode :: enum(),First :: integer(),Count :: integer(),Primcount :: integer(),Baseinstance :: integer().
drawArraysInstancedBaseInstance(Mode,First,Count,Primcount,Baseinstance) ->
- cast(5857, <<Mode:?GLenum,First:?GLint,Count:?GLsizei,Primcount:?GLsizei,Baseinstance:?GLuint>>).
+ cast(5858, <<Mode:?GLenum,First:?GLint,Count:?GLsizei,Primcount:?GLsizei,Baseinstance:?GLuint>>).
%% @doc Draw multiple instances of a set of elements with offset applied to instanced attributes
%%
@@ -16395,10 +16540,10 @@ drawArraysInstancedBaseInstance(Mode,First,Count,Primcount,Baseinstance) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElementsInstancedBaseInstance.xml">external</a> documentation.
-spec drawElementsInstancedBaseInstance(Mode, Count, Type, Indices, Primcount, Baseinstance) -> 'ok' when Mode :: enum(),Count :: integer(),Type :: enum(),Indices :: offset()|mem(),Primcount :: integer(),Baseinstance :: integer().
drawElementsInstancedBaseInstance(Mode,Count,Type,Indices,Primcount,Baseinstance) when is_integer(Indices) ->
- cast(5858, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei,Baseinstance:?GLuint>>);
+ cast(5859, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei,Baseinstance:?GLuint>>);
drawElementsInstancedBaseInstance(Mode,Count,Type,Indices,Primcount,Baseinstance) ->
send_bin(Indices),
- cast(5859, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei,Baseinstance:?GLuint>>).
+ cast(5860, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei,Baseinstance:?GLuint>>).
%% @doc Render multiple instances of a set of primitives from array data with a per-element offset
%%
@@ -16419,31 +16564,31 @@ drawElementsInstancedBaseInstance(Mode,Count,Type,Indices,Primcount,Baseinstance
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElementsInstancedBaseVertexBaseInstance.xml">external</a> documentation.
-spec drawElementsInstancedBaseVertexBaseInstance(Mode, Count, Type, Indices, Primcount, Basevertex, Baseinstance) -> 'ok' when Mode :: enum(),Count :: integer(),Type :: enum(),Indices :: offset()|mem(),Primcount :: integer(),Basevertex :: integer(),Baseinstance :: integer().
drawElementsInstancedBaseVertexBaseInstance(Mode,Count,Type,Indices,Primcount,Basevertex,Baseinstance) when is_integer(Indices) ->
- cast(5860, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei,Basevertex:?GLint,Baseinstance:?GLuint>>);
+ cast(5861, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei,Basevertex:?GLint,Baseinstance:?GLuint>>);
drawElementsInstancedBaseVertexBaseInstance(Mode,Count,Type,Indices,Primcount,Basevertex,Baseinstance) ->
send_bin(Indices),
- cast(5861, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei,Basevertex:?GLint,Baseinstance:?GLuint>>).
+ cast(5862, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei,Basevertex:?GLint,Baseinstance:?GLuint>>).
%% @doc glDrawTransformFeedbackInstance
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawTransformFeedbackInstance.xml">external</a> documentation.
-spec drawTransformFeedbackInstanced(Mode, Id, Primcount) -> 'ok' when Mode :: enum(),Id :: integer(),Primcount :: integer().
drawTransformFeedbackInstanced(Mode,Id,Primcount) ->
- cast(5862, <<Mode:?GLenum,Id:?GLuint,Primcount:?GLsizei>>).
+ cast(5863, <<Mode:?GLenum,Id:?GLuint,Primcount:?GLsizei>>).
%% @doc glDrawTransformFeedbackStreamInstance
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawTransformFeedbackStreamInstance.xml">external</a> documentation.
-spec drawTransformFeedbackStreamInstanced(Mode, Id, Stream, Primcount) -> 'ok' when Mode :: enum(),Id :: integer(),Stream :: integer(),Primcount :: integer().
drawTransformFeedbackStreamInstanced(Mode,Id,Stream,Primcount) ->
- cast(5863, <<Mode:?GLenum,Id:?GLuint,Stream:?GLuint,Primcount:?GLsizei>>).
+ cast(5864, <<Mode:?GLenum,Id:?GLuint,Stream:?GLuint,Primcount:?GLsizei>>).
%% @doc glGetInternalformat
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetInternalformat.xml">external</a> documentation.
-spec getInternalformativ(Target, Internalformat, Pname, BufSize) -> [integer()] when Target :: enum(),Internalformat :: enum(),Pname :: enum(),BufSize :: integer().
getInternalformativ(Target,Internalformat,Pname,BufSize) ->
- call(5864, <<Target:?GLenum,Internalformat:?GLenum,Pname:?GLenum,BufSize:?GLsizei>>).
+ call(5865, <<Target:?GLenum,Internalformat:?GLenum,Pname:?GLenum,BufSize:?GLsizei>>).
%% @doc Bind a level of a texture to an image unit
%%
@@ -16505,7 +16650,7 @@ getInternalformativ(Target,Internalformat,Pname,BufSize) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindImageTexture.xml">external</a> documentation.
-spec bindImageTexture(Unit, Texture, Level, Layered, Layer, Access, Format) -> 'ok' when Unit :: integer(),Texture :: integer(),Level :: integer(),Layered :: 0|1,Layer :: integer(),Access :: enum(),Format :: enum().
bindImageTexture(Unit,Texture,Level,Layered,Layer,Access,Format) ->
- cast(5865, <<Unit:?GLuint,Texture:?GLuint,Level:?GLint,Layered:?GLboolean,0:24,Layer:?GLint,Access:?GLenum,Format:?GLenum>>).
+ cast(5866, <<Unit:?GLuint,Texture:?GLuint,Level:?GLint,Layered:?GLboolean,0:24,Layer:?GLint,Access:?GLenum,Format:?GLenum>>).
%% @doc Defines a barrier ordering memory transactions
%%
@@ -16632,7 +16777,7 @@ bindImageTexture(Unit,Texture,Level,Layered,Layer,Access,Format) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMemoryBarrier.xml">external</a> documentation.
-spec memoryBarrier(Barriers) -> 'ok' when Barriers :: integer().
memoryBarrier(Barriers) ->
- cast(5866, <<Barriers:?GLbitfield>>).
+ cast(5867, <<Barriers:?GLbitfield>>).
%% @doc Simultaneously specify storage for all levels of a one-dimensional texture
%%
@@ -16665,7 +16810,7 @@ memoryBarrier(Barriers) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexStorage1D.xml">external</a> documentation.
-spec texStorage1D(Target, Levels, Internalformat, Width) -> 'ok' when Target :: enum(),Levels :: integer(),Internalformat :: enum(),Width :: integer().
texStorage1D(Target,Levels,Internalformat,Width) ->
- cast(5867, <<Target:?GLenum,Levels:?GLsizei,Internalformat:?GLenum,Width:?GLsizei>>).
+ cast(5868, <<Target:?GLenum,Levels:?GLsizei,Internalformat:?GLenum,Width:?GLsizei>>).
%% @doc Simultaneously specify storage for all levels of a two-dimensional or one-dimensional array texture
%%
@@ -16710,7 +16855,7 @@ texStorage1D(Target,Levels,Internalformat,Width) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexStorage2D.xml">external</a> documentation.
-spec texStorage2D(Target, Levels, Internalformat, Width, Height) -> 'ok' when Target :: enum(),Levels :: integer(),Internalformat :: enum(),Width :: integer(),Height :: integer().
texStorage2D(Target,Levels,Internalformat,Width,Height) ->
- cast(5868, <<Target:?GLenum,Levels:?GLsizei,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5869, <<Target:?GLenum,Levels:?GLsizei,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei>>).
%% @doc Simultaneously specify storage for all levels of a three-dimensional, two-dimensional array or cube-map array texture
%%
@@ -16753,19 +16898,19 @@ texStorage2D(Target,Levels,Internalformat,Width,Height) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexStorage3D.xml">external</a> documentation.
-spec texStorage3D(Target, Levels, Internalformat, Width, Height, Depth) -> 'ok' when Target :: enum(),Levels :: integer(),Internalformat :: enum(),Width :: integer(),Height :: integer(),Depth :: integer().
texStorage3D(Target,Levels,Internalformat,Width,Height,Depth) ->
- cast(5869, <<Target:?GLenum,Levels:?GLsizei,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei>>).
+ cast(5870, <<Target:?GLenum,Levels:?GLsizei,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei>>).
%% @doc glDepthBoundsEXT
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDepthBoundsEXT.xml">external</a> documentation.
-spec depthBoundsEXT(Zmin, Zmax) -> 'ok' when Zmin :: clamp(),Zmax :: clamp().
depthBoundsEXT(Zmin,Zmax) ->
- cast(5870, <<Zmin:?GLclampd,Zmax:?GLclampd>>).
+ cast(5871, <<Zmin:?GLclampd,Zmax:?GLclampd>>).
%% @doc glStencilClearTagEXT
%%
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilClearTagEXT.xml">external</a> documentation.
-spec stencilClearTagEXT(StencilTagBits, StencilClearTag) -> 'ok' when StencilTagBits :: integer(),StencilClearTag :: integer().
stencilClearTagEXT(StencilTagBits,StencilClearTag) ->
- cast(5871, <<StencilTagBits:?GLsizei,StencilClearTag:?GLuint>>).
+ cast(5872, <<StencilTagBits:?GLsizei,StencilClearTag:?GLuint>>).
diff --git a/lib/wx/src/gen/glu.erl b/lib/wx/src/gen/glu.erl
index 47d9a83999..f641f41262 100644
--- a/lib/wx/src/gen/glu.erl
+++ b/lib/wx/src/gen/glu.erl
@@ -334,7 +334,9 @@ build3DMipmaps(Target,InternalFormat,Width,Height,Depth,Format,Type,Data) ->
%% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluCheckExtension.xml">external</a> documentation.
-spec checkExtension(ExtName, ExtString) -> 0|1 when ExtName :: string(),ExtString :: string().
checkExtension(ExtName,ExtString) ->
- call(5016, <<(list_to_binary([ExtName|[0]]))/binary,0:((8-((length(ExtName)+ 1) rem 8)) rem 8),(list_to_binary([ExtString|[0]]))/binary,0:((8-((length(ExtString)+ 1) rem 8)) rem 8)>>).
+ ExtNameLen = length(ExtName),
+ ExtStringLen = length(ExtString),
+ call(5016, <<(list_to_binary([ExtName|[0]]))/binary,0:((8-((ExtNameLen+ 1) rem 8)) rem 8),(list_to_binary([ExtString|[0]]))/binary,0:((8-((ExtStringLen+ 1) rem 8)) rem 8)>>).
%% @doc Draw a cylinder
%%
diff --git a/lib/wx/src/gen/wxArtProvider.erl b/lib/wx/src/gen/wxArtProvider.erl
index da220a90c8..b52c141b1f 100644
--- a/lib/wx/src/gen/wxArtProvider.erl
+++ b/lib/wx/src/gen/wxArtProvider.erl
@@ -40,7 +40,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
Id::unicode:chardata().
getBitmap(Id)
- when is_list(Id) ->
+ when ?is_chardata(Id) ->
getBitmap(Id, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxartprovider.html#wxartprovidergetbitmap">external documentation</a>.
@@ -49,7 +49,7 @@ getBitmap(Id)
Option :: {'client', unicode:chardata()}
| {'size', {W::integer(), H::integer()}}.
getBitmap(Id, Options)
- when is_list(Id),is_list(Options) ->
+ when ?is_chardata(Id),is_list(Options) ->
Id_UC = unicode:characters_to_binary([Id,0]),
MOpts = fun({client, Client}, Acc) -> Client_UC = unicode:characters_to_binary([Client, $_, $C,0]),[<<1:32/?UI,(byte_size(Client_UC)):32/?UI,(Client_UC)/binary, 0:(((8- ((0+byte_size(Client_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
({size, {SizeW,SizeH}}, Acc) -> [<<2:32/?UI,SizeW:32/?UI,SizeH:32/?UI,0:32>>|Acc];
@@ -63,7 +63,7 @@ getBitmap(Id, Options)
Id::unicode:chardata().
getIcon(Id)
- when is_list(Id) ->
+ when ?is_chardata(Id) ->
getIcon(Id, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxartprovider.html#wxartprovidergeticon">external documentation</a>.
@@ -72,7 +72,7 @@ getIcon(Id)
Option :: {'client', unicode:chardata()}
| {'size', {W::integer(), H::integer()}}.
getIcon(Id, Options)
- when is_list(Id),is_list(Options) ->
+ when ?is_chardata(Id),is_list(Options) ->
Id_UC = unicode:characters_to_binary([Id,0]),
MOpts = fun({client, Client}, Acc) -> Client_UC = unicode:characters_to_binary([Client, $_, $C,0]),[<<1:32/?UI,(byte_size(Client_UC)):32/?UI,(Client_UC)/binary, 0:(((8- ((0+byte_size(Client_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
({size, {SizeW,SizeH}}, Acc) -> [<<2:32/?UI,SizeW:32/?UI,SizeH:32/?UI,0:32>>|Acc];
diff --git a/lib/wx/src/gen/wxAuiManager.erl b/lib/wx/src/gen/wxAuiManager.erl
index 37693060e1..a33e5e9a65 100644
--- a/lib/wx/src/gen/wxAuiManager.erl
+++ b/lib/wx/src/gen/wxAuiManager.erl
@@ -177,7 +177,7 @@ getManager(#wx_ref{type=WindowT,ref=WindowRef}) ->
(This, Window) -> wxAuiPaneInfo:wxAuiPaneInfo() when
This::wxAuiManager(), Window::wxWindow:wxWindow().
getPane(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxAuiManager),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxAuiManager_GetPane_1_0,
@@ -223,7 +223,7 @@ insertPane(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef},#
-spec loadPaneInfo(This, Pane_part, Pane) -> 'ok' when
This::wxAuiManager(), Pane_part::unicode:chardata(), Pane::wxAuiPaneInfo:wxAuiPaneInfo().
loadPaneInfo(#wx_ref{type=ThisT,ref=ThisRef},Pane_part,#wx_ref{type=PaneT,ref=PaneRef})
- when is_list(Pane_part) ->
+ when ?is_chardata(Pane_part) ->
?CLASS(ThisT,wxAuiManager),
Pane_part_UC = unicode:characters_to_binary([Pane_part,0]),
?CLASS(PaneT,wxAuiPaneInfo),
@@ -235,7 +235,7 @@ loadPaneInfo(#wx_ref{type=ThisT,ref=ThisRef},Pane_part,#wx_ref{type=PaneT,ref=Pa
This::wxAuiManager(), Perspective::unicode:chardata().
loadPerspective(This,Perspective)
- when is_record(This, wx_ref),is_list(Perspective) ->
+ when is_record(This, wx_ref),?is_chardata(Perspective) ->
loadPerspective(This,Perspective, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxauimanager.html#wxauimanagerloadperspective">external documentation</a>.
@@ -243,7 +243,7 @@ loadPerspective(This,Perspective)
This::wxAuiManager(), Perspective::unicode:chardata(),
Option :: {'update', boolean()}.
loadPerspective(#wx_ref{type=ThisT,ref=ThisRef},Perspective, Options)
- when is_list(Perspective),is_list(Options) ->
+ when ?is_chardata(Perspective),is_list(Options) ->
?CLASS(ThisT,wxAuiManager),
Perspective_UC = unicode:characters_to_binary([Perspective,0]),
MOpts = fun({update, Update}, Acc) -> [<<1:32/?UI,(wxe_util:from_bool(Update)):32/?UI>>|Acc];
diff --git a/lib/wx/src/gen/wxAuiNotebook.erl b/lib/wx/src/gen/wxAuiNotebook.erl
index 42da35e16a..adb90c224f 100644
--- a/lib/wx/src/gen/wxAuiNotebook.erl
+++ b/lib/wx/src/gen/wxAuiNotebook.erl
@@ -42,38 +42,38 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setForegroundColour/2,
- setHelpText/2,setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,
- setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,setPalette/2,
- setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
- setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
- setWindowVariant/2,shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
- transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
- validate/1,warpPointer/3]).
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
+ setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
+ show/1,show/2,thaw/1,transferDataFromWindow/1,transferDataToWindow/1,
+ update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
-export_type([wxAuiNotebook/0]).
%% @hidden
@@ -121,7 +121,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
This::wxAuiNotebook(), Page::wxWindow:wxWindow(), Caption::unicode:chardata().
addPage(This,Page,Caption)
- when is_record(This, wx_ref),is_record(Page, wx_ref),is_list(Caption) ->
+ when is_record(This, wx_ref),is_record(Page, wx_ref),?is_chardata(Caption) ->
addPage(This,Page,Caption, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxauinotebook.html#wxauinotebookaddpage">external documentation</a>.
@@ -130,7 +130,7 @@ addPage(This,Page,Caption)
Option :: {'select', boolean()}
| {'bitmap', wxBitmap:wxBitmap()}.
addPage(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PageT,ref=PageRef},Caption, Options)
- when is_list(Caption),is_list(Options) ->
+ when ?is_chardata(Caption),is_list(Options) ->
?CLASS(ThisT,wxAuiNotebook),
?CLASS(PageT,wxWindow),
Caption_UC = unicode:characters_to_binary([Caption,0]),
@@ -243,7 +243,7 @@ getSelection(#wx_ref{type=ThisT,ref=ThisRef}) ->
This::wxAuiNotebook(), Page_idx::integer(), Page::wxWindow:wxWindow(), Caption::unicode:chardata().
insertPage(This,Page_idx,Page,Caption)
- when is_record(This, wx_ref),is_integer(Page_idx),is_record(Page, wx_ref),is_list(Caption) ->
+ when is_record(This, wx_ref),is_integer(Page_idx),is_record(Page, wx_ref),?is_chardata(Caption) ->
insertPage(This,Page_idx,Page,Caption, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxauinotebook.html#wxauinotebookinsertpage">external documentation</a>.
@@ -252,7 +252,7 @@ insertPage(This,Page_idx,Page,Caption)
Option :: {'select', boolean()}
| {'bitmap', wxBitmap:wxBitmap()}.
insertPage(#wx_ref{type=ThisT,ref=ThisRef},Page_idx,#wx_ref{type=PageT,ref=PageRef},Caption, Options)
- when is_integer(Page_idx),is_list(Caption),is_list(Options) ->
+ when is_integer(Page_idx),?is_chardata(Caption),is_list(Options) ->
?CLASS(ThisT,wxAuiNotebook),
?CLASS(PageT,wxWindow),
Caption_UC = unicode:characters_to_binary([Caption,0]),
@@ -304,7 +304,7 @@ setPageBitmap(#wx_ref{type=ThisT,ref=ThisRef},Page,#wx_ref{type=BitmapT,ref=Bitm
-spec setPageText(This, Page, Text) -> boolean() when
This::wxAuiNotebook(), Page::integer(), Text::unicode:chardata().
setPageText(#wx_ref{type=ThisT,ref=ThisRef},Page,Text)
- when is_integer(Page),is_list(Text) ->
+ when is_integer(Page),?is_chardata(Text) ->
?CLASS(ThisT,wxAuiNotebook),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxAuiNotebook_SetPageText,
@@ -350,6 +350,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -664,6 +666,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxAuiPaneInfo.erl b/lib/wx/src/gen/wxAuiPaneInfo.erl
index 858da200be..c8273269ac 100644
--- a/lib/wx/src/gen/wxAuiPaneInfo.erl
+++ b/lib/wx/src/gen/wxAuiPaneInfo.erl
@@ -117,7 +117,7 @@ bottomDockable(#wx_ref{type=ThisT,ref=ThisRef}, Options)
-spec caption(This, C) -> wxAuiPaneInfo() when
This::wxAuiPaneInfo(), C::unicode:chardata().
caption(#wx_ref{type=ThisT,ref=ThisRef},C)
- when is_list(C) ->
+ when ?is_chardata(C) ->
?CLASS(ThisT,wxAuiPaneInfo),
C_UC = unicode:characters_to_binary([C,0]),
wxe_util:call(?wxAuiPaneInfo_Caption,
@@ -689,7 +689,7 @@ movable(#wx_ref{type=ThisT,ref=ThisRef}, Options)
-spec name(This, N) -> wxAuiPaneInfo() when
This::wxAuiPaneInfo(), N::unicode:chardata().
name(#wx_ref{type=ThisT,ref=ThisRef},N)
- when is_list(N) ->
+ when ?is_chardata(N) ->
?CLASS(ThisT,wxAuiPaneInfo),
N_UC = unicode:characters_to_binary([N,0]),
wxe_util:call(?wxAuiPaneInfo_Name,
diff --git a/lib/wx/src/gen/wxBitmap.erl b/lib/wx/src/gen/wxBitmap.erl
index e7830dae9b..4a6e308d8d 100644
--- a/lib/wx/src/gen/wxBitmap.erl
+++ b/lib/wx/src/gen/wxBitmap.erl
@@ -56,7 +56,7 @@ new() ->
Image::wxImage:wxImage().
new(Filename)
- when is_list(Filename) ->
+ when ?is_chardata(Filename) ->
new(Filename, []);
new(Image)
@@ -86,7 +86,7 @@ new(Width,Height)
when is_integer(Width),is_integer(Height) ->
new(Width,Height, []);
new(Filename, Options)
- when is_list(Filename),is_list(Options) ->
+ when ?is_chardata(Filename),is_list(Options) ->
Filename_UC = unicode:characters_to_binary([Filename,0]),
MOpts = fun({type, Type}, Acc) -> [<<1:32/?UI,Type:32/?UI>>|Acc];
(BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
@@ -230,7 +230,7 @@ getSubBitmap(#wx_ref{type=ThisT,ref=ThisRef},{RectX,RectY,RectW,RectH})
This::wxBitmap(), Name::unicode:chardata().
loadFile(This,Name)
- when is_record(This, wx_ref),is_list(Name) ->
+ when is_record(This, wx_ref),?is_chardata(Name) ->
loadFile(This,Name, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxbitmap.html#wxbitmaploadfile">external documentation</a>.
@@ -239,7 +239,7 @@ loadFile(This,Name)
This::wxBitmap(), Name::unicode:chardata(),
Option :: {'type', wx:wx_enum()}.
loadFile(#wx_ref{type=ThisT,ref=ThisRef},Name, Options)
- when is_list(Name),is_list(Options) ->
+ when ?is_chardata(Name),is_list(Options) ->
?CLASS(ThisT,wxBitmap),
Name_UC = unicode:characters_to_binary([Name,0]),
MOpts = fun({type, Type}, Acc) -> [<<1:32/?UI,Type:32/?UI>>|Acc];
@@ -261,7 +261,7 @@ ok(#wx_ref{type=ThisT,ref=ThisRef}) ->
This::wxBitmap(), Name::unicode:chardata(), Type::wx:wx_enum().
saveFile(This,Name,Type)
- when is_record(This, wx_ref),is_list(Name),is_integer(Type) ->
+ when is_record(This, wx_ref),?is_chardata(Name),is_integer(Type) ->
saveFile(This,Name,Type, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxbitmap.html#wxbitmapsavefile">external documentation</a>.
@@ -270,7 +270,7 @@ saveFile(This,Name,Type)
This::wxBitmap(), Name::unicode:chardata(), Type::wx:wx_enum(),
Option :: {'palette', wxPalette:wxPalette()}.
saveFile(#wx_ref{type=ThisT,ref=ThisRef},Name,Type, Options)
- when is_list(Name),is_integer(Type),is_list(Options) ->
+ when ?is_chardata(Name),is_integer(Type),is_list(Options) ->
?CLASS(ThisT,wxBitmap),
Name_UC = unicode:characters_to_binary([Name,0]),
MOpts = fun({palette, #wx_ref{type=PaletteT,ref=PaletteRef}}, Acc) -> ?CLASS(PaletteT,wxPalette),[<<1:32/?UI,PaletteRef:32/?UI>>|Acc];
diff --git a/lib/wx/src/gen/wxBitmapButton.erl b/lib/wx/src/gen/wxBitmapButton.erl
index d1d2d037e8..d0a810bdb9 100644
--- a/lib/wx/src/gen/wxBitmapButton.erl
+++ b/lib/wx/src/gen/wxBitmapButton.erl
@@ -41,38 +41,39 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDefault/1,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
- setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
- setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
- setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
- setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
- setThemeEnabled/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
- setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
- setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
- show/1,show/2,thaw/1,transferDataFromWindow/1,transferDataToWindow/1,
- update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDefault/1,setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,
+ setFocus/1,setFocusFromKbd/1,setFont/2,setForegroundColour/2,setHelpText/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
+ setScrollPos/4,setScrollbar/5,setScrollbar/6,setSize/2,setSize/3,setSize/5,
+ setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,setSizer/2,
+ setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,setToolTip/2,
+ setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
+ setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
+ setWindowVariant/2,shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
+ transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
+ validate/1,warpPointer/3]).
-export_type([wxBitmapButton/0]).
%% @hidden
@@ -230,6 +231,8 @@ setDefault(This) -> wxButton:setDefault(This).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -546,6 +549,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxButton.erl b/lib/wx/src/gen/wxButton.erl
index 65d45caa13..f8e24beffa 100644
--- a/lib/wx/src/gen/wxButton.erl
+++ b/lib/wx/src/gen/wxButton.erl
@@ -39,30 +39,30 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setMaxSize/2,setMinSize/2,
- setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setMaxSize/2,
+ setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
@@ -163,7 +163,7 @@ setDefault(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setLabel(This, Label) -> 'ok' when
This::wxButton(), Label::unicode:chardata().
setLabel(#wx_ref{type=ThisT,ref=ThisRef},Label)
- when is_list(Label) ->
+ when ?is_chardata(Label) ->
?CLASS(ThisT,wxButton),
Label_UC = unicode:characters_to_binary([Label,0]),
wxe_util:cast(?wxButton_SetLabel,
@@ -180,6 +180,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -496,6 +498,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxCalendarCtrl.erl b/lib/wx/src/gen/wxCalendarCtrl.erl
index 93fcb61464..bf033e9bc0 100644
--- a/lib/wx/src/gen/wxCalendarCtrl.erl
+++ b/lib/wx/src/gen/wxCalendarCtrl.erl
@@ -43,29 +43,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -364,6 +364,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -680,6 +682,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxCheckBox.erl b/lib/wx/src/gen/wxCheckBox.erl
index 3276add802..5ed49d91b6 100644
--- a/lib/wx/src/gen/wxCheckBox.erl
+++ b/lib/wx/src/gen/wxCheckBox.erl
@@ -40,29 +40,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -92,7 +92,7 @@ new() ->
Parent::wxWindow:wxWindow(), Id::integer(), Label::unicode:chardata().
new(Parent,Id,Label)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Label) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Label) ->
new(Parent,Id,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxcheckbox.html#wxcheckboxwxcheckbox">external documentation</a>.
@@ -103,7 +103,7 @@ new(Parent,Id,Label)
| {'style', integer()}
| {'validator', wx:wx_object()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
- when is_integer(Id),is_list(Label),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Label),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
MOpts = fun({pos, {PosX,PosY}}, Acc) -> [<<1:32/?UI,PosX:32/?UI,PosY:32/?UI,0:32>>|Acc];
@@ -120,7 +120,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
This::wxCheckBox(), Parent::wxWindow:wxWindow(), Id::integer(), Label::unicode:chardata().
create(This,Parent,Id,Label)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Label) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Label) ->
create(This,Parent,Id,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxcheckbox.html#wxcheckboxcreate">external documentation</a>.
@@ -131,7 +131,7 @@ create(This,Parent,Id,Label)
| {'style', integer()}
| {'validator', wx:wx_object()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
- when is_integer(Id),is_list(Label),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxCheckBox),
?CLASS(ParentT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
@@ -217,6 +217,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -533,6 +535,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxCheckListBox.erl b/lib/wx/src/gen/wxCheckListBox.erl
index 888f29518c..ddef6a29e2 100644
--- a/lib/wx/src/gen/wxCheckListBox.erl
+++ b/lib/wx/src/gen/wxCheckListBox.erl
@@ -41,13 +41,14 @@
clientToScreen/2,clientToScreen/3,close/1,close/2,connect/2,connect/3,
convertDialogToPixels/2,convertPixelsToDialog/2,delete/2,deselect/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findString/2,findString/3,findWindow/2,fit/1,fitInside/1,
- freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
- getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
- getClientData/2,getClientSize/1,getContainingSizer/1,getCount/1,getCursor/1,
- getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ dragAcceptFiles/2,enable/1,enable/2,findString/2,findString/3,findWindow/2,
+ fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,getBackgroundColour/1,
+ getBackgroundStyle/1,getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,
+ getChildren/1,getClientData/2,getClientSize/1,getContainingSizer/1,
+ getContentScaleFactor/1,getCount/1,getCursor/1,getDropTarget/1,getEventHandler/1,
+ getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
+ getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,getMinSize/1,
+ getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
getSelection/1,getSelections/1,getSize/1,getSizer/1,getString/2,getStringSelection/1,
getTextExtent/2,getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
@@ -222,6 +223,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -538,6 +541,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxChoice.erl b/lib/wx/src/gen/wxChoice.erl
index 1a90b275d1..266a257442 100644
--- a/lib/wx/src/gen/wxChoice.erl
+++ b/lib/wx/src/gen/wxChoice.erl
@@ -40,40 +40,41 @@
centre/1,centre/2,centreOnParent/1,centreOnParent/2,clear/1,clearBackground/1,
clientToScreen/2,clientToScreen/3,close/1,close/2,connect/2,connect/3,
convertDialogToPixels/2,convertPixelsToDialog/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- findString/2,findString/3,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientData/2,getClientSize/1,
- getContainingSizer/1,getCount/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,getMinSize/1,
- getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
- getSelection/1,getSize/1,getSizer/1,getString/2,getStringSelection/1,
- getTextExtent/2,getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
- getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
- hasTransparentBackground/1,hide/1,inheritAttributes/1,initDialog/1,
- insert/3,insert/4,invalidateBestSize/1,isDoubleBuffered/1,isEmpty/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,select/2,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientData/3,
- setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
- setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
- setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
- setSelection/2,setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,
- setSizeHints/3,setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,
- setSizerAndFit/3,setString/3,setStringSelection/2,setThemeEnabled/2,
- setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
- setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
- setWindowVariant/2,shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,findString/2,findString/3,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientData/2,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCount/1,getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,
+ getFont/1,getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSelection/1,getSize/1,getSizer/1,
+ getString/2,getStringSelection/1,getTextExtent/2,getTextExtent/3,
+ getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
+ getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
+ hide/1,inheritAttributes/1,initDialog/1,insert/3,insert/4,invalidateBestSize/1,
+ isDoubleBuffered/1,isEmpty/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
+ isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,
+ makeModal/1,makeModal/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
+ moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
+ popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,select/2,setAcceleratorTable/2,
+ setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,
+ setClientData/3,setClientSize/2,setClientSize/3,setContainingSizer/2,
+ setCursor/2,setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,
+ setFocusFromKbd/1,setFont/2,setForegroundColour/2,setHelpText/2,setId/2,
+ setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
+ setScrollPos/4,setScrollbar/5,setScrollbar/6,setSelection/2,setSize/2,
+ setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
+ setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setString/3,
+ setStringSelection/2,setThemeEnabled/2,setToolTip/2,setTransparent/2,
+ setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,
+ setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,
+ shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
validate/1,warpPointer/3]).
@@ -237,6 +238,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -553,6 +556,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxChoicebook.erl b/lib/wx/src/gen/wxChoicebook.erl
index 34b62ff75b..c6e0941978 100644
--- a/lib/wx/src/gen/wxChoicebook.erl
+++ b/lib/wx/src/gen/wxChoicebook.erl
@@ -43,29 +43,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -120,7 +120,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
This::wxChoicebook(), Page::wxWindow:wxWindow(), Text::unicode:chardata().
addPage(This,Page,Text)
- when is_record(This, wx_ref),is_record(Page, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),is_record(Page, wx_ref),?is_chardata(Text) ->
addPage(This,Page,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxchoicebook.html#wxchoicebookaddpage">external documentation</a>.
@@ -129,7 +129,7 @@ addPage(This,Page,Text)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
addPage(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PageT,ref=PageRef},Text, Options)
- when is_list(Text),is_list(Options) ->
+ when ?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxChoicebook),
?CLASS(PageT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
@@ -296,7 +296,7 @@ hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PtX,PtY})
This::wxChoicebook(), N::integer(), Page::wxWindow:wxWindow(), Text::unicode:chardata().
insertPage(This,N,Page,Text)
- when is_record(This, wx_ref),is_integer(N),is_record(Page, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(N),is_record(Page, wx_ref),?is_chardata(Text) ->
insertPage(This,N,Page,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxchoicebook.html#wxchoicebookinsertpage">external documentation</a>.
@@ -305,7 +305,7 @@ insertPage(This,N,Page,Text)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
insertPage(#wx_ref{type=ThisT,ref=ThisRef},N,#wx_ref{type=PageT,ref=PageRef},Text, Options)
- when is_integer(N),is_list(Text),is_list(Options) ->
+ when is_integer(N),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxChoicebook),
?CLASS(PageT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
@@ -347,7 +347,7 @@ setPageImage(#wx_ref{type=ThisT,ref=ThisRef},N,ImageId)
-spec setPageText(This, N, StrText) -> boolean() when
This::wxChoicebook(), N::integer(), StrText::unicode:chardata().
setPageText(#wx_ref{type=ThisT,ref=ThisRef},N,StrText)
- when is_integer(N),is_list(StrText) ->
+ when is_integer(N),?is_chardata(StrText) ->
?CLASS(ThisT,wxChoicebook),
StrText_UC = unicode:characters_to_binary([StrText,0]),
wxe_util:call(?wxChoicebook_SetPageText,
@@ -384,6 +384,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -700,6 +702,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxColourDialog.erl b/lib/wx/src/gen/wxColourDialog.erl
index 9c97434d9a..936c0e5099 100644
--- a/lib/wx/src/gen/wxColourDialog.erl
+++ b/lib/wx/src/gen/wxColourDialog.erl
@@ -40,40 +40,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
@@ -223,6 +223,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -539,6 +541,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxColourPickerCtrl.erl b/lib/wx/src/gen/wxColourPickerCtrl.erl
index 9c987b4dde..c97b194bc8 100644
--- a/lib/wx/src/gen/wxColourPickerCtrl.erl
+++ b/lib/wx/src/gen/wxColourPickerCtrl.erl
@@ -39,17 +39,18 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getInternalMargin/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,
- getParent/1,getPickerCtrlProportion/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
- getSize/1,getSizer/1,getTextCtrl/1,getTextCtrlProportion/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTextCtrl/1,hasTransparentBackground/1,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getInternalMargin/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPickerCtrlProportion/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextCtrl/1,
+ getTextCtrlProportion/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTextCtrl/1,hasTransparentBackground/1,
hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isPickerCtrlGrowable/1,
isRetained/1,isShown/1,isTextCtrlGrowable/1,isTopLevel/1,layout/1,
@@ -168,7 +169,7 @@ getColour(#wx_ref{type=ThisT,ref=ThisRef}) ->
(This, Col) -> 'ok' when
This::wxColourPickerCtrl(), Col::wx:wx_colour().
setColour(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxColourPickerCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxColourPickerCtrl_SetColour_1_0,
@@ -221,6 +222,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -537,6 +540,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxComboBox.erl b/lib/wx/src/gen/wxComboBox.erl
index bbdf768eba..9f8cf77445 100644
--- a/lib/wx/src/gen/wxComboBox.erl
+++ b/lib/wx/src/gen/wxComboBox.erl
@@ -42,42 +42,42 @@
centre/1,centre/2,centreOnParent/1,centreOnParent/2,clear/1,clearBackground/1,
clientToScreen/2,clientToScreen/3,close/1,close/2,connect/2,connect/3,
convertDialogToPixels/2,convertPixelsToDialog/2,delete/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- findString/2,findString/3,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientData/2,getClientSize/1,
- getContainingSizer/1,getCount/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,getMinSize/1,
- getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
- getSelection/1,getSize/1,getSizer/1,getString/2,getStringSelection/1,
- getTextExtent/2,getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
- getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
- hasTransparentBackground/1,hide/1,inheritAttributes/1,initDialog/1,
- insert/3,insert/4,invalidateBestSize/1,isDoubleBuffered/1,isEmpty/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,select/2,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientData/3,
- setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
- setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
- setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
- setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
- setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
- setString/3,setStringSelection/2,setThemeEnabled/2,setToolTip/2,setTransparent/2,
- setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,
- setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,
- shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
- transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
- validate/1,warpPointer/3]).
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,findString/2,findString/3,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientData/2,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCount/1,getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,
+ getFont/1,getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSelection/1,getSize/1,getSizer/1,
+ getString/2,getStringSelection/1,getTextExtent/2,getTextExtent/3,
+ getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
+ getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
+ hide/1,inheritAttributes/1,initDialog/1,insert/3,insert/4,invalidateBestSize/1,
+ isDoubleBuffered/1,isEmpty/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
+ isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,
+ makeModal/1,makeModal/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
+ moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
+ popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,select/2,setAcceleratorTable/2,
+ setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,
+ setClientData/3,setClientSize/2,setClientSize/3,setContainingSizer/2,
+ setCursor/2,setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,
+ setFocusFromKbd/1,setFont/2,setForegroundColour/2,setHelpText/2,setId/2,
+ setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
+ setScrollPos/4,setScrollbar/5,setScrollbar/6,setSize/2,setSize/3,setSize/5,
+ setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,setSizer/2,
+ setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setString/3,setStringSelection/2,
+ setThemeEnabled/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
+ show/1,show/2,thaw/1,transferDataFromWindow/1,transferDataToWindow/1,
+ update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
-export_type([wxComboBox/0]).
%% @hidden
@@ -130,7 +130,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
This::wxComboBox(), Parent::wxWindow:wxWindow(), Id::integer(), Value::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()].
create(This,Parent,Id,Value,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Value),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Value),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
create(This,Parent,Id,Value,Pos,Size,Choices, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxcombobox.html#wxcomboboxcreate">external documentation</a>.
@@ -139,7 +139,7 @@ create(This,Parent,Id,Value,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
Option :: {'style', integer()}
| {'validator', wx:wx_object()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Value,{PosX,PosY},{SizeW,SizeH},Choices, Options)
- when is_integer(Id),is_list(Value),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Value),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) ->
?CLASS(ThisT,wxComboBox),
?CLASS(ParentT,wxWindow),
Value_UC = unicode:characters_to_binary([Value,0]),
@@ -252,7 +252,7 @@ redo(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec replace(This, From, To, Value) -> 'ok' when
This::wxComboBox(), From::integer(), To::integer(), Value::unicode:chardata().
replace(#wx_ref{type=ThisT,ref=ThisRef},From,To,Value)
- when is_integer(From),is_integer(To),is_list(Value) ->
+ when is_integer(From),is_integer(To),?is_chardata(Value) ->
?CLASS(ThisT,wxComboBox),
Value_UC = unicode:characters_to_binary([Value,0]),
wxe_util:cast(?wxComboBox_Replace,
@@ -306,7 +306,7 @@ setSelection(#wx_ref{type=ThisT,ref=ThisRef},From,To)
-spec setValue(This, Value) -> 'ok' when
This::wxComboBox(), Value::unicode:chardata().
setValue(#wx_ref{type=ThisT,ref=ThisRef},Value)
- when is_list(Value) ->
+ when ?is_chardata(Value) ->
?CLASS(ThisT,wxComboBox),
Value_UC = unicode:characters_to_binary([Value,0]),
wxe_util:cast(?wxComboBox_SetValue,
@@ -372,6 +372,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -688,6 +690,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxCommandEvent.erl b/lib/wx/src/gen/wxCommandEvent.erl
index 781482aebb..18ccbc65f0 100644
--- a/lib/wx/src/gen/wxCommandEvent.erl
+++ b/lib/wx/src/gen/wxCommandEvent.erl
@@ -114,7 +114,7 @@ setInt(#wx_ref{type=ThisT,ref=ThisRef},I)
-spec setString(This, S) -> 'ok' when
This::wxCommandEvent(), S::unicode:chardata().
setString(#wx_ref{type=ThisT,ref=ThisRef},S)
- when is_list(S) ->
+ when ?is_chardata(S) ->
?CLASS(ThisT,wxCommandEvent),
S_UC = unicode:characters_to_binary([S,0]),
wxe_util:cast(?wxCommandEvent_SetString,
diff --git a/lib/wx/src/gen/wxControl.erl b/lib/wx/src/gen/wxControl.erl
index a84f88639b..6be7574d80 100644
--- a/lib/wx/src/gen/wxControl.erl
+++ b/lib/wx/src/gen/wxControl.erl
@@ -37,30 +37,30 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
- getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setMaxSize/2,setMinSize/2,
- setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getMaxSize/1,getMinSize/1,
+ getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setMaxSize/2,
+ setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
@@ -89,7 +89,7 @@ getLabel(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setLabel(This, Label) -> 'ok' when
This::wxControl(), Label::unicode:chardata().
setLabel(#wx_ref{type=ThisT,ref=ThisRef},Label)
- when is_list(Label) ->
+ when ?is_chardata(Label) ->
?CLASS(ThisT,wxControl),
Label_UC = unicode:characters_to_binary([Label,0]),
wxe_util:cast(?wxControl_SetLabel,
@@ -97,6 +97,8 @@ setLabel(#wx_ref{type=ThisT,ref=ThisRef},Label)
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -413,6 +415,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxControlWithItems.erl b/lib/wx/src/gen/wxControlWithItems.erl
index c4d116d9e7..47ffa7dcba 100644
--- a/lib/wx/src/gen/wxControlWithItems.erl
+++ b/lib/wx/src/gen/wxControlWithItems.erl
@@ -41,29 +41,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -86,7 +86,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
-spec append(This, Item) -> integer() when
This::wxControlWithItems(), Item::unicode:chardata().
append(#wx_ref{type=ThisT,ref=ThisRef},Item)
- when is_list(Item) ->
+ when ?is_chardata(Item) ->
?CLASS(ThisT,wxControlWithItems),
Item_UC = unicode:characters_to_binary([Item,0]),
wxe_util:call(?wxControlWithItems_Append_1,
@@ -96,7 +96,7 @@ append(#wx_ref{type=ThisT,ref=ThisRef},Item)
-spec append(This, Item, ClientData) -> integer() when
This::wxControlWithItems(), Item::unicode:chardata(), ClientData::term().
append(#wx_ref{type=ThisT,ref=ThisRef},Item,ClientData)
- when is_list(Item) ->
+ when ?is_chardata(Item) ->
?CLASS(ThisT,wxControlWithItems),
Item_UC = unicode:characters_to_binary([Item,0]),
wxe_util:send_bin(term_to_binary(ClientData)),
@@ -136,7 +136,7 @@ delete(#wx_ref{type=ThisT,ref=ThisRef},N)
This::wxControlWithItems(), S::unicode:chardata().
findString(This,S)
- when is_record(This, wx_ref),is_list(S) ->
+ when is_record(This, wx_ref),?is_chardata(S) ->
findString(This,S, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxcontrolwithitems.html#wxcontrolwithitemsfindstring">external documentation</a>.
@@ -144,7 +144,7 @@ findString(This,S)
This::wxControlWithItems(), S::unicode:chardata(),
Option :: {'bCase', boolean()}.
findString(#wx_ref{type=ThisT,ref=ThisRef},S, Options)
- when is_list(S),is_list(Options) ->
+ when ?is_chardata(S),is_list(Options) ->
?CLASS(ThisT,wxControlWithItems),
S_UC = unicode:characters_to_binary([S,0]),
MOpts = fun({bCase, BCase}, Acc) -> [<<1:32/?UI,(wxe_util:from_bool(BCase)):32/?UI>>|Acc];
@@ -209,7 +209,7 @@ getStringSelection(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec insert(This, Item, Pos) -> integer() when
This::wxControlWithItems(), Item::unicode:chardata(), Pos::integer().
insert(#wx_ref{type=ThisT,ref=ThisRef},Item,Pos)
- when is_list(Item),is_integer(Pos) ->
+ when ?is_chardata(Item),is_integer(Pos) ->
?CLASS(ThisT,wxControlWithItems),
Item_UC = unicode:characters_to_binary([Item,0]),
wxe_util:call(?wxControlWithItems_Insert_2,
@@ -219,7 +219,7 @@ insert(#wx_ref{type=ThisT,ref=ThisRef},Item,Pos)
-spec insert(This, Item, Pos, ClientData) -> integer() when
This::wxControlWithItems(), Item::unicode:chardata(), Pos::integer(), ClientData::term().
insert(#wx_ref{type=ThisT,ref=ThisRef},Item,Pos,ClientData)
- when is_list(Item),is_integer(Pos) ->
+ when ?is_chardata(Item),is_integer(Pos) ->
?CLASS(ThisT,wxControlWithItems),
Item_UC = unicode:characters_to_binary([Item,0]),
wxe_util:send_bin(term_to_binary(ClientData)),
@@ -256,7 +256,7 @@ setSelection(#wx_ref{type=ThisT,ref=ThisRef},N)
-spec setString(This, N, S) -> 'ok' when
This::wxControlWithItems(), N::integer(), S::unicode:chardata().
setString(#wx_ref{type=ThisT,ref=ThisRef},N,S)
- when is_integer(N),is_list(S) ->
+ when is_integer(N),?is_chardata(S) ->
?CLASS(ThisT,wxControlWithItems),
S_UC = unicode:characters_to_binary([S,0]),
wxe_util:cast(?wxControlWithItems_SetString,
@@ -266,7 +266,7 @@ setString(#wx_ref{type=ThisT,ref=ThisRef},N,S)
-spec setStringSelection(This, S) -> boolean() when
This::wxControlWithItems(), S::unicode:chardata().
setStringSelection(#wx_ref{type=ThisT,ref=ThisRef},S)
- when is_list(S) ->
+ when ?is_chardata(S) ->
?CLASS(ThisT,wxControlWithItems),
S_UC = unicode:characters_to_binary([S,0]),
wxe_util:call(?wxControlWithItems_SetStringSelection,
@@ -279,6 +279,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -595,6 +597,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxDC.erl b/lib/wx/src/gen/wxDC.erl
index ad7a4251ec..16bfcc3463 100644
--- a/lib/wx/src/gen/wxDC.erl
+++ b/lib/wx/src/gen/wxDC.erl
@@ -253,7 +253,7 @@ drawIcon(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=IconT,ref=IconRef},{PtX,Pt
This::wxDC(), Text::unicode:chardata(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}.
drawLabel(This,Text,Rect={RectX,RectY,RectW,RectH})
- when is_record(This, wx_ref),is_list(Text),is_integer(RectX),is_integer(RectY),is_integer(RectW),is_integer(RectH) ->
+ when is_record(This, wx_ref),?is_chardata(Text),is_integer(RectX),is_integer(RectY),is_integer(RectW),is_integer(RectH) ->
drawLabel(This,Text,Rect, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdc.html#wxdcdrawlabel">external documentation</a>.
@@ -262,7 +262,7 @@ drawLabel(This,Text,Rect={RectX,RectY,RectW,RectH})
Option :: {'alignment', integer()}
| {'indexAccel', integer()}.
drawLabel(#wx_ref{type=ThisT,ref=ThisRef},Text,{RectX,RectY,RectW,RectH}, Options)
- when is_list(Text),is_integer(RectX),is_integer(RectY),is_integer(RectW),is_integer(RectH),is_list(Options) ->
+ when ?is_chardata(Text),is_integer(RectX),is_integer(RectY),is_integer(RectW),is_integer(RectH),is_list(Options) ->
?CLASS(ThisT,wxDC),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({alignment, Alignment}, Acc) -> [<<1:32/?UI,Alignment:32/?UI>>|Acc];
@@ -363,7 +363,7 @@ drawRectangle(#wx_ref{type=ThisT,ref=ThisRef},{PtX,PtY},{SzW,SzH})
-spec drawRotatedText(This, Text, Pt, Angle) -> 'ok' when
This::wxDC(), Text::unicode:chardata(), Pt::{X::integer(), Y::integer()}, Angle::number().
drawRotatedText(#wx_ref{type=ThisT,ref=ThisRef},Text,{PtX,PtY},Angle)
- when is_list(Text),is_integer(PtX),is_integer(PtY),is_number(Angle) ->
+ when ?is_chardata(Text),is_integer(PtX),is_integer(PtY),is_number(Angle) ->
?CLASS(ThisT,wxDC),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxDC_DrawRotatedText,
@@ -391,7 +391,7 @@ drawRoundedRectangle(#wx_ref{type=ThisT,ref=ThisRef},{PtX,PtY},{SzW,SzH},Radius)
-spec drawText(This, Text, Pt) -> 'ok' when
This::wxDC(), Text::unicode:chardata(), Pt::{X::integer(), Y::integer()}.
drawText(#wx_ref{type=ThisT,ref=ThisRef},Text,{PtX,PtY})
- when is_list(Text),is_integer(PtX),is_integer(PtY) ->
+ when ?is_chardata(Text),is_integer(PtX),is_integer(PtY) ->
?CLASS(ThisT,wxDC),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxDC_DrawText,
@@ -521,7 +521,7 @@ getMapMode(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec getMultiLineTextExtent(This, String) -> {W::integer(), H::integer()} when
This::wxDC(), String::unicode:chardata().
getMultiLineTextExtent(#wx_ref{type=ThisT,ref=ThisRef},String)
- when is_list(String) ->
+ when ?is_chardata(String) ->
?CLASS(ThisT,wxDC),
String_UC = unicode:characters_to_binary([String,0]),
wxe_util:call(?wxDC_GetMultiLineTextExtent_1,
@@ -532,7 +532,7 @@ getMultiLineTextExtent(#wx_ref{type=ThisT,ref=ThisRef},String)
This::wxDC(), String::unicode:chardata(),
Option :: {'font', wxFont:wxFont()}.
getMultiLineTextExtent(#wx_ref{type=ThisT,ref=ThisRef},String, Options)
- when is_list(String),is_list(Options) ->
+ when ?is_chardata(String),is_list(Options) ->
?CLASS(ThisT,wxDC),
String_UC = unicode:characters_to_binary([String,0]),
MOpts = fun({font, #wx_ref{type=FontT,ref=FontRef}}, Acc) -> ?CLASS(FontT,wxFont),[<<1:32/?UI,FontRef:32/?UI>>|Acc];
@@ -546,7 +546,7 @@ getMultiLineTextExtent(#wx_ref{type=ThisT,ref=ThisRef},String, Options)
Result ::{Res ::boolean(), Widths::[integer()]},
This::wxDC(), Text::unicode:chardata().
getPartialTextExtents(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxDC),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxDC_GetPartialTextExtents,
@@ -606,7 +606,7 @@ getTextBackground(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec getTextExtent(This, String) -> {W::integer(), H::integer()} when
This::wxDC(), String::unicode:chardata().
getTextExtent(#wx_ref{type=ThisT,ref=ThisRef},String)
- when is_list(String) ->
+ when ?is_chardata(String) ->
?CLASS(ThisT,wxDC),
String_UC = unicode:characters_to_binary([String,0]),
wxe_util:call(?wxDC_GetTextExtent_1,
@@ -618,7 +618,7 @@ getTextExtent(#wx_ref{type=ThisT,ref=ThisRef},String)
This::wxDC(), String::unicode:chardata(),
Option :: {'theFont', wxFont:wxFont()}.
getTextExtent(#wx_ref{type=ThisT,ref=ThisRef},String, Options)
- when is_list(String),is_list(Options) ->
+ when ?is_chardata(String),is_list(Options) ->
?CLASS(ThisT,wxDC),
String_UC = unicode:characters_to_binary([String,0]),
MOpts = fun({theFont, #wx_ref{type=TheFontT,ref=TheFontRef}}, Acc) -> ?CLASS(TheFontT,wxFont),[<<1:32/?UI,TheFontRef:32/?UI>>|Acc];
@@ -929,7 +929,7 @@ setUserScale(#wx_ref{type=ThisT,ref=ThisRef},X,Y)
-spec startDoc(This, Message) -> boolean() when
This::wxDC(), Message::unicode:chardata().
startDoc(#wx_ref{type=ThisT,ref=ThisRef},Message)
- when is_list(Message) ->
+ when ?is_chardata(Message) ->
?CLASS(ThisT,wxDC),
Message_UC = unicode:characters_to_binary([Message,0]),
wxe_util:call(?wxDC_StartDoc,
diff --git a/lib/wx/src/gen/wxDatePickerCtrl.erl b/lib/wx/src/gen/wxDatePickerCtrl.erl
index 1b306a498a..743169c58e 100644
--- a/lib/wx/src/gen/wxDatePickerCtrl.erl
+++ b/lib/wx/src/gen/wxDatePickerCtrl.erl
@@ -39,17 +39,18 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getInternalMargin/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,
- getParent/1,getPickerCtrlProportion/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
- getSize/1,getSizer/1,getTextCtrl/1,getTextCtrlProportion/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTextCtrl/1,hasTransparentBackground/1,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getInternalMargin/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPickerCtrlProportion/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextCtrl/1,
+ getTextCtrlProportion/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTextCtrl/1,hasTransparentBackground/1,
hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isPickerCtrlGrowable/1,
isRetained/1,isShown/1,isTextCtrlGrowable/1,isTopLevel/1,layout/1,
@@ -197,6 +198,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -513,6 +516,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxDialog.erl b/lib/wx/src/gen/wxDialog.erl
index 9f98644828..72f37df178 100644
--- a/lib/wx/src/gen/wxDialog.erl
+++ b/lib/wx/src/gen/wxDialog.erl
@@ -41,32 +41,32 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,
- getParent/1,getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,
- getScrollPos/2,getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,
- getTextExtent/2,getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,
- getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
- hasScrollbar/2,hasTransparentBackground/1,hide/1,iconize/1,iconize/2,
- inheritAttributes/1,initDialog/1,invalidateBestSize/1,isActive/1,
- isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
- isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,isShown/1,isTopLevel/1,
- layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,maximize/1,
- maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,requestUserAttention/1,requestUserAttention/2,screenToClient/1,
- screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
- setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
- setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
- setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
+ getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
+ getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
+ getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
+ hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
+ isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
+ isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,
+ isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
+ makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
+ moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
+ popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
+ screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
+ scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,
+ setBackgroundStyle/2,setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,
+ setCursor/2,setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,
+ setFocusFromKbd/1,setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,
+ setIcons/2,setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
@@ -97,7 +97,7 @@ new() ->
Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata().
new(Parent,Id,Title)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Title) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title) ->
new(Parent,Id,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdialog.html#wxdialogwxdialog">external documentation</a>.
@@ -107,7 +107,7 @@ new(Parent,Id,Title)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
- when is_integer(Id),is_list(Title),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
MOpts = fun({pos, {PosX,PosY}}, Acc) -> [<<1:32/?UI,PosX:32/?UI,PosY:32/?UI,0:32>>|Acc];
@@ -123,7 +123,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
This::wxDialog(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata().
create(This,Parent,Id,Title)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Title) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title) ->
create(This,Parent,Id,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdialog.html#wxdialogcreate">external documentation</a>.
@@ -133,7 +133,7 @@ create(This,Parent,Id,Title)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
- when is_integer(Id),is_list(Title),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_list(Options) ->
?CLASS(ThisT,wxDialog),
?CLASS(ParentT,wxWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
@@ -298,6 +298,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -614,6 +616,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxDirDialog.erl b/lib/wx/src/gen/wxDirDialog.erl
index 1b6b9dba71..5a2df9821a 100644
--- a/lib/wx/src/gen/wxDirDialog.erl
+++ b/lib/wx/src/gen/wxDirDialog.erl
@@ -40,40 +40,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
@@ -137,7 +137,7 @@ getMessage(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setMessage(This, Message) -> 'ok' when
This::wxDirDialog(), Message::unicode:chardata().
setMessage(#wx_ref{type=ThisT,ref=ThisRef},Message)
- when is_list(Message) ->
+ when ?is_chardata(Message) ->
?CLASS(ThisT,wxDirDialog),
Message_UC = unicode:characters_to_binary([Message,0]),
wxe_util:cast(?wxDirDialog_SetMessage,
@@ -147,7 +147,7 @@ setMessage(#wx_ref{type=ThisT,ref=ThisRef},Message)
-spec setPath(This, Path) -> 'ok' when
This::wxDirDialog(), Path::unicode:chardata().
setPath(#wx_ref{type=ThisT,ref=ThisRef},Path)
- when is_list(Path) ->
+ when ?is_chardata(Path) ->
?CLASS(ThisT,wxDirDialog),
Path_UC = unicode:characters_to_binary([Path,0]),
wxe_util:cast(?wxDirDialog_SetPath,
@@ -231,6 +231,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -547,6 +549,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxDirPickerCtrl.erl b/lib/wx/src/gen/wxDirPickerCtrl.erl
index 3bedeb7f54..9bfff7d458 100644
--- a/lib/wx/src/gen/wxDirPickerCtrl.erl
+++ b/lib/wx/src/gen/wxDirPickerCtrl.erl
@@ -39,17 +39,18 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getInternalMargin/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,
- getParent/1,getPickerCtrlProportion/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
- getSize/1,getSizer/1,getTextCtrl/1,getTextCtrlProportion/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTextCtrl/1,hasTransparentBackground/1,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getInternalMargin/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPickerCtrlProportion/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextCtrl/1,
+ getTextCtrlProportion/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTextCtrl/1,hasTransparentBackground/1,
hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isPickerCtrlGrowable/1,
isRetained/1,isShown/1,isTextCtrlGrowable/1,isTopLevel/1,layout/1,
@@ -166,7 +167,7 @@ getPath(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setPath(This, Str) -> 'ok' when
This::wxDirPickerCtrl(), Str::unicode:chardata().
setPath(#wx_ref{type=ThisT,ref=ThisRef},Str)
- when is_list(Str) ->
+ when ?is_chardata(Str) ->
?CLASS(ThisT,wxDirPickerCtrl),
Str_UC = unicode:characters_to_binary([Str,0]),
wxe_util:cast(?wxDirPickerCtrl_SetPath,
@@ -214,6 +215,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -530,6 +533,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxDropFilesEvent.erl b/lib/wx/src/gen/wxDropFilesEvent.erl
new file mode 100644
index 0000000000..d28dcd1f7a
--- /dev/null
+++ b/lib/wx/src/gen/wxDropFilesEvent.erl
@@ -0,0 +1,89 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-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 file is generated DO NOT EDIT
+
+%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdropfilesevent.html">wxDropFilesEvent</a>.
+%% <dl><dt>Use {@link wxEvtHandler:connect/3.} with EventType:</dt>
+%% <dd><em>drop_files</em></dd></dl>
+%% See also the message variant {@link wxEvtHandler:wxDropFiles(). #wxDropFiles{}} event record type.
+%%
+%% <p>This class is derived (and can use functions) from:
+%% <br />{@link wxEvent}
+%% </p>
+%% @type wxDropFilesEvent(). An object reference, The representation is internal
+%% and can be changed without notice. It can't be used for comparsion
+%% stored on disc or distributed for use on other nodes.
+
+-module(wxDropFilesEvent).
+-include("wxe.hrl").
+-export([getFiles/1,getNumberOfFiles/1,getPosition/1]).
+
+%% inherited exports
+-export([getId/1,getSkipped/1,getTimestamp/1,isCommandEvent/1,parent_class/1,
+ resumePropagation/2,shouldPropagate/1,skip/1,skip/2,stopPropagation/1]).
+
+-export_type([wxDropFilesEvent/0]).
+%% @hidden
+parent_class(wxEvent) -> true;
+parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
+
+-type wxDropFilesEvent() :: wx:wx_object().
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdropfilesevent.html#wxdropfileseventgetposition">external documentation</a>.
+-spec getPosition(This) -> {X::integer(), Y::integer()} when
+ This::wxDropFilesEvent().
+getPosition(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxDropFilesEvent),
+ wxe_util:call(?wxDropFilesEvent_GetPosition,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdropfilesevent.html#wxdropfileseventgetnumberoffiles">external documentation</a>.
+-spec getNumberOfFiles(This) -> integer() when
+ This::wxDropFilesEvent().
+getNumberOfFiles(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxDropFilesEvent),
+ wxe_util:call(?wxDropFilesEvent_GetNumberOfFiles,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdropfilesevent.html#wxdropfileseventgetfiles">external documentation</a>.
+-spec getFiles(This) -> [unicode:charlist()] when
+ This::wxDropFilesEvent().
+getFiles(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxDropFilesEvent),
+ wxe_util:call(?wxDropFilesEvent_GetFiles,
+ <<ThisRef:32/?UI>>).
+
+ %% From wxEvent
+%% @hidden
+stopPropagation(This) -> wxEvent:stopPropagation(This).
+%% @hidden
+skip(This, Options) -> wxEvent:skip(This, Options).
+%% @hidden
+skip(This) -> wxEvent:skip(This).
+%% @hidden
+shouldPropagate(This) -> wxEvent:shouldPropagate(This).
+%% @hidden
+resumePropagation(This,PropagationLevel) -> wxEvent:resumePropagation(This,PropagationLevel).
+%% @hidden
+isCommandEvent(This) -> wxEvent:isCommandEvent(This).
+%% @hidden
+getTimestamp(This) -> wxEvent:getTimestamp(This).
+%% @hidden
+getSkipped(This) -> wxEvent:getSkipped(This).
+%% @hidden
+getId(This) -> wxEvent:getId(This).
diff --git a/lib/wx/src/gen/wxFileDataObject.erl b/lib/wx/src/gen/wxFileDataObject.erl
index 06d8ceb9cd..d4e9198225 100644
--- a/lib/wx/src/gen/wxFileDataObject.erl
+++ b/lib/wx/src/gen/wxFileDataObject.erl
@@ -49,7 +49,7 @@ new() ->
-spec addFile(This, Filename) -> 'ok' when
This::wxFileDataObject(), Filename::unicode:chardata().
addFile(#wx_ref{type=ThisT,ref=ThisRef},Filename)
- when is_list(Filename) ->
+ when ?is_chardata(Filename) ->
?CLASS(ThisT,wxFileDataObject),
Filename_UC = unicode:characters_to_binary([Filename,0]),
wxe_util:cast(?wxFileDataObject_AddFile,
diff --git a/lib/wx/src/gen/wxFileDialog.erl b/lib/wx/src/gen/wxFileDialog.erl
index 070fce3a39..6032e38a16 100644
--- a/lib/wx/src/gen/wxFileDialog.erl
+++ b/lib/wx/src/gen/wxFileDialog.erl
@@ -42,40 +42,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
@@ -191,7 +191,7 @@ getWildcard(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setDirectory(This, Dir) -> 'ok' when
This::wxFileDialog(), Dir::unicode:chardata().
setDirectory(#wx_ref{type=ThisT,ref=ThisRef},Dir)
- when is_list(Dir) ->
+ when ?is_chardata(Dir) ->
?CLASS(ThisT,wxFileDialog),
Dir_UC = unicode:characters_to_binary([Dir,0]),
wxe_util:cast(?wxFileDialog_SetDirectory,
@@ -201,7 +201,7 @@ setDirectory(#wx_ref{type=ThisT,ref=ThisRef},Dir)
-spec setFilename(This, Name) -> 'ok' when
This::wxFileDialog(), Name::unicode:chardata().
setFilename(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxFileDialog),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:cast(?wxFileDialog_SetFilename,
@@ -220,7 +220,7 @@ setFilterIndex(#wx_ref{type=ThisT,ref=ThisRef},FilterIndex)
-spec setMessage(This, Message) -> 'ok' when
This::wxFileDialog(), Message::unicode:chardata().
setMessage(#wx_ref{type=ThisT,ref=ThisRef},Message)
- when is_list(Message) ->
+ when ?is_chardata(Message) ->
?CLASS(ThisT,wxFileDialog),
Message_UC = unicode:characters_to_binary([Message,0]),
wxe_util:cast(?wxFileDialog_SetMessage,
@@ -230,7 +230,7 @@ setMessage(#wx_ref{type=ThisT,ref=ThisRef},Message)
-spec setPath(This, Path) -> 'ok' when
This::wxFileDialog(), Path::unicode:chardata().
setPath(#wx_ref{type=ThisT,ref=ThisRef},Path)
- when is_list(Path) ->
+ when ?is_chardata(Path) ->
?CLASS(ThisT,wxFileDialog),
Path_UC = unicode:characters_to_binary([Path,0]),
wxe_util:cast(?wxFileDialog_SetPath,
@@ -240,7 +240,7 @@ setPath(#wx_ref{type=ThisT,ref=ThisRef},Path)
-spec setWildcard(This, WildCard) -> 'ok' when
This::wxFileDialog(), WildCard::unicode:chardata().
setWildcard(#wx_ref{type=ThisT,ref=ThisRef},WildCard)
- when is_list(WildCard) ->
+ when ?is_chardata(WildCard) ->
?CLASS(ThisT,wxFileDialog),
WildCard_UC = unicode:characters_to_binary([WildCard,0]),
wxe_util:cast(?wxFileDialog_SetWildcard,
@@ -324,6 +324,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -640,6 +642,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxFilePickerCtrl.erl b/lib/wx/src/gen/wxFilePickerCtrl.erl
index 396403dc22..3b1943cbee 100644
--- a/lib/wx/src/gen/wxFilePickerCtrl.erl
+++ b/lib/wx/src/gen/wxFilePickerCtrl.erl
@@ -39,17 +39,18 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getInternalMargin/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,
- getParent/1,getPickerCtrlProportion/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
- getSize/1,getSizer/1,getTextCtrl/1,getTextCtrlProportion/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTextCtrl/1,hasTransparentBackground/1,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getInternalMargin/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPickerCtrlProportion/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextCtrl/1,
+ getTextCtrlProportion/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTextCtrl/1,hasTransparentBackground/1,
hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isPickerCtrlGrowable/1,
isRetained/1,isShown/1,isTextCtrlGrowable/1,isTopLevel/1,layout/1,
@@ -170,7 +171,7 @@ getPath(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setPath(This, Str) -> 'ok' when
This::wxFilePickerCtrl(), Str::unicode:chardata().
setPath(#wx_ref{type=ThisT,ref=ThisRef},Str)
- when is_list(Str) ->
+ when ?is_chardata(Str) ->
?CLASS(ThisT,wxFilePickerCtrl),
Str_UC = unicode:characters_to_binary([Str,0]),
wxe_util:cast(?wxFilePickerCtrl_SetPath,
@@ -218,6 +219,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -534,6 +537,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxFindReplaceData.erl b/lib/wx/src/gen/wxFindReplaceData.erl
index 9b4b910f7e..388bf5f238 100644
--- a/lib/wx/src/gen/wxFindReplaceData.erl
+++ b/lib/wx/src/gen/wxFindReplaceData.erl
@@ -87,7 +87,7 @@ setFlags(#wx_ref{type=ThisT,ref=ThisRef},Flags)
-spec setFindString(This, Str) -> 'ok' when
This::wxFindReplaceData(), Str::unicode:chardata().
setFindString(#wx_ref{type=ThisT,ref=ThisRef},Str)
- when is_list(Str) ->
+ when ?is_chardata(Str) ->
?CLASS(ThisT,wxFindReplaceData),
Str_UC = unicode:characters_to_binary([Str,0]),
wxe_util:cast(?wxFindReplaceData_SetFindString,
@@ -97,7 +97,7 @@ setFindString(#wx_ref{type=ThisT,ref=ThisRef},Str)
-spec setReplaceString(This, Str) -> 'ok' when
This::wxFindReplaceData(), Str::unicode:chardata().
setReplaceString(#wx_ref{type=ThisT,ref=ThisRef},Str)
- when is_list(Str) ->
+ when ?is_chardata(Str) ->
?CLASS(ThisT,wxFindReplaceData),
Str_UC = unicode:characters_to_binary([Str,0]),
wxe_util:cast(?wxFindReplaceData_SetReplaceString,
diff --git a/lib/wx/src/gen/wxFindReplaceDialog.erl b/lib/wx/src/gen/wxFindReplaceDialog.erl
index a34fc329ae..9250c2fa2f 100644
--- a/lib/wx/src/gen/wxFindReplaceDialog.erl
+++ b/lib/wx/src/gen/wxFindReplaceDialog.erl
@@ -40,40 +40,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
@@ -99,7 +99,7 @@ new() ->
Parent::wxWindow:wxWindow(), Data::wxFindReplaceData:wxFindReplaceData(), Title::unicode:chardata().
new(Parent,Data,Title)
- when is_record(Parent, wx_ref),is_record(Data, wx_ref),is_list(Title) ->
+ when is_record(Parent, wx_ref),is_record(Data, wx_ref),?is_chardata(Title) ->
new(Parent,Data,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxfindreplacedialog.html#wxfindreplacedialogwxfindreplacedialog">external documentation</a>.
@@ -107,7 +107,7 @@ new(Parent,Data,Title)
Parent::wxWindow:wxWindow(), Data::wxFindReplaceData:wxFindReplaceData(), Title::unicode:chardata(),
Option :: {'style', integer()}.
new(#wx_ref{type=ParentT,ref=ParentRef},#wx_ref{type=DataT,ref=DataRef},Title, Options)
- when is_list(Title),is_list(Options) ->
+ when ?is_chardata(Title),is_list(Options) ->
?CLASS(ParentT,wxWindow),
?CLASS(DataT,wxFindReplaceData),
Title_UC = unicode:characters_to_binary([Title,0]),
@@ -122,7 +122,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},#wx_ref{type=DataT,ref=DataRef},Title, O
This::wxFindReplaceDialog(), Parent::wxWindow:wxWindow(), Data::wxFindReplaceData:wxFindReplaceData(), Title::unicode:chardata().
create(This,Parent,Data,Title)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_record(Data, wx_ref),is_list(Title) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_record(Data, wx_ref),?is_chardata(Title) ->
create(This,Parent,Data,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxfindreplacedialog.html#wxfindreplacedialogcreate">external documentation</a>.
@@ -130,7 +130,7 @@ create(This,Parent,Data,Title)
This::wxFindReplaceDialog(), Parent::wxWindow:wxWindow(), Data::wxFindReplaceData:wxFindReplaceData(), Title::unicode:chardata(),
Option :: {'style', integer()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},#wx_ref{type=DataT,ref=DataRef},Title, Options)
- when is_list(Title),is_list(Options) ->
+ when ?is_chardata(Title),is_list(Options) ->
?CLASS(ThisT,wxFindReplaceDialog),
?CLASS(ParentT,wxWindow),
?CLASS(DataT,wxFindReplaceData),
@@ -227,6 +227,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -543,6 +545,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxFont.erl b/lib/wx/src/gen/wxFont.erl
index 56e6e96b6d..fb0ba8b505 100644
--- a/lib/wx/src/gen/wxFont.erl
+++ b/lib/wx/src/gen/wxFont.erl
@@ -49,7 +49,7 @@ new() ->
-spec new(Fontname) -> wxFont() when
Fontname::unicode:chardata().
new(Fontname)
- when is_list(Fontname) ->
+ when ?is_chardata(Fontname) ->
Fontname_UC = unicode:characters_to_binary([Fontname,0]),
wxe_util:construct(?wxFont_new_1,
<<(byte_size(Fontname_UC)):32/?UI,(Fontname_UC)/binary, 0:(((8- ((4+byte_size(Fontname_UC)) band 16#7)) band 16#7))/unit:8>>).
@@ -183,7 +183,7 @@ setDefaultEncoding(Encoding)
-spec setFaceName(This, FaceName) -> boolean() when
This::wxFont(), FaceName::unicode:chardata().
setFaceName(#wx_ref{type=ThisT,ref=ThisRef},FaceName)
- when is_list(FaceName) ->
+ when ?is_chardata(FaceName) ->
?CLASS(ThisT,wxFont),
FaceName_UC = unicode:characters_to_binary([FaceName,0]),
wxe_util:call(?wxFont_SetFaceName,
diff --git a/lib/wx/src/gen/wxFontDialog.erl b/lib/wx/src/gen/wxFontDialog.erl
index 3e6a913973..26010aa46b 100644
--- a/lib/wx/src/gen/wxFontDialog.erl
+++ b/lib/wx/src/gen/wxFontDialog.erl
@@ -40,40 +40,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
@@ -199,6 +199,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -515,6 +517,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxFontPickerCtrl.erl b/lib/wx/src/gen/wxFontPickerCtrl.erl
index 0356f795eb..d1234c5589 100644
--- a/lib/wx/src/gen/wxFontPickerCtrl.erl
+++ b/lib/wx/src/gen/wxFontPickerCtrl.erl
@@ -40,17 +40,18 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getInternalMargin/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,
- getParent/1,getPickerCtrlProportion/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
- getSize/1,getSizer/1,getTextCtrl/1,getTextCtrlProportion/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTextCtrl/1,hasTransparentBackground/1,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getInternalMargin/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPickerCtrlProportion/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextCtrl/1,
+ getTextCtrlProportion/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTextCtrl/1,hasTransparentBackground/1,
hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isPickerCtrlGrowable/1,
isRetained/1,isShown/1,isTextCtrlGrowable/1,isTopLevel/1,layout/1,
@@ -227,6 +228,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -543,6 +546,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxFrame.erl b/lib/wx/src/gen/wxFrame.erl
index 93aad5b235..1dc6106bfa 100644
--- a/lib/wx/src/gen/wxFrame.erl
+++ b/lib/wx/src/gen/wxFrame.erl
@@ -43,32 +43,32 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,
- getParent/1,getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,
- getScrollPos/2,getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,
- getTextExtent/2,getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,
- getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
- hasScrollbar/2,hasTransparentBackground/1,hide/1,iconize/1,iconize/2,
- inheritAttributes/1,initDialog/1,invalidateBestSize/1,isActive/1,
- isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
- isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,isShown/1,isTopLevel/1,
- layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,maximize/1,
- maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,requestUserAttention/1,requestUserAttention/2,screenToClient/1,
- screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
- setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
- setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
- setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
+ getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
+ getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
+ getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
+ hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
+ isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
+ isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,
+ isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
+ makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
+ moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
+ popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
+ screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
+ scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,
+ setBackgroundStyle/2,setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,
+ setCursor/2,setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,
+ setFocusFromKbd/1,setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,
+ setIcons/2,setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
@@ -99,7 +99,7 @@ new() ->
Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata().
new(Parent,Id,Title)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Title) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title) ->
new(Parent,Id,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxframe.html#wxframewxframe">external documentation</a>.
@@ -109,7 +109,7 @@ new(Parent,Id,Title)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
- when is_integer(Id),is_list(Title),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
MOpts = fun({pos, {PosX,PosY}}, Acc) -> [<<1:32/?UI,PosX:32/?UI,PosY:32/?UI,0:32>>|Acc];
@@ -125,7 +125,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
This::wxFrame(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata().
create(This,Parent,Id,Title)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Title) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title) ->
create(This,Parent,Id,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxframe.html#wxframecreate">external documentation</a>.
@@ -135,7 +135,7 @@ create(This,Parent,Id,Title)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
- when is_integer(Id),is_list(Title),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_list(Options) ->
?CLASS(ThisT,wxFrame),
?CLASS(ParentT,wxWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
@@ -284,7 +284,7 @@ setStatusBarPane(#wx_ref{type=ThisT,ref=ThisRef},N)
This::wxFrame(), Text::unicode:chardata().
setStatusText(This,Text)
- when is_record(This, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),?is_chardata(Text) ->
setStatusText(This,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxframe.html#wxframesetstatustext">external documentation</a>.
@@ -292,7 +292,7 @@ setStatusText(This,Text)
This::wxFrame(), Text::unicode:chardata(),
Option :: {'number', integer()}.
setStatusText(#wx_ref{type=ThisT,ref=ThisRef},Text, Options)
- when is_list(Text),is_list(Options) ->
+ when ?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxFrame),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({number, Number}, Acc) -> [<<1:32/?UI,Number:32/?UI>>|Acc];
@@ -375,6 +375,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -695,6 +697,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxGLCanvas.erl b/lib/wx/src/gen/wxGLCanvas.erl
index 2007047bd3..8a94d1df26 100644
--- a/lib/wx/src/gen/wxGLCanvas.erl
+++ b/lib/wx/src/gen/wxGLCanvas.erl
@@ -37,29 +37,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -194,6 +194,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
ok.
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -514,6 +516,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxGauge.erl b/lib/wx/src/gen/wxGauge.erl
index fa2e7618e8..40f7f120b0 100644
--- a/lib/wx/src/gen/wxGauge.erl
+++ b/lib/wx/src/gen/wxGauge.erl
@@ -39,29 +39,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -204,6 +204,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -520,6 +522,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxGenericDirCtrl.erl b/lib/wx/src/gen/wxGenericDirCtrl.erl
index 3ea99c682d..383d592269 100644
--- a/lib/wx/src/gen/wxGenericDirCtrl.erl
+++ b/lib/wx/src/gen/wxGenericDirCtrl.erl
@@ -41,29 +41,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -175,7 +175,7 @@ collapseTree(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec expandPath(This, Path) -> boolean() when
This::wxGenericDirCtrl(), Path::unicode:chardata().
expandPath(#wx_ref{type=ThisT,ref=ThisRef},Path)
- when is_list(Path) ->
+ when ?is_chardata(Path) ->
?CLASS(ThisT,wxGenericDirCtrl),
Path_UC = unicode:characters_to_binary([Path,0]),
wxe_util:call(?wxGenericDirCtrl_ExpandPath,
@@ -249,7 +249,7 @@ reCreateTree(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setDefaultPath(This, Path) -> 'ok' when
This::wxGenericDirCtrl(), Path::unicode:chardata().
setDefaultPath(#wx_ref{type=ThisT,ref=ThisRef},Path)
- when is_list(Path) ->
+ when ?is_chardata(Path) ->
?CLASS(ThisT,wxGenericDirCtrl),
Path_UC = unicode:characters_to_binary([Path,0]),
wxe_util:cast(?wxGenericDirCtrl_SetDefaultPath,
@@ -259,7 +259,7 @@ setDefaultPath(#wx_ref{type=ThisT,ref=ThisRef},Path)
-spec setFilter(This, Filter) -> 'ok' when
This::wxGenericDirCtrl(), Filter::unicode:chardata().
setFilter(#wx_ref{type=ThisT,ref=ThisRef},Filter)
- when is_list(Filter) ->
+ when ?is_chardata(Filter) ->
?CLASS(ThisT,wxGenericDirCtrl),
Filter_UC = unicode:characters_to_binary([Filter,0]),
wxe_util:cast(?wxGenericDirCtrl_SetFilter,
@@ -278,7 +278,7 @@ setFilterIndex(#wx_ref{type=ThisT,ref=ThisRef},N)
-spec setPath(This, Path) -> 'ok' when
This::wxGenericDirCtrl(), Path::unicode:chardata().
setPath(#wx_ref{type=ThisT,ref=ThisRef},Path)
- when is_list(Path) ->
+ when ?is_chardata(Path) ->
?CLASS(ThisT,wxGenericDirCtrl),
Path_UC = unicode:characters_to_binary([Path,0]),
wxe_util:cast(?wxGenericDirCtrl_SetPath,
@@ -297,6 +297,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -613,6 +615,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxGraphicsContext.erl b/lib/wx/src/gen/wxGraphicsContext.erl
index 0aa2119210..2d0271ac48 100644
--- a/lib/wx/src/gen/wxGraphicsContext.erl
+++ b/lib/wx/src/gen/wxGraphicsContext.erl
@@ -287,7 +287,7 @@ drawRoundedRectangle(#wx_ref{type=ThisT,ref=ThisRef},X,Y,W,H,Radius)
-spec drawText(This, Str, X, Y) -> 'ok' when
This::wxGraphicsContext(), Str::unicode:chardata(), X::number(), Y::number().
drawText(#wx_ref{type=ThisT,ref=ThisRef},Str,X,Y)
- when is_list(Str),is_number(X),is_number(Y) ->
+ when ?is_chardata(Str),is_number(X),is_number(Y) ->
?CLASS(ThisT,wxGraphicsContext),
Str_UC = unicode:characters_to_binary([Str,0]),
wxe_util:cast(?wxGraphicsContext_DrawText_3,
@@ -303,13 +303,13 @@ drawText(#wx_ref{type=ThisT,ref=ThisRef},Str,X,Y)
(This, Str, X, Y, BackgroundBrush) -> 'ok' when
This::wxGraphicsContext(), Str::unicode:chardata(), X::number(), Y::number(), BackgroundBrush::wxGraphicsBrush:wxGraphicsBrush().
drawText(#wx_ref{type=ThisT,ref=ThisRef},Str,X,Y,Angle)
- when is_list(Str),is_number(X),is_number(Y),is_number(Angle) ->
+ when ?is_chardata(Str),is_number(X),is_number(Y),is_number(Angle) ->
?CLASS(ThisT,wxGraphicsContext),
Str_UC = unicode:characters_to_binary([Str,0]),
wxe_util:cast(?wxGraphicsContext_DrawText_4_0,
<<ThisRef:32/?UI,(byte_size(Str_UC)):32/?UI,(Str_UC)/binary, 0:(((8- ((0+byte_size(Str_UC)) band 16#7)) band 16#7))/unit:8,X:64/?F,Y:64/?F,Angle:64/?F>>);
drawText(#wx_ref{type=ThisT,ref=ThisRef},Str,X,Y,#wx_ref{type=BackgroundBrushT,ref=BackgroundBrushRef})
- when is_list(Str),is_number(X),is_number(Y) ->
+ when ?is_chardata(Str),is_number(X),is_number(Y) ->
?CLASS(ThisT,wxGraphicsContext),
Str_UC = unicode:characters_to_binary([Str,0]),
?CLASS(BackgroundBrushT,wxGraphicsBrush),
@@ -320,7 +320,7 @@ drawText(#wx_ref{type=ThisT,ref=ThisRef},Str,X,Y,#wx_ref{type=BackgroundBrushT,r
-spec drawText(This, Str, X, Y, Angle, BackgroundBrush) -> 'ok' when
This::wxGraphicsContext(), Str::unicode:chardata(), X::number(), Y::number(), Angle::number(), BackgroundBrush::wxGraphicsBrush:wxGraphicsBrush().
drawText(#wx_ref{type=ThisT,ref=ThisRef},Str,X,Y,Angle,#wx_ref{type=BackgroundBrushT,ref=BackgroundBrushRef})
- when is_list(Str),is_number(X),is_number(Y),is_number(Angle) ->
+ when ?is_chardata(Str),is_number(X),is_number(Y),is_number(Angle) ->
?CLASS(ThisT,wxGraphicsContext),
Str_UC = unicode:characters_to_binary([Str,0]),
?CLASS(BackgroundBrushT,wxGraphicsBrush),
@@ -363,7 +363,7 @@ strokePath(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PathT,ref=PathRef}) ->
-spec getPartialTextExtents(This, Text) -> [number()] when
This::wxGraphicsContext(), Text::unicode:chardata().
getPartialTextExtents(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxGraphicsContext),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxGraphicsContext_GetPartialTextExtents,
@@ -374,7 +374,7 @@ getPartialTextExtents(#wx_ref{type=ThisT,ref=ThisRef},Text)
Result ::{Width::number(), Height::number(), Descent::number(), ExternalLeading::number()},
This::wxGraphicsContext(), Text::unicode:chardata().
getTextExtent(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxGraphicsContext),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxGraphicsContext_GetTextExtent,
diff --git a/lib/wx/src/gen/wxGrid.erl b/lib/wx/src/gen/wxGrid.erl
index a1533cbd9f..b8c94ab555 100644
--- a/lib/wx/src/gen/wxGrid.erl
+++ b/lib/wx/src/gen/wxGrid.erl
@@ -91,37 +91,37 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- doPrepareDC/2,enable/1,enable/2,enableScrolling/3,findWindow/2,fitInside/1,
- freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
- getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
- getClientSize/1,getContainingSizer/1,getCursor/1,getDropTarget/1,
- getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPixelsPerUnit/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getToolTip/1,getUpdateRegion/1,getViewStart/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,prepareDC/2,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,screenToClient/1,screenToClient/2,scroll/3,
- scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
- setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,
- setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFocusIgnoringChildren/1,setFont/2,setForegroundColour/2,setHelpText/2,
- setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
- setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
- setScrollPos/4,setScrollRate/3,setScrollbar/5,setScrollbar/6,setScrollbars/5,
- setScrollbars/6,setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,
- setSizeHints/3,setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,
- setSizerAndFit/3,setTargetWindow/2,setThemeEnabled/2,setToolTip/2,
- setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
+ doPrepareDC/2,dragAcceptFiles/2,enable/1,enable/2,enableScrolling/3,
+ findWindow/2,fitInside/1,freeze/1,getAcceleratorTable/1,getBackgroundColour/1,
+ getBackgroundStyle/1,getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,
+ getChildren/1,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
+ getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPixelsPerUnit/1,
+ getScrollPos/2,getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,
+ getTextExtent/2,getTextExtent/3,getToolTip/1,getUpdateRegion/1,getViewStart/1,
+ getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
+ hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,prepareDC/2,raise/1,refresh/1,refresh/2,
+ refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,
+ screenToClient/1,screenToClient/2,scroll/3,scrollLines/2,scrollPages/2,
+ scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
+ setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
+ setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
+ setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFocusIgnoringChildren/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
+ setPalette/2,setScrollPos/3,setScrollPos/4,setScrollRate/3,setScrollbar/5,
+ setScrollbar/6,setScrollbars/5,setScrollbars/6,setSize/2,setSize/3,
+ setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,setSizer/2,
+ setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setTargetWindow/2,setThemeEnabled/2,
+ setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
setWindowVariant/2,shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
@@ -851,7 +851,7 @@ getDefaultEditorForCell(#wx_ref{type=ThisT,ref=ThisRef},Row,Col)
-spec getDefaultEditorForType(This, TypeName) -> wxGridCellEditor:wxGridCellEditor() when
This::wxGrid(), TypeName::unicode:chardata().
getDefaultEditorForType(#wx_ref{type=ThisT,ref=ThisRef},TypeName)
- when is_list(TypeName) ->
+ when ?is_chardata(TypeName) ->
?CLASS(ThisT,wxGrid),
TypeName_UC = unicode:characters_to_binary([TypeName,0]),
wxe_util:call(?wxGrid_GetDefaultEditorForType,
@@ -878,7 +878,7 @@ getDefaultRendererForCell(#wx_ref{type=ThisT,ref=ThisRef},Row,Col)
-spec getDefaultRendererForType(This, TypeName) -> wxGridCellRenderer:wxGridCellRenderer() when
This::wxGrid(), TypeName::unicode:chardata().
getDefaultRendererForType(#wx_ref{type=ThisT,ref=ThisRef},TypeName)
- when is_list(TypeName) ->
+ when ?is_chardata(TypeName) ->
?CLASS(ThisT,wxGrid),
TypeName_UC = unicode:characters_to_binary([TypeName,0]),
wxe_util:call(?wxGrid_GetDefaultRendererForType,
@@ -1407,7 +1407,7 @@ movePageUp(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec registerDataType(This, TypeName, Renderer, Editor) -> 'ok' when
This::wxGrid(), TypeName::unicode:chardata(), Renderer::wxGridCellRenderer:wxGridCellRenderer(), Editor::wxGridCellEditor:wxGridCellEditor().
registerDataType(#wx_ref{type=ThisT,ref=ThisRef},TypeName,#wx_ref{type=RendererT,ref=RendererRef},#wx_ref{type=EditorT,ref=EditorRef})
- when is_list(TypeName) ->
+ when ?is_chardata(TypeName) ->
?CLASS(ThisT,wxGrid),
TypeName_UC = unicode:characters_to_binary([TypeName,0]),
?CLASS(RendererT,wxGridCellRenderer),
@@ -1634,7 +1634,7 @@ setCellTextColour(#wx_ref{type=ThisT,ref=ThisRef},Val,Row,Col)
-spec setCellValue(This, Coords, S) -> 'ok' when
This::wxGrid(), Coords::{R::integer(), C::integer()}, S::unicode:chardata().
setCellValue(#wx_ref{type=ThisT,ref=ThisRef},{CoordsR,CoordsC},S)
- when is_integer(CoordsR),is_integer(CoordsC),is_list(S) ->
+ when is_integer(CoordsR),is_integer(CoordsC),?is_chardata(S) ->
?CLASS(ThisT,wxGrid),
S_UC = unicode:characters_to_binary([S,0]),
wxe_util:cast(?wxGrid_SetCellValue_2,
@@ -1650,13 +1650,13 @@ setCellValue(#wx_ref{type=ThisT,ref=ThisRef},{CoordsR,CoordsC},S)
(This, Val, Row, Col) -> 'ok' when
This::wxGrid(), Val::unicode:chardata(), Row::integer(), Col::integer().
setCellValue(#wx_ref{type=ThisT,ref=ThisRef},Row,Col,S)
- when is_integer(Row),is_integer(Col),is_list(S) ->
+ when is_integer(Row),is_integer(Col),?is_chardata(S) ->
?CLASS(ThisT,wxGrid),
S_UC = unicode:characters_to_binary([S,0]),
wxe_util:cast(?wxGrid_SetCellValue_3_0,
<<ThisRef:32/?UI,Row:32/?UI,Col:32/?UI,(byte_size(S_UC)):32/?UI,(S_UC)/binary, 0:(((8- ((0+byte_size(S_UC)) band 16#7)) band 16#7))/unit:8>>);
setCellValue(#wx_ref{type=ThisT,ref=ThisRef},Val,Row,Col)
- when is_list(Val),is_integer(Row),is_integer(Col) ->
+ when ?is_chardata(Val),is_integer(Row),is_integer(Col) ->
?CLASS(ThisT,wxGrid),
Val_UC = unicode:characters_to_binary([Val,0]),
wxe_util:cast(?wxGrid_SetCellValue_3_1,
@@ -1717,7 +1717,7 @@ setColFormatFloat(#wx_ref{type=ThisT,ref=ThisRef},Col, Options)
-spec setColFormatCustom(This, Col, TypeName) -> 'ok' when
This::wxGrid(), Col::integer(), TypeName::unicode:chardata().
setColFormatCustom(#wx_ref{type=ThisT,ref=ThisRef},Col,TypeName)
- when is_integer(Col),is_list(TypeName) ->
+ when is_integer(Col),?is_chardata(TypeName) ->
?CLASS(ThisT,wxGrid),
TypeName_UC = unicode:characters_to_binary([TypeName,0]),
wxe_util:cast(?wxGrid_SetColFormatCustom,
@@ -1745,7 +1745,7 @@ setColLabelSize(#wx_ref{type=ThisT,ref=ThisRef},Height)
-spec setColLabelValue(This, Col, Val) -> 'ok' when
This::wxGrid(), Col::integer(), Val::unicode:chardata().
setColLabelValue(#wx_ref{type=ThisT,ref=ThisRef},Col,Val)
- when is_integer(Col),is_list(Val) ->
+ when is_integer(Col),?is_chardata(Val) ->
?CLASS(ThisT,wxGrid),
Val_UC = unicode:characters_to_binary([Val,0]),
wxe_util:cast(?wxGrid_SetColLabelValue,
@@ -1981,7 +1981,7 @@ setRowLabelSize(#wx_ref{type=ThisT,ref=ThisRef},Width)
-spec setRowLabelValue(This, Row, Val) -> 'ok' when
This::wxGrid(), Row::integer(), Val::unicode:chardata().
setRowLabelValue(#wx_ref{type=ThisT,ref=ThisRef},Row,Val)
- when is_integer(Row),is_list(Val) ->
+ when is_integer(Row),?is_chardata(Val) ->
?CLASS(ThisT,wxGrid),
Val_UC = unicode:characters_to_binary([Val,0]),
wxe_util:cast(?wxGrid_SetRowLabelValue,
@@ -2158,6 +2158,8 @@ setFocusIgnoringChildren(This) -> wxPanel:setFocusIgnoringChildren(This).
initDialog(This) -> wxPanel:initDialog(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -2474,6 +2476,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxGridCellBoolEditor.erl b/lib/wx/src/gen/wxGridCellBoolEditor.erl
index 1d949d54ff..59348f94f8 100644
--- a/lib/wx/src/gen/wxGridCellBoolEditor.erl
+++ b/lib/wx/src/gen/wxGridCellBoolEditor.erl
@@ -52,7 +52,7 @@ new() ->
-spec isTrueValue(Value) -> boolean() when
Value::unicode:chardata().
isTrueValue(Value)
- when is_list(Value) ->
+ when ?is_chardata(Value) ->
Value_UC = unicode:characters_to_binary([Value,0]),
wxe_util:call(?wxGridCellBoolEditor_IsTrueValue,
<<(byte_size(Value_UC)):32/?UI,(Value_UC)/binary, 0:(((8- ((4+byte_size(Value_UC)) band 16#7)) band 16#7))/unit:8>>).
diff --git a/lib/wx/src/gen/wxGridCellChoiceEditor.erl b/lib/wx/src/gen/wxGridCellChoiceEditor.erl
index d5487c3618..8f4a07a0bb 100644
--- a/lib/wx/src/gen/wxGridCellChoiceEditor.erl
+++ b/lib/wx/src/gen/wxGridCellChoiceEditor.erl
@@ -68,7 +68,7 @@ new(Choices, Options)
-spec setParameters(This, Params) -> 'ok' when
This::wxGridCellChoiceEditor(), Params::unicode:chardata().
setParameters(#wx_ref{type=ThisT,ref=ThisRef},Params)
- when is_list(Params) ->
+ when ?is_chardata(Params) ->
?CLASS(ThisT,wxGridCellChoiceEditor),
Params_UC = unicode:characters_to_binary([Params,0]),
wxe_util:cast(?wxGridCellChoiceEditor_SetParameters,
diff --git a/lib/wx/src/gen/wxGridCellFloatEditor.erl b/lib/wx/src/gen/wxGridCellFloatEditor.erl
index 6e85469ecf..90b9542afc 100644
--- a/lib/wx/src/gen/wxGridCellFloatEditor.erl
+++ b/lib/wx/src/gen/wxGridCellFloatEditor.erl
@@ -65,7 +65,7 @@ new(Options)
-spec setParameters(This, Params) -> 'ok' when
This::wxGridCellFloatEditor(), Params::unicode:chardata().
setParameters(#wx_ref{type=ThisT,ref=ThisRef},Params)
- when is_list(Params) ->
+ when ?is_chardata(Params) ->
?CLASS(ThisT,wxGridCellFloatEditor),
Params_UC = unicode:characters_to_binary([Params,0]),
wxe_util:cast(?wxGridCellFloatEditor_SetParameters,
diff --git a/lib/wx/src/gen/wxGridCellFloatRenderer.erl b/lib/wx/src/gen/wxGridCellFloatRenderer.erl
index ccb29902b3..72bdc6fc29 100644
--- a/lib/wx/src/gen/wxGridCellFloatRenderer.erl
+++ b/lib/wx/src/gen/wxGridCellFloatRenderer.erl
@@ -81,7 +81,7 @@ getWidth(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setParameters(This, Params) -> 'ok' when
This::wxGridCellFloatRenderer(), Params::unicode:chardata().
setParameters(#wx_ref{type=ThisT,ref=ThisRef},Params)
- when is_list(Params) ->
+ when ?is_chardata(Params) ->
?CLASS(ThisT,wxGridCellFloatRenderer),
Params_UC = unicode:characters_to_binary([Params,0]),
wxe_util:cast(?wxGridCellFloatRenderer_SetParameters,
diff --git a/lib/wx/src/gen/wxGridCellNumberEditor.erl b/lib/wx/src/gen/wxGridCellNumberEditor.erl
index 7a47024b2f..22f9a1839c 100644
--- a/lib/wx/src/gen/wxGridCellNumberEditor.erl
+++ b/lib/wx/src/gen/wxGridCellNumberEditor.erl
@@ -75,7 +75,7 @@ getValue(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setParameters(This, Params) -> 'ok' when
This::wxGridCellNumberEditor(), Params::unicode:chardata().
setParameters(#wx_ref{type=ThisT,ref=ThisRef},Params)
- when is_list(Params) ->
+ when ?is_chardata(Params) ->
?CLASS(ThisT,wxGridCellNumberEditor),
Params_UC = unicode:characters_to_binary([Params,0]),
wxe_util:cast(?wxGridCellNumberEditor_SetParameters,
diff --git a/lib/wx/src/gen/wxGridCellTextEditor.erl b/lib/wx/src/gen/wxGridCellTextEditor.erl
index 4ddb4a7028..39adda5d8b 100644
--- a/lib/wx/src/gen/wxGridCellTextEditor.erl
+++ b/lib/wx/src/gen/wxGridCellTextEditor.erl
@@ -52,7 +52,7 @@ new() ->
-spec setParameters(This, Params) -> 'ok' when
This::wxGridCellTextEditor(), Params::unicode:chardata().
setParameters(#wx_ref{type=ThisT,ref=ThisRef},Params)
- when is_list(Params) ->
+ when ?is_chardata(Params) ->
?CLASS(ThisT,wxGridCellTextEditor),
Params_UC = unicode:characters_to_binary([Params,0]),
wxe_util:cast(?wxGridCellTextEditor_SetParameters,
diff --git a/lib/wx/src/gen/wxHtmlEasyPrinting.erl b/lib/wx/src/gen/wxHtmlEasyPrinting.erl
index a2cf46ed8d..dfb9dcfa1c 100644
--- a/lib/wx/src/gen/wxHtmlEasyPrinting.erl
+++ b/lib/wx/src/gen/wxHtmlEasyPrinting.erl
@@ -77,7 +77,7 @@ getPageSetupData(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec previewFile(This, Htmlfile) -> boolean() when
This::wxHtmlEasyPrinting(), Htmlfile::unicode:chardata().
previewFile(#wx_ref{type=ThisT,ref=ThisRef},Htmlfile)
- when is_list(Htmlfile) ->
+ when ?is_chardata(Htmlfile) ->
?CLASS(ThisT,wxHtmlEasyPrinting),
Htmlfile_UC = unicode:characters_to_binary([Htmlfile,0]),
wxe_util:call(?wxHtmlEasyPrinting_PreviewFile,
@@ -88,7 +88,7 @@ previewFile(#wx_ref{type=ThisT,ref=ThisRef},Htmlfile)
This::wxHtmlEasyPrinting(), Htmltext::unicode:chardata().
previewText(This,Htmltext)
- when is_record(This, wx_ref),is_list(Htmltext) ->
+ when is_record(This, wx_ref),?is_chardata(Htmltext) ->
previewText(This,Htmltext, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxhtmleasyprinting.html#wxhtmleasyprintingpreviewtext">external documentation</a>.
@@ -96,7 +96,7 @@ previewText(This,Htmltext)
This::wxHtmlEasyPrinting(), Htmltext::unicode:chardata(),
Option :: {'basepath', unicode:chardata()}.
previewText(#wx_ref{type=ThisT,ref=ThisRef},Htmltext, Options)
- when is_list(Htmltext),is_list(Options) ->
+ when ?is_chardata(Htmltext),is_list(Options) ->
?CLASS(ThisT,wxHtmlEasyPrinting),
Htmltext_UC = unicode:characters_to_binary([Htmltext,0]),
MOpts = fun({basepath, Basepath}, Acc) -> Basepath_UC = unicode:characters_to_binary([Basepath,0]),[<<1:32/?UI,(byte_size(Basepath_UC)):32/?UI,(Basepath_UC)/binary, 0:(((8- ((0+byte_size(Basepath_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -109,7 +109,7 @@ previewText(#wx_ref{type=ThisT,ref=ThisRef},Htmltext, Options)
-spec printFile(This, Htmlfile) -> boolean() when
This::wxHtmlEasyPrinting(), Htmlfile::unicode:chardata().
printFile(#wx_ref{type=ThisT,ref=ThisRef},Htmlfile)
- when is_list(Htmlfile) ->
+ when ?is_chardata(Htmlfile) ->
?CLASS(ThisT,wxHtmlEasyPrinting),
Htmlfile_UC = unicode:characters_to_binary([Htmlfile,0]),
wxe_util:call(?wxHtmlEasyPrinting_PrintFile,
@@ -120,7 +120,7 @@ printFile(#wx_ref{type=ThisT,ref=ThisRef},Htmlfile)
This::wxHtmlEasyPrinting(), Htmltext::unicode:chardata().
printText(This,Htmltext)
- when is_record(This, wx_ref),is_list(Htmltext) ->
+ when is_record(This, wx_ref),?is_chardata(Htmltext) ->
printText(This,Htmltext, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxhtmleasyprinting.html#wxhtmleasyprintingprinttext">external documentation</a>.
@@ -128,7 +128,7 @@ printText(This,Htmltext)
This::wxHtmlEasyPrinting(), Htmltext::unicode:chardata(),
Option :: {'basepath', unicode:chardata()}.
printText(#wx_ref{type=ThisT,ref=ThisRef},Htmltext, Options)
- when is_list(Htmltext),is_list(Options) ->
+ when ?is_chardata(Htmltext),is_list(Options) ->
?CLASS(ThisT,wxHtmlEasyPrinting),
Htmltext_UC = unicode:characters_to_binary([Htmltext,0]),
MOpts = fun({basepath, Basepath}, Acc) -> Basepath_UC = unicode:characters_to_binary([Basepath,0]),[<<1:32/?UI,(byte_size(Basepath_UC)):32/?UI,(Basepath_UC)/binary, 0:(((8- ((0+byte_size(Basepath_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -150,7 +150,7 @@ pageSetup(#wx_ref{type=ThisT,ref=ThisRef}) ->
This::wxHtmlEasyPrinting(), Normal_face::unicode:chardata(), Fixed_face::unicode:chardata().
setFonts(This,Normal_face,Fixed_face)
- when is_record(This, wx_ref),is_list(Normal_face),is_list(Fixed_face) ->
+ when is_record(This, wx_ref),?is_chardata(Normal_face),?is_chardata(Fixed_face) ->
setFonts(This,Normal_face,Fixed_face, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxhtmleasyprinting.html#wxhtmleasyprintingsetfonts">external documentation</a>.
@@ -158,7 +158,7 @@ setFonts(This,Normal_face,Fixed_face)
This::wxHtmlEasyPrinting(), Normal_face::unicode:chardata(), Fixed_face::unicode:chardata(),
Option :: {'sizes', [integer()]}.
setFonts(#wx_ref{type=ThisT,ref=ThisRef},Normal_face,Fixed_face, Options)
- when is_list(Normal_face),is_list(Fixed_face),is_list(Options) ->
+ when ?is_chardata(Normal_face),?is_chardata(Fixed_face),is_list(Options) ->
?CLASS(ThisT,wxHtmlEasyPrinting),
Normal_face_UC = unicode:characters_to_binary([Normal_face,0]),
Fixed_face_UC = unicode:characters_to_binary([Fixed_face,0]),
@@ -174,7 +174,7 @@ setFonts(#wx_ref{type=ThisT,ref=ThisRef},Normal_face,Fixed_face, Options)
This::wxHtmlEasyPrinting(), Header::unicode:chardata().
setHeader(This,Header)
- when is_record(This, wx_ref),is_list(Header) ->
+ when is_record(This, wx_ref),?is_chardata(Header) ->
setHeader(This,Header, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxhtmleasyprinting.html#wxhtmleasyprintingsetheader">external documentation</a>.
@@ -182,7 +182,7 @@ setHeader(This,Header)
This::wxHtmlEasyPrinting(), Header::unicode:chardata(),
Option :: {'pg', integer()}.
setHeader(#wx_ref{type=ThisT,ref=ThisRef},Header, Options)
- when is_list(Header),is_list(Options) ->
+ when ?is_chardata(Header),is_list(Options) ->
?CLASS(ThisT,wxHtmlEasyPrinting),
Header_UC = unicode:characters_to_binary([Header,0]),
MOpts = fun({pg, Pg}, Acc) -> [<<1:32/?UI,Pg:32/?UI>>|Acc];
@@ -196,7 +196,7 @@ setHeader(#wx_ref{type=ThisT,ref=ThisRef},Header, Options)
This::wxHtmlEasyPrinting(), Footer::unicode:chardata().
setFooter(This,Footer)
- when is_record(This, wx_ref),is_list(Footer) ->
+ when is_record(This, wx_ref),?is_chardata(Footer) ->
setFooter(This,Footer, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxhtmleasyprinting.html#wxhtmleasyprintingsetfooter">external documentation</a>.
@@ -204,7 +204,7 @@ setFooter(This,Footer)
This::wxHtmlEasyPrinting(), Footer::unicode:chardata(),
Option :: {'pg', integer()}.
setFooter(#wx_ref{type=ThisT,ref=ThisRef},Footer, Options)
- when is_list(Footer),is_list(Options) ->
+ when ?is_chardata(Footer),is_list(Options) ->
?CLASS(ThisT,wxHtmlEasyPrinting),
Footer_UC = unicode:characters_to_binary([Footer,0]),
MOpts = fun({pg, Pg}, Acc) -> [<<1:32/?UI,Pg:32/?UI>>|Acc];
diff --git a/lib/wx/src/gen/wxHtmlWindow.erl b/lib/wx/src/gen/wxHtmlWindow.erl
index f9bb135fcf..7e3906b9a9 100644
--- a/lib/wx/src/gen/wxHtmlWindow.erl
+++ b/lib/wx/src/gen/wxHtmlWindow.erl
@@ -45,37 +45,37 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- doPrepareDC/2,enable/1,enable/2,enableScrolling/3,findWindow/2,fit/1,
- fitInside/1,freeze/1,getAcceleratorTable/1,getBackgroundColour/1,
+ doPrepareDC/2,dragAcceptFiles/2,enable/1,enable/2,enableScrolling/3,
+ findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,getBackgroundColour/1,
getBackgroundStyle/1,getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,
- getChildren/1,getClientSize/1,getContainingSizer/1,getCursor/1,getDropTarget/1,
- getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPixelsPerUnit/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getToolTip/1,getUpdateRegion/1,getViewStart/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,prepareDC/2,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,screenToClient/1,screenToClient/2,scroll/3,
- scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
- setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,
- setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFocusIgnoringChildren/1,setFont/2,setForegroundColour/2,setHelpText/2,
- setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
- setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
- setScrollPos/4,setScrollRate/3,setScrollbar/5,setScrollbar/6,setScrollbars/5,
- setScrollbars/6,setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,
- setSizeHints/3,setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,
- setSizerAndFit/3,setTargetWindow/2,setThemeEnabled/2,setToolTip/2,
- setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
+ getChildren/1,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
+ getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPixelsPerUnit/1,
+ getScrollPos/2,getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,
+ getTextExtent/2,getTextExtent/3,getToolTip/1,getUpdateRegion/1,getViewStart/1,
+ getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
+ hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,prepareDC/2,raise/1,refresh/1,refresh/2,
+ refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,
+ screenToClient/1,screenToClient/2,scroll/3,scrollLines/2,scrollPages/2,
+ scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
+ setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
+ setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
+ setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFocusIgnoringChildren/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
+ setPalette/2,setScrollPos/3,setScrollPos/4,setScrollRate/3,setScrollbar/5,
+ setScrollbar/6,setScrollbars/5,setScrollbars/6,setSize/2,setSize/3,
+ setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,setSizer/2,
+ setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setTargetWindow/2,setThemeEnabled/2,
+ setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
setWindowVariant/2,shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
@@ -127,7 +127,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
-spec appendToPage(This, Source) -> boolean() when
This::wxHtmlWindow(), Source::unicode:chardata().
appendToPage(#wx_ref{type=ThisT,ref=ThisRef},Source)
- when is_list(Source) ->
+ when ?is_chardata(Source) ->
?CLASS(ThisT,wxHtmlWindow),
Source_UC = unicode:characters_to_binary([Source,0]),
wxe_util:call(?wxHtmlWindow_AppendToPage,
@@ -209,7 +209,7 @@ historyForward(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec loadFile(This, Filename) -> boolean() when
This::wxHtmlWindow(), Filename::unicode:chardata().
loadFile(#wx_ref{type=ThisT,ref=ThisRef},Filename)
- when is_list(Filename) ->
+ when ?is_chardata(Filename) ->
?CLASS(ThisT,wxHtmlWindow),
Filename_UC = unicode:characters_to_binary([Filename,0]),
wxe_util:call(?wxHtmlWindow_LoadFile,
@@ -219,7 +219,7 @@ loadFile(#wx_ref{type=ThisT,ref=ThisRef},Filename)
-spec loadPage(This, Location) -> boolean() when
This::wxHtmlWindow(), Location::unicode:chardata().
loadPage(#wx_ref{type=ThisT,ref=ThisRef},Location)
- when is_list(Location) ->
+ when ?is_chardata(Location) ->
?CLASS(ThisT,wxHtmlWindow),
Location_UC = unicode:characters_to_binary([Location,0]),
wxe_util:call(?wxHtmlWindow_LoadPage,
@@ -273,7 +273,7 @@ setBorders(#wx_ref{type=ThisT,ref=ThisRef},B)
This::wxHtmlWindow(), Normal_face::unicode:chardata(), Fixed_face::unicode:chardata().
setFonts(This,Normal_face,Fixed_face)
- when is_record(This, wx_ref),is_list(Normal_face),is_list(Fixed_face) ->
+ when is_record(This, wx_ref),?is_chardata(Normal_face),?is_chardata(Fixed_face) ->
setFonts(This,Normal_face,Fixed_face, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxhtmlwindow.html#wxhtmlwindowsetfonts">external documentation</a>.
@@ -281,7 +281,7 @@ setFonts(This,Normal_face,Fixed_face)
This::wxHtmlWindow(), Normal_face::unicode:chardata(), Fixed_face::unicode:chardata(),
Option :: {'sizes', integer()}.
setFonts(#wx_ref{type=ThisT,ref=ThisRef},Normal_face,Fixed_face, Options)
- when is_list(Normal_face),is_list(Fixed_face),is_list(Options) ->
+ when ?is_chardata(Normal_face),?is_chardata(Fixed_face),is_list(Options) ->
?CLASS(ThisT,wxHtmlWindow),
Normal_face_UC = unicode:characters_to_binary([Normal_face,0]),
Fixed_face_UC = unicode:characters_to_binary([Fixed_face,0]),
@@ -295,7 +295,7 @@ setFonts(#wx_ref{type=ThisT,ref=ThisRef},Normal_face,Fixed_face, Options)
-spec setPage(This, Source) -> boolean() when
This::wxHtmlWindow(), Source::unicode:chardata().
setPage(#wx_ref{type=ThisT,ref=ThisRef},Source)
- when is_list(Source) ->
+ when ?is_chardata(Source) ->
?CLASS(ThisT,wxHtmlWindow),
Source_UC = unicode:characters_to_binary([Source,0]),
wxe_util:call(?wxHtmlWindow_SetPage,
@@ -305,7 +305,7 @@ setPage(#wx_ref{type=ThisT,ref=ThisRef},Source)
-spec setRelatedFrame(This, Frame, Format) -> 'ok' when
This::wxHtmlWindow(), Frame::wxFrame:wxFrame(), Format::unicode:chardata().
setRelatedFrame(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=FrameT,ref=FrameRef},Format)
- when is_list(Format) ->
+ when ?is_chardata(Format) ->
?CLASS(ThisT,wxHtmlWindow),
?CLASS(FrameT,wxFrame),
Format_UC = unicode:characters_to_binary([Format,0]),
@@ -371,6 +371,8 @@ setFocusIgnoringChildren(This) -> wxPanel:setFocusIgnoringChildren(This).
initDialog(This) -> wxPanel:initDialog(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -689,6 +691,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxIcon.erl b/lib/wx/src/gen/wxIcon.erl
index c9ec32dffc..9739a403ff 100644
--- a/lib/wx/src/gen/wxIcon.erl
+++ b/lib/wx/src/gen/wxIcon.erl
@@ -60,7 +60,7 @@ new() ->
Loc::wx:wx_object().
new(Filename)
- when is_list(Filename) ->
+ when ?is_chardata(Filename) ->
new(Filename, []);
new(#wx_ref{type=LocT,ref=LocRef}) ->
?CLASS(LocT,wx),
@@ -75,7 +75,7 @@ new(#wx_ref{type=LocT,ref=LocRef}) ->
| {'desiredWidth', integer()}
| {'desiredHeight', integer()}.
new(Filename, Options)
- when is_list(Filename),is_list(Options) ->
+ when ?is_chardata(Filename),is_list(Options) ->
Filename_UC = unicode:characters_to_binary([Filename,0]),
MOpts = fun({type, Type}, Acc) -> [<<1:32/?UI,Type:32/?UI>>|Acc];
({desiredWidth, DesiredWidth}, Acc) -> [<<2:32/?UI,DesiredWidth:32/?UI>>|Acc];
diff --git a/lib/wx/src/gen/wxIconBundle.erl b/lib/wx/src/gen/wxIconBundle.erl
index 47785963e3..aecf382a9f 100644
--- a/lib/wx/src/gen/wxIconBundle.erl
+++ b/lib/wx/src/gen/wxIconBundle.erl
@@ -58,7 +58,7 @@ new(#wx_ref{type=IcT,ref=IcRef}) ->
-spec new(File, Type) -> wxIconBundle() when
File::unicode:chardata(), Type::integer().
new(File,Type)
- when is_list(File),is_integer(Type) ->
+ when ?is_chardata(File),is_integer(Type) ->
File_UC = unicode:characters_to_binary([File,0]),
wxe_util:construct(?wxIconBundle_new_2,
<<(byte_size(File_UC)):32/?UI,(File_UC)/binary, 0:(((8- ((4+byte_size(File_UC)) band 16#7)) band 16#7))/unit:8,Type:32/?UI>>).
@@ -76,7 +76,7 @@ addIcon(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=IconT,ref=IconRef}) ->
-spec addIcon(This, File, Type) -> 'ok' when
This::wxIconBundle(), File::unicode:chardata(), Type::integer().
addIcon(#wx_ref{type=ThisT,ref=ThisRef},File,Type)
- when is_list(File),is_integer(Type) ->
+ when ?is_chardata(File),is_integer(Type) ->
?CLASS(ThisT,wxIconBundle),
File_UC = unicode:characters_to_binary([File,0]),
wxe_util:cast(?wxIconBundle_AddIcon_2,
diff --git a/lib/wx/src/gen/wxImage.erl b/lib/wx/src/gen/wxImage.erl
index e82f3d609e..f3b3d393d1 100644
--- a/lib/wx/src/gen/wxImage.erl
+++ b/lib/wx/src/gen/wxImage.erl
@@ -64,7 +64,7 @@ new() ->
Name::unicode:chardata().
new(Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
new(Name, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wximage.html#wximagewximage">external documentation</a>.
@@ -85,7 +85,7 @@ new(Width,Height)
when is_integer(Width),is_integer(Height) ->
new(Width,Height, []);
new(Name, Options)
- when is_list(Name),is_list(Options) ->
+ when ?is_chardata(Name),is_list(Options) ->
Name_UC = unicode:characters_to_binary([Name,0]),
MOpts = fun({type, Type}, Acc) -> [<<1:32/?UI,Type:32/?UI>>|Acc];
({index, Index}, Acc) -> [<<2:32/?UI,Index:32/?UI>>|Acc];
@@ -123,7 +123,7 @@ new(Width,Height, Options)
wxe_util:construct(?wxImage_new_3_0,
<<Width:32/?UI,Height:32/?UI, BinOpt/binary>>);
new(Name,Mimetype, Options)
- when is_list(Name),is_list(Mimetype),is_list(Options) ->
+ when ?is_chardata(Name),?is_chardata(Mimetype),is_list(Options) ->
Name_UC = unicode:characters_to_binary([Name,0]),
Mimetype_UC = unicode:characters_to_binary([Mimetype,0]),
MOpts = fun({index, Index}, Acc) -> [<<1:32/?UI,Index:32/?UI>>|Acc];
@@ -421,7 +421,7 @@ getGreen(#wx_ref{type=ThisT,ref=ThisRef},X,Y)
Name::unicode:chardata().
getImageCount(Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
getImageCount(Name, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wximage.html#wximagegetimagecount">external documentation</a>.
@@ -430,7 +430,7 @@ getImageCount(Name)
Name::unicode:chardata(),
Option :: {'type', wx:wx_enum()}.
getImageCount(Name, Options)
- when is_list(Name),is_list(Options) ->
+ when ?is_chardata(Name),is_list(Options) ->
Name_UC = unicode:characters_to_binary([Name,0]),
MOpts = fun({type, Type}, Acc) -> [<<1:32/?UI,Type:32/?UI>>|Acc];
(BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
@@ -533,7 +533,7 @@ hasMask(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec getOption(This, Name) -> unicode:charlist() when
This::wxImage(), Name::unicode:chardata().
getOption(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxImage),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxImage_GetOption,
@@ -543,7 +543,7 @@ getOption(#wx_ref{type=ThisT,ref=ThisRef},Name)
-spec getOptionInt(This, Name) -> integer() when
This::wxImage(), Name::unicode:chardata().
getOptionInt(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxImage),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxImage_GetOptionInt,
@@ -553,7 +553,7 @@ getOptionInt(#wx_ref{type=ThisT,ref=ThisRef},Name)
-spec hasOption(This, Name) -> boolean() when
This::wxImage(), Name::unicode:chardata().
hasOption(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxImage),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxImage_HasOption,
@@ -599,7 +599,7 @@ isTransparent(#wx_ref{type=ThisT,ref=ThisRef},X,Y, Options)
This::wxImage(), Name::unicode:chardata().
loadFile(This,Name)
- when is_record(This, wx_ref),is_list(Name) ->
+ when is_record(This, wx_ref),?is_chardata(Name) ->
loadFile(This,Name, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wximage.html#wximageloadfile">external documentation</a>.
@@ -608,7 +608,7 @@ loadFile(This,Name)
Option :: {'type', integer()}
| {'index', integer()}.
loadFile(#wx_ref{type=ThisT,ref=ThisRef},Name, Options)
- when is_list(Name),is_list(Options) ->
+ when ?is_chardata(Name),is_list(Options) ->
?CLASS(ThisT,wxImage),
Name_UC = unicode:characters_to_binary([Name,0]),
MOpts = fun({type, Type}, Acc) -> [<<1:32/?UI,Type:32/?UI>>|Acc];
@@ -623,7 +623,7 @@ loadFile(#wx_ref{type=ThisT,ref=ThisRef},Name, Options)
This::wxImage(), Name::unicode:chardata(), Mimetype::unicode:chardata(),
Option :: {'index', integer()}.
loadFile(#wx_ref{type=ThisT,ref=ThisRef},Name,Mimetype, Options)
- when is_list(Name),is_list(Mimetype),is_list(Options) ->
+ when ?is_chardata(Name),?is_chardata(Mimetype),is_list(Options) ->
?CLASS(ThisT,wxImage),
Name_UC = unicode:characters_to_binary([Name,0]),
Mimetype_UC = unicode:characters_to_binary([Mimetype,0]),
@@ -645,7 +645,7 @@ ok(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec removeHandler(Name) -> boolean() when
Name::unicode:chardata().
removeHandler(Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxImage_RemoveHandler,
<<(byte_size(Name_UC)):32/?UI,(Name_UC)/binary, 0:(((8- ((4+byte_size(Name_UC)) band 16#7)) band 16#7))/unit:8>>).
@@ -784,7 +784,7 @@ rotate90(#wx_ref{type=ThisT,ref=ThisRef}, Options)
-spec saveFile(This, Name) -> boolean() when
This::wxImage(), Name::unicode:chardata().
saveFile(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxImage),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxImage_SaveFile_1,
@@ -800,13 +800,13 @@ saveFile(#wx_ref{type=ThisT,ref=ThisRef},Name)
(This, Name, Mimetype) -> boolean() when
This::wxImage(), Name::unicode:chardata(), Mimetype::unicode:chardata().
saveFile(#wx_ref{type=ThisT,ref=ThisRef},Name,Type)
- when is_list(Name),is_integer(Type) ->
+ when ?is_chardata(Name),is_integer(Type) ->
?CLASS(ThisT,wxImage),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxImage_SaveFile_2_0,
<<ThisRef:32/?UI,(byte_size(Name_UC)):32/?UI,(Name_UC)/binary, 0:(((8- ((0+byte_size(Name_UC)) band 16#7)) band 16#7))/unit:8,Type:32/?UI>>);
saveFile(#wx_ref{type=ThisT,ref=ThisRef},Name,Mimetype)
- when is_list(Name),is_list(Mimetype) ->
+ when ?is_chardata(Name),?is_chardata(Mimetype) ->
?CLASS(ThisT,wxImage),
Name_UC = unicode:characters_to_binary([Name,0]),
Mimetype_UC = unicode:characters_to_binary([Mimetype,0]),
@@ -985,13 +985,13 @@ setMaskFromImage(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=MaskT,ref=MaskRef}
(This, Name, Value) -> 'ok' when
This::wxImage(), Name::unicode:chardata(), Value::unicode:chardata().
setOption(#wx_ref{type=ThisT,ref=ThisRef},Name,Value)
- when is_list(Name),is_integer(Value) ->
+ when ?is_chardata(Name),is_integer(Value) ->
?CLASS(ThisT,wxImage),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:cast(?wxImage_SetOption_2_0,
<<ThisRef:32/?UI,(byte_size(Name_UC)):32/?UI,(Name_UC)/binary, 0:(((8- ((0+byte_size(Name_UC)) band 16#7)) band 16#7))/unit:8,Value:32/?UI>>);
setOption(#wx_ref{type=ThisT,ref=ThisRef},Name,Value)
- when is_list(Name),is_list(Value) ->
+ when ?is_chardata(Name),?is_chardata(Value) ->
?CLASS(ThisT,wxImage),
Name_UC = unicode:characters_to_binary([Name,0]),
Value_UC = unicode:characters_to_binary([Value,0]),
diff --git a/lib/wx/src/gen/wxListBox.erl b/lib/wx/src/gen/wxListBox.erl
index 9ab3616e9b..86d8d41f36 100644
--- a/lib/wx/src/gen/wxListBox.erl
+++ b/lib/wx/src/gen/wxListBox.erl
@@ -40,40 +40,41 @@
centre/1,centre/2,centreOnParent/1,centreOnParent/2,clear/1,clearBackground/1,
clientToScreen/2,clientToScreen/3,close/1,close/2,connect/2,connect/3,
convertDialogToPixels/2,convertPixelsToDialog/2,delete/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- findString/2,findString/3,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientData/2,getClientSize/1,
- getContainingSizer/1,getCount/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,getMinSize/1,
- getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
- getSelection/1,getSize/1,getSizer/1,getString/2,getStringSelection/1,
- getTextExtent/2,getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
- getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
- hasTransparentBackground/1,hide/1,inheritAttributes/1,initDialog/1,
- insert/3,insert/4,invalidateBestSize/1,isDoubleBuffered/1,isEmpty/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,select/2,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientData/3,
- setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
- setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
- setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
- setSelection/2,setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,
- setSizeHints/3,setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,
- setSizerAndFit/3,setString/3,setStringSelection/2,setThemeEnabled/2,
- setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
- setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
- setWindowVariant/2,shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,findString/2,findString/3,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientData/2,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCount/1,getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,
+ getFont/1,getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSelection/1,getSize/1,getSizer/1,
+ getString/2,getStringSelection/1,getTextExtent/2,getTextExtent/3,
+ getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
+ getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
+ hide/1,inheritAttributes/1,initDialog/1,insert/3,insert/4,invalidateBestSize/1,
+ isDoubleBuffered/1,isEmpty/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
+ isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,
+ makeModal/1,makeModal/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
+ moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
+ popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,select/2,setAcceleratorTable/2,
+ setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,
+ setClientData/3,setClientSize/2,setClientSize/3,setContainingSizer/2,
+ setCursor/2,setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,
+ setFocusFromKbd/1,setFont/2,setForegroundColour/2,setHelpText/2,setId/2,
+ setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
+ setScrollPos/4,setScrollbar/5,setScrollbar/6,setSelection/2,setSize/2,
+ setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
+ setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setString/3,
+ setStringSelection/2,setThemeEnabled/2,setToolTip/2,setTransparent/2,
+ setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,
+ setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,
+ shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
validate/1,warpPointer/3]).
@@ -220,7 +221,7 @@ setFirstItem(#wx_ref{type=ThisT,ref=ThisRef},N)
wxe_util:cast(?wxListBox_SetFirstItem_1_0,
<<ThisRef:32/?UI,N:32/?UI>>);
setFirstItem(#wx_ref{type=ThisT,ref=ThisRef},S)
- when is_list(S) ->
+ when ?is_chardata(S) ->
?CLASS(ThisT,wxListBox),
S_UC = unicode:characters_to_binary([S,0]),
wxe_util:cast(?wxListBox_SetFirstItem_1_1,
@@ -280,6 +281,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -596,6 +599,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxListCtrl.erl b/lib/wx/src/gen/wxListCtrl.erl
index d1a063d900..10dc76f276 100644
--- a/lib/wx/src/gen/wxListCtrl.erl
+++ b/lib/wx/src/gen/wxListCtrl.erl
@@ -52,38 +52,38 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundStyle/2,setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,
- setCursor/2,setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,
- setFocusFromKbd/1,setFont/2,setForegroundColour/2,setHelpText/2,setId/2,
- setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
- setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setSize/2,setSize/3,setSize/5,
- setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,setSizer/2,
- setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,setToolTip/2,
- setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
- setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowVariant/2,
- shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
- transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
- validate/1,warpPointer/3]).
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundStyle/2,setCaret/2,
+ setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
+ setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ setWindowStyle/2,setWindowVariant/2,shouldInheritColours/1,show/1,
+ show/2,thaw/1,transferDataFromWindow/1,transferDataToWindow/1,update/1,
+ updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
-export_type([wxListCtrl/0]).
%% @hidden
@@ -94,31 +94,34 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
-type wxListCtrl() :: wx:wx_object().
-%% @spec () -> wxListCtrl()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new() -> wxListCtrl().
new() ->
wxe_util:construct(?wxListCtrl_new_0, <<>>).
-%% @spec (Parent::wxWindow:wxWindow()) -> wxListCtrl()
-%% @equiv new(Parent, [])
+-spec new(Parent) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow().
new(Parent)
when is_record(Parent, wx_ref) ->
new(Parent, []).
-%% @spec (Parent::wxWindow:wxWindow(), [Option]) -> wxListCtrl()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
+%% @doc Creates a listctrl with optional callback functions:
%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
+%% OnGetItemText = (This, Item, Column) -> unicode:charlist()
+%% OnGetItemAttr = (This, Item) -> wxListItemAttr:wxListItemAttr()
%% OnGetItemColumnImage = (This, Item, Column) -> integer()
-%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+%%
+%% See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new(Parent, [Option]) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options)->
@@ -185,26 +188,27 @@ clearAll(#wx_ref{type=ThisT,ref=ThisRef}) ->
<<ThisRef:32/?UI>>).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow()) -> bool()
%% @equiv create(This,Parent, [])
+-spec create(This, Parent) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow().
create(This,Parent)
when is_record(This, wx_ref),is_record(Parent, wx_ref) ->
create(This,Parent, []).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow(), [Option]) -> bool()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
-%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
-%% OnGetItemColumnImage = (This, Item, Column) -> integer()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlcreate">external documentation</a>.
+-spec create(This, Parent, [Option]) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
+
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
@@ -268,7 +272,7 @@ ensureVisible(#wx_ref{type=ThisT,ref=ThisRef},Item)
This::wxListCtrl(), Start::integer(), Str::unicode:chardata().
findItem(This,Start,Str)
- when is_record(This, wx_ref),is_integer(Start),is_list(Str) ->
+ when is_record(This, wx_ref),is_integer(Start),?is_chardata(Str) ->
findItem(This,Start,Str, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlistctrl.html#wxlistctrlfinditem">external documentation</a>.
@@ -282,7 +286,7 @@ findItem(This,Start,Str)
(This, Start, Pt, Direction) -> integer() when
This::wxListCtrl(), Start::integer(), Pt::{X::integer(), Y::integer()}, Direction::integer().
findItem(#wx_ref{type=ThisT,ref=ThisRef},Start,Str, Options)
- when is_integer(Start),is_list(Str),is_list(Options) ->
+ when is_integer(Start),?is_chardata(Str),is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
Str_UC = unicode:characters_to_binary([Str,0]),
MOpts = fun({partial, Partial}, Acc) -> [<<1:32/?UI,(wxe_util:from_bool(Partial)):32/?UI>>|Acc];
@@ -536,7 +540,7 @@ hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PointX,PointY})
This::wxListCtrl(), Col::integer(), Info::wxListItem:wxListItem().
insertColumn(This,Col,Heading)
- when is_record(This, wx_ref),is_integer(Col),is_list(Heading) ->
+ when is_record(This, wx_ref),is_integer(Col),?is_chardata(Heading) ->
insertColumn(This,Col,Heading, []);
insertColumn(#wx_ref{type=ThisT,ref=ThisRef},Col,#wx_ref{type=InfoT,ref=InfoRef})
when is_integer(Col) ->
@@ -551,7 +555,7 @@ insertColumn(#wx_ref{type=ThisT,ref=ThisRef},Col,#wx_ref{type=InfoT,ref=InfoRef}
Option :: {'format', integer()}
| {'width', integer()}.
insertColumn(#wx_ref{type=ThisT,ref=ThisRef},Col,Heading, Options)
- when is_integer(Col),is_list(Heading),is_list(Options) ->
+ when is_integer(Col),?is_chardata(Heading),is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
Heading_UC = unicode:characters_to_binary([Heading,0]),
MOpts = fun({format, Format}, Acc) -> [<<1:32/?UI,Format:32/?UI>>|Acc];
@@ -585,7 +589,7 @@ insertItem(#wx_ref{type=ThisT,ref=ThisRef},Index,ImageIndex)
wxe_util:call(?wxListCtrl_InsertItem_2_0,
<<ThisRef:32/?UI,Index:32/?UI,ImageIndex:32/?UI>>);
insertItem(#wx_ref{type=ThisT,ref=ThisRef},Index,Label)
- when is_integer(Index),is_list(Label) ->
+ when is_integer(Index),?is_chardata(Label) ->
?CLASS(ThisT,wxListCtrl),
Label_UC = unicode:characters_to_binary([Label,0]),
wxe_util:call(?wxListCtrl_InsertItem_2_1,
@@ -595,7 +599,7 @@ insertItem(#wx_ref{type=ThisT,ref=ThisRef},Index,Label)
-spec insertItem(This, Index, Label, ImageIndex) -> integer() when
This::wxListCtrl(), Index::integer(), Label::unicode:chardata(), ImageIndex::integer().
insertItem(#wx_ref{type=ThisT,ref=ThisRef},Index,Label,ImageIndex)
- when is_integer(Index),is_list(Label),is_integer(ImageIndex) ->
+ when is_integer(Index),?is_chardata(Label),is_integer(ImageIndex) ->
?CLASS(ThisT,wxListCtrl),
Label_UC = unicode:characters_to_binary([Label,0]),
wxe_util:call(?wxListCtrl_InsertItem_3,
@@ -680,7 +684,7 @@ setItem(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=InfoT,ref=InfoRef}) ->
This::wxListCtrl(), Index::integer(), Col::integer(), Label::unicode:chardata().
setItem(This,Index,Col,Label)
- when is_record(This, wx_ref),is_integer(Index),is_integer(Col),is_list(Label) ->
+ when is_record(This, wx_ref),is_integer(Index),is_integer(Col),?is_chardata(Label) ->
setItem(This,Index,Col,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlistctrl.html#wxlistctrlsetitem">external documentation</a>.
@@ -688,7 +692,7 @@ setItem(This,Index,Col,Label)
This::wxListCtrl(), Index::integer(), Col::integer(), Label::unicode:chardata(),
Option :: {'imageId', integer()}.
setItem(#wx_ref{type=ThisT,ref=ThisRef},Index,Col,Label, Options)
- when is_integer(Index),is_integer(Col),is_list(Label),is_list(Options) ->
+ when is_integer(Index),is_integer(Col),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
Label_UC = unicode:characters_to_binary([Label,0]),
MOpts = fun({imageId, ImageId}, Acc) -> [<<1:32/?UI,ImageId:32/?UI>>|Acc];
@@ -786,7 +790,7 @@ setItemState(#wx_ref{type=ThisT,ref=ThisRef},Item,State,StateMask)
-spec setItemText(This, Item, Str) -> 'ok' when
This::wxListCtrl(), Item::integer(), Str::unicode:chardata().
setItemText(#wx_ref{type=ThisT,ref=ThisRef},Item,Str)
- when is_integer(Item),is_list(Str) ->
+ when is_integer(Item),?is_chardata(Str) ->
?CLASS(ThisT,wxListCtrl),
Str_UC = unicode:characters_to_binary([Str,0]),
wxe_util:cast(?wxListCtrl_SetItemText,
@@ -871,6 +875,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -1183,6 +1189,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxListItem.erl b/lib/wx/src/gen/wxListItem.erl
index 1530a8c514..56a1719c55 100644
--- a/lib/wx/src/gen/wxListItem.erl
+++ b/lib/wx/src/gen/wxListItem.erl
@@ -236,7 +236,7 @@ setStateMask(#wx_ref{type=ThisT,ref=ThisRef},StateMask)
-spec setText(This, Text) -> 'ok' when
This::wxListItem(), Text::unicode:chardata().
setText(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxListItem),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxListItem_SetText,
diff --git a/lib/wx/src/gen/wxListView.erl b/lib/wx/src/gen/wxListView.erl
index 908bf4c1c9..adba629d5a 100644
--- a/lib/wx/src/gen/wxListView.erl
+++ b/lib/wx/src/gen/wxListView.erl
@@ -39,29 +39,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -169,6 +169,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -485,6 +487,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxListbook.erl b/lib/wx/src/gen/wxListbook.erl
index a7050e1db9..e2ea559587 100644
--- a/lib/wx/src/gen/wxListbook.erl
+++ b/lib/wx/src/gen/wxListbook.erl
@@ -43,29 +43,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -120,7 +120,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
This::wxListbook(), Page::wxWindow:wxWindow(), Text::unicode:chardata().
addPage(This,Page,Text)
- when is_record(This, wx_ref),is_record(Page, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),is_record(Page, wx_ref),?is_chardata(Text) ->
addPage(This,Page,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlistbook.html#wxlistbookaddpage">external documentation</a>.
@@ -129,7 +129,7 @@ addPage(This,Page,Text)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
addPage(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PageT,ref=PageRef},Text, Options)
- when is_list(Text),is_list(Options) ->
+ when ?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxListbook),
?CLASS(PageT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
@@ -296,7 +296,7 @@ hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PtX,PtY})
This::wxListbook(), N::integer(), Page::wxWindow:wxWindow(), Text::unicode:chardata().
insertPage(This,N,Page,Text)
- when is_record(This, wx_ref),is_integer(N),is_record(Page, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(N),is_record(Page, wx_ref),?is_chardata(Text) ->
insertPage(This,N,Page,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlistbook.html#wxlistbookinsertpage">external documentation</a>.
@@ -305,7 +305,7 @@ insertPage(This,N,Page,Text)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
insertPage(#wx_ref{type=ThisT,ref=ThisRef},N,#wx_ref{type=PageT,ref=PageRef},Text, Options)
- when is_integer(N),is_list(Text),is_list(Options) ->
+ when is_integer(N),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxListbook),
?CLASS(PageT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
@@ -347,7 +347,7 @@ setPageImage(#wx_ref{type=ThisT,ref=ThisRef},N,ImageId)
-spec setPageText(This, N, StrText) -> boolean() when
This::wxListbook(), N::integer(), StrText::unicode:chardata().
setPageText(#wx_ref{type=ThisT,ref=ThisRef},N,StrText)
- when is_integer(N),is_list(StrText) ->
+ when is_integer(N),?is_chardata(StrText) ->
?CLASS(ThisT,wxListbook),
StrText_UC = unicode:characters_to_binary([StrText,0]),
wxe_util:call(?wxListbook_SetPageText,
@@ -384,6 +384,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -700,6 +702,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxLocale.erl b/lib/wx/src/gen/wxLocale.erl
index d473731bf8..1ce7c3e3e8 100644
--- a/lib/wx/src/gen/wxLocale.erl
+++ b/lib/wx/src/gen/wxLocale.erl
@@ -92,7 +92,7 @@ init(#wx_ref{type=ThisT,ref=ThisRef}, Options)
-spec addCatalog(This, SzDomain) -> boolean() when
This::wxLocale(), SzDomain::unicode:chardata().
addCatalog(#wx_ref{type=ThisT,ref=ThisRef},SzDomain)
- when is_list(SzDomain) ->
+ when ?is_chardata(SzDomain) ->
?CLASS(ThisT,wxLocale),
SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),
wxe_util:call(?wxLocale_AddCatalog_1,
@@ -103,7 +103,7 @@ addCatalog(#wx_ref{type=ThisT,ref=ThisRef},SzDomain)
-spec addCatalog(This, SzDomain, MsgIdLanguage, MsgIdCharset) -> boolean() when
This::wxLocale(), SzDomain::unicode:chardata(), MsgIdLanguage::wx:wx_enum(), MsgIdCharset::unicode:chardata().
addCatalog(#wx_ref{type=ThisT,ref=ThisRef},SzDomain,MsgIdLanguage,MsgIdCharset)
- when is_list(SzDomain),is_integer(MsgIdLanguage),is_list(MsgIdCharset) ->
+ when ?is_chardata(SzDomain),is_integer(MsgIdLanguage),?is_chardata(MsgIdCharset) ->
?CLASS(ThisT,wxLocale),
SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),
MsgIdCharset_UC = unicode:characters_to_binary([MsgIdCharset,0]),
@@ -114,7 +114,7 @@ addCatalog(#wx_ref{type=ThisT,ref=ThisRef},SzDomain,MsgIdLanguage,MsgIdCharset)
-spec addCatalogLookupPathPrefix(Prefix) -> 'ok' when
Prefix::unicode:chardata().
addCatalogLookupPathPrefix(Prefix)
- when is_list(Prefix) ->
+ when ?is_chardata(Prefix) ->
Prefix_UC = unicode:characters_to_binary([Prefix,0]),
wxe_util:cast(?wxLocale_AddCatalogLookupPathPrefix,
<<(byte_size(Prefix_UC)):32/?UI,(Prefix_UC)/binary, 0:(((8- ((4+byte_size(Prefix_UC)) band 16#7)) band 16#7))/unit:8>>).
@@ -164,7 +164,7 @@ getName(#wx_ref{type=ThisT,ref=ThisRef}) ->
This::wxLocale(), SzOrigString::unicode:chardata().
getString(This,SzOrigString)
- when is_record(This, wx_ref),is_list(SzOrigString) ->
+ when is_record(This, wx_ref),?is_chardata(SzOrigString) ->
getString(This,SzOrigString, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetstring">external documentation</a>.
@@ -172,7 +172,7 @@ getString(This,SzOrigString)
This::wxLocale(), SzOrigString::unicode:chardata(),
Option :: {'szDomain', unicode:chardata()}.
getString(#wx_ref{type=ThisT,ref=ThisRef},SzOrigString, Options)
- when is_list(SzOrigString),is_list(Options) ->
+ when ?is_chardata(SzOrigString),is_list(Options) ->
?CLASS(ThisT,wxLocale),
SzOrigString_UC = unicode:characters_to_binary([SzOrigString,0]),
MOpts = fun({szDomain, SzDomain}, Acc) -> SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),[<<1:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -186,7 +186,7 @@ getString(#wx_ref{type=ThisT,ref=ThisRef},SzOrigString, Options)
This::wxLocale(), SzOrigString::unicode:chardata(), SzOrigString2::unicode:chardata(), N::integer().
getString(This,SzOrigString,SzOrigString2,N)
- when is_record(This, wx_ref),is_list(SzOrigString),is_list(SzOrigString2),is_integer(N) ->
+ when is_record(This, wx_ref),?is_chardata(SzOrigString),?is_chardata(SzOrigString2),is_integer(N) ->
getString(This,SzOrigString,SzOrigString2,N, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetstring">external documentation</a>.
@@ -194,7 +194,7 @@ getString(This,SzOrigString,SzOrigString2,N)
This::wxLocale(), SzOrigString::unicode:chardata(), SzOrigString2::unicode:chardata(), N::integer(),
Option :: {'szDomain', unicode:chardata()}.
getString(#wx_ref{type=ThisT,ref=ThisRef},SzOrigString,SzOrigString2,N, Options)
- when is_list(SzOrigString),is_list(SzOrigString2),is_integer(N),is_list(Options) ->
+ when ?is_chardata(SzOrigString),?is_chardata(SzOrigString2),is_integer(N),is_list(Options) ->
?CLASS(ThisT,wxLocale),
SzOrigString_UC = unicode:characters_to_binary([SzOrigString,0]),
SzOrigString2_UC = unicode:characters_to_binary([SzOrigString2,0]),
@@ -209,7 +209,7 @@ getString(#wx_ref{type=ThisT,ref=ThisRef},SzOrigString,SzOrigString2,N, Options)
This::wxLocale(), SzHeader::unicode:chardata().
getHeaderValue(This,SzHeader)
- when is_record(This, wx_ref),is_list(SzHeader) ->
+ when is_record(This, wx_ref),?is_chardata(SzHeader) ->
getHeaderValue(This,SzHeader, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetheadervalue">external documentation</a>.
@@ -217,7 +217,7 @@ getHeaderValue(This,SzHeader)
This::wxLocale(), SzHeader::unicode:chardata(),
Option :: {'szDomain', unicode:chardata()}.
getHeaderValue(#wx_ref{type=ThisT,ref=ThisRef},SzHeader, Options)
- when is_list(SzHeader),is_list(Options) ->
+ when ?is_chardata(SzHeader),is_list(Options) ->
?CLASS(ThisT,wxLocale),
SzHeader_UC = unicode:characters_to_binary([SzHeader,0]),
MOpts = fun({szDomain, SzDomain}, Acc) -> SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),[<<1:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -257,7 +257,7 @@ getSystemLanguage() ->
-spec isLoaded(This, SzDomain) -> boolean() when
This::wxLocale(), SzDomain::unicode:chardata().
isLoaded(#wx_ref{type=ThisT,ref=ThisRef},SzDomain)
- when is_list(SzDomain) ->
+ when ?is_chardata(SzDomain) ->
?CLASS(ThisT,wxLocale),
SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),
wxe_util:call(?wxLocale_IsLoaded,
diff --git a/lib/wx/src/gen/wxMDIChildFrame.erl b/lib/wx/src/gen/wxMDIChildFrame.erl
index b294e4898d..0d388c016a 100644
--- a/lib/wx/src/gen/wxMDIChildFrame.erl
+++ b/lib/wx/src/gen/wxMDIChildFrame.erl
@@ -42,45 +42,45 @@
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createStatusBar/1,createStatusBar/2,createToolBar/1,createToolBar/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientAreaOrigin/1,
- getClientSize/1,getContainingSizer/1,getCursor/1,getDropTarget/1,
- getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
- getLabel/1,getMaxSize/1,getMenuBar/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getStatusBar/1,
- getStatusBarPane/1,getTextExtent/2,getTextExtent/3,getTitle/1,getToolBar/1,
- getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,processCommand/2,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,sendSizeEvent/1,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMenuBar/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
- setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setStatusBar/2,
- setStatusBarPane/2,setStatusText/2,setStatusText/3,setStatusWidths/2,
- setThemeEnabled/2,setTitle/2,setToolBar/2,setToolTip/2,setTransparent/2,
- setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,
- setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,
- shouldInheritColours/1,show/1,show/2,showFullScreen/2,showFullScreen/3,
- thaw/1,transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
- updateWindowUI/2,validate/1,warpPointer/3]).
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientAreaOrigin/1,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
+ getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,getMenuBar/1,getMinSize/1,
+ getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getStatusBar/1,getStatusBarPane/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolBar/1,getToolTip/1,getUpdateRegion/1,
+ getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
+ hasScrollbar/2,hasTransparentBackground/1,hide/1,iconize/1,iconize/2,
+ inheritAttributes/1,initDialog/1,invalidateBestSize/1,isActive/1,
+ isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
+ isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,processCommand/2,raise/1,refresh/1,
+ refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
+ reparent/2,requestUserAttention/1,requestUserAttention/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ sendSizeEvent/1,setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,
+ setBackgroundStyle/2,setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,
+ setCursor/2,setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,
+ setFocusFromKbd/1,setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,
+ setIcons/2,setId/2,setLabel/2,setMaxSize/2,setMenuBar/2,setMinSize/2,
+ setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
+ setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
+ setShape/2,setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setStatusBar/2,setStatusBarPane/2,setStatusText/2,setStatusText/3,
+ setStatusWidths/2,setThemeEnabled/2,setTitle/2,setToolBar/2,setToolTip/2,
+ setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
+ setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
+ setWindowVariant/2,shouldInheritColours/1,show/1,show/2,showFullScreen/2,
+ showFullScreen/3,thaw/1,transferDataFromWindow/1,transferDataToWindow/1,
+ update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
-export_type([wxMDIChildFrame/0]).
%% @hidden
@@ -102,7 +102,7 @@ new() ->
Parent::wxMDIParentFrame:wxMDIParentFrame(), Id::integer(), Title::unicode:chardata().
new(Parent,Id,Title)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Title) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title) ->
new(Parent,Id,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmdichildframe.html#wxmdichildframewxmdichildframe">external documentation</a>.
@@ -112,7 +112,7 @@ new(Parent,Id,Title)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
- when is_integer(Id),is_list(Title),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_list(Options) ->
?CLASS(ParentT,wxMDIParentFrame),
Title_UC = unicode:characters_to_binary([Title,0]),
MOpts = fun({pos, {PosX,PosY}}, Acc) -> [<<1:32/?UI,PosX:32/?UI,PosY:32/?UI,0:32>>|Acc];
@@ -136,7 +136,7 @@ activate(#wx_ref{type=ThisT,ref=ThisRef}) ->
This::wxMDIChildFrame(), Parent::wxMDIParentFrame:wxMDIParentFrame(), Id::integer(), Title::unicode:chardata().
create(This,Parent,Id,Title)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Title) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title) ->
create(This,Parent,Id,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmdichildframe.html#wxmdichildframecreate">external documentation</a>.
@@ -146,7 +146,7 @@ create(This,Parent,Id,Title)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
- when is_integer(Id),is_list(Title),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_list(Options) ->
?CLASS(ThisT,wxMDIChildFrame),
?CLASS(ParentT,wxMDIParentFrame),
Title_UC = unicode:characters_to_binary([Title,0]),
@@ -275,6 +275,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -595,6 +597,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxMDIClientWindow.erl b/lib/wx/src/gen/wxMDIClientWindow.erl
index ddc31760f4..4fc080c64d 100644
--- a/lib/wx/src/gen/wxMDIClientWindow.erl
+++ b/lib/wx/src/gen/wxMDIClientWindow.erl
@@ -37,29 +37,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -136,6 +136,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
ok.
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -456,6 +458,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxMDIParentFrame.erl b/lib/wx/src/gen/wxMDIParentFrame.erl
index 63b4ec8e23..59f24a1b40 100644
--- a/lib/wx/src/gen/wxMDIParentFrame.erl
+++ b/lib/wx/src/gen/wxMDIParentFrame.erl
@@ -43,27 +43,28 @@
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createStatusBar/1,createStatusBar/2,createToolBar/1,createToolBar/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientAreaOrigin/1,
- getClientSize/1,getContainingSizer/1,getCursor/1,getDropTarget/1,
- getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
- getLabel/1,getMaxSize/1,getMenuBar/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getStatusBar/1,
- getStatusBarPane/1,getTextExtent/2,getTextExtent/3,getTitle/1,getToolBar/1,
- getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- processCommand/2,raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,
- releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientAreaOrigin/1,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
+ getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,getMenuBar/1,getMinSize/1,
+ getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getStatusBar/1,getStatusBarPane/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolBar/1,getToolTip/1,getUpdateRegion/1,
+ getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
+ hasScrollbar/2,hasTransparentBackground/1,hide/1,iconize/1,iconize/2,
+ inheritAttributes/1,initDialog/1,invalidateBestSize/1,isActive/1,
+ isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
+ isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,maximize/1,
+ maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
+ navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
+ popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,processCommand/2,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
scrollWindow/4,sendSizeEvent/1,setAcceleratorTable/2,setAutoLayout/2,
setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
@@ -103,7 +104,7 @@ new() ->
Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata().
new(Parent,Id,Title)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Title) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title) ->
new(Parent,Id,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmdiparentframe.html#wxmdiparentframewxmdiparentframe">external documentation</a>.
@@ -113,7 +114,7 @@ new(Parent,Id,Title)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
- when is_integer(Id),is_list(Title),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
MOpts = fun({pos, {PosX,PosY}}, Acc) -> [<<1:32/?UI,PosX:32/?UI,PosY:32/?UI,0:32>>|Acc];
@@ -161,7 +162,7 @@ cascade(#wx_ref{type=ThisT,ref=ThisRef}) ->
This::wxMDIParentFrame(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata().
create(This,Parent,Id,Title)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Title) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title) ->
create(This,Parent,Id,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmdiparentframe.html#wxmdiparentframecreate">external documentation</a>.
@@ -171,7 +172,7 @@ create(This,Parent,Id,Title)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
- when is_integer(Id),is_list(Title),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_list(Options) ->
?CLASS(ThisT,wxMDIParentFrame),
?CLASS(ParentT,wxWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
@@ -313,6 +314,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -633,6 +636,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxMenu.erl b/lib/wx/src/gen/wxMenu.erl
index 317ea38685..26fe23701d 100644
--- a/lib/wx/src/gen/wxMenu.erl
+++ b/lib/wx/src/gen/wxMenu.erl
@@ -69,7 +69,7 @@ new(Options)
Title::unicode:chardata(),
Option :: {'style', integer()}.
new(Title, Options)
- when is_list(Title),is_list(Options) ->
+ when ?is_chardata(Title),is_list(Options) ->
Title_UC = unicode:characters_to_binary([Title,0]),
MOpts = fun({style, Style}, Acc) -> [<<1:32/?UI,Style:32/?UI>>|Acc];
(BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
@@ -91,7 +91,7 @@ append(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ItemT,ref=ItemRef}) ->
This::wxMenu(), Itemid::integer(), Text::unicode:chardata().
append(This,Itemid,Text)
- when is_record(This, wx_ref),is_integer(Itemid),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Itemid),?is_chardata(Text) ->
append(This,Itemid,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmenu.html#wxmenuappend">external documentation</a>.
@@ -110,10 +110,10 @@ append(This,Itemid,Text)
| {'kind', wx:wx_enum()}.
append(This,Itemid,Text,Submenu)
- when is_record(This, wx_ref),is_integer(Itemid),is_list(Text),is_record(Submenu, wx_ref) ->
+ when is_record(This, wx_ref),is_integer(Itemid),?is_chardata(Text),is_record(Submenu, wx_ref) ->
append(This,Itemid,Text,Submenu, []);
append(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text, Options)
- when is_integer(Itemid),is_list(Text),is_list(Options) ->
+ when is_integer(Itemid),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({help, Help}, Acc) -> Help_UC = unicode:characters_to_binary([Help,0]),[<<1:32/?UI,(byte_size(Help_UC)):32/?UI,(Help_UC)/binary, 0:(((8- ((0+byte_size(Help_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -135,14 +135,14 @@ append(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text, Options)
This::wxMenu(), Itemid::integer(), Text::unicode:chardata(), Submenu::wxMenu(),
Option :: {'help', unicode:chardata()}.
append(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text,Help,IsCheckable)
- when is_integer(Itemid),is_list(Text),is_list(Help),is_boolean(IsCheckable) ->
+ when is_integer(Itemid),?is_chardata(Text),?is_chardata(Help),is_boolean(IsCheckable) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
Help_UC = unicode:characters_to_binary([Help,0]),
wxe_util:cast(?wxMenu_Append_4_0,
<<ThisRef:32/?UI,Itemid:32/?UI,(byte_size(Text_UC)):32/?UI,(Text_UC)/binary, 0:(((8- ((4+byte_size(Text_UC)) band 16#7)) band 16#7))/unit:8,(byte_size(Help_UC)):32/?UI,(Help_UC)/binary, 0:(((8- ((4+byte_size(Help_UC)) band 16#7)) band 16#7))/unit:8,(wxe_util:from_bool(IsCheckable)):32/?UI>>);
append(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text,#wx_ref{type=SubmenuT,ref=SubmenuRef}, Options)
- when is_integer(Itemid),is_list(Text),is_list(Options) ->
+ when is_integer(Itemid),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
?CLASS(SubmenuT,wxMenu),
@@ -157,7 +157,7 @@ append(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text,#wx_ref{type=SubmenuT,ref=Sub
This::wxMenu(), Itemid::integer(), Text::unicode:chardata().
appendCheckItem(This,Itemid,Text)
- when is_record(This, wx_ref),is_integer(Itemid),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Itemid),?is_chardata(Text) ->
appendCheckItem(This,Itemid,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmenu.html#wxmenuappendcheckitem">external documentation</a>.
@@ -165,7 +165,7 @@ appendCheckItem(This,Itemid,Text)
This::wxMenu(), Itemid::integer(), Text::unicode:chardata(),
Option :: {'help', unicode:chardata()}.
appendCheckItem(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text, Options)
- when is_integer(Itemid),is_list(Text),is_list(Options) ->
+ when is_integer(Itemid),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({help, Help}, Acc) -> Help_UC = unicode:characters_to_binary([Help,0]),[<<1:32/?UI,(byte_size(Help_UC)):32/?UI,(Help_UC)/binary, 0:(((8- ((0+byte_size(Help_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -179,7 +179,7 @@ appendCheckItem(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text, Options)
This::wxMenu(), Itemid::integer(), Text::unicode:chardata().
appendRadioItem(This,Itemid,Text)
- when is_record(This, wx_ref),is_integer(Itemid),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Itemid),?is_chardata(Text) ->
appendRadioItem(This,Itemid,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmenu.html#wxmenuappendradioitem">external documentation</a>.
@@ -187,7 +187,7 @@ appendRadioItem(This,Itemid,Text)
This::wxMenu(), Itemid::integer(), Text::unicode:chardata(),
Option :: {'help', unicode:chardata()}.
appendRadioItem(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text, Options)
- when is_integer(Itemid),is_list(Text),is_list(Options) ->
+ when is_integer(Itemid),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({help, Help}, Acc) -> Help_UC = unicode:characters_to_binary([Help,0]),[<<1:32/?UI,(byte_size(Help_UC)):32/?UI,(Help_UC)/binary, 0:(((8- ((0+byte_size(Help_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -285,7 +285,7 @@ findItem(#wx_ref{type=ThisT,ref=ThisRef},Itemid)
wxe_util:call(?wxMenu_FindItem_2,
<<ThisRef:32/?UI,Itemid:32/?UI>>);
findItem(#wx_ref{type=ThisT,ref=ThisRef},Item)
- when is_list(Item) ->
+ when ?is_chardata(Item) ->
?CLASS(ThisT,wxMenu),
Item_UC = unicode:characters_to_binary([Item,0]),
wxe_util:call(?wxMenu_FindItem_1,
@@ -386,7 +386,7 @@ insert(#wx_ref{type=ThisT,ref=ThisRef},Pos,Itemid, Options)
This::wxMenu(), Pos::integer(), Itemid::integer(), Text::unicode:chardata(), Submenu::wxMenu().
insert(This,Pos,Itemid,Text,Submenu)
- when is_record(This, wx_ref),is_integer(Pos),is_integer(Itemid),is_list(Text),is_record(Submenu, wx_ref) ->
+ when is_record(This, wx_ref),is_integer(Pos),is_integer(Itemid),?is_chardata(Text),is_record(Submenu, wx_ref) ->
insert(This,Pos,Itemid,Text,Submenu, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmenu.html#wxmenuinsert">external documentation</a>.
@@ -401,14 +401,14 @@ insert(This,Pos,Itemid,Text,Submenu)
This::wxMenu(), Pos::integer(), Itemid::integer(), Text::unicode:chardata(), Submenu::wxMenu(),
Option :: {'help', unicode:chardata()}.
insert(#wx_ref{type=ThisT,ref=ThisRef},Pos,Itemid,Text,Help,IsCheckable)
- when is_integer(Pos),is_integer(Itemid),is_list(Text),is_list(Help),is_boolean(IsCheckable) ->
+ when is_integer(Pos),is_integer(Itemid),?is_chardata(Text),?is_chardata(Help),is_boolean(IsCheckable) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
Help_UC = unicode:characters_to_binary([Help,0]),
wxe_util:cast(?wxMenu_Insert_5_0,
<<ThisRef:32/?UI,Pos:32/?UI,Itemid:32/?UI,(byte_size(Text_UC)):32/?UI,(Text_UC)/binary, 0:(((8- ((0+byte_size(Text_UC)) band 16#7)) band 16#7))/unit:8,(byte_size(Help_UC)):32/?UI,(Help_UC)/binary, 0:(((8- ((4+byte_size(Help_UC)) band 16#7)) band 16#7))/unit:8,(wxe_util:from_bool(IsCheckable)):32/?UI>>);
insert(#wx_ref{type=ThisT,ref=ThisRef},Pos,Itemid,Text,#wx_ref{type=SubmenuT,ref=SubmenuRef}, Options)
- when is_integer(Pos),is_integer(Itemid),is_list(Text),is_list(Options) ->
+ when is_integer(Pos),is_integer(Itemid),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
?CLASS(SubmenuT,wxMenu),
@@ -423,7 +423,7 @@ insert(#wx_ref{type=ThisT,ref=ThisRef},Pos,Itemid,Text,#wx_ref{type=SubmenuT,ref
This::wxMenu(), Pos::integer(), Itemid::integer(), Text::unicode:chardata().
insertCheckItem(This,Pos,Itemid,Text)
- when is_record(This, wx_ref),is_integer(Pos),is_integer(Itemid),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Pos),is_integer(Itemid),?is_chardata(Text) ->
insertCheckItem(This,Pos,Itemid,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmenu.html#wxmenuinsertcheckitem">external documentation</a>.
@@ -431,7 +431,7 @@ insertCheckItem(This,Pos,Itemid,Text)
This::wxMenu(), Pos::integer(), Itemid::integer(), Text::unicode:chardata(),
Option :: {'help', unicode:chardata()}.
insertCheckItem(#wx_ref{type=ThisT,ref=ThisRef},Pos,Itemid,Text, Options)
- when is_integer(Pos),is_integer(Itemid),is_list(Text),is_list(Options) ->
+ when is_integer(Pos),is_integer(Itemid),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({help, Help}, Acc) -> Help_UC = unicode:characters_to_binary([Help,0]),[<<1:32/?UI,(byte_size(Help_UC)):32/?UI,(Help_UC)/binary, 0:(((8- ((0+byte_size(Help_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -445,7 +445,7 @@ insertCheckItem(#wx_ref{type=ThisT,ref=ThisRef},Pos,Itemid,Text, Options)
This::wxMenu(), Pos::integer(), Itemid::integer(), Text::unicode:chardata().
insertRadioItem(This,Pos,Itemid,Text)
- when is_record(This, wx_ref),is_integer(Pos),is_integer(Itemid),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Pos),is_integer(Itemid),?is_chardata(Text) ->
insertRadioItem(This,Pos,Itemid,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmenu.html#wxmenuinsertradioitem">external documentation</a>.
@@ -453,7 +453,7 @@ insertRadioItem(This,Pos,Itemid,Text)
This::wxMenu(), Pos::integer(), Itemid::integer(), Text::unicode:chardata(),
Option :: {'help', unicode:chardata()}.
insertRadioItem(#wx_ref{type=ThisT,ref=ThisRef},Pos,Itemid,Text, Options)
- when is_integer(Pos),is_integer(Itemid),is_list(Text),is_list(Options) ->
+ when is_integer(Pos),is_integer(Itemid),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({help, Help}, Acc) -> Help_UC = unicode:characters_to_binary([Help,0]),[<<1:32/?UI,(byte_size(Help_UC)):32/?UI,(Help_UC)/binary, 0:(((8- ((0+byte_size(Help_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -532,7 +532,7 @@ prepend(#wx_ref{type=ThisT,ref=ThisRef},Itemid, Options)
This::wxMenu(), Itemid::integer(), Text::unicode:chardata(), Submenu::wxMenu().
prepend(This,Itemid,Text,Submenu)
- when is_record(This, wx_ref),is_integer(Itemid),is_list(Text),is_record(Submenu, wx_ref) ->
+ when is_record(This, wx_ref),is_integer(Itemid),?is_chardata(Text),is_record(Submenu, wx_ref) ->
prepend(This,Itemid,Text,Submenu, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmenu.html#wxmenuprepend">external documentation</a>.
@@ -547,14 +547,14 @@ prepend(This,Itemid,Text,Submenu)
This::wxMenu(), Itemid::integer(), Text::unicode:chardata(), Submenu::wxMenu(),
Option :: {'help', unicode:chardata()}.
prepend(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text,Help,IsCheckable)
- when is_integer(Itemid),is_list(Text),is_list(Help),is_boolean(IsCheckable) ->
+ when is_integer(Itemid),?is_chardata(Text),?is_chardata(Help),is_boolean(IsCheckable) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
Help_UC = unicode:characters_to_binary([Help,0]),
wxe_util:cast(?wxMenu_Prepend_4_0,
<<ThisRef:32/?UI,Itemid:32/?UI,(byte_size(Text_UC)):32/?UI,(Text_UC)/binary, 0:(((8- ((4+byte_size(Text_UC)) band 16#7)) band 16#7))/unit:8,(byte_size(Help_UC)):32/?UI,(Help_UC)/binary, 0:(((8- ((4+byte_size(Help_UC)) band 16#7)) band 16#7))/unit:8,(wxe_util:from_bool(IsCheckable)):32/?UI>>);
prepend(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text,#wx_ref{type=SubmenuT,ref=SubmenuRef}, Options)
- when is_integer(Itemid),is_list(Text),is_list(Options) ->
+ when is_integer(Itemid),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
?CLASS(SubmenuT,wxMenu),
@@ -569,7 +569,7 @@ prepend(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text,#wx_ref{type=SubmenuT,ref=Su
This::wxMenu(), Itemid::integer(), Text::unicode:chardata().
prependCheckItem(This,Itemid,Text)
- when is_record(This, wx_ref),is_integer(Itemid),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Itemid),?is_chardata(Text) ->
prependCheckItem(This,Itemid,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmenu.html#wxmenuprependcheckitem">external documentation</a>.
@@ -577,7 +577,7 @@ prependCheckItem(This,Itemid,Text)
This::wxMenu(), Itemid::integer(), Text::unicode:chardata(),
Option :: {'help', unicode:chardata()}.
prependCheckItem(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text, Options)
- when is_integer(Itemid),is_list(Text),is_list(Options) ->
+ when is_integer(Itemid),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({help, Help}, Acc) -> Help_UC = unicode:characters_to_binary([Help,0]),[<<1:32/?UI,(byte_size(Help_UC)):32/?UI,(Help_UC)/binary, 0:(((8- ((0+byte_size(Help_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -591,7 +591,7 @@ prependCheckItem(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text, Options)
This::wxMenu(), Itemid::integer(), Text::unicode:chardata().
prependRadioItem(This,Itemid,Text)
- when is_record(This, wx_ref),is_integer(Itemid),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Itemid),?is_chardata(Text) ->
prependRadioItem(This,Itemid,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmenu.html#wxmenuprependradioitem">external documentation</a>.
@@ -599,7 +599,7 @@ prependRadioItem(This,Itemid,Text)
This::wxMenu(), Itemid::integer(), Text::unicode:chardata(),
Option :: {'help', unicode:chardata()}.
prependRadioItem(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Text, Options)
- when is_integer(Itemid),is_list(Text),is_list(Options) ->
+ when is_integer(Itemid),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxMenu),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({help, Help}, Acc) -> Help_UC = unicode:characters_to_binary([Help,0]),[<<1:32/?UI,(byte_size(Help_UC)):32/?UI,(Help_UC)/binary, 0:(((8- ((0+byte_size(Help_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -640,7 +640,7 @@ remove(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ItemT,ref=ItemRef}) ->
-spec setHelpString(This, Itemid, HelpString) -> 'ok' when
This::wxMenu(), Itemid::integer(), HelpString::unicode:chardata().
setHelpString(#wx_ref{type=ThisT,ref=ThisRef},Itemid,HelpString)
- when is_integer(Itemid),is_list(HelpString) ->
+ when is_integer(Itemid),?is_chardata(HelpString) ->
?CLASS(ThisT,wxMenu),
HelpString_UC = unicode:characters_to_binary([HelpString,0]),
wxe_util:cast(?wxMenu_SetHelpString,
@@ -650,7 +650,7 @@ setHelpString(#wx_ref{type=ThisT,ref=ThisRef},Itemid,HelpString)
-spec setLabel(This, Itemid, Label) -> 'ok' when
This::wxMenu(), Itemid::integer(), Label::unicode:chardata().
setLabel(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Label)
- when is_integer(Itemid),is_list(Label) ->
+ when is_integer(Itemid),?is_chardata(Label) ->
?CLASS(ThisT,wxMenu),
Label_UC = unicode:characters_to_binary([Label,0]),
wxe_util:cast(?wxMenu_SetLabel,
@@ -660,7 +660,7 @@ setLabel(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Label)
-spec setTitle(This, Title) -> 'ok' when
This::wxMenu(), Title::unicode:chardata().
setTitle(#wx_ref{type=ThisT,ref=ThisRef},Title)
- when is_list(Title) ->
+ when ?is_chardata(Title) ->
?CLASS(ThisT,wxMenu),
Title_UC = unicode:characters_to_binary([Title,0]),
wxe_util:cast(?wxMenu_SetTitle,
diff --git a/lib/wx/src/gen/wxMenuBar.erl b/lib/wx/src/gen/wxMenuBar.erl
index 05f7a423d5..fa613c9f3b 100644
--- a/lib/wx/src/gen/wxMenuBar.erl
+++ b/lib/wx/src/gen/wxMenuBar.erl
@@ -41,26 +41,27 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,getBackgroundColour/1,
- getBackgroundStyle/1,getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,
- getChildren/1,getClientSize/1,getContainingSizer/1,getCursor/1,getDropTarget/1,
- getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getMaxSize/1,getMinSize/1,
- getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
- getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
- getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
- hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
- initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isExposed/2,
- isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,layout/1,
- lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,move/3,move/4,
- moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
- pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
- popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
- refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
- screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
- setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
- setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ dragAcceptFiles/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
+ getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
+ getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
+ getContentScaleFactor/1,getCursor/1,getDropTarget/1,getEventHandler/1,
+ getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
+ getHandle/1,getHelpText/1,getId/1,getMaxSize/1,getMinSize/1,getName/1,
+ getParent/1,getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,
+ getScrollPos/2,getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,
+ getTextExtent/2,getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,inheritAttributes/1,initDialog/1,
+ invalidateBestSize/1,isDoubleBuffered/1,isExposed/2,isExposed/3,isExposed/5,
+ isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,
+ makeModal/1,makeModal/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
+ moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
+ popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,
+ setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
@@ -98,7 +99,7 @@ new(Style)
-spec append(This, Menu, Title) -> boolean() when
This::wxMenuBar(), Menu::wxMenu:wxMenu(), Title::unicode:chardata().
append(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=MenuT,ref=MenuRef},Title)
- when is_list(Title) ->
+ when ?is_chardata(Title) ->
?CLASS(ThisT,wxMenuBar),
?CLASS(MenuT,wxMenu),
Title_UC = unicode:characters_to_binary([Title,0]),
@@ -157,7 +158,7 @@ enableTop(#wx_ref{type=ThisT,ref=ThisRef},Pos,Flag)
-spec findMenu(This, Title) -> integer() when
This::wxMenuBar(), Title::unicode:chardata().
findMenu(#wx_ref{type=ThisT,ref=ThisRef},Title)
- when is_list(Title) ->
+ when ?is_chardata(Title) ->
?CLASS(ThisT,wxMenuBar),
Title_UC = unicode:characters_to_binary([Title,0]),
wxe_util:call(?wxMenuBar_FindMenu,
@@ -167,7 +168,7 @@ findMenu(#wx_ref{type=ThisT,ref=ThisRef},Title)
-spec findMenuItem(This, MenuString, ItemString) -> integer() when
This::wxMenuBar(), MenuString::unicode:chardata(), ItemString::unicode:chardata().
findMenuItem(#wx_ref{type=ThisT,ref=ThisRef},MenuString,ItemString)
- when is_list(MenuString),is_list(ItemString) ->
+ when ?is_chardata(MenuString),?is_chardata(ItemString) ->
?CLASS(ThisT,wxMenuBar),
MenuString_UC = unicode:characters_to_binary([MenuString,0]),
ItemString_UC = unicode:characters_to_binary([ItemString,0]),
@@ -239,7 +240,7 @@ getMenuCount(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec insert(This, Pos, Menu, Title) -> boolean() when
This::wxMenuBar(), Pos::integer(), Menu::wxMenu:wxMenu(), Title::unicode:chardata().
insert(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=MenuT,ref=MenuRef},Title)
- when is_integer(Pos),is_list(Title) ->
+ when is_integer(Pos),?is_chardata(Title) ->
?CLASS(ThisT,wxMenuBar),
?CLASS(MenuT,wxMenu),
Title_UC = unicode:characters_to_binary([Title,0]),
@@ -285,7 +286,7 @@ remove(#wx_ref{type=ThisT,ref=ThisRef},Pos)
-spec replace(This, Pos, Menu, Title) -> wxMenu:wxMenu() when
This::wxMenuBar(), Pos::integer(), Menu::wxMenu:wxMenu(), Title::unicode:chardata().
replace(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=MenuT,ref=MenuRef},Title)
- when is_integer(Pos),is_list(Title) ->
+ when is_integer(Pos),?is_chardata(Title) ->
?CLASS(ThisT,wxMenuBar),
?CLASS(MenuT,wxMenu),
Title_UC = unicode:characters_to_binary([Title,0]),
@@ -296,7 +297,7 @@ replace(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=MenuT,ref=MenuRef},Titl
-spec setHelpString(This, Itemid, HelpString) -> 'ok' when
This::wxMenuBar(), Itemid::integer(), HelpString::unicode:chardata().
setHelpString(#wx_ref{type=ThisT,ref=ThisRef},Itemid,HelpString)
- when is_integer(Itemid),is_list(HelpString) ->
+ when is_integer(Itemid),?is_chardata(HelpString) ->
?CLASS(ThisT,wxMenuBar),
HelpString_UC = unicode:characters_to_binary([HelpString,0]),
wxe_util:cast(?wxMenuBar_SetHelpString,
@@ -306,7 +307,7 @@ setHelpString(#wx_ref{type=ThisT,ref=ThisRef},Itemid,HelpString)
-spec setLabel(This, S) -> 'ok' when
This::wxMenuBar(), S::unicode:chardata().
setLabel(#wx_ref{type=ThisT,ref=ThisRef},S)
- when is_list(S) ->
+ when ?is_chardata(S) ->
?CLASS(ThisT,wxMenuBar),
S_UC = unicode:characters_to_binary([S,0]),
wxe_util:cast(?wxMenuBar_SetLabel_1,
@@ -316,7 +317,7 @@ setLabel(#wx_ref{type=ThisT,ref=ThisRef},S)
-spec setLabel(This, Itemid, Label) -> 'ok' when
This::wxMenuBar(), Itemid::integer(), Label::unicode:chardata().
setLabel(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Label)
- when is_integer(Itemid),is_list(Label) ->
+ when is_integer(Itemid),?is_chardata(Label) ->
?CLASS(ThisT,wxMenuBar),
Label_UC = unicode:characters_to_binary([Label,0]),
wxe_util:cast(?wxMenuBar_SetLabel_2,
@@ -326,7 +327,7 @@ setLabel(#wx_ref{type=ThisT,ref=ThisRef},Itemid,Label)
-spec setLabelTop(This, Pos, Label) -> 'ok' when
This::wxMenuBar(), Pos::integer(), Label::unicode:chardata().
setLabelTop(#wx_ref{type=ThisT,ref=ThisRef},Pos,Label)
- when is_integer(Pos),is_list(Label) ->
+ when is_integer(Pos),?is_chardata(Label) ->
?CLASS(ThisT,wxMenuBar),
Label_UC = unicode:characters_to_binary([Label,0]),
wxe_util:cast(?wxMenuBar_SetLabelTop,
@@ -340,6 +341,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
ok.
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -650,6 +653,8 @@ fit(This) -> wxWindow:fit(This).
%% @hidden
findWindow(This,Winid) -> wxWindow:findWindow(This,Winid).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxMenuItem.erl b/lib/wx/src/gen/wxMenuItem.erl
index 324910d15d..2e0a1c756a 100644
--- a/lib/wx/src/gen/wxMenuItem.erl
+++ b/lib/wx/src/gen/wxMenuItem.erl
@@ -153,7 +153,7 @@ getLabel(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec getLabelFromText(Text) -> unicode:charlist() when
Text::unicode:chardata().
getLabelFromText(Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxMenuItem_GetLabelFromText,
<<(byte_size(Text_UC)):32/?UI,(Text_UC)/binary, 0:(((8- ((4+byte_size(Text_UC)) band 16#7)) band 16#7))/unit:8>>).
@@ -235,7 +235,7 @@ setBitmap(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=BitmapT,ref=BitmapRef}) -
-spec setHelp(This, Str) -> 'ok' when
This::wxMenuItem(), Str::unicode:chardata().
setHelp(#wx_ref{type=ThisT,ref=ThisRef},Str)
- when is_list(Str) ->
+ when ?is_chardata(Str) ->
?CLASS(ThisT,wxMenuItem),
Str_UC = unicode:characters_to_binary([Str,0]),
wxe_util:cast(?wxMenuItem_SetHelp,
@@ -263,7 +263,7 @@ setSubMenu(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=MenuT,ref=MenuRef}) ->
-spec setText(This, Str) -> 'ok' when
This::wxMenuItem(), Str::unicode:chardata().
setText(#wx_ref{type=ThisT,ref=ThisRef},Str)
- when is_list(Str) ->
+ when ?is_chardata(Str) ->
?CLASS(ThisT,wxMenuItem),
Str_UC = unicode:characters_to_binary([Str,0]),
wxe_util:cast(?wxMenuItem_SetText,
diff --git a/lib/wx/src/gen/wxMessageDialog.erl b/lib/wx/src/gen/wxMessageDialog.erl
index 30e47529cc..a63f66741b 100644
--- a/lib/wx/src/gen/wxMessageDialog.erl
+++ b/lib/wx/src/gen/wxMessageDialog.erl
@@ -40,40 +40,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
@@ -93,7 +93,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
Parent::wxWindow:wxWindow(), Message::unicode:chardata().
new(Parent,Message)
- when is_record(Parent, wx_ref),is_list(Message) ->
+ when is_record(Parent, wx_ref),?is_chardata(Message) ->
new(Parent,Message, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmessagedialog.html#wxmessagedialogwxmessagedialog">external documentation</a>.
@@ -103,7 +103,7 @@ new(Parent,Message)
| {'style', integer()}
| {'pos', {X::integer(), Y::integer()}}.
new(#wx_ref{type=ParentT,ref=ParentRef},Message, Options)
- when is_list(Message),is_list(Options) ->
+ when ?is_chardata(Message),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Message_UC = unicode:characters_to_binary([Message,0]),
MOpts = fun({caption, Caption}, Acc) -> Caption_UC = unicode:characters_to_binary([Caption,0]),[<<1:32/?UI,(byte_size(Caption_UC)):32/?UI,(Caption_UC)/binary, 0:(((8- ((0+byte_size(Caption_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -192,6 +192,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -508,6 +510,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxMiniFrame.erl b/lib/wx/src/gen/wxMiniFrame.erl
index 4ae9764819..37dc2f2e04 100644
--- a/lib/wx/src/gen/wxMiniFrame.erl
+++ b/lib/wx/src/gen/wxMiniFrame.erl
@@ -41,27 +41,28 @@
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createStatusBar/1,createStatusBar/2,createToolBar/1,createToolBar/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientAreaOrigin/1,
- getClientSize/1,getContainingSizer/1,getCursor/1,getDropTarget/1,
- getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
- getLabel/1,getMaxSize/1,getMenuBar/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getStatusBar/1,
- getStatusBarPane/1,getTextExtent/2,getTextExtent/3,getTitle/1,getToolBar/1,
- getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- processCommand/2,raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,
- releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientAreaOrigin/1,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
+ getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,getMenuBar/1,getMinSize/1,
+ getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getStatusBar/1,getStatusBarPane/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolBar/1,getToolTip/1,getUpdateRegion/1,
+ getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
+ hasScrollbar/2,hasTransparentBackground/1,hide/1,iconize/1,iconize/2,
+ inheritAttributes/1,initDialog/1,invalidateBestSize/1,isActive/1,
+ isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
+ isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,maximize/1,
+ maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
+ navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
+ popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,processCommand/2,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
scrollWindow/4,sendSizeEvent/1,setAcceleratorTable/2,setAutoLayout/2,
setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
@@ -101,7 +102,7 @@ new() ->
Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata().
new(Parent,Id,Title)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Title) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title) ->
new(Parent,Id,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxminiframe.html#wxminiframewxminiframe">external documentation</a>.
@@ -111,7 +112,7 @@ new(Parent,Id,Title)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
- when is_integer(Id),is_list(Title),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
MOpts = fun({pos, {PosX,PosY}}, Acc) -> [<<1:32/?UI,PosX:32/?UI,PosY:32/?UI,0:32>>|Acc];
@@ -127,7 +128,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
This::wxMiniFrame(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata().
create(This,Parent,Id,Title)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Title) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title) ->
create(This,Parent,Id,Title, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxminiframe.html#wxminiframecreate">external documentation</a>.
@@ -137,7 +138,7 @@ create(This,Parent,Id,Title)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Title, Options)
- when is_integer(Id),is_list(Title),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_list(Options) ->
?CLASS(ThisT,wxMiniFrame),
?CLASS(ParentT,wxWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
@@ -241,6 +242,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -561,6 +564,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxMultiChoiceDialog.erl b/lib/wx/src/gen/wxMultiChoiceDialog.erl
index 0436515256..eef15d561e 100644
--- a/lib/wx/src/gen/wxMultiChoiceDialog.erl
+++ b/lib/wx/src/gen/wxMultiChoiceDialog.erl
@@ -40,40 +40,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
@@ -99,7 +99,7 @@ new() ->
Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[unicode:chardata()].
new(Parent,Message,Caption,Choices)
- when is_record(Parent, wx_ref),is_list(Message),is_list(Caption),is_list(Choices) ->
+ when is_record(Parent, wx_ref),?is_chardata(Message),?is_chardata(Caption),is_list(Choices) ->
new(Parent,Message,Caption,Choices, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxmultichoicedialog.html#wxmultichoicedialogwxmultichoicedialog">external documentation</a>.
@@ -108,7 +108,7 @@ new(Parent,Message,Caption,Choices)
Option :: {'style', integer()}
| {'pos', {X::integer(), Y::integer()}}.
new(#wx_ref{type=ParentT,ref=ParentRef},Message,Caption,Choices, Options)
- when is_list(Message),is_list(Caption),is_list(Choices),is_list(Options) ->
+ when ?is_chardata(Message),?is_chardata(Caption),is_list(Choices),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Message_UC = unicode:characters_to_binary([Message,0]),
Caption_UC = unicode:characters_to_binary([Caption,0]),
@@ -217,6 +217,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -533,6 +535,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxNotebook.erl b/lib/wx/src/gen/wxNotebook.erl
index 24f96ac88c..d7d1b0f87f 100644
--- a/lib/wx/src/gen/wxNotebook.erl
+++ b/lib/wx/src/gen/wxNotebook.erl
@@ -43,29 +43,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -120,7 +120,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Winid, Options)
This::wxNotebook(), Page::wxWindow:wxWindow(), Text::unicode:chardata().
addPage(This,Page,Text)
- when is_record(This, wx_ref),is_record(Page, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),is_record(Page, wx_ref),?is_chardata(Text) ->
addPage(This,Page,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxnotebook.html#wxnotebookaddpage">external documentation</a>.
@@ -129,7 +129,7 @@ addPage(This,Page,Text)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
addPage(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PageT,ref=PageRef},Text, Options)
- when is_list(Text),is_list(Options) ->
+ when ?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxNotebook),
?CLASS(PageT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
@@ -312,7 +312,7 @@ hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PtX,PtY})
This::wxNotebook(), Position::integer(), Win::wxWindow:wxWindow(), StrText::unicode:chardata().
insertPage(This,Position,Win,StrText)
- when is_record(This, wx_ref),is_integer(Position),is_record(Win, wx_ref),is_list(StrText) ->
+ when is_record(This, wx_ref),is_integer(Position),is_record(Win, wx_ref),?is_chardata(StrText) ->
insertPage(This,Position,Win,StrText, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxnotebook.html#wxnotebookinsertpage">external documentation</a>.
@@ -321,7 +321,7 @@ insertPage(This,Position,Win,StrText)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
insertPage(#wx_ref{type=ThisT,ref=ThisRef},Position,#wx_ref{type=WinT,ref=WinRef},StrText, Options)
- when is_integer(Position),is_list(StrText),is_list(Options) ->
+ when is_integer(Position),?is_chardata(StrText),is_list(Options) ->
?CLASS(ThisT,wxNotebook),
?CLASS(WinT,wxWindow),
StrText_UC = unicode:characters_to_binary([StrText,0]),
@@ -372,7 +372,7 @@ setPageImage(#wx_ref{type=ThisT,ref=ThisRef},NPage,NImage)
-spec setPageText(This, NPage, StrText) -> boolean() when
This::wxNotebook(), NPage::integer(), StrText::unicode:chardata().
setPageText(#wx_ref{type=ThisT,ref=ThisRef},NPage,StrText)
- when is_integer(NPage),is_list(StrText) ->
+ when is_integer(NPage),?is_chardata(StrText) ->
?CLASS(ThisT,wxNotebook),
StrText_UC = unicode:characters_to_binary([StrText,0]),
wxe_util:call(?wxNotebook_SetPageText,
@@ -409,6 +409,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -725,6 +727,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxPanel.erl b/lib/wx/src/gen/wxPanel.erl
index 07898cf19e..fc48d569af 100644
--- a/lib/wx/src/gen/wxPanel.erl
+++ b/lib/wx/src/gen/wxPanel.erl
@@ -37,29 +37,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
+ isExposed/5,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,
+ lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
+ moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
+ popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,
+ setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -155,6 +155,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
ok.
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -473,6 +475,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxPasswordEntryDialog.erl b/lib/wx/src/gen/wxPasswordEntryDialog.erl
index e8ee8c8f37..4667376783 100644
--- a/lib/wx/src/gen/wxPasswordEntryDialog.erl
+++ b/lib/wx/src/gen/wxPasswordEntryDialog.erl
@@ -41,34 +41,34 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getValue/1,getVirtualSize/1,
- getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
- hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
- initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
- isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
- isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
- lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
- move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
- pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
- popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
- refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
- requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
- scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
- setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
- setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
- setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getValue/1,
+ getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
+ hasScrollbar/2,hasTransparentBackground/1,hide/1,iconize/1,iconize/2,
+ inheritAttributes/1,initDialog/1,invalidateBestSize/1,isActive/1,
+ isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
+ isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,isShown/1,
+ isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
+ maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
+ navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
+ popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
+ refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
+ reparent/2,requestUserAttention/1,requestUserAttention/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,
+ setBackgroundStyle/2,setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,
+ setCursor/2,setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,
+ setFocusFromKbd/1,setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,
+ setIcons/2,setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -95,7 +95,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
Parent::wxWindow:wxWindow(), Message::unicode:chardata().
new(Parent,Message)
- when is_record(Parent, wx_ref),is_list(Message) ->
+ when is_record(Parent, wx_ref),?is_chardata(Message) ->
new(Parent,Message, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxpasswordentrydialog.html#wxpasswordentrydialogwxpasswordentrydialog">external documentation</a>.
@@ -106,7 +106,7 @@ new(Parent,Message)
| {'style', integer()}
| {'pos', {X::integer(), Y::integer()}}.
new(#wx_ref{type=ParentT,ref=ParentRef},Message, Options)
- when is_list(Message),is_list(Options) ->
+ when ?is_chardata(Message),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Message_UC = unicode:characters_to_binary([Message,0]),
MOpts = fun({caption, Caption}, Acc) -> Caption_UC = unicode:characters_to_binary([Caption,0]),[<<1:32/?UI,(byte_size(Caption_UC)):32/?UI,(Caption_UC)/binary, 0:(((8- ((0+byte_size(Caption_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -201,6 +201,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -517,6 +519,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxPickerBase.erl b/lib/wx/src/gen/wxPickerBase.erl
index 33931f9119..0ea3a36d33 100644
--- a/lib/wx/src/gen/wxPickerBase.erl
+++ b/lib/wx/src/gen/wxPickerBase.erl
@@ -41,29 +41,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -214,6 +214,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -530,6 +532,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxPopupTransientWindow.erl b/lib/wx/src/gen/wxPopupTransientWindow.erl
index b1feef6dcb..6ec2fcae62 100644
--- a/lib/wx/src/gen/wxPopupTransientWindow.erl
+++ b/lib/wx/src/gen/wxPopupTransientWindow.erl
@@ -38,38 +38,39 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,position/3,raise/1,
- refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
- setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
- setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
- setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
- setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
- setThemeEnabled/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
- setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
- setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
- show/1,show/2,thaw/1,transferDataFromWindow/1,transferDataToWindow/1,
- update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,position/3,raise/1,refresh/1,refresh/2,
+ refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,
+ screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
+ scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,
+ setBackgroundStyle/2,setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,
+ setCursor/2,setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,
+ setFocusFromKbd/1,setFont/2,setForegroundColour/2,setHelpText/2,setId/2,
+ setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
+ setScrollPos/4,setScrollbar/5,setScrollbar/6,setSize/2,setSize/3,setSize/5,
+ setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,setSizer/2,
+ setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,setToolTip/2,
+ setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
+ setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
+ setWindowVariant/2,shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
+ transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
+ validate/1,warpPointer/3]).
-export_type([wxPopupTransientWindow/0]).
%% @hidden
@@ -146,6 +147,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
position(This,PtOrigin,Size) -> wxPopupWindow:position(This,PtOrigin,Size).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -466,6 +469,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxPopupWindow.erl b/lib/wx/src/gen/wxPopupWindow.erl
index 962fd9903b..ddf1033841 100644
--- a/lib/wx/src/gen/wxPopupWindow.erl
+++ b/lib/wx/src/gen/wxPopupWindow.erl
@@ -37,29 +37,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -143,6 +143,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
ok.
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -463,6 +465,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxPreviewCanvas.erl b/lib/wx/src/gen/wxPreviewCanvas.erl
index 67357dbd65..4e869dd6f2 100644
--- a/lib/wx/src/gen/wxPreviewCanvas.erl
+++ b/lib/wx/src/gen/wxPreviewCanvas.erl
@@ -40,37 +40,37 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- doPrepareDC/2,enable/1,enable/2,enableScrolling/3,findWindow/2,fit/1,
- fitInside/1,freeze/1,getAcceleratorTable/1,getBackgroundColour/1,
+ doPrepareDC/2,dragAcceptFiles/2,enable/1,enable/2,enableScrolling/3,
+ findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,getBackgroundColour/1,
getBackgroundStyle/1,getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,
- getChildren/1,getClientSize/1,getContainingSizer/1,getCursor/1,getDropTarget/1,
- getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPixelsPerUnit/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getToolTip/1,getUpdateRegion/1,getViewStart/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,prepareDC/2,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,screenToClient/1,screenToClient/2,scroll/3,
- scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
- setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,
- setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFocusIgnoringChildren/1,setFont/2,setForegroundColour/2,setHelpText/2,
- setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
- setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
- setScrollPos/4,setScrollRate/3,setScrollbar/5,setScrollbar/6,setScrollbars/5,
- setScrollbars/6,setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,
- setSizeHints/3,setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,
- setSizerAndFit/3,setTargetWindow/2,setThemeEnabled/2,setToolTip/2,
- setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
+ getChildren/1,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
+ getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPixelsPerUnit/1,
+ getScrollPos/2,getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,
+ getTextExtent/2,getTextExtent/3,getToolTip/1,getUpdateRegion/1,getViewStart/1,
+ getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
+ hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,prepareDC/2,raise/1,refresh/1,refresh/2,
+ refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,
+ screenToClient/1,screenToClient/2,scroll/3,scrollLines/2,scrollPages/2,
+ scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
+ setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
+ setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
+ setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFocusIgnoringChildren/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
+ setPalette/2,setScrollPos/3,setScrollPos/4,setScrollRate/3,setScrollbar/5,
+ setScrollbar/6,setScrollbars/5,setScrollbars/6,setSize/2,setSize/3,
+ setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,setSizer/2,
+ setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setTargetWindow/2,setThemeEnabled/2,
+ setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
setWindowVariant/2,shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
@@ -121,6 +121,8 @@ setFocusIgnoringChildren(This) -> wxPanel:setFocusIgnoringChildren(This).
initDialog(This) -> wxPanel:initDialog(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -439,6 +441,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxPreviewControlBar.erl b/lib/wx/src/gen/wxPreviewControlBar.erl
index 4dd224b26b..cf17f40527 100644
--- a/lib/wx/src/gen/wxPreviewControlBar.erl
+++ b/lib/wx/src/gen/wxPreviewControlBar.erl
@@ -39,38 +39,39 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFocusIgnoringChildren/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
- setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
- setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
- setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
- setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
- setThemeEnabled/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
- setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
- setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
- show/1,show/2,thaw/1,transferDataFromWindow/1,transferDataToWindow/1,
- update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFocusIgnoringChildren/1,setFont/2,setForegroundColour/2,setHelpText/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
+ setScrollPos/4,setScrollbar/5,setScrollbar/6,setSize/2,setSize/3,setSize/5,
+ setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,setSizer/2,
+ setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,setToolTip/2,
+ setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
+ setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
+ setWindowVariant/2,shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
+ transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
+ validate/1,warpPointer/3]).
-export_type([wxPreviewControlBar/0]).
%% @hidden
@@ -152,6 +153,8 @@ setFocusIgnoringChildren(This) -> wxPanel:setFocusIgnoringChildren(This).
initDialog(This) -> wxPanel:initDialog(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -470,6 +473,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxPreviewFrame.erl b/lib/wx/src/gen/wxPreviewFrame.erl
index 2f530b49ea..022b0cd46e 100644
--- a/lib/wx/src/gen/wxPreviewFrame.erl
+++ b/lib/wx/src/gen/wxPreviewFrame.erl
@@ -42,27 +42,28 @@
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createStatusBar/1,createStatusBar/2,createToolBar/1,createToolBar/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientAreaOrigin/1,
- getClientSize/1,getContainingSizer/1,getCursor/1,getDropTarget/1,
- getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
- getLabel/1,getMaxSize/1,getMenuBar/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getStatusBar/1,
- getStatusBarPane/1,getTextExtent/2,getTextExtent/3,getTitle/1,getToolBar/1,
- getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- processCommand/2,raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,
- releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientAreaOrigin/1,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
+ getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,getMenuBar/1,getMinSize/1,
+ getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getStatusBar/1,getStatusBarPane/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolBar/1,getToolTip/1,getUpdateRegion/1,
+ getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
+ hasScrollbar/2,hasTransparentBackground/1,hide/1,iconize/1,iconize/2,
+ inheritAttributes/1,initDialog/1,invalidateBestSize/1,isActive/1,
+ isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
+ isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,maximize/1,
+ maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
+ navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
+ popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,processCommand/2,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
scrollWindow/4,sendSizeEvent/1,setAcceleratorTable/2,setAutoLayout/2,
setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
@@ -244,6 +245,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -564,6 +567,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxPrintData.erl b/lib/wx/src/gen/wxPrintData.erl
index 6b6b678adf..b6815da90b 100644
--- a/lib/wx/src/gen/wxPrintData.erl
+++ b/lib/wx/src/gen/wxPrintData.erl
@@ -205,7 +205,7 @@ setPaperId(#wx_ref{type=ThisT,ref=ThisRef},SizeId)
-spec setPrinterName(This, Name) -> 'ok' when
This::wxPrintData(), Name::unicode:chardata().
setPrinterName(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxPrintData),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:cast(?wxPrintData_SetPrinterName,
diff --git a/lib/wx/src/gen/wxPrintDialog.erl b/lib/wx/src/gen/wxPrintDialog.erl
index d4188e79c4..4024036166 100644
--- a/lib/wx/src/gen/wxPrintDialog.erl
+++ b/lib/wx/src/gen/wxPrintDialog.erl
@@ -40,40 +40,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
@@ -214,6 +214,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -530,6 +532,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxPrinter.erl b/lib/wx/src/gen/wxPrinter.erl
index b9fb1c07bd..bdca37b4a8 100644
--- a/lib/wx/src/gen/wxPrinter.erl
+++ b/lib/wx/src/gen/wxPrinter.erl
@@ -122,7 +122,7 @@ printDialog(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef})
-spec reportError(This, Parent, Printout, Message) -> 'ok' when
This::wxPrinter(), Parent::wxWindow:wxWindow(), Printout::wxPrintout:wxPrintout(), Message::unicode:chardata().
reportError(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},#wx_ref{type=PrintoutT,ref=PrintoutRef},Message)
- when is_list(Message) ->
+ when ?is_chardata(Message) ->
?CLASS(ThisT,wxPrinter),
?CLASS(ParentT,wxWindow),
?CLASS(PrintoutT,wxPrintout),
diff --git a/lib/wx/src/gen/wxProgressDialog.erl b/lib/wx/src/gen/wxProgressDialog.erl
index 8e275f9dc8..a17cb0383a 100644
--- a/lib/wx/src/gen/wxProgressDialog.erl
+++ b/lib/wx/src/gen/wxProgressDialog.erl
@@ -40,40 +40,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,updateWindowUI/1,
@@ -93,7 +93,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
Title::unicode:chardata(), Message::unicode:chardata().
new(Title,Message)
- when is_list(Title),is_list(Message) ->
+ when ?is_chardata(Title),?is_chardata(Message) ->
new(Title,Message, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxprogressdialog.html#wxprogressdialogwxprogressdialog">external documentation</a>.
@@ -103,7 +103,7 @@ new(Title,Message)
| {'parent', wxWindow:wxWindow()}
| {'style', integer()}.
new(Title,Message, Options)
- when is_list(Title),is_list(Message),is_list(Options) ->
+ when ?is_chardata(Title),?is_chardata(Message),is_list(Options) ->
Title_UC = unicode:characters_to_binary([Title,0]),
Message_UC = unicode:characters_to_binary([Message,0]),
MOpts = fun({maximum, Maximum}, Acc) -> [<<1:32/?UI,Maximum:32/?UI>>|Acc];
@@ -229,6 +229,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -543,6 +545,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxRadioBox.erl b/lib/wx/src/gen/wxRadioBox.erl
index 348d75db2a..13d3a496ac 100644
--- a/lib/wx/src/gen/wxRadioBox.erl
+++ b/lib/wx/src/gen/wxRadioBox.erl
@@ -41,12 +41,13 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,getBackgroundColour/1,
- getBackgroundStyle/1,getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,
- getChildren/1,getClientSize/1,getContainingSizer/1,getCursor/1,getDropTarget/1,
- getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ dragAcceptFiles/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
+ getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
+ getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
+ getContentScaleFactor/1,getCursor/1,getDropTarget/1,getEventHandler/1,
+ getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
+ getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,getMinSize/1,
+ getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
@@ -86,7 +87,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()].
new(Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
new(Parent,Id,Title,Pos,Size,Choices, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxradiobox.html#wxradioboxwxradiobox">external documentation</a>.
@@ -96,7 +97,7 @@ new(Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
| {'style', integer()}
| {'val', wx:wx_object()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title,{PosX,PosY},{SizeW,SizeH},Choices, Options)
- when is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
Choices_UCA = [unicode:characters_to_binary([ChoicesTemp,0]) ||
@@ -114,7 +115,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title,{PosX,PosY},{SizeW,SizeH},Choic
This::wxRadioBox(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()].
create(This,Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
create(This,Parent,Id,Title,Pos,Size,Choices, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxradiobox.html#wxradioboxcreate">external documentation</a>.
@@ -124,7 +125,7 @@ create(This,Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
| {'style', integer()}
| {'val', wx:wx_object()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Title,{PosX,PosY},{SizeW,SizeH},Choices, Options)
- when is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) ->
?CLASS(ThisT,wxRadioBox),
?CLASS(ParentT,wxWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
@@ -319,7 +320,7 @@ isItemShown(#wx_ref{type=ThisT,ref=ThisRef},N)
-spec setItemHelpText(This, N, HelpText) -> 'ok' when
This::wxRadioBox(), N::integer(), HelpText::unicode:chardata().
setItemHelpText(#wx_ref{type=ThisT,ref=ThisRef},N,HelpText)
- when is_integer(N),is_list(HelpText) ->
+ when is_integer(N),?is_chardata(HelpText) ->
?CLASS(ThisT,wxRadioBox),
HelpText_UC = unicode:characters_to_binary([HelpText,0]),
wxe_util:cast(?wxRadioBox_SetItemHelpText,
@@ -329,7 +330,7 @@ setItemHelpText(#wx_ref{type=ThisT,ref=ThisRef},N,HelpText)
-spec setItemToolTip(This, Item, Text) -> 'ok' when
This::wxRadioBox(), Item::integer(), Text::unicode:chardata().
setItemToolTip(#wx_ref{type=ThisT,ref=ThisRef},Item,Text)
- when is_integer(Item),is_list(Text) ->
+ when is_integer(Item),?is_chardata(Text) ->
?CLASS(ThisT,wxRadioBox),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxRadioBox_SetItemToolTip,
@@ -348,6 +349,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -656,6 +659,8 @@ fit(This) -> wxWindow:fit(This).
%% @hidden
findWindow(This,Winid) -> wxWindow:findWindow(This,Winid).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxRadioButton.erl b/lib/wx/src/gen/wxRadioButton.erl
index 4487724be6..b5635e8afe 100644
--- a/lib/wx/src/gen/wxRadioButton.erl
+++ b/lib/wx/src/gen/wxRadioButton.erl
@@ -38,29 +38,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -90,7 +90,7 @@ new() ->
Parent::wxWindow:wxWindow(), Id::integer(), Label::unicode:chardata().
new(Parent,Id,Label)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Label) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Label) ->
new(Parent,Id,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxradiobutton.html#wxradiobuttonwxradiobutton">external documentation</a>.
@@ -101,7 +101,7 @@ new(Parent,Id,Label)
| {'style', integer()}
| {'validator', wx:wx_object()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
- when is_integer(Id),is_list(Label),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Label),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
MOpts = fun({pos, {PosX,PosY}}, Acc) -> [<<1:32/?UI,PosX:32/?UI,PosY:32/?UI,0:32>>|Acc];
@@ -118,7 +118,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
This::wxRadioButton(), Parent::wxWindow:wxWindow(), Id::integer(), Label::unicode:chardata().
create(This,Parent,Id,Label)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Label) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Label) ->
create(This,Parent,Id,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxradiobutton.html#wxradiobuttoncreate">external documentation</a>.
@@ -129,7 +129,7 @@ create(This,Parent,Id,Label)
| {'style', integer()}
| {'validator', wx:wx_object()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
- when is_integer(Id),is_list(Label),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxRadioButton),
?CLASS(ParentT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
@@ -172,6 +172,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -488,6 +490,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxSashLayoutWindow.erl b/lib/wx/src/gen/wxSashLayoutWindow.erl
index ae081ae800..a0e240b25d 100644
--- a/lib/wx/src/gen/wxSashLayoutWindow.erl
+++ b/lib/wx/src/gen/wxSashLayoutWindow.erl
@@ -39,30 +39,31 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMaximumSizeX/1,getMaximumSizeY/1,
- getMinSize/1,getMinimumSizeX/1,getMinimumSizeY/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getSashVisible/2,getScreenPosition/1,getScreenRect/1,
- getScrollPos/2,getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,
- getTextExtent/2,getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
- getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
- hasTransparentBackground/1,hide/1,inheritAttributes/1,initDialog/1,
- invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,
- lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,
- scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
- setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,
- setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMaximumSizeX/1,getMaximumSizeY/1,getMinSize/1,getMinimumSizeX/1,
+ getMinimumSizeY/1,getName/1,getParent/1,getPosition/1,getRect/1,getSashVisible/2,
+ getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
+ getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
+ getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
+ getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
+ hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
+ isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
+ isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
+ move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
+ navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
+ popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
+ refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
+ reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
+ scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
+ setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
+ setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
+ setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
+ setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMaximumSizeX/2,setMaximumSizeY/2,setMinSize/2,setMinimumSizeX/2,
setMinimumSizeY/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
setOwnForegroundColour/2,setPalette/2,setSashVisible/3,setScrollPos/3,
@@ -220,6 +221,8 @@ getMaximumSizeX(This) -> wxSashWindow:getMaximumSizeX(This).
getSashVisible(This,Edge) -> wxSashWindow:getSashVisible(This,Edge).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -540,6 +543,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxSashWindow.erl b/lib/wx/src/gen/wxSashWindow.erl
index 773e0f1ab0..5e66d79b11 100644
--- a/lib/wx/src/gen/wxSashWindow.erl
+++ b/lib/wx/src/gen/wxSashWindow.erl
@@ -39,29 +39,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -208,6 +208,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
ok.
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -528,6 +530,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxScrollBar.erl b/lib/wx/src/gen/wxScrollBar.erl
index 7f70d9d97b..a1138725cd 100644
--- a/lib/wx/src/gen/wxScrollBar.erl
+++ b/lib/wx/src/gen/wxScrollBar.erl
@@ -39,29 +39,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setSize/2,setSize/3,setSize/5,
setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,setSizer/2,
@@ -216,6 +216,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -528,6 +530,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxScrolledWindow.erl b/lib/wx/src/gen/wxScrolledWindow.erl
index 70bc5adbd9..e3f6122cfa 100644
--- a/lib/wx/src/gen/wxScrolledWindow.erl
+++ b/lib/wx/src/gen/wxScrolledWindow.erl
@@ -41,38 +41,39 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFocusIgnoringChildren/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
- setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
- setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
- setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
- setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
- setThemeEnabled/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
- setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
- setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
- show/1,show/2,thaw/1,transferDataFromWindow/1,transferDataToWindow/1,
- update/1,updateWindowUI/1,updateWindowUI/2,validate/1,warpPointer/3]).
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFocusIgnoringChildren/1,setFont/2,setForegroundColour/2,setHelpText/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setScrollPos/3,
+ setScrollPos/4,setScrollbar/5,setScrollbar/6,setSize/2,setSize/3,setSize/5,
+ setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,setSizer/2,
+ setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,setToolTip/2,
+ setTransparent/2,setVirtualSize/2,setVirtualSize/3,setVirtualSizeHints/2,
+ setVirtualSizeHints/3,setVirtualSizeHints/4,setWindowStyle/2,setWindowStyleFlag/2,
+ setWindowVariant/2,shouldInheritColours/1,show/1,show/2,thaw/1,transferDataFromWindow/1,
+ transferDataToWindow/1,update/1,updateWindowUI/1,updateWindowUI/2,
+ validate/1,warpPointer/3]).
-export_type([wxScrolledWindow/0]).
%% @hidden
@@ -259,6 +260,8 @@ setFocusIgnoringChildren(This) -> wxPanel:setFocusIgnoringChildren(This).
initDialog(This) -> wxPanel:initDialog(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -577,6 +580,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxSingleChoiceDialog.erl b/lib/wx/src/gen/wxSingleChoiceDialog.erl
index baff296d11..8b2991da4d 100644
--- a/lib/wx/src/gen/wxSingleChoiceDialog.erl
+++ b/lib/wx/src/gen/wxSingleChoiceDialog.erl
@@ -40,40 +40,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
@@ -99,7 +99,7 @@ new() ->
Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[unicode:chardata()].
new(Parent,Message,Caption,Choices)
- when is_record(Parent, wx_ref),is_list(Message),is_list(Caption),is_list(Choices) ->
+ when is_record(Parent, wx_ref),?is_chardata(Message),?is_chardata(Caption),is_list(Choices) ->
new(Parent,Message,Caption,Choices, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxsinglechoicedialog.html#wxsinglechoicedialogwxsinglechoicedialog">external documentation</a>.
@@ -108,7 +108,7 @@ new(Parent,Message,Caption,Choices)
Option :: {'style', integer()}
| {'pos', {X::integer(), Y::integer()}}.
new(#wx_ref{type=ParentT,ref=ParentRef},Message,Caption,Choices, Options)
- when is_list(Message),is_list(Caption),is_list(Choices),is_list(Options) ->
+ when ?is_chardata(Message),?is_chardata(Caption),is_list(Choices),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Message_UC = unicode:characters_to_binary([Message,0]),
Caption_UC = unicode:characters_to_binary([Caption,0]),
@@ -224,6 +224,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -540,6 +542,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxSlider.erl b/lib/wx/src/gen/wxSlider.erl
index a215b3c1d5..7bdff00589 100644
--- a/lib/wx/src/gen/wxSlider.erl
+++ b/lib/wx/src/gen/wxSlider.erl
@@ -40,29 +40,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -248,6 +248,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -564,6 +566,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxSpinButton.erl b/lib/wx/src/gen/wxSpinButton.erl
index 1c8d674d05..6c24176974 100644
--- a/lib/wx/src/gen/wxSpinButton.erl
+++ b/lib/wx/src/gen/wxSpinButton.erl
@@ -39,29 +39,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -196,6 +196,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -512,6 +514,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxSpinCtrl.erl b/lib/wx/src/gen/wxSpinCtrl.erl
index f8a46dd146..c229b4caf1 100644
--- a/lib/wx/src/gen/wxSpinCtrl.erl
+++ b/lib/wx/src/gen/wxSpinCtrl.erl
@@ -39,29 +39,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -172,7 +172,7 @@ setValue(#wx_ref{type=ThisT,ref=ThisRef},Value)
wxe_util:cast(?wxSpinCtrl_SetValue_1_0,
<<ThisRef:32/?UI,Value:32/?UI>>);
setValue(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxSpinCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxSpinCtrl_SetValue_1_1,
@@ -233,6 +233,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -549,6 +551,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxSplashScreen.erl b/lib/wx/src/gen/wxSplashScreen.erl
index 3b53e670c1..9729b3529b 100644
--- a/lib/wx/src/gen/wxSplashScreen.erl
+++ b/lib/wx/src/gen/wxSplashScreen.erl
@@ -41,27 +41,28 @@
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createStatusBar/1,createStatusBar/2,createToolBar/1,createToolBar/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientAreaOrigin/1,
- getClientSize/1,getContainingSizer/1,getCursor/1,getDropTarget/1,
- getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
- getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
- getLabel/1,getMaxSize/1,getMenuBar/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getStatusBar/1,
- getStatusBarPane/1,getTextExtent/2,getTextExtent/3,getTitle/1,getToolBar/1,
- getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- processCommand/2,raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,
- releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientAreaOrigin/1,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
+ getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,getMenuBar/1,getMinSize/1,
+ getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getStatusBar/1,getStatusBarPane/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolBar/1,getToolTip/1,getUpdateRegion/1,
+ getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,
+ hasScrollbar/2,hasTransparentBackground/1,hide/1,iconize/1,iconize/2,
+ inheritAttributes/1,initDialog/1,invalidateBestSize/1,isActive/1,
+ isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,isExposed/5,
+ isFullScreen/1,isIconized/1,isMaximized/1,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,maximize/1,
+ maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
+ navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
+ popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,processCommand/2,
+ raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
+ removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
scrollWindow/4,sendSizeEvent/1,setAcceleratorTable/2,setAutoLayout/2,
setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
@@ -230,6 +231,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -550,6 +553,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxSplitterWindow.erl b/lib/wx/src/gen/wxSplitterWindow.erl
index f311d5011f..fb2f355f43 100644
--- a/lib/wx/src/gen/wxSplitterWindow.erl
+++ b/lib/wx/src/gen/wxSplitterWindow.erl
@@ -42,29 +42,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -359,6 +359,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
ok.
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -679,6 +681,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxStaticBitmap.erl b/lib/wx/src/gen/wxStaticBitmap.erl
index 3ef1499a28..64d8cc4364 100644
--- a/lib/wx/src/gen/wxStaticBitmap.erl
+++ b/lib/wx/src/gen/wxStaticBitmap.erl
@@ -38,29 +38,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -168,6 +168,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -484,6 +486,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxStaticBox.erl b/lib/wx/src/gen/wxStaticBox.erl
index 46cca3b6f3..728c02e9e4 100644
--- a/lib/wx/src/gen/wxStaticBox.erl
+++ b/lib/wx/src/gen/wxStaticBox.erl
@@ -38,29 +38,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -90,7 +90,7 @@ new() ->
Parent::wxWindow:wxWindow(), Id::integer(), Label::unicode:chardata().
new(Parent,Id,Label)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Label) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Label) ->
new(Parent,Id,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxstaticbox.html#wxstaticboxwxstaticbox">external documentation</a>.
@@ -100,7 +100,7 @@ new(Parent,Id,Label)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
- when is_integer(Id),is_list(Label),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Label),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
MOpts = fun({pos, {PosX,PosY}}, Acc) -> [<<1:32/?UI,PosX:32/?UI,PosY:32/?UI,0:32>>|Acc];
@@ -116,7 +116,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
This::wxStaticBox(), Parent::wxWindow:wxWindow(), Id::integer(), Label::unicode:chardata().
create(This,Parent,Id,Label)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Label) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Label) ->
create(This,Parent,Id,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxstaticbox.html#wxstaticboxcreate">external documentation</a>.
@@ -126,7 +126,7 @@ create(This,Parent,Id,Label)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
- when is_integer(Id),is_list(Label),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxStaticBox),
?CLASS(ParentT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
@@ -151,6 +151,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -467,6 +469,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxStaticLine.erl b/lib/wx/src/gen/wxStaticLine.erl
index 94dd339cfc..3c648b7746 100644
--- a/lib/wx/src/gen/wxStaticLine.erl
+++ b/lib/wx/src/gen/wxStaticLine.erl
@@ -39,29 +39,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -168,6 +168,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -484,6 +486,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxStaticText.erl b/lib/wx/src/gen/wxStaticText.erl
index 37d0219bc6..34216f975f 100644
--- a/lib/wx/src/gen/wxStaticText.erl
+++ b/lib/wx/src/gen/wxStaticText.erl
@@ -38,30 +38,30 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
- getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setMaxSize/2,setMinSize/2,
- setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getMaxSize/1,getMinSize/1,
+ getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setMaxSize/2,
+ setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
@@ -90,7 +90,7 @@ new() ->
Parent::wxWindow:wxWindow(), Id::integer(), Label::unicode:chardata().
new(Parent,Id,Label)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Label) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Label) ->
new(Parent,Id,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxstatictext.html#wxstatictextwxstatictext">external documentation</a>.
@@ -100,7 +100,7 @@ new(Parent,Id,Label)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
- when is_integer(Id),is_list(Label),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Label),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
MOpts = fun({pos, {PosX,PosY}}, Acc) -> [<<1:32/?UI,PosX:32/?UI,PosY:32/?UI,0:32>>|Acc];
@@ -116,7 +116,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
This::wxStaticText(), Parent::wxWindow:wxWindow(), Id::integer(), Label::unicode:chardata().
create(This,Parent,Id,Label)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Label) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Label) ->
create(This,Parent,Id,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxstatictext.html#wxstatictextcreate">external documentation</a>.
@@ -126,7 +126,7 @@ create(This,Parent,Id,Label)
| {'size', {W::integer(), H::integer()}}
| {'style', integer()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
- when is_integer(Id),is_list(Label),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxStaticText),
?CLASS(ParentT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
@@ -150,7 +150,7 @@ getLabel(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setLabel(This, Label) -> 'ok' when
This::wxStaticText(), Label::unicode:chardata().
setLabel(#wx_ref{type=ThisT,ref=ThisRef},Label)
- when is_list(Label) ->
+ when ?is_chardata(Label) ->
?CLASS(ThisT,wxStaticText),
Label_UC = unicode:characters_to_binary([Label,0]),
wxe_util:cast(?wxStaticText_SetLabel,
@@ -174,6 +174,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
%% From wxControl
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -490,6 +492,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxStatusBar.erl b/lib/wx/src/gen/wxStatusBar.erl
index d0ce65273b..8d9f77c209 100644
--- a/lib/wx/src/gen/wxStatusBar.erl
+++ b/lib/wx/src/gen/wxStatusBar.erl
@@ -40,29 +40,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -198,7 +198,7 @@ popStatusText(#wx_ref{type=ThisT,ref=ThisRef}, Options)
This::wxStatusBar(), Text::unicode:chardata().
pushStatusText(This,Text)
- when is_record(This, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),?is_chardata(Text) ->
pushStatusText(This,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxstatusbar.html#wxstatusbarpushstatustext">external documentation</a>.
@@ -206,7 +206,7 @@ pushStatusText(This,Text)
This::wxStatusBar(), Text::unicode:chardata(),
Option :: {'number', integer()}.
pushStatusText(#wx_ref{type=ThisT,ref=ThisRef},Text, Options)
- when is_list(Text),is_list(Options) ->
+ when ?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxStatusBar),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({number, Number}, Acc) -> [<<1:32/?UI,Number:32/?UI>>|Acc];
@@ -251,7 +251,7 @@ setMinHeight(#wx_ref{type=ThisT,ref=ThisRef},Height)
This::wxStatusBar(), Text::unicode:chardata().
setStatusText(This,Text)
- when is_record(This, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),?is_chardata(Text) ->
setStatusText(This,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxstatusbar.html#wxstatusbarsetstatustext">external documentation</a>.
@@ -259,7 +259,7 @@ setStatusText(This,Text)
This::wxStatusBar(), Text::unicode:chardata(),
Option :: {'number', integer()}.
setStatusText(#wx_ref{type=ThisT,ref=ThisRef},Text, Options)
- when is_list(Text),is_list(Options) ->
+ when ?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxStatusBar),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({number, Number}, Acc) -> [<<1:32/?UI,Number:32/?UI>>|Acc];
@@ -296,6 +296,8 @@ destroy(Obj=#wx_ref{type=Type}) ->
ok.
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -616,6 +618,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxStyledTextCtrl.erl b/lib/wx/src/gen/wxStyledTextCtrl.erl
index 15ef728659..7e45245a72 100644
--- a/lib/wx/src/gen/wxStyledTextCtrl.erl
+++ b/lib/wx/src/gen/wxStyledTextCtrl.erl
@@ -140,28 +140,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lower/1,makeModal/1,makeModal/2,move/2,move/3,
- move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
- parent_class/1,popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,
- popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,
- releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,screenToClient/2,
- scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
- setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,
- setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lower/1,makeModal/1,makeModal/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
+ moveBeforeInTabOrder/2,navigate/1,navigate/2,parent_class/1,popEventHandler/1,
+ popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
+ refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
+ reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
+ scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
+ setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
+ setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
+ setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
+ setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -245,7 +246,7 @@ create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Opti
-spec addText(This, Text) -> 'ok' when
This::wxStyledTextCtrl(), Text::unicode:chardata().
addText(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxStyledTextCtrl_AddText,
@@ -264,7 +265,7 @@ addStyledText(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=DataT,ref=DataRef}) -
-spec insertText(This, Pos, Text) -> 'ok' when
This::wxStyledTextCtrl(), Pos::integer(), Text::unicode:chardata().
insertText(#wx_ref{type=ThisT,ref=ThisRef},Pos,Text)
- when is_integer(Pos),is_list(Text) ->
+ when is_integer(Pos),?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxStyledTextCtrl_InsertText,
@@ -822,7 +823,7 @@ styleSetSize(#wx_ref{type=ThisT,ref=ThisRef},Style,SizePoints)
-spec styleSetFaceName(This, Style, FontName) -> 'ok' when
This::wxStyledTextCtrl(), Style::integer(), FontName::unicode:chardata().
styleSetFaceName(#wx_ref{type=ThisT,ref=ThisRef},Style,FontName)
- when is_integer(Style),is_list(FontName) ->
+ when is_integer(Style),?is_chardata(FontName) ->
?CLASS(ThisT,wxStyledTextCtrl),
FontName_UC = unicode:characters_to_binary([FontName,0]),
wxe_util:cast(?wxStyledTextCtrl_StyleSetFaceName,
@@ -981,7 +982,7 @@ setCaretPeriod(#wx_ref{type=ThisT,ref=ThisRef},PeriodMilliseconds)
-spec setWordChars(This, Characters) -> 'ok' when
This::wxStyledTextCtrl(), Characters::unicode:chardata().
setWordChars(#wx_ref{type=ThisT,ref=ThisRef},Characters)
- when is_list(Characters) ->
+ when ?is_chardata(Characters) ->
?CLASS(ThisT,wxStyledTextCtrl),
Characters_UC = unicode:characters_to_binary([Characters,0]),
wxe_util:cast(?wxStyledTextCtrl_SetWordChars,
@@ -1129,7 +1130,7 @@ setCaretLineBackground(#wx_ref{type=ThisT,ref=ThisRef},Back)
-spec autoCompShow(This, LenEntered, ItemList) -> 'ok' when
This::wxStyledTextCtrl(), LenEntered::integer(), ItemList::unicode:chardata().
autoCompShow(#wx_ref{type=ThisT,ref=ThisRef},LenEntered,ItemList)
- when is_integer(LenEntered),is_list(ItemList) ->
+ when is_integer(LenEntered),?is_chardata(ItemList) ->
?CLASS(ThisT,wxStyledTextCtrl),
ItemList_UC = unicode:characters_to_binary([ItemList,0]),
wxe_util:cast(?wxStyledTextCtrl_AutoCompShow,
@@ -1171,7 +1172,7 @@ autoCompComplete(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec autoCompStops(This, CharacterSet) -> 'ok' when
This::wxStyledTextCtrl(), CharacterSet::unicode:chardata().
autoCompStops(#wx_ref{type=ThisT,ref=ThisRef},CharacterSet)
- when is_list(CharacterSet) ->
+ when ?is_chardata(CharacterSet) ->
?CLASS(ThisT,wxStyledTextCtrl),
CharacterSet_UC = unicode:characters_to_binary([CharacterSet,0]),
wxe_util:cast(?wxStyledTextCtrl_AutoCompStops,
@@ -1198,7 +1199,7 @@ autoCompGetSeparator(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec autoCompSelect(This, Text) -> 'ok' when
This::wxStyledTextCtrl(), Text::unicode:chardata().
autoCompSelect(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxStyledTextCtrl_AutoCompSelect,
@@ -1225,7 +1226,7 @@ autoCompGetCancelAtStart(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec autoCompSetFillUps(This, CharacterSet) -> 'ok' when
This::wxStyledTextCtrl(), CharacterSet::unicode:chardata().
autoCompSetFillUps(#wx_ref{type=ThisT,ref=ThisRef},CharacterSet)
- when is_list(CharacterSet) ->
+ when ?is_chardata(CharacterSet) ->
?CLASS(ThisT,wxStyledTextCtrl),
CharacterSet_UC = unicode:characters_to_binary([CharacterSet,0]),
wxe_util:cast(?wxStyledTextCtrl_AutoCompSetFillUps,
@@ -1269,7 +1270,7 @@ autoCompGetIgnoreCase(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec userListShow(This, ListType, ItemList) -> 'ok' when
This::wxStyledTextCtrl(), ListType::integer(), ItemList::unicode:chardata().
userListShow(#wx_ref{type=ThisT,ref=ThisRef},ListType,ItemList)
- when is_integer(ListType),is_list(ItemList) ->
+ when is_integer(ListType),?is_chardata(ItemList) ->
?CLASS(ThisT,wxStyledTextCtrl),
ItemList_UC = unicode:characters_to_binary([ItemList,0]),
wxe_util:cast(?wxStyledTextCtrl_UserListShow,
@@ -1614,7 +1615,7 @@ getPrintColourMode(#wx_ref{type=ThisT,ref=ThisRef}) ->
This::wxStyledTextCtrl(), MinPos::integer(), MaxPos::integer(), Text::unicode:chardata().
findText(This,MinPos,MaxPos,Text)
- when is_record(This, wx_ref),is_integer(MinPos),is_integer(MaxPos),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(MinPos),is_integer(MaxPos),?is_chardata(Text) ->
findText(This,MinPos,MaxPos,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxstyledtextctrl.html#wxstyledtextctrlfindtext">external documentation</a>.
@@ -1622,7 +1623,7 @@ findText(This,MinPos,MaxPos,Text)
This::wxStyledTextCtrl(), MinPos::integer(), MaxPos::integer(), Text::unicode:chardata(),
Option :: {'flags', integer()}.
findText(#wx_ref{type=ThisT,ref=ThisRef},MinPos,MaxPos,Text, Options)
- when is_integer(MinPos),is_integer(MaxPos),is_list(Text),is_list(Options) ->
+ when is_integer(MinPos),is_integer(MaxPos),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({flags, Flags}, Acc) -> [<<1:32/?UI,Flags:32/?UI>>|Acc];
@@ -1783,7 +1784,7 @@ ensureCaretVisible(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec replaceSelection(This, Text) -> 'ok' when
This::wxStyledTextCtrl(), Text::unicode:chardata().
replaceSelection(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxStyledTextCtrl_ReplaceSelection,
@@ -1866,7 +1867,7 @@ clear(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setText(This, Text) -> 'ok' when
This::wxStyledTextCtrl(), Text::unicode:chardata().
setText(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxStyledTextCtrl_SetText,
@@ -1951,7 +1952,7 @@ getTargetEnd(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec replaceTarget(This, Text) -> integer() when
This::wxStyledTextCtrl(), Text::unicode:chardata().
replaceTarget(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxStyledTextCtrl_ReplaceTarget,
@@ -1961,7 +1962,7 @@ replaceTarget(#wx_ref{type=ThisT,ref=ThisRef},Text)
-spec searchInTarget(This, Text) -> integer() when
This::wxStyledTextCtrl(), Text::unicode:chardata().
searchInTarget(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxStyledTextCtrl_SearchInTarget,
@@ -1988,7 +1989,7 @@ getSearchFlags(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec callTipShow(This, Pos, Definition) -> 'ok' when
This::wxStyledTextCtrl(), Pos::integer(), Definition::unicode:chardata().
callTipShow(#wx_ref{type=ThisT,ref=ThisRef},Pos,Definition)
- when is_integer(Pos),is_list(Definition) ->
+ when is_integer(Pos),?is_chardata(Definition) ->
?CLASS(ThisT,wxStyledTextCtrl),
Definition_UC = unicode:characters_to_binary([Definition,0]),
wxe_util:cast(?wxStyledTextCtrl_CallTipShow,
@@ -2382,7 +2383,7 @@ getScrollWidth(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec textWidth(This, Style, Text) -> integer() when
This::wxStyledTextCtrl(), Style::integer(), Text::unicode:chardata().
textWidth(#wx_ref{type=ThisT,ref=ThisRef},Style,Text)
- when is_integer(Style),is_list(Text) ->
+ when is_integer(Style),?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxStyledTextCtrl_TextWidth,
@@ -2426,7 +2427,7 @@ getUseVerticalScrollBar(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec appendText(This, Text) -> 'ok' when
This::wxStyledTextCtrl(), Text::unicode:chardata().
appendText(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxStyledTextCtrl_AppendText,
@@ -3073,7 +3074,7 @@ searchAnchor(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec searchNext(This, Flags, Text) -> integer() when
This::wxStyledTextCtrl(), Flags::integer(), Text::unicode:chardata().
searchNext(#wx_ref{type=ThisT,ref=ThisRef},Flags,Text)
- when is_integer(Flags),is_list(Text) ->
+ when is_integer(Flags),?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxStyledTextCtrl_SearchNext,
@@ -3083,7 +3084,7 @@ searchNext(#wx_ref{type=ThisT,ref=ThisRef},Flags,Text)
-spec searchPrev(This, Flags, Text) -> integer() when
This::wxStyledTextCtrl(), Flags::integer(), Text::unicode:chardata().
searchPrev(#wx_ref{type=ThisT,ref=ThisRef},Flags,Text)
- when is_integer(Flags),is_list(Text) ->
+ when is_integer(Flags),?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:call(?wxStyledTextCtrl_SearchPrev,
@@ -3414,7 +3415,7 @@ copyRange(#wx_ref{type=ThisT,ref=ThisRef},Start,End)
-spec copyText(This, Length, Text) -> 'ok' when
This::wxStyledTextCtrl(), Length::integer(), Text::unicode:chardata().
copyText(#wx_ref{type=ThisT,ref=ThisRef},Length,Text)
- when is_integer(Length),is_list(Text) ->
+ when is_integer(Length),?is_chardata(Text) ->
?CLASS(ThisT,wxStyledTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxStyledTextCtrl_CopyText,
@@ -3577,7 +3578,7 @@ wordRightEndExtend(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setWhitespaceChars(This, Characters) -> 'ok' when
This::wxStyledTextCtrl(), Characters::unicode:chardata().
setWhitespaceChars(#wx_ref{type=ThisT,ref=ThisRef},Characters)
- when is_list(Characters) ->
+ when ?is_chardata(Characters) ->
?CLASS(ThisT,wxStyledTextCtrl),
Characters_UC = unicode:characters_to_binary([Characters,0]),
wxe_util:cast(?wxStyledTextCtrl_SetWhitespaceChars,
@@ -3730,7 +3731,7 @@ colourise(#wx_ref{type=ThisT,ref=ThisRef},Start,End)
-spec setProperty(This, Key, Value) -> 'ok' when
This::wxStyledTextCtrl(), Key::unicode:chardata(), Value::unicode:chardata().
setProperty(#wx_ref{type=ThisT,ref=ThisRef},Key,Value)
- when is_list(Key),is_list(Value) ->
+ when ?is_chardata(Key),?is_chardata(Value) ->
?CLASS(ThisT,wxStyledTextCtrl),
Key_UC = unicode:characters_to_binary([Key,0]),
Value_UC = unicode:characters_to_binary([Value,0]),
@@ -3741,7 +3742,7 @@ setProperty(#wx_ref{type=ThisT,ref=ThisRef},Key,Value)
-spec setKeyWords(This, KeywordSet, KeyWords) -> 'ok' when
This::wxStyledTextCtrl(), KeywordSet::integer(), KeyWords::unicode:chardata().
setKeyWords(#wx_ref{type=ThisT,ref=ThisRef},KeywordSet,KeyWords)
- when is_integer(KeywordSet),is_list(KeyWords) ->
+ when is_integer(KeywordSet),?is_chardata(KeyWords) ->
?CLASS(ThisT,wxStyledTextCtrl),
KeyWords_UC = unicode:characters_to_binary([KeyWords,0]),
wxe_util:cast(?wxStyledTextCtrl_SetKeyWords,
@@ -3751,7 +3752,7 @@ setKeyWords(#wx_ref{type=ThisT,ref=ThisRef},KeywordSet,KeyWords)
-spec setLexerLanguage(This, Language) -> 'ok' when
This::wxStyledTextCtrl(), Language::unicode:chardata().
setLexerLanguage(#wx_ref{type=ThisT,ref=ThisRef},Language)
- when is_list(Language) ->
+ when ?is_chardata(Language) ->
?CLASS(ThisT,wxStyledTextCtrl),
Language_UC = unicode:characters_to_binary([Language,0]),
wxe_util:cast(?wxStyledTextCtrl_SetLexerLanguage,
@@ -3761,7 +3762,7 @@ setLexerLanguage(#wx_ref{type=ThisT,ref=ThisRef},Language)
-spec getProperty(This, Key) -> unicode:charlist() when
This::wxStyledTextCtrl(), Key::unicode:chardata().
getProperty(#wx_ref{type=ThisT,ref=ThisRef},Key)
- when is_list(Key) ->
+ when ?is_chardata(Key) ->
?CLASS(ThisT,wxStyledTextCtrl),
Key_UC = unicode:characters_to_binary([Key,0]),
wxe_util:call(?wxStyledTextCtrl_GetProperty,
@@ -3787,7 +3788,7 @@ getCurrentLine(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec styleSetSpec(This, StyleNum, Spec) -> 'ok' when
This::wxStyledTextCtrl(), StyleNum::integer(), Spec::unicode:chardata().
styleSetSpec(#wx_ref{type=ThisT,ref=ThisRef},StyleNum,Spec)
- when is_integer(StyleNum),is_list(Spec) ->
+ when is_integer(StyleNum),?is_chardata(Spec) ->
?CLASS(ThisT,wxStyledTextCtrl),
Spec_UC = unicode:characters_to_binary([Spec,0]),
wxe_util:cast(?wxStyledTextCtrl_StyleSetSpec,
@@ -3808,7 +3809,7 @@ styleSetFont(#wx_ref{type=ThisT,ref=ThisRef},StyleNum,#wx_ref{type=FontT,ref=Fon
This::wxStyledTextCtrl(), StyleNum::integer(), Size::integer(), FaceName::unicode:chardata(), Bold::boolean(), Italic::boolean(), Underline::boolean().
styleSetFontAttr(This,StyleNum,Size,FaceName,Bold,Italic,Underline)
- when is_record(This, wx_ref),is_integer(StyleNum),is_integer(Size),is_list(FaceName),is_boolean(Bold),is_boolean(Italic),is_boolean(Underline) ->
+ when is_record(This, wx_ref),is_integer(StyleNum),is_integer(Size),?is_chardata(FaceName),is_boolean(Bold),is_boolean(Italic),is_boolean(Underline) ->
styleSetFontAttr(This,StyleNum,Size,FaceName,Bold,Italic,Underline, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxstyledtextctrl.html#wxstyledtextctrlstylesetfontattr">external documentation</a>.
@@ -3817,7 +3818,7 @@ styleSetFontAttr(This,StyleNum,Size,FaceName,Bold,Italic,Underline)
This::wxStyledTextCtrl(), StyleNum::integer(), Size::integer(), FaceName::unicode:chardata(), Bold::boolean(), Italic::boolean(), Underline::boolean(),
Option :: {'encoding', wx:wx_enum()}.
styleSetFontAttr(#wx_ref{type=ThisT,ref=ThisRef},StyleNum,Size,FaceName,Bold,Italic,Underline, Options)
- when is_integer(StyleNum),is_integer(Size),is_list(FaceName),is_boolean(Bold),is_boolean(Italic),is_boolean(Underline),is_list(Options) ->
+ when is_integer(StyleNum),is_integer(Size),?is_chardata(FaceName),is_boolean(Bold),is_boolean(Italic),is_boolean(Underline),is_list(Options) ->
?CLASS(ThisT,wxStyledTextCtrl),
FaceName_UC = unicode:characters_to_binary([FaceName,0]),
MOpts = fun({encoding, Encoding}, Acc) -> [<<1:32/?UI,Encoding:32/?UI>>|Acc];
@@ -3937,7 +3938,7 @@ setLastKeydownProcessed(#wx_ref{type=ThisT,ref=ThisRef},Val)
-spec saveFile(This, Filename) -> boolean() when
This::wxStyledTextCtrl(), Filename::unicode:chardata().
saveFile(#wx_ref{type=ThisT,ref=ThisRef},Filename)
- when is_list(Filename) ->
+ when ?is_chardata(Filename) ->
?CLASS(ThisT,wxStyledTextCtrl),
Filename_UC = unicode:characters_to_binary([Filename,0]),
wxe_util:call(?wxStyledTextCtrl_SaveFile,
@@ -3947,7 +3948,7 @@ saveFile(#wx_ref{type=ThisT,ref=ThisRef},Filename)
-spec loadFile(This, Filename) -> boolean() when
This::wxStyledTextCtrl(), Filename::unicode:chardata().
loadFile(#wx_ref{type=ThisT,ref=ThisRef},Filename)
- when is_list(Filename) ->
+ when ?is_chardata(Filename) ->
?CLASS(ThisT,wxStyledTextCtrl),
Filename_UC = unicode:characters_to_binary([Filename,0]),
wxe_util:call(?wxStyledTextCtrl_LoadFile,
@@ -3968,7 +3969,7 @@ doDragOver(#wx_ref{type=ThisT,ref=ThisRef},X,Y,Def)
-spec doDropText(This, X, Y, Data) -> boolean() when
This::wxStyledTextCtrl(), X::integer(), Y::integer(), Data::unicode:chardata().
doDropText(#wx_ref{type=ThisT,ref=ThisRef},X,Y,Data)
- when is_integer(X),is_integer(Y),is_list(Data) ->
+ when is_integer(X),is_integer(Y),?is_chardata(Data) ->
?CLASS(ThisT,wxStyledTextCtrl),
Data_UC = unicode:characters_to_binary([Data,0]),
wxe_util:call(?wxStyledTextCtrl_DoDropText,
@@ -4078,6 +4079,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -4386,6 +4389,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxSystemOptions.erl b/lib/wx/src/gen/wxSystemOptions.erl
index 28d77b1e26..c613d66c73 100644
--- a/lib/wx/src/gen/wxSystemOptions.erl
+++ b/lib/wx/src/gen/wxSystemOptions.erl
@@ -39,7 +39,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
-spec getOption(Name) -> unicode:charlist() when
Name::unicode:chardata().
getOption(Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxSystemOptions_GetOption,
<<(byte_size(Name_UC)):32/?UI,(Name_UC)/binary, 0:(((8- ((4+byte_size(Name_UC)) band 16#7)) band 16#7))/unit:8>>).
@@ -48,7 +48,7 @@ getOption(Name)
-spec getOptionInt(Name) -> integer() when
Name::unicode:chardata().
getOptionInt(Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxSystemOptions_GetOptionInt,
<<(byte_size(Name_UC)):32/?UI,(Name_UC)/binary, 0:(((8- ((4+byte_size(Name_UC)) band 16#7)) band 16#7))/unit:8>>).
@@ -57,7 +57,7 @@ getOptionInt(Name)
-spec hasOption(Name) -> boolean() when
Name::unicode:chardata().
hasOption(Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxSystemOptions_HasOption,
<<(byte_size(Name_UC)):32/?UI,(Name_UC)/binary, 0:(((8- ((4+byte_size(Name_UC)) band 16#7)) band 16#7))/unit:8>>).
@@ -66,7 +66,7 @@ hasOption(Name)
-spec isFalse(Name) -> boolean() when
Name::unicode:chardata().
isFalse(Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxSystemOptions_IsFalse,
<<(byte_size(Name_UC)):32/?UI,(Name_UC)/binary, 0:(((8- ((4+byte_size(Name_UC)) band 16#7)) band 16#7))/unit:8>>).
@@ -81,12 +81,12 @@ isFalse(Name)
(Name, Value) -> 'ok' when
Name::unicode:chardata(), Value::unicode:chardata().
setOption(Name,Value)
- when is_list(Name),is_integer(Value) ->
+ when ?is_chardata(Name),is_integer(Value) ->
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:cast(?wxSystemOptions_SetOption_2_0,
<<(byte_size(Name_UC)):32/?UI,(Name_UC)/binary, 0:(((8- ((4+byte_size(Name_UC)) band 16#7)) band 16#7))/unit:8,Value:32/?UI>>);
setOption(Name,Value)
- when is_list(Name),is_list(Value) ->
+ when ?is_chardata(Name),?is_chardata(Value) ->
Name_UC = unicode:characters_to_binary([Name,0]),
Value_UC = unicode:characters_to_binary([Value,0]),
wxe_util:cast(?wxSystemOptions_SetOption_2_1,
diff --git a/lib/wx/src/gen/wxTextCtrl.erl b/lib/wx/src/gen/wxTextCtrl.erl
index 5f03398959..3acc61ce02 100644
--- a/lib/wx/src/gen/wxTextCtrl.erl
+++ b/lib/wx/src/gen/wxTextCtrl.erl
@@ -46,29 +46,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -126,7 +126,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
-spec appendText(This, Text) -> 'ok' when
This::wxTextCtrl(), Text::unicode:chardata().
appendText(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxTextCtrl_AppendText,
@@ -238,7 +238,7 @@ discardEdits(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec changeValue(This, Value) -> 'ok' when
This::wxTextCtrl(), Value::unicode:chardata().
changeValue(#wx_ref{type=ThisT,ref=ThisRef},Value)
- when is_list(Value) ->
+ when ?is_chardata(Value) ->
?CLASS(ThisT,wxTextCtrl),
Value_UC = unicode:characters_to_binary([Value,0]),
wxe_util:cast(?wxTextCtrl_ChangeValue,
@@ -383,7 +383,7 @@ isSingleLine(#wx_ref{type=ThisT,ref=ThisRef}) ->
This::wxTextCtrl(), File::unicode:chardata().
loadFile(This,File)
- when is_record(This, wx_ref),is_list(File) ->
+ when is_record(This, wx_ref),?is_chardata(File) ->
loadFile(This,File, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtextctrl.html#wxtextctrlloadfile">external documentation</a>.
@@ -391,7 +391,7 @@ loadFile(This,File)
This::wxTextCtrl(), File::unicode:chardata(),
Option :: {'fileType', integer()}.
loadFile(#wx_ref{type=ThisT,ref=ThisRef},File, Options)
- when is_list(File),is_list(Options) ->
+ when ?is_chardata(File),is_list(Options) ->
?CLASS(ThisT,wxTextCtrl),
File_UC = unicode:characters_to_binary([File,0]),
MOpts = fun({fileType, FileType}, Acc) -> [<<1:32/?UI,FileType:32/?UI>>|Acc];
@@ -447,7 +447,7 @@ remove(#wx_ref{type=ThisT,ref=ThisRef},From,To)
-spec replace(This, From, To, Value) -> 'ok' when
This::wxTextCtrl(), From::integer(), To::integer(), Value::unicode:chardata().
replace(#wx_ref{type=ThisT,ref=ThisRef},From,To,Value)
- when is_integer(From),is_integer(To),is_list(Value) ->
+ when is_integer(From),is_integer(To),?is_chardata(Value) ->
?CLASS(ThisT,wxTextCtrl),
Value_UC = unicode:characters_to_binary([Value,0]),
wxe_util:cast(?wxTextCtrl_Replace,
@@ -543,7 +543,7 @@ setStyle(#wx_ref{type=ThisT,ref=ThisRef},Start,End,#wx_ref{type=StyleT,ref=Style
-spec setValue(This, Value) -> 'ok' when
This::wxTextCtrl(), Value::unicode:chardata().
setValue(#wx_ref{type=ThisT,ref=ThisRef},Value)
- when is_list(Value) ->
+ when ?is_chardata(Value) ->
?CLASS(ThisT,wxTextCtrl),
Value_UC = unicode:characters_to_binary([Value,0]),
wxe_util:cast(?wxTextCtrl_SetValue,
@@ -570,7 +570,7 @@ undo(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec writeText(This, Text) -> 'ok' when
This::wxTextCtrl(), Text::unicode:chardata().
writeText(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxTextCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxTextCtrl_WriteText,
@@ -598,6 +598,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -914,6 +916,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxTextDataObject.erl b/lib/wx/src/gen/wxTextDataObject.erl
index eb3e1f4bff..5c8a44a879 100644
--- a/lib/wx/src/gen/wxTextDataObject.erl
+++ b/lib/wx/src/gen/wxTextDataObject.erl
@@ -76,7 +76,7 @@ getText(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setText(This, Text) -> 'ok' when
This::wxTextDataObject(), Text::unicode:chardata().
setText(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxTextDataObject),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxTextDataObject_SetText,
diff --git a/lib/wx/src/gen/wxTextEntryDialog.erl b/lib/wx/src/gen/wxTextEntryDialog.erl
index eaf6dc3926..c43a2d12b8 100644
--- a/lib/wx/src/gen/wxTextEntryDialog.erl
+++ b/lib/wx/src/gen/wxTextEntryDialog.erl
@@ -40,40 +40,40 @@
centreOnScreen/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
createButtonSizer/2,createStdDialogButtonSizer/2,destroyChildren/1,
- disable/1,disconnect/1,disconnect/2,disconnect/3,enable/1,enable/2,
- endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,getLabel/1,getMaxSize/1,
- getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getReturnCode/1,
- getScreenPosition/1,getScreenRect/1,getScrollPos/2,getScrollRange/2,
- getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,
- getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,iconize/1,iconize/2,inheritAttributes/1,initDialog/1,invalidateBestSize/1,
- isActive/1,isDoubleBuffered/1,isEnabled/1,isExposed/2,isExposed/3,
- isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,isModal/1,isRetained/1,
- isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,
- makeModal/2,maximize/1,maximize/2,move/2,move/3,move/4,moveAfterInTabOrder/2,
- moveBeforeInTabOrder/2,navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,
- popEventHandler/1,popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,
- raise/1,refresh/1,refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,
- removeChild/2,reparent/2,requestUserAttention/1,requestUserAttention/2,
- screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,
- scrollWindow/4,setAcceleratorTable/2,setAffirmativeId/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,setId/2,setLabel/2,
- setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,
- setOwnForegroundColour/2,setPalette/2,setReturnCode/2,setScrollPos/3,
- setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,setSize/2,
- setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,setSizeHints/4,
- setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,setThemeEnabled/2,
- setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,setVirtualSize/3,
- setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
+ disable/1,disconnect/1,disconnect/2,disconnect/3,dragAcceptFiles/2,
+ enable/1,enable/2,endModal/2,findWindow/2,fit/1,fitInside/1,freeze/1,
+ getAcceleratorTable/1,getAffirmativeId/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getIcon/1,getIcons/1,getId/1,
+ getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,getPosition/1,
+ getRect/1,getReturnCode/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getTitle/1,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,
+ getWindowStyleFlag/1,getWindowVariant/1,hasCapture/1,hasScrollbar/2,
+ hasTransparentBackground/1,hide/1,iconize/1,iconize/2,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isActive/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isFullScreen/1,isIconized/1,isMaximized/1,
+ isModal/1,isRetained/1,isShown/1,isTopLevel/1,layout/1,lineDown/1,lineUp/1,
+ lower/1,makeModal/1,makeModal/2,maximize/1,maximize/2,move/2,move/3,
+ move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,navigate/2,
+ pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,requestUserAttention/1,
+ requestUserAttention/2,screenToClient/1,screenToClient/2,scrollLines/2,
+ scrollPages/2,scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,
+ setAffirmativeId/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setIcon/2,setIcons/2,
+ setId/2,setLabel/2,setMaxSize/2,setMinSize/2,setName/2,setOwnBackgroundColour/2,
+ setOwnFont/2,setOwnForegroundColour/2,setPalette/2,setReturnCode/2,
+ setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,setShape/2,
+ setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
+ setSizeHints/4,setSizer/2,setSizer/3,setSizerAndFit/2,setSizerAndFit/3,
+ setThemeEnabled/2,setTitle/2,setToolTip/2,setTransparent/2,setVirtualSize/2,
+ setVirtualSize/3,setVirtualSizeHints/2,setVirtualSizeHints/3,setVirtualSizeHints/4,
setWindowStyle/2,setWindowStyleFlag/2,setWindowVariant/2,shouldInheritColours/1,
show/1,show/2,showFullScreen/2,showFullScreen/3,showModal/1,thaw/1,
transferDataFromWindow/1,transferDataToWindow/1,update/1,updateWindowUI/1,
@@ -93,7 +93,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
Parent::wxWindow:wxWindow(), Message::unicode:chardata().
new(Parent,Message)
- when is_record(Parent, wx_ref),is_list(Message) ->
+ when is_record(Parent, wx_ref),?is_chardata(Message) ->
new(Parent,Message, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtextentrydialog.html#wxtextentrydialogwxtextentrydialog">external documentation</a>.
@@ -104,7 +104,7 @@ new(Parent,Message)
| {'style', integer()}
| {'pos', {X::integer(), Y::integer()}}.
new(#wx_ref{type=ParentT,ref=ParentRef},Message, Options)
- when is_list(Message),is_list(Options) ->
+ when ?is_chardata(Message),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Message_UC = unicode:characters_to_binary([Message,0]),
MOpts = fun({caption, Caption}, Acc) -> Caption_UC = unicode:characters_to_binary([Caption,0]),[<<1:32/?UI,(byte_size(Caption_UC)):32/?UI,(Caption_UC)/binary, 0:(((8- ((0+byte_size(Caption_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -128,7 +128,7 @@ getValue(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setValue(This, Val) -> 'ok' when
This::wxTextEntryDialog(), Val::unicode:chardata().
setValue(#wx_ref{type=ThisT,ref=ThisRef},Val)
- when is_list(Val) ->
+ when ?is_chardata(Val) ->
?CLASS(ThisT,wxTextEntryDialog),
Val_UC = unicode:characters_to_binary([Val,0]),
wxe_util:cast(?wxTextEntryDialog_SetValue,
@@ -212,6 +212,8 @@ getIcons(This) -> wxTopLevelWindow:getIcons(This).
getIcon(This) -> wxTopLevelWindow:getIcon(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -528,6 +530,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxToggleButton.erl b/lib/wx/src/gen/wxToggleButton.erl
index 9e8dab1c32..b82ff4fe68 100644
--- a/lib/wx/src/gen/wxToggleButton.erl
+++ b/lib/wx/src/gen/wxToggleButton.erl
@@ -38,29 +38,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -90,7 +90,7 @@ new() ->
Parent::wxWindow:wxWindow(), Id::integer(), Label::unicode:chardata().
new(Parent,Id,Label)
- when is_record(Parent, wx_ref),is_integer(Id),is_list(Label) ->
+ when is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Label) ->
new(Parent,Id,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtogglebutton.html#wxtogglebuttonwxtogglebutton">external documentation</a>.
@@ -101,7 +101,7 @@ new(Parent,Id,Label)
| {'style', integer()}
| {'validator', wx:wx_object()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
- when is_integer(Id),is_list(Label),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Label),is_list(Options) ->
?CLASS(ParentT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
MOpts = fun({pos, {PosX,PosY}}, Acc) -> [<<1:32/?UI,PosX:32/?UI,PosY:32/?UI,0:32>>|Acc];
@@ -118,7 +118,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
This::wxToggleButton(), Parent::wxWindow:wxWindow(), Id::integer(), Label::unicode:chardata().
create(This,Parent,Id,Label)
- when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Label) ->
+ when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),?is_chardata(Label) ->
create(This,Parent,Id,Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtogglebutton.html#wxtogglebuttoncreate">external documentation</a>.
@@ -129,7 +129,7 @@ create(This,Parent,Id,Label)
| {'style', integer()}
| {'validator', wx:wx_object()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options)
- when is_integer(Id),is_list(Label),is_list(Options) ->
+ when is_integer(Id),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxToggleButton),
?CLASS(ParentT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
@@ -172,6 +172,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -488,6 +490,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxToolBar.erl b/lib/wx/src/gen/wxToolBar.erl
index 9be37f943c..e2835bf7c4 100644
--- a/lib/wx/src/gen/wxToolBar.erl
+++ b/lib/wx/src/gen/wxToolBar.erl
@@ -47,29 +47,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -142,7 +142,7 @@ addTool(This,Toolid,Bitmap)
| {'longHelpString', unicode:chardata()}.
addTool(This,Toolid,Label,Bitmap)
- when is_record(This, wx_ref),is_integer(Toolid),is_list(Label),is_record(Bitmap, wx_ref) ->
+ when is_record(This, wx_ref),is_integer(Toolid),?is_chardata(Label),is_record(Bitmap, wx_ref) ->
addTool(This,Toolid,Label,Bitmap, []);
addTool(This,Toolid,Bitmap,BmpDisabled)
@@ -187,10 +187,10 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,#wx_ref{type=BitmapT,ref=BitmapRe
| {'longHelpString', unicode:chardata()}.
addTool(This,Toolid,Label,Bitmap,BmpDisabled)
- when is_record(This, wx_ref),is_integer(Toolid),is_list(Label),is_record(Bitmap, wx_ref),is_record(BmpDisabled, wx_ref) ->
+ when is_record(This, wx_ref),is_integer(Toolid),?is_chardata(Label),is_record(Bitmap, wx_ref),is_record(BmpDisabled, wx_ref) ->
addTool(This,Toolid,Label,Bitmap,BmpDisabled, []);
addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef}, Options)
- when is_integer(Toolid),is_list(Label),is_list(Options) ->
+ when is_integer(Toolid),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxToolBar),
Label_UC = unicode:characters_to_binary([Label,0]),
?CLASS(BitmapT,wxBitmap),
@@ -237,7 +237,7 @@ addTool(This,Toolid,Bitmap,BmpDisabled,Toggle,XPos)
when is_record(This, wx_ref),is_integer(Toolid),is_record(Bitmap, wx_ref),is_record(BmpDisabled, wx_ref),is_boolean(Toggle),is_integer(XPos) ->
addTool(This,Toolid,Bitmap,BmpDisabled,Toggle,XPos, []);
addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef},#wx_ref{type=BmpDisabledT,ref=BmpDisabledRef}, Options)
- when is_integer(Toolid),is_list(Label),is_list(Options) ->
+ when is_integer(Toolid),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxToolBar),
Label_UC = unicode:characters_to_binary([Label,0]),
?CLASS(BitmapT,wxBitmap),
@@ -277,7 +277,7 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,#wx_ref{type=BitmapT,ref=BitmapRe
This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap().
addCheckTool(This,Toolid,Label,Bitmap)
- when is_record(This, wx_ref),is_integer(Toolid),is_list(Label),is_record(Bitmap, wx_ref) ->
+ when is_record(This, wx_ref),is_integer(Toolid),?is_chardata(Label),is_record(Bitmap, wx_ref) ->
addCheckTool(This,Toolid,Label,Bitmap, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtoolbar.html#wxtoolbaraddchecktool">external documentation</a>.
@@ -288,7 +288,7 @@ addCheckTool(This,Toolid,Label,Bitmap)
| {'longHelp', unicode:chardata()}
| {'data', wx:wx_object()}.
addCheckTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef}, Options)
- when is_integer(Toolid),is_list(Label),is_list(Options) ->
+ when is_integer(Toolid),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxToolBar),
Label_UC = unicode:characters_to_binary([Label,0]),
?CLASS(BitmapT,wxBitmap),
@@ -306,7 +306,7 @@ addCheckTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,r
This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap().
addRadioTool(This,Toolid,Label,Bitmap)
- when is_record(This, wx_ref),is_integer(Toolid),is_list(Label),is_record(Bitmap, wx_ref) ->
+ when is_record(This, wx_ref),is_integer(Toolid),?is_chardata(Label),is_record(Bitmap, wx_ref) ->
addRadioTool(This,Toolid,Label,Bitmap, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtoolbar.html#wxtoolbaraddradiotool">external documentation</a>.
@@ -317,7 +317,7 @@ addRadioTool(This,Toolid,Label,Bitmap)
| {'longHelp', unicode:chardata()}
| {'data', wx:wx_object()}.
addRadioTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef}, Options)
- when is_integer(Toolid),is_list(Label),is_list(Options) ->
+ when is_integer(Toolid),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxToolBar),
Label_UC = unicode:characters_to_binary([Label,0]),
?CLASS(BitmapT,wxBitmap),
@@ -545,7 +545,7 @@ insertTool(This,Pos,Toolid,Bitmap)
| {'longHelp', unicode:chardata()}.
insertTool(This,Pos,Toolid,Label,Bitmap)
- when is_record(This, wx_ref),is_integer(Pos),is_integer(Toolid),is_list(Label),is_record(Bitmap, wx_ref) ->
+ when is_record(This, wx_ref),is_integer(Pos),is_integer(Toolid),?is_chardata(Label),is_record(Bitmap, wx_ref) ->
insertTool(This,Pos,Toolid,Label,Bitmap, []);
insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,Toolid,#wx_ref{type=BitmapT,ref=BitmapRef}, Options)
when is_integer(Pos),is_integer(Toolid),is_list(Options) ->
@@ -571,7 +571,7 @@ insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,Toolid,#wx_ref{type=BitmapT,ref=B
| {'longHelp', unicode:chardata()}
| {'clientData', wx:wx_object()}.
insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef}, Options)
- when is_integer(Pos),is_integer(Toolid),is_list(Label),is_list(Options) ->
+ when is_integer(Pos),is_integer(Toolid),?is_chardata(Label),is_list(Options) ->
?CLASS(ThisT,wxToolBar),
Label_UC = unicode:characters_to_binary([Label,0]),
?CLASS(BitmapT,wxBitmap),
@@ -624,7 +624,7 @@ setToolBitmapSize(#wx_ref{type=ThisT,ref=ThisRef},{SizeW,SizeH})
-spec setToolLongHelp(This, Toolid, HelpString) -> 'ok' when
This::wxToolBar(), Toolid::integer(), HelpString::unicode:chardata().
setToolLongHelp(#wx_ref{type=ThisT,ref=ThisRef},Toolid,HelpString)
- when is_integer(Toolid),is_list(HelpString) ->
+ when is_integer(Toolid),?is_chardata(HelpString) ->
?CLASS(ThisT,wxToolBar),
HelpString_UC = unicode:characters_to_binary([HelpString,0]),
wxe_util:cast(?wxToolBar_SetToolLongHelp,
@@ -643,7 +643,7 @@ setToolPacking(#wx_ref{type=ThisT,ref=ThisRef},Packing)
-spec setToolShortHelp(This, Id, HelpString) -> 'ok' when
This::wxToolBar(), Id::integer(), HelpString::unicode:chardata().
setToolShortHelp(#wx_ref{type=ThisT,ref=ThisRef},Id,HelpString)
- when is_integer(Id),is_list(HelpString) ->
+ when is_integer(Id),?is_chardata(HelpString) ->
?CLASS(ThisT,wxToolBar),
HelpString_UC = unicode:characters_to_binary([HelpString,0]),
wxe_util:cast(?wxToolBar_SetToolShortHelp,
@@ -674,6 +674,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -990,6 +992,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxToolTip.erl b/lib/wx/src/gen/wxToolTip.erl
index 163e764d8c..0596173ebe 100644
--- a/lib/wx/src/gen/wxToolTip.erl
+++ b/lib/wx/src/gen/wxToolTip.erl
@@ -55,7 +55,7 @@ setDelay(Msecs)
-spec new(Tip) -> wxToolTip() when
Tip::unicode:chardata().
new(Tip)
- when is_list(Tip) ->
+ when ?is_chardata(Tip) ->
Tip_UC = unicode:characters_to_binary([Tip,0]),
wxe_util:construct(?wxToolTip_new,
<<(byte_size(Tip_UC)):32/?UI,(Tip_UC)/binary, 0:(((8- ((4+byte_size(Tip_UC)) band 16#7)) band 16#7))/unit:8>>).
@@ -64,7 +64,7 @@ new(Tip)
-spec setTip(This, Tip) -> 'ok' when
This::wxToolTip(), Tip::unicode:chardata().
setTip(#wx_ref{type=ThisT,ref=ThisRef},Tip)
- when is_list(Tip) ->
+ when ?is_chardata(Tip) ->
?CLASS(ThisT,wxToolTip),
Tip_UC = unicode:characters_to_binary([Tip,0]),
wxe_util:cast(?wxToolTip_SetTip,
diff --git a/lib/wx/src/gen/wxToolbook.erl b/lib/wx/src/gen/wxToolbook.erl
index 92dcbc726b..c22719680c 100644
--- a/lib/wx/src/gen/wxToolbook.erl
+++ b/lib/wx/src/gen/wxToolbook.erl
@@ -43,29 +43,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -120,7 +120,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
This::wxToolbook(), Page::wxWindow:wxWindow(), Text::unicode:chardata().
addPage(This,Page,Text)
- when is_record(This, wx_ref),is_record(Page, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),is_record(Page, wx_ref),?is_chardata(Text) ->
addPage(This,Page,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtoolbook.html#wxtoolbookaddpage">external documentation</a>.
@@ -129,7 +129,7 @@ addPage(This,Page,Text)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
addPage(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PageT,ref=PageRef},Text, Options)
- when is_list(Text),is_list(Options) ->
+ when ?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxToolbook),
?CLASS(PageT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
@@ -296,7 +296,7 @@ hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PtX,PtY})
This::wxToolbook(), N::integer(), Page::wxWindow:wxWindow(), Text::unicode:chardata().
insertPage(This,N,Page,Text)
- when is_record(This, wx_ref),is_integer(N),is_record(Page, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(N),is_record(Page, wx_ref),?is_chardata(Text) ->
insertPage(This,N,Page,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtoolbook.html#wxtoolbookinsertpage">external documentation</a>.
@@ -305,7 +305,7 @@ insertPage(This,N,Page,Text)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
insertPage(#wx_ref{type=ThisT,ref=ThisRef},N,#wx_ref{type=PageT,ref=PageRef},Text, Options)
- when is_integer(N),is_list(Text),is_list(Options) ->
+ when is_integer(N),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxToolbook),
?CLASS(PageT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
@@ -347,7 +347,7 @@ setPageImage(#wx_ref{type=ThisT,ref=ThisRef},N,ImageId)
-spec setPageText(This, N, StrText) -> boolean() when
This::wxToolbook(), N::integer(), StrText::unicode:chardata().
setPageText(#wx_ref{type=ThisT,ref=ThisRef},N,StrText)
- when is_integer(N),is_list(StrText) ->
+ when is_integer(N),?is_chardata(StrText) ->
?CLASS(ThisT,wxToolbook),
StrText_UC = unicode:characters_to_binary([StrText,0]),
wxe_util:call(?wxToolbook_SetPageText,
@@ -384,6 +384,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -700,6 +702,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxTopLevelWindow.erl b/lib/wx/src/gen/wxTopLevelWindow.erl
index 6ba81691c7..64713099f6 100644
--- a/lib/wx/src/gen/wxTopLevelWindow.erl
+++ b/lib/wx/src/gen/wxTopLevelWindow.erl
@@ -41,29 +41,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -273,7 +273,7 @@ setShape(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=RegionT,ref=RegionRef}) ->
-spec setTitle(This, Title) -> 'ok' when
This::wxTopLevelWindow(), Title::unicode:chardata().
setTitle(#wx_ref{type=ThisT,ref=ThisRef},Title)
- when is_list(Title) ->
+ when ?is_chardata(Title) ->
?CLASS(ThisT,wxTopLevelWindow),
Title_UC = unicode:characters_to_binary([Title,0]),
wxe_util:cast(?wxTopLevelWindow_SetTitle,
@@ -302,6 +302,8 @@ showFullScreen(#wx_ref{type=ThisT,ref=ThisRef},Show, Options)
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -622,6 +624,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxTreeCtrl.erl b/lib/wx/src/gen/wxTreeCtrl.erl
index 97b43309ad..373cb4c77f 100644
--- a/lib/wx/src/gen/wxTreeCtrl.erl
+++ b/lib/wx/src/gen/wxTreeCtrl.erl
@@ -59,29 +59,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -140,7 +140,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
This::wxTreeCtrl(), Text::unicode:chardata().
addRoot(This,Text)
- when is_record(This, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),?is_chardata(Text) ->
addRoot(This,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtreectrl.html#wxtreectrladdroot">external documentation</a>.
@@ -150,7 +150,7 @@ addRoot(This,Text)
| {'selectedImage', integer()}
| {'data', term()}.
addRoot(#wx_ref{type=ThisT,ref=ThisRef},Text, Options)
- when is_list(Text),is_list(Options) ->
+ when ?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxTreeCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({image, Image}, Acc) -> [<<1:32/?UI,Image:32/?UI>>|Acc];
@@ -166,7 +166,7 @@ addRoot(#wx_ref{type=ThisT,ref=ThisRef},Text, Options)
This::wxTreeCtrl(), Parent::integer(), Text::unicode:chardata().
appendItem(This,Parent,Text)
- when is_record(This, wx_ref),is_integer(Parent),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Parent),?is_chardata(Text) ->
appendItem(This,Parent,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtreectrl.html#wxtreectrlappenditem">external documentation</a>.
@@ -176,7 +176,7 @@ appendItem(This,Parent,Text)
| {'selectedImage', integer()}
| {'data', term()}.
appendItem(#wx_ref{type=ThisT,ref=ThisRef},Parent,Text, Options)
- when is_integer(Parent),is_list(Text),is_list(Options) ->
+ when is_integer(Parent),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxTreeCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({image, Image}, Acc) -> [<<1:32/?UI,Image:32/?UI>>|Acc];
@@ -580,7 +580,7 @@ hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PointX,PointY})
This::wxTreeCtrl(), Parent::integer(), Pos::integer(), Text::unicode:chardata().
insertItem(This,Parent,Pos,Text)
- when is_record(This, wx_ref),is_integer(Parent),is_integer(Pos),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Parent),is_integer(Pos),?is_chardata(Text) ->
insertItem(This,Parent,Pos,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtreectrl.html#wxtreectrlinsertitem">external documentation</a>.
@@ -590,7 +590,7 @@ insertItem(This,Parent,Pos,Text)
| {'selImage', integer()}
| {'data', term()}.
insertItem(#wx_ref{type=ThisT,ref=ThisRef},Parent,Pos,Text, Options)
- when is_integer(Parent),is_integer(Pos),is_list(Text),is_list(Options) ->
+ when is_integer(Parent),is_integer(Pos),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxTreeCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({image, Image}, Acc) -> [<<1:32/?UI,Image:32/?UI>>|Acc];
@@ -659,7 +659,7 @@ isTreeItemIdOk(Id)
This::wxTreeCtrl(), Parent::integer(), Text::unicode:chardata().
prependItem(This,Parent,Text)
- when is_record(This, wx_ref),is_integer(Parent),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Parent),?is_chardata(Text) ->
prependItem(This,Parent,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtreectrl.html#wxtreectrlprependitem">external documentation</a>.
@@ -669,7 +669,7 @@ prependItem(This,Parent,Text)
| {'selectedImage', integer()}
| {'data', term()}.
prependItem(#wx_ref{type=ThisT,ref=ThisRef},Parent,Text, Options)
- when is_integer(Parent),is_list(Text),is_list(Options) ->
+ when is_integer(Parent),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxTreeCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
MOpts = fun({image, Image}, Acc) -> [<<1:32/?UI,Image:32/?UI>>|Acc];
@@ -848,7 +848,7 @@ setItemImage(#wx_ref{type=ThisT,ref=ThisRef},Item,Image, Options)
-spec setItemText(This, Item, Text) -> 'ok' when
This::wxTreeCtrl(), Item::integer(), Text::unicode:chardata().
setItemText(#wx_ref{type=ThisT,ref=ThisRef},Item,Text)
- when is_integer(Item),is_list(Text) ->
+ when is_integer(Item),?is_chardata(Text) ->
?CLASS(ThisT,wxTreeCtrl),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxTreeCtrl_SetItemText,
@@ -946,6 +946,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -1260,6 +1262,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxTreeEvent.erl b/lib/wx/src/gen/wxTreeEvent.erl
index 41e86fe41f..368f32a386 100644
--- a/lib/wx/src/gen/wxTreeEvent.erl
+++ b/lib/wx/src/gen/wxTreeEvent.erl
@@ -111,7 +111,7 @@ isEditCancelled(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec setToolTip(This, ToolTip) -> 'ok' when
This::wxTreeEvent(), ToolTip::unicode:chardata().
setToolTip(#wx_ref{type=ThisT,ref=ThisRef},ToolTip)
- when is_list(ToolTip) ->
+ when ?is_chardata(ToolTip) ->
?CLASS(ThisT,wxTreeEvent),
ToolTip_UC = unicode:characters_to_binary([ToolTip,0]),
wxe_util:cast(?wxTreeEvent_SetToolTip,
diff --git a/lib/wx/src/gen/wxTreebook.erl b/lib/wx/src/gen/wxTreebook.erl
index 5d3c177f7e..dc95730224 100644
--- a/lib/wx/src/gen/wxTreebook.erl
+++ b/lib/wx/src/gen/wxTreebook.erl
@@ -44,29 +44,29 @@
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,connect/2,connect/3,convertDialogToPixels/2,convertPixelsToDialog/2,
destroyChildren/1,disable/1,disconnect/1,disconnect/2,disconnect/3,
- enable/1,enable/2,findWindow/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCaret/1,
- getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,getContainingSizer/1,
- getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
- getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
- getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
- getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
- getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
- getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
- getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
- hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
- isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
- isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
- move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
- navigate/1,navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,
- popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
- refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
- reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
- scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
- setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
- setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
- setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
- setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ dragAcceptFiles/2,enable/1,enable/2,findWindow/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,
+ getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,getCursor/1,
+ getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,getForegroundColour/1,
+ getGrandParent/1,getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,
+ getMinSize/1,getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
+ getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
+ getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
+ getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
+ hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
+ initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
+ isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
+ layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
+ move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
+ navigate/2,pageDown/1,pageUp/1,parent_class/1,popEventHandler/1,popEventHandler/2,
+ popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
+ refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
+ screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
+ setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
+ setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
+ setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
+ setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -121,7 +121,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
This::wxTreebook(), Page::wxWindow:wxWindow(), Text::unicode:chardata().
addPage(This,Page,Text)
- when is_record(This, wx_ref),is_record(Page, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),is_record(Page, wx_ref),?is_chardata(Text) ->
addPage(This,Page,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtreebook.html#wxtreebookaddpage">external documentation</a>.
@@ -130,7 +130,7 @@ addPage(This,Page,Text)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
addPage(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PageT,ref=PageRef},Text, Options)
- when is_list(Text),is_list(Options) ->
+ when ?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxTreebook),
?CLASS(PageT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
@@ -327,7 +327,7 @@ hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PtX,PtY})
This::wxTreebook(), Pos::integer(), Page::wxWindow:wxWindow(), Text::unicode:chardata().
insertPage(This,Pos,Page,Text)
- when is_record(This, wx_ref),is_integer(Pos),is_record(Page, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Pos),is_record(Page, wx_ref),?is_chardata(Text) ->
insertPage(This,Pos,Page,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtreebook.html#wxtreebookinsertpage">external documentation</a>.
@@ -336,7 +336,7 @@ insertPage(This,Pos,Page,Text)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
insertPage(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=PageT,ref=PageRef},Text, Options)
- when is_integer(Pos),is_list(Text),is_list(Options) ->
+ when is_integer(Pos),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxTreebook),
?CLASS(PageT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
@@ -352,7 +352,7 @@ insertPage(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=PageT,ref=PageRef},T
This::wxTreebook(), Pos::integer(), Page::wxWindow:wxWindow(), Text::unicode:chardata().
insertSubPage(This,Pos,Page,Text)
- when is_record(This, wx_ref),is_integer(Pos),is_record(Page, wx_ref),is_list(Text) ->
+ when is_record(This, wx_ref),is_integer(Pos),is_record(Page, wx_ref),?is_chardata(Text) ->
insertSubPage(This,Pos,Page,Text, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxtreebook.html#wxtreebookinsertsubpage">external documentation</a>.
@@ -361,7 +361,7 @@ insertSubPage(This,Pos,Page,Text)
Option :: {'bSelect', boolean()}
| {'imageId', integer()}.
insertSubPage(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=PageT,ref=PageRef},Text, Options)
- when is_integer(Pos),is_list(Text),is_list(Options) ->
+ when is_integer(Pos),?is_chardata(Text),is_list(Options) ->
?CLASS(ThisT,wxTreebook),
?CLASS(PageT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
@@ -403,7 +403,7 @@ setPageImage(#wx_ref{type=ThisT,ref=ThisRef},N,ImageId)
-spec setPageText(This, N, StrText) -> boolean() when
This::wxTreebook(), N::integer(), StrText::unicode:chardata().
setPageText(#wx_ref{type=ThisT,ref=ThisRef},N,StrText)
- when is_integer(N),is_list(StrText) ->
+ when is_integer(N),?is_chardata(StrText) ->
?CLASS(ThisT,wxTreebook),
StrText_UC = unicode:characters_to_binary([StrText,0]),
wxe_util:call(?wxTreebook_SetPageText,
@@ -440,6 +440,8 @@ setLabel(This,Label) -> wxControl:setLabel(This,Label).
getLabel(This) -> wxControl:getLabel(This).
%% From wxWindow
%% @hidden
+getContentScaleFactor(This) -> wxWindow:getContentScaleFactor(This).
+%% @hidden
setDoubleBuffered(This,On) -> wxWindow:setDoubleBuffered(This,On).
%% @hidden
isDoubleBuffered(This) -> wxWindow:isDoubleBuffered(This).
@@ -756,6 +758,8 @@ enable(This, Options) -> wxWindow:enable(This, Options).
%% @hidden
enable(This) -> wxWindow:enable(This).
%% @hidden
+dragAcceptFiles(This,Accept) -> wxWindow:dragAcceptFiles(This,Accept).
+%% @hidden
disable(This) -> wxWindow:disable(This).
%% @hidden
destroyChildren(This) -> wxWindow:destroyChildren(This).
diff --git a/lib/wx/src/gen/wxUpdateUIEvent.erl b/lib/wx/src/gen/wxUpdateUIEvent.erl
index fec42ed8eb..e7bda40a7a 100644
--- a/lib/wx/src/gen/wxUpdateUIEvent.erl
+++ b/lib/wx/src/gen/wxUpdateUIEvent.erl
@@ -181,7 +181,7 @@ setMode(Mode)
-spec setText(This, Text) -> 'ok' when
This::wxUpdateUIEvent(), Text::unicode:chardata().
setText(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxUpdateUIEvent),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxUpdateUIEvent_SetText,
diff --git a/lib/wx/src/gen/wxWindow.erl b/lib/wx/src/gen/wxWindow.erl
index 4ac7cc5d75..9ace4533c8 100644
--- a/lib/wx/src/gen/wxWindow.erl
+++ b/lib/wx/src/gen/wxWindow.erl
@@ -32,31 +32,32 @@
center/2,centerOnParent/1,centerOnParent/2,centre/1,centre/2,centreOnParent/1,
centreOnParent/2,clearBackground/1,clientToScreen/2,clientToScreen/3,
close/1,close/2,convertDialogToPixels/2,convertPixelsToDialog/2,destroy/1,
- destroyChildren/1,disable/1,enable/1,enable/2,findFocus/0,findWindow/2,
- findWindowById/1,findWindowById/2,findWindowByLabel/1,findWindowByLabel/2,
- findWindowByName/1,findWindowByName/2,fit/1,fitInside/1,freeze/1,getAcceleratorTable/1,
- getBackgroundColour/1,getBackgroundStyle/1,getBestSize/1,getCapture/0,
- getCaret/1,getCharHeight/1,getCharWidth/1,getChildren/1,getClientSize/1,
- getContainingSizer/1,getCursor/1,getDropTarget/1,getEventHandler/1,
- getExtraStyle/1,getFont/1,getForegroundColour/1,getGrandParent/1,
- getHandle/1,getHelpText/1,getId/1,getLabel/1,getMaxSize/1,getMinSize/1,
- getName/1,getParent/1,getPosition/1,getRect/1,getScreenPosition/1,
- getScreenRect/1,getScrollPos/2,getScrollRange/2,getScrollThumb/2,
- getSize/1,getSizer/1,getTextExtent/2,getTextExtent/3,getToolTip/1,
- getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,getWindowVariant/1,
- hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,hide/1,inheritAttributes/1,
- initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,isEnabled/1,
- isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,isTopLevel/1,
- layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,move/2,
- move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,navigate/1,
- navigate/2,new/0,new/2,new/3,pageDown/1,pageUp/1,popEventHandler/1,popEventHandler/2,
- popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,refresh/2,refreshRect/2,
- refreshRect/3,releaseMouse/1,removeChild/2,reparent/2,screenToClient/1,
- screenToClient/2,scrollLines/2,scrollPages/2,scrollWindow/3,scrollWindow/4,
- setAcceleratorTable/2,setAutoLayout/2,setBackgroundColour/2,setBackgroundStyle/2,
- setCaret/2,setClientSize/2,setClientSize/3,setContainingSizer/2,setCursor/2,
- setDoubleBuffered/2,setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,
- setFont/2,setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
+ destroyChildren/1,disable/1,dragAcceptFiles/2,enable/1,enable/2,findFocus/0,
+ findWindow/2,findWindowById/1,findWindowById/2,findWindowByLabel/1,
+ findWindowByLabel/2,findWindowByName/1,findWindowByName/2,fit/1,fitInside/1,
+ freeze/1,getAcceleratorTable/1,getBackgroundColour/1,getBackgroundStyle/1,
+ getBestSize/1,getCapture/0,getCaret/1,getCharHeight/1,getCharWidth/1,
+ getChildren/1,getClientSize/1,getContainingSizer/1,getContentScaleFactor/1,
+ getCursor/1,getDropTarget/1,getEventHandler/1,getExtraStyle/1,getFont/1,
+ getForegroundColour/1,getGrandParent/1,getHandle/1,getHelpText/1,
+ getId/1,getLabel/1,getMaxSize/1,getMinSize/1,getName/1,getParent/1,
+ getPosition/1,getRect/1,getScreenPosition/1,getScreenRect/1,getScrollPos/2,
+ getScrollRange/2,getScrollThumb/2,getSize/1,getSizer/1,getTextExtent/2,
+ getTextExtent/3,getToolTip/1,getUpdateRegion/1,getVirtualSize/1,getWindowStyleFlag/1,
+ getWindowVariant/1,hasCapture/1,hasScrollbar/2,hasTransparentBackground/1,
+ hide/1,inheritAttributes/1,initDialog/1,invalidateBestSize/1,isDoubleBuffered/1,
+ isEnabled/1,isExposed/2,isExposed/3,isExposed/5,isRetained/1,isShown/1,
+ isTopLevel/1,layout/1,lineDown/1,lineUp/1,lower/1,makeModal/1,makeModal/2,
+ move/2,move/3,move/4,moveAfterInTabOrder/2,moveBeforeInTabOrder/2,
+ navigate/1,navigate/2,new/0,new/2,new/3,pageDown/1,pageUp/1,popEventHandler/1,
+ popEventHandler/2,popupMenu/2,popupMenu/3,popupMenu/4,raise/1,refresh/1,
+ refresh/2,refreshRect/2,refreshRect/3,releaseMouse/1,removeChild/2,
+ reparent/2,screenToClient/1,screenToClient/2,scrollLines/2,scrollPages/2,
+ scrollWindow/3,scrollWindow/4,setAcceleratorTable/2,setAutoLayout/2,
+ setBackgroundColour/2,setBackgroundStyle/2,setCaret/2,setClientSize/2,
+ setClientSize/3,setContainingSizer/2,setCursor/2,setDoubleBuffered/2,
+ setDropTarget/2,setExtraStyle/2,setFocus/1,setFocusFromKbd/1,setFont/2,
+ setForegroundColour/2,setHelpText/2,setId/2,setLabel/2,setMaxSize/2,
setMinSize/2,setName/2,setOwnBackgroundColour/2,setOwnFont/2,setOwnForegroundColour/2,
setPalette/2,setScrollPos/3,setScrollPos/4,setScrollbar/5,setScrollbar/6,
setSize/2,setSize/3,setSize/5,setSize/6,setSizeHints/2,setSizeHints/3,
@@ -297,6 +298,15 @@ disable(#wx_ref{type=ThisT,ref=ThisRef}) ->
wxe_util:call(?wxWindow_Disable,
<<ThisRef:32/?UI>>).
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxwindow.html#wxwindowdragacceptfiles">external documentation</a>.
+-spec dragAcceptFiles(This, Accept) -> 'ok' when
+ This::wxWindow(), Accept::boolean().
+dragAcceptFiles(#wx_ref{type=ThisT,ref=ThisRef},Accept)
+ when is_boolean(Accept) ->
+ ?CLASS(ThisT,wxWindow),
+ wxe_util:cast(?wxWindow_DragAcceptFiles,
+ <<ThisRef:32/?UI,(wxe_util:from_bool(Accept)):32/?UI>>).
+
%% @equiv enable(This, [])
-spec enable(This) -> boolean() when
This::wxWindow().
@@ -339,7 +349,7 @@ findWindow(#wx_ref{type=ThisT,ref=ThisRef},Winid)
wxe_util:call(?wxWindow_FindWindow_1_0,
<<ThisRef:32/?UI,Winid:32/?UI>>);
findWindow(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxWindow),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxWindow_FindWindow_1_1,
@@ -370,7 +380,7 @@ findWindowById(Winid, Options)
Name::unicode:chardata().
findWindowByName(Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
findWindowByName(Name, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxwindow.html#wxwindowfindwindowbyname">external documentation</a>.
@@ -378,7 +388,7 @@ findWindowByName(Name)
Name::unicode:chardata(),
Option :: {'parent', wxWindow()}.
findWindowByName(Name, Options)
- when is_list(Name),is_list(Options) ->
+ when ?is_chardata(Name),is_list(Options) ->
Name_UC = unicode:characters_to_binary([Name,0]),
MOpts = fun({parent, #wx_ref{type=ParentT,ref=ParentRef}}, Acc) -> ?CLASS(ParentT,wxWindow),[<<1:32/?UI,ParentRef:32/?UI>>|Acc];
(BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
@@ -391,7 +401,7 @@ findWindowByName(Name, Options)
Label::unicode:chardata().
findWindowByLabel(Label)
- when is_list(Label) ->
+ when ?is_chardata(Label) ->
findWindowByLabel(Label, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxwindow.html#wxwindowfindwindowbylabel">external documentation</a>.
@@ -399,7 +409,7 @@ findWindowByLabel(Label)
Label::unicode:chardata(),
Option :: {'parent', wxWindow()}.
findWindowByLabel(Label, Options)
- when is_list(Label),is_list(Options) ->
+ when ?is_chardata(Label),is_list(Options) ->
Label_UC = unicode:characters_to_binary([Label,0]),
MOpts = fun({parent, #wx_ref{type=ParentT,ref=ParentRef}}, Acc) -> ?CLASS(ParentT,wxWindow),[<<1:32/?UI,ParentRef:32/?UI>>|Acc];
(BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
@@ -719,7 +729,7 @@ getSizer(#wx_ref{type=ThisT,ref=ThisRef}) ->
This::wxWindow(), String::unicode:chardata().
getTextExtent(This,String)
- when is_record(This, wx_ref),is_list(String) ->
+ when is_record(This, wx_ref),?is_chardata(String) ->
getTextExtent(This,String, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxwindow.html#wxwindowgettextextent">external documentation</a>.
@@ -728,7 +738,7 @@ getTextExtent(This,String)
This::wxWindow(), String::unicode:chardata(),
Option :: {'theFont', wxFont:wxFont()}.
getTextExtent(#wx_ref{type=ThisT,ref=ThisRef},String, Options)
- when is_list(String),is_list(Options) ->
+ when ?is_chardata(String),is_list(Options) ->
?CLASS(ThisT,wxWindow),
String_UC = unicode:characters_to_binary([String,0]),
MOpts = fun({theFont, #wx_ref{type=TheFontT,ref=TheFontRef}}, Acc) -> ?CLASS(TheFontT,wxFont),[<<1:32/?UI,TheFontRef:32/?UI>>|Acc];
@@ -1439,7 +1449,7 @@ setForegroundColour(#wx_ref{type=ThisT,ref=ThisRef},Colour)
-spec setHelpText(This, Text) -> 'ok' when
This::wxWindow(), Text::unicode:chardata().
setHelpText(#wx_ref{type=ThisT,ref=ThisRef},Text)
- when is_list(Text) ->
+ when ?is_chardata(Text) ->
?CLASS(ThisT,wxWindow),
Text_UC = unicode:characters_to_binary([Text,0]),
wxe_util:cast(?wxWindow_SetHelpText,
@@ -1458,7 +1468,7 @@ setId(#wx_ref{type=ThisT,ref=ThisRef},Winid)
-spec setLabel(This, Label) -> 'ok' when
This::wxWindow(), Label::unicode:chardata().
setLabel(#wx_ref{type=ThisT,ref=ThisRef},Label)
- when is_list(Label) ->
+ when ?is_chardata(Label) ->
?CLASS(ThisT,wxWindow),
Label_UC = unicode:characters_to_binary([Label,0]),
wxe_util:cast(?wxWindow_SetLabel,
@@ -1468,7 +1478,7 @@ setLabel(#wx_ref{type=ThisT,ref=ThisRef},Label)
-spec setName(This, Name) -> 'ok' when
This::wxWindow(), Name::unicode:chardata().
setName(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxWindow),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:cast(?wxWindow_SetName,
@@ -1707,7 +1717,7 @@ setThemeEnabled(#wx_ref{type=ThisT,ref=ThisRef},EnableTheme)
(This, Tip) -> 'ok' when
This::wxWindow(), Tip::wxToolTip:wxToolTip().
setToolTip(#wx_ref{type=ThisT,ref=ThisRef},Tip)
- when is_list(Tip) ->
+ when ?is_chardata(Tip) ->
?CLASS(ThisT,wxWindow),
Tip_UC = unicode:characters_to_binary([Tip,0]),
wxe_util:cast(?wxWindow_SetToolTip_1_0,
@@ -1944,6 +1954,14 @@ setDoubleBuffered(#wx_ref{type=ThisT,ref=ThisRef},On)
wxe_util:cast(?wxWindow_SetDoubleBuffered,
<<ThisRef:32/?UI,(wxe_util:from_bool(On)):32/?UI>>).
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxwindow.html#wxwindowgetcontentscalefactor">external documentation</a>.
+-spec getContentScaleFactor(This) -> number() when
+ This::wxWindow().
+getContentScaleFactor(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxWindow),
+ wxe_util:call(?wxWindow_GetContentScaleFactor,
+ <<ThisRef:32/?UI>>).
+
%% @doc Destroys this object, do not use object again
-spec destroy(This::wxWindow()) -> 'ok'.
destroy(Obj=#wx_ref{type=Type}) ->
diff --git a/lib/wx/src/gen/wxXmlResource.erl b/lib/wx/src/gen/wxXmlResource.erl
index ae02c74751..51f6231f48 100644
--- a/lib/wx/src/gen/wxXmlResource.erl
+++ b/lib/wx/src/gen/wxXmlResource.erl
@@ -65,7 +65,7 @@ new(Options)
Option :: {'flags', integer()}
| {'domain', unicode:chardata()}.
new(Filemask, Options)
- when is_list(Filemask),is_list(Options) ->
+ when ?is_chardata(Filemask),is_list(Options) ->
Filemask_UC = unicode:characters_to_binary([Filemask,0]),
MOpts = fun({flags, Flags}, Acc) -> [<<1:32/?UI,Flags:32/?UI>>|Acc];
({domain, Domain}, Acc) -> Domain_UC = unicode:characters_to_binary([Domain,0]),[<<2:32/?UI,(byte_size(Domain_UC)):32/?UI,(Domain_UC)/binary, 0:(((8- ((0+byte_size(Domain_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
@@ -79,7 +79,7 @@ new(Filemask, Options)
This::wxXmlResource(), Name::unicode:chardata(), Control::wxWindow:wxWindow().
attachUnknownControl(This,Name,Control)
- when is_record(This, wx_ref),is_list(Name),is_record(Control, wx_ref) ->
+ when is_record(This, wx_ref),?is_chardata(Name),is_record(Control, wx_ref) ->
attachUnknownControl(This,Name,Control, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxxmlresource.html#wxxmlresourceattachunknowncontrol">external documentation</a>.
@@ -87,7 +87,7 @@ attachUnknownControl(This,Name,Control)
This::wxXmlResource(), Name::unicode:chardata(), Control::wxWindow:wxWindow(),
Option :: {'parent', wxWindow:wxWindow()}.
attachUnknownControl(#wx_ref{type=ThisT,ref=ThisRef},Name,#wx_ref{type=ControlT,ref=ControlRef}, Options)
- when is_list(Name),is_list(Options) ->
+ when ?is_chardata(Name),is_list(Options) ->
?CLASS(ThisT,wxXmlResource),
Name_UC = unicode:characters_to_binary([Name,0]),
?CLASS(ControlT,wxWindow),
@@ -169,7 +169,7 @@ initAllHandlers(#wx_ref{type=ThisT,ref=ThisRef}) ->
-spec load(This, Filemask) -> boolean() when
This::wxXmlResource(), Filemask::unicode:chardata().
load(#wx_ref{type=ThisT,ref=ThisRef},Filemask)
- when is_list(Filemask) ->
+ when ?is_chardata(Filemask) ->
?CLASS(ThisT,wxXmlResource),
Filemask_UC = unicode:characters_to_binary([Filemask,0]),
wxe_util:call(?wxXmlResource_Load,
@@ -179,7 +179,7 @@ load(#wx_ref{type=ThisT,ref=ThisRef},Filemask)
-spec loadBitmap(This, Name) -> wxBitmap:wxBitmap() when
This::wxXmlResource(), Name::unicode:chardata().
loadBitmap(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxXmlResource_LoadBitmap,
@@ -189,7 +189,7 @@ loadBitmap(#wx_ref{type=ThisT,ref=ThisRef},Name)
-spec loadDialog(This, Parent, Name) -> wxDialog:wxDialog() when
This::wxXmlResource(), Parent::wxWindow:wxWindow(), Name::unicode:chardata().
loadDialog(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
?CLASS(ParentT,wxWindow),
Name_UC = unicode:characters_to_binary([Name,0]),
@@ -200,7 +200,7 @@ loadDialog(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},N
-spec loadDialog(This, Dlg, Parent, Name) -> boolean() when
This::wxXmlResource(), Dlg::wxDialog:wxDialog(), Parent::wxWindow:wxWindow(), Name::unicode:chardata().
loadDialog(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=DlgT,ref=DlgRef},#wx_ref{type=ParentT,ref=ParentRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
?CLASS(DlgT,wxDialog),
?CLASS(ParentT,wxWindow),
@@ -212,7 +212,7 @@ loadDialog(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=DlgT,ref=DlgRef},#wx_ref
-spec loadFrame(This, Parent, Name) -> wxFrame:wxFrame() when
This::wxXmlResource(), Parent::wxWindow:wxWindow(), Name::unicode:chardata().
loadFrame(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
?CLASS(ParentT,wxWindow),
Name_UC = unicode:characters_to_binary([Name,0]),
@@ -223,7 +223,7 @@ loadFrame(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Na
-spec loadFrame(This, Frame, Parent, Name) -> boolean() when
This::wxXmlResource(), Frame::wxFrame:wxFrame(), Parent::wxWindow:wxWindow(), Name::unicode:chardata().
loadFrame(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=FrameT,ref=FrameRef},#wx_ref{type=ParentT,ref=ParentRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
?CLASS(FrameT,wxFrame),
?CLASS(ParentT,wxWindow),
@@ -235,7 +235,7 @@ loadFrame(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=FrameT,ref=FrameRef},#wx_
-spec loadIcon(This, Name) -> wxIcon:wxIcon() when
This::wxXmlResource(), Name::unicode:chardata().
loadIcon(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxXmlResource_LoadIcon,
@@ -245,7 +245,7 @@ loadIcon(#wx_ref{type=ThisT,ref=ThisRef},Name)
-spec loadMenu(This, Name) -> wxMenu:wxMenu() when
This::wxXmlResource(), Name::unicode:chardata().
loadMenu(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxXmlResource_LoadMenu,
@@ -255,7 +255,7 @@ loadMenu(#wx_ref{type=ThisT,ref=ThisRef},Name)
-spec loadMenuBar(This, Name) -> wxMenuBar:wxMenuBar() when
This::wxXmlResource(), Name::unicode:chardata().
loadMenuBar(#wx_ref{type=ThisT,ref=ThisRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
Name_UC = unicode:characters_to_binary([Name,0]),
wxe_util:call(?wxXmlResource_LoadMenuBar_1,
@@ -265,7 +265,7 @@ loadMenuBar(#wx_ref{type=ThisT,ref=ThisRef},Name)
-spec loadMenuBar(This, Parent, Name) -> wxMenuBar:wxMenuBar() when
This::wxXmlResource(), Parent::wxWindow:wxWindow(), Name::unicode:chardata().
loadMenuBar(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
?CLASS(ParentT,wxWindow),
Name_UC = unicode:characters_to_binary([Name,0]),
@@ -276,7 +276,7 @@ loadMenuBar(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},
-spec loadPanel(This, Parent, Name) -> wxPanel:wxPanel() when
This::wxXmlResource(), Parent::wxWindow:wxWindow(), Name::unicode:chardata().
loadPanel(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
?CLASS(ParentT,wxWindow),
Name_UC = unicode:characters_to_binary([Name,0]),
@@ -287,7 +287,7 @@ loadPanel(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Na
-spec loadPanel(This, Panel, Parent, Name) -> boolean() when
This::wxXmlResource(), Panel::wxPanel:wxPanel(), Parent::wxWindow:wxWindow(), Name::unicode:chardata().
loadPanel(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PanelT,ref=PanelRef},#wx_ref{type=ParentT,ref=ParentRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
?CLASS(PanelT,wxPanel),
?CLASS(ParentT,wxWindow),
@@ -299,7 +299,7 @@ loadPanel(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=PanelT,ref=PanelRef},#wx_
-spec loadToolBar(This, Parent, Name) -> wxToolBar:wxToolBar() when
This::wxXmlResource(), Parent::wxWindow:wxWindow(), Name::unicode:chardata().
loadToolBar(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Name)
- when is_list(Name) ->
+ when ?is_chardata(Name) ->
?CLASS(ThisT,wxXmlResource),
?CLASS(ParentT,wxWindow),
Name_UC = unicode:characters_to_binary([Name,0]),
@@ -327,15 +327,13 @@ setFlags(#wx_ref{type=ThisT,ref=ThisRef},Flags)
-spec unload(This, Filename) -> boolean() when
This::wxXmlResource(), Filename::unicode:chardata().
unload(#wx_ref{type=ThisT,ref=ThisRef},Filename)
- when is_list(Filename) ->
+ when ?is_chardata(Filename) ->
?CLASS(ThisT,wxXmlResource),
Filename_UC = unicode:characters_to_binary([Filename,0]),
wxe_util:call(?wxXmlResource_Unload,
<<ThisRef:32/?UI,(byte_size(Filename_UC)):32/?UI,(Filename_UC)/binary, 0:(((8- ((0+byte_size(Filename_UC)) band 16#7)) band 16#7))/unit:8>>).
-%% @spec (Window::wxWindow:wxWindow(),Name::string(), Type::atom()) -> wx:wxObject()
-
%% @doc Looks up a control with Name in a window created with XML
%% resources. You can use it to set/get values from controls.
%% The object is type casted to <b>Type</b>.
@@ -345,6 +343,10 @@ unload(#wx_ref{type=ThisT,ref=ThisRef},Filename)
%% true = wxXmlResource:loadDialog(Xrc, Dlg, Frame, "controls_dialog"), <br />
%% LCtrl = xrcctrl(Dlg, "controls_listctrl", wxListCtrl), <br />
%% wxListCtrl:insertColumn(LCtrl, 0, "Name", [{width, 200}]), <br />
+-spec xrcctrl(Window, Name, Type) -> wx:wx_object() when
+ Window::wxWindow:wxWindow(),
+ Name::string(),
+ Type::atom().
xrcctrl(Window = #wx_ref{}, Name, Type) when is_list(Name), is_atom(Type) ->
%% Func Id ?wxXmlResource_xrcctrl
diff --git a/lib/wx/src/gen/wx_misc.erl b/lib/wx/src/gen/wx_misc.erl
index ce5d917136..66b1756a2f 100644
--- a/lib/wx/src/gen/wx_misc.erl
+++ b/lib/wx/src/gen/wx_misc.erl
@@ -72,7 +72,7 @@ bell() ->
-spec findMenuItemId(Frame, MenuString, ItemString) -> integer() when
Frame::wxFrame:wxFrame(), MenuString::unicode:chardata(), ItemString::unicode:chardata().
findMenuItemId(#wx_ref{type=FrameT,ref=FrameRef},MenuString,ItemString)
- when is_list(MenuString),is_list(ItemString) ->
+ when ?is_chardata(MenuString),?is_chardata(ItemString) ->
?CLASS(FrameT,wxFrame),
MenuString_UC = unicode:characters_to_binary([MenuString,0]),
ItemString_UC = unicode:characters_to_binary([ItemString,0]),
@@ -155,7 +155,7 @@ shell(Options)
Url::unicode:chardata().
launchDefaultBrowser(Url)
- when is_list(Url) ->
+ when ?is_chardata(Url) ->
launchDefaultBrowser(Url, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_miscellany.html#wxlaunchdefaultbrowser">external documentation</a>.
@@ -163,7 +163,7 @@ launchDefaultBrowser(Url)
Url::unicode:chardata(),
Option :: {'flags', integer()}.
launchDefaultBrowser(Url, Options)
- when is_list(Url),is_list(Options) ->
+ when ?is_chardata(Url),is_list(Options) ->
Url_UC = unicode:characters_to_binary([Url,0]),
MOpts = fun({flags, Flags}, Acc) -> [<<1:32/?UI,Flags:32/?UI>>|Acc];
(BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
diff --git a/lib/wx/src/gen/wxe_debug.hrl b/lib/wx/src/gen/wxe_debug.hrl
index 78c6577439..58cb5298e6 100644
--- a/lib/wx/src/gen/wxe_debug.hrl
+++ b/lib/wx/src/gen/wxe_debug.hrl
@@ -45,3333 +45,3338 @@ wxdebug_table() ->
{123, {wxWindow, 'Destroy', 0}},
{124, {wxWindow, destroyChildren, 0}},
{125, {wxWindow, disable, 0}},
- {126, {wxWindow, enable, 1}},
- {127, {wxWindow, findFocus, 0}},
- {128, {wxWindow, findWindow_1_0, 1}},
- {129, {wxWindow, findWindow_1_1, 1}},
- {130, {wxWindow, findWindowById, 2}},
- {131, {wxWindow, findWindowByName, 2}},
- {132, {wxWindow, findWindowByLabel, 2}},
- {133, {wxWindow, fit, 0}},
- {134, {wxWindow, fitInside, 0}},
- {135, {wxWindow, freeze, 0}},
- {136, {wxWindow, getAcceleratorTable, 0}},
- {137, {wxWindow, getBackgroundColour, 0}},
- {138, {wxWindow, getBackgroundStyle, 0}},
- {139, {wxWindow, getBestSize, 0}},
- {141, {wxWindow, getCaret, 0}},
- {142, {wxWindow, getCapture, 0}},
- {143, {wxWindow, getCharHeight, 0}},
- {144, {wxWindow, getCharWidth, 0}},
- {145, {wxWindow, getChildren, 0}},
- {148, {wxWindow, getClientSize, 0}},
- {149, {wxWindow, getContainingSizer, 0}},
- {150, {wxWindow, getCursor, 0}},
- {151, {wxWindow, getDropTarget, 0}},
- {152, {wxWindow, getEventHandler, 0}},
- {153, {wxWindow, getExtraStyle, 0}},
- {154, {wxWindow, getFont, 0}},
- {155, {wxWindow, getForegroundColour, 0}},
- {156, {wxWindow, getGrandParent, 0}},
- {157, {wxWindow, getHandle, 0}},
- {158, {wxWindow, getHelpText, 0}},
- {159, {wxWindow, getId, 0}},
- {160, {wxWindow, getLabel, 0}},
- {161, {wxWindow, getMaxSize, 0}},
- {162, {wxWindow, getMinSize, 0}},
- {163, {wxWindow, getName, 0}},
- {164, {wxWindow, getParent, 0}},
- {166, {wxWindow, getPosition, 0}},
- {167, {wxWindow, getRect, 0}},
- {169, {wxWindow, getScreenPosition, 0}},
- {170, {wxWindow, getScreenRect, 0}},
- {171, {wxWindow, getScrollPos, 1}},
- {172, {wxWindow, getScrollRange, 1}},
- {173, {wxWindow, getScrollThumb, 1}},
- {175, {wxWindow, getSize, 0}},
- {176, {wxWindow, getSizer, 0}},
- {177, {wxWindow, getTextExtent, 4}},
- {178, {wxWindow, getToolTip, 0}},
- {179, {wxWindow, getUpdateRegion, 0}},
- {181, {wxWindow, getVirtualSize, 0}},
- {183, {wxWindow, getWindowStyleFlag, 0}},
- {184, {wxWindow, getWindowVariant, 0}},
- {185, {wxWindow, hasCapture, 0}},
- {186, {wxWindow, hasScrollbar, 1}},
- {187, {wxWindow, hasTransparentBackground, 0}},
- {188, {wxWindow, hide, 0}},
- {189, {wxWindow, inheritAttributes, 0}},
- {190, {wxWindow, initDialog, 0}},
- {191, {wxWindow, invalidateBestSize, 0}},
- {192, {wxWindow, isEnabled, 0}},
- {193, {wxWindow, isExposed_2, 2}},
- {194, {wxWindow, isExposed_4, 4}},
- {195, {wxWindow, isExposed_1_0, 1}},
- {196, {wxWindow, isExposed_1_1, 1}},
- {197, {wxWindow, isRetained, 0}},
- {198, {wxWindow, isShown, 0}},
- {199, {wxWindow, isTopLevel, 0}},
- {200, {wxWindow, layout, 0}},
- {201, {wxWindow, lineDown, 0}},
- {202, {wxWindow, lineUp, 0}},
- {203, {wxWindow, lower, 0}},
- {204, {wxWindow, makeModal, 1}},
- {205, {wxWindow, move_3, 3}},
- {206, {wxWindow, move_2, 2}},
- {207, {wxWindow, moveAfterInTabOrder, 1}},
- {208, {wxWindow, moveBeforeInTabOrder, 1}},
- {209, {wxWindow, navigate, 1}},
- {210, {wxWindow, pageDown, 0}},
- {211, {wxWindow, pageUp, 0}},
- {212, {wxWindow, popEventHandler, 1}},
- {213, {wxWindow, popupMenu_2, 2}},
- {214, {wxWindow, popupMenu_3, 3}},
- {215, {wxWindow, raise, 0}},
- {216, {wxWindow, refresh, 1}},
- {217, {wxWindow, refreshRect, 2}},
- {218, {wxWindow, releaseMouse, 0}},
- {219, {wxWindow, removeChild, 1}},
- {220, {wxWindow, reparent, 1}},
- {221, {wxWindow, screenToClient_2, 2}},
- {222, {wxWindow, screenToClient_1, 1}},
- {224, {wxWindow, scrollLines, 1}},
- {226, {wxWindow, scrollPages, 1}},
- {227, {wxWindow, scrollWindow, 3}},
- {228, {wxWindow, setAcceleratorTable, 1}},
- {229, {wxWindow, setAutoLayout, 1}},
- {230, {wxWindow, setBackgroundColour, 1}},
- {231, {wxWindow, setBackgroundStyle, 1}},
- {232, {wxWindow, setCaret, 1}},
- {233, {wxWindow, setClientSize_2, 2}},
- {234, {wxWindow, setClientSize_1_0, 1}},
- {235, {wxWindow, setClientSize_1_1, 1}},
- {236, {wxWindow, setContainingSizer, 1}},
- {237, {wxWindow, setCursor, 1}},
- {238, {wxWindow, setMaxSize, 1}},
- {239, {wxWindow, setMinSize, 1}},
- {240, {wxWindow, setOwnBackgroundColour, 1}},
- {241, {wxWindow, setOwnFont, 1}},
- {242, {wxWindow, setOwnForegroundColour, 1}},
- {243, {wxWindow, setDropTarget, 1}},
- {244, {wxWindow, setExtraStyle, 1}},
- {245, {wxWindow, setFocus, 0}},
- {246, {wxWindow, setFocusFromKbd, 0}},
- {247, {wxWindow, setFont, 1}},
- {248, {wxWindow, setForegroundColour, 1}},
- {249, {wxWindow, setHelpText, 1}},
- {250, {wxWindow, setId, 1}},
- {252, {wxWindow, setLabel, 1}},
- {253, {wxWindow, setName, 1}},
- {254, {wxWindow, setPalette, 1}},
- {255, {wxWindow, setScrollbar, 5}},
- {256, {wxWindow, setScrollPos, 3}},
- {257, {wxWindow, setSize_5, 5}},
- {258, {wxWindow, setSize_2_0, 2}},
- {259, {wxWindow, setSize_1, 1}},
- {260, {wxWindow, setSize_2_1, 2}},
- {261, {wxWindow, setSizeHints_3, 3}},
- {262, {wxWindow, setSizeHints_2, 2}},
- {263, {wxWindow, setSizer, 2}},
- {264, {wxWindow, setSizerAndFit, 2}},
- {265, {wxWindow, setThemeEnabled, 1}},
- {266, {wxWindow, setToolTip_1_0, 1}},
- {267, {wxWindow, setToolTip_1_1, 1}},
- {268, {wxWindow, setVirtualSize_1, 1}},
- {269, {wxWindow, setVirtualSize_2, 2}},
- {270, {wxWindow, setVirtualSizeHints_3, 3}},
- {271, {wxWindow, setVirtualSizeHints_2, 2}},
- {272, {wxWindow, setWindowStyle, 1}},
- {273, {wxWindow, setWindowStyleFlag, 1}},
- {274, {wxWindow, setWindowVariant, 1}},
- {275, {wxWindow, shouldInheritColours, 0}},
- {276, {wxWindow, show, 1}},
- {277, {wxWindow, thaw, 0}},
- {278, {wxWindow, transferDataFromWindow, 0}},
- {279, {wxWindow, transferDataToWindow, 0}},
- {280, {wxWindow, update, 0}},
- {281, {wxWindow, updateWindowUI, 1}},
- {282, {wxWindow, validate, 0}},
- {283, {wxWindow, warpPointer, 2}},
- {284, {wxWindow, setTransparent, 1}},
- {285, {wxWindow, canSetTransparent, 0}},
- {286, {wxWindow, isDoubleBuffered, 0}},
- {287, {wxWindow, setDoubleBuffered, 1}},
- {288, {wxTopLevelWindow, getIcon, 0}},
- {289, {wxTopLevelWindow, getIcons, 0}},
- {290, {wxTopLevelWindow, getTitle, 0}},
- {291, {wxTopLevelWindow, isActive, 0}},
- {292, {wxTopLevelWindow, iconize, 1}},
- {293, {wxTopLevelWindow, isFullScreen, 0}},
- {294, {wxTopLevelWindow, isIconized, 0}},
- {295, {wxTopLevelWindow, isMaximized, 0}},
- {296, {wxTopLevelWindow, maximize, 1}},
- {297, {wxTopLevelWindow, requestUserAttention, 1}},
- {298, {wxTopLevelWindow, setIcon, 1}},
- {299, {wxTopLevelWindow, setIcons, 1}},
- {300, {wxTopLevelWindow, centerOnScreen, 1}},
- {301, {wxTopLevelWindow, centreOnScreen, 1}},
- {303, {wxTopLevelWindow, setShape, 1}},
- {304, {wxTopLevelWindow, setTitle, 1}},
- {305, {wxTopLevelWindow, showFullScreen, 2}},
- {307, {wxFrame, new_4, 4}},
- {308, {wxFrame, new_0, 0}},
- {310, {wxFrame, destruct, 0}},
- {311, {wxFrame, create, 4}},
- {312, {wxFrame, createStatusBar, 1}},
- {313, {wxFrame, createToolBar, 1}},
- {314, {wxFrame, getClientAreaOrigin, 0}},
- {315, {wxFrame, getMenuBar, 0}},
- {316, {wxFrame, getStatusBar, 0}},
- {317, {wxFrame, getStatusBarPane, 0}},
- {318, {wxFrame, getToolBar, 0}},
- {319, {wxFrame, processCommand, 1}},
- {320, {wxFrame, sendSizeEvent, 0}},
- {321, {wxFrame, setMenuBar, 1}},
- {322, {wxFrame, setStatusBar, 1}},
- {323, {wxFrame, setStatusBarPane, 1}},
- {324, {wxFrame, setStatusText, 2}},
- {325, {wxFrame, setStatusWidths, 2}},
- {326, {wxFrame, setToolBar, 1}},
- {327, {wxMiniFrame, new_0, 0}},
- {328, {wxMiniFrame, new_4, 4}},
- {329, {wxMiniFrame, create, 4}},
- {330, {wxMiniFrame, 'Destroy', undefined}},
- {331, {wxSplashScreen, new_0, 0}},
- {332, {wxSplashScreen, new_6, 6}},
- {333, {wxSplashScreen, destruct, 0}},
- {334, {wxSplashScreen, getSplashStyle, 0}},
- {335, {wxSplashScreen, getTimeout, 0}},
- {336, {wxPanel, new_0, 0}},
- {337, {wxPanel, new_6, 6}},
- {338, {wxPanel, new_2, 2}},
- {339, {wxPanel, destruct, 0}},
- {340, {wxPanel, initDialog, 0}},
- {341, {wxPanel, setFocusIgnoringChildren, 0}},
- {342, {wxScrolledWindow, new_0, 0}},
- {343, {wxScrolledWindow, new_2, 2}},
- {344, {wxScrolledWindow, destruct, 0}},
- {345, {wxScrolledWindow, calcScrolledPosition_4, 4}},
- {346, {wxScrolledWindow, calcScrolledPosition_1, 1}},
- {347, {wxScrolledWindow, calcUnscrolledPosition_4, 4}},
- {348, {wxScrolledWindow, calcUnscrolledPosition_1, 1}},
- {349, {wxScrolledWindow, enableScrolling, 2}},
- {350, {wxScrolledWindow, getScrollPixelsPerUnit, 2}},
- {351, {wxScrolledWindow, getViewStart, 2}},
- {352, {wxScrolledWindow, doPrepareDC, 1}},
- {353, {wxScrolledWindow, prepareDC, 1}},
- {354, {wxScrolledWindow, scroll, 2}},
- {355, {wxScrolledWindow, setScrollbars, 5}},
- {356, {wxScrolledWindow, setScrollRate, 2}},
- {357, {wxScrolledWindow, setTargetWindow, 1}},
- {358, {wxSashWindow, new_0, 0}},
- {359, {wxSashWindow, new_2, 2}},
- {360, {wxSashWindow, destruct, 0}},
- {361, {wxSashWindow, getSashVisible, 1}},
- {362, {wxSashWindow, getMaximumSizeX, 0}},
- {363, {wxSashWindow, getMaximumSizeY, 0}},
- {364, {wxSashWindow, getMinimumSizeX, 0}},
- {365, {wxSashWindow, getMinimumSizeY, 0}},
- {366, {wxSashWindow, setMaximumSizeX, 1}},
- {367, {wxSashWindow, setMaximumSizeY, 1}},
- {368, {wxSashWindow, setMinimumSizeX, 1}},
- {369, {wxSashWindow, setMinimumSizeY, 1}},
- {370, {wxSashWindow, setSashVisible, 2}},
- {371, {wxSashLayoutWindow, new_0, 0}},
- {372, {wxSashLayoutWindow, new_2, 2}},
- {373, {wxSashLayoutWindow, create, 2}},
- {374, {wxSashLayoutWindow, getAlignment, 0}},
- {375, {wxSashLayoutWindow, getOrientation, 0}},
- {376, {wxSashLayoutWindow, setAlignment, 1}},
- {377, {wxSashLayoutWindow, setDefaultSize, 1}},
- {378, {wxSashLayoutWindow, setOrientation, 1}},
- {379, {wxSashLayoutWindow, 'Destroy', undefined}},
- {380, {wxGrid, new_0, 0}},
- {381, {wxGrid, new_3, 3}},
- {382, {wxGrid, new_4, 4}},
- {383, {wxGrid, destruct, 0}},
- {384, {wxGrid, appendCols, 1}},
- {385, {wxGrid, appendRows, 1}},
- {386, {wxGrid, autoSize, 0}},
- {387, {wxGrid, autoSizeColumn, 2}},
- {388, {wxGrid, autoSizeColumns, 1}},
- {389, {wxGrid, autoSizeRow, 2}},
- {390, {wxGrid, autoSizeRows, 1}},
- {391, {wxGrid, beginBatch, 0}},
- {392, {wxGrid, blockToDeviceRect, 2}},
- {393, {wxGrid, canDragColSize, 0}},
- {394, {wxGrid, canDragRowSize, 0}},
- {395, {wxGrid, canDragGridSize, 0}},
- {396, {wxGrid, canEnableCellControl, 0}},
- {397, {wxGrid, cellToRect_2, 2}},
- {398, {wxGrid, cellToRect_1, 1}},
- {399, {wxGrid, clearGrid, 0}},
- {400, {wxGrid, clearSelection, 0}},
- {401, {wxGrid, createGrid, 3}},
- {402, {wxGrid, deleteCols, 1}},
- {403, {wxGrid, deleteRows, 1}},
- {404, {wxGrid, disableCellEditControl, 0}},
- {405, {wxGrid, disableDragColSize, 0}},
- {406, {wxGrid, disableDragGridSize, 0}},
- {407, {wxGrid, disableDragRowSize, 0}},
- {408, {wxGrid, enableCellEditControl, 1}},
- {409, {wxGrid, enableDragColSize, 1}},
- {410, {wxGrid, enableDragGridSize, 1}},
- {411, {wxGrid, enableDragRowSize, 1}},
- {412, {wxGrid, enableEditing, 1}},
- {413, {wxGrid, enableGridLines, 1}},
- {414, {wxGrid, endBatch, 0}},
- {415, {wxGrid, fit, 0}},
- {416, {wxGrid, forceRefresh, 0}},
- {417, {wxGrid, getBatchCount, 0}},
- {418, {wxGrid, getCellAlignment, 4}},
- {419, {wxGrid, getCellBackgroundColour, 2}},
- {420, {wxGrid, getCellEditor, 2}},
- {421, {wxGrid, getCellFont, 2}},
- {422, {wxGrid, getCellRenderer, 2}},
- {423, {wxGrid, getCellTextColour, 2}},
- {424, {wxGrid, getCellValue_2, 2}},
- {425, {wxGrid, getCellValue_1, 1}},
- {426, {wxGrid, getColLabelAlignment, 2}},
- {427, {wxGrid, getColLabelSize, 0}},
- {428, {wxGrid, getColLabelValue, 1}},
- {429, {wxGrid, getColMinimalAcceptableWidth, 0}},
- {430, {wxGrid, getDefaultCellAlignment, 2}},
- {431, {wxGrid, getDefaultCellBackgroundColour, 0}},
- {432, {wxGrid, getDefaultCellFont, 0}},
- {433, {wxGrid, getDefaultCellTextColour, 0}},
- {434, {wxGrid, getDefaultColLabelSize, 0}},
- {435, {wxGrid, getDefaultColSize, 0}},
- {436, {wxGrid, getDefaultEditor, 0}},
- {437, {wxGrid, getDefaultEditorForCell_2, 2}},
- {438, {wxGrid, getDefaultEditorForCell_1, 1}},
- {439, {wxGrid, getDefaultEditorForType, 1}},
- {440, {wxGrid, getDefaultRenderer, 0}},
- {441, {wxGrid, getDefaultRendererForCell, 2}},
- {442, {wxGrid, getDefaultRendererForType, 1}},
- {443, {wxGrid, getDefaultRowLabelSize, 0}},
- {444, {wxGrid, getDefaultRowSize, 0}},
- {445, {wxGrid, getGridCursorCol, 0}},
- {446, {wxGrid, getGridCursorRow, 0}},
- {447, {wxGrid, getGridLineColour, 0}},
- {448, {wxGrid, gridLinesEnabled, 0}},
- {449, {wxGrid, getLabelBackgroundColour, 0}},
- {450, {wxGrid, getLabelFont, 0}},
- {451, {wxGrid, getLabelTextColour, 0}},
- {452, {wxGrid, getNumberCols, 0}},
- {453, {wxGrid, getNumberRows, 0}},
- {454, {wxGrid, getOrCreateCellAttr, 2}},
- {455, {wxGrid, getRowMinimalAcceptableHeight, 0}},
- {456, {wxGrid, getRowLabelAlignment, 2}},
- {457, {wxGrid, getRowLabelSize, 0}},
- {458, {wxGrid, getRowLabelValue, 1}},
- {459, {wxGrid, getRowSize, 1}},
- {460, {wxGrid, getScrollLineX, 0}},
- {461, {wxGrid, getScrollLineY, 0}},
- {462, {wxGrid, getSelectedCells, 0}},
- {463, {wxGrid, getSelectedCols, 0}},
- {464, {wxGrid, getSelectedRows, 0}},
- {465, {wxGrid, getSelectionBackground, 0}},
- {466, {wxGrid, getSelectionBlockTopLeft, 0}},
- {467, {wxGrid, getSelectionBlockBottomRight, 0}},
- {468, {wxGrid, getSelectionForeground, 0}},
- {469, {wxGrid, getViewWidth, 0}},
- {470, {wxGrid, getGridWindow, 0}},
- {471, {wxGrid, getGridRowLabelWindow, 0}},
- {472, {wxGrid, getGridColLabelWindow, 0}},
- {473, {wxGrid, getGridCornerLabelWindow, 0}},
- {474, {wxGrid, hideCellEditControl, 0}},
- {475, {wxGrid, insertCols, 1}},
- {476, {wxGrid, insertRows, 1}},
- {477, {wxGrid, isCellEditControlEnabled, 0}},
- {478, {wxGrid, isCurrentCellReadOnly, 0}},
- {479, {wxGrid, isEditable, 0}},
- {480, {wxGrid, isInSelection_2, 2}},
- {481, {wxGrid, isInSelection_1, 1}},
- {482, {wxGrid, isReadOnly, 2}},
- {483, {wxGrid, isSelection, 0}},
- {484, {wxGrid, isVisible_3, 3}},
- {485, {wxGrid, isVisible_2, 2}},
- {486, {wxGrid, makeCellVisible_2, 2}},
- {487, {wxGrid, makeCellVisible_1, 1}},
- {488, {wxGrid, moveCursorDown, 1}},
- {489, {wxGrid, moveCursorLeft, 1}},
- {490, {wxGrid, moveCursorRight, 1}},
- {491, {wxGrid, moveCursorUp, 1}},
- {492, {wxGrid, moveCursorDownBlock, 1}},
- {493, {wxGrid, moveCursorLeftBlock, 1}},
- {494, {wxGrid, moveCursorRightBlock, 1}},
- {495, {wxGrid, moveCursorUpBlock, 1}},
- {496, {wxGrid, movePageDown, 0}},
- {497, {wxGrid, movePageUp, 0}},
- {498, {wxGrid, registerDataType, 3}},
- {499, {wxGrid, saveEditControlValue, 0}},
- {500, {wxGrid, selectAll, 0}},
- {501, {wxGrid, selectBlock_5, 5}},
- {502, {wxGrid, selectBlock_3, 3}},
- {503, {wxGrid, selectCol, 2}},
- {504, {wxGrid, selectRow, 2}},
- {505, {wxGrid, setCellAlignment_4, 4}},
- {506, {wxGrid, setCellAlignment_3, 3}},
- {507, {wxGrid, setCellAlignment_1, 1}},
- {508, {wxGrid, setCellBackgroundColour_3_0, 3}},
- {509, {wxGrid, setCellBackgroundColour_1, 1}},
- {510, {wxGrid, setCellBackgroundColour_3_1, 3}},
- {511, {wxGrid, setCellEditor, 3}},
- {512, {wxGrid, setCellFont, 3}},
- {513, {wxGrid, setCellRenderer, 3}},
- {514, {wxGrid, setCellTextColour_3_0, 3}},
- {515, {wxGrid, setCellTextColour_3_1, 3}},
- {516, {wxGrid, setCellTextColour_1, 1}},
- {517, {wxGrid, setCellValue_3_0, 3}},
- {518, {wxGrid, setCellValue_2, 2}},
- {519, {wxGrid, setCellValue_3_1, 3}},
- {520, {wxGrid, setColAttr, 2}},
- {521, {wxGrid, setColFormatBool, 1}},
- {522, {wxGrid, setColFormatNumber, 1}},
- {523, {wxGrid, setColFormatFloat, 2}},
- {524, {wxGrid, setColFormatCustom, 2}},
- {525, {wxGrid, setColLabelAlignment, 2}},
- {526, {wxGrid, setColLabelSize, 1}},
- {527, {wxGrid, setColLabelValue, 2}},
- {528, {wxGrid, setColMinimalWidth, 2}},
- {529, {wxGrid, setColMinimalAcceptableWidth, 1}},
- {530, {wxGrid, setColSize, 2}},
- {531, {wxGrid, setDefaultCellAlignment, 2}},
- {532, {wxGrid, setDefaultCellBackgroundColour, 1}},
- {533, {wxGrid, setDefaultCellFont, 1}},
- {534, {wxGrid, setDefaultCellTextColour, 1}},
- {535, {wxGrid, setDefaultEditor, 1}},
- {536, {wxGrid, setDefaultRenderer, 1}},
- {537, {wxGrid, setDefaultColSize, 2}},
- {538, {wxGrid, setDefaultRowSize, 2}},
- {539, {wxGrid, setGridCursor, 2}},
- {540, {wxGrid, setGridLineColour, 1}},
- {541, {wxGrid, setLabelBackgroundColour, 1}},
- {542, {wxGrid, setLabelFont, 1}},
- {543, {wxGrid, setLabelTextColour, 1}},
- {544, {wxGrid, setMargins, 2}},
- {545, {wxGrid, setReadOnly, 3}},
- {546, {wxGrid, setRowAttr, 2}},
- {547, {wxGrid, setRowLabelAlignment, 2}},
- {548, {wxGrid, setRowLabelSize, 1}},
- {549, {wxGrid, setRowLabelValue, 2}},
- {550, {wxGrid, setRowMinimalHeight, 2}},
- {551, {wxGrid, setRowMinimalAcceptableHeight, 1}},
- {552, {wxGrid, setRowSize, 2}},
- {553, {wxGrid, setScrollLineX, 1}},
- {554, {wxGrid, setScrollLineY, 1}},
- {555, {wxGrid, setSelectionBackground, 1}},
- {556, {wxGrid, setSelectionForeground, 1}},
- {557, {wxGrid, setSelectionMode, 1}},
- {558, {wxGrid, showCellEditControl, 0}},
- {559, {wxGrid, xToCol, 2}},
- {560, {wxGrid, xToEdgeOfCol, 1}},
- {561, {wxGrid, yToEdgeOfRow, 1}},
- {562, {wxGrid, yToRow, 1}},
- {563, {wxGridCellRenderer, draw, 7}},
- {564, {wxGridCellRenderer, getBestSize, 5}},
- {565, {wxGridCellEditor, create, 3}},
- {566, {wxGridCellEditor, isCreated, 0}},
- {567, {wxGridCellEditor, setSize, 1}},
- {568, {wxGridCellEditor, show, 2}},
- {569, {wxGridCellEditor, paintBackground, 2}},
- {570, {wxGridCellEditor, beginEdit, 3}},
- {571, {wxGridCellEditor, endEdit, 3}},
- {572, {wxGridCellEditor, reset, 0}},
- {573, {wxGridCellEditor, startingKey, 1}},
- {574, {wxGridCellEditor, startingClick, 0}},
- {575, {wxGridCellEditor, handleReturn, 1}},
- {576, {wxGridCellBoolRenderer, new, 0}},
- {577, {wxGridCellBoolRenderer, 'Destroy', undefined}},
- {578, {wxGridCellBoolEditor, new, 0}},
- {579, {wxGridCellBoolEditor, isTrueValue, 1}},
- {580, {wxGridCellBoolEditor, useStringValues, 1}},
- {581, {wxGridCellBoolEditor, 'Destroy', undefined}},
- {582, {wxGridCellFloatRenderer, new, 1}},
- {583, {wxGridCellFloatRenderer, getPrecision, 0}},
- {584, {wxGridCellFloatRenderer, getWidth, 0}},
- {585, {wxGridCellFloatRenderer, setParameters, 1}},
- {586, {wxGridCellFloatRenderer, setPrecision, 1}},
- {587, {wxGridCellFloatRenderer, setWidth, 1}},
- {588, {wxGridCellFloatRenderer, 'Destroy', undefined}},
- {589, {wxGridCellFloatEditor, new, 1}},
- {590, {wxGridCellFloatEditor, setParameters, 1}},
- {591, {wxGridCellFloatEditor, 'Destroy', undefined}},
- {592, {wxGridCellStringRenderer, new, 0}},
- {593, {wxGridCellStringRenderer, 'Destroy', undefined}},
- {594, {wxGridCellTextEditor, new, 0}},
- {595, {wxGridCellTextEditor, setParameters, 1}},
- {596, {wxGridCellTextEditor, 'Destroy', undefined}},
- {598, {wxGridCellChoiceEditor, new, 2}},
- {599, {wxGridCellChoiceEditor, setParameters, 1}},
- {600, {wxGridCellChoiceEditor, 'Destroy', undefined}},
- {601, {wxGridCellNumberRenderer, new, 0}},
- {602, {wxGridCellNumberRenderer, 'Destroy', undefined}},
- {603, {wxGridCellNumberEditor, new, 1}},
- {604, {wxGridCellNumberEditor, getValue, 0}},
- {605, {wxGridCellNumberEditor, setParameters, 1}},
- {606, {wxGridCellNumberEditor, 'Destroy', undefined}},
- {607, {wxGridCellAttr, setTextColour, 1}},
- {608, {wxGridCellAttr, setBackgroundColour, 1}},
- {609, {wxGridCellAttr, setFont, 1}},
- {610, {wxGridCellAttr, setAlignment, 2}},
- {611, {wxGridCellAttr, setReadOnly, 1}},
- {612, {wxGridCellAttr, setRenderer, 1}},
- {613, {wxGridCellAttr, setEditor, 1}},
- {614, {wxGridCellAttr, hasTextColour, 0}},
- {615, {wxGridCellAttr, hasBackgroundColour, 0}},
- {616, {wxGridCellAttr, hasFont, 0}},
- {617, {wxGridCellAttr, hasAlignment, 0}},
- {618, {wxGridCellAttr, hasRenderer, 0}},
- {619, {wxGridCellAttr, hasEditor, 0}},
- {620, {wxGridCellAttr, getTextColour, 0}},
- {621, {wxGridCellAttr, getBackgroundColour, 0}},
- {622, {wxGridCellAttr, getFont, 0}},
- {623, {wxGridCellAttr, getAlignment, 2}},
- {624, {wxGridCellAttr, getRenderer, 3}},
- {625, {wxGridCellAttr, getEditor, 3}},
- {626, {wxGridCellAttr, isReadOnly, 0}},
- {627, {wxGridCellAttr, setDefAttr, 1}},
- {628, {wxDC, blit, 5}},
- {629, {wxDC, calcBoundingBox, 2}},
- {630, {wxDC, clear, 0}},
- {631, {wxDC, computeScaleAndOrigin, 0}},
- {632, {wxDC, crossHair, 1}},
- {633, {wxDC, destroyClippingRegion, 0}},
- {634, {wxDC, deviceToLogicalX, 1}},
- {635, {wxDC, deviceToLogicalXRel, 1}},
- {636, {wxDC, deviceToLogicalY, 1}},
- {637, {wxDC, deviceToLogicalYRel, 1}},
- {638, {wxDC, drawArc, 3}},
- {639, {wxDC, drawBitmap, 3}},
- {640, {wxDC, drawCheckMark, 1}},
- {641, {wxDC, drawCircle, 2}},
- {643, {wxDC, drawEllipse_2, 2}},
- {644, {wxDC, drawEllipse_1, 1}},
- {645, {wxDC, drawEllipticArc, 4}},
- {646, {wxDC, drawIcon, 2}},
- {647, {wxDC, drawLabel, 3}},
- {648, {wxDC, drawLine, 2}},
- {649, {wxDC, drawLines, 3}},
- {651, {wxDC, drawPolygon, 3}},
- {653, {wxDC, drawPoint, 1}},
- {655, {wxDC, drawRectangle_2, 2}},
- {656, {wxDC, drawRectangle_1, 1}},
- {657, {wxDC, drawRotatedText, 3}},
- {659, {wxDC, drawRoundedRectangle_3, 3}},
- {660, {wxDC, drawRoundedRectangle_2, 2}},
- {661, {wxDC, drawText, 2}},
- {662, {wxDC, endDoc, 0}},
- {663, {wxDC, endPage, 0}},
- {664, {wxDC, floodFill, 3}},
- {665, {wxDC, getBackground, 0}},
- {666, {wxDC, getBackgroundMode, 0}},
- {667, {wxDC, getBrush, 0}},
- {668, {wxDC, getCharHeight, 0}},
- {669, {wxDC, getCharWidth, 0}},
- {670, {wxDC, getClippingBox, 4}},
- {672, {wxDC, getFont, 0}},
- {673, {wxDC, getLayoutDirection, 0}},
- {674, {wxDC, getLogicalFunction, 0}},
- {675, {wxDC, getMapMode, 0}},
- {676, {wxDC, getMultiLineTextExtent_4, 4}},
- {677, {wxDC, getMultiLineTextExtent_1, 1}},
- {678, {wxDC, getPartialTextExtents, 2}},
- {679, {wxDC, getPen, 0}},
- {680, {wxDC, getPixel, 2}},
- {681, {wxDC, getPPI, 0}},
- {683, {wxDC, getSize, 0}},
- {685, {wxDC, getSizeMM, 0}},
- {686, {wxDC, getTextBackground, 0}},
- {687, {wxDC, getTextExtent_4, 4}},
- {688, {wxDC, getTextExtent_1, 1}},
- {690, {wxDC, getTextForeground, 0}},
- {691, {wxDC, getUserScale, 2}},
- {692, {wxDC, gradientFillConcentric_3, 3}},
- {693, {wxDC, gradientFillConcentric_4, 4}},
- {694, {wxDC, gradientFillLinear, 4}},
- {695, {wxDC, logicalToDeviceX, 1}},
- {696, {wxDC, logicalToDeviceXRel, 1}},
- {697, {wxDC, logicalToDeviceY, 1}},
- {698, {wxDC, logicalToDeviceYRel, 1}},
- {699, {wxDC, maxX, 0}},
- {700, {wxDC, maxY, 0}},
- {701, {wxDC, minX, 0}},
- {702, {wxDC, minY, 0}},
- {703, {wxDC, isOk, 0}},
- {704, {wxDC, resetBoundingBox, 0}},
- {705, {wxDC, setAxisOrientation, 2}},
- {706, {wxDC, setBackground, 1}},
- {707, {wxDC, setBackgroundMode, 1}},
- {708, {wxDC, setBrush, 1}},
- {710, {wxDC, setClippingRegion_2, 2}},
- {711, {wxDC, setClippingRegion_1_1, 1}},
- {712, {wxDC, setClippingRegion_1_0, 1}},
- {713, {wxDC, setDeviceOrigin, 2}},
- {714, {wxDC, setFont, 1}},
- {715, {wxDC, setLayoutDirection, 1}},
- {716, {wxDC, setLogicalFunction, 1}},
- {717, {wxDC, setMapMode, 1}},
- {718, {wxDC, setPalette, 1}},
- {719, {wxDC, setPen, 1}},
- {720, {wxDC, setTextBackground, 1}},
- {721, {wxDC, setTextForeground, 1}},
- {722, {wxDC, setUserScale, 2}},
- {723, {wxDC, startDoc, 1}},
- {724, {wxDC, startPage, 0}},
- {725, {wxMirrorDC, new, 2}},
- {726, {wxMirrorDC, 'Destroy', undefined}},
- {727, {wxScreenDC, new, 0}},
- {728, {wxScreenDC, destruct, 0}},
- {729, {wxPostScriptDC, new_0, 0}},
- {730, {wxPostScriptDC, new_1, 1}},
- {731, {wxPostScriptDC, destruct, 0}},
- {732, {wxPostScriptDC, setResolution, 1}},
- {733, {wxPostScriptDC, getResolution, 0}},
- {734, {wxWindowDC, new_0, 0}},
- {735, {wxWindowDC, new_1, 1}},
- {736, {wxWindowDC, destruct, 0}},
- {737, {wxClientDC, new_0, 0}},
- {738, {wxClientDC, new_1, 1}},
- {739, {wxClientDC, 'Destroy', undefined}},
- {740, {wxPaintDC, new_0, 0}},
- {741, {wxPaintDC, new_1, 1}},
- {742, {wxPaintDC, 'Destroy', undefined}},
- {744, {wxMemoryDC, new_1_0, 1}},
- {745, {wxMemoryDC, new_1_1, 1}},
- {746, {wxMemoryDC, new_0, 0}},
- {748, {wxMemoryDC, destruct, 0}},
- {749, {wxMemoryDC, selectObject, 1}},
- {750, {wxMemoryDC, selectObjectAsSource, 1}},
- {751, {wxBufferedDC, new_0, 0}},
- {752, {wxBufferedDC, new_2, 2}},
- {753, {wxBufferedDC, new_3, 3}},
- {754, {wxBufferedDC, destruct, 0}},
- {755, {wxBufferedDC, init_2, 2}},
- {756, {wxBufferedDC, init_3, 3}},
- {757, {wxBufferedPaintDC, new_3, 3}},
- {758, {wxBufferedPaintDC, new_2, 2}},
- {759, {wxBufferedPaintDC, destruct, 0}},
- {760, {wxGraphicsObject, destruct, 0}},
- {761, {wxGraphicsObject, getRenderer, 0}},
- {762, {wxGraphicsObject, isNull, 0}},
- {763, {wxGraphicsContext, destruct, 0}},
- {764, {wxGraphicsContext, create_1_1, 1}},
- {765, {wxGraphicsContext, create_1_0, 1}},
- {766, {wxGraphicsContext, create_0, 0}},
- {767, {wxGraphicsContext, createPen, 1}},
- {768, {wxGraphicsContext, createBrush, 1}},
- {769, {wxGraphicsContext, createRadialGradientBrush, 7}},
- {770, {wxGraphicsContext, createLinearGradientBrush, 6}},
- {771, {wxGraphicsContext, createFont, 2}},
- {772, {wxGraphicsContext, createMatrix, 1}},
- {773, {wxGraphicsContext, createPath, 0}},
- {774, {wxGraphicsContext, clip_1, 1}},
- {775, {wxGraphicsContext, clip_4, 4}},
- {776, {wxGraphicsContext, resetClip, 0}},
- {777, {wxGraphicsContext, drawBitmap, 5}},
- {778, {wxGraphicsContext, drawEllipse, 4}},
- {779, {wxGraphicsContext, drawIcon, 5}},
- {780, {wxGraphicsContext, drawLines, 3}},
- {781, {wxGraphicsContext, drawPath, 2}},
- {782, {wxGraphicsContext, drawRectangle, 4}},
- {783, {wxGraphicsContext, drawRoundedRectangle, 5}},
- {784, {wxGraphicsContext, drawText_3, 3}},
- {785, {wxGraphicsContext, drawText_4_0, 4}},
- {786, {wxGraphicsContext, drawText_4_1, 4}},
- {787, {wxGraphicsContext, drawText_5, 5}},
- {788, {wxGraphicsContext, fillPath, 2}},
- {789, {wxGraphicsContext, strokePath, 1}},
- {790, {wxGraphicsContext, getPartialTextExtents, 2}},
- {791, {wxGraphicsContext, getTextExtent, 5}},
- {792, {wxGraphicsContext, rotate, 1}},
- {793, {wxGraphicsContext, scale, 2}},
- {794, {wxGraphicsContext, translate, 2}},
- {795, {wxGraphicsContext, getTransform, 0}},
- {796, {wxGraphicsContext, setTransform, 1}},
- {797, {wxGraphicsContext, concatTransform, 1}},
- {798, {wxGraphicsContext, setBrush_1_1, 1}},
- {799, {wxGraphicsContext, setBrush_1_0, 1}},
- {800, {wxGraphicsContext, setFont_1, 1}},
- {801, {wxGraphicsContext, setFont_2, 2}},
- {802, {wxGraphicsContext, setPen_1_0, 1}},
- {803, {wxGraphicsContext, setPen_1_1, 1}},
- {804, {wxGraphicsContext, strokeLine, 4}},
- {805, {wxGraphicsContext, strokeLines, 2}},
- {807, {wxGraphicsMatrix, concat, 1}},
- {809, {wxGraphicsMatrix, get, 1}},
- {810, {wxGraphicsMatrix, invert, 0}},
- {811, {wxGraphicsMatrix, isEqual, 1}},
- {813, {wxGraphicsMatrix, isIdentity, 0}},
- {814, {wxGraphicsMatrix, rotate, 1}},
- {815, {wxGraphicsMatrix, scale, 2}},
- {816, {wxGraphicsMatrix, translate, 2}},
- {817, {wxGraphicsMatrix, set, 1}},
- {818, {wxGraphicsMatrix, transformPoint, 2}},
- {819, {wxGraphicsMatrix, transformDistance, 2}},
- {820, {wxGraphicsPath, moveToPoint_2, 2}},
- {821, {wxGraphicsPath, moveToPoint_1, 1}},
- {822, {wxGraphicsPath, addArc_6, 6}},
- {823, {wxGraphicsPath, addArc_5, 5}},
- {824, {wxGraphicsPath, addArcToPoint, 5}},
- {825, {wxGraphicsPath, addCircle, 3}},
- {826, {wxGraphicsPath, addCurveToPoint_6, 6}},
- {827, {wxGraphicsPath, addCurveToPoint_3, 3}},
- {828, {wxGraphicsPath, addEllipse, 4}},
- {829, {wxGraphicsPath, addLineToPoint_2, 2}},
- {830, {wxGraphicsPath, addLineToPoint_1, 1}},
- {831, {wxGraphicsPath, addPath, 1}},
- {832, {wxGraphicsPath, addQuadCurveToPoint, 4}},
- {833, {wxGraphicsPath, addRectangle, 4}},
- {834, {wxGraphicsPath, addRoundedRectangle, 5}},
- {835, {wxGraphicsPath, closeSubpath, 0}},
- {836, {wxGraphicsPath, contains_3, 3}},
- {837, {wxGraphicsPath, contains_2, 2}},
- {839, {wxGraphicsPath, getBox, 0}},
- {841, {wxGraphicsPath, getCurrentPoint, 0}},
- {842, {wxGraphicsPath, transform, 1}},
- {843, {wxGraphicsRenderer, getDefaultRenderer, 0}},
- {844, {wxGraphicsRenderer, createContext_1_1, 1}},
- {845, {wxGraphicsRenderer, createContext_1_0, 1}},
- {846, {wxGraphicsRenderer, createPen, 1}},
- {847, {wxGraphicsRenderer, createBrush, 1}},
- {848, {wxGraphicsRenderer, createLinearGradientBrush, 6}},
- {849, {wxGraphicsRenderer, createRadialGradientBrush, 7}},
- {850, {wxGraphicsRenderer, createFont, 2}},
- {851, {wxGraphicsRenderer, createMatrix, 1}},
- {852, {wxGraphicsRenderer, createPath, 0}},
- {854, {wxMenuBar, new_1, 1}},
- {856, {wxMenuBar, new_0, 0}},
- {858, {wxMenuBar, destruct, 0}},
- {859, {wxMenuBar, append, 2}},
- {860, {wxMenuBar, check, 2}},
- {861, {wxMenuBar, enable_2, 2}},
- {862, {wxMenuBar, enable_1, 1}},
- {863, {wxMenuBar, enableTop, 2}},
- {864, {wxMenuBar, findMenu, 1}},
- {865, {wxMenuBar, findMenuItem, 2}},
- {866, {wxMenuBar, findItem, 2}},
- {867, {wxMenuBar, getHelpString, 1}},
- {868, {wxMenuBar, getLabel_1, 1}},
- {869, {wxMenuBar, getLabel_0, 0}},
- {870, {wxMenuBar, getLabelTop, 1}},
- {871, {wxMenuBar, getMenu, 1}},
- {872, {wxMenuBar, getMenuCount, 0}},
- {873, {wxMenuBar, insert, 3}},
- {874, {wxMenuBar, isChecked, 1}},
- {875, {wxMenuBar, isEnabled_1, 1}},
- {876, {wxMenuBar, isEnabled_0, 0}},
- {877, {wxMenuBar, remove, 1}},
- {878, {wxMenuBar, replace, 3}},
- {879, {wxMenuBar, setHelpString, 2}},
- {880, {wxMenuBar, setLabel_2, 2}},
- {881, {wxMenuBar, setLabel_1, 1}},
- {882, {wxMenuBar, setLabelTop, 2}},
- {883, {wxControl, getLabel, 0}},
- {884, {wxControl, setLabel, 1}},
- {885, {wxControlWithItems, append_1, 1}},
- {886, {wxControlWithItems, append_2, 2}},
- {887, {wxControlWithItems, appendStrings_1, 1}},
- {888, {wxControlWithItems, clear, 0}},
- {889, {wxControlWithItems, delete, 1}},
- {890, {wxControlWithItems, findString, 2}},
- {891, {wxControlWithItems, getClientData, 1}},
- {892, {wxControlWithItems, setClientData, 2}},
- {893, {wxControlWithItems, getCount, 0}},
- {894, {wxControlWithItems, getSelection, 0}},
- {895, {wxControlWithItems, getString, 1}},
- {896, {wxControlWithItems, getStringSelection, 0}},
- {897, {wxControlWithItems, insert_2, 2}},
- {898, {wxControlWithItems, insert_3, 3}},
- {899, {wxControlWithItems, isEmpty, 0}},
- {900, {wxControlWithItems, select, 1}},
- {901, {wxControlWithItems, setSelection, 1}},
- {902, {wxControlWithItems, setString, 2}},
- {903, {wxControlWithItems, setStringSelection, 1}},
- {906, {wxMenu, new_2, 2}},
- {907, {wxMenu, new_1, 1}},
- {909, {wxMenu, destruct, 0}},
- {910, {wxMenu, append_3, 3}},
- {911, {wxMenu, append_1, 1}},
- {912, {wxMenu, append_4_0, 4}},
- {913, {wxMenu, append_4_1, 4}},
- {914, {wxMenu, appendCheckItem, 3}},
- {915, {wxMenu, appendRadioItem, 3}},
- {916, {wxMenu, appendSeparator, 0}},
- {917, {wxMenu, break, 0}},
- {918, {wxMenu, check, 2}},
- {919, {wxMenu, delete_1_0, 1}},
- {920, {wxMenu, delete_1_1, 1}},
- {921, {wxMenu, destroy_1_0, 1}},
- {922, {wxMenu, destroy_1_1, 1}},
- {923, {wxMenu, enable, 2}},
- {924, {wxMenu, findItem_1, 1}},
- {925, {wxMenu, findItem_2, 2}},
- {926, {wxMenu, findItemByPosition, 1}},
- {927, {wxMenu, getHelpString, 1}},
- {928, {wxMenu, getLabel, 1}},
- {929, {wxMenu, getMenuItemCount, 0}},
- {930, {wxMenu, getMenuItems, 0}},
- {932, {wxMenu, getTitle, 0}},
- {933, {wxMenu, insert_2, 2}},
- {934, {wxMenu, insert_3, 3}},
- {935, {wxMenu, insert_5_1, 5}},
- {936, {wxMenu, insert_5_0, 5}},
- {937, {wxMenu, insertCheckItem, 4}},
- {938, {wxMenu, insertRadioItem, 4}},
- {939, {wxMenu, insertSeparator, 1}},
- {940, {wxMenu, isChecked, 1}},
- {941, {wxMenu, isEnabled, 1}},
- {942, {wxMenu, prepend_1, 1}},
- {943, {wxMenu, prepend_2, 2}},
- {944, {wxMenu, prepend_4_1, 4}},
- {945, {wxMenu, prepend_4_0, 4}},
- {946, {wxMenu, prependCheckItem, 3}},
- {947, {wxMenu, prependRadioItem, 3}},
- {948, {wxMenu, prependSeparator, 0}},
- {949, {wxMenu, remove_1_0, 1}},
- {950, {wxMenu, remove_1_1, 1}},
- {951, {wxMenu, setHelpString, 2}},
- {952, {wxMenu, setLabel, 2}},
- {953, {wxMenu, setTitle, 1}},
- {954, {wxMenuItem, new, 1}},
- {956, {wxMenuItem, destruct, 0}},
- {957, {wxMenuItem, check, 1}},
- {958, {wxMenuItem, enable, 1}},
- {959, {wxMenuItem, getBitmap, 0}},
- {960, {wxMenuItem, getHelp, 0}},
- {961, {wxMenuItem, getId, 0}},
- {962, {wxMenuItem, getKind, 0}},
- {963, {wxMenuItem, getLabel, 0}},
- {964, {wxMenuItem, getLabelFromText, 1}},
- {965, {wxMenuItem, getMenu, 0}},
- {966, {wxMenuItem, getText, 0}},
- {967, {wxMenuItem, getSubMenu, 0}},
- {968, {wxMenuItem, isCheckable, 0}},
- {969, {wxMenuItem, isChecked, 0}},
- {970, {wxMenuItem, isEnabled, 0}},
- {971, {wxMenuItem, isSeparator, 0}},
- {972, {wxMenuItem, isSubMenu, 0}},
- {973, {wxMenuItem, setBitmap, 1}},
- {974, {wxMenuItem, setHelp, 1}},
- {975, {wxMenuItem, setMenu, 1}},
- {976, {wxMenuItem, setSubMenu, 1}},
- {977, {wxMenuItem, setText, 1}},
- {978, {wxToolBar, addControl, 1}},
- {979, {wxToolBar, addSeparator, 0}},
- {980, {wxToolBar, addTool_5, 5}},
- {981, {wxToolBar, addTool_4_0, 4}},
- {982, {wxToolBar, addTool_1, 1}},
- {983, {wxToolBar, addTool_4_1, 4}},
- {984, {wxToolBar, addTool_3, 3}},
- {985, {wxToolBar, addTool_6, 6}},
- {986, {wxToolBar, addCheckTool, 4}},
- {987, {wxToolBar, addRadioTool, 4}},
- {988, {wxToolBar, addStretchableSpace, 0}},
- {989, {wxToolBar, insertStretchableSpace, 1}},
- {990, {wxToolBar, deleteTool, 1}},
- {991, {wxToolBar, deleteToolByPos, 1}},
- {992, {wxToolBar, enableTool, 2}},
- {993, {wxToolBar, findById, 1}},
- {994, {wxToolBar, findControl, 1}},
- {995, {wxToolBar, findToolForPosition, 2}},
- {996, {wxToolBar, getToolSize, 0}},
- {997, {wxToolBar, getToolBitmapSize, 0}},
- {998, {wxToolBar, getMargins, 0}},
- {999, {wxToolBar, getToolEnabled, 1}},
- {1000, {wxToolBar, getToolLongHelp, 1}},
- {1001, {wxToolBar, getToolPacking, 0}},
- {1002, {wxToolBar, getToolPos, 1}},
- {1003, {wxToolBar, getToolSeparation, 0}},
- {1004, {wxToolBar, getToolShortHelp, 1}},
- {1005, {wxToolBar, getToolState, 1}},
- {1006, {wxToolBar, insertControl, 2}},
- {1007, {wxToolBar, insertSeparator, 1}},
- {1008, {wxToolBar, insertTool_5, 5}},
- {1009, {wxToolBar, insertTool_2, 2}},
- {1010, {wxToolBar, insertTool_4, 4}},
- {1011, {wxToolBar, realize, 0}},
- {1012, {wxToolBar, removeTool, 1}},
- {1013, {wxToolBar, setMargins, 2}},
- {1014, {wxToolBar, setToolBitmapSize, 1}},
- {1015, {wxToolBar, setToolLongHelp, 2}},
- {1016, {wxToolBar, setToolPacking, 1}},
- {1017, {wxToolBar, setToolShortHelp, 2}},
- {1018, {wxToolBar, setToolSeparation, 1}},
- {1019, {wxToolBar, toggleTool, 2}},
- {1021, {wxStatusBar, new_0, 0}},
- {1022, {wxStatusBar, new_2, 2}},
- {1024, {wxStatusBar, destruct, 0}},
- {1025, {wxStatusBar, create, 2}},
- {1026, {wxStatusBar, getFieldRect, 2}},
- {1027, {wxStatusBar, getFieldsCount, 0}},
- {1028, {wxStatusBar, getStatusText, 1}},
- {1029, {wxStatusBar, popStatusText, 1}},
- {1030, {wxStatusBar, pushStatusText, 2}},
- {1031, {wxStatusBar, setFieldsCount, 2}},
- {1032, {wxStatusBar, setMinHeight, 1}},
- {1033, {wxStatusBar, setStatusText, 2}},
- {1034, {wxStatusBar, setStatusWidths, 2}},
- {1035, {wxStatusBar, setStatusStyles, 2}},
- {1036, {wxBitmap, new_0, 0}},
- {1037, {wxBitmap, new_3, 3}},
- {1038, {wxBitmap, new_4, 4}},
- {1039, {wxBitmap, new_2_0, 2}},
- {1040, {wxBitmap, new_2_1, 2}},
- {1041, {wxBitmap, destruct, 0}},
- {1042, {wxBitmap, convertToImage, 0}},
- {1043, {wxBitmap, copyFromIcon, 1}},
- {1044, {wxBitmap, create, 3}},
- {1045, {wxBitmap, getDepth, 0}},
- {1046, {wxBitmap, getHeight, 0}},
- {1047, {wxBitmap, getPalette, 0}},
- {1048, {wxBitmap, getMask, 0}},
- {1049, {wxBitmap, getWidth, 0}},
- {1050, {wxBitmap, getSubBitmap, 1}},
- {1051, {wxBitmap, loadFile, 2}},
- {1052, {wxBitmap, ok, 0}},
- {1053, {wxBitmap, saveFile, 3}},
- {1054, {wxBitmap, setDepth, 1}},
- {1055, {wxBitmap, setHeight, 1}},
- {1056, {wxBitmap, setMask, 1}},
- {1057, {wxBitmap, setPalette, 1}},
- {1058, {wxBitmap, setWidth, 1}},
- {1059, {wxIcon, new_0, 0}},
- {1060, {wxIcon, new_2, 2}},
- {1061, {wxIcon, new_1, 1}},
- {1062, {wxIcon, copyFromBitmap, 1}},
- {1063, {wxIcon, 'Destroy', undefined}},
- {1064, {wxIconBundle, new_0, 0}},
- {1065, {wxIconBundle, new_2, 2}},
- {1066, {wxIconBundle, new_1_0, 1}},
- {1067, {wxIconBundle, new_1_1, 1}},
- {1068, {wxIconBundle, destruct, 0}},
- {1069, {wxIconBundle, addIcon_2, 2}},
- {1070, {wxIconBundle, addIcon_1, 1}},
- {1071, {wxIconBundle, getIcon_1_1, 1}},
- {1072, {wxIconBundle, getIcon_1_0, 1}},
- {1073, {wxCursor, new_0, 0}},
- {1074, {wxCursor, new_1_0, 1}},
- {1075, {wxCursor, new_1_1, 1}},
- {1076, {wxCursor, new_4, 4}},
- {1077, {wxCursor, destruct, 0}},
- {1078, {wxCursor, ok, 0}},
- {1079, {wxMask, new_0, 0}},
- {1080, {wxMask, new_2_1, 2}},
- {1081, {wxMask, new_2_0, 2}},
- {1082, {wxMask, new_1, 1}},
- {1083, {wxMask, destruct, 0}},
- {1084, {wxMask, create_2_1, 2}},
- {1085, {wxMask, create_2_0, 2}},
- {1086, {wxMask, create_1, 1}},
- {1087, {wxImage, new_0, 0}},
- {1088, {wxImage, new_3_0, 3}},
- {1089, {wxImage, new_4, 4}},
- {1090, {wxImage, new_5, 5}},
- {1091, {wxImage, new_2, 2}},
- {1092, {wxImage, new_3_1, 3}},
- {1093, {wxImage, blur, 1}},
- {1094, {wxImage, blurHorizontal, 1}},
- {1095, {wxImage, blurVertical, 1}},
- {1096, {wxImage, convertAlphaToMask, 1}},
- {1097, {wxImage, convertToGreyscale, 1}},
- {1098, {wxImage, convertToMono, 3}},
- {1099, {wxImage, copy, 0}},
- {1100, {wxImage, create_3, 3}},
- {1101, {wxImage, create_4, 4}},
- {1102, {wxImage, create_5, 5}},
- {1103, {wxImage, 'Destroy', 0}},
- {1104, {wxImage, findFirstUnusedColour, 4}},
- {1105, {wxImage, getImageExtWildcard, 0}},
- {1106, {wxImage, getAlpha_2, 2}},
- {1107, {wxImage, getAlpha_0, 0}},
- {1108, {wxImage, getBlue, 2}},
- {1109, {wxImage, getData, 0}},
- {1110, {wxImage, getGreen, 2}},
- {1111, {wxImage, getImageCount, 2}},
- {1112, {wxImage, getHeight, 0}},
- {1113, {wxImage, getMaskBlue, 0}},
- {1114, {wxImage, getMaskGreen, 0}},
- {1115, {wxImage, getMaskRed, 0}},
- {1116, {wxImage, getOrFindMaskColour, 3}},
- {1117, {wxImage, getPalette, 0}},
- {1118, {wxImage, getRed, 2}},
- {1119, {wxImage, getSubImage, 1}},
- {1120, {wxImage, getWidth, 0}},
- {1121, {wxImage, hasAlpha, 0}},
- {1122, {wxImage, hasMask, 0}},
- {1123, {wxImage, getOption, 1}},
- {1124, {wxImage, getOptionInt, 1}},
- {1125, {wxImage, hasOption, 1}},
- {1126, {wxImage, initAlpha, 0}},
- {1127, {wxImage, initStandardHandlers, 0}},
- {1128, {wxImage, isTransparent, 3}},
- {1129, {wxImage, loadFile_2, 2}},
- {1130, {wxImage, loadFile_3, 3}},
- {1131, {wxImage, ok, 0}},
- {1132, {wxImage, removeHandler, 1}},
- {1133, {wxImage, mirror, 1}},
- {1134, {wxImage, replace, 6}},
- {1135, {wxImage, rescale, 3}},
- {1136, {wxImage, resize, 3}},
- {1137, {wxImage, rotate, 3}},
- {1138, {wxImage, rotateHue, 1}},
- {1139, {wxImage, rotate90, 1}},
- {1140, {wxImage, saveFile_1, 1}},
- {1141, {wxImage, saveFile_2_0, 2}},
- {1142, {wxImage, saveFile_2_1, 2}},
- {1143, {wxImage, scale, 3}},
- {1144, {wxImage, size, 3}},
- {1145, {wxImage, setAlpha_3, 3}},
- {1146, {wxImage, setAlpha_2, 2}},
- {1147, {wxImage, setData_2, 2}},
- {1148, {wxImage, setData_4, 4}},
- {1149, {wxImage, setMask, 1}},
- {1150, {wxImage, setMaskColour, 3}},
- {1151, {wxImage, setMaskFromImage, 4}},
- {1152, {wxImage, setOption_2_1, 2}},
- {1153, {wxImage, setOption_2_0, 2}},
- {1154, {wxImage, setPalette, 1}},
- {1155, {wxImage, setRGB_5, 5}},
- {1156, {wxImage, setRGB_4, 4}},
- {1157, {wxImage, 'Destroy', undefined}},
- {1158, {wxBrush, new_0, 0}},
- {1159, {wxBrush, new_2, 2}},
- {1160, {wxBrush, new_1, 1}},
- {1162, {wxBrush, destruct, 0}},
- {1163, {wxBrush, getColour, 0}},
- {1164, {wxBrush, getStipple, 0}},
- {1165, {wxBrush, getStyle, 0}},
- {1166, {wxBrush, isHatch, 0}},
- {1167, {wxBrush, isOk, 0}},
- {1168, {wxBrush, setColour_1, 1}},
- {1169, {wxBrush, setColour_3, 3}},
- {1170, {wxBrush, setStipple, 1}},
- {1171, {wxBrush, setStyle, 1}},
- {1172, {wxPen, new_0, 0}},
- {1173, {wxPen, new_2, 2}},
- {1174, {wxPen, destruct, 0}},
- {1175, {wxPen, getCap, 0}},
- {1176, {wxPen, getColour, 0}},
- {1177, {wxPen, getJoin, 0}},
- {1178, {wxPen, getStyle, 0}},
- {1179, {wxPen, getWidth, 0}},
- {1180, {wxPen, isOk, 0}},
- {1181, {wxPen, setCap, 1}},
- {1182, {wxPen, setColour_1, 1}},
- {1183, {wxPen, setColour_3, 3}},
- {1184, {wxPen, setJoin, 1}},
- {1185, {wxPen, setStyle, 1}},
- {1186, {wxPen, setWidth, 1}},
- {1187, {wxRegion, new_0, 0}},
- {1188, {wxRegion, new_4, 4}},
- {1189, {wxRegion, new_2, 2}},
- {1190, {wxRegion, new_1_1, 1}},
- {1192, {wxRegion, new_1_0, 1}},
- {1194, {wxRegion, destruct, 0}},
- {1195, {wxRegion, clear, 0}},
- {1196, {wxRegion, contains_2, 2}},
- {1197, {wxRegion, contains_1_0, 1}},
- {1198, {wxRegion, contains_4, 4}},
- {1199, {wxRegion, contains_1_1, 1}},
- {1200, {wxRegion, convertToBitmap, 0}},
- {1201, {wxRegion, getBox, 0}},
- {1202, {wxRegion, intersect_4, 4}},
- {1203, {wxRegion, intersect_1_1, 1}},
- {1204, {wxRegion, intersect_1_0, 1}},
- {1205, {wxRegion, isEmpty, 0}},
- {1206, {wxRegion, subtract_4, 4}},
- {1207, {wxRegion, subtract_1_1, 1}},
- {1208, {wxRegion, subtract_1_0, 1}},
- {1209, {wxRegion, offset_2, 2}},
- {1210, {wxRegion, offset_1, 1}},
- {1211, {wxRegion, union_4, 4}},
- {1212, {wxRegion, union_1_2, 1}},
- {1213, {wxRegion, union_1_1, 1}},
- {1214, {wxRegion, union_1_0, 1}},
- {1215, {wxRegion, union_3, 3}},
- {1216, {wxRegion, xor_4, 4}},
- {1217, {wxRegion, xor_1_1, 1}},
- {1218, {wxRegion, xor_1_0, 1}},
- {1219, {wxAcceleratorTable, new_0, 0}},
- {1220, {wxAcceleratorTable, new_2, 2}},
- {1221, {wxAcceleratorTable, destruct, 0}},
- {1222, {wxAcceleratorTable, ok, 0}},
- {1223, {wxAcceleratorEntry, new_1_0, 1}},
- {1224, {wxAcceleratorEntry, new_1_1, 1}},
- {1225, {wxAcceleratorEntry, getCommand, 0}},
- {1226, {wxAcceleratorEntry, getFlags, 0}},
- {1227, {wxAcceleratorEntry, getKeyCode, 0}},
- {1228, {wxAcceleratorEntry, set, 4}},
- {1229, {wxAcceleratorEntry, 'Destroy', undefined}},
- {1234, {wxCaret, new_3, 3}},
- {1235, {wxCaret, new_2, 2}},
- {1237, {wxCaret, destruct, 0}},
- {1238, {wxCaret, create_3, 3}},
- {1239, {wxCaret, create_2, 2}},
- {1240, {wxCaret, getBlinkTime, 0}},
- {1242, {wxCaret, getPosition, 0}},
- {1244, {wxCaret, getSize, 0}},
- {1245, {wxCaret, getWindow, 0}},
- {1246, {wxCaret, hide, 0}},
- {1247, {wxCaret, isOk, 0}},
- {1248, {wxCaret, isVisible, 0}},
- {1249, {wxCaret, move_2, 2}},
- {1250, {wxCaret, move_1, 1}},
- {1251, {wxCaret, setBlinkTime, 1}},
- {1252, {wxCaret, setSize_2, 2}},
- {1253, {wxCaret, setSize_1, 1}},
- {1254, {wxCaret, show, 1}},
- {1255, {wxSizer, add_2_1, 2}},
- {1256, {wxSizer, add_2_0, 2}},
- {1257, {wxSizer, add_3, 3}},
- {1258, {wxSizer, add_2_3, 2}},
- {1259, {wxSizer, add_2_2, 2}},
- {1260, {wxSizer, addSpacer, 1}},
- {1261, {wxSizer, addStretchSpacer, 1}},
- {1262, {wxSizer, calcMin, 0}},
- {1263, {wxSizer, clear, 1}},
- {1264, {wxSizer, detach_1_2, 1}},
- {1265, {wxSizer, detach_1_1, 1}},
- {1266, {wxSizer, detach_1_0, 1}},
- {1267, {wxSizer, fit, 1}},
- {1268, {wxSizer, fitInside, 1}},
- {1269, {wxSizer, getChildren, 0}},
- {1270, {wxSizer, getItem_2_1, 2}},
- {1271, {wxSizer, getItem_2_0, 2}},
- {1272, {wxSizer, getItem_1, 1}},
- {1273, {wxSizer, getSize, 0}},
- {1274, {wxSizer, getPosition, 0}},
- {1275, {wxSizer, getMinSize, 0}},
- {1276, {wxSizer, hide_2_0, 2}},
- {1277, {wxSizer, hide_2_1, 2}},
- {1278, {wxSizer, hide_1, 1}},
- {1279, {wxSizer, insert_3_1, 3}},
- {1280, {wxSizer, insert_3_0, 3}},
- {1281, {wxSizer, insert_4, 4}},
- {1282, {wxSizer, insert_3_3, 3}},
- {1283, {wxSizer, insert_3_2, 3}},
- {1284, {wxSizer, insert_2, 2}},
- {1285, {wxSizer, insertSpacer, 2}},
- {1286, {wxSizer, insertStretchSpacer, 2}},
- {1287, {wxSizer, isShown_1_2, 1}},
- {1288, {wxSizer, isShown_1_1, 1}},
- {1289, {wxSizer, isShown_1_0, 1}},
- {1290, {wxSizer, layout, 0}},
- {1291, {wxSizer, prepend_2_1, 2}},
- {1292, {wxSizer, prepend_2_0, 2}},
- {1293, {wxSizer, prepend_3, 3}},
- {1294, {wxSizer, prepend_2_3, 2}},
- {1295, {wxSizer, prepend_2_2, 2}},
- {1296, {wxSizer, prepend_1, 1}},
- {1297, {wxSizer, prependSpacer, 1}},
- {1298, {wxSizer, prependStretchSpacer, 1}},
- {1299, {wxSizer, recalcSizes, 0}},
- {1300, {wxSizer, remove_1_1, 1}},
- {1301, {wxSizer, remove_1_0, 1}},
- {1302, {wxSizer, replace_3_1, 3}},
- {1303, {wxSizer, replace_3_0, 3}},
- {1304, {wxSizer, replace_2, 2}},
- {1305, {wxSizer, setDimension, 4}},
- {1306, {wxSizer, setMinSize_2, 2}},
- {1307, {wxSizer, setMinSize_1, 1}},
- {1308, {wxSizer, setItemMinSize_3_2, 3}},
- {1309, {wxSizer, setItemMinSize_2_2, 2}},
- {1310, {wxSizer, setItemMinSize_3_1, 3}},
- {1311, {wxSizer, setItemMinSize_2_1, 2}},
- {1312, {wxSizer, setItemMinSize_3_0, 3}},
- {1313, {wxSizer, setItemMinSize_2_0, 2}},
- {1314, {wxSizer, setSizeHints, 1}},
- {1315, {wxSizer, setVirtualSizeHints, 1}},
- {1316, {wxSizer, show_2_2, 2}},
- {1317, {wxSizer, show_2_1, 2}},
- {1318, {wxSizer, show_2_0, 2}},
- {1319, {wxSizer, show_1, 1}},
- {1320, {wxSizerFlags, new, 1}},
- {1321, {wxSizerFlags, align, 1}},
- {1322, {wxSizerFlags, border_2, 2}},
- {1323, {wxSizerFlags, border_1, 1}},
- {1324, {wxSizerFlags, center, 0}},
- {1325, {wxSizerFlags, centre, 0}},
- {1326, {wxSizerFlags, expand, 0}},
- {1327, {wxSizerFlags, left, 0}},
- {1328, {wxSizerFlags, proportion, 1}},
- {1329, {wxSizerFlags, right, 0}},
- {1330, {wxSizerFlags, 'Destroy', undefined}},
- {1331, {wxSizerItem, new_5_1, 5}},
- {1332, {wxSizerItem, new_2_1, 2}},
- {1333, {wxSizerItem, new_5_0, 5}},
- {1334, {wxSizerItem, new_2_0, 2}},
- {1335, {wxSizerItem, new_6, 6}},
- {1336, {wxSizerItem, new_3, 3}},
- {1337, {wxSizerItem, new_0, 0}},
- {1338, {wxSizerItem, destruct, 0}},
- {1339, {wxSizerItem, calcMin, 0}},
- {1340, {wxSizerItem, deleteWindows, 0}},
- {1341, {wxSizerItem, detachSizer, 0}},
- {1342, {wxSizerItem, getBorder, 0}},
- {1343, {wxSizerItem, getFlag, 0}},
- {1344, {wxSizerItem, getMinSize, 0}},
- {1345, {wxSizerItem, getPosition, 0}},
- {1346, {wxSizerItem, getProportion, 0}},
- {1347, {wxSizerItem, getRatio, 0}},
- {1348, {wxSizerItem, getRect, 0}},
- {1349, {wxSizerItem, getSize, 0}},
- {1350, {wxSizerItem, getSizer, 0}},
- {1351, {wxSizerItem, getSpacer, 0}},
- {1352, {wxSizerItem, getUserData, 0}},
- {1353, {wxSizerItem, getWindow, 0}},
- {1354, {wxSizerItem, isSizer, 0}},
- {1355, {wxSizerItem, isShown, 0}},
- {1356, {wxSizerItem, isSpacer, 0}},
- {1357, {wxSizerItem, isWindow, 0}},
- {1358, {wxSizerItem, setBorder, 1}},
- {1359, {wxSizerItem, setDimension, 2}},
- {1360, {wxSizerItem, setFlag, 1}},
- {1361, {wxSizerItem, setInitSize, 2}},
- {1362, {wxSizerItem, setMinSize_1, 1}},
- {1363, {wxSizerItem, setMinSize_2, 2}},
- {1364, {wxSizerItem, setProportion, 1}},
- {1365, {wxSizerItem, setRatio_2, 2}},
- {1366, {wxSizerItem, setRatio_1_1, 1}},
- {1367, {wxSizerItem, setRatio_1_0, 1}},
- {1368, {wxSizerItem, setSizer, 1}},
- {1369, {wxSizerItem, setSpacer_1, 1}},
- {1370, {wxSizerItem, setSpacer_2, 2}},
- {1371, {wxSizerItem, setWindow, 1}},
- {1372, {wxSizerItem, show, 1}},
- {1373, {wxBoxSizer, new, 1}},
- {1374, {wxBoxSizer, getOrientation, 0}},
- {1375, {wxBoxSizer, 'Destroy', undefined}},
- {1376, {wxStaticBoxSizer, new_2, 2}},
- {1377, {wxStaticBoxSizer, new_3, 3}},
- {1378, {wxStaticBoxSizer, getStaticBox, 0}},
- {1379, {wxStaticBoxSizer, 'Destroy', undefined}},
- {1380, {wxGridSizer, new_4, 4}},
- {1381, {wxGridSizer, new_2, 2}},
- {1382, {wxGridSizer, getCols, 0}},
- {1383, {wxGridSizer, getHGap, 0}},
- {1384, {wxGridSizer, getRows, 0}},
- {1385, {wxGridSizer, getVGap, 0}},
- {1386, {wxGridSizer, setCols, 1}},
- {1387, {wxGridSizer, setHGap, 1}},
- {1388, {wxGridSizer, setRows, 1}},
- {1389, {wxGridSizer, setVGap, 1}},
- {1390, {wxGridSizer, 'Destroy', undefined}},
- {1391, {wxFlexGridSizer, new_4, 4}},
- {1392, {wxFlexGridSizer, new_2, 2}},
- {1393, {wxFlexGridSizer, addGrowableCol, 2}},
- {1394, {wxFlexGridSizer, addGrowableRow, 2}},
- {1395, {wxFlexGridSizer, getFlexibleDirection, 0}},
- {1396, {wxFlexGridSizer, getNonFlexibleGrowMode, 0}},
- {1397, {wxFlexGridSizer, removeGrowableCol, 1}},
- {1398, {wxFlexGridSizer, removeGrowableRow, 1}},
- {1399, {wxFlexGridSizer, setFlexibleDirection, 1}},
- {1400, {wxFlexGridSizer, setNonFlexibleGrowMode, 1}},
- {1401, {wxFlexGridSizer, 'Destroy', undefined}},
- {1402, {wxGridBagSizer, new, 1}},
- {1403, {wxGridBagSizer, add_3_2, 3}},
- {1404, {wxGridBagSizer, add_3_1, 3}},
- {1405, {wxGridBagSizer, add_4, 4}},
- {1406, {wxGridBagSizer, add_1_0, 1}},
- {1407, {wxGridBagSizer, add_2_1, 2}},
- {1408, {wxGridBagSizer, add_2_0, 2}},
- {1409, {wxGridBagSizer, add_3_0, 3}},
- {1410, {wxGridBagSizer, add_1_1, 1}},
- {1411, {wxGridBagSizer, calcMin, 0}},
- {1412, {wxGridBagSizer, checkForIntersection_2, 2}},
- {1413, {wxGridBagSizer, checkForIntersection_3, 3}},
- {1414, {wxGridBagSizer, findItem_1_1, 1}},
- {1415, {wxGridBagSizer, findItem_1_0, 1}},
- {1416, {wxGridBagSizer, findItemAtPoint, 1}},
- {1417, {wxGridBagSizer, findItemAtPosition, 1}},
- {1418, {wxGridBagSizer, findItemWithData, 1}},
- {1419, {wxGridBagSizer, getCellSize, 2}},
- {1420, {wxGridBagSizer, getEmptyCellSize, 0}},
- {1421, {wxGridBagSizer, getItemPosition_1_2, 1}},
- {1422, {wxGridBagSizer, getItemPosition_1_1, 1}},
- {1423, {wxGridBagSizer, getItemPosition_1_0, 1}},
- {1424, {wxGridBagSizer, getItemSpan_1_2, 1}},
- {1425, {wxGridBagSizer, getItemSpan_1_1, 1}},
- {1426, {wxGridBagSizer, getItemSpan_1_0, 1}},
- {1427, {wxGridBagSizer, setEmptyCellSize, 1}},
- {1428, {wxGridBagSizer, setItemPosition_2_2, 2}},
- {1429, {wxGridBagSizer, setItemPosition_2_1, 2}},
- {1430, {wxGridBagSizer, setItemPosition_2_0, 2}},
- {1431, {wxGridBagSizer, setItemSpan_2_2, 2}},
- {1432, {wxGridBagSizer, setItemSpan_2_1, 2}},
- {1433, {wxGridBagSizer, setItemSpan_2_0, 2}},
- {1434, {wxGridBagSizer, 'Destroy', undefined}},
- {1435, {wxStdDialogButtonSizer, new, 0}},
- {1436, {wxStdDialogButtonSizer, addButton, 1}},
- {1437, {wxStdDialogButtonSizer, realize, 0}},
- {1438, {wxStdDialogButtonSizer, setAffirmativeButton, 1}},
- {1439, {wxStdDialogButtonSizer, setCancelButton, 1}},
- {1440, {wxStdDialogButtonSizer, setNegativeButton, 1}},
- {1441, {wxStdDialogButtonSizer, 'Destroy', undefined}},
- {1442, {wxFont, new_0, 0}},
- {1443, {wxFont, new_1, 1}},
- {1444, {wxFont, new_5, 5}},
- {1446, {wxFont, destruct, 0}},
- {1447, {wxFont, isFixedWidth, 0}},
- {1448, {wxFont, getDefaultEncoding, 0}},
- {1449, {wxFont, getFaceName, 0}},
- {1450, {wxFont, getFamily, 0}},
- {1451, {wxFont, getNativeFontInfoDesc, 0}},
- {1452, {wxFont, getNativeFontInfoUserDesc, 0}},
- {1453, {wxFont, getPointSize, 0}},
- {1454, {wxFont, getStyle, 0}},
- {1455, {wxFont, getUnderlined, 0}},
- {1456, {wxFont, getWeight, 0}},
- {1457, {wxFont, ok, 0}},
- {1458, {wxFont, setDefaultEncoding, 1}},
- {1459, {wxFont, setFaceName, 1}},
- {1460, {wxFont, setFamily, 1}},
- {1461, {wxFont, setPointSize, 1}},
- {1462, {wxFont, setStyle, 1}},
- {1463, {wxFont, setUnderlined, 1}},
- {1464, {wxFont, setWeight, 1}},
- {1465, {wxToolTip, enable, 1}},
- {1466, {wxToolTip, setDelay, 1}},
- {1467, {wxToolTip, new, 1}},
- {1468, {wxToolTip, setTip, 1}},
- {1469, {wxToolTip, getTip, 0}},
- {1470, {wxToolTip, getWindow, 0}},
- {1471, {wxToolTip, 'Destroy', undefined}},
- {1473, {wxButton, new_3, 3}},
- {1474, {wxButton, new_0, 0}},
- {1475, {wxButton, destruct, 0}},
- {1476, {wxButton, create, 3}},
- {1477, {wxButton, getDefaultSize, 0}},
- {1478, {wxButton, setDefault, 0}},
- {1479, {wxButton, setLabel, 1}},
- {1481, {wxBitmapButton, new_4, 4}},
- {1482, {wxBitmapButton, new_0, 0}},
- {1483, {wxBitmapButton, create, 4}},
- {1484, {wxBitmapButton, getBitmapDisabled, 0}},
- {1486, {wxBitmapButton, getBitmapFocus, 0}},
- {1488, {wxBitmapButton, getBitmapLabel, 0}},
- {1490, {wxBitmapButton, getBitmapSelected, 0}},
- {1492, {wxBitmapButton, setBitmapDisabled, 1}},
- {1493, {wxBitmapButton, setBitmapFocus, 1}},
- {1494, {wxBitmapButton, setBitmapLabel, 1}},
- {1495, {wxBitmapButton, setBitmapSelected, 1}},
- {1496, {wxBitmapButton, 'Destroy', undefined}},
- {1497, {wxToggleButton, new_0, 0}},
- {1498, {wxToggleButton, new_4, 4}},
- {1499, {wxToggleButton, create, 4}},
- {1500, {wxToggleButton, getValue, 0}},
- {1501, {wxToggleButton, setValue, 1}},
- {1502, {wxToggleButton, 'Destroy', undefined}},
- {1503, {wxCalendarCtrl, new_0, 0}},
- {1504, {wxCalendarCtrl, new_3, 3}},
- {1505, {wxCalendarCtrl, create, 3}},
- {1506, {wxCalendarCtrl, destruct, 0}},
- {1507, {wxCalendarCtrl, setDate, 1}},
- {1508, {wxCalendarCtrl, getDate, 0}},
- {1509, {wxCalendarCtrl, enableYearChange, 1}},
- {1510, {wxCalendarCtrl, enableMonthChange, 1}},
- {1511, {wxCalendarCtrl, enableHolidayDisplay, 1}},
- {1512, {wxCalendarCtrl, setHeaderColours, 2}},
- {1513, {wxCalendarCtrl, getHeaderColourFg, 0}},
- {1514, {wxCalendarCtrl, getHeaderColourBg, 0}},
- {1515, {wxCalendarCtrl, setHighlightColours, 2}},
- {1516, {wxCalendarCtrl, getHighlightColourFg, 0}},
- {1517, {wxCalendarCtrl, getHighlightColourBg, 0}},
- {1518, {wxCalendarCtrl, setHolidayColours, 2}},
- {1519, {wxCalendarCtrl, getHolidayColourFg, 0}},
- {1520, {wxCalendarCtrl, getHolidayColourBg, 0}},
- {1521, {wxCalendarCtrl, getAttr, 1}},
- {1522, {wxCalendarCtrl, setAttr, 2}},
- {1523, {wxCalendarCtrl, setHoliday, 1}},
- {1524, {wxCalendarCtrl, resetAttr, 1}},
- {1525, {wxCalendarCtrl, hitTest, 2}},
- {1526, {wxCalendarDateAttr, new_0, 0}},
- {1527, {wxCalendarDateAttr, new_2_1, 2}},
- {1528, {wxCalendarDateAttr, new_2_0, 2}},
- {1529, {wxCalendarDateAttr, setTextColour, 1}},
- {1530, {wxCalendarDateAttr, setBackgroundColour, 1}},
- {1531, {wxCalendarDateAttr, setBorderColour, 1}},
- {1532, {wxCalendarDateAttr, setFont, 1}},
- {1533, {wxCalendarDateAttr, setBorder, 1}},
- {1534, {wxCalendarDateAttr, setHoliday, 1}},
- {1535, {wxCalendarDateAttr, hasTextColour, 0}},
- {1536, {wxCalendarDateAttr, hasBackgroundColour, 0}},
- {1537, {wxCalendarDateAttr, hasBorderColour, 0}},
- {1538, {wxCalendarDateAttr, hasFont, 0}},
- {1539, {wxCalendarDateAttr, hasBorder, 0}},
- {1540, {wxCalendarDateAttr, isHoliday, 0}},
- {1541, {wxCalendarDateAttr, getTextColour, 0}},
- {1542, {wxCalendarDateAttr, getBackgroundColour, 0}},
- {1543, {wxCalendarDateAttr, getBorderColour, 0}},
- {1544, {wxCalendarDateAttr, getFont, 0}},
- {1545, {wxCalendarDateAttr, getBorder, 0}},
- {1546, {wxCalendarDateAttr, 'Destroy', undefined}},
- {1548, {wxCheckBox, new_4, 4}},
- {1549, {wxCheckBox, new_0, 0}},
- {1550, {wxCheckBox, create, 4}},
- {1551, {wxCheckBox, getValue, 0}},
- {1552, {wxCheckBox, get3StateValue, 0}},
- {1553, {wxCheckBox, is3rdStateAllowedForUser, 0}},
- {1554, {wxCheckBox, is3State, 0}},
- {1555, {wxCheckBox, isChecked, 0}},
- {1556, {wxCheckBox, setValue, 1}},
- {1557, {wxCheckBox, set3StateValue, 1}},
- {1558, {wxCheckBox, 'Destroy', undefined}},
- {1559, {wxCheckListBox, new_0, 0}},
- {1561, {wxCheckListBox, new_3, 3}},
- {1562, {wxCheckListBox, check, 2}},
- {1563, {wxCheckListBox, isChecked, 1}},
- {1564, {wxCheckListBox, 'Destroy', undefined}},
- {1567, {wxChoice, new_3, 3}},
- {1568, {wxChoice, new_0, 0}},
- {1570, {wxChoice, destruct, 0}},
- {1572, {wxChoice, create, 6}},
- {1573, {wxChoice, delete, 1}},
- {1574, {wxChoice, getColumns, 0}},
- {1575, {wxChoice, setColumns, 1}},
- {1576, {wxComboBox, new_0, 0}},
- {1578, {wxComboBox, new_3, 3}},
- {1579, {wxComboBox, destruct, 0}},
- {1581, {wxComboBox, create, 7}},
- {1582, {wxComboBox, canCopy, 0}},
- {1583, {wxComboBox, canCut, 0}},
- {1584, {wxComboBox, canPaste, 0}},
- {1585, {wxComboBox, canRedo, 0}},
- {1586, {wxComboBox, canUndo, 0}},
- {1587, {wxComboBox, copy, 0}},
- {1588, {wxComboBox, cut, 0}},
- {1589, {wxComboBox, getInsertionPoint, 0}},
- {1590, {wxComboBox, getLastPosition, 0}},
- {1591, {wxComboBox, getValue, 0}},
- {1592, {wxComboBox, paste, 0}},
- {1593, {wxComboBox, redo, 0}},
- {1594, {wxComboBox, replace, 3}},
- {1595, {wxComboBox, remove, 2}},
- {1596, {wxComboBox, setInsertionPoint, 1}},
- {1597, {wxComboBox, setInsertionPointEnd, 0}},
- {1598, {wxComboBox, setSelection_1, 1}},
- {1599, {wxComboBox, setSelection_2, 2}},
- {1600, {wxComboBox, setValue, 1}},
- {1601, {wxComboBox, undo, 0}},
- {1602, {wxGauge, new_0, 0}},
- {1603, {wxGauge, new_4, 4}},
- {1604, {wxGauge, create, 4}},
- {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}},
+ {126, {wxWindow, dragAcceptFiles, 1}},
+ {127, {wxWindow, enable, 1}},
+ {128, {wxWindow, findFocus, 0}},
+ {129, {wxWindow, findWindow_1_0, 1}},
+ {130, {wxWindow, findWindow_1_1, 1}},
+ {131, {wxWindow, findWindowById, 2}},
+ {132, {wxWindow, findWindowByName, 2}},
+ {133, {wxWindow, findWindowByLabel, 2}},
+ {134, {wxWindow, fit, 0}},
+ {135, {wxWindow, fitInside, 0}},
+ {136, {wxWindow, freeze, 0}},
+ {137, {wxWindow, getAcceleratorTable, 0}},
+ {138, {wxWindow, getBackgroundColour, 0}},
+ {139, {wxWindow, getBackgroundStyle, 0}},
+ {140, {wxWindow, getBestSize, 0}},
+ {142, {wxWindow, getCaret, 0}},
+ {143, {wxWindow, getCapture, 0}},
+ {144, {wxWindow, getCharHeight, 0}},
+ {145, {wxWindow, getCharWidth, 0}},
+ {146, {wxWindow, getChildren, 0}},
+ {149, {wxWindow, getClientSize, 0}},
+ {150, {wxWindow, getContainingSizer, 0}},
+ {151, {wxWindow, getCursor, 0}},
+ {152, {wxWindow, getDropTarget, 0}},
+ {153, {wxWindow, getEventHandler, 0}},
+ {154, {wxWindow, getExtraStyle, 0}},
+ {155, {wxWindow, getFont, 0}},
+ {156, {wxWindow, getForegroundColour, 0}},
+ {157, {wxWindow, getGrandParent, 0}},
+ {158, {wxWindow, getHandle, 0}},
+ {159, {wxWindow, getHelpText, 0}},
+ {160, {wxWindow, getId, 0}},
+ {161, {wxWindow, getLabel, 0}},
+ {162, {wxWindow, getMaxSize, 0}},
+ {163, {wxWindow, getMinSize, 0}},
+ {164, {wxWindow, getName, 0}},
+ {165, {wxWindow, getParent, 0}},
+ {167, {wxWindow, getPosition, 0}},
+ {168, {wxWindow, getRect, 0}},
+ {170, {wxWindow, getScreenPosition, 0}},
+ {171, {wxWindow, getScreenRect, 0}},
+ {172, {wxWindow, getScrollPos, 1}},
+ {173, {wxWindow, getScrollRange, 1}},
+ {174, {wxWindow, getScrollThumb, 1}},
+ {176, {wxWindow, getSize, 0}},
+ {177, {wxWindow, getSizer, 0}},
+ {178, {wxWindow, getTextExtent, 4}},
+ {179, {wxWindow, getToolTip, 0}},
+ {180, {wxWindow, getUpdateRegion, 0}},
+ {182, {wxWindow, getVirtualSize, 0}},
+ {184, {wxWindow, getWindowStyleFlag, 0}},
+ {185, {wxWindow, getWindowVariant, 0}},
+ {186, {wxWindow, hasCapture, 0}},
+ {187, {wxWindow, hasScrollbar, 1}},
+ {188, {wxWindow, hasTransparentBackground, 0}},
+ {189, {wxWindow, hide, 0}},
+ {190, {wxWindow, inheritAttributes, 0}},
+ {191, {wxWindow, initDialog, 0}},
+ {192, {wxWindow, invalidateBestSize, 0}},
+ {193, {wxWindow, isEnabled, 0}},
+ {194, {wxWindow, isExposed_2, 2}},
+ {195, {wxWindow, isExposed_4, 4}},
+ {196, {wxWindow, isExposed_1_0, 1}},
+ {197, {wxWindow, isExposed_1_1, 1}},
+ {198, {wxWindow, isRetained, 0}},
+ {199, {wxWindow, isShown, 0}},
+ {200, {wxWindow, isTopLevel, 0}},
+ {201, {wxWindow, layout, 0}},
+ {202, {wxWindow, lineDown, 0}},
+ {203, {wxWindow, lineUp, 0}},
+ {204, {wxWindow, lower, 0}},
+ {205, {wxWindow, makeModal, 1}},
+ {206, {wxWindow, move_3, 3}},
+ {207, {wxWindow, move_2, 2}},
+ {208, {wxWindow, moveAfterInTabOrder, 1}},
+ {209, {wxWindow, moveBeforeInTabOrder, 1}},
+ {210, {wxWindow, navigate, 1}},
+ {211, {wxWindow, pageDown, 0}},
+ {212, {wxWindow, pageUp, 0}},
+ {213, {wxWindow, popEventHandler, 1}},
+ {214, {wxWindow, popupMenu_2, 2}},
+ {215, {wxWindow, popupMenu_3, 3}},
+ {216, {wxWindow, raise, 0}},
+ {217, {wxWindow, refresh, 1}},
+ {218, {wxWindow, refreshRect, 2}},
+ {219, {wxWindow, releaseMouse, 0}},
+ {220, {wxWindow, removeChild, 1}},
+ {221, {wxWindow, reparent, 1}},
+ {222, {wxWindow, screenToClient_2, 2}},
+ {223, {wxWindow, screenToClient_1, 1}},
+ {225, {wxWindow, scrollLines, 1}},
+ {227, {wxWindow, scrollPages, 1}},
+ {228, {wxWindow, scrollWindow, 3}},
+ {229, {wxWindow, setAcceleratorTable, 1}},
+ {230, {wxWindow, setAutoLayout, 1}},
+ {231, {wxWindow, setBackgroundColour, 1}},
+ {232, {wxWindow, setBackgroundStyle, 1}},
+ {233, {wxWindow, setCaret, 1}},
+ {234, {wxWindow, setClientSize_2, 2}},
+ {235, {wxWindow, setClientSize_1_0, 1}},
+ {236, {wxWindow, setClientSize_1_1, 1}},
+ {237, {wxWindow, setContainingSizer, 1}},
+ {238, {wxWindow, setCursor, 1}},
+ {239, {wxWindow, setMaxSize, 1}},
+ {240, {wxWindow, setMinSize, 1}},
+ {241, {wxWindow, setOwnBackgroundColour, 1}},
+ {242, {wxWindow, setOwnFont, 1}},
+ {243, {wxWindow, setOwnForegroundColour, 1}},
+ {244, {wxWindow, setDropTarget, 1}},
+ {245, {wxWindow, setExtraStyle, 1}},
+ {246, {wxWindow, setFocus, 0}},
+ {247, {wxWindow, setFocusFromKbd, 0}},
+ {248, {wxWindow, setFont, 1}},
+ {249, {wxWindow, setForegroundColour, 1}},
+ {250, {wxWindow, setHelpText, 1}},
+ {251, {wxWindow, setId, 1}},
+ {253, {wxWindow, setLabel, 1}},
+ {254, {wxWindow, setName, 1}},
+ {255, {wxWindow, setPalette, 1}},
+ {256, {wxWindow, setScrollbar, 5}},
+ {257, {wxWindow, setScrollPos, 3}},
+ {258, {wxWindow, setSize_5, 5}},
+ {259, {wxWindow, setSize_2_0, 2}},
+ {260, {wxWindow, setSize_1, 1}},
+ {261, {wxWindow, setSize_2_1, 2}},
+ {262, {wxWindow, setSizeHints_3, 3}},
+ {263, {wxWindow, setSizeHints_2, 2}},
+ {264, {wxWindow, setSizer, 2}},
+ {265, {wxWindow, setSizerAndFit, 2}},
+ {266, {wxWindow, setThemeEnabled, 1}},
+ {267, {wxWindow, setToolTip_1_0, 1}},
+ {268, {wxWindow, setToolTip_1_1, 1}},
+ {269, {wxWindow, setVirtualSize_1, 1}},
+ {270, {wxWindow, setVirtualSize_2, 2}},
+ {271, {wxWindow, setVirtualSizeHints_3, 3}},
+ {272, {wxWindow, setVirtualSizeHints_2, 2}},
+ {273, {wxWindow, setWindowStyle, 1}},
+ {274, {wxWindow, setWindowStyleFlag, 1}},
+ {275, {wxWindow, setWindowVariant, 1}},
+ {276, {wxWindow, shouldInheritColours, 0}},
+ {277, {wxWindow, show, 1}},
+ {278, {wxWindow, thaw, 0}},
+ {279, {wxWindow, transferDataFromWindow, 0}},
+ {280, {wxWindow, transferDataToWindow, 0}},
+ {281, {wxWindow, update, 0}},
+ {282, {wxWindow, updateWindowUI, 1}},
+ {283, {wxWindow, validate, 0}},
+ {284, {wxWindow, warpPointer, 2}},
+ {285, {wxWindow, setTransparent, 1}},
+ {286, {wxWindow, canSetTransparent, 0}},
+ {287, {wxWindow, isDoubleBuffered, 0}},
+ {288, {wxWindow, setDoubleBuffered, 1}},
+ {289, {wxWindow, getContentScaleFactor, 0}},
+ {290, {wxTopLevelWindow, getIcon, 0}},
+ {291, {wxTopLevelWindow, getIcons, 0}},
+ {292, {wxTopLevelWindow, getTitle, 0}},
+ {293, {wxTopLevelWindow, isActive, 0}},
+ {294, {wxTopLevelWindow, iconize, 1}},
+ {295, {wxTopLevelWindow, isFullScreen, 0}},
+ {296, {wxTopLevelWindow, isIconized, 0}},
+ {297, {wxTopLevelWindow, isMaximized, 0}},
+ {298, {wxTopLevelWindow, maximize, 1}},
+ {299, {wxTopLevelWindow, requestUserAttention, 1}},
+ {300, {wxTopLevelWindow, setIcon, 1}},
+ {301, {wxTopLevelWindow, setIcons, 1}},
+ {302, {wxTopLevelWindow, centerOnScreen, 1}},
+ {303, {wxTopLevelWindow, centreOnScreen, 1}},
+ {305, {wxTopLevelWindow, setShape, 1}},
+ {306, {wxTopLevelWindow, setTitle, 1}},
+ {307, {wxTopLevelWindow, showFullScreen, 2}},
+ {309, {wxFrame, new_4, 4}},
+ {310, {wxFrame, new_0, 0}},
+ {312, {wxFrame, destruct, 0}},
+ {313, {wxFrame, create, 4}},
+ {314, {wxFrame, createStatusBar, 1}},
+ {315, {wxFrame, createToolBar, 1}},
+ {316, {wxFrame, getClientAreaOrigin, 0}},
+ {317, {wxFrame, getMenuBar, 0}},
+ {318, {wxFrame, getStatusBar, 0}},
+ {319, {wxFrame, getStatusBarPane, 0}},
+ {320, {wxFrame, getToolBar, 0}},
+ {321, {wxFrame, processCommand, 1}},
+ {322, {wxFrame, sendSizeEvent, 0}},
+ {323, {wxFrame, setMenuBar, 1}},
+ {324, {wxFrame, setStatusBar, 1}},
+ {325, {wxFrame, setStatusBarPane, 1}},
+ {326, {wxFrame, setStatusText, 2}},
+ {327, {wxFrame, setStatusWidths, 2}},
+ {328, {wxFrame, setToolBar, 1}},
+ {329, {wxMiniFrame, new_0, 0}},
+ {330, {wxMiniFrame, new_4, 4}},
+ {331, {wxMiniFrame, create, 4}},
+ {332, {wxMiniFrame, 'Destroy', undefined}},
+ {333, {wxSplashScreen, new_0, 0}},
+ {334, {wxSplashScreen, new_6, 6}},
+ {335, {wxSplashScreen, destruct, 0}},
+ {336, {wxSplashScreen, getSplashStyle, 0}},
+ {337, {wxSplashScreen, getTimeout, 0}},
+ {338, {wxPanel, new_0, 0}},
+ {339, {wxPanel, new_6, 6}},
+ {340, {wxPanel, new_2, 2}},
+ {341, {wxPanel, destruct, 0}},
+ {342, {wxPanel, initDialog, 0}},
+ {343, {wxPanel, setFocusIgnoringChildren, 0}},
+ {344, {wxScrolledWindow, new_0, 0}},
+ {345, {wxScrolledWindow, new_2, 2}},
+ {346, {wxScrolledWindow, destruct, 0}},
+ {347, {wxScrolledWindow, calcScrolledPosition_4, 4}},
+ {348, {wxScrolledWindow, calcScrolledPosition_1, 1}},
+ {349, {wxScrolledWindow, calcUnscrolledPosition_4, 4}},
+ {350, {wxScrolledWindow, calcUnscrolledPosition_1, 1}},
+ {351, {wxScrolledWindow, enableScrolling, 2}},
+ {352, {wxScrolledWindow, getScrollPixelsPerUnit, 2}},
+ {353, {wxScrolledWindow, getViewStart, 2}},
+ {354, {wxScrolledWindow, doPrepareDC, 1}},
+ {355, {wxScrolledWindow, prepareDC, 1}},
+ {356, {wxScrolledWindow, scroll, 2}},
+ {357, {wxScrolledWindow, setScrollbars, 5}},
+ {358, {wxScrolledWindow, setScrollRate, 2}},
+ {359, {wxScrolledWindow, setTargetWindow, 1}},
+ {360, {wxSashWindow, new_0, 0}},
+ {361, {wxSashWindow, new_2, 2}},
+ {362, {wxSashWindow, destruct, 0}},
+ {363, {wxSashWindow, getSashVisible, 1}},
+ {364, {wxSashWindow, getMaximumSizeX, 0}},
+ {365, {wxSashWindow, getMaximumSizeY, 0}},
+ {366, {wxSashWindow, getMinimumSizeX, 0}},
+ {367, {wxSashWindow, getMinimumSizeY, 0}},
+ {368, {wxSashWindow, setMaximumSizeX, 1}},
+ {369, {wxSashWindow, setMaximumSizeY, 1}},
+ {370, {wxSashWindow, setMinimumSizeX, 1}},
+ {371, {wxSashWindow, setMinimumSizeY, 1}},
+ {372, {wxSashWindow, setSashVisible, 2}},
+ {373, {wxSashLayoutWindow, new_0, 0}},
+ {374, {wxSashLayoutWindow, new_2, 2}},
+ {375, {wxSashLayoutWindow, create, 2}},
+ {376, {wxSashLayoutWindow, getAlignment, 0}},
+ {377, {wxSashLayoutWindow, getOrientation, 0}},
+ {378, {wxSashLayoutWindow, setAlignment, 1}},
+ {379, {wxSashLayoutWindow, setDefaultSize, 1}},
+ {380, {wxSashLayoutWindow, setOrientation, 1}},
+ {381, {wxSashLayoutWindow, 'Destroy', undefined}},
+ {382, {wxGrid, new_0, 0}},
+ {383, {wxGrid, new_3, 3}},
+ {384, {wxGrid, new_4, 4}},
+ {385, {wxGrid, destruct, 0}},
+ {386, {wxGrid, appendCols, 1}},
+ {387, {wxGrid, appendRows, 1}},
+ {388, {wxGrid, autoSize, 0}},
+ {389, {wxGrid, autoSizeColumn, 2}},
+ {390, {wxGrid, autoSizeColumns, 1}},
+ {391, {wxGrid, autoSizeRow, 2}},
+ {392, {wxGrid, autoSizeRows, 1}},
+ {393, {wxGrid, beginBatch, 0}},
+ {394, {wxGrid, blockToDeviceRect, 2}},
+ {395, {wxGrid, canDragColSize, 0}},
+ {396, {wxGrid, canDragRowSize, 0}},
+ {397, {wxGrid, canDragGridSize, 0}},
+ {398, {wxGrid, canEnableCellControl, 0}},
+ {399, {wxGrid, cellToRect_2, 2}},
+ {400, {wxGrid, cellToRect_1, 1}},
+ {401, {wxGrid, clearGrid, 0}},
+ {402, {wxGrid, clearSelection, 0}},
+ {403, {wxGrid, createGrid, 3}},
+ {404, {wxGrid, deleteCols, 1}},
+ {405, {wxGrid, deleteRows, 1}},
+ {406, {wxGrid, disableCellEditControl, 0}},
+ {407, {wxGrid, disableDragColSize, 0}},
+ {408, {wxGrid, disableDragGridSize, 0}},
+ {409, {wxGrid, disableDragRowSize, 0}},
+ {410, {wxGrid, enableCellEditControl, 1}},
+ {411, {wxGrid, enableDragColSize, 1}},
+ {412, {wxGrid, enableDragGridSize, 1}},
+ {413, {wxGrid, enableDragRowSize, 1}},
+ {414, {wxGrid, enableEditing, 1}},
+ {415, {wxGrid, enableGridLines, 1}},
+ {416, {wxGrid, endBatch, 0}},
+ {417, {wxGrid, fit, 0}},
+ {418, {wxGrid, forceRefresh, 0}},
+ {419, {wxGrid, getBatchCount, 0}},
+ {420, {wxGrid, getCellAlignment, 4}},
+ {421, {wxGrid, getCellBackgroundColour, 2}},
+ {422, {wxGrid, getCellEditor, 2}},
+ {423, {wxGrid, getCellFont, 2}},
+ {424, {wxGrid, getCellRenderer, 2}},
+ {425, {wxGrid, getCellTextColour, 2}},
+ {426, {wxGrid, getCellValue_2, 2}},
+ {427, {wxGrid, getCellValue_1, 1}},
+ {428, {wxGrid, getColLabelAlignment, 2}},
+ {429, {wxGrid, getColLabelSize, 0}},
+ {430, {wxGrid, getColLabelValue, 1}},
+ {431, {wxGrid, getColMinimalAcceptableWidth, 0}},
+ {432, {wxGrid, getDefaultCellAlignment, 2}},
+ {433, {wxGrid, getDefaultCellBackgroundColour, 0}},
+ {434, {wxGrid, getDefaultCellFont, 0}},
+ {435, {wxGrid, getDefaultCellTextColour, 0}},
+ {436, {wxGrid, getDefaultColLabelSize, 0}},
+ {437, {wxGrid, getDefaultColSize, 0}},
+ {438, {wxGrid, getDefaultEditor, 0}},
+ {439, {wxGrid, getDefaultEditorForCell_2, 2}},
+ {440, {wxGrid, getDefaultEditorForCell_1, 1}},
+ {441, {wxGrid, getDefaultEditorForType, 1}},
+ {442, {wxGrid, getDefaultRenderer, 0}},
+ {443, {wxGrid, getDefaultRendererForCell, 2}},
+ {444, {wxGrid, getDefaultRendererForType, 1}},
+ {445, {wxGrid, getDefaultRowLabelSize, 0}},
+ {446, {wxGrid, getDefaultRowSize, 0}},
+ {447, {wxGrid, getGridCursorCol, 0}},
+ {448, {wxGrid, getGridCursorRow, 0}},
+ {449, {wxGrid, getGridLineColour, 0}},
+ {450, {wxGrid, gridLinesEnabled, 0}},
+ {451, {wxGrid, getLabelBackgroundColour, 0}},
+ {452, {wxGrid, getLabelFont, 0}},
+ {453, {wxGrid, getLabelTextColour, 0}},
+ {454, {wxGrid, getNumberCols, 0}},
+ {455, {wxGrid, getNumberRows, 0}},
+ {456, {wxGrid, getOrCreateCellAttr, 2}},
+ {457, {wxGrid, getRowMinimalAcceptableHeight, 0}},
+ {458, {wxGrid, getRowLabelAlignment, 2}},
+ {459, {wxGrid, getRowLabelSize, 0}},
+ {460, {wxGrid, getRowLabelValue, 1}},
+ {461, {wxGrid, getRowSize, 1}},
+ {462, {wxGrid, getScrollLineX, 0}},
+ {463, {wxGrid, getScrollLineY, 0}},
+ {464, {wxGrid, getSelectedCells, 0}},
+ {465, {wxGrid, getSelectedCols, 0}},
+ {466, {wxGrid, getSelectedRows, 0}},
+ {467, {wxGrid, getSelectionBackground, 0}},
+ {468, {wxGrid, getSelectionBlockTopLeft, 0}},
+ {469, {wxGrid, getSelectionBlockBottomRight, 0}},
+ {470, {wxGrid, getSelectionForeground, 0}},
+ {471, {wxGrid, getViewWidth, 0}},
+ {472, {wxGrid, getGridWindow, 0}},
+ {473, {wxGrid, getGridRowLabelWindow, 0}},
+ {474, {wxGrid, getGridColLabelWindow, 0}},
+ {475, {wxGrid, getGridCornerLabelWindow, 0}},
+ {476, {wxGrid, hideCellEditControl, 0}},
+ {477, {wxGrid, insertCols, 1}},
+ {478, {wxGrid, insertRows, 1}},
+ {479, {wxGrid, isCellEditControlEnabled, 0}},
+ {480, {wxGrid, isCurrentCellReadOnly, 0}},
+ {481, {wxGrid, isEditable, 0}},
+ {482, {wxGrid, isInSelection_2, 2}},
+ {483, {wxGrid, isInSelection_1, 1}},
+ {484, {wxGrid, isReadOnly, 2}},
+ {485, {wxGrid, isSelection, 0}},
+ {486, {wxGrid, isVisible_3, 3}},
+ {487, {wxGrid, isVisible_2, 2}},
+ {488, {wxGrid, makeCellVisible_2, 2}},
+ {489, {wxGrid, makeCellVisible_1, 1}},
+ {490, {wxGrid, moveCursorDown, 1}},
+ {491, {wxGrid, moveCursorLeft, 1}},
+ {492, {wxGrid, moveCursorRight, 1}},
+ {493, {wxGrid, moveCursorUp, 1}},
+ {494, {wxGrid, moveCursorDownBlock, 1}},
+ {495, {wxGrid, moveCursorLeftBlock, 1}},
+ {496, {wxGrid, moveCursorRightBlock, 1}},
+ {497, {wxGrid, moveCursorUpBlock, 1}},
+ {498, {wxGrid, movePageDown, 0}},
+ {499, {wxGrid, movePageUp, 0}},
+ {500, {wxGrid, registerDataType, 3}},
+ {501, {wxGrid, saveEditControlValue, 0}},
+ {502, {wxGrid, selectAll, 0}},
+ {503, {wxGrid, selectBlock_5, 5}},
+ {504, {wxGrid, selectBlock_3, 3}},
+ {505, {wxGrid, selectCol, 2}},
+ {506, {wxGrid, selectRow, 2}},
+ {507, {wxGrid, setCellAlignment_4, 4}},
+ {508, {wxGrid, setCellAlignment_3, 3}},
+ {509, {wxGrid, setCellAlignment_1, 1}},
+ {510, {wxGrid, setCellBackgroundColour_3_0, 3}},
+ {511, {wxGrid, setCellBackgroundColour_1, 1}},
+ {512, {wxGrid, setCellBackgroundColour_3_1, 3}},
+ {513, {wxGrid, setCellEditor, 3}},
+ {514, {wxGrid, setCellFont, 3}},
+ {515, {wxGrid, setCellRenderer, 3}},
+ {516, {wxGrid, setCellTextColour_3_0, 3}},
+ {517, {wxGrid, setCellTextColour_3_1, 3}},
+ {518, {wxGrid, setCellTextColour_1, 1}},
+ {519, {wxGrid, setCellValue_3_0, 3}},
+ {520, {wxGrid, setCellValue_2, 2}},
+ {521, {wxGrid, setCellValue_3_1, 3}},
+ {522, {wxGrid, setColAttr, 2}},
+ {523, {wxGrid, setColFormatBool, 1}},
+ {524, {wxGrid, setColFormatNumber, 1}},
+ {525, {wxGrid, setColFormatFloat, 2}},
+ {526, {wxGrid, setColFormatCustom, 2}},
+ {527, {wxGrid, setColLabelAlignment, 2}},
+ {528, {wxGrid, setColLabelSize, 1}},
+ {529, {wxGrid, setColLabelValue, 2}},
+ {530, {wxGrid, setColMinimalWidth, 2}},
+ {531, {wxGrid, setColMinimalAcceptableWidth, 1}},
+ {532, {wxGrid, setColSize, 2}},
+ {533, {wxGrid, setDefaultCellAlignment, 2}},
+ {534, {wxGrid, setDefaultCellBackgroundColour, 1}},
+ {535, {wxGrid, setDefaultCellFont, 1}},
+ {536, {wxGrid, setDefaultCellTextColour, 1}},
+ {537, {wxGrid, setDefaultEditor, 1}},
+ {538, {wxGrid, setDefaultRenderer, 1}},
+ {539, {wxGrid, setDefaultColSize, 2}},
+ {540, {wxGrid, setDefaultRowSize, 2}},
+ {541, {wxGrid, setGridCursor, 2}},
+ {542, {wxGrid, setGridLineColour, 1}},
+ {543, {wxGrid, setLabelBackgroundColour, 1}},
+ {544, {wxGrid, setLabelFont, 1}},
+ {545, {wxGrid, setLabelTextColour, 1}},
+ {546, {wxGrid, setMargins, 2}},
+ {547, {wxGrid, setReadOnly, 3}},
+ {548, {wxGrid, setRowAttr, 2}},
+ {549, {wxGrid, setRowLabelAlignment, 2}},
+ {550, {wxGrid, setRowLabelSize, 1}},
+ {551, {wxGrid, setRowLabelValue, 2}},
+ {552, {wxGrid, setRowMinimalHeight, 2}},
+ {553, {wxGrid, setRowMinimalAcceptableHeight, 1}},
+ {554, {wxGrid, setRowSize, 2}},
+ {555, {wxGrid, setScrollLineX, 1}},
+ {556, {wxGrid, setScrollLineY, 1}},
+ {557, {wxGrid, setSelectionBackground, 1}},
+ {558, {wxGrid, setSelectionForeground, 1}},
+ {559, {wxGrid, setSelectionMode, 1}},
+ {560, {wxGrid, showCellEditControl, 0}},
+ {561, {wxGrid, xToCol, 2}},
+ {562, {wxGrid, xToEdgeOfCol, 1}},
+ {563, {wxGrid, yToEdgeOfRow, 1}},
+ {564, {wxGrid, yToRow, 1}},
+ {565, {wxGridCellRenderer, draw, 7}},
+ {566, {wxGridCellRenderer, getBestSize, 5}},
+ {567, {wxGridCellEditor, create, 3}},
+ {568, {wxGridCellEditor, isCreated, 0}},
+ {569, {wxGridCellEditor, setSize, 1}},
+ {570, {wxGridCellEditor, show, 2}},
+ {571, {wxGridCellEditor, paintBackground, 2}},
+ {572, {wxGridCellEditor, beginEdit, 3}},
+ {573, {wxGridCellEditor, endEdit, 3}},
+ {574, {wxGridCellEditor, reset, 0}},
+ {575, {wxGridCellEditor, startingKey, 1}},
+ {576, {wxGridCellEditor, startingClick, 0}},
+ {577, {wxGridCellEditor, handleReturn, 1}},
+ {578, {wxGridCellBoolRenderer, new, 0}},
+ {579, {wxGridCellBoolRenderer, 'Destroy', undefined}},
+ {580, {wxGridCellBoolEditor, new, 0}},
+ {581, {wxGridCellBoolEditor, isTrueValue, 1}},
+ {582, {wxGridCellBoolEditor, useStringValues, 1}},
+ {583, {wxGridCellBoolEditor, 'Destroy', undefined}},
+ {584, {wxGridCellFloatRenderer, new, 1}},
+ {585, {wxGridCellFloatRenderer, getPrecision, 0}},
+ {586, {wxGridCellFloatRenderer, getWidth, 0}},
+ {587, {wxGridCellFloatRenderer, setParameters, 1}},
+ {588, {wxGridCellFloatRenderer, setPrecision, 1}},
+ {589, {wxGridCellFloatRenderer, setWidth, 1}},
+ {590, {wxGridCellFloatRenderer, 'Destroy', undefined}},
+ {591, {wxGridCellFloatEditor, new, 1}},
+ {592, {wxGridCellFloatEditor, setParameters, 1}},
+ {593, {wxGridCellFloatEditor, 'Destroy', undefined}},
+ {594, {wxGridCellStringRenderer, new, 0}},
+ {595, {wxGridCellStringRenderer, 'Destroy', undefined}},
+ {596, {wxGridCellTextEditor, new, 0}},
+ {597, {wxGridCellTextEditor, setParameters, 1}},
+ {598, {wxGridCellTextEditor, 'Destroy', undefined}},
+ {600, {wxGridCellChoiceEditor, new, 2}},
+ {601, {wxGridCellChoiceEditor, setParameters, 1}},
+ {602, {wxGridCellChoiceEditor, 'Destroy', undefined}},
+ {603, {wxGridCellNumberRenderer, new, 0}},
+ {604, {wxGridCellNumberRenderer, 'Destroy', undefined}},
+ {605, {wxGridCellNumberEditor, new, 1}},
+ {606, {wxGridCellNumberEditor, getValue, 0}},
+ {607, {wxGridCellNumberEditor, setParameters, 1}},
+ {608, {wxGridCellNumberEditor, 'Destroy', undefined}},
+ {609, {wxGridCellAttr, setTextColour, 1}},
+ {610, {wxGridCellAttr, setBackgroundColour, 1}},
+ {611, {wxGridCellAttr, setFont, 1}},
+ {612, {wxGridCellAttr, setAlignment, 2}},
+ {613, {wxGridCellAttr, setReadOnly, 1}},
+ {614, {wxGridCellAttr, setRenderer, 1}},
+ {615, {wxGridCellAttr, setEditor, 1}},
+ {616, {wxGridCellAttr, hasTextColour, 0}},
+ {617, {wxGridCellAttr, hasBackgroundColour, 0}},
+ {618, {wxGridCellAttr, hasFont, 0}},
+ {619, {wxGridCellAttr, hasAlignment, 0}},
+ {620, {wxGridCellAttr, hasRenderer, 0}},
+ {621, {wxGridCellAttr, hasEditor, 0}},
+ {622, {wxGridCellAttr, getTextColour, 0}},
+ {623, {wxGridCellAttr, getBackgroundColour, 0}},
+ {624, {wxGridCellAttr, getFont, 0}},
+ {625, {wxGridCellAttr, getAlignment, 2}},
+ {626, {wxGridCellAttr, getRenderer, 3}},
+ {627, {wxGridCellAttr, getEditor, 3}},
+ {628, {wxGridCellAttr, isReadOnly, 0}},
+ {629, {wxGridCellAttr, setDefAttr, 1}},
+ {630, {wxDC, blit, 5}},
+ {631, {wxDC, calcBoundingBox, 2}},
+ {632, {wxDC, clear, 0}},
+ {633, {wxDC, computeScaleAndOrigin, 0}},
+ {634, {wxDC, crossHair, 1}},
+ {635, {wxDC, destroyClippingRegion, 0}},
+ {636, {wxDC, deviceToLogicalX, 1}},
+ {637, {wxDC, deviceToLogicalXRel, 1}},
+ {638, {wxDC, deviceToLogicalY, 1}},
+ {639, {wxDC, deviceToLogicalYRel, 1}},
+ {640, {wxDC, drawArc, 3}},
+ {641, {wxDC, drawBitmap, 3}},
+ {642, {wxDC, drawCheckMark, 1}},
+ {643, {wxDC, drawCircle, 2}},
+ {645, {wxDC, drawEllipse_2, 2}},
+ {646, {wxDC, drawEllipse_1, 1}},
+ {647, {wxDC, drawEllipticArc, 4}},
+ {648, {wxDC, drawIcon, 2}},
+ {649, {wxDC, drawLabel, 3}},
+ {650, {wxDC, drawLine, 2}},
+ {651, {wxDC, drawLines, 3}},
+ {653, {wxDC, drawPolygon, 3}},
+ {655, {wxDC, drawPoint, 1}},
+ {657, {wxDC, drawRectangle_2, 2}},
+ {658, {wxDC, drawRectangle_1, 1}},
+ {659, {wxDC, drawRotatedText, 3}},
+ {661, {wxDC, drawRoundedRectangle_3, 3}},
+ {662, {wxDC, drawRoundedRectangle_2, 2}},
+ {663, {wxDC, drawText, 2}},
+ {664, {wxDC, endDoc, 0}},
+ {665, {wxDC, endPage, 0}},
+ {666, {wxDC, floodFill, 3}},
+ {667, {wxDC, getBackground, 0}},
+ {668, {wxDC, getBackgroundMode, 0}},
+ {669, {wxDC, getBrush, 0}},
+ {670, {wxDC, getCharHeight, 0}},
+ {671, {wxDC, getCharWidth, 0}},
+ {672, {wxDC, getClippingBox, 4}},
+ {674, {wxDC, getFont, 0}},
+ {675, {wxDC, getLayoutDirection, 0}},
+ {676, {wxDC, getLogicalFunction, 0}},
+ {677, {wxDC, getMapMode, 0}},
+ {678, {wxDC, getMultiLineTextExtent_4, 4}},
+ {679, {wxDC, getMultiLineTextExtent_1, 1}},
+ {680, {wxDC, getPartialTextExtents, 2}},
+ {681, {wxDC, getPen, 0}},
+ {682, {wxDC, getPixel, 2}},
+ {683, {wxDC, getPPI, 0}},
+ {685, {wxDC, getSize, 0}},
+ {687, {wxDC, getSizeMM, 0}},
+ {688, {wxDC, getTextBackground, 0}},
+ {689, {wxDC, getTextExtent_4, 4}},
+ {690, {wxDC, getTextExtent_1, 1}},
+ {692, {wxDC, getTextForeground, 0}},
+ {693, {wxDC, getUserScale, 2}},
+ {694, {wxDC, gradientFillConcentric_3, 3}},
+ {695, {wxDC, gradientFillConcentric_4, 4}},
+ {696, {wxDC, gradientFillLinear, 4}},
+ {697, {wxDC, logicalToDeviceX, 1}},
+ {698, {wxDC, logicalToDeviceXRel, 1}},
+ {699, {wxDC, logicalToDeviceY, 1}},
+ {700, {wxDC, logicalToDeviceYRel, 1}},
+ {701, {wxDC, maxX, 0}},
+ {702, {wxDC, maxY, 0}},
+ {703, {wxDC, minX, 0}},
+ {704, {wxDC, minY, 0}},
+ {705, {wxDC, isOk, 0}},
+ {706, {wxDC, resetBoundingBox, 0}},
+ {707, {wxDC, setAxisOrientation, 2}},
+ {708, {wxDC, setBackground, 1}},
+ {709, {wxDC, setBackgroundMode, 1}},
+ {710, {wxDC, setBrush, 1}},
+ {712, {wxDC, setClippingRegion_2, 2}},
+ {713, {wxDC, setClippingRegion_1_1, 1}},
+ {714, {wxDC, setClippingRegion_1_0, 1}},
+ {715, {wxDC, setDeviceOrigin, 2}},
+ {716, {wxDC, setFont, 1}},
+ {717, {wxDC, setLayoutDirection, 1}},
+ {718, {wxDC, setLogicalFunction, 1}},
+ {719, {wxDC, setMapMode, 1}},
+ {720, {wxDC, setPalette, 1}},
+ {721, {wxDC, setPen, 1}},
+ {722, {wxDC, setTextBackground, 1}},
+ {723, {wxDC, setTextForeground, 1}},
+ {724, {wxDC, setUserScale, 2}},
+ {725, {wxDC, startDoc, 1}},
+ {726, {wxDC, startPage, 0}},
+ {727, {wxMirrorDC, new, 2}},
+ {728, {wxMirrorDC, 'Destroy', undefined}},
+ {729, {wxScreenDC, new, 0}},
+ {730, {wxScreenDC, destruct, 0}},
+ {731, {wxPostScriptDC, new_0, 0}},
+ {732, {wxPostScriptDC, new_1, 1}},
+ {733, {wxPostScriptDC, destruct, 0}},
+ {734, {wxPostScriptDC, setResolution, 1}},
+ {735, {wxPostScriptDC, getResolution, 0}},
+ {736, {wxWindowDC, new_0, 0}},
+ {737, {wxWindowDC, new_1, 1}},
+ {738, {wxWindowDC, destruct, 0}},
+ {739, {wxClientDC, new_0, 0}},
+ {740, {wxClientDC, new_1, 1}},
+ {741, {wxClientDC, 'Destroy', undefined}},
+ {742, {wxPaintDC, new_0, 0}},
+ {743, {wxPaintDC, new_1, 1}},
+ {744, {wxPaintDC, 'Destroy', undefined}},
+ {746, {wxMemoryDC, new_1_0, 1}},
+ {747, {wxMemoryDC, new_1_1, 1}},
+ {748, {wxMemoryDC, new_0, 0}},
+ {750, {wxMemoryDC, destruct, 0}},
+ {751, {wxMemoryDC, selectObject, 1}},
+ {752, {wxMemoryDC, selectObjectAsSource, 1}},
+ {753, {wxBufferedDC, new_0, 0}},
+ {754, {wxBufferedDC, new_2, 2}},
+ {755, {wxBufferedDC, new_3, 3}},
+ {756, {wxBufferedDC, destruct, 0}},
+ {757, {wxBufferedDC, init_2, 2}},
+ {758, {wxBufferedDC, init_3, 3}},
+ {759, {wxBufferedPaintDC, new_3, 3}},
+ {760, {wxBufferedPaintDC, new_2, 2}},
+ {761, {wxBufferedPaintDC, destruct, 0}},
+ {762, {wxGraphicsObject, destruct, 0}},
+ {763, {wxGraphicsObject, getRenderer, 0}},
+ {764, {wxGraphicsObject, isNull, 0}},
+ {765, {wxGraphicsContext, destruct, 0}},
+ {766, {wxGraphicsContext, create_1_1, 1}},
+ {767, {wxGraphicsContext, create_1_0, 1}},
+ {768, {wxGraphicsContext, create_0, 0}},
+ {769, {wxGraphicsContext, createPen, 1}},
+ {770, {wxGraphicsContext, createBrush, 1}},
+ {771, {wxGraphicsContext, createRadialGradientBrush, 7}},
+ {772, {wxGraphicsContext, createLinearGradientBrush, 6}},
+ {773, {wxGraphicsContext, createFont, 2}},
+ {774, {wxGraphicsContext, createMatrix, 1}},
+ {775, {wxGraphicsContext, createPath, 0}},
+ {776, {wxGraphicsContext, clip_1, 1}},
+ {777, {wxGraphicsContext, clip_4, 4}},
+ {778, {wxGraphicsContext, resetClip, 0}},
+ {779, {wxGraphicsContext, drawBitmap, 5}},
+ {780, {wxGraphicsContext, drawEllipse, 4}},
+ {781, {wxGraphicsContext, drawIcon, 5}},
+ {782, {wxGraphicsContext, drawLines, 3}},
+ {783, {wxGraphicsContext, drawPath, 2}},
+ {784, {wxGraphicsContext, drawRectangle, 4}},
+ {785, {wxGraphicsContext, drawRoundedRectangle, 5}},
+ {786, {wxGraphicsContext, drawText_3, 3}},
+ {787, {wxGraphicsContext, drawText_4_0, 4}},
+ {788, {wxGraphicsContext, drawText_4_1, 4}},
+ {789, {wxGraphicsContext, drawText_5, 5}},
+ {790, {wxGraphicsContext, fillPath, 2}},
+ {791, {wxGraphicsContext, strokePath, 1}},
+ {792, {wxGraphicsContext, getPartialTextExtents, 2}},
+ {793, {wxGraphicsContext, getTextExtent, 5}},
+ {794, {wxGraphicsContext, rotate, 1}},
+ {795, {wxGraphicsContext, scale, 2}},
+ {796, {wxGraphicsContext, translate, 2}},
+ {797, {wxGraphicsContext, getTransform, 0}},
+ {798, {wxGraphicsContext, setTransform, 1}},
+ {799, {wxGraphicsContext, concatTransform, 1}},
+ {800, {wxGraphicsContext, setBrush_1_1, 1}},
+ {801, {wxGraphicsContext, setBrush_1_0, 1}},
+ {802, {wxGraphicsContext, setFont_1, 1}},
+ {803, {wxGraphicsContext, setFont_2, 2}},
+ {804, {wxGraphicsContext, setPen_1_0, 1}},
+ {805, {wxGraphicsContext, setPen_1_1, 1}},
+ {806, {wxGraphicsContext, strokeLine, 4}},
+ {807, {wxGraphicsContext, strokeLines, 2}},
+ {809, {wxGraphicsMatrix, concat, 1}},
+ {811, {wxGraphicsMatrix, get, 1}},
+ {812, {wxGraphicsMatrix, invert, 0}},
+ {813, {wxGraphicsMatrix, isEqual, 1}},
+ {815, {wxGraphicsMatrix, isIdentity, 0}},
+ {816, {wxGraphicsMatrix, rotate, 1}},
+ {817, {wxGraphicsMatrix, scale, 2}},
+ {818, {wxGraphicsMatrix, translate, 2}},
+ {819, {wxGraphicsMatrix, set, 1}},
+ {820, {wxGraphicsMatrix, transformPoint, 2}},
+ {821, {wxGraphicsMatrix, transformDistance, 2}},
+ {822, {wxGraphicsPath, moveToPoint_2, 2}},
+ {823, {wxGraphicsPath, moveToPoint_1, 1}},
+ {824, {wxGraphicsPath, addArc_6, 6}},
+ {825, {wxGraphicsPath, addArc_5, 5}},
+ {826, {wxGraphicsPath, addArcToPoint, 5}},
+ {827, {wxGraphicsPath, addCircle, 3}},
+ {828, {wxGraphicsPath, addCurveToPoint_6, 6}},
+ {829, {wxGraphicsPath, addCurveToPoint_3, 3}},
+ {830, {wxGraphicsPath, addEllipse, 4}},
+ {831, {wxGraphicsPath, addLineToPoint_2, 2}},
+ {832, {wxGraphicsPath, addLineToPoint_1, 1}},
+ {833, {wxGraphicsPath, addPath, 1}},
+ {834, {wxGraphicsPath, addQuadCurveToPoint, 4}},
+ {835, {wxGraphicsPath, addRectangle, 4}},
+ {836, {wxGraphicsPath, addRoundedRectangle, 5}},
+ {837, {wxGraphicsPath, closeSubpath, 0}},
+ {838, {wxGraphicsPath, contains_3, 3}},
+ {839, {wxGraphicsPath, contains_2, 2}},
+ {841, {wxGraphicsPath, getBox, 0}},
+ {843, {wxGraphicsPath, getCurrentPoint, 0}},
+ {844, {wxGraphicsPath, transform, 1}},
+ {845, {wxGraphicsRenderer, getDefaultRenderer, 0}},
+ {846, {wxGraphicsRenderer, createContext_1_1, 1}},
+ {847, {wxGraphicsRenderer, createContext_1_0, 1}},
+ {848, {wxGraphicsRenderer, createPen, 1}},
+ {849, {wxGraphicsRenderer, createBrush, 1}},
+ {850, {wxGraphicsRenderer, createLinearGradientBrush, 6}},
+ {851, {wxGraphicsRenderer, createRadialGradientBrush, 7}},
+ {852, {wxGraphicsRenderer, createFont, 2}},
+ {853, {wxGraphicsRenderer, createMatrix, 1}},
+ {854, {wxGraphicsRenderer, createPath, 0}},
+ {856, {wxMenuBar, new_1, 1}},
+ {858, {wxMenuBar, new_0, 0}},
+ {860, {wxMenuBar, destruct, 0}},
+ {861, {wxMenuBar, append, 2}},
+ {862, {wxMenuBar, check, 2}},
+ {863, {wxMenuBar, enable_2, 2}},
+ {864, {wxMenuBar, enable_1, 1}},
+ {865, {wxMenuBar, enableTop, 2}},
+ {866, {wxMenuBar, findMenu, 1}},
+ {867, {wxMenuBar, findMenuItem, 2}},
+ {868, {wxMenuBar, findItem, 2}},
+ {869, {wxMenuBar, getHelpString, 1}},
+ {870, {wxMenuBar, getLabel_1, 1}},
+ {871, {wxMenuBar, getLabel_0, 0}},
+ {872, {wxMenuBar, getLabelTop, 1}},
+ {873, {wxMenuBar, getMenu, 1}},
+ {874, {wxMenuBar, getMenuCount, 0}},
+ {875, {wxMenuBar, insert, 3}},
+ {876, {wxMenuBar, isChecked, 1}},
+ {877, {wxMenuBar, isEnabled_1, 1}},
+ {878, {wxMenuBar, isEnabled_0, 0}},
+ {879, {wxMenuBar, remove, 1}},
+ {880, {wxMenuBar, replace, 3}},
+ {881, {wxMenuBar, setHelpString, 2}},
+ {882, {wxMenuBar, setLabel_2, 2}},
+ {883, {wxMenuBar, setLabel_1, 1}},
+ {884, {wxMenuBar, setLabelTop, 2}},
+ {885, {wxControl, getLabel, 0}},
+ {886, {wxControl, setLabel, 1}},
+ {887, {wxControlWithItems, append_1, 1}},
+ {888, {wxControlWithItems, append_2, 2}},
+ {889, {wxControlWithItems, appendStrings_1, 1}},
+ {890, {wxControlWithItems, clear, 0}},
+ {891, {wxControlWithItems, delete, 1}},
+ {892, {wxControlWithItems, findString, 2}},
+ {893, {wxControlWithItems, getClientData, 1}},
+ {894, {wxControlWithItems, setClientData, 2}},
+ {895, {wxControlWithItems, getCount, 0}},
+ {896, {wxControlWithItems, getSelection, 0}},
+ {897, {wxControlWithItems, getString, 1}},
+ {898, {wxControlWithItems, getStringSelection, 0}},
+ {899, {wxControlWithItems, insert_2, 2}},
+ {900, {wxControlWithItems, insert_3, 3}},
+ {901, {wxControlWithItems, isEmpty, 0}},
+ {902, {wxControlWithItems, select, 1}},
+ {903, {wxControlWithItems, setSelection, 1}},
+ {904, {wxControlWithItems, setString, 2}},
+ {905, {wxControlWithItems, setStringSelection, 1}},
+ {908, {wxMenu, new_2, 2}},
+ {909, {wxMenu, new_1, 1}},
+ {911, {wxMenu, destruct, 0}},
+ {912, {wxMenu, append_3, 3}},
+ {913, {wxMenu, append_1, 1}},
+ {914, {wxMenu, append_4_0, 4}},
+ {915, {wxMenu, append_4_1, 4}},
+ {916, {wxMenu, appendCheckItem, 3}},
+ {917, {wxMenu, appendRadioItem, 3}},
+ {918, {wxMenu, appendSeparator, 0}},
+ {919, {wxMenu, break, 0}},
+ {920, {wxMenu, check, 2}},
+ {921, {wxMenu, delete_1_0, 1}},
+ {922, {wxMenu, delete_1_1, 1}},
+ {923, {wxMenu, destroy_1_0, 1}},
+ {924, {wxMenu, destroy_1_1, 1}},
+ {925, {wxMenu, enable, 2}},
+ {926, {wxMenu, findItem_1, 1}},
+ {927, {wxMenu, findItem_2, 2}},
+ {928, {wxMenu, findItemByPosition, 1}},
+ {929, {wxMenu, getHelpString, 1}},
+ {930, {wxMenu, getLabel, 1}},
+ {931, {wxMenu, getMenuItemCount, 0}},
+ {932, {wxMenu, getMenuItems, 0}},
+ {934, {wxMenu, getTitle, 0}},
+ {935, {wxMenu, insert_2, 2}},
+ {936, {wxMenu, insert_3, 3}},
+ {937, {wxMenu, insert_5_1, 5}},
+ {938, {wxMenu, insert_5_0, 5}},
+ {939, {wxMenu, insertCheckItem, 4}},
+ {940, {wxMenu, insertRadioItem, 4}},
+ {941, {wxMenu, insertSeparator, 1}},
+ {942, {wxMenu, isChecked, 1}},
+ {943, {wxMenu, isEnabled, 1}},
+ {944, {wxMenu, prepend_1, 1}},
+ {945, {wxMenu, prepend_2, 2}},
+ {946, {wxMenu, prepend_4_1, 4}},
+ {947, {wxMenu, prepend_4_0, 4}},
+ {948, {wxMenu, prependCheckItem, 3}},
+ {949, {wxMenu, prependRadioItem, 3}},
+ {950, {wxMenu, prependSeparator, 0}},
+ {951, {wxMenu, remove_1_0, 1}},
+ {952, {wxMenu, remove_1_1, 1}},
+ {953, {wxMenu, setHelpString, 2}},
+ {954, {wxMenu, setLabel, 2}},
+ {955, {wxMenu, setTitle, 1}},
+ {956, {wxMenuItem, new, 1}},
+ {958, {wxMenuItem, destruct, 0}},
+ {959, {wxMenuItem, check, 1}},
+ {960, {wxMenuItem, enable, 1}},
+ {961, {wxMenuItem, getBitmap, 0}},
+ {962, {wxMenuItem, getHelp, 0}},
+ {963, {wxMenuItem, getId, 0}},
+ {964, {wxMenuItem, getKind, 0}},
+ {965, {wxMenuItem, getLabel, 0}},
+ {966, {wxMenuItem, getLabelFromText, 1}},
+ {967, {wxMenuItem, getMenu, 0}},
+ {968, {wxMenuItem, getText, 0}},
+ {969, {wxMenuItem, getSubMenu, 0}},
+ {970, {wxMenuItem, isCheckable, 0}},
+ {971, {wxMenuItem, isChecked, 0}},
+ {972, {wxMenuItem, isEnabled, 0}},
+ {973, {wxMenuItem, isSeparator, 0}},
+ {974, {wxMenuItem, isSubMenu, 0}},
+ {975, {wxMenuItem, setBitmap, 1}},
+ {976, {wxMenuItem, setHelp, 1}},
+ {977, {wxMenuItem, setMenu, 1}},
+ {978, {wxMenuItem, setSubMenu, 1}},
+ {979, {wxMenuItem, setText, 1}},
+ {980, {wxToolBar, addControl, 1}},
+ {981, {wxToolBar, addSeparator, 0}},
+ {982, {wxToolBar, addTool_5, 5}},
+ {983, {wxToolBar, addTool_4_0, 4}},
+ {984, {wxToolBar, addTool_1, 1}},
+ {985, {wxToolBar, addTool_4_1, 4}},
+ {986, {wxToolBar, addTool_3, 3}},
+ {987, {wxToolBar, addTool_6, 6}},
+ {988, {wxToolBar, addCheckTool, 4}},
+ {989, {wxToolBar, addRadioTool, 4}},
+ {990, {wxToolBar, addStretchableSpace, 0}},
+ {991, {wxToolBar, insertStretchableSpace, 1}},
+ {992, {wxToolBar, deleteTool, 1}},
+ {993, {wxToolBar, deleteToolByPos, 1}},
+ {994, {wxToolBar, enableTool, 2}},
+ {995, {wxToolBar, findById, 1}},
+ {996, {wxToolBar, findControl, 1}},
+ {997, {wxToolBar, findToolForPosition, 2}},
+ {998, {wxToolBar, getToolSize, 0}},
+ {999, {wxToolBar, getToolBitmapSize, 0}},
+ {1000, {wxToolBar, getMargins, 0}},
+ {1001, {wxToolBar, getToolEnabled, 1}},
+ {1002, {wxToolBar, getToolLongHelp, 1}},
+ {1003, {wxToolBar, getToolPacking, 0}},
+ {1004, {wxToolBar, getToolPos, 1}},
+ {1005, {wxToolBar, getToolSeparation, 0}},
+ {1006, {wxToolBar, getToolShortHelp, 1}},
+ {1007, {wxToolBar, getToolState, 1}},
+ {1008, {wxToolBar, insertControl, 2}},
+ {1009, {wxToolBar, insertSeparator, 1}},
+ {1010, {wxToolBar, insertTool_5, 5}},
+ {1011, {wxToolBar, insertTool_2, 2}},
+ {1012, {wxToolBar, insertTool_4, 4}},
+ {1013, {wxToolBar, realize, 0}},
+ {1014, {wxToolBar, removeTool, 1}},
+ {1015, {wxToolBar, setMargins, 2}},
+ {1016, {wxToolBar, setToolBitmapSize, 1}},
+ {1017, {wxToolBar, setToolLongHelp, 2}},
+ {1018, {wxToolBar, setToolPacking, 1}},
+ {1019, {wxToolBar, setToolShortHelp, 2}},
+ {1020, {wxToolBar, setToolSeparation, 1}},
+ {1021, {wxToolBar, toggleTool, 2}},
+ {1023, {wxStatusBar, new_0, 0}},
+ {1024, {wxStatusBar, new_2, 2}},
+ {1026, {wxStatusBar, destruct, 0}},
+ {1027, {wxStatusBar, create, 2}},
+ {1028, {wxStatusBar, getFieldRect, 2}},
+ {1029, {wxStatusBar, getFieldsCount, 0}},
+ {1030, {wxStatusBar, getStatusText, 1}},
+ {1031, {wxStatusBar, popStatusText, 1}},
+ {1032, {wxStatusBar, pushStatusText, 2}},
+ {1033, {wxStatusBar, setFieldsCount, 2}},
+ {1034, {wxStatusBar, setMinHeight, 1}},
+ {1035, {wxStatusBar, setStatusText, 2}},
+ {1036, {wxStatusBar, setStatusWidths, 2}},
+ {1037, {wxStatusBar, setStatusStyles, 2}},
+ {1038, {wxBitmap, new_0, 0}},
+ {1039, {wxBitmap, new_3, 3}},
+ {1040, {wxBitmap, new_4, 4}},
+ {1041, {wxBitmap, new_2_0, 2}},
+ {1042, {wxBitmap, new_2_1, 2}},
+ {1043, {wxBitmap, destruct, 0}},
+ {1044, {wxBitmap, convertToImage, 0}},
+ {1045, {wxBitmap, copyFromIcon, 1}},
+ {1046, {wxBitmap, create, 3}},
+ {1047, {wxBitmap, getDepth, 0}},
+ {1048, {wxBitmap, getHeight, 0}},
+ {1049, {wxBitmap, getPalette, 0}},
+ {1050, {wxBitmap, getMask, 0}},
+ {1051, {wxBitmap, getWidth, 0}},
+ {1052, {wxBitmap, getSubBitmap, 1}},
+ {1053, {wxBitmap, loadFile, 2}},
+ {1054, {wxBitmap, ok, 0}},
+ {1055, {wxBitmap, saveFile, 3}},
+ {1056, {wxBitmap, setDepth, 1}},
+ {1057, {wxBitmap, setHeight, 1}},
+ {1058, {wxBitmap, setMask, 1}},
+ {1059, {wxBitmap, setPalette, 1}},
+ {1060, {wxBitmap, setWidth, 1}},
+ {1061, {wxIcon, new_0, 0}},
+ {1062, {wxIcon, new_2, 2}},
+ {1063, {wxIcon, new_1, 1}},
+ {1064, {wxIcon, copyFromBitmap, 1}},
+ {1065, {wxIcon, 'Destroy', undefined}},
+ {1066, {wxIconBundle, new_0, 0}},
+ {1067, {wxIconBundle, new_2, 2}},
+ {1068, {wxIconBundle, new_1_0, 1}},
+ {1069, {wxIconBundle, new_1_1, 1}},
+ {1070, {wxIconBundle, destruct, 0}},
+ {1071, {wxIconBundle, addIcon_2, 2}},
+ {1072, {wxIconBundle, addIcon_1, 1}},
+ {1073, {wxIconBundle, getIcon_1_1, 1}},
+ {1074, {wxIconBundle, getIcon_1_0, 1}},
+ {1075, {wxCursor, new_0, 0}},
+ {1076, {wxCursor, new_1_0, 1}},
+ {1077, {wxCursor, new_1_1, 1}},
+ {1078, {wxCursor, new_4, 4}},
+ {1079, {wxCursor, destruct, 0}},
+ {1080, {wxCursor, ok, 0}},
+ {1081, {wxMask, new_0, 0}},
+ {1082, {wxMask, new_2_1, 2}},
+ {1083, {wxMask, new_2_0, 2}},
+ {1084, {wxMask, new_1, 1}},
+ {1085, {wxMask, destruct, 0}},
+ {1086, {wxMask, create_2_1, 2}},
+ {1087, {wxMask, create_2_0, 2}},
+ {1088, {wxMask, create_1, 1}},
+ {1089, {wxImage, new_0, 0}},
+ {1090, {wxImage, new_3_0, 3}},
+ {1091, {wxImage, new_4, 4}},
+ {1092, {wxImage, new_5, 5}},
+ {1093, {wxImage, new_2, 2}},
+ {1094, {wxImage, new_3_1, 3}},
+ {1095, {wxImage, blur, 1}},
+ {1096, {wxImage, blurHorizontal, 1}},
+ {1097, {wxImage, blurVertical, 1}},
+ {1098, {wxImage, convertAlphaToMask, 1}},
+ {1099, {wxImage, convertToGreyscale, 1}},
+ {1100, {wxImage, convertToMono, 3}},
+ {1101, {wxImage, copy, 0}},
+ {1102, {wxImage, create_3, 3}},
+ {1103, {wxImage, create_4, 4}},
+ {1104, {wxImage, create_5, 5}},
+ {1105, {wxImage, 'Destroy', 0}},
+ {1106, {wxImage, findFirstUnusedColour, 4}},
+ {1107, {wxImage, getImageExtWildcard, 0}},
+ {1108, {wxImage, getAlpha_2, 2}},
+ {1109, {wxImage, getAlpha_0, 0}},
+ {1110, {wxImage, getBlue, 2}},
+ {1111, {wxImage, getData, 0}},
+ {1112, {wxImage, getGreen, 2}},
+ {1113, {wxImage, getImageCount, 2}},
+ {1114, {wxImage, getHeight, 0}},
+ {1115, {wxImage, getMaskBlue, 0}},
+ {1116, {wxImage, getMaskGreen, 0}},
+ {1117, {wxImage, getMaskRed, 0}},
+ {1118, {wxImage, getOrFindMaskColour, 3}},
+ {1119, {wxImage, getPalette, 0}},
+ {1120, {wxImage, getRed, 2}},
+ {1121, {wxImage, getSubImage, 1}},
+ {1122, {wxImage, getWidth, 0}},
+ {1123, {wxImage, hasAlpha, 0}},
+ {1124, {wxImage, hasMask, 0}},
+ {1125, {wxImage, getOption, 1}},
+ {1126, {wxImage, getOptionInt, 1}},
+ {1127, {wxImage, hasOption, 1}},
+ {1128, {wxImage, initAlpha, 0}},
+ {1129, {wxImage, initStandardHandlers, 0}},
+ {1130, {wxImage, isTransparent, 3}},
+ {1131, {wxImage, loadFile_2, 2}},
+ {1132, {wxImage, loadFile_3, 3}},
+ {1133, {wxImage, ok, 0}},
+ {1134, {wxImage, removeHandler, 1}},
+ {1135, {wxImage, mirror, 1}},
+ {1136, {wxImage, replace, 6}},
+ {1137, {wxImage, rescale, 3}},
+ {1138, {wxImage, resize, 3}},
+ {1139, {wxImage, rotate, 3}},
+ {1140, {wxImage, rotateHue, 1}},
+ {1141, {wxImage, rotate90, 1}},
+ {1142, {wxImage, saveFile_1, 1}},
+ {1143, {wxImage, saveFile_2_0, 2}},
+ {1144, {wxImage, saveFile_2_1, 2}},
+ {1145, {wxImage, scale, 3}},
+ {1146, {wxImage, size, 3}},
+ {1147, {wxImage, setAlpha_3, 3}},
+ {1148, {wxImage, setAlpha_2, 2}},
+ {1149, {wxImage, setData_2, 2}},
+ {1150, {wxImage, setData_4, 4}},
+ {1151, {wxImage, setMask, 1}},
+ {1152, {wxImage, setMaskColour, 3}},
+ {1153, {wxImage, setMaskFromImage, 4}},
+ {1154, {wxImage, setOption_2_1, 2}},
+ {1155, {wxImage, setOption_2_0, 2}},
+ {1156, {wxImage, setPalette, 1}},
+ {1157, {wxImage, setRGB_5, 5}},
+ {1158, {wxImage, setRGB_4, 4}},
+ {1159, {wxImage, 'Destroy', undefined}},
+ {1160, {wxBrush, new_0, 0}},
+ {1161, {wxBrush, new_2, 2}},
+ {1162, {wxBrush, new_1, 1}},
+ {1164, {wxBrush, destruct, 0}},
+ {1165, {wxBrush, getColour, 0}},
+ {1166, {wxBrush, getStipple, 0}},
+ {1167, {wxBrush, getStyle, 0}},
+ {1168, {wxBrush, isHatch, 0}},
+ {1169, {wxBrush, isOk, 0}},
+ {1170, {wxBrush, setColour_1, 1}},
+ {1171, {wxBrush, setColour_3, 3}},
+ {1172, {wxBrush, setStipple, 1}},
+ {1173, {wxBrush, setStyle, 1}},
+ {1174, {wxPen, new_0, 0}},
+ {1175, {wxPen, new_2, 2}},
+ {1176, {wxPen, destruct, 0}},
+ {1177, {wxPen, getCap, 0}},
+ {1178, {wxPen, getColour, 0}},
+ {1179, {wxPen, getJoin, 0}},
+ {1180, {wxPen, getStyle, 0}},
+ {1181, {wxPen, getWidth, 0}},
+ {1182, {wxPen, isOk, 0}},
+ {1183, {wxPen, setCap, 1}},
+ {1184, {wxPen, setColour_1, 1}},
+ {1185, {wxPen, setColour_3, 3}},
+ {1186, {wxPen, setJoin, 1}},
+ {1187, {wxPen, setStyle, 1}},
+ {1188, {wxPen, setWidth, 1}},
+ {1189, {wxRegion, new_0, 0}},
+ {1190, {wxRegion, new_4, 4}},
+ {1191, {wxRegion, new_2, 2}},
+ {1192, {wxRegion, new_1_1, 1}},
+ {1194, {wxRegion, new_1_0, 1}},
+ {1196, {wxRegion, destruct, 0}},
+ {1197, {wxRegion, clear, 0}},
+ {1198, {wxRegion, contains_2, 2}},
+ {1199, {wxRegion, contains_1_0, 1}},
+ {1200, {wxRegion, contains_4, 4}},
+ {1201, {wxRegion, contains_1_1, 1}},
+ {1202, {wxRegion, convertToBitmap, 0}},
+ {1203, {wxRegion, getBox, 0}},
+ {1204, {wxRegion, intersect_4, 4}},
+ {1205, {wxRegion, intersect_1_1, 1}},
+ {1206, {wxRegion, intersect_1_0, 1}},
+ {1207, {wxRegion, isEmpty, 0}},
+ {1208, {wxRegion, subtract_4, 4}},
+ {1209, {wxRegion, subtract_1_1, 1}},
+ {1210, {wxRegion, subtract_1_0, 1}},
+ {1211, {wxRegion, offset_2, 2}},
+ {1212, {wxRegion, offset_1, 1}},
+ {1213, {wxRegion, union_4, 4}},
+ {1214, {wxRegion, union_1_2, 1}},
+ {1215, {wxRegion, union_1_1, 1}},
+ {1216, {wxRegion, union_1_0, 1}},
+ {1217, {wxRegion, union_3, 3}},
+ {1218, {wxRegion, xor_4, 4}},
+ {1219, {wxRegion, xor_1_1, 1}},
+ {1220, {wxRegion, xor_1_0, 1}},
+ {1221, {wxAcceleratorTable, new_0, 0}},
+ {1222, {wxAcceleratorTable, new_2, 2}},
+ {1223, {wxAcceleratorTable, destruct, 0}},
+ {1224, {wxAcceleratorTable, ok, 0}},
+ {1225, {wxAcceleratorEntry, new_1_0, 1}},
+ {1226, {wxAcceleratorEntry, new_1_1, 1}},
+ {1227, {wxAcceleratorEntry, getCommand, 0}},
+ {1228, {wxAcceleratorEntry, getFlags, 0}},
+ {1229, {wxAcceleratorEntry, getKeyCode, 0}},
+ {1230, {wxAcceleratorEntry, set, 4}},
+ {1231, {wxAcceleratorEntry, 'Destroy', undefined}},
+ {1236, {wxCaret, new_3, 3}},
+ {1237, {wxCaret, new_2, 2}},
+ {1239, {wxCaret, destruct, 0}},
+ {1240, {wxCaret, create_3, 3}},
+ {1241, {wxCaret, create_2, 2}},
+ {1242, {wxCaret, getBlinkTime, 0}},
+ {1244, {wxCaret, getPosition, 0}},
+ {1246, {wxCaret, getSize, 0}},
+ {1247, {wxCaret, getWindow, 0}},
+ {1248, {wxCaret, hide, 0}},
+ {1249, {wxCaret, isOk, 0}},
+ {1250, {wxCaret, isVisible, 0}},
+ {1251, {wxCaret, move_2, 2}},
+ {1252, {wxCaret, move_1, 1}},
+ {1253, {wxCaret, setBlinkTime, 1}},
+ {1254, {wxCaret, setSize_2, 2}},
+ {1255, {wxCaret, setSize_1, 1}},
+ {1256, {wxCaret, show, 1}},
+ {1257, {wxSizer, add_2_1, 2}},
+ {1258, {wxSizer, add_2_0, 2}},
+ {1259, {wxSizer, add_3, 3}},
+ {1260, {wxSizer, add_2_3, 2}},
+ {1261, {wxSizer, add_2_2, 2}},
+ {1262, {wxSizer, addSpacer, 1}},
+ {1263, {wxSizer, addStretchSpacer, 1}},
+ {1264, {wxSizer, calcMin, 0}},
+ {1265, {wxSizer, clear, 1}},
+ {1266, {wxSizer, detach_1_2, 1}},
+ {1267, {wxSizer, detach_1_1, 1}},
+ {1268, {wxSizer, detach_1_0, 1}},
+ {1269, {wxSizer, fit, 1}},
+ {1270, {wxSizer, fitInside, 1}},
+ {1271, {wxSizer, getChildren, 0}},
+ {1272, {wxSizer, getItem_2_1, 2}},
+ {1273, {wxSizer, getItem_2_0, 2}},
+ {1274, {wxSizer, getItem_1, 1}},
+ {1275, {wxSizer, getSize, 0}},
+ {1276, {wxSizer, getPosition, 0}},
+ {1277, {wxSizer, getMinSize, 0}},
+ {1278, {wxSizer, hide_2_0, 2}},
+ {1279, {wxSizer, hide_2_1, 2}},
+ {1280, {wxSizer, hide_1, 1}},
+ {1281, {wxSizer, insert_3_1, 3}},
+ {1282, {wxSizer, insert_3_0, 3}},
+ {1283, {wxSizer, insert_4, 4}},
+ {1284, {wxSizer, insert_3_3, 3}},
+ {1285, {wxSizer, insert_3_2, 3}},
+ {1286, {wxSizer, insert_2, 2}},
+ {1287, {wxSizer, insertSpacer, 2}},
+ {1288, {wxSizer, insertStretchSpacer, 2}},
+ {1289, {wxSizer, isShown_1_2, 1}},
+ {1290, {wxSizer, isShown_1_1, 1}},
+ {1291, {wxSizer, isShown_1_0, 1}},
+ {1292, {wxSizer, layout, 0}},
+ {1293, {wxSizer, prepend_2_1, 2}},
+ {1294, {wxSizer, prepend_2_0, 2}},
+ {1295, {wxSizer, prepend_3, 3}},
+ {1296, {wxSizer, prepend_2_3, 2}},
+ {1297, {wxSizer, prepend_2_2, 2}},
+ {1298, {wxSizer, prepend_1, 1}},
+ {1299, {wxSizer, prependSpacer, 1}},
+ {1300, {wxSizer, prependStretchSpacer, 1}},
+ {1301, {wxSizer, recalcSizes, 0}},
+ {1302, {wxSizer, remove_1_1, 1}},
+ {1303, {wxSizer, remove_1_0, 1}},
+ {1304, {wxSizer, replace_3_1, 3}},
+ {1305, {wxSizer, replace_3_0, 3}},
+ {1306, {wxSizer, replace_2, 2}},
+ {1307, {wxSizer, setDimension, 4}},
+ {1308, {wxSizer, setMinSize_2, 2}},
+ {1309, {wxSizer, setMinSize_1, 1}},
+ {1310, {wxSizer, setItemMinSize_3_2, 3}},
+ {1311, {wxSizer, setItemMinSize_2_2, 2}},
+ {1312, {wxSizer, setItemMinSize_3_1, 3}},
+ {1313, {wxSizer, setItemMinSize_2_1, 2}},
+ {1314, {wxSizer, setItemMinSize_3_0, 3}},
+ {1315, {wxSizer, setItemMinSize_2_0, 2}},
+ {1316, {wxSizer, setSizeHints, 1}},
+ {1317, {wxSizer, setVirtualSizeHints, 1}},
+ {1318, {wxSizer, show_2_2, 2}},
+ {1319, {wxSizer, show_2_1, 2}},
+ {1320, {wxSizer, show_2_0, 2}},
+ {1321, {wxSizer, show_1, 1}},
+ {1322, {wxSizerFlags, new, 1}},
+ {1323, {wxSizerFlags, align, 1}},
+ {1324, {wxSizerFlags, border_2, 2}},
+ {1325, {wxSizerFlags, border_1, 1}},
+ {1326, {wxSizerFlags, center, 0}},
+ {1327, {wxSizerFlags, centre, 0}},
+ {1328, {wxSizerFlags, expand, 0}},
+ {1329, {wxSizerFlags, left, 0}},
+ {1330, {wxSizerFlags, proportion, 1}},
+ {1331, {wxSizerFlags, right, 0}},
+ {1332, {wxSizerFlags, 'Destroy', undefined}},
+ {1333, {wxSizerItem, new_5_1, 5}},
+ {1334, {wxSizerItem, new_2_1, 2}},
+ {1335, {wxSizerItem, new_5_0, 5}},
+ {1336, {wxSizerItem, new_2_0, 2}},
+ {1337, {wxSizerItem, new_6, 6}},
+ {1338, {wxSizerItem, new_3, 3}},
+ {1339, {wxSizerItem, new_0, 0}},
+ {1340, {wxSizerItem, destruct, 0}},
+ {1341, {wxSizerItem, calcMin, 0}},
+ {1342, {wxSizerItem, deleteWindows, 0}},
+ {1343, {wxSizerItem, detachSizer, 0}},
+ {1344, {wxSizerItem, getBorder, 0}},
+ {1345, {wxSizerItem, getFlag, 0}},
+ {1346, {wxSizerItem, getMinSize, 0}},
+ {1347, {wxSizerItem, getPosition, 0}},
+ {1348, {wxSizerItem, getProportion, 0}},
+ {1349, {wxSizerItem, getRatio, 0}},
+ {1350, {wxSizerItem, getRect, 0}},
+ {1351, {wxSizerItem, getSize, 0}},
+ {1352, {wxSizerItem, getSizer, 0}},
+ {1353, {wxSizerItem, getSpacer, 0}},
+ {1354, {wxSizerItem, getUserData, 0}},
+ {1355, {wxSizerItem, getWindow, 0}},
+ {1356, {wxSizerItem, isSizer, 0}},
+ {1357, {wxSizerItem, isShown, 0}},
+ {1358, {wxSizerItem, isSpacer, 0}},
+ {1359, {wxSizerItem, isWindow, 0}},
+ {1360, {wxSizerItem, setBorder, 1}},
+ {1361, {wxSizerItem, setDimension, 2}},
+ {1362, {wxSizerItem, setFlag, 1}},
+ {1363, {wxSizerItem, setInitSize, 2}},
+ {1364, {wxSizerItem, setMinSize_1, 1}},
+ {1365, {wxSizerItem, setMinSize_2, 2}},
+ {1366, {wxSizerItem, setProportion, 1}},
+ {1367, {wxSizerItem, setRatio_2, 2}},
+ {1368, {wxSizerItem, setRatio_1_1, 1}},
+ {1369, {wxSizerItem, setRatio_1_0, 1}},
+ {1370, {wxSizerItem, setSizer, 1}},
+ {1371, {wxSizerItem, setSpacer_1, 1}},
+ {1372, {wxSizerItem, setSpacer_2, 2}},
+ {1373, {wxSizerItem, setWindow, 1}},
+ {1374, {wxSizerItem, show, 1}},
+ {1375, {wxBoxSizer, new, 1}},
+ {1376, {wxBoxSizer, getOrientation, 0}},
+ {1377, {wxBoxSizer, 'Destroy', undefined}},
+ {1378, {wxStaticBoxSizer, new_2, 2}},
+ {1379, {wxStaticBoxSizer, new_3, 3}},
+ {1380, {wxStaticBoxSizer, getStaticBox, 0}},
+ {1381, {wxStaticBoxSizer, 'Destroy', undefined}},
+ {1382, {wxGridSizer, new_4, 4}},
+ {1383, {wxGridSizer, new_2, 2}},
+ {1384, {wxGridSizer, getCols, 0}},
+ {1385, {wxGridSizer, getHGap, 0}},
+ {1386, {wxGridSizer, getRows, 0}},
+ {1387, {wxGridSizer, getVGap, 0}},
+ {1388, {wxGridSizer, setCols, 1}},
+ {1389, {wxGridSizer, setHGap, 1}},
+ {1390, {wxGridSizer, setRows, 1}},
+ {1391, {wxGridSizer, setVGap, 1}},
+ {1392, {wxGridSizer, 'Destroy', undefined}},
+ {1393, {wxFlexGridSizer, new_4, 4}},
+ {1394, {wxFlexGridSizer, new_2, 2}},
+ {1395, {wxFlexGridSizer, addGrowableCol, 2}},
+ {1396, {wxFlexGridSizer, addGrowableRow, 2}},
+ {1397, {wxFlexGridSizer, getFlexibleDirection, 0}},
+ {1398, {wxFlexGridSizer, getNonFlexibleGrowMode, 0}},
+ {1399, {wxFlexGridSizer, removeGrowableCol, 1}},
+ {1400, {wxFlexGridSizer, removeGrowableRow, 1}},
+ {1401, {wxFlexGridSizer, setFlexibleDirection, 1}},
+ {1402, {wxFlexGridSizer, setNonFlexibleGrowMode, 1}},
+ {1403, {wxFlexGridSizer, 'Destroy', undefined}},
+ {1404, {wxGridBagSizer, new, 1}},
+ {1405, {wxGridBagSizer, add_3_2, 3}},
+ {1406, {wxGridBagSizer, add_3_1, 3}},
+ {1407, {wxGridBagSizer, add_4, 4}},
+ {1408, {wxGridBagSizer, add_1_0, 1}},
+ {1409, {wxGridBagSizer, add_2_1, 2}},
+ {1410, {wxGridBagSizer, add_2_0, 2}},
+ {1411, {wxGridBagSizer, add_3_0, 3}},
+ {1412, {wxGridBagSizer, add_1_1, 1}},
+ {1413, {wxGridBagSizer, calcMin, 0}},
+ {1414, {wxGridBagSizer, checkForIntersection_2, 2}},
+ {1415, {wxGridBagSizer, checkForIntersection_3, 3}},
+ {1416, {wxGridBagSizer, findItem_1_1, 1}},
+ {1417, {wxGridBagSizer, findItem_1_0, 1}},
+ {1418, {wxGridBagSizer, findItemAtPoint, 1}},
+ {1419, {wxGridBagSizer, findItemAtPosition, 1}},
+ {1420, {wxGridBagSizer, findItemWithData, 1}},
+ {1421, {wxGridBagSizer, getCellSize, 2}},
+ {1422, {wxGridBagSizer, getEmptyCellSize, 0}},
+ {1423, {wxGridBagSizer, getItemPosition_1_2, 1}},
+ {1424, {wxGridBagSizer, getItemPosition_1_1, 1}},
+ {1425, {wxGridBagSizer, getItemPosition_1_0, 1}},
+ {1426, {wxGridBagSizer, getItemSpan_1_2, 1}},
+ {1427, {wxGridBagSizer, getItemSpan_1_1, 1}},
+ {1428, {wxGridBagSizer, getItemSpan_1_0, 1}},
+ {1429, {wxGridBagSizer, setEmptyCellSize, 1}},
+ {1430, {wxGridBagSizer, setItemPosition_2_2, 2}},
+ {1431, {wxGridBagSizer, setItemPosition_2_1, 2}},
+ {1432, {wxGridBagSizer, setItemPosition_2_0, 2}},
+ {1433, {wxGridBagSizer, setItemSpan_2_2, 2}},
+ {1434, {wxGridBagSizer, setItemSpan_2_1, 2}},
+ {1435, {wxGridBagSizer, setItemSpan_2_0, 2}},
+ {1436, {wxGridBagSizer, 'Destroy', undefined}},
+ {1437, {wxStdDialogButtonSizer, new, 0}},
+ {1438, {wxStdDialogButtonSizer, addButton, 1}},
+ {1439, {wxStdDialogButtonSizer, realize, 0}},
+ {1440, {wxStdDialogButtonSizer, setAffirmativeButton, 1}},
+ {1441, {wxStdDialogButtonSizer, setCancelButton, 1}},
+ {1442, {wxStdDialogButtonSizer, setNegativeButton, 1}},
+ {1443, {wxStdDialogButtonSizer, 'Destroy', undefined}},
+ {1444, {wxFont, new_0, 0}},
+ {1445, {wxFont, new_1, 1}},
+ {1446, {wxFont, new_5, 5}},
+ {1448, {wxFont, destruct, 0}},
+ {1449, {wxFont, isFixedWidth, 0}},
+ {1450, {wxFont, getDefaultEncoding, 0}},
+ {1451, {wxFont, getFaceName, 0}},
+ {1452, {wxFont, getFamily, 0}},
+ {1453, {wxFont, getNativeFontInfoDesc, 0}},
+ {1454, {wxFont, getNativeFontInfoUserDesc, 0}},
+ {1455, {wxFont, getPointSize, 0}},
+ {1456, {wxFont, getStyle, 0}},
+ {1457, {wxFont, getUnderlined, 0}},
+ {1458, {wxFont, getWeight, 0}},
+ {1459, {wxFont, ok, 0}},
+ {1460, {wxFont, setDefaultEncoding, 1}},
+ {1461, {wxFont, setFaceName, 1}},
+ {1462, {wxFont, setFamily, 1}},
+ {1463, {wxFont, setPointSize, 1}},
+ {1464, {wxFont, setStyle, 1}},
+ {1465, {wxFont, setUnderlined, 1}},
+ {1466, {wxFont, setWeight, 1}},
+ {1467, {wxToolTip, enable, 1}},
+ {1468, {wxToolTip, setDelay, 1}},
+ {1469, {wxToolTip, new, 1}},
+ {1470, {wxToolTip, setTip, 1}},
+ {1471, {wxToolTip, getTip, 0}},
+ {1472, {wxToolTip, getWindow, 0}},
+ {1473, {wxToolTip, 'Destroy', undefined}},
+ {1475, {wxButton, new_3, 3}},
+ {1476, {wxButton, new_0, 0}},
+ {1477, {wxButton, destruct, 0}},
+ {1478, {wxButton, create, 3}},
+ {1479, {wxButton, getDefaultSize, 0}},
+ {1480, {wxButton, setDefault, 0}},
+ {1481, {wxButton, setLabel, 1}},
+ {1483, {wxBitmapButton, new_4, 4}},
+ {1484, {wxBitmapButton, new_0, 0}},
+ {1485, {wxBitmapButton, create, 4}},
+ {1486, {wxBitmapButton, getBitmapDisabled, 0}},
+ {1488, {wxBitmapButton, getBitmapFocus, 0}},
+ {1490, {wxBitmapButton, getBitmapLabel, 0}},
+ {1492, {wxBitmapButton, getBitmapSelected, 0}},
+ {1494, {wxBitmapButton, setBitmapDisabled, 1}},
+ {1495, {wxBitmapButton, setBitmapFocus, 1}},
+ {1496, {wxBitmapButton, setBitmapLabel, 1}},
+ {1497, {wxBitmapButton, setBitmapSelected, 1}},
+ {1498, {wxBitmapButton, 'Destroy', undefined}},
+ {1499, {wxToggleButton, new_0, 0}},
+ {1500, {wxToggleButton, new_4, 4}},
+ {1501, {wxToggleButton, create, 4}},
+ {1502, {wxToggleButton, getValue, 0}},
+ {1503, {wxToggleButton, setValue, 1}},
+ {1504, {wxToggleButton, 'Destroy', undefined}},
+ {1505, {wxCalendarCtrl, new_0, 0}},
+ {1506, {wxCalendarCtrl, new_3, 3}},
+ {1507, {wxCalendarCtrl, create, 3}},
+ {1508, {wxCalendarCtrl, destruct, 0}},
+ {1509, {wxCalendarCtrl, setDate, 1}},
+ {1510, {wxCalendarCtrl, getDate, 0}},
+ {1511, {wxCalendarCtrl, enableYearChange, 1}},
+ {1512, {wxCalendarCtrl, enableMonthChange, 1}},
+ {1513, {wxCalendarCtrl, enableHolidayDisplay, 1}},
+ {1514, {wxCalendarCtrl, setHeaderColours, 2}},
+ {1515, {wxCalendarCtrl, getHeaderColourFg, 0}},
+ {1516, {wxCalendarCtrl, getHeaderColourBg, 0}},
+ {1517, {wxCalendarCtrl, setHighlightColours, 2}},
+ {1518, {wxCalendarCtrl, getHighlightColourFg, 0}},
+ {1519, {wxCalendarCtrl, getHighlightColourBg, 0}},
+ {1520, {wxCalendarCtrl, setHolidayColours, 2}},
+ {1521, {wxCalendarCtrl, getHolidayColourFg, 0}},
+ {1522, {wxCalendarCtrl, getHolidayColourBg, 0}},
+ {1523, {wxCalendarCtrl, getAttr, 1}},
+ {1524, {wxCalendarCtrl, setAttr, 2}},
+ {1525, {wxCalendarCtrl, setHoliday, 1}},
+ {1526, {wxCalendarCtrl, resetAttr, 1}},
+ {1527, {wxCalendarCtrl, hitTest, 2}},
+ {1528, {wxCalendarDateAttr, new_0, 0}},
+ {1529, {wxCalendarDateAttr, new_2_1, 2}},
+ {1530, {wxCalendarDateAttr, new_2_0, 2}},
+ {1531, {wxCalendarDateAttr, setTextColour, 1}},
+ {1532, {wxCalendarDateAttr, setBackgroundColour, 1}},
+ {1533, {wxCalendarDateAttr, setBorderColour, 1}},
+ {1534, {wxCalendarDateAttr, setFont, 1}},
+ {1535, {wxCalendarDateAttr, setBorder, 1}},
+ {1536, {wxCalendarDateAttr, setHoliday, 1}},
+ {1537, {wxCalendarDateAttr, hasTextColour, 0}},
+ {1538, {wxCalendarDateAttr, hasBackgroundColour, 0}},
+ {1539, {wxCalendarDateAttr, hasBorderColour, 0}},
+ {1540, {wxCalendarDateAttr, hasFont, 0}},
+ {1541, {wxCalendarDateAttr, hasBorder, 0}},
+ {1542, {wxCalendarDateAttr, isHoliday, 0}},
+ {1543, {wxCalendarDateAttr, getTextColour, 0}},
+ {1544, {wxCalendarDateAttr, getBackgroundColour, 0}},
+ {1545, {wxCalendarDateAttr, getBorderColour, 0}},
+ {1546, {wxCalendarDateAttr, getFont, 0}},
+ {1547, {wxCalendarDateAttr, getBorder, 0}},
+ {1548, {wxCalendarDateAttr, 'Destroy', undefined}},
+ {1550, {wxCheckBox, new_4, 4}},
+ {1551, {wxCheckBox, new_0, 0}},
+ {1552, {wxCheckBox, create, 4}},
+ {1553, {wxCheckBox, getValue, 0}},
+ {1554, {wxCheckBox, get3StateValue, 0}},
+ {1555, {wxCheckBox, is3rdStateAllowedForUser, 0}},
+ {1556, {wxCheckBox, is3State, 0}},
+ {1557, {wxCheckBox, isChecked, 0}},
+ {1558, {wxCheckBox, setValue, 1}},
+ {1559, {wxCheckBox, set3StateValue, 1}},
+ {1560, {wxCheckBox, 'Destroy', undefined}},
+ {1561, {wxCheckListBox, new_0, 0}},
+ {1563, {wxCheckListBox, new_3, 3}},
+ {1564, {wxCheckListBox, check, 2}},
+ {1565, {wxCheckListBox, isChecked, 1}},
+ {1566, {wxCheckListBox, 'Destroy', undefined}},
+ {1569, {wxChoice, new_3, 3}},
+ {1570, {wxChoice, new_0, 0}},
+ {1572, {wxChoice, destruct, 0}},
+ {1574, {wxChoice, create, 6}},
+ {1575, {wxChoice, delete, 1}},
+ {1576, {wxChoice, getColumns, 0}},
+ {1577, {wxChoice, setColumns, 1}},
+ {1578, {wxComboBox, new_0, 0}},
+ {1580, {wxComboBox, new_3, 3}},
+ {1581, {wxComboBox, destruct, 0}},
+ {1583, {wxComboBox, create, 7}},
+ {1584, {wxComboBox, canCopy, 0}},
+ {1585, {wxComboBox, canCut, 0}},
+ {1586, {wxComboBox, canPaste, 0}},
+ {1587, {wxComboBox, canRedo, 0}},
+ {1588, {wxComboBox, canUndo, 0}},
+ {1589, {wxComboBox, copy, 0}},
+ {1590, {wxComboBox, cut, 0}},
+ {1591, {wxComboBox, getInsertionPoint, 0}},
+ {1592, {wxComboBox, getLastPosition, 0}},
+ {1593, {wxComboBox, getValue, 0}},
+ {1594, {wxComboBox, paste, 0}},
+ {1595, {wxComboBox, redo, 0}},
+ {1596, {wxComboBox, replace, 3}},
+ {1597, {wxComboBox, remove, 2}},
+ {1598, {wxComboBox, setInsertionPoint, 1}},
+ {1599, {wxComboBox, setInsertionPointEnd, 0}},
+ {1600, {wxComboBox, setSelection_1, 1}},
+ {1601, {wxComboBox, setSelection_2, 2}},
+ {1602, {wxComboBox, setValue, 1}},
+ {1603, {wxComboBox, undo, 0}},
+ {1604, {wxGauge, new_0, 0}},
+ {1605, {wxGauge, new_4, 4}},
+ {1606, {wxGauge, create, 4}},
+ {1607, {wxGauge, getRange, 0}},
+ {1608, {wxGauge, getValue, 0}},
+ {1609, {wxGauge, isVertical, 0}},
+ {1610, {wxGauge, setRange, 1}},
+ {1611, {wxGauge, setValue, 1}},
+ {1612, {wxGauge, pulse, 0}},
+ {1613, {wxGauge, 'Destroy', undefined}},
+ {1614, {wxGenericDirCtrl, new_0, 0}},
+ {1615, {wxGenericDirCtrl, new_2, 2}},
+ {1616, {wxGenericDirCtrl, destruct, 0}},
+ {1617, {wxGenericDirCtrl, create, 2}},
+ {1618, {wxGenericDirCtrl, init, 0}},
+ {1619, {wxGenericDirCtrl, collapseTree, 0}},
+ {1620, {wxGenericDirCtrl, expandPath, 1}},
+ {1621, {wxGenericDirCtrl, getDefaultPath, 0}},
+ {1622, {wxGenericDirCtrl, getPath, 0}},
+ {1623, {wxGenericDirCtrl, getFilePath, 0}},
+ {1624, {wxGenericDirCtrl, getFilter, 0}},
+ {1625, {wxGenericDirCtrl, getFilterIndex, 0}},
+ {1626, {wxGenericDirCtrl, getRootId, 0}},
+ {1627, {wxGenericDirCtrl, getTreeCtrl, 0}},
+ {1628, {wxGenericDirCtrl, reCreateTree, 0}},
+ {1629, {wxGenericDirCtrl, setDefaultPath, 1}},
+ {1630, {wxGenericDirCtrl, setFilter, 1}},
+ {1631, {wxGenericDirCtrl, setFilterIndex, 1}},
+ {1632, {wxGenericDirCtrl, setPath, 1}},
+ {1634, {wxStaticBox, new_4, 4}},
+ {1635, {wxStaticBox, new_0, 0}},
+ {1636, {wxStaticBox, create, 4}},
+ {1637, {wxStaticBox, 'Destroy', undefined}},
+ {1639, {wxStaticLine, new_2, 2}},
+ {1640, {wxStaticLine, new_0, 0}},
+ {1641, {wxStaticLine, create, 2}},
+ {1642, {wxStaticLine, isVertical, 0}},
+ {1643, {wxStaticLine, getDefaultSize, 0}},
+ {1644, {wxStaticLine, 'Destroy', undefined}},
+ {1647, {wxListBox, new_3, 3}},
+ {1648, {wxListBox, new_0, 0}},
+ {1650, {wxListBox, destruct, 0}},
+ {1652, {wxListBox, create, 6}},
+ {1653, {wxListBox, deselect, 1}},
+ {1654, {wxListBox, getSelections, 1}},
+ {1655, {wxListBox, insertItems, 2}},
+ {1656, {wxListBox, isSelected, 1}},
+ {1657, {wxListBox, set, 1}},
+ {1658, {wxListBox, hitTest, 1}},
+ {1659, {wxListBox, setFirstItem_1_0, 1}},
+ {1660, {wxListBox, setFirstItem_1_1, 1}},
+ {1661, {wxListCtrl, new_0, 0}},
+ {1662, {wxListCtrl, new_2, 2}},
+ {1663, {wxListCtrl, arrange, 1}},
+ {1664, {wxListCtrl, assignImageList, 2}},
+ {1665, {wxListCtrl, clearAll, 0}},
+ {1666, {wxListCtrl, create, 2}},
+ {1667, {wxListCtrl, deleteAllItems, 0}},
+ {1668, {wxListCtrl, deleteColumn, 1}},
+ {1669, {wxListCtrl, deleteItem, 1}},
+ {1670, {wxListCtrl, editLabel, 1}},
+ {1671, {wxListCtrl, ensureVisible, 1}},
+ {1672, {wxListCtrl, findItem_3_0, 3}},
+ {1673, {wxListCtrl, findItem_3_1, 3}},
+ {1674, {wxListCtrl, getColumn, 2}},
+ {1675, {wxListCtrl, getColumnCount, 0}},
+ {1676, {wxListCtrl, getColumnWidth, 1}},
+ {1677, {wxListCtrl, getCountPerPage, 0}},
+ {1678, {wxListCtrl, getEditControl, 0}},
+ {1679, {wxListCtrl, getImageList, 1}},
+ {1680, {wxListCtrl, getItem, 1}},
+ {1681, {wxListCtrl, getItemBackgroundColour, 1}},
+ {1682, {wxListCtrl, getItemCount, 0}},
+ {1683, {wxListCtrl, getItemData, 1}},
+ {1684, {wxListCtrl, getItemFont, 1}},
+ {1685, {wxListCtrl, getItemPosition, 2}},
+ {1686, {wxListCtrl, getItemRect, 3}},
+ {1687, {wxListCtrl, getItemSpacing, 0}},
+ {1688, {wxListCtrl, getItemState, 2}},
+ {1689, {wxListCtrl, getItemText, 1}},
+ {1690, {wxListCtrl, getItemTextColour, 1}},
+ {1691, {wxListCtrl, getNextItem, 2}},
+ {1692, {wxListCtrl, getSelectedItemCount, 0}},
+ {1693, {wxListCtrl, getTextColour, 0}},
+ {1694, {wxListCtrl, getTopItem, 0}},
+ {1695, {wxListCtrl, getViewRect, 0}},
+ {1696, {wxListCtrl, hitTest, 3}},
+ {1697, {wxListCtrl, insertColumn_2, 2}},
+ {1698, {wxListCtrl, insertColumn_3, 3}},
+ {1699, {wxListCtrl, insertItem_1, 1}},
+ {1700, {wxListCtrl, insertItem_2_1, 2}},
+ {1701, {wxListCtrl, insertItem_2_0, 2}},
+ {1702, {wxListCtrl, insertItem_3, 3}},
+ {1703, {wxListCtrl, refreshItem, 1}},
+ {1704, {wxListCtrl, refreshItems, 2}},
+ {1705, {wxListCtrl, scrollList, 2}},
+ {1706, {wxListCtrl, setBackgroundColour, 1}},
+ {1707, {wxListCtrl, setColumn, 2}},
+ {1708, {wxListCtrl, setColumnWidth, 2}},
+ {1709, {wxListCtrl, setImageList, 2}},
+ {1710, {wxListCtrl, setItem_1, 1}},
+ {1711, {wxListCtrl, setItem_4, 4}},
+ {1712, {wxListCtrl, setItemBackgroundColour, 2}},
+ {1713, {wxListCtrl, setItemCount, 1}},
+ {1714, {wxListCtrl, setItemData, 2}},
+ {1715, {wxListCtrl, setItemFont, 2}},
+ {1716, {wxListCtrl, setItemImage, 3}},
+ {1717, {wxListCtrl, setItemColumnImage, 3}},
+ {1718, {wxListCtrl, setItemPosition, 2}},
+ {1719, {wxListCtrl, setItemState, 3}},
+ {1720, {wxListCtrl, setItemText, 2}},
+ {1721, {wxListCtrl, setItemTextColour, 2}},
+ {1722, {wxListCtrl, setSingleStyle, 2}},
+ {1723, {wxListCtrl, setTextColour, 1}},
+ {1724, {wxListCtrl, setWindowStyleFlag, 1}},
+ {1725, {wxListCtrl, sortItems, 2}},
+ {1726, {wxListCtrl, 'Destroy', undefined}},
+ {1727, {wxListView, clearColumnImage, 1}},
+ {1728, {wxListView, focus, 1}},
+ {1729, {wxListView, getFirstSelected, 0}},
+ {1730, {wxListView, getFocusedItem, 0}},
+ {1731, {wxListView, getNextSelected, 1}},
+ {1732, {wxListView, isSelected, 1}},
+ {1733, {wxListView, select, 2}},
+ {1734, {wxListView, setColumnImage, 2}},
+ {1735, {wxListItem, new_0, 0}},
+ {1736, {wxListItem, new_1, 1}},
+ {1737, {wxListItem, destruct, 0}},
+ {1738, {wxListItem, clear, 0}},
+ {1739, {wxListItem, getAlign, 0}},
+ {1740, {wxListItem, getBackgroundColour, 0}},
+ {1741, {wxListItem, getColumn, 0}},
+ {1742, {wxListItem, getFont, 0}},
+ {1743, {wxListItem, getId, 0}},
+ {1744, {wxListItem, getImage, 0}},
+ {1745, {wxListItem, getMask, 0}},
+ {1746, {wxListItem, getState, 0}},
+ {1747, {wxListItem, getText, 0}},
+ {1748, {wxListItem, getTextColour, 0}},
+ {1749, {wxListItem, getWidth, 0}},
+ {1750, {wxListItem, setAlign, 1}},
+ {1751, {wxListItem, setBackgroundColour, 1}},
+ {1752, {wxListItem, setColumn, 1}},
+ {1753, {wxListItem, setFont, 1}},
+ {1754, {wxListItem, setId, 1}},
+ {1755, {wxListItem, setImage, 1}},
+ {1756, {wxListItem, setMask, 1}},
+ {1757, {wxListItem, setState, 1}},
+ {1758, {wxListItem, setStateMask, 1}},
+ {1759, {wxListItem, setText, 1}},
+ {1760, {wxListItem, setTextColour, 1}},
+ {1761, {wxListItem, setWidth, 1}},
+ {1762, {wxListItemAttr, new_0, 0}},
+ {1763, {wxListItemAttr, new_3, 3}},
+ {1764, {wxListItemAttr, getBackgroundColour, 0}},
+ {1765, {wxListItemAttr, getFont, 0}},
+ {1766, {wxListItemAttr, getTextColour, 0}},
+ {1767, {wxListItemAttr, hasBackgroundColour, 0}},
+ {1768, {wxListItemAttr, hasFont, 0}},
+ {1769, {wxListItemAttr, hasTextColour, 0}},
+ {1770, {wxListItemAttr, setBackgroundColour, 1}},
+ {1771, {wxListItemAttr, setFont, 1}},
+ {1772, {wxListItemAttr, setTextColour, 1}},
+ {1773, {wxListItemAttr, 'Destroy', undefined}},
+ {1774, {wxImageList, new_0, 0}},
+ {1775, {wxImageList, new_3, 3}},
+ {1776, {wxImageList, add_1, 1}},
+ {1777, {wxImageList, add_2_0, 2}},
+ {1778, {wxImageList, add_2_1, 2}},
+ {1779, {wxImageList, create, 3}},
+ {1781, {wxImageList, draw, 5}},
+ {1782, {wxImageList, getBitmap, 1}},
+ {1783, {wxImageList, getIcon, 1}},
+ {1784, {wxImageList, getImageCount, 0}},
+ {1785, {wxImageList, getSize, 3}},
+ {1786, {wxImageList, remove, 1}},
+ {1787, {wxImageList, removeAll, 0}},
+ {1788, {wxImageList, replace_2, 2}},
+ {1789, {wxImageList, replace_3, 3}},
+ {1790, {wxImageList, 'Destroy', undefined}},
+ {1791, {wxTextAttr, new_0, 0}},
+ {1792, {wxTextAttr, new_2, 2}},
+ {1793, {wxTextAttr, getAlignment, 0}},
+ {1794, {wxTextAttr, getBackgroundColour, 0}},
+ {1795, {wxTextAttr, getFont, 0}},
+ {1796, {wxTextAttr, getLeftIndent, 0}},
+ {1797, {wxTextAttr, getLeftSubIndent, 0}},
+ {1798, {wxTextAttr, getRightIndent, 0}},
+ {1799, {wxTextAttr, getTabs, 0}},
+ {1800, {wxTextAttr, getTextColour, 0}},
+ {1801, {wxTextAttr, hasBackgroundColour, 0}},
+ {1802, {wxTextAttr, hasFont, 0}},
+ {1803, {wxTextAttr, hasTextColour, 0}},
+ {1804, {wxTextAttr, getFlags, 0}},
+ {1805, {wxTextAttr, isDefault, 0}},
+ {1806, {wxTextAttr, setAlignment, 1}},
+ {1807, {wxTextAttr, setBackgroundColour, 1}},
+ {1808, {wxTextAttr, setFlags, 1}},
+ {1809, {wxTextAttr, setFont, 2}},
+ {1810, {wxTextAttr, setLeftIndent, 2}},
+ {1811, {wxTextAttr, setRightIndent, 1}},
+ {1812, {wxTextAttr, setTabs, 1}},
+ {1813, {wxTextAttr, setTextColour, 1}},
+ {1814, {wxTextAttr, 'Destroy', undefined}},
+ {1816, {wxTextCtrl, new_3, 3}},
+ {1817, {wxTextCtrl, new_0, 0}},
+ {1819, {wxTextCtrl, destruct, 0}},
+ {1820, {wxTextCtrl, appendText, 1}},
+ {1821, {wxTextCtrl, canCopy, 0}},
+ {1822, {wxTextCtrl, canCut, 0}},
+ {1823, {wxTextCtrl, canPaste, 0}},
+ {1824, {wxTextCtrl, canRedo, 0}},
+ {1825, {wxTextCtrl, canUndo, 0}},
+ {1826, {wxTextCtrl, clear, 0}},
+ {1827, {wxTextCtrl, copy, 0}},
+ {1828, {wxTextCtrl, create, 3}},
+ {1829, {wxTextCtrl, cut, 0}},
+ {1830, {wxTextCtrl, discardEdits, 0}},
+ {1831, {wxTextCtrl, changeValue, 1}},
+ {1832, {wxTextCtrl, emulateKeyPress, 1}},
+ {1833, {wxTextCtrl, getDefaultStyle, 0}},
+ {1834, {wxTextCtrl, getInsertionPoint, 0}},
+ {1835, {wxTextCtrl, getLastPosition, 0}},
+ {1836, {wxTextCtrl, getLineLength, 1}},
+ {1837, {wxTextCtrl, getLineText, 1}},
+ {1838, {wxTextCtrl, getNumberOfLines, 0}},
+ {1839, {wxTextCtrl, getRange, 2}},
+ {1840, {wxTextCtrl, getSelection, 2}},
+ {1841, {wxTextCtrl, getStringSelection, 0}},
+ {1842, {wxTextCtrl, getStyle, 2}},
+ {1843, {wxTextCtrl, getValue, 0}},
+ {1844, {wxTextCtrl, isEditable, 0}},
+ {1845, {wxTextCtrl, isModified, 0}},
+ {1846, {wxTextCtrl, isMultiLine, 0}},
+ {1847, {wxTextCtrl, isSingleLine, 0}},
+ {1848, {wxTextCtrl, loadFile, 2}},
+ {1849, {wxTextCtrl, markDirty, 0}},
+ {1850, {wxTextCtrl, paste, 0}},
+ {1851, {wxTextCtrl, positionToXY, 3}},
+ {1852, {wxTextCtrl, redo, 0}},
+ {1853, {wxTextCtrl, remove, 2}},
+ {1854, {wxTextCtrl, replace, 3}},
+ {1855, {wxTextCtrl, saveFile, 1}},
+ {1856, {wxTextCtrl, setDefaultStyle, 1}},
+ {1857, {wxTextCtrl, setEditable, 1}},
+ {1858, {wxTextCtrl, setInsertionPoint, 1}},
+ {1859, {wxTextCtrl, setInsertionPointEnd, 0}},
+ {1861, {wxTextCtrl, setMaxLength, 1}},
+ {1862, {wxTextCtrl, setSelection, 2}},
+ {1863, {wxTextCtrl, setStyle, 3}},
+ {1864, {wxTextCtrl, setValue, 1}},
+ {1865, {wxTextCtrl, showPosition, 1}},
+ {1866, {wxTextCtrl, undo, 0}},
+ {1867, {wxTextCtrl, writeText, 1}},
+ {1868, {wxTextCtrl, xYToPosition, 2}},
+ {1871, {wxNotebook, new_0, 0}},
+ {1872, {wxNotebook, new_3, 3}},
+ {1873, {wxNotebook, destruct, 0}},
+ {1874, {wxNotebook, addPage, 3}},
+ {1875, {wxNotebook, advanceSelection, 1}},
+ {1876, {wxNotebook, assignImageList, 1}},
+ {1877, {wxNotebook, create, 3}},
+ {1878, {wxNotebook, deleteAllPages, 0}},
+ {1879, {wxNotebook, deletePage, 1}},
+ {1880, {wxNotebook, removePage, 1}},
+ {1881, {wxNotebook, getCurrentPage, 0}},
+ {1882, {wxNotebook, getImageList, 0}},
+ {1884, {wxNotebook, getPage, 1}},
+ {1885, {wxNotebook, getPageCount, 0}},
+ {1886, {wxNotebook, getPageImage, 1}},
+ {1887, {wxNotebook, getPageText, 1}},
+ {1888, {wxNotebook, getRowCount, 0}},
+ {1889, {wxNotebook, getSelection, 0}},
+ {1890, {wxNotebook, getThemeBackgroundColour, 0}},
+ {1892, {wxNotebook, hitTest, 2}},
+ {1894, {wxNotebook, insertPage, 4}},
+ {1895, {wxNotebook, setImageList, 1}},
+ {1896, {wxNotebook, setPadding, 1}},
+ {1897, {wxNotebook, setPageSize, 1}},
+ {1898, {wxNotebook, setPageImage, 2}},
+ {1899, {wxNotebook, setPageText, 2}},
+ {1900, {wxNotebook, setSelection, 1}},
+ {1901, {wxNotebook, changeSelection, 1}},
+ {1902, {wxChoicebook, new_0, 0}},
+ {1903, {wxChoicebook, new_3, 3}},
+ {1904, {wxChoicebook, addPage, 3}},
+ {1905, {wxChoicebook, advanceSelection, 1}},
+ {1906, {wxChoicebook, assignImageList, 1}},
+ {1907, {wxChoicebook, create, 3}},
+ {1908, {wxChoicebook, deleteAllPages, 0}},
+ {1909, {wxChoicebook, deletePage, 1}},
+ {1910, {wxChoicebook, removePage, 1}},
+ {1911, {wxChoicebook, getCurrentPage, 0}},
+ {1912, {wxChoicebook, getImageList, 0}},
+ {1914, {wxChoicebook, getPage, 1}},
+ {1915, {wxChoicebook, getPageCount, 0}},
+ {1916, {wxChoicebook, getPageImage, 1}},
+ {1917, {wxChoicebook, getPageText, 1}},
+ {1918, {wxChoicebook, getSelection, 0}},
+ {1919, {wxChoicebook, hitTest, 2}},
+ {1920, {wxChoicebook, insertPage, 4}},
+ {1921, {wxChoicebook, setImageList, 1}},
+ {1922, {wxChoicebook, setPageSize, 1}},
+ {1923, {wxChoicebook, setPageImage, 2}},
+ {1924, {wxChoicebook, setPageText, 2}},
+ {1925, {wxChoicebook, setSelection, 1}},
+ {1926, {wxChoicebook, changeSelection, 1}},
+ {1927, {wxChoicebook, 'Destroy', undefined}},
+ {1928, {wxToolbook, new_0, 0}},
+ {1929, {wxToolbook, new_3, 3}},
+ {1930, {wxToolbook, addPage, 3}},
+ {1931, {wxToolbook, advanceSelection, 1}},
+ {1932, {wxToolbook, assignImageList, 1}},
+ {1933, {wxToolbook, create, 3}},
+ {1934, {wxToolbook, deleteAllPages, 0}},
+ {1935, {wxToolbook, deletePage, 1}},
+ {1936, {wxToolbook, removePage, 1}},
+ {1937, {wxToolbook, getCurrentPage, 0}},
+ {1938, {wxToolbook, getImageList, 0}},
+ {1940, {wxToolbook, getPage, 1}},
+ {1941, {wxToolbook, getPageCount, 0}},
+ {1942, {wxToolbook, getPageImage, 1}},
+ {1943, {wxToolbook, getPageText, 1}},
+ {1944, {wxToolbook, getSelection, 0}},
+ {1946, {wxToolbook, hitTest, 2}},
+ {1947, {wxToolbook, insertPage, 4}},
+ {1948, {wxToolbook, setImageList, 1}},
+ {1949, {wxToolbook, setPageSize, 1}},
+ {1950, {wxToolbook, setPageImage, 2}},
+ {1951, {wxToolbook, setPageText, 2}},
+ {1952, {wxToolbook, setSelection, 1}},
+ {1953, {wxToolbook, changeSelection, 1}},
+ {1954, {wxToolbook, 'Destroy', undefined}},
+ {1955, {wxListbook, new_0, 0}},
+ {1956, {wxListbook, new_3, 3}},
+ {1957, {wxListbook, addPage, 3}},
+ {1958, {wxListbook, advanceSelection, 1}},
+ {1959, {wxListbook, assignImageList, 1}},
+ {1960, {wxListbook, create, 3}},
+ {1961, {wxListbook, deleteAllPages, 0}},
+ {1962, {wxListbook, deletePage, 1}},
+ {1963, {wxListbook, removePage, 1}},
+ {1964, {wxListbook, getCurrentPage, 0}},
+ {1965, {wxListbook, getImageList, 0}},
+ {1967, {wxListbook, getPage, 1}},
+ {1968, {wxListbook, getPageCount, 0}},
+ {1969, {wxListbook, getPageImage, 1}},
+ {1970, {wxListbook, getPageText, 1}},
+ {1971, {wxListbook, getSelection, 0}},
+ {1973, {wxListbook, hitTest, 2}},
+ {1974, {wxListbook, insertPage, 4}},
+ {1975, {wxListbook, setImageList, 1}},
+ {1976, {wxListbook, setPageSize, 1}},
+ {1977, {wxListbook, setPageImage, 2}},
+ {1978, {wxListbook, setPageText, 2}},
+ {1979, {wxListbook, setSelection, 1}},
+ {1980, {wxListbook, changeSelection, 1}},
+ {1981, {wxListbook, 'Destroy', undefined}},
+ {1982, {wxTreebook, new_0, 0}},
+ {1983, {wxTreebook, new_3, 3}},
+ {1984, {wxTreebook, addPage, 3}},
+ {1985, {wxTreebook, advanceSelection, 1}},
+ {1986, {wxTreebook, assignImageList, 1}},
+ {1987, {wxTreebook, create, 3}},
+ {1988, {wxTreebook, deleteAllPages, 0}},
+ {1989, {wxTreebook, deletePage, 1}},
+ {1990, {wxTreebook, removePage, 1}},
+ {1991, {wxTreebook, getCurrentPage, 0}},
+ {1992, {wxTreebook, getImageList, 0}},
+ {1994, {wxTreebook, getPage, 1}},
+ {1995, {wxTreebook, getPageCount, 0}},
+ {1996, {wxTreebook, getPageImage, 1}},
+ {1997, {wxTreebook, getPageText, 1}},
+ {1998, {wxTreebook, getSelection, 0}},
+ {1999, {wxTreebook, expandNode, 2}},
+ {2000, {wxTreebook, isNodeExpanded, 1}},
+ {2002, {wxTreebook, hitTest, 2}},
+ {2003, {wxTreebook, insertPage, 4}},
+ {2004, {wxTreebook, insertSubPage, 4}},
+ {2005, {wxTreebook, setImageList, 1}},
+ {2006, {wxTreebook, setPageSize, 1}},
+ {2007, {wxTreebook, setPageImage, 2}},
+ {2008, {wxTreebook, setPageText, 2}},
+ {2009, {wxTreebook, setSelection, 1}},
+ {2010, {wxTreebook, changeSelection, 1}},
+ {2011, {wxTreebook, 'Destroy', undefined}},
+ {2014, {wxTreeCtrl, new_2, 2}},
+ {2015, {wxTreeCtrl, new_0, 0}},
+ {2017, {wxTreeCtrl, destruct, 0}},
+ {2018, {wxTreeCtrl, addRoot, 2}},
+ {2019, {wxTreeCtrl, appendItem, 3}},
+ {2020, {wxTreeCtrl, assignImageList, 1}},
+ {2021, {wxTreeCtrl, assignStateImageList, 1}},
+ {2022, {wxTreeCtrl, collapse, 1}},
+ {2023, {wxTreeCtrl, collapseAndReset, 1}},
+ {2024, {wxTreeCtrl, create, 2}},
+ {2025, {wxTreeCtrl, delete, 1}},
+ {2026, {wxTreeCtrl, deleteAllItems, 0}},
+ {2027, {wxTreeCtrl, deleteChildren, 1}},
+ {2028, {wxTreeCtrl, editLabel, 1}},
+ {2029, {wxTreeCtrl, ensureVisible, 1}},
+ {2030, {wxTreeCtrl, expand, 1}},
+ {2031, {wxTreeCtrl, getBoundingRect, 3}},
+ {2033, {wxTreeCtrl, getChildrenCount, 2}},
+ {2034, {wxTreeCtrl, getCount, 0}},
+ {2035, {wxTreeCtrl, getEditControl, 0}},
+ {2036, {wxTreeCtrl, getFirstChild, 2}},
+ {2037, {wxTreeCtrl, getNextChild, 2}},
+ {2038, {wxTreeCtrl, getFirstVisibleItem, 0}},
+ {2039, {wxTreeCtrl, getImageList, 0}},
+ {2040, {wxTreeCtrl, getIndent, 0}},
+ {2041, {wxTreeCtrl, getItemBackgroundColour, 1}},
+ {2042, {wxTreeCtrl, getItemData, 1}},
+ {2043, {wxTreeCtrl, getItemFont, 1}},
+ {2044, {wxTreeCtrl, getItemImage_1, 1}},
+ {2045, {wxTreeCtrl, getItemImage_2, 2}},
+ {2046, {wxTreeCtrl, getItemText, 1}},
+ {2047, {wxTreeCtrl, getItemTextColour, 1}},
+ {2048, {wxTreeCtrl, getLastChild, 1}},
+ {2049, {wxTreeCtrl, getNextSibling, 1}},
+ {2050, {wxTreeCtrl, getNextVisible, 1}},
+ {2051, {wxTreeCtrl, getItemParent, 1}},
+ {2052, {wxTreeCtrl, getPrevSibling, 1}},
+ {2053, {wxTreeCtrl, getPrevVisible, 1}},
+ {2054, {wxTreeCtrl, getRootItem, 0}},
+ {2055, {wxTreeCtrl, getSelection, 0}},
+ {2056, {wxTreeCtrl, getSelections, 1}},
+ {2057, {wxTreeCtrl, getStateImageList, 0}},
+ {2058, {wxTreeCtrl, hitTest, 2}},
+ {2060, {wxTreeCtrl, insertItem, 4}},
+ {2061, {wxTreeCtrl, isBold, 1}},
+ {2062, {wxTreeCtrl, isExpanded, 1}},
+ {2063, {wxTreeCtrl, isSelected, 1}},
+ {2064, {wxTreeCtrl, isVisible, 1}},
+ {2065, {wxTreeCtrl, itemHasChildren, 1}},
+ {2066, {wxTreeCtrl, isTreeItemIdOk, 1}},
+ {2067, {wxTreeCtrl, prependItem, 3}},
+ {2068, {wxTreeCtrl, scrollTo, 1}},
+ {2069, {wxTreeCtrl, selectItem_1, 1}},
+ {2070, {wxTreeCtrl, selectItem_2, 2}},
+ {2071, {wxTreeCtrl, setIndent, 1}},
+ {2072, {wxTreeCtrl, setImageList, 1}},
+ {2073, {wxTreeCtrl, setItemBackgroundColour, 2}},
+ {2074, {wxTreeCtrl, setItemBold, 2}},
+ {2075, {wxTreeCtrl, setItemData, 2}},
+ {2076, {wxTreeCtrl, setItemDropHighlight, 2}},
+ {2077, {wxTreeCtrl, setItemFont, 2}},
+ {2078, {wxTreeCtrl, setItemHasChildren, 2}},
+ {2079, {wxTreeCtrl, setItemImage_2, 2}},
+ {2080, {wxTreeCtrl, setItemImage_3, 3}},
+ {2081, {wxTreeCtrl, setItemText, 2}},
+ {2082, {wxTreeCtrl, setItemTextColour, 2}},
+ {2083, {wxTreeCtrl, setStateImageList, 1}},
+ {2084, {wxTreeCtrl, setWindowStyle, 1}},
+ {2085, {wxTreeCtrl, sortChildren, 1}},
+ {2086, {wxTreeCtrl, toggle, 1}},
+ {2087, {wxTreeCtrl, toggleItemSelection, 1}},
+ {2088, {wxTreeCtrl, unselect, 0}},
+ {2089, {wxTreeCtrl, unselectAll, 0}},
+ {2090, {wxTreeCtrl, unselectItem, 1}},
+ {2091, {wxScrollBar, new_0, 0}},
+ {2092, {wxScrollBar, new_3, 3}},
+ {2093, {wxScrollBar, destruct, 0}},
+ {2094, {wxScrollBar, create, 3}},
+ {2095, {wxScrollBar, getRange, 0}},
+ {2096, {wxScrollBar, getPageSize, 0}},
+ {2097, {wxScrollBar, getThumbPosition, 0}},
+ {2098, {wxScrollBar, getThumbSize, 0}},
+ {2099, {wxScrollBar, setThumbPosition, 1}},
+ {2100, {wxScrollBar, setScrollbar, 5}},
+ {2102, {wxSpinButton, new_2, 2}},
+ {2103, {wxSpinButton, new_0, 0}},
+ {2104, {wxSpinButton, create, 2}},
+ {2105, {wxSpinButton, getMax, 0}},
+ {2106, {wxSpinButton, getMin, 0}},
+ {2107, {wxSpinButton, getValue, 0}},
+ {2108, {wxSpinButton, setRange, 2}},
+ {2109, {wxSpinButton, setValue, 1}},
+ {2110, {wxSpinButton, 'Destroy', undefined}},
+ {2111, {wxSpinCtrl, new_0, 0}},
+ {2112, {wxSpinCtrl, new_2, 2}},
+ {2114, {wxSpinCtrl, create, 2}},
+ {2117, {wxSpinCtrl, setValue_1_1, 1}},
+ {2118, {wxSpinCtrl, setValue_1_0, 1}},
+ {2120, {wxSpinCtrl, getValue, 0}},
+ {2122, {wxSpinCtrl, setRange, 2}},
+ {2123, {wxSpinCtrl, setSelection, 2}},
+ {2125, {wxSpinCtrl, getMin, 0}},
+ {2127, {wxSpinCtrl, getMax, 0}},
+ {2128, {wxSpinCtrl, 'Destroy', undefined}},
+ {2129, {wxStaticText, new_0, 0}},
+ {2130, {wxStaticText, new_4, 4}},
+ {2131, {wxStaticText, create, 4}},
+ {2132, {wxStaticText, getLabel, 0}},
+ {2133, {wxStaticText, setLabel, 1}},
+ {2134, {wxStaticText, wrap, 1}},
+ {2135, {wxStaticText, 'Destroy', undefined}},
+ {2136, {wxStaticBitmap, new_0, 0}},
+ {2137, {wxStaticBitmap, new_4, 4}},
+ {2138, {wxStaticBitmap, create, 4}},
+ {2139, {wxStaticBitmap, getBitmap, 0}},
+ {2140, {wxStaticBitmap, setBitmap, 1}},
+ {2141, {wxStaticBitmap, 'Destroy', undefined}},
+ {2142, {wxRadioBox, new, 7}},
+ {2144, {wxRadioBox, destruct, 0}},
+ {2145, {wxRadioBox, create, 7}},
+ {2146, {wxRadioBox, enable_2, 2}},
+ {2147, {wxRadioBox, enable_1, 1}},
+ {2148, {wxRadioBox, getSelection, 0}},
+ {2149, {wxRadioBox, getString, 1}},
+ {2150, {wxRadioBox, setSelection, 1}},
+ {2151, {wxRadioBox, show_2, 2}},
+ {2152, {wxRadioBox, show_1, 1}},
+ {2153, {wxRadioBox, getColumnCount, 0}},
+ {2154, {wxRadioBox, getItemHelpText, 1}},
+ {2155, {wxRadioBox, getItemToolTip, 1}},
+ {2157, {wxRadioBox, getItemFromPoint, 1}},
+ {2158, {wxRadioBox, getRowCount, 0}},
+ {2159, {wxRadioBox, isItemEnabled, 1}},
+ {2160, {wxRadioBox, isItemShown, 1}},
+ {2161, {wxRadioBox, setItemHelpText, 2}},
+ {2162, {wxRadioBox, setItemToolTip, 2}},
+ {2163, {wxRadioButton, new_0, 0}},
+ {2164, {wxRadioButton, new_4, 4}},
+ {2165, {wxRadioButton, create, 4}},
+ {2166, {wxRadioButton, getValue, 0}},
+ {2167, {wxRadioButton, setValue, 1}},
+ {2168, {wxRadioButton, 'Destroy', undefined}},
+ {2170, {wxSlider, new_6, 6}},
+ {2171, {wxSlider, new_0, 0}},
+ {2172, {wxSlider, create, 6}},
+ {2173, {wxSlider, getLineSize, 0}},
+ {2174, {wxSlider, getMax, 0}},
+ {2175, {wxSlider, getMin, 0}},
+ {2176, {wxSlider, getPageSize, 0}},
+ {2177, {wxSlider, getThumbLength, 0}},
+ {2178, {wxSlider, getValue, 0}},
+ {2179, {wxSlider, setLineSize, 1}},
+ {2180, {wxSlider, setPageSize, 1}},
+ {2181, {wxSlider, setRange, 2}},
+ {2182, {wxSlider, setThumbLength, 1}},
+ {2183, {wxSlider, setValue, 1}},
+ {2184, {wxSlider, 'Destroy', undefined}},
+ {2186, {wxDialog, new_4, 4}},
+ {2187, {wxDialog, new_0, 0}},
+ {2189, {wxDialog, destruct, 0}},
+ {2190, {wxDialog, create, 4}},
+ {2191, {wxDialog, createButtonSizer, 1}},
+ {2192, {wxDialog, createStdDialogButtonSizer, 1}},
+ {2193, {wxDialog, endModal, 1}},
+ {2194, {wxDialog, getAffirmativeId, 0}},
+ {2195, {wxDialog, getReturnCode, 0}},
+ {2196, {wxDialog, isModal, 0}},
+ {2197, {wxDialog, setAffirmativeId, 1}},
+ {2198, {wxDialog, setReturnCode, 1}},
+ {2199, {wxDialog, show, 1}},
+ {2200, {wxDialog, showModal, 0}},
+ {2201, {wxColourDialog, new_0, 0}},
+ {2202, {wxColourDialog, new_2, 2}},
+ {2203, {wxColourDialog, destruct, 0}},
+ {2204, {wxColourDialog, create, 2}},
+ {2205, {wxColourDialog, getColourData, 0}},
+ {2206, {wxColourData, new_0, 0}},
+ {2207, {wxColourData, new_1, 1}},
+ {2208, {wxColourData, destruct, 0}},
+ {2209, {wxColourData, getChooseFull, 0}},
+ {2210, {wxColourData, getColour, 0}},
+ {2212, {wxColourData, getCustomColour, 1}},
+ {2213, {wxColourData, setChooseFull, 1}},
+ {2214, {wxColourData, setColour, 1}},
+ {2215, {wxColourData, setCustomColour, 2}},
+ {2216, {wxPalette, new_0, 0}},
+ {2217, {wxPalette, new_4, 4}},
+ {2219, {wxPalette, destruct, 0}},
+ {2220, {wxPalette, create, 4}},
+ {2221, {wxPalette, getColoursCount, 0}},
+ {2222, {wxPalette, getPixel, 3}},
+ {2223, {wxPalette, getRGB, 4}},
+ {2224, {wxPalette, isOk, 0}},
+ {2228, {wxDirDialog, new, 2}},
+ {2229, {wxDirDialog, destruct, 0}},
+ {2230, {wxDirDialog, getPath, 0}},
+ {2231, {wxDirDialog, getMessage, 0}},
+ {2232, {wxDirDialog, setMessage, 1}},
+ {2233, {wxDirDialog, setPath, 1}},
+ {2237, {wxFileDialog, new, 2}},
+ {2238, {wxFileDialog, destruct, 0}},
+ {2239, {wxFileDialog, getDirectory, 0}},
+ {2240, {wxFileDialog, getFilename, 0}},
+ {2241, {wxFileDialog, getFilenames, 1}},
+ {2242, {wxFileDialog, getFilterIndex, 0}},
+ {2243, {wxFileDialog, getMessage, 0}},
+ {2244, {wxFileDialog, getPath, 0}},
+ {2245, {wxFileDialog, getPaths, 1}},
+ {2246, {wxFileDialog, getWildcard, 0}},
+ {2247, {wxFileDialog, setDirectory, 1}},
+ {2248, {wxFileDialog, setFilename, 1}},
+ {2249, {wxFileDialog, setFilterIndex, 1}},
+ {2250, {wxFileDialog, setMessage, 1}},
+ {2251, {wxFileDialog, setPath, 1}},
+ {2252, {wxFileDialog, setWildcard, 1}},
+ {2253, {wxPickerBase, setInternalMargin, 1}},
+ {2254, {wxPickerBase, getInternalMargin, 0}},
+ {2255, {wxPickerBase, setTextCtrlProportion, 1}},
+ {2256, {wxPickerBase, setPickerCtrlProportion, 1}},
+ {2257, {wxPickerBase, getTextCtrlProportion, 0}},
+ {2258, {wxPickerBase, getPickerCtrlProportion, 0}},
+ {2259, {wxPickerBase, hasTextCtrl, 0}},
+ {2260, {wxPickerBase, getTextCtrl, 0}},
+ {2261, {wxPickerBase, isTextCtrlGrowable, 0}},
+ {2262, {wxPickerBase, setPickerCtrlGrowable, 1}},
+ {2263, {wxPickerBase, setTextCtrlGrowable, 1}},
+ {2264, {wxPickerBase, isPickerCtrlGrowable, 0}},
+ {2265, {wxFilePickerCtrl, new_0, 0}},
+ {2266, {wxFilePickerCtrl, new_3, 3}},
+ {2267, {wxFilePickerCtrl, create, 3}},
+ {2268, {wxFilePickerCtrl, getPath, 0}},
+ {2269, {wxFilePickerCtrl, setPath, 1}},
+ {2270, {wxFilePickerCtrl, 'Destroy', undefined}},
+ {2271, {wxDirPickerCtrl, new_0, 0}},
+ {2272, {wxDirPickerCtrl, new_3, 3}},
+ {2273, {wxDirPickerCtrl, create, 3}},
+ {2274, {wxDirPickerCtrl, getPath, 0}},
+ {2275, {wxDirPickerCtrl, setPath, 1}},
+ {2276, {wxDirPickerCtrl, 'Destroy', undefined}},
+ {2277, {wxColourPickerCtrl, new_0, 0}},
+ {2278, {wxColourPickerCtrl, new_3, 3}},
+ {2279, {wxColourPickerCtrl, create, 3}},
+ {2280, {wxColourPickerCtrl, getColour, 0}},
+ {2281, {wxColourPickerCtrl, setColour_1_1, 1}},
+ {2282, {wxColourPickerCtrl, setColour_1_0, 1}},
+ {2283, {wxColourPickerCtrl, 'Destroy', undefined}},
+ {2284, {wxDatePickerCtrl, new_0, 0}},
+ {2285, {wxDatePickerCtrl, new_3, 3}},
+ {2286, {wxDatePickerCtrl, getRange, 2}},
+ {2287, {wxDatePickerCtrl, getValue, 0}},
+ {2288, {wxDatePickerCtrl, setRange, 2}},
+ {2289, {wxDatePickerCtrl, setValue, 1}},
+ {2290, {wxDatePickerCtrl, 'Destroy', undefined}},
+ {2291, {wxFontPickerCtrl, new_0, 0}},
+ {2292, {wxFontPickerCtrl, new_3, 3}},
+ {2293, {wxFontPickerCtrl, create, 3}},
+ {2294, {wxFontPickerCtrl, getSelectedFont, 0}},
+ {2295, {wxFontPickerCtrl, setSelectedFont, 1}},
+ {2296, {wxFontPickerCtrl, getMaxPointSize, 0}},
+ {2297, {wxFontPickerCtrl, setMaxPointSize, 1}},
+ {2298, {wxFontPickerCtrl, 'Destroy', undefined}},
+ {2301, {wxFindReplaceDialog, new_0, 0}},
+ {2302, {wxFindReplaceDialog, new_4, 4}},
+ {2303, {wxFindReplaceDialog, destruct, 0}},
+ {2304, {wxFindReplaceDialog, create, 4}},
+ {2305, {wxFindReplaceDialog, getData, 0}},
+ {2306, {wxFindReplaceData, new_0, 0}},
+ {2307, {wxFindReplaceData, new_1, 1}},
+ {2308, {wxFindReplaceData, getFindString, 0}},
+ {2309, {wxFindReplaceData, getReplaceString, 0}},
+ {2310, {wxFindReplaceData, getFlags, 0}},
+ {2311, {wxFindReplaceData, setFlags, 1}},
+ {2312, {wxFindReplaceData, setFindString, 1}},
+ {2313, {wxFindReplaceData, setReplaceString, 1}},
+ {2314, {wxFindReplaceData, 'Destroy', undefined}},
+ {2315, {wxMultiChoiceDialog, new_0, 0}},
+ {2317, {wxMultiChoiceDialog, new_5, 5}},
+ {2318, {wxMultiChoiceDialog, getSelections, 0}},
+ {2319, {wxMultiChoiceDialog, setSelections, 1}},
+ {2320, {wxMultiChoiceDialog, 'Destroy', undefined}},
+ {2321, {wxSingleChoiceDialog, new_0, 0}},
+ {2323, {wxSingleChoiceDialog, new_5, 5}},
+ {2324, {wxSingleChoiceDialog, getSelection, 0}},
+ {2325, {wxSingleChoiceDialog, getStringSelection, 0}},
+ {2326, {wxSingleChoiceDialog, setSelection, 1}},
+ {2327, {wxSingleChoiceDialog, 'Destroy', undefined}},
+ {2328, {wxTextEntryDialog, new, 3}},
+ {2329, {wxTextEntryDialog, getValue, 0}},
+ {2330, {wxTextEntryDialog, setValue, 1}},
+ {2331, {wxTextEntryDialog, 'Destroy', undefined}},
+ {2332, {wxPasswordEntryDialog, new, 3}},
+ {2333, {wxPasswordEntryDialog, 'Destroy', undefined}},
+ {2334, {wxFontData, new_0, 0}},
+ {2335, {wxFontData, new_1, 1}},
+ {2336, {wxFontData, destruct, 0}},
+ {2337, {wxFontData, enableEffects, 1}},
+ {2338, {wxFontData, getAllowSymbols, 0}},
+ {2339, {wxFontData, getColour, 0}},
+ {2340, {wxFontData, getChosenFont, 0}},
+ {2341, {wxFontData, getEnableEffects, 0}},
+ {2342, {wxFontData, getInitialFont, 0}},
+ {2343, {wxFontData, getShowHelp, 0}},
+ {2344, {wxFontData, setAllowSymbols, 1}},
+ {2345, {wxFontData, setChosenFont, 1}},
+ {2346, {wxFontData, setColour, 1}},
+ {2347, {wxFontData, setInitialFont, 1}},
+ {2348, {wxFontData, setRange, 2}},
+ {2349, {wxFontData, setShowHelp, 1}},
+ {2353, {wxFontDialog, new_0, 0}},
+ {2355, {wxFontDialog, new_2, 2}},
+ {2357, {wxFontDialog, create, 2}},
+ {2358, {wxFontDialog, getFontData, 0}},
+ {2360, {wxFontDialog, 'Destroy', undefined}},
+ {2361, {wxProgressDialog, new, 3}},
+ {2362, {wxProgressDialog, destruct, 0}},
+ {2363, {wxProgressDialog, resume, 0}},
+ {2364, {wxProgressDialog, update_2, 2}},
+ {2365, {wxProgressDialog, update_0, 0}},
+ {2366, {wxMessageDialog, new, 3}},
+ {2367, {wxMessageDialog, destruct, 0}},
+ {2368, {wxPageSetupDialog, new, 2}},
+ {2369, {wxPageSetupDialog, destruct, 0}},
+ {2370, {wxPageSetupDialog, getPageSetupData, 0}},
+ {2371, {wxPageSetupDialog, showModal, 0}},
+ {2372, {wxPageSetupDialogData, new_0, 0}},
+ {2373, {wxPageSetupDialogData, new_1_0, 1}},
+ {2374, {wxPageSetupDialogData, new_1_1, 1}},
+ {2375, {wxPageSetupDialogData, destruct, 0}},
+ {2376, {wxPageSetupDialogData, enableHelp, 1}},
+ {2377, {wxPageSetupDialogData, enableMargins, 1}},
+ {2378, {wxPageSetupDialogData, enableOrientation, 1}},
+ {2379, {wxPageSetupDialogData, enablePaper, 1}},
+ {2380, {wxPageSetupDialogData, enablePrinter, 1}},
+ {2381, {wxPageSetupDialogData, getDefaultMinMargins, 0}},
+ {2382, {wxPageSetupDialogData, getEnableMargins, 0}},
+ {2383, {wxPageSetupDialogData, getEnableOrientation, 0}},
+ {2384, {wxPageSetupDialogData, getEnablePaper, 0}},
+ {2385, {wxPageSetupDialogData, getEnablePrinter, 0}},
+ {2386, {wxPageSetupDialogData, getEnableHelp, 0}},
+ {2387, {wxPageSetupDialogData, getDefaultInfo, 0}},
+ {2388, {wxPageSetupDialogData, getMarginTopLeft, 0}},
+ {2389, {wxPageSetupDialogData, getMarginBottomRight, 0}},
+ {2390, {wxPageSetupDialogData, getMinMarginTopLeft, 0}},
+ {2391, {wxPageSetupDialogData, getMinMarginBottomRight, 0}},
+ {2392, {wxPageSetupDialogData, getPaperId, 0}},
+ {2393, {wxPageSetupDialogData, getPaperSize, 0}},
+ {2395, {wxPageSetupDialogData, getPrintData, 0}},
+ {2396, {wxPageSetupDialogData, isOk, 0}},
+ {2397, {wxPageSetupDialogData, setDefaultInfo, 1}},
+ {2398, {wxPageSetupDialogData, setDefaultMinMargins, 1}},
+ {2399, {wxPageSetupDialogData, setMarginTopLeft, 1}},
+ {2400, {wxPageSetupDialogData, setMarginBottomRight, 1}},
+ {2401, {wxPageSetupDialogData, setMinMarginTopLeft, 1}},
+ {2402, {wxPageSetupDialogData, setMinMarginBottomRight, 1}},
+ {2403, {wxPageSetupDialogData, setPaperId, 1}},
+ {2404, {wxPageSetupDialogData, setPaperSize_1_1, 1}},
+ {2405, {wxPageSetupDialogData, setPaperSize_1_0, 1}},
+ {2406, {wxPageSetupDialogData, setPrintData, 1}},
+ {2407, {wxPrintDialog, new_2_0, 2}},
+ {2408, {wxPrintDialog, new_2_1, 2}},
+ {2409, {wxPrintDialog, destruct, 0}},
+ {2410, {wxPrintDialog, getPrintDialogData, 0}},
+ {2411, {wxPrintDialog, getPrintDC, 0}},
+ {2412, {wxPrintDialogData, new_0, 0}},
+ {2413, {wxPrintDialogData, new_1_1, 1}},
+ {2414, {wxPrintDialogData, new_1_0, 1}},
+ {2415, {wxPrintDialogData, destruct, 0}},
+ {2416, {wxPrintDialogData, enableHelp, 1}},
+ {2417, {wxPrintDialogData, enablePageNumbers, 1}},
+ {2418, {wxPrintDialogData, enablePrintToFile, 1}},
+ {2419, {wxPrintDialogData, enableSelection, 1}},
+ {2420, {wxPrintDialogData, getAllPages, 0}},
+ {2421, {wxPrintDialogData, getCollate, 0}},
+ {2422, {wxPrintDialogData, getFromPage, 0}},
+ {2423, {wxPrintDialogData, getMaxPage, 0}},
+ {2424, {wxPrintDialogData, getMinPage, 0}},
+ {2425, {wxPrintDialogData, getNoCopies, 0}},
+ {2426, {wxPrintDialogData, getPrintData, 0}},
+ {2427, {wxPrintDialogData, getPrintToFile, 0}},
+ {2428, {wxPrintDialogData, getSelection, 0}},
+ {2429, {wxPrintDialogData, getToPage, 0}},
+ {2430, {wxPrintDialogData, isOk, 0}},
+ {2431, {wxPrintDialogData, setCollate, 1}},
+ {2432, {wxPrintDialogData, setFromPage, 1}},
+ {2433, {wxPrintDialogData, setMaxPage, 1}},
+ {2434, {wxPrintDialogData, setMinPage, 1}},
+ {2435, {wxPrintDialogData, setNoCopies, 1}},
+ {2436, {wxPrintDialogData, setPrintData, 1}},
+ {2437, {wxPrintDialogData, setPrintToFile, 1}},
+ {2438, {wxPrintDialogData, setSelection, 1}},
+ {2439, {wxPrintDialogData, setToPage, 1}},
+ {2440, {wxPrintData, new_0, 0}},
+ {2441, {wxPrintData, new_1, 1}},
+ {2442, {wxPrintData, destruct, 0}},
+ {2443, {wxPrintData, getCollate, 0}},
+ {2444, {wxPrintData, getBin, 0}},
+ {2445, {wxPrintData, getColour, 0}},
+ {2446, {wxPrintData, getDuplex, 0}},
+ {2447, {wxPrintData, getNoCopies, 0}},
+ {2448, {wxPrintData, getOrientation, 0}},
+ {2449, {wxPrintData, getPaperId, 0}},
+ {2450, {wxPrintData, getPrinterName, 0}},
+ {2451, {wxPrintData, getQuality, 0}},
+ {2452, {wxPrintData, isOk, 0}},
+ {2453, {wxPrintData, setBin, 1}},
+ {2454, {wxPrintData, setCollate, 1}},
+ {2455, {wxPrintData, setColour, 1}},
+ {2456, {wxPrintData, setDuplex, 1}},
+ {2457, {wxPrintData, setNoCopies, 1}},
+ {2458, {wxPrintData, setOrientation, 1}},
+ {2459, {wxPrintData, setPaperId, 1}},
+ {2460, {wxPrintData, setPrinterName, 1}},
+ {2461, {wxPrintData, setQuality, 1}},
+ {2464, {wxPrintPreview, new_2, 2}},
+ {2465, {wxPrintPreview, new_3, 3}},
+ {2467, {wxPrintPreview, destruct, 0}},
+ {2468, {wxPrintPreview, getCanvas, 0}},
+ {2469, {wxPrintPreview, getCurrentPage, 0}},
+ {2470, {wxPrintPreview, getFrame, 0}},
+ {2471, {wxPrintPreview, getMaxPage, 0}},
+ {2472, {wxPrintPreview, getMinPage, 0}},
+ {2473, {wxPrintPreview, getPrintout, 0}},
+ {2474, {wxPrintPreview, getPrintoutForPrinting, 0}},
+ {2475, {wxPrintPreview, isOk, 0}},
+ {2476, {wxPrintPreview, paintPage, 2}},
+ {2477, {wxPrintPreview, print, 1}},
+ {2478, {wxPrintPreview, renderPage, 1}},
+ {2479, {wxPrintPreview, setCanvas, 1}},
+ {2480, {wxPrintPreview, setCurrentPage, 1}},
+ {2481, {wxPrintPreview, setFrame, 1}},
+ {2482, {wxPrintPreview, setPrintout, 1}},
+ {2483, {wxPrintPreview, setZoom, 1}},
+ {2484, {wxPreviewFrame, new, 3}},
+ {2485, {wxPreviewFrame, destruct, 0}},
+ {2486, {wxPreviewFrame, createControlBar, 0}},
+ {2487, {wxPreviewFrame, createCanvas, 0}},
+ {2488, {wxPreviewFrame, initialize, 0}},
+ {2489, {wxPreviewFrame, onCloseWindow, 1}},
+ {2490, {wxPreviewControlBar, new, 4}},
+ {2491, {wxPreviewControlBar, destruct, 0}},
+ {2492, {wxPreviewControlBar, createButtons, 0}},
+ {2493, {wxPreviewControlBar, getPrintPreview, 0}},
+ {2494, {wxPreviewControlBar, getZoomControl, 0}},
+ {2495, {wxPreviewControlBar, setZoomControl, 1}},
+ {2497, {wxPrinter, new, 1}},
+ {2498, {wxPrinter, createAbortWindow, 2}},
+ {2499, {wxPrinter, getAbort, 0}},
+ {2500, {wxPrinter, getLastError, 0}},
+ {2501, {wxPrinter, getPrintDialogData, 0}},
+ {2502, {wxPrinter, print, 3}},
+ {2503, {wxPrinter, printDialog, 1}},
+ {2504, {wxPrinter, reportError, 3}},
+ {2505, {wxPrinter, setup, 1}},
+ {2506, {wxPrinter, 'Destroy', undefined}},
+ {2507, {wxXmlResource, new_1, 1}},
+ {2508, {wxXmlResource, new_2, 2}},
+ {2509, {wxXmlResource, destruct, 0}},
+ {2510, {wxXmlResource, attachUnknownControl, 3}},
+ {2511, {wxXmlResource, clearHandlers, 0}},
+ {2512, {wxXmlResource, compareVersion, 4}},
+ {2513, {wxXmlResource, get, 0}},
+ {2514, {wxXmlResource, getFlags, 0}},
+ {2515, {wxXmlResource, getVersion, 0}},
+ {2516, {wxXmlResource, getXRCID, 2}},
+ {2517, {wxXmlResource, initAllHandlers, 0}},
+ {2518, {wxXmlResource, load, 1}},
+ {2519, {wxXmlResource, loadBitmap, 1}},
+ {2520, {wxXmlResource, loadDialog_2, 2}},
+ {2521, {wxXmlResource, loadDialog_3, 3}},
+ {2522, {wxXmlResource, loadFrame_2, 2}},
+ {2523, {wxXmlResource, loadFrame_3, 3}},
+ {2524, {wxXmlResource, loadIcon, 1}},
+ {2525, {wxXmlResource, loadMenu, 1}},
+ {2526, {wxXmlResource, loadMenuBar_2, 2}},
+ {2527, {wxXmlResource, loadMenuBar_1, 1}},
+ {2528, {wxXmlResource, loadPanel_2, 2}},
+ {2529, {wxXmlResource, loadPanel_3, 3}},
+ {2530, {wxXmlResource, loadToolBar, 2}},
+ {2531, {wxXmlResource, set, 1}},
+ {2532, {wxXmlResource, setFlags, 1}},
+ {2533, {wxXmlResource, unload, 1}},
+ {2534, {wxXmlResource, xrcctrl, 3}},
+ {2535, {wxHtmlEasyPrinting, new, 1}},
+ {2536, {wxHtmlEasyPrinting, destruct, 0}},
+ {2537, {wxHtmlEasyPrinting, getPrintData, 0}},
+ {2538, {wxHtmlEasyPrinting, getPageSetupData, 0}},
+ {2539, {wxHtmlEasyPrinting, previewFile, 1}},
+ {2540, {wxHtmlEasyPrinting, previewText, 2}},
+ {2541, {wxHtmlEasyPrinting, printFile, 1}},
+ {2542, {wxHtmlEasyPrinting, printText, 2}},
+ {2543, {wxHtmlEasyPrinting, pageSetup, 0}},
+ {2544, {wxHtmlEasyPrinting, setFonts, 3}},
+ {2545, {wxHtmlEasyPrinting, setHeader, 2}},
+ {2546, {wxHtmlEasyPrinting, setFooter, 2}},
+ {2548, {wxGLCanvas, new_2, 2}},
+ {2549, {wxGLCanvas, new_3_1, 3}},
+ {2550, {wxGLCanvas, new_3_0, 3}},
+ {2551, {wxGLCanvas, getContext, 0}},
+ {2553, {wxGLCanvas, setCurrent, 0}},
+ {2554, {wxGLCanvas, swapBuffers, 0}},
+ {2555, {wxGLCanvas, 'Destroy', undefined}},
+ {2556, {wxAuiManager, new, 1}},
+ {2557, {wxAuiManager, destruct, 0}},
+ {2558, {wxAuiManager, addPane_2_1, 2}},
+ {2559, {wxAuiManager, addPane_3, 3}},
+ {2560, {wxAuiManager, addPane_2_0, 2}},
+ {2561, {wxAuiManager, detachPane, 1}},
+ {2562, {wxAuiManager, getAllPanes, 0}},
+ {2563, {wxAuiManager, getArtProvider, 0}},
+ {2564, {wxAuiManager, getDockSizeConstraint, 2}},
+ {2565, {wxAuiManager, getFlags, 0}},
+ {2566, {wxAuiManager, getManagedWindow, 0}},
+ {2567, {wxAuiManager, getManager, 1}},
+ {2568, {wxAuiManager, getPane_1_1, 1}},
+ {2569, {wxAuiManager, getPane_1_0, 1}},
+ {2570, {wxAuiManager, hideHint, 0}},
+ {2571, {wxAuiManager, insertPane, 3}},
+ {2572, {wxAuiManager, loadPaneInfo, 2}},
+ {2573, {wxAuiManager, loadPerspective, 2}},
+ {2574, {wxAuiManager, savePaneInfo, 1}},
+ {2575, {wxAuiManager, savePerspective, 0}},
+ {2576, {wxAuiManager, setArtProvider, 1}},
+ {2577, {wxAuiManager, setDockSizeConstraint, 2}},
+ {2578, {wxAuiManager, setFlags, 1}},
+ {2579, {wxAuiManager, setManagedWindow, 1}},
+ {2580, {wxAuiManager, showHint, 1}},
+ {2581, {wxAuiManager, unInit, 0}},
+ {2582, {wxAuiManager, update, 0}},
+ {2583, {wxAuiPaneInfo, new_0, 0}},
+ {2584, {wxAuiPaneInfo, new_1, 1}},
+ {2585, {wxAuiPaneInfo, destruct, 0}},
+ {2586, {wxAuiPaneInfo, bestSize_1, 1}},
+ {2587, {wxAuiPaneInfo, bestSize_2, 2}},
+ {2588, {wxAuiPaneInfo, bottom, 0}},
+ {2589, {wxAuiPaneInfo, bottomDockable, 1}},
+ {2590, {wxAuiPaneInfo, caption, 1}},
+ {2591, {wxAuiPaneInfo, captionVisible, 1}},
+ {2592, {wxAuiPaneInfo, centre, 0}},
+ {2593, {wxAuiPaneInfo, centrePane, 0}},
+ {2594, {wxAuiPaneInfo, closeButton, 1}},
+ {2595, {wxAuiPaneInfo, defaultPane, 0}},
+ {2596, {wxAuiPaneInfo, destroyOnClose, 1}},
+ {2597, {wxAuiPaneInfo, direction, 1}},
+ {2598, {wxAuiPaneInfo, dock, 0}},
+ {2599, {wxAuiPaneInfo, dockable, 1}},
+ {2600, {wxAuiPaneInfo, fixed, 0}},
+ {2601, {wxAuiPaneInfo, float, 0}},
+ {2602, {wxAuiPaneInfo, floatable, 1}},
+ {2603, {wxAuiPaneInfo, floatingPosition_1, 1}},
+ {2604, {wxAuiPaneInfo, floatingPosition_2, 2}},
+ {2605, {wxAuiPaneInfo, floatingSize_1, 1}},
+ {2606, {wxAuiPaneInfo, floatingSize_2, 2}},
+ {2607, {wxAuiPaneInfo, gripper, 1}},
+ {2608, {wxAuiPaneInfo, gripperTop, 1}},
+ {2609, {wxAuiPaneInfo, hasBorder, 0}},
+ {2610, {wxAuiPaneInfo, hasCaption, 0}},
+ {2611, {wxAuiPaneInfo, hasCloseButton, 0}},
+ {2612, {wxAuiPaneInfo, hasFlag, 1}},
+ {2613, {wxAuiPaneInfo, hasGripper, 0}},
+ {2614, {wxAuiPaneInfo, hasGripperTop, 0}},
+ {2615, {wxAuiPaneInfo, hasMaximizeButton, 0}},
+ {2616, {wxAuiPaneInfo, hasMinimizeButton, 0}},
+ {2617, {wxAuiPaneInfo, hasPinButton, 0}},
+ {2618, {wxAuiPaneInfo, hide, 0}},
+ {2619, {wxAuiPaneInfo, isBottomDockable, 0}},
+ {2620, {wxAuiPaneInfo, isDocked, 0}},
+ {2621, {wxAuiPaneInfo, isFixed, 0}},
+ {2622, {wxAuiPaneInfo, isFloatable, 0}},
+ {2623, {wxAuiPaneInfo, isFloating, 0}},
+ {2624, {wxAuiPaneInfo, isLeftDockable, 0}},
+ {2625, {wxAuiPaneInfo, isMovable, 0}},
+ {2626, {wxAuiPaneInfo, isOk, 0}},
+ {2627, {wxAuiPaneInfo, isResizable, 0}},
+ {2628, {wxAuiPaneInfo, isRightDockable, 0}},
+ {2629, {wxAuiPaneInfo, isShown, 0}},
+ {2630, {wxAuiPaneInfo, isToolbar, 0}},
+ {2631, {wxAuiPaneInfo, isTopDockable, 0}},
+ {2632, {wxAuiPaneInfo, layer, 1}},
+ {2633, {wxAuiPaneInfo, left, 0}},
+ {2634, {wxAuiPaneInfo, leftDockable, 1}},
+ {2635, {wxAuiPaneInfo, maxSize_1, 1}},
+ {2636, {wxAuiPaneInfo, maxSize_2, 2}},
+ {2637, {wxAuiPaneInfo, maximizeButton, 1}},
+ {2638, {wxAuiPaneInfo, minSize_1, 1}},
+ {2639, {wxAuiPaneInfo, minSize_2, 2}},
+ {2640, {wxAuiPaneInfo, minimizeButton, 1}},
+ {2641, {wxAuiPaneInfo, movable, 1}},
+ {2642, {wxAuiPaneInfo, name, 1}},
+ {2643, {wxAuiPaneInfo, paneBorder, 1}},
+ {2644, {wxAuiPaneInfo, pinButton, 1}},
+ {2645, {wxAuiPaneInfo, position, 1}},
+ {2646, {wxAuiPaneInfo, resizable, 1}},
+ {2647, {wxAuiPaneInfo, right, 0}},
+ {2648, {wxAuiPaneInfo, rightDockable, 1}},
+ {2649, {wxAuiPaneInfo, row, 1}},
+ {2650, {wxAuiPaneInfo, safeSet, 1}},
+ {2651, {wxAuiPaneInfo, setFlag, 2}},
+ {2652, {wxAuiPaneInfo, show, 1}},
+ {2653, {wxAuiPaneInfo, toolbarPane, 0}},
+ {2654, {wxAuiPaneInfo, top, 0}},
+ {2655, {wxAuiPaneInfo, topDockable, 1}},
+ {2656, {wxAuiPaneInfo, window, 1}},
+ {2657, {wxAuiPaneInfo, getWindow, 0}},
+ {2658, {wxAuiPaneInfo, getFrame, 0}},
+ {2659, {wxAuiPaneInfo, getDirection, 0}},
+ {2660, {wxAuiPaneInfo, getLayer, 0}},
+ {2661, {wxAuiPaneInfo, getRow, 0}},
+ {2662, {wxAuiPaneInfo, getPosition, 0}},
+ {2663, {wxAuiPaneInfo, getFloatingPosition, 0}},
+ {2664, {wxAuiPaneInfo, getFloatingSize, 0}},
+ {2665, {wxAuiNotebook, new_0, 0}},
+ {2666, {wxAuiNotebook, new_2, 2}},
+ {2667, {wxAuiNotebook, addPage, 3}},
+ {2668, {wxAuiNotebook, create, 2}},
+ {2669, {wxAuiNotebook, deletePage, 1}},
+ {2670, {wxAuiNotebook, getArtProvider, 0}},
+ {2671, {wxAuiNotebook, getPage, 1}},
+ {2672, {wxAuiNotebook, getPageBitmap, 1}},
+ {2673, {wxAuiNotebook, getPageCount, 0}},
+ {2674, {wxAuiNotebook, getPageIndex, 1}},
+ {2675, {wxAuiNotebook, getPageText, 1}},
+ {2676, {wxAuiNotebook, getSelection, 0}},
+ {2677, {wxAuiNotebook, insertPage, 4}},
+ {2678, {wxAuiNotebook, removePage, 1}},
+ {2679, {wxAuiNotebook, setArtProvider, 1}},
+ {2680, {wxAuiNotebook, setFont, 1}},
+ {2681, {wxAuiNotebook, setPageBitmap, 2}},
+ {2682, {wxAuiNotebook, setPageText, 2}},
+ {2683, {wxAuiNotebook, setSelection, 1}},
+ {2684, {wxAuiNotebook, setTabCtrlHeight, 1}},
+ {2685, {wxAuiNotebook, setUniformBitmapSize, 1}},
+ {2686, {wxAuiNotebook, 'Destroy', undefined}},
+ {2687, {wxAuiTabArt, setFlags, 1}},
+ {2688, {wxAuiTabArt, setMeasuringFont, 1}},
+ {2689, {wxAuiTabArt, setNormalFont, 1}},
+ {2690, {wxAuiTabArt, setSelectedFont, 1}},
+ {2691, {wxAuiTabArt, setColour, 1}},
+ {2692, {wxAuiTabArt, setActiveColour, 1}},
+ {2693, {wxAuiDockArt, getColour, 1}},
+ {2694, {wxAuiDockArt, getFont, 1}},
+ {2695, {wxAuiDockArt, getMetric, 1}},
+ {2696, {wxAuiDockArt, setColour, 2}},
+ {2697, {wxAuiDockArt, setFont, 2}},
+ {2698, {wxAuiDockArt, setMetric, 2}},
+ {2699, {wxAuiSimpleTabArt, new, 0}},
+ {2700, {wxAuiSimpleTabArt, 'Destroy', undefined}},
+ {2701, {wxMDIParentFrame, new_0, 0}},
+ {2702, {wxMDIParentFrame, new_4, 4}},
+ {2703, {wxMDIParentFrame, destruct, 0}},
+ {2704, {wxMDIParentFrame, activateNext, 0}},
+ {2705, {wxMDIParentFrame, activatePrevious, 0}},
+ {2706, {wxMDIParentFrame, arrangeIcons, 0}},
+ {2707, {wxMDIParentFrame, cascade, 0}},
+ {2708, {wxMDIParentFrame, create, 4}},
+ {2709, {wxMDIParentFrame, getActiveChild, 0}},
+ {2710, {wxMDIParentFrame, getClientWindow, 0}},
+ {2711, {wxMDIParentFrame, tile, 1}},
+ {2712, {wxMDIChildFrame, new_0, 0}},
+ {2713, {wxMDIChildFrame, new_4, 4}},
+ {2714, {wxMDIChildFrame, destruct, 0}},
+ {2715, {wxMDIChildFrame, activate, 0}},
+ {2716, {wxMDIChildFrame, create, 4}},
+ {2717, {wxMDIChildFrame, maximize, 1}},
+ {2718, {wxMDIChildFrame, restore, 0}},
+ {2719, {wxMDIClientWindow, new_0, 0}},
+ {2720, {wxMDIClientWindow, new_2, 2}},
+ {2721, {wxMDIClientWindow, destruct, 0}},
+ {2722, {wxMDIClientWindow, createClient, 2}},
+ {2723, {wxLayoutAlgorithm, new, 0}},
+ {2724, {wxLayoutAlgorithm, layoutFrame, 2}},
+ {2725, {wxLayoutAlgorithm, layoutMDIFrame, 2}},
+ {2726, {wxLayoutAlgorithm, layoutWindow, 2}},
+ {2727, {wxLayoutAlgorithm, 'Destroy', undefined}},
+ {2728, {wxEvent, getId, 0}},
+ {2729, {wxEvent, getSkipped, 0}},
+ {2730, {wxEvent, getTimestamp, 0}},
+ {2731, {wxEvent, isCommandEvent, 0}},
+ {2732, {wxEvent, resumePropagation, 1}},
+ {2733, {wxEvent, shouldPropagate, 0}},
+ {2734, {wxEvent, skip, 1}},
+ {2735, {wxEvent, stopPropagation, 0}},
+ {2736, {wxCommandEvent, getClientData, 0}},
+ {2737, {wxCommandEvent, getExtraLong, 0}},
+ {2738, {wxCommandEvent, getInt, 0}},
+ {2739, {wxCommandEvent, getSelection, 0}},
+ {2740, {wxCommandEvent, getString, 0}},
+ {2741, {wxCommandEvent, isChecked, 0}},
+ {2742, {wxCommandEvent, isSelection, 0}},
+ {2743, {wxCommandEvent, setInt, 1}},
+ {2744, {wxCommandEvent, setString, 1}},
+ {2745, {wxScrollEvent, getOrientation, 0}},
+ {2746, {wxScrollEvent, getPosition, 0}},
+ {2747, {wxScrollWinEvent, getOrientation, 0}},
+ {2748, {wxScrollWinEvent, getPosition, 0}},
+ {2749, {wxMouseEvent, altDown, 0}},
+ {2750, {wxMouseEvent, button, 1}},
+ {2751, {wxMouseEvent, buttonDClick, 1}},
+ {2752, {wxMouseEvent, buttonDown, 1}},
+ {2753, {wxMouseEvent, buttonUp, 1}},
+ {2754, {wxMouseEvent, cmdDown, 0}},
+ {2755, {wxMouseEvent, controlDown, 0}},
+ {2756, {wxMouseEvent, dragging, 0}},
+ {2757, {wxMouseEvent, entering, 0}},
+ {2758, {wxMouseEvent, getButton, 0}},
+ {2761, {wxMouseEvent, getPosition, 0}},
+ {2762, {wxMouseEvent, getLogicalPosition, 1}},
+ {2763, {wxMouseEvent, getLinesPerAction, 0}},
+ {2764, {wxMouseEvent, getWheelRotation, 0}},
+ {2765, {wxMouseEvent, getWheelDelta, 0}},
+ {2766, {wxMouseEvent, getX, 0}},
+ {2767, {wxMouseEvent, getY, 0}},
+ {2768, {wxMouseEvent, isButton, 0}},
+ {2769, {wxMouseEvent, isPageScroll, 0}},
+ {2770, {wxMouseEvent, leaving, 0}},
+ {2771, {wxMouseEvent, leftDClick, 0}},
+ {2772, {wxMouseEvent, leftDown, 0}},
+ {2773, {wxMouseEvent, leftIsDown, 0}},
+ {2774, {wxMouseEvent, leftUp, 0}},
+ {2775, {wxMouseEvent, metaDown, 0}},
+ {2776, {wxMouseEvent, middleDClick, 0}},
+ {2777, {wxMouseEvent, middleDown, 0}},
+ {2778, {wxMouseEvent, middleIsDown, 0}},
+ {2779, {wxMouseEvent, middleUp, 0}},
+ {2780, {wxMouseEvent, moving, 0}},
+ {2781, {wxMouseEvent, rightDClick, 0}},
+ {2782, {wxMouseEvent, rightDown, 0}},
+ {2783, {wxMouseEvent, rightIsDown, 0}},
+ {2784, {wxMouseEvent, rightUp, 0}},
+ {2785, {wxMouseEvent, shiftDown, 0}},
+ {2786, {wxSetCursorEvent, getCursor, 0}},
+ {2787, {wxSetCursorEvent, getX, 0}},
+ {2788, {wxSetCursorEvent, getY, 0}},
+ {2789, {wxSetCursorEvent, hasCursor, 0}},
+ {2790, {wxSetCursorEvent, setCursor, 1}},
+ {2791, {wxKeyEvent, altDown, 0}},
+ {2792, {wxKeyEvent, cmdDown, 0}},
+ {2793, {wxKeyEvent, controlDown, 0}},
+ {2794, {wxKeyEvent, getKeyCode, 0}},
+ {2795, {wxKeyEvent, getModifiers, 0}},
+ {2798, {wxKeyEvent, getPosition, 0}},
+ {2799, {wxKeyEvent, getRawKeyCode, 0}},
+ {2800, {wxKeyEvent, getRawKeyFlags, 0}},
+ {2801, {wxKeyEvent, getUnicodeKey, 0}},
+ {2802, {wxKeyEvent, getX, 0}},
+ {2803, {wxKeyEvent, getY, 0}},
+ {2804, {wxKeyEvent, hasModifiers, 0}},
+ {2805, {wxKeyEvent, metaDown, 0}},
+ {2806, {wxKeyEvent, shiftDown, 0}},
+ {2807, {wxSizeEvent, getSize, 0}},
+ {2808, {wxMoveEvent, getPosition, 0}},
+ {2809, {wxEraseEvent, getDC, 0}},
+ {2810, {wxFocusEvent, getWindow, 0}},
+ {2811, {wxChildFocusEvent, getWindow, 0}},
+ {2812, {wxMenuEvent, getMenu, 0}},
+ {2813, {wxMenuEvent, getMenuId, 0}},
+ {2814, {wxMenuEvent, isPopup, 0}},
+ {2815, {wxCloseEvent, canVeto, 0}},
+ {2816, {wxCloseEvent, getLoggingOff, 0}},
+ {2817, {wxCloseEvent, setCanVeto, 1}},
+ {2818, {wxCloseEvent, setLoggingOff, 1}},
+ {2819, {wxCloseEvent, veto, 1}},
+ {2820, {wxShowEvent, setShow, 1}},
+ {2821, {wxShowEvent, getShow, 0}},
+ {2822, {wxIconizeEvent, iconized, 0}},
+ {2823, {wxJoystickEvent, buttonDown, 1}},
+ {2824, {wxJoystickEvent, buttonIsDown, 1}},
+ {2825, {wxJoystickEvent, buttonUp, 1}},
+ {2826, {wxJoystickEvent, getButtonChange, 0}},
+ {2827, {wxJoystickEvent, getButtonState, 0}},
+ {2828, {wxJoystickEvent, getJoystick, 0}},
+ {2829, {wxJoystickEvent, getPosition, 0}},
+ {2830, {wxJoystickEvent, getZPosition, 0}},
+ {2831, {wxJoystickEvent, isButton, 0}},
+ {2832, {wxJoystickEvent, isMove, 0}},
+ {2833, {wxJoystickEvent, isZMove, 0}},
+ {2834, {wxUpdateUIEvent, canUpdate, 1}},
+ {2835, {wxUpdateUIEvent, check, 1}},
+ {2836, {wxUpdateUIEvent, enable, 1}},
+ {2837, {wxUpdateUIEvent, show, 1}},
+ {2838, {wxUpdateUIEvent, getChecked, 0}},
+ {2839, {wxUpdateUIEvent, getEnabled, 0}},
+ {2840, {wxUpdateUIEvent, getShown, 0}},
+ {2841, {wxUpdateUIEvent, getSetChecked, 0}},
+ {2842, {wxUpdateUIEvent, getSetEnabled, 0}},
+ {2843, {wxUpdateUIEvent, getSetShown, 0}},
+ {2844, {wxUpdateUIEvent, getSetText, 0}},
+ {2845, {wxUpdateUIEvent, getText, 0}},
+ {2846, {wxUpdateUIEvent, getMode, 0}},
+ {2847, {wxUpdateUIEvent, getUpdateInterval, 0}},
+ {2848, {wxUpdateUIEvent, resetUpdateTime, 0}},
+ {2849, {wxUpdateUIEvent, setMode, 1}},
+ {2850, {wxUpdateUIEvent, setText, 1}},
+ {2851, {wxUpdateUIEvent, setUpdateInterval, 1}},
+ {2852, {wxMouseCaptureChangedEvent, getCapturedWindow, 0}},
+ {2853, {wxPaletteChangedEvent, setChangedWindow, 1}},
+ {2854, {wxPaletteChangedEvent, getChangedWindow, 0}},
+ {2855, {wxQueryNewPaletteEvent, setPaletteRealized, 1}},
+ {2856, {wxQueryNewPaletteEvent, getPaletteRealized, 0}},
+ {2857, {wxNavigationKeyEvent, getDirection, 0}},
+ {2858, {wxNavigationKeyEvent, setDirection, 1}},
+ {2859, {wxNavigationKeyEvent, isWindowChange, 0}},
+ {2860, {wxNavigationKeyEvent, setWindowChange, 1}},
+ {2861, {wxNavigationKeyEvent, isFromTab, 0}},
+ {2862, {wxNavigationKeyEvent, setFromTab, 1}},
+ {2863, {wxNavigationKeyEvent, getCurrentFocus, 0}},
+ {2864, {wxNavigationKeyEvent, setCurrentFocus, 1}},
+ {2865, {wxHelpEvent, getOrigin, 0}},
+ {2866, {wxHelpEvent, getPosition, 0}},
+ {2867, {wxHelpEvent, setOrigin, 1}},
+ {2868, {wxHelpEvent, setPosition, 1}},
+ {2869, {wxContextMenuEvent, getPosition, 0}},
+ {2870, {wxContextMenuEvent, setPosition, 1}},
+ {2871, {wxIdleEvent, canSend, 1}},
+ {2872, {wxIdleEvent, getMode, 0}},
+ {2873, {wxIdleEvent, requestMore, 1}},
+ {2874, {wxIdleEvent, moreRequested, 0}},
+ {2875, {wxIdleEvent, setMode, 1}},
+ {2876, {wxGridEvent, altDown, 0}},
+ {2877, {wxGridEvent, controlDown, 0}},
+ {2878, {wxGridEvent, getCol, 0}},
+ {2879, {wxGridEvent, getPosition, 0}},
+ {2880, {wxGridEvent, getRow, 0}},
+ {2881, {wxGridEvent, metaDown, 0}},
+ {2882, {wxGridEvent, selecting, 0}},
+ {2883, {wxGridEvent, shiftDown, 0}},
+ {2884, {wxNotifyEvent, allow, 0}},
+ {2885, {wxNotifyEvent, isAllowed, 0}},
+ {2886, {wxNotifyEvent, veto, 0}},
+ {2887, {wxSashEvent, getEdge, 0}},
+ {2888, {wxSashEvent, getDragRect, 0}},
+ {2889, {wxSashEvent, getDragStatus, 0}},
+ {2890, {wxListEvent, getCacheFrom, 0}},
+ {2891, {wxListEvent, getCacheTo, 0}},
+ {2892, {wxListEvent, getKeyCode, 0}},
+ {2893, {wxListEvent, getIndex, 0}},
+ {2894, {wxListEvent, getColumn, 0}},
+ {2895, {wxListEvent, getPoint, 0}},
+ {2896, {wxListEvent, getLabel, 0}},
+ {2897, {wxListEvent, getText, 0}},
+ {2898, {wxListEvent, getImage, 0}},
+ {2899, {wxListEvent, getData, 0}},
+ {2900, {wxListEvent, getMask, 0}},
+ {2901, {wxListEvent, getItem, 0}},
+ {2902, {wxListEvent, isEditCancelled, 0}},
+ {2903, {wxDateEvent, getDate, 0}},
+ {2904, {wxCalendarEvent, getWeekDay, 0}},
+ {2905, {wxFileDirPickerEvent, getPath, 0}},
+ {2906, {wxColourPickerEvent, getColour, 0}},
+ {2907, {wxFontPickerEvent, getFont, 0}},
+ {2908, {wxStyledTextEvent, getPosition, 0}},
+ {2909, {wxStyledTextEvent, getKey, 0}},
+ {2910, {wxStyledTextEvent, getModifiers, 0}},
+ {2911, {wxStyledTextEvent, getModificationType, 0}},
+ {2912, {wxStyledTextEvent, getText, 0}},
+ {2913, {wxStyledTextEvent, getLength, 0}},
+ {2914, {wxStyledTextEvent, getLinesAdded, 0}},
+ {2915, {wxStyledTextEvent, getLine, 0}},
+ {2916, {wxStyledTextEvent, getFoldLevelNow, 0}},
+ {2917, {wxStyledTextEvent, getFoldLevelPrev, 0}},
+ {2918, {wxStyledTextEvent, getMargin, 0}},
+ {2919, {wxStyledTextEvent, getMessage, 0}},
+ {2920, {wxStyledTextEvent, getWParam, 0}},
+ {2921, {wxStyledTextEvent, getLParam, 0}},
+ {2922, {wxStyledTextEvent, getListType, 0}},
+ {2923, {wxStyledTextEvent, getX, 0}},
+ {2924, {wxStyledTextEvent, getY, 0}},
+ {2925, {wxStyledTextEvent, getDragText, 0}},
+ {2926, {wxStyledTextEvent, getDragAllowMove, 0}},
+ {2927, {wxStyledTextEvent, getDragResult, 0}},
+ {2928, {wxStyledTextEvent, getShift, 0}},
+ {2929, {wxStyledTextEvent, getControl, 0}},
+ {2930, {wxStyledTextEvent, getAlt, 0}},
+ {2931, {utils, getKeyState, 1}},
+ {2932, {utils, getMousePosition, 2}},
+ {2933, {utils, getMouseState, 0}},
+ {2934, {utils, setDetectableAutoRepeat, 1}},
+ {2935, {utils, bell, 0}},
+ {2936, {utils, findMenuItemId, 3}},
+ {2937, {utils, genericFindWindowAtPoint, 1}},
+ {2938, {utils, findWindowAtPoint, 1}},
+ {2939, {utils, beginBusyCursor, 1}},
+ {2940, {utils, endBusyCursor, 0}},
+ {2941, {utils, isBusy, 0}},
+ {2942, {utils, shutdown, 1}},
+ {2943, {utils, shell, 1}},
+ {2944, {utils, launchDefaultBrowser, 2}},
+ {2945, {utils, getEmailAddress, 0}},
+ {2946, {utils, getUserId, 0}},
+ {2947, {utils, getHomeDir, 0}},
+ {2948, {utils, newId, 0}},
+ {2949, {utils, registerId, 1}},
+ {2950, {utils, getCurrentId, 0}},
+ {2951, {utils, getOsDescription, 0}},
+ {2952, {utils, isPlatformLittleEndian, 0}},
+ {2953, {utils, isPlatform64Bit, 0}},
+ {2954, {gdicmn, displaySize, 2}},
+ {2955, {gdicmn, setCursor, 1}},
+ {2956, {wxPrintout, new, 1}},
+ {2957, {wxPrintout, destruct, 0}},
+ {2958, {wxPrintout, getDC, 0}},
+ {2959, {wxPrintout, getPageSizeMM, 2}},
+ {2960, {wxPrintout, getPageSizePixels, 2}},
+ {2961, {wxPrintout, getPaperRectPixels, 0}},
+ {2962, {wxPrintout, getPPIPrinter, 2}},
+ {2963, {wxPrintout, getPPIScreen, 2}},
+ {2964, {wxPrintout, getTitle, 0}},
+ {2965, {wxPrintout, isPreview, 0}},
+ {2966, {wxPrintout, fitThisSizeToPaper, 1}},
+ {2967, {wxPrintout, fitThisSizeToPage, 1}},
+ {2968, {wxPrintout, fitThisSizeToPageMargins, 2}},
+ {2969, {wxPrintout, mapScreenSizeToPaper, 0}},
+ {2970, {wxPrintout, mapScreenSizeToPage, 0}},
+ {2971, {wxPrintout, mapScreenSizeToPageMargins, 1}},
+ {2972, {wxPrintout, mapScreenSizeToDevice, 0}},
+ {2973, {wxPrintout, getLogicalPaperRect, 0}},
+ {2974, {wxPrintout, getLogicalPageRect, 0}},
+ {2975, {wxPrintout, getLogicalPageMarginsRect, 1}},
+ {2976, {wxPrintout, setLogicalOrigin, 2}},
+ {2977, {wxPrintout, offsetLogicalOrigin, 2}},
+ {2978, {wxStyledTextCtrl, new_2, 2}},
+ {2979, {wxStyledTextCtrl, new_0, 0}},
+ {2980, {wxStyledTextCtrl, destruct, 0}},
+ {2981, {wxStyledTextCtrl, create, 2}},
+ {2982, {wxStyledTextCtrl, addText, 1}},
+ {2983, {wxStyledTextCtrl, addStyledText, 1}},
+ {2984, {wxStyledTextCtrl, insertText, 2}},
+ {2985, {wxStyledTextCtrl, clearAll, 0}},
+ {2986, {wxStyledTextCtrl, clearDocumentStyle, 0}},
+ {2987, {wxStyledTextCtrl, getLength, 0}},
+ {2988, {wxStyledTextCtrl, getCharAt, 1}},
+ {2989, {wxStyledTextCtrl, getCurrentPos, 0}},
+ {2990, {wxStyledTextCtrl, getAnchor, 0}},
+ {2991, {wxStyledTextCtrl, getStyleAt, 1}},
+ {2992, {wxStyledTextCtrl, redo, 0}},
+ {2993, {wxStyledTextCtrl, setUndoCollection, 1}},
+ {2994, {wxStyledTextCtrl, selectAll, 0}},
+ {2995, {wxStyledTextCtrl, setSavePoint, 0}},
+ {2996, {wxStyledTextCtrl, getStyledText, 2}},
+ {2997, {wxStyledTextCtrl, canRedo, 0}},
+ {2998, {wxStyledTextCtrl, markerLineFromHandle, 1}},
+ {2999, {wxStyledTextCtrl, markerDeleteHandle, 1}},
+ {3000, {wxStyledTextCtrl, getUndoCollection, 0}},
+ {3001, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
+ {3002, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
+ {3003, {wxStyledTextCtrl, positionFromPoint, 1}},
+ {3004, {wxStyledTextCtrl, positionFromPointClose, 2}},
+ {3005, {wxStyledTextCtrl, gotoLine, 1}},
+ {3006, {wxStyledTextCtrl, gotoPos, 1}},
+ {3007, {wxStyledTextCtrl, setAnchor, 1}},
+ {3008, {wxStyledTextCtrl, getCurLine, 1}},
+ {3009, {wxStyledTextCtrl, getEndStyled, 0}},
+ {3010, {wxStyledTextCtrl, convertEOLs, 1}},
+ {3011, {wxStyledTextCtrl, getEOLMode, 0}},
+ {3012, {wxStyledTextCtrl, setEOLMode, 1}},
+ {3013, {wxStyledTextCtrl, startStyling, 2}},
+ {3014, {wxStyledTextCtrl, setStyling, 2}},
+ {3015, {wxStyledTextCtrl, getBufferedDraw, 0}},
+ {3016, {wxStyledTextCtrl, setBufferedDraw, 1}},
+ {3017, {wxStyledTextCtrl, setTabWidth, 1}},
+ {3018, {wxStyledTextCtrl, getTabWidth, 0}},
+ {3019, {wxStyledTextCtrl, setCodePage, 1}},
+ {3020, {wxStyledTextCtrl, markerDefine, 3}},
+ {3021, {wxStyledTextCtrl, markerSetForeground, 2}},
+ {3022, {wxStyledTextCtrl, markerSetBackground, 2}},
+ {3023, {wxStyledTextCtrl, markerAdd, 2}},
+ {3024, {wxStyledTextCtrl, markerDelete, 2}},
+ {3025, {wxStyledTextCtrl, markerDeleteAll, 1}},
+ {3026, {wxStyledTextCtrl, markerGet, 1}},
+ {3027, {wxStyledTextCtrl, markerNext, 2}},
+ {3028, {wxStyledTextCtrl, markerPrevious, 2}},
+ {3029, {wxStyledTextCtrl, markerDefineBitmap, 2}},
+ {3030, {wxStyledTextCtrl, markerAddSet, 2}},
+ {3031, {wxStyledTextCtrl, markerSetAlpha, 2}},
+ {3032, {wxStyledTextCtrl, setMarginType, 2}},
+ {3033, {wxStyledTextCtrl, getMarginType, 1}},
+ {3034, {wxStyledTextCtrl, setMarginWidth, 2}},
+ {3035, {wxStyledTextCtrl, getMarginWidth, 1}},
+ {3036, {wxStyledTextCtrl, setMarginMask, 2}},
+ {3037, {wxStyledTextCtrl, getMarginMask, 1}},
+ {3038, {wxStyledTextCtrl, setMarginSensitive, 2}},
+ {3039, {wxStyledTextCtrl, getMarginSensitive, 1}},
+ {3040, {wxStyledTextCtrl, styleClearAll, 0}},
+ {3041, {wxStyledTextCtrl, styleSetForeground, 2}},
+ {3042, {wxStyledTextCtrl, styleSetBackground, 2}},
+ {3043, {wxStyledTextCtrl, styleSetBold, 2}},
+ {3044, {wxStyledTextCtrl, styleSetItalic, 2}},
+ {3045, {wxStyledTextCtrl, styleSetSize, 2}},
+ {3046, {wxStyledTextCtrl, styleSetFaceName, 2}},
+ {3047, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
+ {3048, {wxStyledTextCtrl, styleResetDefault, 0}},
+ {3049, {wxStyledTextCtrl, styleSetUnderline, 2}},
+ {3050, {wxStyledTextCtrl, styleSetCase, 2}},
+ {3051, {wxStyledTextCtrl, styleSetHotSpot, 2}},
+ {3052, {wxStyledTextCtrl, setSelForeground, 2}},
+ {3053, {wxStyledTextCtrl, setSelBackground, 2}},
+ {3054, {wxStyledTextCtrl, getSelAlpha, 0}},
+ {3055, {wxStyledTextCtrl, setSelAlpha, 1}},
+ {3056, {wxStyledTextCtrl, setCaretForeground, 1}},
+ {3057, {wxStyledTextCtrl, cmdKeyAssign, 3}},
+ {3058, {wxStyledTextCtrl, cmdKeyClear, 2}},
+ {3059, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
+ {3060, {wxStyledTextCtrl, setStyleBytes, 2}},
+ {3061, {wxStyledTextCtrl, styleSetVisible, 2}},
+ {3062, {wxStyledTextCtrl, getCaretPeriod, 0}},
+ {3063, {wxStyledTextCtrl, setCaretPeriod, 1}},
+ {3064, {wxStyledTextCtrl, setWordChars, 1}},
+ {3065, {wxStyledTextCtrl, beginUndoAction, 0}},
+ {3066, {wxStyledTextCtrl, endUndoAction, 0}},
+ {3067, {wxStyledTextCtrl, indicatorSetStyle, 2}},
+ {3068, {wxStyledTextCtrl, indicatorGetStyle, 1}},
+ {3069, {wxStyledTextCtrl, indicatorSetForeground, 2}},
+ {3070, {wxStyledTextCtrl, indicatorGetForeground, 1}},
+ {3071, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
+ {3072, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
+ {3073, {wxStyledTextCtrl, getStyleBits, 0}},
+ {3074, {wxStyledTextCtrl, setLineState, 2}},
+ {3075, {wxStyledTextCtrl, getLineState, 1}},
+ {3076, {wxStyledTextCtrl, getMaxLineState, 0}},
+ {3077, {wxStyledTextCtrl, getCaretLineVisible, 0}},
+ {3078, {wxStyledTextCtrl, setCaretLineVisible, 1}},
+ {3079, {wxStyledTextCtrl, getCaretLineBackground, 0}},
+ {3080, {wxStyledTextCtrl, setCaretLineBackground, 1}},
+ {3081, {wxStyledTextCtrl, autoCompShow, 2}},
+ {3082, {wxStyledTextCtrl, autoCompCancel, 0}},
+ {3083, {wxStyledTextCtrl, autoCompActive, 0}},
+ {3084, {wxStyledTextCtrl, autoCompPosStart, 0}},
+ {3085, {wxStyledTextCtrl, autoCompComplete, 0}},
+ {3086, {wxStyledTextCtrl, autoCompStops, 1}},
+ {3087, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
+ {3088, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
+ {3089, {wxStyledTextCtrl, autoCompSelect, 1}},
+ {3090, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
+ {3091, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
+ {3092, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
+ {3093, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
+ {3094, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
+ {3095, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
+ {3096, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
+ {3097, {wxStyledTextCtrl, userListShow, 2}},
+ {3098, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
+ {3099, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
+ {3100, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
+ {3101, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
+ {3102, {wxStyledTextCtrl, registerImage, 2}},
+ {3103, {wxStyledTextCtrl, clearRegisteredImages, 0}},
+ {3104, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
+ {3105, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
+ {3106, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
+ {3107, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
+ {3108, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
+ {3109, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
+ {3110, {wxStyledTextCtrl, setIndent, 1}},
+ {3111, {wxStyledTextCtrl, getIndent, 0}},
+ {3112, {wxStyledTextCtrl, setUseTabs, 1}},
+ {3113, {wxStyledTextCtrl, getUseTabs, 0}},
+ {3114, {wxStyledTextCtrl, setLineIndentation, 2}},
+ {3115, {wxStyledTextCtrl, getLineIndentation, 1}},
+ {3116, {wxStyledTextCtrl, getLineIndentPosition, 1}},
+ {3117, {wxStyledTextCtrl, getColumn, 1}},
+ {3118, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
+ {3119, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
+ {3120, {wxStyledTextCtrl, setIndentationGuides, 1}},
+ {3121, {wxStyledTextCtrl, getIndentationGuides, 0}},
+ {3122, {wxStyledTextCtrl, setHighlightGuide, 1}},
+ {3123, {wxStyledTextCtrl, getHighlightGuide, 0}},
+ {3124, {wxStyledTextCtrl, getLineEndPosition, 1}},
+ {3125, {wxStyledTextCtrl, getCodePage, 0}},
+ {3126, {wxStyledTextCtrl, getCaretForeground, 0}},
+ {3127, {wxStyledTextCtrl, getReadOnly, 0}},
+ {3128, {wxStyledTextCtrl, setCurrentPos, 1}},
+ {3129, {wxStyledTextCtrl, setSelectionStart, 1}},
+ {3130, {wxStyledTextCtrl, getSelectionStart, 0}},
+ {3131, {wxStyledTextCtrl, setSelectionEnd, 1}},
+ {3132, {wxStyledTextCtrl, getSelectionEnd, 0}},
+ {3133, {wxStyledTextCtrl, setPrintMagnification, 1}},
+ {3134, {wxStyledTextCtrl, getPrintMagnification, 0}},
+ {3135, {wxStyledTextCtrl, setPrintColourMode, 1}},
+ {3136, {wxStyledTextCtrl, getPrintColourMode, 0}},
+ {3137, {wxStyledTextCtrl, findText, 4}},
+ {3138, {wxStyledTextCtrl, formatRange, 7}},
+ {3139, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
+ {3140, {wxStyledTextCtrl, getLine, 1}},
+ {3141, {wxStyledTextCtrl, getLineCount, 0}},
+ {3142, {wxStyledTextCtrl, setMarginLeft, 1}},
+ {3143, {wxStyledTextCtrl, getMarginLeft, 0}},
+ {3144, {wxStyledTextCtrl, setMarginRight, 1}},
+ {3145, {wxStyledTextCtrl, getMarginRight, 0}},
+ {3146, {wxStyledTextCtrl, getModify, 0}},
+ {3147, {wxStyledTextCtrl, setSelection, 2}},
+ {3148, {wxStyledTextCtrl, getSelectedText, 0}},
+ {3149, {wxStyledTextCtrl, getTextRange, 2}},
+ {3150, {wxStyledTextCtrl, hideSelection, 1}},
+ {3151, {wxStyledTextCtrl, lineFromPosition, 1}},
+ {3152, {wxStyledTextCtrl, positionFromLine, 1}},
+ {3153, {wxStyledTextCtrl, lineScroll, 2}},
+ {3154, {wxStyledTextCtrl, ensureCaretVisible, 0}},
+ {3155, {wxStyledTextCtrl, replaceSelection, 1}},
+ {3156, {wxStyledTextCtrl, setReadOnly, 1}},
+ {3157, {wxStyledTextCtrl, canPaste, 0}},
+ {3158, {wxStyledTextCtrl, canUndo, 0}},
+ {3159, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
+ {3160, {wxStyledTextCtrl, undo, 0}},
+ {3161, {wxStyledTextCtrl, cut, 0}},
+ {3162, {wxStyledTextCtrl, copy, 0}},
+ {3163, {wxStyledTextCtrl, paste, 0}},
+ {3164, {wxStyledTextCtrl, clear, 0}},
+ {3165, {wxStyledTextCtrl, setText, 1}},
+ {3166, {wxStyledTextCtrl, getText, 0}},
+ {3167, {wxStyledTextCtrl, getTextLength, 0}},
+ {3168, {wxStyledTextCtrl, getOvertype, 0}},
+ {3169, {wxStyledTextCtrl, setCaretWidth, 1}},
+ {3170, {wxStyledTextCtrl, getCaretWidth, 0}},
+ {3171, {wxStyledTextCtrl, setTargetStart, 1}},
+ {3172, {wxStyledTextCtrl, getTargetStart, 0}},
+ {3173, {wxStyledTextCtrl, setTargetEnd, 1}},
+ {3174, {wxStyledTextCtrl, getTargetEnd, 0}},
+ {3175, {wxStyledTextCtrl, replaceTarget, 1}},
+ {3176, {wxStyledTextCtrl, searchInTarget, 1}},
+ {3177, {wxStyledTextCtrl, setSearchFlags, 1}},
+ {3178, {wxStyledTextCtrl, getSearchFlags, 0}},
+ {3179, {wxStyledTextCtrl, callTipShow, 2}},
+ {3180, {wxStyledTextCtrl, callTipCancel, 0}},
+ {3181, {wxStyledTextCtrl, callTipActive, 0}},
+ {3182, {wxStyledTextCtrl, callTipPosAtStart, 0}},
+ {3183, {wxStyledTextCtrl, callTipSetHighlight, 2}},
+ {3184, {wxStyledTextCtrl, callTipSetBackground, 1}},
+ {3185, {wxStyledTextCtrl, callTipSetForeground, 1}},
+ {3186, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
+ {3187, {wxStyledTextCtrl, callTipUseStyle, 1}},
+ {3188, {wxStyledTextCtrl, visibleFromDocLine, 1}},
+ {3189, {wxStyledTextCtrl, docLineFromVisible, 1}},
+ {3190, {wxStyledTextCtrl, wrapCount, 1}},
+ {3191, {wxStyledTextCtrl, setFoldLevel, 2}},
+ {3192, {wxStyledTextCtrl, getFoldLevel, 1}},
+ {3193, {wxStyledTextCtrl, getLastChild, 2}},
+ {3194, {wxStyledTextCtrl, getFoldParent, 1}},
+ {3195, {wxStyledTextCtrl, showLines, 2}},
+ {3196, {wxStyledTextCtrl, hideLines, 2}},
+ {3197, {wxStyledTextCtrl, getLineVisible, 1}},
+ {3198, {wxStyledTextCtrl, setFoldExpanded, 2}},
+ {3199, {wxStyledTextCtrl, getFoldExpanded, 1}},
+ {3200, {wxStyledTextCtrl, toggleFold, 1}},
+ {3201, {wxStyledTextCtrl, ensureVisible, 1}},
+ {3202, {wxStyledTextCtrl, setFoldFlags, 1}},
+ {3203, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
+ {3204, {wxStyledTextCtrl, setTabIndents, 1}},
+ {3205, {wxStyledTextCtrl, getTabIndents, 0}},
+ {3206, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
+ {3207, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
+ {3208, {wxStyledTextCtrl, setMouseDwellTime, 1}},
+ {3209, {wxStyledTextCtrl, getMouseDwellTime, 0}},
+ {3210, {wxStyledTextCtrl, wordStartPosition, 2}},
+ {3211, {wxStyledTextCtrl, wordEndPosition, 2}},
+ {3212, {wxStyledTextCtrl, setWrapMode, 1}},
+ {3213, {wxStyledTextCtrl, getWrapMode, 0}},
+ {3214, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
+ {3215, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
+ {3216, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
+ {3217, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
+ {3218, {wxStyledTextCtrl, setWrapStartIndent, 1}},
+ {3219, {wxStyledTextCtrl, getWrapStartIndent, 0}},
+ {3220, {wxStyledTextCtrl, setLayoutCache, 1}},
+ {3221, {wxStyledTextCtrl, getLayoutCache, 0}},
+ {3222, {wxStyledTextCtrl, setScrollWidth, 1}},
+ {3223, {wxStyledTextCtrl, getScrollWidth, 0}},
+ {3224, {wxStyledTextCtrl, textWidth, 2}},
+ {3225, {wxStyledTextCtrl, getEndAtLastLine, 0}},
+ {3226, {wxStyledTextCtrl, textHeight, 1}},
+ {3227, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
+ {3228, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
+ {3229, {wxStyledTextCtrl, appendText, 1}},
+ {3230, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
+ {3231, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
+ {3232, {wxStyledTextCtrl, targetFromSelection, 0}},
+ {3233, {wxStyledTextCtrl, linesJoin, 0}},
+ {3234, {wxStyledTextCtrl, linesSplit, 1}},
+ {3235, {wxStyledTextCtrl, setFoldMarginColour, 2}},
+ {3236, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
+ {3237, {wxStyledTextCtrl, lineDown, 0}},
+ {3238, {wxStyledTextCtrl, lineDownExtend, 0}},
+ {3239, {wxStyledTextCtrl, lineUp, 0}},
+ {3240, {wxStyledTextCtrl, lineUpExtend, 0}},
+ {3241, {wxStyledTextCtrl, charLeft, 0}},
+ {3242, {wxStyledTextCtrl, charLeftExtend, 0}},
+ {3243, {wxStyledTextCtrl, charRight, 0}},
+ {3244, {wxStyledTextCtrl, charRightExtend, 0}},
+ {3245, {wxStyledTextCtrl, wordLeft, 0}},
+ {3246, {wxStyledTextCtrl, wordLeftExtend, 0}},
+ {3247, {wxStyledTextCtrl, wordRight, 0}},
+ {3248, {wxStyledTextCtrl, wordRightExtend, 0}},
+ {3249, {wxStyledTextCtrl, home, 0}},
+ {3250, {wxStyledTextCtrl, homeExtend, 0}},
+ {3251, {wxStyledTextCtrl, lineEnd, 0}},
+ {3252, {wxStyledTextCtrl, lineEndExtend, 0}},
+ {3253, {wxStyledTextCtrl, documentStart, 0}},
+ {3254, {wxStyledTextCtrl, documentStartExtend, 0}},
+ {3255, {wxStyledTextCtrl, documentEnd, 0}},
+ {3256, {wxStyledTextCtrl, documentEndExtend, 0}},
+ {3257, {wxStyledTextCtrl, pageUp, 0}},
+ {3258, {wxStyledTextCtrl, pageUpExtend, 0}},
+ {3259, {wxStyledTextCtrl, pageDown, 0}},
+ {3260, {wxStyledTextCtrl, pageDownExtend, 0}},
+ {3261, {wxStyledTextCtrl, editToggleOvertype, 0}},
+ {3262, {wxStyledTextCtrl, cancel, 0}},
+ {3263, {wxStyledTextCtrl, deleteBack, 0}},
+ {3264, {wxStyledTextCtrl, tab, 0}},
+ {3265, {wxStyledTextCtrl, backTab, 0}},
+ {3266, {wxStyledTextCtrl, newLine, 0}},
+ {3267, {wxStyledTextCtrl, formFeed, 0}},
+ {3268, {wxStyledTextCtrl, vCHome, 0}},
+ {3269, {wxStyledTextCtrl, vCHomeExtend, 0}},
+ {3270, {wxStyledTextCtrl, zoomIn, 0}},
+ {3271, {wxStyledTextCtrl, zoomOut, 0}},
+ {3272, {wxStyledTextCtrl, delWordLeft, 0}},
+ {3273, {wxStyledTextCtrl, delWordRight, 0}},
+ {3274, {wxStyledTextCtrl, lineCut, 0}},
+ {3275, {wxStyledTextCtrl, lineDelete, 0}},
+ {3276, {wxStyledTextCtrl, lineTranspose, 0}},
+ {3277, {wxStyledTextCtrl, lineDuplicate, 0}},
+ {3278, {wxStyledTextCtrl, lowerCase, 0}},
+ {3279, {wxStyledTextCtrl, upperCase, 0}},
+ {3280, {wxStyledTextCtrl, lineScrollDown, 0}},
+ {3281, {wxStyledTextCtrl, lineScrollUp, 0}},
+ {3282, {wxStyledTextCtrl, deleteBackNotLine, 0}},
+ {3283, {wxStyledTextCtrl, homeDisplay, 0}},
+ {3284, {wxStyledTextCtrl, homeDisplayExtend, 0}},
+ {3285, {wxStyledTextCtrl, lineEndDisplay, 0}},
+ {3286, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
+ {3287, {wxStyledTextCtrl, homeWrapExtend, 0}},
+ {3288, {wxStyledTextCtrl, lineEndWrap, 0}},
+ {3289, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
+ {3290, {wxStyledTextCtrl, vCHomeWrap, 0}},
+ {3291, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
+ {3292, {wxStyledTextCtrl, lineCopy, 0}},
+ {3293, {wxStyledTextCtrl, moveCaretInsideView, 0}},
+ {3294, {wxStyledTextCtrl, lineLength, 1}},
+ {3295, {wxStyledTextCtrl, braceHighlight, 2}},
+ {3296, {wxStyledTextCtrl, braceBadLight, 1}},
+ {3297, {wxStyledTextCtrl, braceMatch, 1}},
+ {3298, {wxStyledTextCtrl, getViewEOL, 0}},
+ {3299, {wxStyledTextCtrl, setViewEOL, 1}},
+ {3300, {wxStyledTextCtrl, setModEventMask, 1}},
+ {3301, {wxStyledTextCtrl, getEdgeColumn, 0}},
+ {3302, {wxStyledTextCtrl, setEdgeColumn, 1}},
+ {3303, {wxStyledTextCtrl, setEdgeMode, 1}},
+ {3304, {wxStyledTextCtrl, getEdgeMode, 0}},
+ {3305, {wxStyledTextCtrl, getEdgeColour, 0}},
+ {3306, {wxStyledTextCtrl, setEdgeColour, 1}},
+ {3307, {wxStyledTextCtrl, searchAnchor, 0}},
+ {3308, {wxStyledTextCtrl, searchNext, 2}},
+ {3309, {wxStyledTextCtrl, searchPrev, 2}},
+ {3310, {wxStyledTextCtrl, linesOnScreen, 0}},
+ {3311, {wxStyledTextCtrl, usePopUp, 1}},
+ {3312, {wxStyledTextCtrl, selectionIsRectangle, 0}},
+ {3313, {wxStyledTextCtrl, setZoom, 1}},
+ {3314, {wxStyledTextCtrl, getZoom, 0}},
+ {3315, {wxStyledTextCtrl, getModEventMask, 0}},
+ {3316, {wxStyledTextCtrl, setSTCFocus, 1}},
+ {3317, {wxStyledTextCtrl, getSTCFocus, 0}},
+ {3318, {wxStyledTextCtrl, setStatus, 1}},
+ {3319, {wxStyledTextCtrl, getStatus, 0}},
+ {3320, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
+ {3321, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
+ {3322, {wxStyledTextCtrl, setSTCCursor, 1}},
+ {3323, {wxStyledTextCtrl, getSTCCursor, 0}},
+ {3324, {wxStyledTextCtrl, setControlCharSymbol, 1}},
+ {3325, {wxStyledTextCtrl, getControlCharSymbol, 0}},
+ {3326, {wxStyledTextCtrl, wordPartLeft, 0}},
+ {3327, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
+ {3328, {wxStyledTextCtrl, wordPartRight, 0}},
+ {3329, {wxStyledTextCtrl, wordPartRightExtend, 0}},
+ {3330, {wxStyledTextCtrl, setVisiblePolicy, 2}},
+ {3331, {wxStyledTextCtrl, delLineLeft, 0}},
+ {3332, {wxStyledTextCtrl, delLineRight, 0}},
+ {3333, {wxStyledTextCtrl, getXOffset, 0}},
+ {3334, {wxStyledTextCtrl, chooseCaretX, 0}},
+ {3335, {wxStyledTextCtrl, setXCaretPolicy, 2}},
+ {3336, {wxStyledTextCtrl, setYCaretPolicy, 2}},
+ {3337, {wxStyledTextCtrl, getPrintWrapMode, 0}},
+ {3338, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
+ {3339, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
+ {3340, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
+ {3341, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
+ {3342, {wxStyledTextCtrl, paraDownExtend, 0}},
+ {3343, {wxStyledTextCtrl, paraUp, 0}},
+ {3344, {wxStyledTextCtrl, paraUpExtend, 0}},
+ {3345, {wxStyledTextCtrl, positionBefore, 1}},
+ {3346, {wxStyledTextCtrl, positionAfter, 1}},
+ {3347, {wxStyledTextCtrl, copyRange, 2}},
+ {3348, {wxStyledTextCtrl, copyText, 2}},
+ {3349, {wxStyledTextCtrl, setSelectionMode, 1}},
+ {3350, {wxStyledTextCtrl, getSelectionMode, 0}},
+ {3351, {wxStyledTextCtrl, lineDownRectExtend, 0}},
+ {3352, {wxStyledTextCtrl, lineUpRectExtend, 0}},
+ {3353, {wxStyledTextCtrl, charLeftRectExtend, 0}},
+ {3354, {wxStyledTextCtrl, charRightRectExtend, 0}},
+ {3355, {wxStyledTextCtrl, homeRectExtend, 0}},
+ {3356, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
+ {3357, {wxStyledTextCtrl, lineEndRectExtend, 0}},
+ {3358, {wxStyledTextCtrl, pageUpRectExtend, 0}},
+ {3359, {wxStyledTextCtrl, pageDownRectExtend, 0}},
+ {3360, {wxStyledTextCtrl, stutteredPageUp, 0}},
+ {3361, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
+ {3362, {wxStyledTextCtrl, stutteredPageDown, 0}},
+ {3363, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
+ {3364, {wxStyledTextCtrl, wordLeftEnd, 0}},
+ {3365, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
+ {3366, {wxStyledTextCtrl, wordRightEnd, 0}},
+ {3367, {wxStyledTextCtrl, wordRightEndExtend, 0}},
+ {3368, {wxStyledTextCtrl, setWhitespaceChars, 1}},
+ {3369, {wxStyledTextCtrl, setCharsDefault, 0}},
+ {3370, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
+ {3371, {wxStyledTextCtrl, allocate, 1}},
+ {3372, {wxStyledTextCtrl, findColumn, 2}},
+ {3373, {wxStyledTextCtrl, getCaretSticky, 0}},
+ {3374, {wxStyledTextCtrl, setCaretSticky, 1}},
+ {3375, {wxStyledTextCtrl, toggleCaretSticky, 0}},
+ {3376, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
+ {3377, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
+ {3378, {wxStyledTextCtrl, selectionDuplicate, 0}},
+ {3379, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
+ {3380, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
+ {3381, {wxStyledTextCtrl, startRecord, 0}},
+ {3382, {wxStyledTextCtrl, stopRecord, 0}},
+ {3383, {wxStyledTextCtrl, setLexer, 1}},
+ {3384, {wxStyledTextCtrl, getLexer, 0}},
+ {3385, {wxStyledTextCtrl, colourise, 2}},
+ {3386, {wxStyledTextCtrl, setProperty, 2}},
+ {3387, {wxStyledTextCtrl, setKeyWords, 2}},
+ {3388, {wxStyledTextCtrl, setLexerLanguage, 1}},
+ {3389, {wxStyledTextCtrl, getProperty, 1}},
+ {3390, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
+ {3391, {wxStyledTextCtrl, getCurrentLine, 0}},
+ {3392, {wxStyledTextCtrl, styleSetSpec, 2}},
+ {3393, {wxStyledTextCtrl, styleSetFont, 2}},
+ {3394, {wxStyledTextCtrl, styleSetFontAttr, 7}},
+ {3395, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
+ {3396, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
+ {3397, {wxStyledTextCtrl, cmdKeyExecute, 1}},
+ {3398, {wxStyledTextCtrl, setMargins, 2}},
+ {3399, {wxStyledTextCtrl, getSelection, 2}},
+ {3400, {wxStyledTextCtrl, pointFromPosition, 1}},
+ {3401, {wxStyledTextCtrl, scrollToLine, 1}},
+ {3402, {wxStyledTextCtrl, scrollToColumn, 1}},
+ {3403, {wxStyledTextCtrl, setVScrollBar, 1}},
+ {3404, {wxStyledTextCtrl, setHScrollBar, 1}},
+ {3405, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
+ {3406, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
+ {3407, {wxStyledTextCtrl, saveFile, 1}},
+ {3408, {wxStyledTextCtrl, loadFile, 1}},
+ {3409, {wxStyledTextCtrl, doDragOver, 3}},
+ {3410, {wxStyledTextCtrl, doDropText, 3}},
+ {3411, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
+ {3412, {wxStyledTextCtrl, addTextRaw, 1}},
+ {3413, {wxStyledTextCtrl, insertTextRaw, 2}},
+ {3414, {wxStyledTextCtrl, getCurLineRaw, 1}},
+ {3415, {wxStyledTextCtrl, getLineRaw, 1}},
+ {3416, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
+ {3417, {wxStyledTextCtrl, getTextRangeRaw, 2}},
+ {3418, {wxStyledTextCtrl, setTextRaw, 1}},
+ {3419, {wxStyledTextCtrl, getTextRaw, 0}},
+ {3420, {wxStyledTextCtrl, appendTextRaw, 1}},
+ {3421, {wxArtProvider, getBitmap, 2}},
+ {3422, {wxArtProvider, getIcon, 2}},
+ {3423, {wxTreeEvent, getKeyCode, 0}},
+ {3424, {wxTreeEvent, getItem, 0}},
+ {3425, {wxTreeEvent, getKeyEvent, 0}},
+ {3426, {wxTreeEvent, getLabel, 0}},
+ {3427, {wxTreeEvent, getOldItem, 0}},
+ {3428, {wxTreeEvent, getPoint, 0}},
+ {3429, {wxTreeEvent, isEditCancelled, 0}},
+ {3430, {wxTreeEvent, setToolTip, 1}},
+ {3431, {wxNotebookEvent, getOldSelection, 0}},
+ {3432, {wxNotebookEvent, getSelection, 0}},
+ {3433, {wxNotebookEvent, setOldSelection, 1}},
+ {3434, {wxNotebookEvent, setSelection, 1}},
+ {3435, {wxFileDataObject, new, 0}},
+ {3436, {wxFileDataObject, addFile, 1}},
+ {3437, {wxFileDataObject, getFilenames, 0}},
+ {3438, {wxFileDataObject, 'Destroy', undefined}},
+ {3439, {wxTextDataObject, new, 1}},
+ {3440, {wxTextDataObject, getTextLength, 0}},
+ {3441, {wxTextDataObject, getText, 0}},
+ {3442, {wxTextDataObject, setText, 1}},
+ {3443, {wxTextDataObject, 'Destroy', undefined}},
+ {3444, {wxBitmapDataObject, new_1_1, 1}},
+ {3445, {wxBitmapDataObject, new_1_0, 1}},
+ {3446, {wxBitmapDataObject, getBitmap, 0}},
+ {3447, {wxBitmapDataObject, setBitmap, 1}},
+ {3448, {wxBitmapDataObject, 'Destroy', undefined}},
+ {3450, {wxClipboard, new, 0}},
+ {3451, {wxClipboard, destruct, 0}},
+ {3452, {wxClipboard, addData, 1}},
+ {3453, {wxClipboard, clear, 0}},
+ {3454, {wxClipboard, close, 0}},
+ {3455, {wxClipboard, flush, 0}},
+ {3456, {wxClipboard, getData, 1}},
+ {3457, {wxClipboard, isOpened, 0}},
+ {3458, {wxClipboard, open, 0}},
+ {3459, {wxClipboard, setData, 1}},
+ {3461, {wxClipboard, usePrimarySelection, 1}},
+ {3462, {wxClipboard, isSupported, 1}},
+ {3463, {wxClipboard, get, 0}},
+ {3464, {wxSpinEvent, getPosition, 0}},
+ {3465, {wxSpinEvent, setPosition, 1}},
+ {3466, {wxSplitterWindow, new_0, 0}},
+ {3467, {wxSplitterWindow, new_2, 2}},
+ {3468, {wxSplitterWindow, destruct, 0}},
+ {3469, {wxSplitterWindow, create, 2}},
+ {3470, {wxSplitterWindow, getMinimumPaneSize, 0}},
+ {3471, {wxSplitterWindow, getSashGravity, 0}},
+ {3472, {wxSplitterWindow, getSashPosition, 0}},
+ {3473, {wxSplitterWindow, getSplitMode, 0}},
+ {3474, {wxSplitterWindow, getWindow1, 0}},
+ {3475, {wxSplitterWindow, getWindow2, 0}},
+ {3476, {wxSplitterWindow, initialize, 1}},
+ {3477, {wxSplitterWindow, isSplit, 0}},
+ {3478, {wxSplitterWindow, replaceWindow, 2}},
+ {3479, {wxSplitterWindow, setSashGravity, 1}},
+ {3480, {wxSplitterWindow, setSashPosition, 2}},
+ {3481, {wxSplitterWindow, setSashSize, 1}},
+ {3482, {wxSplitterWindow, setMinimumPaneSize, 1}},
+ {3483, {wxSplitterWindow, setSplitMode, 1}},
+ {3484, {wxSplitterWindow, splitHorizontally, 3}},
+ {3485, {wxSplitterWindow, splitVertically, 3}},
+ {3486, {wxSplitterWindow, unsplit, 1}},
+ {3487, {wxSplitterWindow, updateSize, 0}},
+ {3488, {wxSplitterEvent, getSashPosition, 0}},
+ {3489, {wxSplitterEvent, getX, 0}},
+ {3490, {wxSplitterEvent, getY, 0}},
+ {3491, {wxSplitterEvent, getWindowBeingRemoved, 0}},
+ {3492, {wxSplitterEvent, setSashPosition, 1}},
+ {3493, {wxHtmlWindow, new_0, 0}},
+ {3494, {wxHtmlWindow, new_2, 2}},
+ {3495, {wxHtmlWindow, appendToPage, 1}},
+ {3496, {wxHtmlWindow, getOpenedAnchor, 0}},
+ {3497, {wxHtmlWindow, getOpenedPage, 0}},
+ {3498, {wxHtmlWindow, getOpenedPageTitle, 0}},
+ {3499, {wxHtmlWindow, getRelatedFrame, 0}},
+ {3500, {wxHtmlWindow, historyBack, 0}},
+ {3501, {wxHtmlWindow, historyCanBack, 0}},
+ {3502, {wxHtmlWindow, historyCanForward, 0}},
+ {3503, {wxHtmlWindow, historyClear, 0}},
+ {3504, {wxHtmlWindow, historyForward, 0}},
+ {3505, {wxHtmlWindow, loadFile, 1}},
+ {3506, {wxHtmlWindow, loadPage, 1}},
+ {3507, {wxHtmlWindow, selectAll, 0}},
+ {3508, {wxHtmlWindow, selectionToText, 0}},
+ {3509, {wxHtmlWindow, selectLine, 1}},
+ {3510, {wxHtmlWindow, selectWord, 1}},
+ {3511, {wxHtmlWindow, setBorders, 1}},
+ {3512, {wxHtmlWindow, setFonts, 3}},
+ {3513, {wxHtmlWindow, setPage, 1}},
+ {3514, {wxHtmlWindow, setRelatedFrame, 2}},
+ {3515, {wxHtmlWindow, setRelatedStatusBar, 1}},
+ {3516, {wxHtmlWindow, toText, 0}},
+ {3517, {wxHtmlWindow, 'Destroy', undefined}},
+ {3518, {wxHtmlLinkEvent, getLinkInfo, 0}},
+ {3519, {wxSystemSettings, getColour, 1}},
+ {3520, {wxSystemSettings, getFont, 1}},
+ {3521, {wxSystemSettings, getMetric, 2}},
+ {3522, {wxSystemSettings, getScreenType, 0}},
+ {3523, {wxSystemOptions, getOption, 1}},
+ {3524, {wxSystemOptions, getOptionInt, 1}},
+ {3525, {wxSystemOptions, hasOption, 1}},
+ {3526, {wxSystemOptions, isFalse, 1}},
+ {3527, {wxSystemOptions, setOption_2_1, 2}},
+ {3528, {wxSystemOptions, setOption_2_0, 2}},
+ {3529, {wxAuiNotebookEvent, setSelection, 1}},
+ {3530, {wxAuiNotebookEvent, getSelection, 0}},
+ {3531, {wxAuiNotebookEvent, setOldSelection, 1}},
+ {3532, {wxAuiNotebookEvent, getOldSelection, 0}},
+ {3533, {wxAuiNotebookEvent, setDragSource, 1}},
+ {3534, {wxAuiNotebookEvent, getDragSource, 0}},
+ {3535, {wxAuiManagerEvent, setManager, 1}},
+ {3536, {wxAuiManagerEvent, getManager, 0}},
+ {3537, {wxAuiManagerEvent, setPane, 1}},
+ {3538, {wxAuiManagerEvent, getPane, 0}},
+ {3539, {wxAuiManagerEvent, setButton, 1}},
+ {3540, {wxAuiManagerEvent, getButton, 0}},
+ {3541, {wxAuiManagerEvent, setDC, 1}},
+ {3542, {wxAuiManagerEvent, getDC, 0}},
+ {3543, {wxAuiManagerEvent, veto, 1}},
+ {3544, {wxAuiManagerEvent, getVeto, 0}},
+ {3545, {wxAuiManagerEvent, setCanVeto, 1}},
+ {3546, {wxAuiManagerEvent, canVeto, 0}},
+ {3547, {wxLogNull, new, 0}},
+ {3548, {wxLogNull, 'Destroy', undefined}},
+ {3549, {wxTaskBarIcon, new, 0}},
+ {3550, {wxTaskBarIcon, destruct, 0}},
+ {3551, {wxTaskBarIcon, popupMenu, 1}},
+ {3552, {wxTaskBarIcon, removeIcon, 0}},
+ {3553, {wxTaskBarIcon, setIcon, 2}},
+ {3554, {wxLocale, new_0, 0}},
+ {3556, {wxLocale, new_2, 2}},
+ {3557, {wxLocale, destruct, 0}},
+ {3559, {wxLocale, init, 1}},
+ {3560, {wxLocale, addCatalog_1, 1}},
+ {3561, {wxLocale, addCatalog_3, 3}},
+ {3562, {wxLocale, addCatalogLookupPathPrefix, 1}},
+ {3563, {wxLocale, getCanonicalName, 0}},
+ {3564, {wxLocale, getLanguage, 0}},
+ {3565, {wxLocale, getLanguageName, 1}},
+ {3566, {wxLocale, getLocale, 0}},
+ {3567, {wxLocale, getName, 0}},
+ {3568, {wxLocale, getString_2, 2}},
+ {3569, {wxLocale, getString_4, 4}},
+ {3570, {wxLocale, getHeaderValue, 2}},
+ {3571, {wxLocale, getSysName, 0}},
+ {3572, {wxLocale, getSystemEncoding, 0}},
+ {3573, {wxLocale, getSystemEncodingName, 0}},
+ {3574, {wxLocale, getSystemLanguage, 0}},
+ {3575, {wxLocale, isLoaded, 1}},
+ {3576, {wxLocale, isOk, 0}},
+ {3577, {wxActivateEvent, getActive, 0}},
+ {3579, {wxPopupWindow, new_2, 2}},
+ {3580, {wxPopupWindow, new_0, 0}},
+ {3582, {wxPopupWindow, destruct, 0}},
+ {3583, {wxPopupWindow, create, 2}},
+ {3584, {wxPopupWindow, position, 2}},
+ {3585, {wxPopupTransientWindow, new_0, 0}},
+ {3586, {wxPopupTransientWindow, new_2, 2}},
+ {3587, {wxPopupTransientWindow, destruct, 0}},
+ {3588, {wxPopupTransientWindow, popup, 1}},
+ {3589, {wxPopupTransientWindow, dismiss, 0}},
+ {3590, {wxOverlay, new, 0}},
+ {3591, {wxOverlay, destruct, 0}},
+ {3592, {wxOverlay, reset, 0}},
+ {3593, {wxDCOverlay, new_6, 6}},
+ {3594, {wxDCOverlay, new_2, 2}},
+ {3595, {wxDCOverlay, destruct, 0}},
+ {3596, {wxDCOverlay, clear, 0}},
+ {3597, {wxDropFilesEvent, getPosition, 0}},
+ {3598, {wxDropFilesEvent, getNumberOfFiles, 0}},
+ {3599, {wxDropFilesEvent, getFiles, 0}},
{-1, {mod, func, -1}}
].
diff --git a/lib/wx/src/gen/wxe_funcs.hrl b/lib/wx/src/gen/wxe_funcs.hrl
index 84fa592aaa..af0cee0dcd 100644
--- a/lib/wx/src/gen/wxe_funcs.hrl
+++ b/lib/wx/src/gen/wxe_funcs.hrl
@@ -42,3330 +42,3335 @@
-define(wxWindow_Destroy, 123).
-define(wxWindow_DestroyChildren, 124).
-define(wxWindow_Disable, 125).
--define(wxWindow_Enable, 126).
--define(wxWindow_FindFocus, 127).
--define(wxWindow_FindWindow_1_0, 128).
--define(wxWindow_FindWindow_1_1, 129).
--define(wxWindow_FindWindowById, 130).
--define(wxWindow_FindWindowByName, 131).
--define(wxWindow_FindWindowByLabel, 132).
--define(wxWindow_Fit, 133).
--define(wxWindow_FitInside, 134).
--define(wxWindow_Freeze, 135).
--define(wxWindow_GetAcceleratorTable, 136).
--define(wxWindow_GetBackgroundColour, 137).
--define(wxWindow_GetBackgroundStyle, 138).
--define(wxWindow_GetBestSize, 139).
--define(wxWindow_GetCaret, 141).
--define(wxWindow_GetCapture, 142).
--define(wxWindow_GetCharHeight, 143).
--define(wxWindow_GetCharWidth, 144).
--define(wxWindow_GetChildren, 145).
--define(wxWindow_GetClientSize, 148).
--define(wxWindow_GetContainingSizer, 149).
--define(wxWindow_GetCursor, 150).
--define(wxWindow_GetDropTarget, 151).
--define(wxWindow_GetEventHandler, 152).
--define(wxWindow_GetExtraStyle, 153).
--define(wxWindow_GetFont, 154).
--define(wxWindow_GetForegroundColour, 155).
--define(wxWindow_GetGrandParent, 156).
--define(wxWindow_GetHandle, 157).
--define(wxWindow_GetHelpText, 158).
--define(wxWindow_GetId, 159).
--define(wxWindow_GetLabel, 160).
--define(wxWindow_GetMaxSize, 161).
--define(wxWindow_GetMinSize, 162).
--define(wxWindow_GetName, 163).
--define(wxWindow_GetParent, 164).
--define(wxWindow_GetPosition, 166).
--define(wxWindow_GetRect, 167).
--define(wxWindow_GetScreenPosition, 169).
--define(wxWindow_GetScreenRect, 170).
--define(wxWindow_GetScrollPos, 171).
--define(wxWindow_GetScrollRange, 172).
--define(wxWindow_GetScrollThumb, 173).
--define(wxWindow_GetSize, 175).
--define(wxWindow_GetSizer, 176).
--define(wxWindow_GetTextExtent, 177).
--define(wxWindow_GetToolTip, 178).
--define(wxWindow_GetUpdateRegion, 179).
--define(wxWindow_GetVirtualSize, 181).
--define(wxWindow_GetWindowStyleFlag, 183).
--define(wxWindow_GetWindowVariant, 184).
--define(wxWindow_HasCapture, 185).
--define(wxWindow_HasScrollbar, 186).
--define(wxWindow_HasTransparentBackground, 187).
--define(wxWindow_Hide, 188).
--define(wxWindow_InheritAttributes, 189).
--define(wxWindow_InitDialog, 190).
--define(wxWindow_InvalidateBestSize, 191).
--define(wxWindow_IsEnabled, 192).
--define(wxWindow_IsExposed_2, 193).
--define(wxWindow_IsExposed_4, 194).
--define(wxWindow_IsExposed_1_0, 195).
--define(wxWindow_IsExposed_1_1, 196).
--define(wxWindow_IsRetained, 197).
--define(wxWindow_IsShown, 198).
--define(wxWindow_IsTopLevel, 199).
--define(wxWindow_Layout, 200).
--define(wxWindow_LineDown, 201).
--define(wxWindow_LineUp, 202).
--define(wxWindow_Lower, 203).
--define(wxWindow_MakeModal, 204).
--define(wxWindow_Move_3, 205).
--define(wxWindow_Move_2, 206).
--define(wxWindow_MoveAfterInTabOrder, 207).
--define(wxWindow_MoveBeforeInTabOrder, 208).
--define(wxWindow_Navigate, 209).
--define(wxWindow_PageDown, 210).
--define(wxWindow_PageUp, 211).
--define(wxWindow_PopEventHandler, 212).
--define(wxWindow_PopupMenu_2, 213).
--define(wxWindow_PopupMenu_3, 214).
--define(wxWindow_Raise, 215).
--define(wxWindow_Refresh, 216).
--define(wxWindow_RefreshRect, 217).
--define(wxWindow_ReleaseMouse, 218).
--define(wxWindow_RemoveChild, 219).
--define(wxWindow_Reparent, 220).
--define(wxWindow_ScreenToClient_2, 221).
--define(wxWindow_ScreenToClient_1, 222).
--define(wxWindow_ScrollLines, 224).
--define(wxWindow_ScrollPages, 226).
--define(wxWindow_ScrollWindow, 227).
--define(wxWindow_SetAcceleratorTable, 228).
--define(wxWindow_SetAutoLayout, 229).
--define(wxWindow_SetBackgroundColour, 230).
--define(wxWindow_SetBackgroundStyle, 231).
--define(wxWindow_SetCaret, 232).
--define(wxWindow_SetClientSize_2, 233).
--define(wxWindow_SetClientSize_1_0, 234).
--define(wxWindow_SetClientSize_1_1, 235).
--define(wxWindow_SetContainingSizer, 236).
--define(wxWindow_SetCursor, 237).
--define(wxWindow_SetMaxSize, 238).
--define(wxWindow_SetMinSize, 239).
--define(wxWindow_SetOwnBackgroundColour, 240).
--define(wxWindow_SetOwnFont, 241).
--define(wxWindow_SetOwnForegroundColour, 242).
--define(wxWindow_SetDropTarget, 243).
--define(wxWindow_SetExtraStyle, 244).
--define(wxWindow_SetFocus, 245).
--define(wxWindow_SetFocusFromKbd, 246).
--define(wxWindow_SetFont, 247).
--define(wxWindow_SetForegroundColour, 248).
--define(wxWindow_SetHelpText, 249).
--define(wxWindow_SetId, 250).
--define(wxWindow_SetLabel, 252).
--define(wxWindow_SetName, 253).
--define(wxWindow_SetPalette, 254).
--define(wxWindow_SetScrollbar, 255).
--define(wxWindow_SetScrollPos, 256).
--define(wxWindow_SetSize_5, 257).
--define(wxWindow_SetSize_2_0, 258).
--define(wxWindow_SetSize_1, 259).
--define(wxWindow_SetSize_2_1, 260).
--define(wxWindow_SetSizeHints_3, 261).
--define(wxWindow_SetSizeHints_2, 262).
--define(wxWindow_SetSizer, 263).
--define(wxWindow_SetSizerAndFit, 264).
--define(wxWindow_SetThemeEnabled, 265).
--define(wxWindow_SetToolTip_1_0, 266).
--define(wxWindow_SetToolTip_1_1, 267).
--define(wxWindow_SetVirtualSize_1, 268).
--define(wxWindow_SetVirtualSize_2, 269).
--define(wxWindow_SetVirtualSizeHints_3, 270).
--define(wxWindow_SetVirtualSizeHints_2, 271).
--define(wxWindow_SetWindowStyle, 272).
--define(wxWindow_SetWindowStyleFlag, 273).
--define(wxWindow_SetWindowVariant, 274).
--define(wxWindow_ShouldInheritColours, 275).
--define(wxWindow_Show, 276).
--define(wxWindow_Thaw, 277).
--define(wxWindow_TransferDataFromWindow, 278).
--define(wxWindow_TransferDataToWindow, 279).
--define(wxWindow_Update, 280).
--define(wxWindow_UpdateWindowUI, 281).
--define(wxWindow_Validate, 282).
--define(wxWindow_WarpPointer, 283).
--define(wxWindow_SetTransparent, 284).
--define(wxWindow_CanSetTransparent, 285).
--define(wxWindow_IsDoubleBuffered, 286).
--define(wxWindow_SetDoubleBuffered, 287).
--define(wxTopLevelWindow_GetIcon, 288).
--define(wxTopLevelWindow_GetIcons, 289).
--define(wxTopLevelWindow_GetTitle, 290).
--define(wxTopLevelWindow_IsActive, 291).
--define(wxTopLevelWindow_Iconize, 292).
--define(wxTopLevelWindow_IsFullScreen, 293).
--define(wxTopLevelWindow_IsIconized, 294).
--define(wxTopLevelWindow_IsMaximized, 295).
--define(wxTopLevelWindow_Maximize, 296).
--define(wxTopLevelWindow_RequestUserAttention, 297).
--define(wxTopLevelWindow_SetIcon, 298).
--define(wxTopLevelWindow_SetIcons, 299).
--define(wxTopLevelWindow_CenterOnScreen, 300).
--define(wxTopLevelWindow_CentreOnScreen, 301).
--define(wxTopLevelWindow_SetShape, 303).
--define(wxTopLevelWindow_SetTitle, 304).
--define(wxTopLevelWindow_ShowFullScreen, 305).
--define(wxFrame_new_4, 307).
--define(wxFrame_new_0, 308).
--define(wxFrame_destruct, 310).
--define(wxFrame_Create, 311).
--define(wxFrame_CreateStatusBar, 312).
--define(wxFrame_CreateToolBar, 313).
--define(wxFrame_GetClientAreaOrigin, 314).
--define(wxFrame_GetMenuBar, 315).
--define(wxFrame_GetStatusBar, 316).
--define(wxFrame_GetStatusBarPane, 317).
--define(wxFrame_GetToolBar, 318).
--define(wxFrame_ProcessCommand, 319).
--define(wxFrame_SendSizeEvent, 320).
--define(wxFrame_SetMenuBar, 321).
--define(wxFrame_SetStatusBar, 322).
--define(wxFrame_SetStatusBarPane, 323).
--define(wxFrame_SetStatusText, 324).
--define(wxFrame_SetStatusWidths, 325).
--define(wxFrame_SetToolBar, 326).
--define(wxMiniFrame_new_0, 327).
--define(wxMiniFrame_new_4, 328).
--define(wxMiniFrame_Create, 329).
--define(wxMiniFrame_destroy, 330).
--define(wxSplashScreen_new_0, 331).
--define(wxSplashScreen_new_6, 332).
--define(wxSplashScreen_destruct, 333).
--define(wxSplashScreen_GetSplashStyle, 334).
--define(wxSplashScreen_GetTimeout, 335).
--define(wxPanel_new_0, 336).
--define(wxPanel_new_6, 337).
--define(wxPanel_new_2, 338).
--define(wxPanel_destruct, 339).
--define(wxPanel_InitDialog, 340).
--define(wxPanel_SetFocusIgnoringChildren, 341).
--define(wxScrolledWindow_new_0, 342).
--define(wxScrolledWindow_new_2, 343).
--define(wxScrolledWindow_destruct, 344).
--define(wxScrolledWindow_CalcScrolledPosition_4, 345).
--define(wxScrolledWindow_CalcScrolledPosition_1, 346).
--define(wxScrolledWindow_CalcUnscrolledPosition_4, 347).
--define(wxScrolledWindow_CalcUnscrolledPosition_1, 348).
--define(wxScrolledWindow_EnableScrolling, 349).
--define(wxScrolledWindow_GetScrollPixelsPerUnit, 350).
--define(wxScrolledWindow_GetViewStart, 351).
--define(wxScrolledWindow_DoPrepareDC, 352).
--define(wxScrolledWindow_PrepareDC, 353).
--define(wxScrolledWindow_Scroll, 354).
--define(wxScrolledWindow_SetScrollbars, 355).
--define(wxScrolledWindow_SetScrollRate, 356).
--define(wxScrolledWindow_SetTargetWindow, 357).
--define(wxSashWindow_new_0, 358).
--define(wxSashWindow_new_2, 359).
--define(wxSashWindow_destruct, 360).
--define(wxSashWindow_GetSashVisible, 361).
--define(wxSashWindow_GetMaximumSizeX, 362).
--define(wxSashWindow_GetMaximumSizeY, 363).
--define(wxSashWindow_GetMinimumSizeX, 364).
--define(wxSashWindow_GetMinimumSizeY, 365).
--define(wxSashWindow_SetMaximumSizeX, 366).
--define(wxSashWindow_SetMaximumSizeY, 367).
--define(wxSashWindow_SetMinimumSizeX, 368).
--define(wxSashWindow_SetMinimumSizeY, 369).
--define(wxSashWindow_SetSashVisible, 370).
--define(wxSashLayoutWindow_new_0, 371).
--define(wxSashLayoutWindow_new_2, 372).
--define(wxSashLayoutWindow_Create, 373).
--define(wxSashLayoutWindow_GetAlignment, 374).
--define(wxSashLayoutWindow_GetOrientation, 375).
--define(wxSashLayoutWindow_SetAlignment, 376).
--define(wxSashLayoutWindow_SetDefaultSize, 377).
--define(wxSashLayoutWindow_SetOrientation, 378).
--define(wxSashLayoutWindow_destroy, 379).
--define(wxGrid_new_0, 380).
--define(wxGrid_new_3, 381).
--define(wxGrid_new_4, 382).
--define(wxGrid_destruct, 383).
--define(wxGrid_AppendCols, 384).
--define(wxGrid_AppendRows, 385).
--define(wxGrid_AutoSize, 386).
--define(wxGrid_AutoSizeColumn, 387).
--define(wxGrid_AutoSizeColumns, 388).
--define(wxGrid_AutoSizeRow, 389).
--define(wxGrid_AutoSizeRows, 390).
--define(wxGrid_BeginBatch, 391).
--define(wxGrid_BlockToDeviceRect, 392).
--define(wxGrid_CanDragColSize, 393).
--define(wxGrid_CanDragRowSize, 394).
--define(wxGrid_CanDragGridSize, 395).
--define(wxGrid_CanEnableCellControl, 396).
--define(wxGrid_CellToRect_2, 397).
--define(wxGrid_CellToRect_1, 398).
--define(wxGrid_ClearGrid, 399).
--define(wxGrid_ClearSelection, 400).
--define(wxGrid_CreateGrid, 401).
--define(wxGrid_DeleteCols, 402).
--define(wxGrid_DeleteRows, 403).
--define(wxGrid_DisableCellEditControl, 404).
--define(wxGrid_DisableDragColSize, 405).
--define(wxGrid_DisableDragGridSize, 406).
--define(wxGrid_DisableDragRowSize, 407).
--define(wxGrid_EnableCellEditControl, 408).
--define(wxGrid_EnableDragColSize, 409).
--define(wxGrid_EnableDragGridSize, 410).
--define(wxGrid_EnableDragRowSize, 411).
--define(wxGrid_EnableEditing, 412).
--define(wxGrid_EnableGridLines, 413).
--define(wxGrid_EndBatch, 414).
--define(wxGrid_Fit, 415).
--define(wxGrid_ForceRefresh, 416).
--define(wxGrid_GetBatchCount, 417).
--define(wxGrid_GetCellAlignment, 418).
--define(wxGrid_GetCellBackgroundColour, 419).
--define(wxGrid_GetCellEditor, 420).
--define(wxGrid_GetCellFont, 421).
--define(wxGrid_GetCellRenderer, 422).
--define(wxGrid_GetCellTextColour, 423).
--define(wxGrid_GetCellValue_2, 424).
--define(wxGrid_GetCellValue_1, 425).
--define(wxGrid_GetColLabelAlignment, 426).
--define(wxGrid_GetColLabelSize, 427).
--define(wxGrid_GetColLabelValue, 428).
--define(wxGrid_GetColMinimalAcceptableWidth, 429).
--define(wxGrid_GetDefaultCellAlignment, 430).
--define(wxGrid_GetDefaultCellBackgroundColour, 431).
--define(wxGrid_GetDefaultCellFont, 432).
--define(wxGrid_GetDefaultCellTextColour, 433).
--define(wxGrid_GetDefaultColLabelSize, 434).
--define(wxGrid_GetDefaultColSize, 435).
--define(wxGrid_GetDefaultEditor, 436).
--define(wxGrid_GetDefaultEditorForCell_2, 437).
--define(wxGrid_GetDefaultEditorForCell_1, 438).
--define(wxGrid_GetDefaultEditorForType, 439).
--define(wxGrid_GetDefaultRenderer, 440).
--define(wxGrid_GetDefaultRendererForCell, 441).
--define(wxGrid_GetDefaultRendererForType, 442).
--define(wxGrid_GetDefaultRowLabelSize, 443).
--define(wxGrid_GetDefaultRowSize, 444).
--define(wxGrid_GetGridCursorCol, 445).
--define(wxGrid_GetGridCursorRow, 446).
--define(wxGrid_GetGridLineColour, 447).
--define(wxGrid_GridLinesEnabled, 448).
--define(wxGrid_GetLabelBackgroundColour, 449).
--define(wxGrid_GetLabelFont, 450).
--define(wxGrid_GetLabelTextColour, 451).
--define(wxGrid_GetNumberCols, 452).
--define(wxGrid_GetNumberRows, 453).
--define(wxGrid_GetOrCreateCellAttr, 454).
--define(wxGrid_GetRowMinimalAcceptableHeight, 455).
--define(wxGrid_GetRowLabelAlignment, 456).
--define(wxGrid_GetRowLabelSize, 457).
--define(wxGrid_GetRowLabelValue, 458).
--define(wxGrid_GetRowSize, 459).
--define(wxGrid_GetScrollLineX, 460).
--define(wxGrid_GetScrollLineY, 461).
--define(wxGrid_GetSelectedCells, 462).
--define(wxGrid_GetSelectedCols, 463).
--define(wxGrid_GetSelectedRows, 464).
--define(wxGrid_GetSelectionBackground, 465).
--define(wxGrid_GetSelectionBlockTopLeft, 466).
--define(wxGrid_GetSelectionBlockBottomRight, 467).
--define(wxGrid_GetSelectionForeground, 468).
--define(wxGrid_GetViewWidth, 469).
--define(wxGrid_GetGridWindow, 470).
--define(wxGrid_GetGridRowLabelWindow, 471).
--define(wxGrid_GetGridColLabelWindow, 472).
--define(wxGrid_GetGridCornerLabelWindow, 473).
--define(wxGrid_HideCellEditControl, 474).
--define(wxGrid_InsertCols, 475).
--define(wxGrid_InsertRows, 476).
--define(wxGrid_IsCellEditControlEnabled, 477).
--define(wxGrid_IsCurrentCellReadOnly, 478).
--define(wxGrid_IsEditable, 479).
--define(wxGrid_IsInSelection_2, 480).
--define(wxGrid_IsInSelection_1, 481).
--define(wxGrid_IsReadOnly, 482).
--define(wxGrid_IsSelection, 483).
--define(wxGrid_IsVisible_3, 484).
--define(wxGrid_IsVisible_2, 485).
--define(wxGrid_MakeCellVisible_2, 486).
--define(wxGrid_MakeCellVisible_1, 487).
--define(wxGrid_MoveCursorDown, 488).
--define(wxGrid_MoveCursorLeft, 489).
--define(wxGrid_MoveCursorRight, 490).
--define(wxGrid_MoveCursorUp, 491).
--define(wxGrid_MoveCursorDownBlock, 492).
--define(wxGrid_MoveCursorLeftBlock, 493).
--define(wxGrid_MoveCursorRightBlock, 494).
--define(wxGrid_MoveCursorUpBlock, 495).
--define(wxGrid_MovePageDown, 496).
--define(wxGrid_MovePageUp, 497).
--define(wxGrid_RegisterDataType, 498).
--define(wxGrid_SaveEditControlValue, 499).
--define(wxGrid_SelectAll, 500).
--define(wxGrid_SelectBlock_5, 501).
--define(wxGrid_SelectBlock_3, 502).
--define(wxGrid_SelectCol, 503).
--define(wxGrid_SelectRow, 504).
--define(wxGrid_SetCellAlignment_4, 505).
--define(wxGrid_SetCellAlignment_3, 506).
--define(wxGrid_SetCellAlignment_1, 507).
--define(wxGrid_SetCellBackgroundColour_3_0, 508).
--define(wxGrid_SetCellBackgroundColour_1, 509).
--define(wxGrid_SetCellBackgroundColour_3_1, 510).
--define(wxGrid_SetCellEditor, 511).
--define(wxGrid_SetCellFont, 512).
--define(wxGrid_SetCellRenderer, 513).
--define(wxGrid_SetCellTextColour_3_0, 514).
--define(wxGrid_SetCellTextColour_3_1, 515).
--define(wxGrid_SetCellTextColour_1, 516).
--define(wxGrid_SetCellValue_3_0, 517).
--define(wxGrid_SetCellValue_2, 518).
--define(wxGrid_SetCellValue_3_1, 519).
--define(wxGrid_SetColAttr, 520).
--define(wxGrid_SetColFormatBool, 521).
--define(wxGrid_SetColFormatNumber, 522).
--define(wxGrid_SetColFormatFloat, 523).
--define(wxGrid_SetColFormatCustom, 524).
--define(wxGrid_SetColLabelAlignment, 525).
--define(wxGrid_SetColLabelSize, 526).
--define(wxGrid_SetColLabelValue, 527).
--define(wxGrid_SetColMinimalWidth, 528).
--define(wxGrid_SetColMinimalAcceptableWidth, 529).
--define(wxGrid_SetColSize, 530).
--define(wxGrid_SetDefaultCellAlignment, 531).
--define(wxGrid_SetDefaultCellBackgroundColour, 532).
--define(wxGrid_SetDefaultCellFont, 533).
--define(wxGrid_SetDefaultCellTextColour, 534).
--define(wxGrid_SetDefaultEditor, 535).
--define(wxGrid_SetDefaultRenderer, 536).
--define(wxGrid_SetDefaultColSize, 537).
--define(wxGrid_SetDefaultRowSize, 538).
--define(wxGrid_SetGridCursor, 539).
--define(wxGrid_SetGridLineColour, 540).
--define(wxGrid_SetLabelBackgroundColour, 541).
--define(wxGrid_SetLabelFont, 542).
--define(wxGrid_SetLabelTextColour, 543).
--define(wxGrid_SetMargins, 544).
--define(wxGrid_SetReadOnly, 545).
--define(wxGrid_SetRowAttr, 546).
--define(wxGrid_SetRowLabelAlignment, 547).
--define(wxGrid_SetRowLabelSize, 548).
--define(wxGrid_SetRowLabelValue, 549).
--define(wxGrid_SetRowMinimalHeight, 550).
--define(wxGrid_SetRowMinimalAcceptableHeight, 551).
--define(wxGrid_SetRowSize, 552).
--define(wxGrid_SetScrollLineX, 553).
--define(wxGrid_SetScrollLineY, 554).
--define(wxGrid_SetSelectionBackground, 555).
--define(wxGrid_SetSelectionForeground, 556).
--define(wxGrid_SetSelectionMode, 557).
--define(wxGrid_ShowCellEditControl, 558).
--define(wxGrid_XToCol, 559).
--define(wxGrid_XToEdgeOfCol, 560).
--define(wxGrid_YToEdgeOfRow, 561).
--define(wxGrid_YToRow, 562).
--define(wxGridCellRenderer_Draw, 563).
--define(wxGridCellRenderer_GetBestSize, 564).
--define(wxGridCellEditor_Create, 565).
--define(wxGridCellEditor_IsCreated, 566).
--define(wxGridCellEditor_SetSize, 567).
--define(wxGridCellEditor_Show, 568).
--define(wxGridCellEditor_PaintBackground, 569).
--define(wxGridCellEditor_BeginEdit, 570).
--define(wxGridCellEditor_EndEdit, 571).
--define(wxGridCellEditor_Reset, 572).
--define(wxGridCellEditor_StartingKey, 573).
--define(wxGridCellEditor_StartingClick, 574).
--define(wxGridCellEditor_HandleReturn, 575).
--define(wxGridCellBoolRenderer_new, 576).
--define(wxGridCellBoolRenderer_destroy, 577).
--define(wxGridCellBoolEditor_new, 578).
--define(wxGridCellBoolEditor_IsTrueValue, 579).
--define(wxGridCellBoolEditor_UseStringValues, 580).
--define(wxGridCellBoolEditor_destroy, 581).
--define(wxGridCellFloatRenderer_new, 582).
--define(wxGridCellFloatRenderer_GetPrecision, 583).
--define(wxGridCellFloatRenderer_GetWidth, 584).
--define(wxGridCellFloatRenderer_SetParameters, 585).
--define(wxGridCellFloatRenderer_SetPrecision, 586).
--define(wxGridCellFloatRenderer_SetWidth, 587).
--define(wxGridCellFloatRenderer_destroy, 588).
--define(wxGridCellFloatEditor_new, 589).
--define(wxGridCellFloatEditor_SetParameters, 590).
--define(wxGridCellFloatEditor_destroy, 591).
--define(wxGridCellStringRenderer_new, 592).
--define(wxGridCellStringRenderer_destroy, 593).
--define(wxGridCellTextEditor_new, 594).
--define(wxGridCellTextEditor_SetParameters, 595).
--define(wxGridCellTextEditor_destroy, 596).
--define(wxGridCellChoiceEditor_new, 598).
--define(wxGridCellChoiceEditor_SetParameters, 599).
--define(wxGridCellChoiceEditor_destroy, 600).
--define(wxGridCellNumberRenderer_new, 601).
--define(wxGridCellNumberRenderer_destroy, 602).
--define(wxGridCellNumberEditor_new, 603).
--define(wxGridCellNumberEditor_GetValue, 604).
--define(wxGridCellNumberEditor_SetParameters, 605).
--define(wxGridCellNumberEditor_destroy, 606).
--define(wxGridCellAttr_SetTextColour, 607).
--define(wxGridCellAttr_SetBackgroundColour, 608).
--define(wxGridCellAttr_SetFont, 609).
--define(wxGridCellAttr_SetAlignment, 610).
--define(wxGridCellAttr_SetReadOnly, 611).
--define(wxGridCellAttr_SetRenderer, 612).
--define(wxGridCellAttr_SetEditor, 613).
--define(wxGridCellAttr_HasTextColour, 614).
--define(wxGridCellAttr_HasBackgroundColour, 615).
--define(wxGridCellAttr_HasFont, 616).
--define(wxGridCellAttr_HasAlignment, 617).
--define(wxGridCellAttr_HasRenderer, 618).
--define(wxGridCellAttr_HasEditor, 619).
--define(wxGridCellAttr_GetTextColour, 620).
--define(wxGridCellAttr_GetBackgroundColour, 621).
--define(wxGridCellAttr_GetFont, 622).
--define(wxGridCellAttr_GetAlignment, 623).
--define(wxGridCellAttr_GetRenderer, 624).
--define(wxGridCellAttr_GetEditor, 625).
--define(wxGridCellAttr_IsReadOnly, 626).
--define(wxGridCellAttr_SetDefAttr, 627).
--define(wxDC_Blit, 628).
--define(wxDC_CalcBoundingBox, 629).
--define(wxDC_Clear, 630).
--define(wxDC_ComputeScaleAndOrigin, 631).
--define(wxDC_CrossHair, 632).
--define(wxDC_DestroyClippingRegion, 633).
--define(wxDC_DeviceToLogicalX, 634).
--define(wxDC_DeviceToLogicalXRel, 635).
--define(wxDC_DeviceToLogicalY, 636).
--define(wxDC_DeviceToLogicalYRel, 637).
--define(wxDC_DrawArc, 638).
--define(wxDC_DrawBitmap, 639).
--define(wxDC_DrawCheckMark, 640).
--define(wxDC_DrawCircle, 641).
--define(wxDC_DrawEllipse_2, 643).
--define(wxDC_DrawEllipse_1, 644).
--define(wxDC_DrawEllipticArc, 645).
--define(wxDC_DrawIcon, 646).
--define(wxDC_DrawLabel, 647).
--define(wxDC_DrawLine, 648).
--define(wxDC_DrawLines, 649).
--define(wxDC_DrawPolygon, 651).
--define(wxDC_DrawPoint, 653).
--define(wxDC_DrawRectangle_2, 655).
--define(wxDC_DrawRectangle_1, 656).
--define(wxDC_DrawRotatedText, 657).
--define(wxDC_DrawRoundedRectangle_3, 659).
--define(wxDC_DrawRoundedRectangle_2, 660).
--define(wxDC_DrawText, 661).
--define(wxDC_EndDoc, 662).
--define(wxDC_EndPage, 663).
--define(wxDC_FloodFill, 664).
--define(wxDC_GetBackground, 665).
--define(wxDC_GetBackgroundMode, 666).
--define(wxDC_GetBrush, 667).
--define(wxDC_GetCharHeight, 668).
--define(wxDC_GetCharWidth, 669).
--define(wxDC_GetClippingBox, 670).
--define(wxDC_GetFont, 672).
--define(wxDC_GetLayoutDirection, 673).
--define(wxDC_GetLogicalFunction, 674).
--define(wxDC_GetMapMode, 675).
--define(wxDC_GetMultiLineTextExtent_4, 676).
--define(wxDC_GetMultiLineTextExtent_1, 677).
--define(wxDC_GetPartialTextExtents, 678).
--define(wxDC_GetPen, 679).
--define(wxDC_GetPixel, 680).
--define(wxDC_GetPPI, 681).
--define(wxDC_GetSize, 683).
--define(wxDC_GetSizeMM, 685).
--define(wxDC_GetTextBackground, 686).
--define(wxDC_GetTextExtent_4, 687).
--define(wxDC_GetTextExtent_1, 688).
--define(wxDC_GetTextForeground, 690).
--define(wxDC_GetUserScale, 691).
--define(wxDC_GradientFillConcentric_3, 692).
--define(wxDC_GradientFillConcentric_4, 693).
--define(wxDC_GradientFillLinear, 694).
--define(wxDC_LogicalToDeviceX, 695).
--define(wxDC_LogicalToDeviceXRel, 696).
--define(wxDC_LogicalToDeviceY, 697).
--define(wxDC_LogicalToDeviceYRel, 698).
--define(wxDC_MaxX, 699).
--define(wxDC_MaxY, 700).
--define(wxDC_MinX, 701).
--define(wxDC_MinY, 702).
--define(wxDC_IsOk, 703).
--define(wxDC_ResetBoundingBox, 704).
--define(wxDC_SetAxisOrientation, 705).
--define(wxDC_SetBackground, 706).
--define(wxDC_SetBackgroundMode, 707).
--define(wxDC_SetBrush, 708).
--define(wxDC_SetClippingRegion_2, 710).
--define(wxDC_SetClippingRegion_1_1, 711).
--define(wxDC_SetClippingRegion_1_0, 712).
--define(wxDC_SetDeviceOrigin, 713).
--define(wxDC_SetFont, 714).
--define(wxDC_SetLayoutDirection, 715).
--define(wxDC_SetLogicalFunction, 716).
--define(wxDC_SetMapMode, 717).
--define(wxDC_SetPalette, 718).
--define(wxDC_SetPen, 719).
--define(wxDC_SetTextBackground, 720).
--define(wxDC_SetTextForeground, 721).
--define(wxDC_SetUserScale, 722).
--define(wxDC_StartDoc, 723).
--define(wxDC_StartPage, 724).
--define(wxMirrorDC_new, 725).
--define(wxMirrorDC_destroy, 726).
--define(wxScreenDC_new, 727).
--define(wxScreenDC_destruct, 728).
--define(wxPostScriptDC_new_0, 729).
--define(wxPostScriptDC_new_1, 730).
--define(wxPostScriptDC_destruct, 731).
--define(wxPostScriptDC_SetResolution, 732).
--define(wxPostScriptDC_GetResolution, 733).
--define(wxWindowDC_new_0, 734).
--define(wxWindowDC_new_1, 735).
--define(wxWindowDC_destruct, 736).
--define(wxClientDC_new_0, 737).
--define(wxClientDC_new_1, 738).
--define(wxClientDC_destroy, 739).
--define(wxPaintDC_new_0, 740).
--define(wxPaintDC_new_1, 741).
--define(wxPaintDC_destroy, 742).
--define(wxMemoryDC_new_1_0, 744).
--define(wxMemoryDC_new_1_1, 745).
--define(wxMemoryDC_new_0, 746).
--define(wxMemoryDC_destruct, 748).
--define(wxMemoryDC_SelectObject, 749).
--define(wxMemoryDC_SelectObjectAsSource, 750).
--define(wxBufferedDC_new_0, 751).
--define(wxBufferedDC_new_2, 752).
--define(wxBufferedDC_new_3, 753).
--define(wxBufferedDC_destruct, 754).
--define(wxBufferedDC_Init_2, 755).
--define(wxBufferedDC_Init_3, 756).
--define(wxBufferedPaintDC_new_3, 757).
--define(wxBufferedPaintDC_new_2, 758).
--define(wxBufferedPaintDC_destruct, 759).
--define(wxGraphicsObject_destruct, 760).
--define(wxGraphicsObject_GetRenderer, 761).
--define(wxGraphicsObject_IsNull, 762).
--define(wxGraphicsContext_destruct, 763).
--define(wxGraphicsContext_Create_1_1, 764).
--define(wxGraphicsContext_Create_1_0, 765).
--define(wxGraphicsContext_Create_0, 766).
--define(wxGraphicsContext_CreatePen, 767).
--define(wxGraphicsContext_CreateBrush, 768).
--define(wxGraphicsContext_CreateRadialGradientBrush, 769).
--define(wxGraphicsContext_CreateLinearGradientBrush, 770).
--define(wxGraphicsContext_CreateFont, 771).
--define(wxGraphicsContext_CreateMatrix, 772).
--define(wxGraphicsContext_CreatePath, 773).
--define(wxGraphicsContext_Clip_1, 774).
--define(wxGraphicsContext_Clip_4, 775).
--define(wxGraphicsContext_ResetClip, 776).
--define(wxGraphicsContext_DrawBitmap, 777).
--define(wxGraphicsContext_DrawEllipse, 778).
--define(wxGraphicsContext_DrawIcon, 779).
--define(wxGraphicsContext_DrawLines, 780).
--define(wxGraphicsContext_DrawPath, 781).
--define(wxGraphicsContext_DrawRectangle, 782).
--define(wxGraphicsContext_DrawRoundedRectangle, 783).
--define(wxGraphicsContext_DrawText_3, 784).
--define(wxGraphicsContext_DrawText_4_0, 785).
--define(wxGraphicsContext_DrawText_4_1, 786).
--define(wxGraphicsContext_DrawText_5, 787).
--define(wxGraphicsContext_FillPath, 788).
--define(wxGraphicsContext_StrokePath, 789).
--define(wxGraphicsContext_GetPartialTextExtents, 790).
--define(wxGraphicsContext_GetTextExtent, 791).
--define(wxGraphicsContext_Rotate, 792).
--define(wxGraphicsContext_Scale, 793).
--define(wxGraphicsContext_Translate, 794).
--define(wxGraphicsContext_GetTransform, 795).
--define(wxGraphicsContext_SetTransform, 796).
--define(wxGraphicsContext_ConcatTransform, 797).
--define(wxGraphicsContext_SetBrush_1_1, 798).
--define(wxGraphicsContext_SetBrush_1_0, 799).
--define(wxGraphicsContext_SetFont_1, 800).
--define(wxGraphicsContext_SetFont_2, 801).
--define(wxGraphicsContext_SetPen_1_0, 802).
--define(wxGraphicsContext_SetPen_1_1, 803).
--define(wxGraphicsContext_StrokeLine, 804).
--define(wxGraphicsContext_StrokeLines, 805).
--define(wxGraphicsMatrix_Concat, 807).
--define(wxGraphicsMatrix_Get, 809).
--define(wxGraphicsMatrix_Invert, 810).
--define(wxGraphicsMatrix_IsEqual, 811).
--define(wxGraphicsMatrix_IsIdentity, 813).
--define(wxGraphicsMatrix_Rotate, 814).
--define(wxGraphicsMatrix_Scale, 815).
--define(wxGraphicsMatrix_Translate, 816).
--define(wxGraphicsMatrix_Set, 817).
--define(wxGraphicsMatrix_TransformPoint, 818).
--define(wxGraphicsMatrix_TransformDistance, 819).
--define(wxGraphicsPath_MoveToPoint_2, 820).
--define(wxGraphicsPath_MoveToPoint_1, 821).
--define(wxGraphicsPath_AddArc_6, 822).
--define(wxGraphicsPath_AddArc_5, 823).
--define(wxGraphicsPath_AddArcToPoint, 824).
--define(wxGraphicsPath_AddCircle, 825).
--define(wxGraphicsPath_AddCurveToPoint_6, 826).
--define(wxGraphicsPath_AddCurveToPoint_3, 827).
--define(wxGraphicsPath_AddEllipse, 828).
--define(wxGraphicsPath_AddLineToPoint_2, 829).
--define(wxGraphicsPath_AddLineToPoint_1, 830).
--define(wxGraphicsPath_AddPath, 831).
--define(wxGraphicsPath_AddQuadCurveToPoint, 832).
--define(wxGraphicsPath_AddRectangle, 833).
--define(wxGraphicsPath_AddRoundedRectangle, 834).
--define(wxGraphicsPath_CloseSubpath, 835).
--define(wxGraphicsPath_Contains_3, 836).
--define(wxGraphicsPath_Contains_2, 837).
--define(wxGraphicsPath_GetBox, 839).
--define(wxGraphicsPath_GetCurrentPoint, 841).
--define(wxGraphicsPath_Transform, 842).
--define(wxGraphicsRenderer_GetDefaultRenderer, 843).
--define(wxGraphicsRenderer_CreateContext_1_1, 844).
--define(wxGraphicsRenderer_CreateContext_1_0, 845).
--define(wxGraphicsRenderer_CreatePen, 846).
--define(wxGraphicsRenderer_CreateBrush, 847).
--define(wxGraphicsRenderer_CreateLinearGradientBrush, 848).
--define(wxGraphicsRenderer_CreateRadialGradientBrush, 849).
--define(wxGraphicsRenderer_CreateFont, 850).
--define(wxGraphicsRenderer_CreateMatrix, 851).
--define(wxGraphicsRenderer_CreatePath, 852).
--define(wxMenuBar_new_1, 854).
--define(wxMenuBar_new_0, 856).
--define(wxMenuBar_destruct, 858).
--define(wxMenuBar_Append, 859).
--define(wxMenuBar_Check, 860).
--define(wxMenuBar_Enable_2, 861).
--define(wxMenuBar_Enable_1, 862).
--define(wxMenuBar_EnableTop, 863).
--define(wxMenuBar_FindMenu, 864).
--define(wxMenuBar_FindMenuItem, 865).
--define(wxMenuBar_FindItem, 866).
--define(wxMenuBar_GetHelpString, 867).
--define(wxMenuBar_GetLabel_1, 868).
--define(wxMenuBar_GetLabel_0, 869).
--define(wxMenuBar_GetLabelTop, 870).
--define(wxMenuBar_GetMenu, 871).
--define(wxMenuBar_GetMenuCount, 872).
--define(wxMenuBar_Insert, 873).
--define(wxMenuBar_IsChecked, 874).
--define(wxMenuBar_IsEnabled_1, 875).
--define(wxMenuBar_IsEnabled_0, 876).
--define(wxMenuBar_Remove, 877).
--define(wxMenuBar_Replace, 878).
--define(wxMenuBar_SetHelpString, 879).
--define(wxMenuBar_SetLabel_2, 880).
--define(wxMenuBar_SetLabel_1, 881).
--define(wxMenuBar_SetLabelTop, 882).
--define(wxControl_GetLabel, 883).
--define(wxControl_SetLabel, 884).
--define(wxControlWithItems_Append_1, 885).
--define(wxControlWithItems_Append_2, 886).
--define(wxControlWithItems_appendStrings_1, 887).
--define(wxControlWithItems_Clear, 888).
--define(wxControlWithItems_Delete, 889).
--define(wxControlWithItems_FindString, 890).
--define(wxControlWithItems_getClientData, 891).
--define(wxControlWithItems_setClientData, 892).
--define(wxControlWithItems_GetCount, 893).
--define(wxControlWithItems_GetSelection, 894).
--define(wxControlWithItems_GetString, 895).
--define(wxControlWithItems_GetStringSelection, 896).
--define(wxControlWithItems_Insert_2, 897).
--define(wxControlWithItems_Insert_3, 898).
--define(wxControlWithItems_IsEmpty, 899).
--define(wxControlWithItems_Select, 900).
--define(wxControlWithItems_SetSelection, 901).
--define(wxControlWithItems_SetString, 902).
--define(wxControlWithItems_SetStringSelection, 903).
--define(wxMenu_new_2, 906).
--define(wxMenu_new_1, 907).
--define(wxMenu_destruct, 909).
--define(wxMenu_Append_3, 910).
--define(wxMenu_Append_1, 911).
--define(wxMenu_Append_4_0, 912).
--define(wxMenu_Append_4_1, 913).
--define(wxMenu_AppendCheckItem, 914).
--define(wxMenu_AppendRadioItem, 915).
--define(wxMenu_AppendSeparator, 916).
--define(wxMenu_Break, 917).
--define(wxMenu_Check, 918).
--define(wxMenu_Delete_1_0, 919).
--define(wxMenu_Delete_1_1, 920).
--define(wxMenu_Destroy_1_0, 921).
--define(wxMenu_Destroy_1_1, 922).
--define(wxMenu_Enable, 923).
--define(wxMenu_FindItem_1, 924).
--define(wxMenu_FindItem_2, 925).
--define(wxMenu_FindItemByPosition, 926).
--define(wxMenu_GetHelpString, 927).
--define(wxMenu_GetLabel, 928).
--define(wxMenu_GetMenuItemCount, 929).
--define(wxMenu_GetMenuItems, 930).
--define(wxMenu_GetTitle, 932).
--define(wxMenu_Insert_2, 933).
--define(wxMenu_Insert_3, 934).
--define(wxMenu_Insert_5_1, 935).
--define(wxMenu_Insert_5_0, 936).
--define(wxMenu_InsertCheckItem, 937).
--define(wxMenu_InsertRadioItem, 938).
--define(wxMenu_InsertSeparator, 939).
--define(wxMenu_IsChecked, 940).
--define(wxMenu_IsEnabled, 941).
--define(wxMenu_Prepend_1, 942).
--define(wxMenu_Prepend_2, 943).
--define(wxMenu_Prepend_4_1, 944).
--define(wxMenu_Prepend_4_0, 945).
--define(wxMenu_PrependCheckItem, 946).
--define(wxMenu_PrependRadioItem, 947).
--define(wxMenu_PrependSeparator, 948).
--define(wxMenu_Remove_1_0, 949).
--define(wxMenu_Remove_1_1, 950).
--define(wxMenu_SetHelpString, 951).
--define(wxMenu_SetLabel, 952).
--define(wxMenu_SetTitle, 953).
--define(wxMenuItem_new, 954).
--define(wxMenuItem_destruct, 956).
--define(wxMenuItem_Check, 957).
--define(wxMenuItem_Enable, 958).
--define(wxMenuItem_GetBitmap, 959).
--define(wxMenuItem_GetHelp, 960).
--define(wxMenuItem_GetId, 961).
--define(wxMenuItem_GetKind, 962).
--define(wxMenuItem_GetLabel, 963).
--define(wxMenuItem_GetLabelFromText, 964).
--define(wxMenuItem_GetMenu, 965).
--define(wxMenuItem_GetText, 966).
--define(wxMenuItem_GetSubMenu, 967).
--define(wxMenuItem_IsCheckable, 968).
--define(wxMenuItem_IsChecked, 969).
--define(wxMenuItem_IsEnabled, 970).
--define(wxMenuItem_IsSeparator, 971).
--define(wxMenuItem_IsSubMenu, 972).
--define(wxMenuItem_SetBitmap, 973).
--define(wxMenuItem_SetHelp, 974).
--define(wxMenuItem_SetMenu, 975).
--define(wxMenuItem_SetSubMenu, 976).
--define(wxMenuItem_SetText, 977).
--define(wxToolBar_AddControl, 978).
--define(wxToolBar_AddSeparator, 979).
--define(wxToolBar_AddTool_5, 980).
--define(wxToolBar_AddTool_4_0, 981).
--define(wxToolBar_AddTool_1, 982).
--define(wxToolBar_AddTool_4_1, 983).
--define(wxToolBar_AddTool_3, 984).
--define(wxToolBar_AddTool_6, 985).
--define(wxToolBar_AddCheckTool, 986).
--define(wxToolBar_AddRadioTool, 987).
--define(wxToolBar_AddStretchableSpace, 988).
--define(wxToolBar_InsertStretchableSpace, 989).
--define(wxToolBar_DeleteTool, 990).
--define(wxToolBar_DeleteToolByPos, 991).
--define(wxToolBar_EnableTool, 992).
--define(wxToolBar_FindById, 993).
--define(wxToolBar_FindControl, 994).
--define(wxToolBar_FindToolForPosition, 995).
--define(wxToolBar_GetToolSize, 996).
--define(wxToolBar_GetToolBitmapSize, 997).
--define(wxToolBar_GetMargins, 998).
--define(wxToolBar_GetToolEnabled, 999).
--define(wxToolBar_GetToolLongHelp, 1000).
--define(wxToolBar_GetToolPacking, 1001).
--define(wxToolBar_GetToolPos, 1002).
--define(wxToolBar_GetToolSeparation, 1003).
--define(wxToolBar_GetToolShortHelp, 1004).
--define(wxToolBar_GetToolState, 1005).
--define(wxToolBar_InsertControl, 1006).
--define(wxToolBar_InsertSeparator, 1007).
--define(wxToolBar_InsertTool_5, 1008).
--define(wxToolBar_InsertTool_2, 1009).
--define(wxToolBar_InsertTool_4, 1010).
--define(wxToolBar_Realize, 1011).
--define(wxToolBar_RemoveTool, 1012).
--define(wxToolBar_SetMargins, 1013).
--define(wxToolBar_SetToolBitmapSize, 1014).
--define(wxToolBar_SetToolLongHelp, 1015).
--define(wxToolBar_SetToolPacking, 1016).
--define(wxToolBar_SetToolShortHelp, 1017).
--define(wxToolBar_SetToolSeparation, 1018).
--define(wxToolBar_ToggleTool, 1019).
--define(wxStatusBar_new_0, 1021).
--define(wxStatusBar_new_2, 1022).
--define(wxStatusBar_destruct, 1024).
--define(wxStatusBar_Create, 1025).
--define(wxStatusBar_GetFieldRect, 1026).
--define(wxStatusBar_GetFieldsCount, 1027).
--define(wxStatusBar_GetStatusText, 1028).
--define(wxStatusBar_PopStatusText, 1029).
--define(wxStatusBar_PushStatusText, 1030).
--define(wxStatusBar_SetFieldsCount, 1031).
--define(wxStatusBar_SetMinHeight, 1032).
--define(wxStatusBar_SetStatusText, 1033).
--define(wxStatusBar_SetStatusWidths, 1034).
--define(wxStatusBar_SetStatusStyles, 1035).
--define(wxBitmap_new_0, 1036).
--define(wxBitmap_new_3, 1037).
--define(wxBitmap_new_4, 1038).
--define(wxBitmap_new_2_0, 1039).
--define(wxBitmap_new_2_1, 1040).
--define(wxBitmap_destruct, 1041).
--define(wxBitmap_ConvertToImage, 1042).
--define(wxBitmap_CopyFromIcon, 1043).
--define(wxBitmap_Create, 1044).
--define(wxBitmap_GetDepth, 1045).
--define(wxBitmap_GetHeight, 1046).
--define(wxBitmap_GetPalette, 1047).
--define(wxBitmap_GetMask, 1048).
--define(wxBitmap_GetWidth, 1049).
--define(wxBitmap_GetSubBitmap, 1050).
--define(wxBitmap_LoadFile, 1051).
--define(wxBitmap_Ok, 1052).
--define(wxBitmap_SaveFile, 1053).
--define(wxBitmap_SetDepth, 1054).
--define(wxBitmap_SetHeight, 1055).
--define(wxBitmap_SetMask, 1056).
--define(wxBitmap_SetPalette, 1057).
--define(wxBitmap_SetWidth, 1058).
--define(wxIcon_new_0, 1059).
--define(wxIcon_new_2, 1060).
--define(wxIcon_new_1, 1061).
--define(wxIcon_CopyFromBitmap, 1062).
--define(wxIcon_destroy, 1063).
--define(wxIconBundle_new_0, 1064).
--define(wxIconBundle_new_2, 1065).
--define(wxIconBundle_new_1_0, 1066).
--define(wxIconBundle_new_1_1, 1067).
--define(wxIconBundle_destruct, 1068).
--define(wxIconBundle_AddIcon_2, 1069).
--define(wxIconBundle_AddIcon_1, 1070).
--define(wxIconBundle_GetIcon_1_1, 1071).
--define(wxIconBundle_GetIcon_1_0, 1072).
--define(wxCursor_new_0, 1073).
--define(wxCursor_new_1_0, 1074).
--define(wxCursor_new_1_1, 1075).
--define(wxCursor_new_4, 1076).
--define(wxCursor_destruct, 1077).
--define(wxCursor_Ok, 1078).
--define(wxMask_new_0, 1079).
--define(wxMask_new_2_1, 1080).
--define(wxMask_new_2_0, 1081).
--define(wxMask_new_1, 1082).
--define(wxMask_destruct, 1083).
--define(wxMask_Create_2_1, 1084).
--define(wxMask_Create_2_0, 1085).
--define(wxMask_Create_1, 1086).
--define(wxImage_new_0, 1087).
--define(wxImage_new_3_0, 1088).
--define(wxImage_new_4, 1089).
--define(wxImage_new_5, 1090).
--define(wxImage_new_2, 1091).
--define(wxImage_new_3_1, 1092).
--define(wxImage_Blur, 1093).
--define(wxImage_BlurHorizontal, 1094).
--define(wxImage_BlurVertical, 1095).
--define(wxImage_ConvertAlphaToMask, 1096).
--define(wxImage_ConvertToGreyscale, 1097).
--define(wxImage_ConvertToMono, 1098).
--define(wxImage_Copy, 1099).
--define(wxImage_Create_3, 1100).
--define(wxImage_Create_4, 1101).
--define(wxImage_Create_5, 1102).
--define(wxImage_Destroy, 1103).
--define(wxImage_FindFirstUnusedColour, 1104).
--define(wxImage_GetImageExtWildcard, 1105).
--define(wxImage_GetAlpha_2, 1106).
--define(wxImage_GetAlpha_0, 1107).
--define(wxImage_GetBlue, 1108).
--define(wxImage_GetData, 1109).
--define(wxImage_GetGreen, 1110).
--define(wxImage_GetImageCount, 1111).
--define(wxImage_GetHeight, 1112).
--define(wxImage_GetMaskBlue, 1113).
--define(wxImage_GetMaskGreen, 1114).
--define(wxImage_GetMaskRed, 1115).
--define(wxImage_GetOrFindMaskColour, 1116).
--define(wxImage_GetPalette, 1117).
--define(wxImage_GetRed, 1118).
--define(wxImage_GetSubImage, 1119).
--define(wxImage_GetWidth, 1120).
--define(wxImage_HasAlpha, 1121).
--define(wxImage_HasMask, 1122).
--define(wxImage_GetOption, 1123).
--define(wxImage_GetOptionInt, 1124).
--define(wxImage_HasOption, 1125).
--define(wxImage_InitAlpha, 1126).
--define(wxImage_InitStandardHandlers, 1127).
--define(wxImage_IsTransparent, 1128).
--define(wxImage_LoadFile_2, 1129).
--define(wxImage_LoadFile_3, 1130).
--define(wxImage_Ok, 1131).
--define(wxImage_RemoveHandler, 1132).
--define(wxImage_Mirror, 1133).
--define(wxImage_Replace, 1134).
--define(wxImage_Rescale, 1135).
--define(wxImage_Resize, 1136).
--define(wxImage_Rotate, 1137).
--define(wxImage_RotateHue, 1138).
--define(wxImage_Rotate90, 1139).
--define(wxImage_SaveFile_1, 1140).
--define(wxImage_SaveFile_2_0, 1141).
--define(wxImage_SaveFile_2_1, 1142).
--define(wxImage_Scale, 1143).
--define(wxImage_Size, 1144).
--define(wxImage_SetAlpha_3, 1145).
--define(wxImage_SetAlpha_2, 1146).
--define(wxImage_SetData_2, 1147).
--define(wxImage_SetData_4, 1148).
--define(wxImage_SetMask, 1149).
--define(wxImage_SetMaskColour, 1150).
--define(wxImage_SetMaskFromImage, 1151).
--define(wxImage_SetOption_2_1, 1152).
--define(wxImage_SetOption_2_0, 1153).
--define(wxImage_SetPalette, 1154).
--define(wxImage_SetRGB_5, 1155).
--define(wxImage_SetRGB_4, 1156).
--define(wxImage_destroy, 1157).
--define(wxBrush_new_0, 1158).
--define(wxBrush_new_2, 1159).
--define(wxBrush_new_1, 1160).
--define(wxBrush_destruct, 1162).
--define(wxBrush_GetColour, 1163).
--define(wxBrush_GetStipple, 1164).
--define(wxBrush_GetStyle, 1165).
--define(wxBrush_IsHatch, 1166).
--define(wxBrush_IsOk, 1167).
--define(wxBrush_SetColour_1, 1168).
--define(wxBrush_SetColour_3, 1169).
--define(wxBrush_SetStipple, 1170).
--define(wxBrush_SetStyle, 1171).
--define(wxPen_new_0, 1172).
--define(wxPen_new_2, 1173).
--define(wxPen_destruct, 1174).
--define(wxPen_GetCap, 1175).
--define(wxPen_GetColour, 1176).
--define(wxPen_GetJoin, 1177).
--define(wxPen_GetStyle, 1178).
--define(wxPen_GetWidth, 1179).
--define(wxPen_IsOk, 1180).
--define(wxPen_SetCap, 1181).
--define(wxPen_SetColour_1, 1182).
--define(wxPen_SetColour_3, 1183).
--define(wxPen_SetJoin, 1184).
--define(wxPen_SetStyle, 1185).
--define(wxPen_SetWidth, 1186).
--define(wxRegion_new_0, 1187).
--define(wxRegion_new_4, 1188).
--define(wxRegion_new_2, 1189).
--define(wxRegion_new_1_1, 1190).
--define(wxRegion_new_1_0, 1192).
--define(wxRegion_destruct, 1194).
--define(wxRegion_Clear, 1195).
--define(wxRegion_Contains_2, 1196).
--define(wxRegion_Contains_1_0, 1197).
--define(wxRegion_Contains_4, 1198).
--define(wxRegion_Contains_1_1, 1199).
--define(wxRegion_ConvertToBitmap, 1200).
--define(wxRegion_GetBox, 1201).
--define(wxRegion_Intersect_4, 1202).
--define(wxRegion_Intersect_1_1, 1203).
--define(wxRegion_Intersect_1_0, 1204).
--define(wxRegion_IsEmpty, 1205).
--define(wxRegion_Subtract_4, 1206).
--define(wxRegion_Subtract_1_1, 1207).
--define(wxRegion_Subtract_1_0, 1208).
--define(wxRegion_Offset_2, 1209).
--define(wxRegion_Offset_1, 1210).
--define(wxRegion_Union_4, 1211).
--define(wxRegion_Union_1_2, 1212).
--define(wxRegion_Union_1_1, 1213).
--define(wxRegion_Union_1_0, 1214).
--define(wxRegion_Union_3, 1215).
--define(wxRegion_Xor_4, 1216).
--define(wxRegion_Xor_1_1, 1217).
--define(wxRegion_Xor_1_0, 1218).
--define(wxAcceleratorTable_new_0, 1219).
--define(wxAcceleratorTable_new_2, 1220).
--define(wxAcceleratorTable_destruct, 1221).
--define(wxAcceleratorTable_Ok, 1222).
--define(wxAcceleratorEntry_new_1_0, 1223).
--define(wxAcceleratorEntry_new_1_1, 1224).
--define(wxAcceleratorEntry_GetCommand, 1225).
--define(wxAcceleratorEntry_GetFlags, 1226).
--define(wxAcceleratorEntry_GetKeyCode, 1227).
--define(wxAcceleratorEntry_Set, 1228).
--define(wxAcceleratorEntry_destroy, 1229).
--define(wxCaret_new_3, 1234).
--define(wxCaret_new_2, 1235).
--define(wxCaret_destruct, 1237).
--define(wxCaret_Create_3, 1238).
--define(wxCaret_Create_2, 1239).
--define(wxCaret_GetBlinkTime, 1240).
--define(wxCaret_GetPosition, 1242).
--define(wxCaret_GetSize, 1244).
--define(wxCaret_GetWindow, 1245).
--define(wxCaret_Hide, 1246).
--define(wxCaret_IsOk, 1247).
--define(wxCaret_IsVisible, 1248).
--define(wxCaret_Move_2, 1249).
--define(wxCaret_Move_1, 1250).
--define(wxCaret_SetBlinkTime, 1251).
--define(wxCaret_SetSize_2, 1252).
--define(wxCaret_SetSize_1, 1253).
--define(wxCaret_Show, 1254).
--define(wxSizer_Add_2_1, 1255).
--define(wxSizer_Add_2_0, 1256).
--define(wxSizer_Add_3, 1257).
--define(wxSizer_Add_2_3, 1258).
--define(wxSizer_Add_2_2, 1259).
--define(wxSizer_AddSpacer, 1260).
--define(wxSizer_AddStretchSpacer, 1261).
--define(wxSizer_CalcMin, 1262).
--define(wxSizer_Clear, 1263).
--define(wxSizer_Detach_1_2, 1264).
--define(wxSizer_Detach_1_1, 1265).
--define(wxSizer_Detach_1_0, 1266).
--define(wxSizer_Fit, 1267).
--define(wxSizer_FitInside, 1268).
--define(wxSizer_GetChildren, 1269).
--define(wxSizer_GetItem_2_1, 1270).
--define(wxSizer_GetItem_2_0, 1271).
--define(wxSizer_GetItem_1, 1272).
--define(wxSizer_GetSize, 1273).
--define(wxSizer_GetPosition, 1274).
--define(wxSizer_GetMinSize, 1275).
--define(wxSizer_Hide_2_0, 1276).
--define(wxSizer_Hide_2_1, 1277).
--define(wxSizer_Hide_1, 1278).
--define(wxSizer_Insert_3_1, 1279).
--define(wxSizer_Insert_3_0, 1280).
--define(wxSizer_Insert_4, 1281).
--define(wxSizer_Insert_3_3, 1282).
--define(wxSizer_Insert_3_2, 1283).
--define(wxSizer_Insert_2, 1284).
--define(wxSizer_InsertSpacer, 1285).
--define(wxSizer_InsertStretchSpacer, 1286).
--define(wxSizer_IsShown_1_2, 1287).
--define(wxSizer_IsShown_1_1, 1288).
--define(wxSizer_IsShown_1_0, 1289).
--define(wxSizer_Layout, 1290).
--define(wxSizer_Prepend_2_1, 1291).
--define(wxSizer_Prepend_2_0, 1292).
--define(wxSizer_Prepend_3, 1293).
--define(wxSizer_Prepend_2_3, 1294).
--define(wxSizer_Prepend_2_2, 1295).
--define(wxSizer_Prepend_1, 1296).
--define(wxSizer_PrependSpacer, 1297).
--define(wxSizer_PrependStretchSpacer, 1298).
--define(wxSizer_RecalcSizes, 1299).
--define(wxSizer_Remove_1_1, 1300).
--define(wxSizer_Remove_1_0, 1301).
--define(wxSizer_Replace_3_1, 1302).
--define(wxSizer_Replace_3_0, 1303).
--define(wxSizer_Replace_2, 1304).
--define(wxSizer_SetDimension, 1305).
--define(wxSizer_SetMinSize_2, 1306).
--define(wxSizer_SetMinSize_1, 1307).
--define(wxSizer_SetItemMinSize_3_2, 1308).
--define(wxSizer_SetItemMinSize_2_2, 1309).
--define(wxSizer_SetItemMinSize_3_1, 1310).
--define(wxSizer_SetItemMinSize_2_1, 1311).
--define(wxSizer_SetItemMinSize_3_0, 1312).
--define(wxSizer_SetItemMinSize_2_0, 1313).
--define(wxSizer_SetSizeHints, 1314).
--define(wxSizer_SetVirtualSizeHints, 1315).
--define(wxSizer_Show_2_2, 1316).
--define(wxSizer_Show_2_1, 1317).
--define(wxSizer_Show_2_0, 1318).
--define(wxSizer_Show_1, 1319).
--define(wxSizerFlags_new, 1320).
--define(wxSizerFlags_Align, 1321).
--define(wxSizerFlags_Border_2, 1322).
--define(wxSizerFlags_Border_1, 1323).
--define(wxSizerFlags_Center, 1324).
--define(wxSizerFlags_Centre, 1325).
--define(wxSizerFlags_Expand, 1326).
--define(wxSizerFlags_Left, 1327).
--define(wxSizerFlags_Proportion, 1328).
--define(wxSizerFlags_Right, 1329).
--define(wxSizerFlags_destroy, 1330).
--define(wxSizerItem_new_5_1, 1331).
--define(wxSizerItem_new_2_1, 1332).
--define(wxSizerItem_new_5_0, 1333).
--define(wxSizerItem_new_2_0, 1334).
--define(wxSizerItem_new_6, 1335).
--define(wxSizerItem_new_3, 1336).
--define(wxSizerItem_new_0, 1337).
--define(wxSizerItem_destruct, 1338).
--define(wxSizerItem_CalcMin, 1339).
--define(wxSizerItem_DeleteWindows, 1340).
--define(wxSizerItem_DetachSizer, 1341).
--define(wxSizerItem_GetBorder, 1342).
--define(wxSizerItem_GetFlag, 1343).
--define(wxSizerItem_GetMinSize, 1344).
--define(wxSizerItem_GetPosition, 1345).
--define(wxSizerItem_GetProportion, 1346).
--define(wxSizerItem_GetRatio, 1347).
--define(wxSizerItem_GetRect, 1348).
--define(wxSizerItem_GetSize, 1349).
--define(wxSizerItem_GetSizer, 1350).
--define(wxSizerItem_GetSpacer, 1351).
--define(wxSizerItem_GetUserData, 1352).
--define(wxSizerItem_GetWindow, 1353).
--define(wxSizerItem_IsSizer, 1354).
--define(wxSizerItem_IsShown, 1355).
--define(wxSizerItem_IsSpacer, 1356).
--define(wxSizerItem_IsWindow, 1357).
--define(wxSizerItem_SetBorder, 1358).
--define(wxSizerItem_SetDimension, 1359).
--define(wxSizerItem_SetFlag, 1360).
--define(wxSizerItem_SetInitSize, 1361).
--define(wxSizerItem_SetMinSize_1, 1362).
--define(wxSizerItem_SetMinSize_2, 1363).
--define(wxSizerItem_SetProportion, 1364).
--define(wxSizerItem_SetRatio_2, 1365).
--define(wxSizerItem_SetRatio_1_1, 1366).
--define(wxSizerItem_SetRatio_1_0, 1367).
--define(wxSizerItem_SetSizer, 1368).
--define(wxSizerItem_SetSpacer_1, 1369).
--define(wxSizerItem_SetSpacer_2, 1370).
--define(wxSizerItem_SetWindow, 1371).
--define(wxSizerItem_Show, 1372).
--define(wxBoxSizer_new, 1373).
--define(wxBoxSizer_GetOrientation, 1374).
--define(wxBoxSizer_destroy, 1375).
--define(wxStaticBoxSizer_new_2, 1376).
--define(wxStaticBoxSizer_new_3, 1377).
--define(wxStaticBoxSizer_GetStaticBox, 1378).
--define(wxStaticBoxSizer_destroy, 1379).
--define(wxGridSizer_new_4, 1380).
--define(wxGridSizer_new_2, 1381).
--define(wxGridSizer_GetCols, 1382).
--define(wxGridSizer_GetHGap, 1383).
--define(wxGridSizer_GetRows, 1384).
--define(wxGridSizer_GetVGap, 1385).
--define(wxGridSizer_SetCols, 1386).
--define(wxGridSizer_SetHGap, 1387).
--define(wxGridSizer_SetRows, 1388).
--define(wxGridSizer_SetVGap, 1389).
--define(wxGridSizer_destroy, 1390).
--define(wxFlexGridSizer_new_4, 1391).
--define(wxFlexGridSizer_new_2, 1392).
--define(wxFlexGridSizer_AddGrowableCol, 1393).
--define(wxFlexGridSizer_AddGrowableRow, 1394).
--define(wxFlexGridSizer_GetFlexibleDirection, 1395).
--define(wxFlexGridSizer_GetNonFlexibleGrowMode, 1396).
--define(wxFlexGridSizer_RemoveGrowableCol, 1397).
--define(wxFlexGridSizer_RemoveGrowableRow, 1398).
--define(wxFlexGridSizer_SetFlexibleDirection, 1399).
--define(wxFlexGridSizer_SetNonFlexibleGrowMode, 1400).
--define(wxFlexGridSizer_destroy, 1401).
--define(wxGridBagSizer_new, 1402).
--define(wxGridBagSizer_Add_3_2, 1403).
--define(wxGridBagSizer_Add_3_1, 1404).
--define(wxGridBagSizer_Add_4, 1405).
--define(wxGridBagSizer_Add_1_0, 1406).
--define(wxGridBagSizer_Add_2_1, 1407).
--define(wxGridBagSizer_Add_2_0, 1408).
--define(wxGridBagSizer_Add_3_0, 1409).
--define(wxGridBagSizer_Add_1_1, 1410).
--define(wxGridBagSizer_CalcMin, 1411).
--define(wxGridBagSizer_CheckForIntersection_2, 1412).
--define(wxGridBagSizer_CheckForIntersection_3, 1413).
--define(wxGridBagSizer_FindItem_1_1, 1414).
--define(wxGridBagSizer_FindItem_1_0, 1415).
--define(wxGridBagSizer_FindItemAtPoint, 1416).
--define(wxGridBagSizer_FindItemAtPosition, 1417).
--define(wxGridBagSizer_FindItemWithData, 1418).
--define(wxGridBagSizer_GetCellSize, 1419).
--define(wxGridBagSizer_GetEmptyCellSize, 1420).
--define(wxGridBagSizer_GetItemPosition_1_2, 1421).
--define(wxGridBagSizer_GetItemPosition_1_1, 1422).
--define(wxGridBagSizer_GetItemPosition_1_0, 1423).
--define(wxGridBagSizer_GetItemSpan_1_2, 1424).
--define(wxGridBagSizer_GetItemSpan_1_1, 1425).
--define(wxGridBagSizer_GetItemSpan_1_0, 1426).
--define(wxGridBagSizer_SetEmptyCellSize, 1427).
--define(wxGridBagSizer_SetItemPosition_2_2, 1428).
--define(wxGridBagSizer_SetItemPosition_2_1, 1429).
--define(wxGridBagSizer_SetItemPosition_2_0, 1430).
--define(wxGridBagSizer_SetItemSpan_2_2, 1431).
--define(wxGridBagSizer_SetItemSpan_2_1, 1432).
--define(wxGridBagSizer_SetItemSpan_2_0, 1433).
--define(wxGridBagSizer_destroy, 1434).
--define(wxStdDialogButtonSizer_new, 1435).
--define(wxStdDialogButtonSizer_AddButton, 1436).
--define(wxStdDialogButtonSizer_Realize, 1437).
--define(wxStdDialogButtonSizer_SetAffirmativeButton, 1438).
--define(wxStdDialogButtonSizer_SetCancelButton, 1439).
--define(wxStdDialogButtonSizer_SetNegativeButton, 1440).
--define(wxStdDialogButtonSizer_destroy, 1441).
--define(wxFont_new_0, 1442).
--define(wxFont_new_1, 1443).
--define(wxFont_new_5, 1444).
--define(wxFont_destruct, 1446).
--define(wxFont_IsFixedWidth, 1447).
--define(wxFont_GetDefaultEncoding, 1448).
--define(wxFont_GetFaceName, 1449).
--define(wxFont_GetFamily, 1450).
--define(wxFont_GetNativeFontInfoDesc, 1451).
--define(wxFont_GetNativeFontInfoUserDesc, 1452).
--define(wxFont_GetPointSize, 1453).
--define(wxFont_GetStyle, 1454).
--define(wxFont_GetUnderlined, 1455).
--define(wxFont_GetWeight, 1456).
--define(wxFont_Ok, 1457).
--define(wxFont_SetDefaultEncoding, 1458).
--define(wxFont_SetFaceName, 1459).
--define(wxFont_SetFamily, 1460).
--define(wxFont_SetPointSize, 1461).
--define(wxFont_SetStyle, 1462).
--define(wxFont_SetUnderlined, 1463).
--define(wxFont_SetWeight, 1464).
--define(wxToolTip_Enable, 1465).
--define(wxToolTip_SetDelay, 1466).
--define(wxToolTip_new, 1467).
--define(wxToolTip_SetTip, 1468).
--define(wxToolTip_GetTip, 1469).
--define(wxToolTip_GetWindow, 1470).
--define(wxToolTip_destroy, 1471).
--define(wxButton_new_3, 1473).
--define(wxButton_new_0, 1474).
--define(wxButton_destruct, 1475).
--define(wxButton_Create, 1476).
--define(wxButton_GetDefaultSize, 1477).
--define(wxButton_SetDefault, 1478).
--define(wxButton_SetLabel, 1479).
--define(wxBitmapButton_new_4, 1481).
--define(wxBitmapButton_new_0, 1482).
--define(wxBitmapButton_Create, 1483).
--define(wxBitmapButton_GetBitmapDisabled, 1484).
--define(wxBitmapButton_GetBitmapFocus, 1486).
--define(wxBitmapButton_GetBitmapLabel, 1488).
--define(wxBitmapButton_GetBitmapSelected, 1490).
--define(wxBitmapButton_SetBitmapDisabled, 1492).
--define(wxBitmapButton_SetBitmapFocus, 1493).
--define(wxBitmapButton_SetBitmapLabel, 1494).
--define(wxBitmapButton_SetBitmapSelected, 1495).
--define(wxBitmapButton_destroy, 1496).
--define(wxToggleButton_new_0, 1497).
--define(wxToggleButton_new_4, 1498).
--define(wxToggleButton_Create, 1499).
--define(wxToggleButton_GetValue, 1500).
--define(wxToggleButton_SetValue, 1501).
--define(wxToggleButton_destroy, 1502).
--define(wxCalendarCtrl_new_0, 1503).
--define(wxCalendarCtrl_new_3, 1504).
--define(wxCalendarCtrl_Create, 1505).
--define(wxCalendarCtrl_destruct, 1506).
--define(wxCalendarCtrl_SetDate, 1507).
--define(wxCalendarCtrl_GetDate, 1508).
--define(wxCalendarCtrl_EnableYearChange, 1509).
--define(wxCalendarCtrl_EnableMonthChange, 1510).
--define(wxCalendarCtrl_EnableHolidayDisplay, 1511).
--define(wxCalendarCtrl_SetHeaderColours, 1512).
--define(wxCalendarCtrl_GetHeaderColourFg, 1513).
--define(wxCalendarCtrl_GetHeaderColourBg, 1514).
--define(wxCalendarCtrl_SetHighlightColours, 1515).
--define(wxCalendarCtrl_GetHighlightColourFg, 1516).
--define(wxCalendarCtrl_GetHighlightColourBg, 1517).
--define(wxCalendarCtrl_SetHolidayColours, 1518).
--define(wxCalendarCtrl_GetHolidayColourFg, 1519).
--define(wxCalendarCtrl_GetHolidayColourBg, 1520).
--define(wxCalendarCtrl_GetAttr, 1521).
--define(wxCalendarCtrl_SetAttr, 1522).
--define(wxCalendarCtrl_SetHoliday, 1523).
--define(wxCalendarCtrl_ResetAttr, 1524).
--define(wxCalendarCtrl_HitTest, 1525).
--define(wxCalendarDateAttr_new_0, 1526).
--define(wxCalendarDateAttr_new_2_1, 1527).
--define(wxCalendarDateAttr_new_2_0, 1528).
--define(wxCalendarDateAttr_SetTextColour, 1529).
--define(wxCalendarDateAttr_SetBackgroundColour, 1530).
--define(wxCalendarDateAttr_SetBorderColour, 1531).
--define(wxCalendarDateAttr_SetFont, 1532).
--define(wxCalendarDateAttr_SetBorder, 1533).
--define(wxCalendarDateAttr_SetHoliday, 1534).
--define(wxCalendarDateAttr_HasTextColour, 1535).
--define(wxCalendarDateAttr_HasBackgroundColour, 1536).
--define(wxCalendarDateAttr_HasBorderColour, 1537).
--define(wxCalendarDateAttr_HasFont, 1538).
--define(wxCalendarDateAttr_HasBorder, 1539).
--define(wxCalendarDateAttr_IsHoliday, 1540).
--define(wxCalendarDateAttr_GetTextColour, 1541).
--define(wxCalendarDateAttr_GetBackgroundColour, 1542).
--define(wxCalendarDateAttr_GetBorderColour, 1543).
--define(wxCalendarDateAttr_GetFont, 1544).
--define(wxCalendarDateAttr_GetBorder, 1545).
--define(wxCalendarDateAttr_destroy, 1546).
--define(wxCheckBox_new_4, 1548).
--define(wxCheckBox_new_0, 1549).
--define(wxCheckBox_Create, 1550).
--define(wxCheckBox_GetValue, 1551).
--define(wxCheckBox_Get3StateValue, 1552).
--define(wxCheckBox_Is3rdStateAllowedForUser, 1553).
--define(wxCheckBox_Is3State, 1554).
--define(wxCheckBox_IsChecked, 1555).
--define(wxCheckBox_SetValue, 1556).
--define(wxCheckBox_Set3StateValue, 1557).
--define(wxCheckBox_destroy, 1558).
--define(wxCheckListBox_new_0, 1559).
--define(wxCheckListBox_new_3, 1561).
--define(wxCheckListBox_Check, 1562).
--define(wxCheckListBox_IsChecked, 1563).
--define(wxCheckListBox_destroy, 1564).
--define(wxChoice_new_3, 1567).
--define(wxChoice_new_0, 1568).
--define(wxChoice_destruct, 1570).
--define(wxChoice_Create, 1572).
--define(wxChoice_Delete, 1573).
--define(wxChoice_GetColumns, 1574).
--define(wxChoice_SetColumns, 1575).
--define(wxComboBox_new_0, 1576).
--define(wxComboBox_new_3, 1578).
--define(wxComboBox_destruct, 1579).
--define(wxComboBox_Create, 1581).
--define(wxComboBox_CanCopy, 1582).
--define(wxComboBox_CanCut, 1583).
--define(wxComboBox_CanPaste, 1584).
--define(wxComboBox_CanRedo, 1585).
--define(wxComboBox_CanUndo, 1586).
--define(wxComboBox_Copy, 1587).
--define(wxComboBox_Cut, 1588).
--define(wxComboBox_GetInsertionPoint, 1589).
--define(wxComboBox_GetLastPosition, 1590).
--define(wxComboBox_GetValue, 1591).
--define(wxComboBox_Paste, 1592).
--define(wxComboBox_Redo, 1593).
--define(wxComboBox_Replace, 1594).
--define(wxComboBox_Remove, 1595).
--define(wxComboBox_SetInsertionPoint, 1596).
--define(wxComboBox_SetInsertionPointEnd, 1597).
--define(wxComboBox_SetSelection_1, 1598).
--define(wxComboBox_SetSelection_2, 1599).
--define(wxComboBox_SetValue, 1600).
--define(wxComboBox_Undo, 1601).
--define(wxGauge_new_0, 1602).
--define(wxGauge_new_4, 1603).
--define(wxGauge_Create, 1604).
--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).
+-define(wxWindow_DragAcceptFiles, 126).
+-define(wxWindow_Enable, 127).
+-define(wxWindow_FindFocus, 128).
+-define(wxWindow_FindWindow_1_0, 129).
+-define(wxWindow_FindWindow_1_1, 130).
+-define(wxWindow_FindWindowById, 131).
+-define(wxWindow_FindWindowByName, 132).
+-define(wxWindow_FindWindowByLabel, 133).
+-define(wxWindow_Fit, 134).
+-define(wxWindow_FitInside, 135).
+-define(wxWindow_Freeze, 136).
+-define(wxWindow_GetAcceleratorTable, 137).
+-define(wxWindow_GetBackgroundColour, 138).
+-define(wxWindow_GetBackgroundStyle, 139).
+-define(wxWindow_GetBestSize, 140).
+-define(wxWindow_GetCaret, 142).
+-define(wxWindow_GetCapture, 143).
+-define(wxWindow_GetCharHeight, 144).
+-define(wxWindow_GetCharWidth, 145).
+-define(wxWindow_GetChildren, 146).
+-define(wxWindow_GetClientSize, 149).
+-define(wxWindow_GetContainingSizer, 150).
+-define(wxWindow_GetCursor, 151).
+-define(wxWindow_GetDropTarget, 152).
+-define(wxWindow_GetEventHandler, 153).
+-define(wxWindow_GetExtraStyle, 154).
+-define(wxWindow_GetFont, 155).
+-define(wxWindow_GetForegroundColour, 156).
+-define(wxWindow_GetGrandParent, 157).
+-define(wxWindow_GetHandle, 158).
+-define(wxWindow_GetHelpText, 159).
+-define(wxWindow_GetId, 160).
+-define(wxWindow_GetLabel, 161).
+-define(wxWindow_GetMaxSize, 162).
+-define(wxWindow_GetMinSize, 163).
+-define(wxWindow_GetName, 164).
+-define(wxWindow_GetParent, 165).
+-define(wxWindow_GetPosition, 167).
+-define(wxWindow_GetRect, 168).
+-define(wxWindow_GetScreenPosition, 170).
+-define(wxWindow_GetScreenRect, 171).
+-define(wxWindow_GetScrollPos, 172).
+-define(wxWindow_GetScrollRange, 173).
+-define(wxWindow_GetScrollThumb, 174).
+-define(wxWindow_GetSize, 176).
+-define(wxWindow_GetSizer, 177).
+-define(wxWindow_GetTextExtent, 178).
+-define(wxWindow_GetToolTip, 179).
+-define(wxWindow_GetUpdateRegion, 180).
+-define(wxWindow_GetVirtualSize, 182).
+-define(wxWindow_GetWindowStyleFlag, 184).
+-define(wxWindow_GetWindowVariant, 185).
+-define(wxWindow_HasCapture, 186).
+-define(wxWindow_HasScrollbar, 187).
+-define(wxWindow_HasTransparentBackground, 188).
+-define(wxWindow_Hide, 189).
+-define(wxWindow_InheritAttributes, 190).
+-define(wxWindow_InitDialog, 191).
+-define(wxWindow_InvalidateBestSize, 192).
+-define(wxWindow_IsEnabled, 193).
+-define(wxWindow_IsExposed_2, 194).
+-define(wxWindow_IsExposed_4, 195).
+-define(wxWindow_IsExposed_1_0, 196).
+-define(wxWindow_IsExposed_1_1, 197).
+-define(wxWindow_IsRetained, 198).
+-define(wxWindow_IsShown, 199).
+-define(wxWindow_IsTopLevel, 200).
+-define(wxWindow_Layout, 201).
+-define(wxWindow_LineDown, 202).
+-define(wxWindow_LineUp, 203).
+-define(wxWindow_Lower, 204).
+-define(wxWindow_MakeModal, 205).
+-define(wxWindow_Move_3, 206).
+-define(wxWindow_Move_2, 207).
+-define(wxWindow_MoveAfterInTabOrder, 208).
+-define(wxWindow_MoveBeforeInTabOrder, 209).
+-define(wxWindow_Navigate, 210).
+-define(wxWindow_PageDown, 211).
+-define(wxWindow_PageUp, 212).
+-define(wxWindow_PopEventHandler, 213).
+-define(wxWindow_PopupMenu_2, 214).
+-define(wxWindow_PopupMenu_3, 215).
+-define(wxWindow_Raise, 216).
+-define(wxWindow_Refresh, 217).
+-define(wxWindow_RefreshRect, 218).
+-define(wxWindow_ReleaseMouse, 219).
+-define(wxWindow_RemoveChild, 220).
+-define(wxWindow_Reparent, 221).
+-define(wxWindow_ScreenToClient_2, 222).
+-define(wxWindow_ScreenToClient_1, 223).
+-define(wxWindow_ScrollLines, 225).
+-define(wxWindow_ScrollPages, 227).
+-define(wxWindow_ScrollWindow, 228).
+-define(wxWindow_SetAcceleratorTable, 229).
+-define(wxWindow_SetAutoLayout, 230).
+-define(wxWindow_SetBackgroundColour, 231).
+-define(wxWindow_SetBackgroundStyle, 232).
+-define(wxWindow_SetCaret, 233).
+-define(wxWindow_SetClientSize_2, 234).
+-define(wxWindow_SetClientSize_1_0, 235).
+-define(wxWindow_SetClientSize_1_1, 236).
+-define(wxWindow_SetContainingSizer, 237).
+-define(wxWindow_SetCursor, 238).
+-define(wxWindow_SetMaxSize, 239).
+-define(wxWindow_SetMinSize, 240).
+-define(wxWindow_SetOwnBackgroundColour, 241).
+-define(wxWindow_SetOwnFont, 242).
+-define(wxWindow_SetOwnForegroundColour, 243).
+-define(wxWindow_SetDropTarget, 244).
+-define(wxWindow_SetExtraStyle, 245).
+-define(wxWindow_SetFocus, 246).
+-define(wxWindow_SetFocusFromKbd, 247).
+-define(wxWindow_SetFont, 248).
+-define(wxWindow_SetForegroundColour, 249).
+-define(wxWindow_SetHelpText, 250).
+-define(wxWindow_SetId, 251).
+-define(wxWindow_SetLabel, 253).
+-define(wxWindow_SetName, 254).
+-define(wxWindow_SetPalette, 255).
+-define(wxWindow_SetScrollbar, 256).
+-define(wxWindow_SetScrollPos, 257).
+-define(wxWindow_SetSize_5, 258).
+-define(wxWindow_SetSize_2_0, 259).
+-define(wxWindow_SetSize_1, 260).
+-define(wxWindow_SetSize_2_1, 261).
+-define(wxWindow_SetSizeHints_3, 262).
+-define(wxWindow_SetSizeHints_2, 263).
+-define(wxWindow_SetSizer, 264).
+-define(wxWindow_SetSizerAndFit, 265).
+-define(wxWindow_SetThemeEnabled, 266).
+-define(wxWindow_SetToolTip_1_0, 267).
+-define(wxWindow_SetToolTip_1_1, 268).
+-define(wxWindow_SetVirtualSize_1, 269).
+-define(wxWindow_SetVirtualSize_2, 270).
+-define(wxWindow_SetVirtualSizeHints_3, 271).
+-define(wxWindow_SetVirtualSizeHints_2, 272).
+-define(wxWindow_SetWindowStyle, 273).
+-define(wxWindow_SetWindowStyleFlag, 274).
+-define(wxWindow_SetWindowVariant, 275).
+-define(wxWindow_ShouldInheritColours, 276).
+-define(wxWindow_Show, 277).
+-define(wxWindow_Thaw, 278).
+-define(wxWindow_TransferDataFromWindow, 279).
+-define(wxWindow_TransferDataToWindow, 280).
+-define(wxWindow_Update, 281).
+-define(wxWindow_UpdateWindowUI, 282).
+-define(wxWindow_Validate, 283).
+-define(wxWindow_WarpPointer, 284).
+-define(wxWindow_SetTransparent, 285).
+-define(wxWindow_CanSetTransparent, 286).
+-define(wxWindow_IsDoubleBuffered, 287).
+-define(wxWindow_SetDoubleBuffered, 288).
+-define(wxWindow_GetContentScaleFactor, 289).
+-define(wxTopLevelWindow_GetIcon, 290).
+-define(wxTopLevelWindow_GetIcons, 291).
+-define(wxTopLevelWindow_GetTitle, 292).
+-define(wxTopLevelWindow_IsActive, 293).
+-define(wxTopLevelWindow_Iconize, 294).
+-define(wxTopLevelWindow_IsFullScreen, 295).
+-define(wxTopLevelWindow_IsIconized, 296).
+-define(wxTopLevelWindow_IsMaximized, 297).
+-define(wxTopLevelWindow_Maximize, 298).
+-define(wxTopLevelWindow_RequestUserAttention, 299).
+-define(wxTopLevelWindow_SetIcon, 300).
+-define(wxTopLevelWindow_SetIcons, 301).
+-define(wxTopLevelWindow_CenterOnScreen, 302).
+-define(wxTopLevelWindow_CentreOnScreen, 303).
+-define(wxTopLevelWindow_SetShape, 305).
+-define(wxTopLevelWindow_SetTitle, 306).
+-define(wxTopLevelWindow_ShowFullScreen, 307).
+-define(wxFrame_new_4, 309).
+-define(wxFrame_new_0, 310).
+-define(wxFrame_destruct, 312).
+-define(wxFrame_Create, 313).
+-define(wxFrame_CreateStatusBar, 314).
+-define(wxFrame_CreateToolBar, 315).
+-define(wxFrame_GetClientAreaOrigin, 316).
+-define(wxFrame_GetMenuBar, 317).
+-define(wxFrame_GetStatusBar, 318).
+-define(wxFrame_GetStatusBarPane, 319).
+-define(wxFrame_GetToolBar, 320).
+-define(wxFrame_ProcessCommand, 321).
+-define(wxFrame_SendSizeEvent, 322).
+-define(wxFrame_SetMenuBar, 323).
+-define(wxFrame_SetStatusBar, 324).
+-define(wxFrame_SetStatusBarPane, 325).
+-define(wxFrame_SetStatusText, 326).
+-define(wxFrame_SetStatusWidths, 327).
+-define(wxFrame_SetToolBar, 328).
+-define(wxMiniFrame_new_0, 329).
+-define(wxMiniFrame_new_4, 330).
+-define(wxMiniFrame_Create, 331).
+-define(wxMiniFrame_destroy, 332).
+-define(wxSplashScreen_new_0, 333).
+-define(wxSplashScreen_new_6, 334).
+-define(wxSplashScreen_destruct, 335).
+-define(wxSplashScreen_GetSplashStyle, 336).
+-define(wxSplashScreen_GetTimeout, 337).
+-define(wxPanel_new_0, 338).
+-define(wxPanel_new_6, 339).
+-define(wxPanel_new_2, 340).
+-define(wxPanel_destruct, 341).
+-define(wxPanel_InitDialog, 342).
+-define(wxPanel_SetFocusIgnoringChildren, 343).
+-define(wxScrolledWindow_new_0, 344).
+-define(wxScrolledWindow_new_2, 345).
+-define(wxScrolledWindow_destruct, 346).
+-define(wxScrolledWindow_CalcScrolledPosition_4, 347).
+-define(wxScrolledWindow_CalcScrolledPosition_1, 348).
+-define(wxScrolledWindow_CalcUnscrolledPosition_4, 349).
+-define(wxScrolledWindow_CalcUnscrolledPosition_1, 350).
+-define(wxScrolledWindow_EnableScrolling, 351).
+-define(wxScrolledWindow_GetScrollPixelsPerUnit, 352).
+-define(wxScrolledWindow_GetViewStart, 353).
+-define(wxScrolledWindow_DoPrepareDC, 354).
+-define(wxScrolledWindow_PrepareDC, 355).
+-define(wxScrolledWindow_Scroll, 356).
+-define(wxScrolledWindow_SetScrollbars, 357).
+-define(wxScrolledWindow_SetScrollRate, 358).
+-define(wxScrolledWindow_SetTargetWindow, 359).
+-define(wxSashWindow_new_0, 360).
+-define(wxSashWindow_new_2, 361).
+-define(wxSashWindow_destruct, 362).
+-define(wxSashWindow_GetSashVisible, 363).
+-define(wxSashWindow_GetMaximumSizeX, 364).
+-define(wxSashWindow_GetMaximumSizeY, 365).
+-define(wxSashWindow_GetMinimumSizeX, 366).
+-define(wxSashWindow_GetMinimumSizeY, 367).
+-define(wxSashWindow_SetMaximumSizeX, 368).
+-define(wxSashWindow_SetMaximumSizeY, 369).
+-define(wxSashWindow_SetMinimumSizeX, 370).
+-define(wxSashWindow_SetMinimumSizeY, 371).
+-define(wxSashWindow_SetSashVisible, 372).
+-define(wxSashLayoutWindow_new_0, 373).
+-define(wxSashLayoutWindow_new_2, 374).
+-define(wxSashLayoutWindow_Create, 375).
+-define(wxSashLayoutWindow_GetAlignment, 376).
+-define(wxSashLayoutWindow_GetOrientation, 377).
+-define(wxSashLayoutWindow_SetAlignment, 378).
+-define(wxSashLayoutWindow_SetDefaultSize, 379).
+-define(wxSashLayoutWindow_SetOrientation, 380).
+-define(wxSashLayoutWindow_destroy, 381).
+-define(wxGrid_new_0, 382).
+-define(wxGrid_new_3, 383).
+-define(wxGrid_new_4, 384).
+-define(wxGrid_destruct, 385).
+-define(wxGrid_AppendCols, 386).
+-define(wxGrid_AppendRows, 387).
+-define(wxGrid_AutoSize, 388).
+-define(wxGrid_AutoSizeColumn, 389).
+-define(wxGrid_AutoSizeColumns, 390).
+-define(wxGrid_AutoSizeRow, 391).
+-define(wxGrid_AutoSizeRows, 392).
+-define(wxGrid_BeginBatch, 393).
+-define(wxGrid_BlockToDeviceRect, 394).
+-define(wxGrid_CanDragColSize, 395).
+-define(wxGrid_CanDragRowSize, 396).
+-define(wxGrid_CanDragGridSize, 397).
+-define(wxGrid_CanEnableCellControl, 398).
+-define(wxGrid_CellToRect_2, 399).
+-define(wxGrid_CellToRect_1, 400).
+-define(wxGrid_ClearGrid, 401).
+-define(wxGrid_ClearSelection, 402).
+-define(wxGrid_CreateGrid, 403).
+-define(wxGrid_DeleteCols, 404).
+-define(wxGrid_DeleteRows, 405).
+-define(wxGrid_DisableCellEditControl, 406).
+-define(wxGrid_DisableDragColSize, 407).
+-define(wxGrid_DisableDragGridSize, 408).
+-define(wxGrid_DisableDragRowSize, 409).
+-define(wxGrid_EnableCellEditControl, 410).
+-define(wxGrid_EnableDragColSize, 411).
+-define(wxGrid_EnableDragGridSize, 412).
+-define(wxGrid_EnableDragRowSize, 413).
+-define(wxGrid_EnableEditing, 414).
+-define(wxGrid_EnableGridLines, 415).
+-define(wxGrid_EndBatch, 416).
+-define(wxGrid_Fit, 417).
+-define(wxGrid_ForceRefresh, 418).
+-define(wxGrid_GetBatchCount, 419).
+-define(wxGrid_GetCellAlignment, 420).
+-define(wxGrid_GetCellBackgroundColour, 421).
+-define(wxGrid_GetCellEditor, 422).
+-define(wxGrid_GetCellFont, 423).
+-define(wxGrid_GetCellRenderer, 424).
+-define(wxGrid_GetCellTextColour, 425).
+-define(wxGrid_GetCellValue_2, 426).
+-define(wxGrid_GetCellValue_1, 427).
+-define(wxGrid_GetColLabelAlignment, 428).
+-define(wxGrid_GetColLabelSize, 429).
+-define(wxGrid_GetColLabelValue, 430).
+-define(wxGrid_GetColMinimalAcceptableWidth, 431).
+-define(wxGrid_GetDefaultCellAlignment, 432).
+-define(wxGrid_GetDefaultCellBackgroundColour, 433).
+-define(wxGrid_GetDefaultCellFont, 434).
+-define(wxGrid_GetDefaultCellTextColour, 435).
+-define(wxGrid_GetDefaultColLabelSize, 436).
+-define(wxGrid_GetDefaultColSize, 437).
+-define(wxGrid_GetDefaultEditor, 438).
+-define(wxGrid_GetDefaultEditorForCell_2, 439).
+-define(wxGrid_GetDefaultEditorForCell_1, 440).
+-define(wxGrid_GetDefaultEditorForType, 441).
+-define(wxGrid_GetDefaultRenderer, 442).
+-define(wxGrid_GetDefaultRendererForCell, 443).
+-define(wxGrid_GetDefaultRendererForType, 444).
+-define(wxGrid_GetDefaultRowLabelSize, 445).
+-define(wxGrid_GetDefaultRowSize, 446).
+-define(wxGrid_GetGridCursorCol, 447).
+-define(wxGrid_GetGridCursorRow, 448).
+-define(wxGrid_GetGridLineColour, 449).
+-define(wxGrid_GridLinesEnabled, 450).
+-define(wxGrid_GetLabelBackgroundColour, 451).
+-define(wxGrid_GetLabelFont, 452).
+-define(wxGrid_GetLabelTextColour, 453).
+-define(wxGrid_GetNumberCols, 454).
+-define(wxGrid_GetNumberRows, 455).
+-define(wxGrid_GetOrCreateCellAttr, 456).
+-define(wxGrid_GetRowMinimalAcceptableHeight, 457).
+-define(wxGrid_GetRowLabelAlignment, 458).
+-define(wxGrid_GetRowLabelSize, 459).
+-define(wxGrid_GetRowLabelValue, 460).
+-define(wxGrid_GetRowSize, 461).
+-define(wxGrid_GetScrollLineX, 462).
+-define(wxGrid_GetScrollLineY, 463).
+-define(wxGrid_GetSelectedCells, 464).
+-define(wxGrid_GetSelectedCols, 465).
+-define(wxGrid_GetSelectedRows, 466).
+-define(wxGrid_GetSelectionBackground, 467).
+-define(wxGrid_GetSelectionBlockTopLeft, 468).
+-define(wxGrid_GetSelectionBlockBottomRight, 469).
+-define(wxGrid_GetSelectionForeground, 470).
+-define(wxGrid_GetViewWidth, 471).
+-define(wxGrid_GetGridWindow, 472).
+-define(wxGrid_GetGridRowLabelWindow, 473).
+-define(wxGrid_GetGridColLabelWindow, 474).
+-define(wxGrid_GetGridCornerLabelWindow, 475).
+-define(wxGrid_HideCellEditControl, 476).
+-define(wxGrid_InsertCols, 477).
+-define(wxGrid_InsertRows, 478).
+-define(wxGrid_IsCellEditControlEnabled, 479).
+-define(wxGrid_IsCurrentCellReadOnly, 480).
+-define(wxGrid_IsEditable, 481).
+-define(wxGrid_IsInSelection_2, 482).
+-define(wxGrid_IsInSelection_1, 483).
+-define(wxGrid_IsReadOnly, 484).
+-define(wxGrid_IsSelection, 485).
+-define(wxGrid_IsVisible_3, 486).
+-define(wxGrid_IsVisible_2, 487).
+-define(wxGrid_MakeCellVisible_2, 488).
+-define(wxGrid_MakeCellVisible_1, 489).
+-define(wxGrid_MoveCursorDown, 490).
+-define(wxGrid_MoveCursorLeft, 491).
+-define(wxGrid_MoveCursorRight, 492).
+-define(wxGrid_MoveCursorUp, 493).
+-define(wxGrid_MoveCursorDownBlock, 494).
+-define(wxGrid_MoveCursorLeftBlock, 495).
+-define(wxGrid_MoveCursorRightBlock, 496).
+-define(wxGrid_MoveCursorUpBlock, 497).
+-define(wxGrid_MovePageDown, 498).
+-define(wxGrid_MovePageUp, 499).
+-define(wxGrid_RegisterDataType, 500).
+-define(wxGrid_SaveEditControlValue, 501).
+-define(wxGrid_SelectAll, 502).
+-define(wxGrid_SelectBlock_5, 503).
+-define(wxGrid_SelectBlock_3, 504).
+-define(wxGrid_SelectCol, 505).
+-define(wxGrid_SelectRow, 506).
+-define(wxGrid_SetCellAlignment_4, 507).
+-define(wxGrid_SetCellAlignment_3, 508).
+-define(wxGrid_SetCellAlignment_1, 509).
+-define(wxGrid_SetCellBackgroundColour_3_0, 510).
+-define(wxGrid_SetCellBackgroundColour_1, 511).
+-define(wxGrid_SetCellBackgroundColour_3_1, 512).
+-define(wxGrid_SetCellEditor, 513).
+-define(wxGrid_SetCellFont, 514).
+-define(wxGrid_SetCellRenderer, 515).
+-define(wxGrid_SetCellTextColour_3_0, 516).
+-define(wxGrid_SetCellTextColour_3_1, 517).
+-define(wxGrid_SetCellTextColour_1, 518).
+-define(wxGrid_SetCellValue_3_0, 519).
+-define(wxGrid_SetCellValue_2, 520).
+-define(wxGrid_SetCellValue_3_1, 521).
+-define(wxGrid_SetColAttr, 522).
+-define(wxGrid_SetColFormatBool, 523).
+-define(wxGrid_SetColFormatNumber, 524).
+-define(wxGrid_SetColFormatFloat, 525).
+-define(wxGrid_SetColFormatCustom, 526).
+-define(wxGrid_SetColLabelAlignment, 527).
+-define(wxGrid_SetColLabelSize, 528).
+-define(wxGrid_SetColLabelValue, 529).
+-define(wxGrid_SetColMinimalWidth, 530).
+-define(wxGrid_SetColMinimalAcceptableWidth, 531).
+-define(wxGrid_SetColSize, 532).
+-define(wxGrid_SetDefaultCellAlignment, 533).
+-define(wxGrid_SetDefaultCellBackgroundColour, 534).
+-define(wxGrid_SetDefaultCellFont, 535).
+-define(wxGrid_SetDefaultCellTextColour, 536).
+-define(wxGrid_SetDefaultEditor, 537).
+-define(wxGrid_SetDefaultRenderer, 538).
+-define(wxGrid_SetDefaultColSize, 539).
+-define(wxGrid_SetDefaultRowSize, 540).
+-define(wxGrid_SetGridCursor, 541).
+-define(wxGrid_SetGridLineColour, 542).
+-define(wxGrid_SetLabelBackgroundColour, 543).
+-define(wxGrid_SetLabelFont, 544).
+-define(wxGrid_SetLabelTextColour, 545).
+-define(wxGrid_SetMargins, 546).
+-define(wxGrid_SetReadOnly, 547).
+-define(wxGrid_SetRowAttr, 548).
+-define(wxGrid_SetRowLabelAlignment, 549).
+-define(wxGrid_SetRowLabelSize, 550).
+-define(wxGrid_SetRowLabelValue, 551).
+-define(wxGrid_SetRowMinimalHeight, 552).
+-define(wxGrid_SetRowMinimalAcceptableHeight, 553).
+-define(wxGrid_SetRowSize, 554).
+-define(wxGrid_SetScrollLineX, 555).
+-define(wxGrid_SetScrollLineY, 556).
+-define(wxGrid_SetSelectionBackground, 557).
+-define(wxGrid_SetSelectionForeground, 558).
+-define(wxGrid_SetSelectionMode, 559).
+-define(wxGrid_ShowCellEditControl, 560).
+-define(wxGrid_XToCol, 561).
+-define(wxGrid_XToEdgeOfCol, 562).
+-define(wxGrid_YToEdgeOfRow, 563).
+-define(wxGrid_YToRow, 564).
+-define(wxGridCellRenderer_Draw, 565).
+-define(wxGridCellRenderer_GetBestSize, 566).
+-define(wxGridCellEditor_Create, 567).
+-define(wxGridCellEditor_IsCreated, 568).
+-define(wxGridCellEditor_SetSize, 569).
+-define(wxGridCellEditor_Show, 570).
+-define(wxGridCellEditor_PaintBackground, 571).
+-define(wxGridCellEditor_BeginEdit, 572).
+-define(wxGridCellEditor_EndEdit, 573).
+-define(wxGridCellEditor_Reset, 574).
+-define(wxGridCellEditor_StartingKey, 575).
+-define(wxGridCellEditor_StartingClick, 576).
+-define(wxGridCellEditor_HandleReturn, 577).
+-define(wxGridCellBoolRenderer_new, 578).
+-define(wxGridCellBoolRenderer_destroy, 579).
+-define(wxGridCellBoolEditor_new, 580).
+-define(wxGridCellBoolEditor_IsTrueValue, 581).
+-define(wxGridCellBoolEditor_UseStringValues, 582).
+-define(wxGridCellBoolEditor_destroy, 583).
+-define(wxGridCellFloatRenderer_new, 584).
+-define(wxGridCellFloatRenderer_GetPrecision, 585).
+-define(wxGridCellFloatRenderer_GetWidth, 586).
+-define(wxGridCellFloatRenderer_SetParameters, 587).
+-define(wxGridCellFloatRenderer_SetPrecision, 588).
+-define(wxGridCellFloatRenderer_SetWidth, 589).
+-define(wxGridCellFloatRenderer_destroy, 590).
+-define(wxGridCellFloatEditor_new, 591).
+-define(wxGridCellFloatEditor_SetParameters, 592).
+-define(wxGridCellFloatEditor_destroy, 593).
+-define(wxGridCellStringRenderer_new, 594).
+-define(wxGridCellStringRenderer_destroy, 595).
+-define(wxGridCellTextEditor_new, 596).
+-define(wxGridCellTextEditor_SetParameters, 597).
+-define(wxGridCellTextEditor_destroy, 598).
+-define(wxGridCellChoiceEditor_new, 600).
+-define(wxGridCellChoiceEditor_SetParameters, 601).
+-define(wxGridCellChoiceEditor_destroy, 602).
+-define(wxGridCellNumberRenderer_new, 603).
+-define(wxGridCellNumberRenderer_destroy, 604).
+-define(wxGridCellNumberEditor_new, 605).
+-define(wxGridCellNumberEditor_GetValue, 606).
+-define(wxGridCellNumberEditor_SetParameters, 607).
+-define(wxGridCellNumberEditor_destroy, 608).
+-define(wxGridCellAttr_SetTextColour, 609).
+-define(wxGridCellAttr_SetBackgroundColour, 610).
+-define(wxGridCellAttr_SetFont, 611).
+-define(wxGridCellAttr_SetAlignment, 612).
+-define(wxGridCellAttr_SetReadOnly, 613).
+-define(wxGridCellAttr_SetRenderer, 614).
+-define(wxGridCellAttr_SetEditor, 615).
+-define(wxGridCellAttr_HasTextColour, 616).
+-define(wxGridCellAttr_HasBackgroundColour, 617).
+-define(wxGridCellAttr_HasFont, 618).
+-define(wxGridCellAttr_HasAlignment, 619).
+-define(wxGridCellAttr_HasRenderer, 620).
+-define(wxGridCellAttr_HasEditor, 621).
+-define(wxGridCellAttr_GetTextColour, 622).
+-define(wxGridCellAttr_GetBackgroundColour, 623).
+-define(wxGridCellAttr_GetFont, 624).
+-define(wxGridCellAttr_GetAlignment, 625).
+-define(wxGridCellAttr_GetRenderer, 626).
+-define(wxGridCellAttr_GetEditor, 627).
+-define(wxGridCellAttr_IsReadOnly, 628).
+-define(wxGridCellAttr_SetDefAttr, 629).
+-define(wxDC_Blit, 630).
+-define(wxDC_CalcBoundingBox, 631).
+-define(wxDC_Clear, 632).
+-define(wxDC_ComputeScaleAndOrigin, 633).
+-define(wxDC_CrossHair, 634).
+-define(wxDC_DestroyClippingRegion, 635).
+-define(wxDC_DeviceToLogicalX, 636).
+-define(wxDC_DeviceToLogicalXRel, 637).
+-define(wxDC_DeviceToLogicalY, 638).
+-define(wxDC_DeviceToLogicalYRel, 639).
+-define(wxDC_DrawArc, 640).
+-define(wxDC_DrawBitmap, 641).
+-define(wxDC_DrawCheckMark, 642).
+-define(wxDC_DrawCircle, 643).
+-define(wxDC_DrawEllipse_2, 645).
+-define(wxDC_DrawEllipse_1, 646).
+-define(wxDC_DrawEllipticArc, 647).
+-define(wxDC_DrawIcon, 648).
+-define(wxDC_DrawLabel, 649).
+-define(wxDC_DrawLine, 650).
+-define(wxDC_DrawLines, 651).
+-define(wxDC_DrawPolygon, 653).
+-define(wxDC_DrawPoint, 655).
+-define(wxDC_DrawRectangle_2, 657).
+-define(wxDC_DrawRectangle_1, 658).
+-define(wxDC_DrawRotatedText, 659).
+-define(wxDC_DrawRoundedRectangle_3, 661).
+-define(wxDC_DrawRoundedRectangle_2, 662).
+-define(wxDC_DrawText, 663).
+-define(wxDC_EndDoc, 664).
+-define(wxDC_EndPage, 665).
+-define(wxDC_FloodFill, 666).
+-define(wxDC_GetBackground, 667).
+-define(wxDC_GetBackgroundMode, 668).
+-define(wxDC_GetBrush, 669).
+-define(wxDC_GetCharHeight, 670).
+-define(wxDC_GetCharWidth, 671).
+-define(wxDC_GetClippingBox, 672).
+-define(wxDC_GetFont, 674).
+-define(wxDC_GetLayoutDirection, 675).
+-define(wxDC_GetLogicalFunction, 676).
+-define(wxDC_GetMapMode, 677).
+-define(wxDC_GetMultiLineTextExtent_4, 678).
+-define(wxDC_GetMultiLineTextExtent_1, 679).
+-define(wxDC_GetPartialTextExtents, 680).
+-define(wxDC_GetPen, 681).
+-define(wxDC_GetPixel, 682).
+-define(wxDC_GetPPI, 683).
+-define(wxDC_GetSize, 685).
+-define(wxDC_GetSizeMM, 687).
+-define(wxDC_GetTextBackground, 688).
+-define(wxDC_GetTextExtent_4, 689).
+-define(wxDC_GetTextExtent_1, 690).
+-define(wxDC_GetTextForeground, 692).
+-define(wxDC_GetUserScale, 693).
+-define(wxDC_GradientFillConcentric_3, 694).
+-define(wxDC_GradientFillConcentric_4, 695).
+-define(wxDC_GradientFillLinear, 696).
+-define(wxDC_LogicalToDeviceX, 697).
+-define(wxDC_LogicalToDeviceXRel, 698).
+-define(wxDC_LogicalToDeviceY, 699).
+-define(wxDC_LogicalToDeviceYRel, 700).
+-define(wxDC_MaxX, 701).
+-define(wxDC_MaxY, 702).
+-define(wxDC_MinX, 703).
+-define(wxDC_MinY, 704).
+-define(wxDC_IsOk, 705).
+-define(wxDC_ResetBoundingBox, 706).
+-define(wxDC_SetAxisOrientation, 707).
+-define(wxDC_SetBackground, 708).
+-define(wxDC_SetBackgroundMode, 709).
+-define(wxDC_SetBrush, 710).
+-define(wxDC_SetClippingRegion_2, 712).
+-define(wxDC_SetClippingRegion_1_1, 713).
+-define(wxDC_SetClippingRegion_1_0, 714).
+-define(wxDC_SetDeviceOrigin, 715).
+-define(wxDC_SetFont, 716).
+-define(wxDC_SetLayoutDirection, 717).
+-define(wxDC_SetLogicalFunction, 718).
+-define(wxDC_SetMapMode, 719).
+-define(wxDC_SetPalette, 720).
+-define(wxDC_SetPen, 721).
+-define(wxDC_SetTextBackground, 722).
+-define(wxDC_SetTextForeground, 723).
+-define(wxDC_SetUserScale, 724).
+-define(wxDC_StartDoc, 725).
+-define(wxDC_StartPage, 726).
+-define(wxMirrorDC_new, 727).
+-define(wxMirrorDC_destroy, 728).
+-define(wxScreenDC_new, 729).
+-define(wxScreenDC_destruct, 730).
+-define(wxPostScriptDC_new_0, 731).
+-define(wxPostScriptDC_new_1, 732).
+-define(wxPostScriptDC_destruct, 733).
+-define(wxPostScriptDC_SetResolution, 734).
+-define(wxPostScriptDC_GetResolution, 735).
+-define(wxWindowDC_new_0, 736).
+-define(wxWindowDC_new_1, 737).
+-define(wxWindowDC_destruct, 738).
+-define(wxClientDC_new_0, 739).
+-define(wxClientDC_new_1, 740).
+-define(wxClientDC_destroy, 741).
+-define(wxPaintDC_new_0, 742).
+-define(wxPaintDC_new_1, 743).
+-define(wxPaintDC_destroy, 744).
+-define(wxMemoryDC_new_1_0, 746).
+-define(wxMemoryDC_new_1_1, 747).
+-define(wxMemoryDC_new_0, 748).
+-define(wxMemoryDC_destruct, 750).
+-define(wxMemoryDC_SelectObject, 751).
+-define(wxMemoryDC_SelectObjectAsSource, 752).
+-define(wxBufferedDC_new_0, 753).
+-define(wxBufferedDC_new_2, 754).
+-define(wxBufferedDC_new_3, 755).
+-define(wxBufferedDC_destruct, 756).
+-define(wxBufferedDC_Init_2, 757).
+-define(wxBufferedDC_Init_3, 758).
+-define(wxBufferedPaintDC_new_3, 759).
+-define(wxBufferedPaintDC_new_2, 760).
+-define(wxBufferedPaintDC_destruct, 761).
+-define(wxGraphicsObject_destruct, 762).
+-define(wxGraphicsObject_GetRenderer, 763).
+-define(wxGraphicsObject_IsNull, 764).
+-define(wxGraphicsContext_destruct, 765).
+-define(wxGraphicsContext_Create_1_1, 766).
+-define(wxGraphicsContext_Create_1_0, 767).
+-define(wxGraphicsContext_Create_0, 768).
+-define(wxGraphicsContext_CreatePen, 769).
+-define(wxGraphicsContext_CreateBrush, 770).
+-define(wxGraphicsContext_CreateRadialGradientBrush, 771).
+-define(wxGraphicsContext_CreateLinearGradientBrush, 772).
+-define(wxGraphicsContext_CreateFont, 773).
+-define(wxGraphicsContext_CreateMatrix, 774).
+-define(wxGraphicsContext_CreatePath, 775).
+-define(wxGraphicsContext_Clip_1, 776).
+-define(wxGraphicsContext_Clip_4, 777).
+-define(wxGraphicsContext_ResetClip, 778).
+-define(wxGraphicsContext_DrawBitmap, 779).
+-define(wxGraphicsContext_DrawEllipse, 780).
+-define(wxGraphicsContext_DrawIcon, 781).
+-define(wxGraphicsContext_DrawLines, 782).
+-define(wxGraphicsContext_DrawPath, 783).
+-define(wxGraphicsContext_DrawRectangle, 784).
+-define(wxGraphicsContext_DrawRoundedRectangle, 785).
+-define(wxGraphicsContext_DrawText_3, 786).
+-define(wxGraphicsContext_DrawText_4_0, 787).
+-define(wxGraphicsContext_DrawText_4_1, 788).
+-define(wxGraphicsContext_DrawText_5, 789).
+-define(wxGraphicsContext_FillPath, 790).
+-define(wxGraphicsContext_StrokePath, 791).
+-define(wxGraphicsContext_GetPartialTextExtents, 792).
+-define(wxGraphicsContext_GetTextExtent, 793).
+-define(wxGraphicsContext_Rotate, 794).
+-define(wxGraphicsContext_Scale, 795).
+-define(wxGraphicsContext_Translate, 796).
+-define(wxGraphicsContext_GetTransform, 797).
+-define(wxGraphicsContext_SetTransform, 798).
+-define(wxGraphicsContext_ConcatTransform, 799).
+-define(wxGraphicsContext_SetBrush_1_1, 800).
+-define(wxGraphicsContext_SetBrush_1_0, 801).
+-define(wxGraphicsContext_SetFont_1, 802).
+-define(wxGraphicsContext_SetFont_2, 803).
+-define(wxGraphicsContext_SetPen_1_0, 804).
+-define(wxGraphicsContext_SetPen_1_1, 805).
+-define(wxGraphicsContext_StrokeLine, 806).
+-define(wxGraphicsContext_StrokeLines, 807).
+-define(wxGraphicsMatrix_Concat, 809).
+-define(wxGraphicsMatrix_Get, 811).
+-define(wxGraphicsMatrix_Invert, 812).
+-define(wxGraphicsMatrix_IsEqual, 813).
+-define(wxGraphicsMatrix_IsIdentity, 815).
+-define(wxGraphicsMatrix_Rotate, 816).
+-define(wxGraphicsMatrix_Scale, 817).
+-define(wxGraphicsMatrix_Translate, 818).
+-define(wxGraphicsMatrix_Set, 819).
+-define(wxGraphicsMatrix_TransformPoint, 820).
+-define(wxGraphicsMatrix_TransformDistance, 821).
+-define(wxGraphicsPath_MoveToPoint_2, 822).
+-define(wxGraphicsPath_MoveToPoint_1, 823).
+-define(wxGraphicsPath_AddArc_6, 824).
+-define(wxGraphicsPath_AddArc_5, 825).
+-define(wxGraphicsPath_AddArcToPoint, 826).
+-define(wxGraphicsPath_AddCircle, 827).
+-define(wxGraphicsPath_AddCurveToPoint_6, 828).
+-define(wxGraphicsPath_AddCurveToPoint_3, 829).
+-define(wxGraphicsPath_AddEllipse, 830).
+-define(wxGraphicsPath_AddLineToPoint_2, 831).
+-define(wxGraphicsPath_AddLineToPoint_1, 832).
+-define(wxGraphicsPath_AddPath, 833).
+-define(wxGraphicsPath_AddQuadCurveToPoint, 834).
+-define(wxGraphicsPath_AddRectangle, 835).
+-define(wxGraphicsPath_AddRoundedRectangle, 836).
+-define(wxGraphicsPath_CloseSubpath, 837).
+-define(wxGraphicsPath_Contains_3, 838).
+-define(wxGraphicsPath_Contains_2, 839).
+-define(wxGraphicsPath_GetBox, 841).
+-define(wxGraphicsPath_GetCurrentPoint, 843).
+-define(wxGraphicsPath_Transform, 844).
+-define(wxGraphicsRenderer_GetDefaultRenderer, 845).
+-define(wxGraphicsRenderer_CreateContext_1_1, 846).
+-define(wxGraphicsRenderer_CreateContext_1_0, 847).
+-define(wxGraphicsRenderer_CreatePen, 848).
+-define(wxGraphicsRenderer_CreateBrush, 849).
+-define(wxGraphicsRenderer_CreateLinearGradientBrush, 850).
+-define(wxGraphicsRenderer_CreateRadialGradientBrush, 851).
+-define(wxGraphicsRenderer_CreateFont, 852).
+-define(wxGraphicsRenderer_CreateMatrix, 853).
+-define(wxGraphicsRenderer_CreatePath, 854).
+-define(wxMenuBar_new_1, 856).
+-define(wxMenuBar_new_0, 858).
+-define(wxMenuBar_destruct, 860).
+-define(wxMenuBar_Append, 861).
+-define(wxMenuBar_Check, 862).
+-define(wxMenuBar_Enable_2, 863).
+-define(wxMenuBar_Enable_1, 864).
+-define(wxMenuBar_EnableTop, 865).
+-define(wxMenuBar_FindMenu, 866).
+-define(wxMenuBar_FindMenuItem, 867).
+-define(wxMenuBar_FindItem, 868).
+-define(wxMenuBar_GetHelpString, 869).
+-define(wxMenuBar_GetLabel_1, 870).
+-define(wxMenuBar_GetLabel_0, 871).
+-define(wxMenuBar_GetLabelTop, 872).
+-define(wxMenuBar_GetMenu, 873).
+-define(wxMenuBar_GetMenuCount, 874).
+-define(wxMenuBar_Insert, 875).
+-define(wxMenuBar_IsChecked, 876).
+-define(wxMenuBar_IsEnabled_1, 877).
+-define(wxMenuBar_IsEnabled_0, 878).
+-define(wxMenuBar_Remove, 879).
+-define(wxMenuBar_Replace, 880).
+-define(wxMenuBar_SetHelpString, 881).
+-define(wxMenuBar_SetLabel_2, 882).
+-define(wxMenuBar_SetLabel_1, 883).
+-define(wxMenuBar_SetLabelTop, 884).
+-define(wxControl_GetLabel, 885).
+-define(wxControl_SetLabel, 886).
+-define(wxControlWithItems_Append_1, 887).
+-define(wxControlWithItems_Append_2, 888).
+-define(wxControlWithItems_appendStrings_1, 889).
+-define(wxControlWithItems_Clear, 890).
+-define(wxControlWithItems_Delete, 891).
+-define(wxControlWithItems_FindString, 892).
+-define(wxControlWithItems_getClientData, 893).
+-define(wxControlWithItems_setClientData, 894).
+-define(wxControlWithItems_GetCount, 895).
+-define(wxControlWithItems_GetSelection, 896).
+-define(wxControlWithItems_GetString, 897).
+-define(wxControlWithItems_GetStringSelection, 898).
+-define(wxControlWithItems_Insert_2, 899).
+-define(wxControlWithItems_Insert_3, 900).
+-define(wxControlWithItems_IsEmpty, 901).
+-define(wxControlWithItems_Select, 902).
+-define(wxControlWithItems_SetSelection, 903).
+-define(wxControlWithItems_SetString, 904).
+-define(wxControlWithItems_SetStringSelection, 905).
+-define(wxMenu_new_2, 908).
+-define(wxMenu_new_1, 909).
+-define(wxMenu_destruct, 911).
+-define(wxMenu_Append_3, 912).
+-define(wxMenu_Append_1, 913).
+-define(wxMenu_Append_4_0, 914).
+-define(wxMenu_Append_4_1, 915).
+-define(wxMenu_AppendCheckItem, 916).
+-define(wxMenu_AppendRadioItem, 917).
+-define(wxMenu_AppendSeparator, 918).
+-define(wxMenu_Break, 919).
+-define(wxMenu_Check, 920).
+-define(wxMenu_Delete_1_0, 921).
+-define(wxMenu_Delete_1_1, 922).
+-define(wxMenu_Destroy_1_0, 923).
+-define(wxMenu_Destroy_1_1, 924).
+-define(wxMenu_Enable, 925).
+-define(wxMenu_FindItem_1, 926).
+-define(wxMenu_FindItem_2, 927).
+-define(wxMenu_FindItemByPosition, 928).
+-define(wxMenu_GetHelpString, 929).
+-define(wxMenu_GetLabel, 930).
+-define(wxMenu_GetMenuItemCount, 931).
+-define(wxMenu_GetMenuItems, 932).
+-define(wxMenu_GetTitle, 934).
+-define(wxMenu_Insert_2, 935).
+-define(wxMenu_Insert_3, 936).
+-define(wxMenu_Insert_5_1, 937).
+-define(wxMenu_Insert_5_0, 938).
+-define(wxMenu_InsertCheckItem, 939).
+-define(wxMenu_InsertRadioItem, 940).
+-define(wxMenu_InsertSeparator, 941).
+-define(wxMenu_IsChecked, 942).
+-define(wxMenu_IsEnabled, 943).
+-define(wxMenu_Prepend_1, 944).
+-define(wxMenu_Prepend_2, 945).
+-define(wxMenu_Prepend_4_1, 946).
+-define(wxMenu_Prepend_4_0, 947).
+-define(wxMenu_PrependCheckItem, 948).
+-define(wxMenu_PrependRadioItem, 949).
+-define(wxMenu_PrependSeparator, 950).
+-define(wxMenu_Remove_1_0, 951).
+-define(wxMenu_Remove_1_1, 952).
+-define(wxMenu_SetHelpString, 953).
+-define(wxMenu_SetLabel, 954).
+-define(wxMenu_SetTitle, 955).
+-define(wxMenuItem_new, 956).
+-define(wxMenuItem_destruct, 958).
+-define(wxMenuItem_Check, 959).
+-define(wxMenuItem_Enable, 960).
+-define(wxMenuItem_GetBitmap, 961).
+-define(wxMenuItem_GetHelp, 962).
+-define(wxMenuItem_GetId, 963).
+-define(wxMenuItem_GetKind, 964).
+-define(wxMenuItem_GetLabel, 965).
+-define(wxMenuItem_GetLabelFromText, 966).
+-define(wxMenuItem_GetMenu, 967).
+-define(wxMenuItem_GetText, 968).
+-define(wxMenuItem_GetSubMenu, 969).
+-define(wxMenuItem_IsCheckable, 970).
+-define(wxMenuItem_IsChecked, 971).
+-define(wxMenuItem_IsEnabled, 972).
+-define(wxMenuItem_IsSeparator, 973).
+-define(wxMenuItem_IsSubMenu, 974).
+-define(wxMenuItem_SetBitmap, 975).
+-define(wxMenuItem_SetHelp, 976).
+-define(wxMenuItem_SetMenu, 977).
+-define(wxMenuItem_SetSubMenu, 978).
+-define(wxMenuItem_SetText, 979).
+-define(wxToolBar_AddControl, 980).
+-define(wxToolBar_AddSeparator, 981).
+-define(wxToolBar_AddTool_5, 982).
+-define(wxToolBar_AddTool_4_0, 983).
+-define(wxToolBar_AddTool_1, 984).
+-define(wxToolBar_AddTool_4_1, 985).
+-define(wxToolBar_AddTool_3, 986).
+-define(wxToolBar_AddTool_6, 987).
+-define(wxToolBar_AddCheckTool, 988).
+-define(wxToolBar_AddRadioTool, 989).
+-define(wxToolBar_AddStretchableSpace, 990).
+-define(wxToolBar_InsertStretchableSpace, 991).
+-define(wxToolBar_DeleteTool, 992).
+-define(wxToolBar_DeleteToolByPos, 993).
+-define(wxToolBar_EnableTool, 994).
+-define(wxToolBar_FindById, 995).
+-define(wxToolBar_FindControl, 996).
+-define(wxToolBar_FindToolForPosition, 997).
+-define(wxToolBar_GetToolSize, 998).
+-define(wxToolBar_GetToolBitmapSize, 999).
+-define(wxToolBar_GetMargins, 1000).
+-define(wxToolBar_GetToolEnabled, 1001).
+-define(wxToolBar_GetToolLongHelp, 1002).
+-define(wxToolBar_GetToolPacking, 1003).
+-define(wxToolBar_GetToolPos, 1004).
+-define(wxToolBar_GetToolSeparation, 1005).
+-define(wxToolBar_GetToolShortHelp, 1006).
+-define(wxToolBar_GetToolState, 1007).
+-define(wxToolBar_InsertControl, 1008).
+-define(wxToolBar_InsertSeparator, 1009).
+-define(wxToolBar_InsertTool_5, 1010).
+-define(wxToolBar_InsertTool_2, 1011).
+-define(wxToolBar_InsertTool_4, 1012).
+-define(wxToolBar_Realize, 1013).
+-define(wxToolBar_RemoveTool, 1014).
+-define(wxToolBar_SetMargins, 1015).
+-define(wxToolBar_SetToolBitmapSize, 1016).
+-define(wxToolBar_SetToolLongHelp, 1017).
+-define(wxToolBar_SetToolPacking, 1018).
+-define(wxToolBar_SetToolShortHelp, 1019).
+-define(wxToolBar_SetToolSeparation, 1020).
+-define(wxToolBar_ToggleTool, 1021).
+-define(wxStatusBar_new_0, 1023).
+-define(wxStatusBar_new_2, 1024).
+-define(wxStatusBar_destruct, 1026).
+-define(wxStatusBar_Create, 1027).
+-define(wxStatusBar_GetFieldRect, 1028).
+-define(wxStatusBar_GetFieldsCount, 1029).
+-define(wxStatusBar_GetStatusText, 1030).
+-define(wxStatusBar_PopStatusText, 1031).
+-define(wxStatusBar_PushStatusText, 1032).
+-define(wxStatusBar_SetFieldsCount, 1033).
+-define(wxStatusBar_SetMinHeight, 1034).
+-define(wxStatusBar_SetStatusText, 1035).
+-define(wxStatusBar_SetStatusWidths, 1036).
+-define(wxStatusBar_SetStatusStyles, 1037).
+-define(wxBitmap_new_0, 1038).
+-define(wxBitmap_new_3, 1039).
+-define(wxBitmap_new_4, 1040).
+-define(wxBitmap_new_2_0, 1041).
+-define(wxBitmap_new_2_1, 1042).
+-define(wxBitmap_destruct, 1043).
+-define(wxBitmap_ConvertToImage, 1044).
+-define(wxBitmap_CopyFromIcon, 1045).
+-define(wxBitmap_Create, 1046).
+-define(wxBitmap_GetDepth, 1047).
+-define(wxBitmap_GetHeight, 1048).
+-define(wxBitmap_GetPalette, 1049).
+-define(wxBitmap_GetMask, 1050).
+-define(wxBitmap_GetWidth, 1051).
+-define(wxBitmap_GetSubBitmap, 1052).
+-define(wxBitmap_LoadFile, 1053).
+-define(wxBitmap_Ok, 1054).
+-define(wxBitmap_SaveFile, 1055).
+-define(wxBitmap_SetDepth, 1056).
+-define(wxBitmap_SetHeight, 1057).
+-define(wxBitmap_SetMask, 1058).
+-define(wxBitmap_SetPalette, 1059).
+-define(wxBitmap_SetWidth, 1060).
+-define(wxIcon_new_0, 1061).
+-define(wxIcon_new_2, 1062).
+-define(wxIcon_new_1, 1063).
+-define(wxIcon_CopyFromBitmap, 1064).
+-define(wxIcon_destroy, 1065).
+-define(wxIconBundle_new_0, 1066).
+-define(wxIconBundle_new_2, 1067).
+-define(wxIconBundle_new_1_0, 1068).
+-define(wxIconBundle_new_1_1, 1069).
+-define(wxIconBundle_destruct, 1070).
+-define(wxIconBundle_AddIcon_2, 1071).
+-define(wxIconBundle_AddIcon_1, 1072).
+-define(wxIconBundle_GetIcon_1_1, 1073).
+-define(wxIconBundle_GetIcon_1_0, 1074).
+-define(wxCursor_new_0, 1075).
+-define(wxCursor_new_1_0, 1076).
+-define(wxCursor_new_1_1, 1077).
+-define(wxCursor_new_4, 1078).
+-define(wxCursor_destruct, 1079).
+-define(wxCursor_Ok, 1080).
+-define(wxMask_new_0, 1081).
+-define(wxMask_new_2_1, 1082).
+-define(wxMask_new_2_0, 1083).
+-define(wxMask_new_1, 1084).
+-define(wxMask_destruct, 1085).
+-define(wxMask_Create_2_1, 1086).
+-define(wxMask_Create_2_0, 1087).
+-define(wxMask_Create_1, 1088).
+-define(wxImage_new_0, 1089).
+-define(wxImage_new_3_0, 1090).
+-define(wxImage_new_4, 1091).
+-define(wxImage_new_5, 1092).
+-define(wxImage_new_2, 1093).
+-define(wxImage_new_3_1, 1094).
+-define(wxImage_Blur, 1095).
+-define(wxImage_BlurHorizontal, 1096).
+-define(wxImage_BlurVertical, 1097).
+-define(wxImage_ConvertAlphaToMask, 1098).
+-define(wxImage_ConvertToGreyscale, 1099).
+-define(wxImage_ConvertToMono, 1100).
+-define(wxImage_Copy, 1101).
+-define(wxImage_Create_3, 1102).
+-define(wxImage_Create_4, 1103).
+-define(wxImage_Create_5, 1104).
+-define(wxImage_Destroy, 1105).
+-define(wxImage_FindFirstUnusedColour, 1106).
+-define(wxImage_GetImageExtWildcard, 1107).
+-define(wxImage_GetAlpha_2, 1108).
+-define(wxImage_GetAlpha_0, 1109).
+-define(wxImage_GetBlue, 1110).
+-define(wxImage_GetData, 1111).
+-define(wxImage_GetGreen, 1112).
+-define(wxImage_GetImageCount, 1113).
+-define(wxImage_GetHeight, 1114).
+-define(wxImage_GetMaskBlue, 1115).
+-define(wxImage_GetMaskGreen, 1116).
+-define(wxImage_GetMaskRed, 1117).
+-define(wxImage_GetOrFindMaskColour, 1118).
+-define(wxImage_GetPalette, 1119).
+-define(wxImage_GetRed, 1120).
+-define(wxImage_GetSubImage, 1121).
+-define(wxImage_GetWidth, 1122).
+-define(wxImage_HasAlpha, 1123).
+-define(wxImage_HasMask, 1124).
+-define(wxImage_GetOption, 1125).
+-define(wxImage_GetOptionInt, 1126).
+-define(wxImage_HasOption, 1127).
+-define(wxImage_InitAlpha, 1128).
+-define(wxImage_InitStandardHandlers, 1129).
+-define(wxImage_IsTransparent, 1130).
+-define(wxImage_LoadFile_2, 1131).
+-define(wxImage_LoadFile_3, 1132).
+-define(wxImage_Ok, 1133).
+-define(wxImage_RemoveHandler, 1134).
+-define(wxImage_Mirror, 1135).
+-define(wxImage_Replace, 1136).
+-define(wxImage_Rescale, 1137).
+-define(wxImage_Resize, 1138).
+-define(wxImage_Rotate, 1139).
+-define(wxImage_RotateHue, 1140).
+-define(wxImage_Rotate90, 1141).
+-define(wxImage_SaveFile_1, 1142).
+-define(wxImage_SaveFile_2_0, 1143).
+-define(wxImage_SaveFile_2_1, 1144).
+-define(wxImage_Scale, 1145).
+-define(wxImage_Size, 1146).
+-define(wxImage_SetAlpha_3, 1147).
+-define(wxImage_SetAlpha_2, 1148).
+-define(wxImage_SetData_2, 1149).
+-define(wxImage_SetData_4, 1150).
+-define(wxImage_SetMask, 1151).
+-define(wxImage_SetMaskColour, 1152).
+-define(wxImage_SetMaskFromImage, 1153).
+-define(wxImage_SetOption_2_1, 1154).
+-define(wxImage_SetOption_2_0, 1155).
+-define(wxImage_SetPalette, 1156).
+-define(wxImage_SetRGB_5, 1157).
+-define(wxImage_SetRGB_4, 1158).
+-define(wxImage_destroy, 1159).
+-define(wxBrush_new_0, 1160).
+-define(wxBrush_new_2, 1161).
+-define(wxBrush_new_1, 1162).
+-define(wxBrush_destruct, 1164).
+-define(wxBrush_GetColour, 1165).
+-define(wxBrush_GetStipple, 1166).
+-define(wxBrush_GetStyle, 1167).
+-define(wxBrush_IsHatch, 1168).
+-define(wxBrush_IsOk, 1169).
+-define(wxBrush_SetColour_1, 1170).
+-define(wxBrush_SetColour_3, 1171).
+-define(wxBrush_SetStipple, 1172).
+-define(wxBrush_SetStyle, 1173).
+-define(wxPen_new_0, 1174).
+-define(wxPen_new_2, 1175).
+-define(wxPen_destruct, 1176).
+-define(wxPen_GetCap, 1177).
+-define(wxPen_GetColour, 1178).
+-define(wxPen_GetJoin, 1179).
+-define(wxPen_GetStyle, 1180).
+-define(wxPen_GetWidth, 1181).
+-define(wxPen_IsOk, 1182).
+-define(wxPen_SetCap, 1183).
+-define(wxPen_SetColour_1, 1184).
+-define(wxPen_SetColour_3, 1185).
+-define(wxPen_SetJoin, 1186).
+-define(wxPen_SetStyle, 1187).
+-define(wxPen_SetWidth, 1188).
+-define(wxRegion_new_0, 1189).
+-define(wxRegion_new_4, 1190).
+-define(wxRegion_new_2, 1191).
+-define(wxRegion_new_1_1, 1192).
+-define(wxRegion_new_1_0, 1194).
+-define(wxRegion_destruct, 1196).
+-define(wxRegion_Clear, 1197).
+-define(wxRegion_Contains_2, 1198).
+-define(wxRegion_Contains_1_0, 1199).
+-define(wxRegion_Contains_4, 1200).
+-define(wxRegion_Contains_1_1, 1201).
+-define(wxRegion_ConvertToBitmap, 1202).
+-define(wxRegion_GetBox, 1203).
+-define(wxRegion_Intersect_4, 1204).
+-define(wxRegion_Intersect_1_1, 1205).
+-define(wxRegion_Intersect_1_0, 1206).
+-define(wxRegion_IsEmpty, 1207).
+-define(wxRegion_Subtract_4, 1208).
+-define(wxRegion_Subtract_1_1, 1209).
+-define(wxRegion_Subtract_1_0, 1210).
+-define(wxRegion_Offset_2, 1211).
+-define(wxRegion_Offset_1, 1212).
+-define(wxRegion_Union_4, 1213).
+-define(wxRegion_Union_1_2, 1214).
+-define(wxRegion_Union_1_1, 1215).
+-define(wxRegion_Union_1_0, 1216).
+-define(wxRegion_Union_3, 1217).
+-define(wxRegion_Xor_4, 1218).
+-define(wxRegion_Xor_1_1, 1219).
+-define(wxRegion_Xor_1_0, 1220).
+-define(wxAcceleratorTable_new_0, 1221).
+-define(wxAcceleratorTable_new_2, 1222).
+-define(wxAcceleratorTable_destruct, 1223).
+-define(wxAcceleratorTable_Ok, 1224).
+-define(wxAcceleratorEntry_new_1_0, 1225).
+-define(wxAcceleratorEntry_new_1_1, 1226).
+-define(wxAcceleratorEntry_GetCommand, 1227).
+-define(wxAcceleratorEntry_GetFlags, 1228).
+-define(wxAcceleratorEntry_GetKeyCode, 1229).
+-define(wxAcceleratorEntry_Set, 1230).
+-define(wxAcceleratorEntry_destroy, 1231).
+-define(wxCaret_new_3, 1236).
+-define(wxCaret_new_2, 1237).
+-define(wxCaret_destruct, 1239).
+-define(wxCaret_Create_3, 1240).
+-define(wxCaret_Create_2, 1241).
+-define(wxCaret_GetBlinkTime, 1242).
+-define(wxCaret_GetPosition, 1244).
+-define(wxCaret_GetSize, 1246).
+-define(wxCaret_GetWindow, 1247).
+-define(wxCaret_Hide, 1248).
+-define(wxCaret_IsOk, 1249).
+-define(wxCaret_IsVisible, 1250).
+-define(wxCaret_Move_2, 1251).
+-define(wxCaret_Move_1, 1252).
+-define(wxCaret_SetBlinkTime, 1253).
+-define(wxCaret_SetSize_2, 1254).
+-define(wxCaret_SetSize_1, 1255).
+-define(wxCaret_Show, 1256).
+-define(wxSizer_Add_2_1, 1257).
+-define(wxSizer_Add_2_0, 1258).
+-define(wxSizer_Add_3, 1259).
+-define(wxSizer_Add_2_3, 1260).
+-define(wxSizer_Add_2_2, 1261).
+-define(wxSizer_AddSpacer, 1262).
+-define(wxSizer_AddStretchSpacer, 1263).
+-define(wxSizer_CalcMin, 1264).
+-define(wxSizer_Clear, 1265).
+-define(wxSizer_Detach_1_2, 1266).
+-define(wxSizer_Detach_1_1, 1267).
+-define(wxSizer_Detach_1_0, 1268).
+-define(wxSizer_Fit, 1269).
+-define(wxSizer_FitInside, 1270).
+-define(wxSizer_GetChildren, 1271).
+-define(wxSizer_GetItem_2_1, 1272).
+-define(wxSizer_GetItem_2_0, 1273).
+-define(wxSizer_GetItem_1, 1274).
+-define(wxSizer_GetSize, 1275).
+-define(wxSizer_GetPosition, 1276).
+-define(wxSizer_GetMinSize, 1277).
+-define(wxSizer_Hide_2_0, 1278).
+-define(wxSizer_Hide_2_1, 1279).
+-define(wxSizer_Hide_1, 1280).
+-define(wxSizer_Insert_3_1, 1281).
+-define(wxSizer_Insert_3_0, 1282).
+-define(wxSizer_Insert_4, 1283).
+-define(wxSizer_Insert_3_3, 1284).
+-define(wxSizer_Insert_3_2, 1285).
+-define(wxSizer_Insert_2, 1286).
+-define(wxSizer_InsertSpacer, 1287).
+-define(wxSizer_InsertStretchSpacer, 1288).
+-define(wxSizer_IsShown_1_2, 1289).
+-define(wxSizer_IsShown_1_1, 1290).
+-define(wxSizer_IsShown_1_0, 1291).
+-define(wxSizer_Layout, 1292).
+-define(wxSizer_Prepend_2_1, 1293).
+-define(wxSizer_Prepend_2_0, 1294).
+-define(wxSizer_Prepend_3, 1295).
+-define(wxSizer_Prepend_2_3, 1296).
+-define(wxSizer_Prepend_2_2, 1297).
+-define(wxSizer_Prepend_1, 1298).
+-define(wxSizer_PrependSpacer, 1299).
+-define(wxSizer_PrependStretchSpacer, 1300).
+-define(wxSizer_RecalcSizes, 1301).
+-define(wxSizer_Remove_1_1, 1302).
+-define(wxSizer_Remove_1_0, 1303).
+-define(wxSizer_Replace_3_1, 1304).
+-define(wxSizer_Replace_3_0, 1305).
+-define(wxSizer_Replace_2, 1306).
+-define(wxSizer_SetDimension, 1307).
+-define(wxSizer_SetMinSize_2, 1308).
+-define(wxSizer_SetMinSize_1, 1309).
+-define(wxSizer_SetItemMinSize_3_2, 1310).
+-define(wxSizer_SetItemMinSize_2_2, 1311).
+-define(wxSizer_SetItemMinSize_3_1, 1312).
+-define(wxSizer_SetItemMinSize_2_1, 1313).
+-define(wxSizer_SetItemMinSize_3_0, 1314).
+-define(wxSizer_SetItemMinSize_2_0, 1315).
+-define(wxSizer_SetSizeHints, 1316).
+-define(wxSizer_SetVirtualSizeHints, 1317).
+-define(wxSizer_Show_2_2, 1318).
+-define(wxSizer_Show_2_1, 1319).
+-define(wxSizer_Show_2_0, 1320).
+-define(wxSizer_Show_1, 1321).
+-define(wxSizerFlags_new, 1322).
+-define(wxSizerFlags_Align, 1323).
+-define(wxSizerFlags_Border_2, 1324).
+-define(wxSizerFlags_Border_1, 1325).
+-define(wxSizerFlags_Center, 1326).
+-define(wxSizerFlags_Centre, 1327).
+-define(wxSizerFlags_Expand, 1328).
+-define(wxSizerFlags_Left, 1329).
+-define(wxSizerFlags_Proportion, 1330).
+-define(wxSizerFlags_Right, 1331).
+-define(wxSizerFlags_destroy, 1332).
+-define(wxSizerItem_new_5_1, 1333).
+-define(wxSizerItem_new_2_1, 1334).
+-define(wxSizerItem_new_5_0, 1335).
+-define(wxSizerItem_new_2_0, 1336).
+-define(wxSizerItem_new_6, 1337).
+-define(wxSizerItem_new_3, 1338).
+-define(wxSizerItem_new_0, 1339).
+-define(wxSizerItem_destruct, 1340).
+-define(wxSizerItem_CalcMin, 1341).
+-define(wxSizerItem_DeleteWindows, 1342).
+-define(wxSizerItem_DetachSizer, 1343).
+-define(wxSizerItem_GetBorder, 1344).
+-define(wxSizerItem_GetFlag, 1345).
+-define(wxSizerItem_GetMinSize, 1346).
+-define(wxSizerItem_GetPosition, 1347).
+-define(wxSizerItem_GetProportion, 1348).
+-define(wxSizerItem_GetRatio, 1349).
+-define(wxSizerItem_GetRect, 1350).
+-define(wxSizerItem_GetSize, 1351).
+-define(wxSizerItem_GetSizer, 1352).
+-define(wxSizerItem_GetSpacer, 1353).
+-define(wxSizerItem_GetUserData, 1354).
+-define(wxSizerItem_GetWindow, 1355).
+-define(wxSizerItem_IsSizer, 1356).
+-define(wxSizerItem_IsShown, 1357).
+-define(wxSizerItem_IsSpacer, 1358).
+-define(wxSizerItem_IsWindow, 1359).
+-define(wxSizerItem_SetBorder, 1360).
+-define(wxSizerItem_SetDimension, 1361).
+-define(wxSizerItem_SetFlag, 1362).
+-define(wxSizerItem_SetInitSize, 1363).
+-define(wxSizerItem_SetMinSize_1, 1364).
+-define(wxSizerItem_SetMinSize_2, 1365).
+-define(wxSizerItem_SetProportion, 1366).
+-define(wxSizerItem_SetRatio_2, 1367).
+-define(wxSizerItem_SetRatio_1_1, 1368).
+-define(wxSizerItem_SetRatio_1_0, 1369).
+-define(wxSizerItem_SetSizer, 1370).
+-define(wxSizerItem_SetSpacer_1, 1371).
+-define(wxSizerItem_SetSpacer_2, 1372).
+-define(wxSizerItem_SetWindow, 1373).
+-define(wxSizerItem_Show, 1374).
+-define(wxBoxSizer_new, 1375).
+-define(wxBoxSizer_GetOrientation, 1376).
+-define(wxBoxSizer_destroy, 1377).
+-define(wxStaticBoxSizer_new_2, 1378).
+-define(wxStaticBoxSizer_new_3, 1379).
+-define(wxStaticBoxSizer_GetStaticBox, 1380).
+-define(wxStaticBoxSizer_destroy, 1381).
+-define(wxGridSizer_new_4, 1382).
+-define(wxGridSizer_new_2, 1383).
+-define(wxGridSizer_GetCols, 1384).
+-define(wxGridSizer_GetHGap, 1385).
+-define(wxGridSizer_GetRows, 1386).
+-define(wxGridSizer_GetVGap, 1387).
+-define(wxGridSizer_SetCols, 1388).
+-define(wxGridSizer_SetHGap, 1389).
+-define(wxGridSizer_SetRows, 1390).
+-define(wxGridSizer_SetVGap, 1391).
+-define(wxGridSizer_destroy, 1392).
+-define(wxFlexGridSizer_new_4, 1393).
+-define(wxFlexGridSizer_new_2, 1394).
+-define(wxFlexGridSizer_AddGrowableCol, 1395).
+-define(wxFlexGridSizer_AddGrowableRow, 1396).
+-define(wxFlexGridSizer_GetFlexibleDirection, 1397).
+-define(wxFlexGridSizer_GetNonFlexibleGrowMode, 1398).
+-define(wxFlexGridSizer_RemoveGrowableCol, 1399).
+-define(wxFlexGridSizer_RemoveGrowableRow, 1400).
+-define(wxFlexGridSizer_SetFlexibleDirection, 1401).
+-define(wxFlexGridSizer_SetNonFlexibleGrowMode, 1402).
+-define(wxFlexGridSizer_destroy, 1403).
+-define(wxGridBagSizer_new, 1404).
+-define(wxGridBagSizer_Add_3_2, 1405).
+-define(wxGridBagSizer_Add_3_1, 1406).
+-define(wxGridBagSizer_Add_4, 1407).
+-define(wxGridBagSizer_Add_1_0, 1408).
+-define(wxGridBagSizer_Add_2_1, 1409).
+-define(wxGridBagSizer_Add_2_0, 1410).
+-define(wxGridBagSizer_Add_3_0, 1411).
+-define(wxGridBagSizer_Add_1_1, 1412).
+-define(wxGridBagSizer_CalcMin, 1413).
+-define(wxGridBagSizer_CheckForIntersection_2, 1414).
+-define(wxGridBagSizer_CheckForIntersection_3, 1415).
+-define(wxGridBagSizer_FindItem_1_1, 1416).
+-define(wxGridBagSizer_FindItem_1_0, 1417).
+-define(wxGridBagSizer_FindItemAtPoint, 1418).
+-define(wxGridBagSizer_FindItemAtPosition, 1419).
+-define(wxGridBagSizer_FindItemWithData, 1420).
+-define(wxGridBagSizer_GetCellSize, 1421).
+-define(wxGridBagSizer_GetEmptyCellSize, 1422).
+-define(wxGridBagSizer_GetItemPosition_1_2, 1423).
+-define(wxGridBagSizer_GetItemPosition_1_1, 1424).
+-define(wxGridBagSizer_GetItemPosition_1_0, 1425).
+-define(wxGridBagSizer_GetItemSpan_1_2, 1426).
+-define(wxGridBagSizer_GetItemSpan_1_1, 1427).
+-define(wxGridBagSizer_GetItemSpan_1_0, 1428).
+-define(wxGridBagSizer_SetEmptyCellSize, 1429).
+-define(wxGridBagSizer_SetItemPosition_2_2, 1430).
+-define(wxGridBagSizer_SetItemPosition_2_1, 1431).
+-define(wxGridBagSizer_SetItemPosition_2_0, 1432).
+-define(wxGridBagSizer_SetItemSpan_2_2, 1433).
+-define(wxGridBagSizer_SetItemSpan_2_1, 1434).
+-define(wxGridBagSizer_SetItemSpan_2_0, 1435).
+-define(wxGridBagSizer_destroy, 1436).
+-define(wxStdDialogButtonSizer_new, 1437).
+-define(wxStdDialogButtonSizer_AddButton, 1438).
+-define(wxStdDialogButtonSizer_Realize, 1439).
+-define(wxStdDialogButtonSizer_SetAffirmativeButton, 1440).
+-define(wxStdDialogButtonSizer_SetCancelButton, 1441).
+-define(wxStdDialogButtonSizer_SetNegativeButton, 1442).
+-define(wxStdDialogButtonSizer_destroy, 1443).
+-define(wxFont_new_0, 1444).
+-define(wxFont_new_1, 1445).
+-define(wxFont_new_5, 1446).
+-define(wxFont_destruct, 1448).
+-define(wxFont_IsFixedWidth, 1449).
+-define(wxFont_GetDefaultEncoding, 1450).
+-define(wxFont_GetFaceName, 1451).
+-define(wxFont_GetFamily, 1452).
+-define(wxFont_GetNativeFontInfoDesc, 1453).
+-define(wxFont_GetNativeFontInfoUserDesc, 1454).
+-define(wxFont_GetPointSize, 1455).
+-define(wxFont_GetStyle, 1456).
+-define(wxFont_GetUnderlined, 1457).
+-define(wxFont_GetWeight, 1458).
+-define(wxFont_Ok, 1459).
+-define(wxFont_SetDefaultEncoding, 1460).
+-define(wxFont_SetFaceName, 1461).
+-define(wxFont_SetFamily, 1462).
+-define(wxFont_SetPointSize, 1463).
+-define(wxFont_SetStyle, 1464).
+-define(wxFont_SetUnderlined, 1465).
+-define(wxFont_SetWeight, 1466).
+-define(wxToolTip_Enable, 1467).
+-define(wxToolTip_SetDelay, 1468).
+-define(wxToolTip_new, 1469).
+-define(wxToolTip_SetTip, 1470).
+-define(wxToolTip_GetTip, 1471).
+-define(wxToolTip_GetWindow, 1472).
+-define(wxToolTip_destroy, 1473).
+-define(wxButton_new_3, 1475).
+-define(wxButton_new_0, 1476).
+-define(wxButton_destruct, 1477).
+-define(wxButton_Create, 1478).
+-define(wxButton_GetDefaultSize, 1479).
+-define(wxButton_SetDefault, 1480).
+-define(wxButton_SetLabel, 1481).
+-define(wxBitmapButton_new_4, 1483).
+-define(wxBitmapButton_new_0, 1484).
+-define(wxBitmapButton_Create, 1485).
+-define(wxBitmapButton_GetBitmapDisabled, 1486).
+-define(wxBitmapButton_GetBitmapFocus, 1488).
+-define(wxBitmapButton_GetBitmapLabel, 1490).
+-define(wxBitmapButton_GetBitmapSelected, 1492).
+-define(wxBitmapButton_SetBitmapDisabled, 1494).
+-define(wxBitmapButton_SetBitmapFocus, 1495).
+-define(wxBitmapButton_SetBitmapLabel, 1496).
+-define(wxBitmapButton_SetBitmapSelected, 1497).
+-define(wxBitmapButton_destroy, 1498).
+-define(wxToggleButton_new_0, 1499).
+-define(wxToggleButton_new_4, 1500).
+-define(wxToggleButton_Create, 1501).
+-define(wxToggleButton_GetValue, 1502).
+-define(wxToggleButton_SetValue, 1503).
+-define(wxToggleButton_destroy, 1504).
+-define(wxCalendarCtrl_new_0, 1505).
+-define(wxCalendarCtrl_new_3, 1506).
+-define(wxCalendarCtrl_Create, 1507).
+-define(wxCalendarCtrl_destruct, 1508).
+-define(wxCalendarCtrl_SetDate, 1509).
+-define(wxCalendarCtrl_GetDate, 1510).
+-define(wxCalendarCtrl_EnableYearChange, 1511).
+-define(wxCalendarCtrl_EnableMonthChange, 1512).
+-define(wxCalendarCtrl_EnableHolidayDisplay, 1513).
+-define(wxCalendarCtrl_SetHeaderColours, 1514).
+-define(wxCalendarCtrl_GetHeaderColourFg, 1515).
+-define(wxCalendarCtrl_GetHeaderColourBg, 1516).
+-define(wxCalendarCtrl_SetHighlightColours, 1517).
+-define(wxCalendarCtrl_GetHighlightColourFg, 1518).
+-define(wxCalendarCtrl_GetHighlightColourBg, 1519).
+-define(wxCalendarCtrl_SetHolidayColours, 1520).
+-define(wxCalendarCtrl_GetHolidayColourFg, 1521).
+-define(wxCalendarCtrl_GetHolidayColourBg, 1522).
+-define(wxCalendarCtrl_GetAttr, 1523).
+-define(wxCalendarCtrl_SetAttr, 1524).
+-define(wxCalendarCtrl_SetHoliday, 1525).
+-define(wxCalendarCtrl_ResetAttr, 1526).
+-define(wxCalendarCtrl_HitTest, 1527).
+-define(wxCalendarDateAttr_new_0, 1528).
+-define(wxCalendarDateAttr_new_2_1, 1529).
+-define(wxCalendarDateAttr_new_2_0, 1530).
+-define(wxCalendarDateAttr_SetTextColour, 1531).
+-define(wxCalendarDateAttr_SetBackgroundColour, 1532).
+-define(wxCalendarDateAttr_SetBorderColour, 1533).
+-define(wxCalendarDateAttr_SetFont, 1534).
+-define(wxCalendarDateAttr_SetBorder, 1535).
+-define(wxCalendarDateAttr_SetHoliday, 1536).
+-define(wxCalendarDateAttr_HasTextColour, 1537).
+-define(wxCalendarDateAttr_HasBackgroundColour, 1538).
+-define(wxCalendarDateAttr_HasBorderColour, 1539).
+-define(wxCalendarDateAttr_HasFont, 1540).
+-define(wxCalendarDateAttr_HasBorder, 1541).
+-define(wxCalendarDateAttr_IsHoliday, 1542).
+-define(wxCalendarDateAttr_GetTextColour, 1543).
+-define(wxCalendarDateAttr_GetBackgroundColour, 1544).
+-define(wxCalendarDateAttr_GetBorderColour, 1545).
+-define(wxCalendarDateAttr_GetFont, 1546).
+-define(wxCalendarDateAttr_GetBorder, 1547).
+-define(wxCalendarDateAttr_destroy, 1548).
+-define(wxCheckBox_new_4, 1550).
+-define(wxCheckBox_new_0, 1551).
+-define(wxCheckBox_Create, 1552).
+-define(wxCheckBox_GetValue, 1553).
+-define(wxCheckBox_Get3StateValue, 1554).
+-define(wxCheckBox_Is3rdStateAllowedForUser, 1555).
+-define(wxCheckBox_Is3State, 1556).
+-define(wxCheckBox_IsChecked, 1557).
+-define(wxCheckBox_SetValue, 1558).
+-define(wxCheckBox_Set3StateValue, 1559).
+-define(wxCheckBox_destroy, 1560).
+-define(wxCheckListBox_new_0, 1561).
+-define(wxCheckListBox_new_3, 1563).
+-define(wxCheckListBox_Check, 1564).
+-define(wxCheckListBox_IsChecked, 1565).
+-define(wxCheckListBox_destroy, 1566).
+-define(wxChoice_new_3, 1569).
+-define(wxChoice_new_0, 1570).
+-define(wxChoice_destruct, 1572).
+-define(wxChoice_Create, 1574).
+-define(wxChoice_Delete, 1575).
+-define(wxChoice_GetColumns, 1576).
+-define(wxChoice_SetColumns, 1577).
+-define(wxComboBox_new_0, 1578).
+-define(wxComboBox_new_3, 1580).
+-define(wxComboBox_destruct, 1581).
+-define(wxComboBox_Create, 1583).
+-define(wxComboBox_CanCopy, 1584).
+-define(wxComboBox_CanCut, 1585).
+-define(wxComboBox_CanPaste, 1586).
+-define(wxComboBox_CanRedo, 1587).
+-define(wxComboBox_CanUndo, 1588).
+-define(wxComboBox_Copy, 1589).
+-define(wxComboBox_Cut, 1590).
+-define(wxComboBox_GetInsertionPoint, 1591).
+-define(wxComboBox_GetLastPosition, 1592).
+-define(wxComboBox_GetValue, 1593).
+-define(wxComboBox_Paste, 1594).
+-define(wxComboBox_Redo, 1595).
+-define(wxComboBox_Replace, 1596).
+-define(wxComboBox_Remove, 1597).
+-define(wxComboBox_SetInsertionPoint, 1598).
+-define(wxComboBox_SetInsertionPointEnd, 1599).
+-define(wxComboBox_SetSelection_1, 1600).
+-define(wxComboBox_SetSelection_2, 1601).
+-define(wxComboBox_SetValue, 1602).
+-define(wxComboBox_Undo, 1603).
+-define(wxGauge_new_0, 1604).
+-define(wxGauge_new_4, 1605).
+-define(wxGauge_Create, 1606).
+-define(wxGauge_GetRange, 1607).
+-define(wxGauge_GetValue, 1608).
+-define(wxGauge_IsVertical, 1609).
+-define(wxGauge_SetRange, 1610).
+-define(wxGauge_SetValue, 1611).
+-define(wxGauge_Pulse, 1612).
+-define(wxGauge_destroy, 1613).
+-define(wxGenericDirCtrl_new_0, 1614).
+-define(wxGenericDirCtrl_new_2, 1615).
+-define(wxGenericDirCtrl_destruct, 1616).
+-define(wxGenericDirCtrl_Create, 1617).
+-define(wxGenericDirCtrl_Init, 1618).
+-define(wxGenericDirCtrl_CollapseTree, 1619).
+-define(wxGenericDirCtrl_ExpandPath, 1620).
+-define(wxGenericDirCtrl_GetDefaultPath, 1621).
+-define(wxGenericDirCtrl_GetPath, 1622).
+-define(wxGenericDirCtrl_GetFilePath, 1623).
+-define(wxGenericDirCtrl_GetFilter, 1624).
+-define(wxGenericDirCtrl_GetFilterIndex, 1625).
+-define(wxGenericDirCtrl_GetRootId, 1626).
+-define(wxGenericDirCtrl_GetTreeCtrl, 1627).
+-define(wxGenericDirCtrl_ReCreateTree, 1628).
+-define(wxGenericDirCtrl_SetDefaultPath, 1629).
+-define(wxGenericDirCtrl_SetFilter, 1630).
+-define(wxGenericDirCtrl_SetFilterIndex, 1631).
+-define(wxGenericDirCtrl_SetPath, 1632).
+-define(wxStaticBox_new_4, 1634).
+-define(wxStaticBox_new_0, 1635).
+-define(wxStaticBox_Create, 1636).
+-define(wxStaticBox_destroy, 1637).
+-define(wxStaticLine_new_2, 1639).
+-define(wxStaticLine_new_0, 1640).
+-define(wxStaticLine_Create, 1641).
+-define(wxStaticLine_IsVertical, 1642).
+-define(wxStaticLine_GetDefaultSize, 1643).
+-define(wxStaticLine_destroy, 1644).
+-define(wxListBox_new_3, 1647).
+-define(wxListBox_new_0, 1648).
+-define(wxListBox_destruct, 1650).
+-define(wxListBox_Create, 1652).
+-define(wxListBox_Deselect, 1653).
+-define(wxListBox_GetSelections, 1654).
+-define(wxListBox_InsertItems, 1655).
+-define(wxListBox_IsSelected, 1656).
+-define(wxListBox_Set, 1657).
+-define(wxListBox_HitTest, 1658).
+-define(wxListBox_SetFirstItem_1_0, 1659).
+-define(wxListBox_SetFirstItem_1_1, 1660).
+-define(wxListCtrl_new_0, 1661).
+-define(wxListCtrl_new_2, 1662).
+-define(wxListCtrl_Arrange, 1663).
+-define(wxListCtrl_AssignImageList, 1664).
+-define(wxListCtrl_ClearAll, 1665).
+-define(wxListCtrl_Create, 1666).
+-define(wxListCtrl_DeleteAllItems, 1667).
+-define(wxListCtrl_DeleteColumn, 1668).
+-define(wxListCtrl_DeleteItem, 1669).
+-define(wxListCtrl_EditLabel, 1670).
+-define(wxListCtrl_EnsureVisible, 1671).
+-define(wxListCtrl_FindItem_3_0, 1672).
+-define(wxListCtrl_FindItem_3_1, 1673).
+-define(wxListCtrl_GetColumn, 1674).
+-define(wxListCtrl_GetColumnCount, 1675).
+-define(wxListCtrl_GetColumnWidth, 1676).
+-define(wxListCtrl_GetCountPerPage, 1677).
+-define(wxListCtrl_GetEditControl, 1678).
+-define(wxListCtrl_GetImageList, 1679).
+-define(wxListCtrl_GetItem, 1680).
+-define(wxListCtrl_GetItemBackgroundColour, 1681).
+-define(wxListCtrl_GetItemCount, 1682).
+-define(wxListCtrl_GetItemData, 1683).
+-define(wxListCtrl_GetItemFont, 1684).
+-define(wxListCtrl_GetItemPosition, 1685).
+-define(wxListCtrl_GetItemRect, 1686).
+-define(wxListCtrl_GetItemSpacing, 1687).
+-define(wxListCtrl_GetItemState, 1688).
+-define(wxListCtrl_GetItemText, 1689).
+-define(wxListCtrl_GetItemTextColour, 1690).
+-define(wxListCtrl_GetNextItem, 1691).
+-define(wxListCtrl_GetSelectedItemCount, 1692).
+-define(wxListCtrl_GetTextColour, 1693).
+-define(wxListCtrl_GetTopItem, 1694).
+-define(wxListCtrl_GetViewRect, 1695).
+-define(wxListCtrl_HitTest, 1696).
+-define(wxListCtrl_InsertColumn_2, 1697).
+-define(wxListCtrl_InsertColumn_3, 1698).
+-define(wxListCtrl_InsertItem_1, 1699).
+-define(wxListCtrl_InsertItem_2_1, 1700).
+-define(wxListCtrl_InsertItem_2_0, 1701).
+-define(wxListCtrl_InsertItem_3, 1702).
+-define(wxListCtrl_RefreshItem, 1703).
+-define(wxListCtrl_RefreshItems, 1704).
+-define(wxListCtrl_ScrollList, 1705).
+-define(wxListCtrl_SetBackgroundColour, 1706).
+-define(wxListCtrl_SetColumn, 1707).
+-define(wxListCtrl_SetColumnWidth, 1708).
+-define(wxListCtrl_SetImageList, 1709).
+-define(wxListCtrl_SetItem_1, 1710).
+-define(wxListCtrl_SetItem_4, 1711).
+-define(wxListCtrl_SetItemBackgroundColour, 1712).
+-define(wxListCtrl_SetItemCount, 1713).
+-define(wxListCtrl_SetItemData, 1714).
+-define(wxListCtrl_SetItemFont, 1715).
+-define(wxListCtrl_SetItemImage, 1716).
+-define(wxListCtrl_SetItemColumnImage, 1717).
+-define(wxListCtrl_SetItemPosition, 1718).
+-define(wxListCtrl_SetItemState, 1719).
+-define(wxListCtrl_SetItemText, 1720).
+-define(wxListCtrl_SetItemTextColour, 1721).
+-define(wxListCtrl_SetSingleStyle, 1722).
+-define(wxListCtrl_SetTextColour, 1723).
+-define(wxListCtrl_SetWindowStyleFlag, 1724).
+-define(wxListCtrl_SortItems, 1725).
+-define(wxListCtrl_destroy, 1726).
+-define(wxListView_ClearColumnImage, 1727).
+-define(wxListView_Focus, 1728).
+-define(wxListView_GetFirstSelected, 1729).
+-define(wxListView_GetFocusedItem, 1730).
+-define(wxListView_GetNextSelected, 1731).
+-define(wxListView_IsSelected, 1732).
+-define(wxListView_Select, 1733).
+-define(wxListView_SetColumnImage, 1734).
+-define(wxListItem_new_0, 1735).
+-define(wxListItem_new_1, 1736).
+-define(wxListItem_destruct, 1737).
+-define(wxListItem_Clear, 1738).
+-define(wxListItem_GetAlign, 1739).
+-define(wxListItem_GetBackgroundColour, 1740).
+-define(wxListItem_GetColumn, 1741).
+-define(wxListItem_GetFont, 1742).
+-define(wxListItem_GetId, 1743).
+-define(wxListItem_GetImage, 1744).
+-define(wxListItem_GetMask, 1745).
+-define(wxListItem_GetState, 1746).
+-define(wxListItem_GetText, 1747).
+-define(wxListItem_GetTextColour, 1748).
+-define(wxListItem_GetWidth, 1749).
+-define(wxListItem_SetAlign, 1750).
+-define(wxListItem_SetBackgroundColour, 1751).
+-define(wxListItem_SetColumn, 1752).
+-define(wxListItem_SetFont, 1753).
+-define(wxListItem_SetId, 1754).
+-define(wxListItem_SetImage, 1755).
+-define(wxListItem_SetMask, 1756).
+-define(wxListItem_SetState, 1757).
+-define(wxListItem_SetStateMask, 1758).
+-define(wxListItem_SetText, 1759).
+-define(wxListItem_SetTextColour, 1760).
+-define(wxListItem_SetWidth, 1761).
+-define(wxListItemAttr_new_0, 1762).
+-define(wxListItemAttr_new_3, 1763).
+-define(wxListItemAttr_GetBackgroundColour, 1764).
+-define(wxListItemAttr_GetFont, 1765).
+-define(wxListItemAttr_GetTextColour, 1766).
+-define(wxListItemAttr_HasBackgroundColour, 1767).
+-define(wxListItemAttr_HasFont, 1768).
+-define(wxListItemAttr_HasTextColour, 1769).
+-define(wxListItemAttr_SetBackgroundColour, 1770).
+-define(wxListItemAttr_SetFont, 1771).
+-define(wxListItemAttr_SetTextColour, 1772).
+-define(wxListItemAttr_destroy, 1773).
+-define(wxImageList_new_0, 1774).
+-define(wxImageList_new_3, 1775).
+-define(wxImageList_Add_1, 1776).
+-define(wxImageList_Add_2_0, 1777).
+-define(wxImageList_Add_2_1, 1778).
+-define(wxImageList_Create, 1779).
+-define(wxImageList_Draw, 1781).
+-define(wxImageList_GetBitmap, 1782).
+-define(wxImageList_GetIcon, 1783).
+-define(wxImageList_GetImageCount, 1784).
+-define(wxImageList_GetSize, 1785).
+-define(wxImageList_Remove, 1786).
+-define(wxImageList_RemoveAll, 1787).
+-define(wxImageList_Replace_2, 1788).
+-define(wxImageList_Replace_3, 1789).
+-define(wxImageList_destroy, 1790).
+-define(wxTextAttr_new_0, 1791).
+-define(wxTextAttr_new_2, 1792).
+-define(wxTextAttr_GetAlignment, 1793).
+-define(wxTextAttr_GetBackgroundColour, 1794).
+-define(wxTextAttr_GetFont, 1795).
+-define(wxTextAttr_GetLeftIndent, 1796).
+-define(wxTextAttr_GetLeftSubIndent, 1797).
+-define(wxTextAttr_GetRightIndent, 1798).
+-define(wxTextAttr_GetTabs, 1799).
+-define(wxTextAttr_GetTextColour, 1800).
+-define(wxTextAttr_HasBackgroundColour, 1801).
+-define(wxTextAttr_HasFont, 1802).
+-define(wxTextAttr_HasTextColour, 1803).
+-define(wxTextAttr_GetFlags, 1804).
+-define(wxTextAttr_IsDefault, 1805).
+-define(wxTextAttr_SetAlignment, 1806).
+-define(wxTextAttr_SetBackgroundColour, 1807).
+-define(wxTextAttr_SetFlags, 1808).
+-define(wxTextAttr_SetFont, 1809).
+-define(wxTextAttr_SetLeftIndent, 1810).
+-define(wxTextAttr_SetRightIndent, 1811).
+-define(wxTextAttr_SetTabs, 1812).
+-define(wxTextAttr_SetTextColour, 1813).
+-define(wxTextAttr_destroy, 1814).
+-define(wxTextCtrl_new_3, 1816).
+-define(wxTextCtrl_new_0, 1817).
+-define(wxTextCtrl_destruct, 1819).
+-define(wxTextCtrl_AppendText, 1820).
+-define(wxTextCtrl_CanCopy, 1821).
+-define(wxTextCtrl_CanCut, 1822).
+-define(wxTextCtrl_CanPaste, 1823).
+-define(wxTextCtrl_CanRedo, 1824).
+-define(wxTextCtrl_CanUndo, 1825).
+-define(wxTextCtrl_Clear, 1826).
+-define(wxTextCtrl_Copy, 1827).
+-define(wxTextCtrl_Create, 1828).
+-define(wxTextCtrl_Cut, 1829).
+-define(wxTextCtrl_DiscardEdits, 1830).
+-define(wxTextCtrl_ChangeValue, 1831).
+-define(wxTextCtrl_EmulateKeyPress, 1832).
+-define(wxTextCtrl_GetDefaultStyle, 1833).
+-define(wxTextCtrl_GetInsertionPoint, 1834).
+-define(wxTextCtrl_GetLastPosition, 1835).
+-define(wxTextCtrl_GetLineLength, 1836).
+-define(wxTextCtrl_GetLineText, 1837).
+-define(wxTextCtrl_GetNumberOfLines, 1838).
+-define(wxTextCtrl_GetRange, 1839).
+-define(wxTextCtrl_GetSelection, 1840).
+-define(wxTextCtrl_GetStringSelection, 1841).
+-define(wxTextCtrl_GetStyle, 1842).
+-define(wxTextCtrl_GetValue, 1843).
+-define(wxTextCtrl_IsEditable, 1844).
+-define(wxTextCtrl_IsModified, 1845).
+-define(wxTextCtrl_IsMultiLine, 1846).
+-define(wxTextCtrl_IsSingleLine, 1847).
+-define(wxTextCtrl_LoadFile, 1848).
+-define(wxTextCtrl_MarkDirty, 1849).
+-define(wxTextCtrl_Paste, 1850).
+-define(wxTextCtrl_PositionToXY, 1851).
+-define(wxTextCtrl_Redo, 1852).
+-define(wxTextCtrl_Remove, 1853).
+-define(wxTextCtrl_Replace, 1854).
+-define(wxTextCtrl_SaveFile, 1855).
+-define(wxTextCtrl_SetDefaultStyle, 1856).
+-define(wxTextCtrl_SetEditable, 1857).
+-define(wxTextCtrl_SetInsertionPoint, 1858).
+-define(wxTextCtrl_SetInsertionPointEnd, 1859).
+-define(wxTextCtrl_SetMaxLength, 1861).
+-define(wxTextCtrl_SetSelection, 1862).
+-define(wxTextCtrl_SetStyle, 1863).
+-define(wxTextCtrl_SetValue, 1864).
+-define(wxTextCtrl_ShowPosition, 1865).
+-define(wxTextCtrl_Undo, 1866).
+-define(wxTextCtrl_WriteText, 1867).
+-define(wxTextCtrl_XYToPosition, 1868).
+-define(wxNotebook_new_0, 1871).
+-define(wxNotebook_new_3, 1872).
+-define(wxNotebook_destruct, 1873).
+-define(wxNotebook_AddPage, 1874).
+-define(wxNotebook_AdvanceSelection, 1875).
+-define(wxNotebook_AssignImageList, 1876).
+-define(wxNotebook_Create, 1877).
+-define(wxNotebook_DeleteAllPages, 1878).
+-define(wxNotebook_DeletePage, 1879).
+-define(wxNotebook_RemovePage, 1880).
+-define(wxNotebook_GetCurrentPage, 1881).
+-define(wxNotebook_GetImageList, 1882).
+-define(wxNotebook_GetPage, 1884).
+-define(wxNotebook_GetPageCount, 1885).
+-define(wxNotebook_GetPageImage, 1886).
+-define(wxNotebook_GetPageText, 1887).
+-define(wxNotebook_GetRowCount, 1888).
+-define(wxNotebook_GetSelection, 1889).
+-define(wxNotebook_GetThemeBackgroundColour, 1890).
+-define(wxNotebook_HitTest, 1892).
+-define(wxNotebook_InsertPage, 1894).
+-define(wxNotebook_SetImageList, 1895).
+-define(wxNotebook_SetPadding, 1896).
+-define(wxNotebook_SetPageSize, 1897).
+-define(wxNotebook_SetPageImage, 1898).
+-define(wxNotebook_SetPageText, 1899).
+-define(wxNotebook_SetSelection, 1900).
+-define(wxNotebook_ChangeSelection, 1901).
+-define(wxChoicebook_new_0, 1902).
+-define(wxChoicebook_new_3, 1903).
+-define(wxChoicebook_AddPage, 1904).
+-define(wxChoicebook_AdvanceSelection, 1905).
+-define(wxChoicebook_AssignImageList, 1906).
+-define(wxChoicebook_Create, 1907).
+-define(wxChoicebook_DeleteAllPages, 1908).
+-define(wxChoicebook_DeletePage, 1909).
+-define(wxChoicebook_RemovePage, 1910).
+-define(wxChoicebook_GetCurrentPage, 1911).
+-define(wxChoicebook_GetImageList, 1912).
+-define(wxChoicebook_GetPage, 1914).
+-define(wxChoicebook_GetPageCount, 1915).
+-define(wxChoicebook_GetPageImage, 1916).
+-define(wxChoicebook_GetPageText, 1917).
+-define(wxChoicebook_GetSelection, 1918).
+-define(wxChoicebook_HitTest, 1919).
+-define(wxChoicebook_InsertPage, 1920).
+-define(wxChoicebook_SetImageList, 1921).
+-define(wxChoicebook_SetPageSize, 1922).
+-define(wxChoicebook_SetPageImage, 1923).
+-define(wxChoicebook_SetPageText, 1924).
+-define(wxChoicebook_SetSelection, 1925).
+-define(wxChoicebook_ChangeSelection, 1926).
+-define(wxChoicebook_destroy, 1927).
+-define(wxToolbook_new_0, 1928).
+-define(wxToolbook_new_3, 1929).
+-define(wxToolbook_AddPage, 1930).
+-define(wxToolbook_AdvanceSelection, 1931).
+-define(wxToolbook_AssignImageList, 1932).
+-define(wxToolbook_Create, 1933).
+-define(wxToolbook_DeleteAllPages, 1934).
+-define(wxToolbook_DeletePage, 1935).
+-define(wxToolbook_RemovePage, 1936).
+-define(wxToolbook_GetCurrentPage, 1937).
+-define(wxToolbook_GetImageList, 1938).
+-define(wxToolbook_GetPage, 1940).
+-define(wxToolbook_GetPageCount, 1941).
+-define(wxToolbook_GetPageImage, 1942).
+-define(wxToolbook_GetPageText, 1943).
+-define(wxToolbook_GetSelection, 1944).
+-define(wxToolbook_HitTest, 1946).
+-define(wxToolbook_InsertPage, 1947).
+-define(wxToolbook_SetImageList, 1948).
+-define(wxToolbook_SetPageSize, 1949).
+-define(wxToolbook_SetPageImage, 1950).
+-define(wxToolbook_SetPageText, 1951).
+-define(wxToolbook_SetSelection, 1952).
+-define(wxToolbook_ChangeSelection, 1953).
+-define(wxToolbook_destroy, 1954).
+-define(wxListbook_new_0, 1955).
+-define(wxListbook_new_3, 1956).
+-define(wxListbook_AddPage, 1957).
+-define(wxListbook_AdvanceSelection, 1958).
+-define(wxListbook_AssignImageList, 1959).
+-define(wxListbook_Create, 1960).
+-define(wxListbook_DeleteAllPages, 1961).
+-define(wxListbook_DeletePage, 1962).
+-define(wxListbook_RemovePage, 1963).
+-define(wxListbook_GetCurrentPage, 1964).
+-define(wxListbook_GetImageList, 1965).
+-define(wxListbook_GetPage, 1967).
+-define(wxListbook_GetPageCount, 1968).
+-define(wxListbook_GetPageImage, 1969).
+-define(wxListbook_GetPageText, 1970).
+-define(wxListbook_GetSelection, 1971).
+-define(wxListbook_HitTest, 1973).
+-define(wxListbook_InsertPage, 1974).
+-define(wxListbook_SetImageList, 1975).
+-define(wxListbook_SetPageSize, 1976).
+-define(wxListbook_SetPageImage, 1977).
+-define(wxListbook_SetPageText, 1978).
+-define(wxListbook_SetSelection, 1979).
+-define(wxListbook_ChangeSelection, 1980).
+-define(wxListbook_destroy, 1981).
+-define(wxTreebook_new_0, 1982).
+-define(wxTreebook_new_3, 1983).
+-define(wxTreebook_AddPage, 1984).
+-define(wxTreebook_AdvanceSelection, 1985).
+-define(wxTreebook_AssignImageList, 1986).
+-define(wxTreebook_Create, 1987).
+-define(wxTreebook_DeleteAllPages, 1988).
+-define(wxTreebook_DeletePage, 1989).
+-define(wxTreebook_RemovePage, 1990).
+-define(wxTreebook_GetCurrentPage, 1991).
+-define(wxTreebook_GetImageList, 1992).
+-define(wxTreebook_GetPage, 1994).
+-define(wxTreebook_GetPageCount, 1995).
+-define(wxTreebook_GetPageImage, 1996).
+-define(wxTreebook_GetPageText, 1997).
+-define(wxTreebook_GetSelection, 1998).
+-define(wxTreebook_ExpandNode, 1999).
+-define(wxTreebook_IsNodeExpanded, 2000).
+-define(wxTreebook_HitTest, 2002).
+-define(wxTreebook_InsertPage, 2003).
+-define(wxTreebook_InsertSubPage, 2004).
+-define(wxTreebook_SetImageList, 2005).
+-define(wxTreebook_SetPageSize, 2006).
+-define(wxTreebook_SetPageImage, 2007).
+-define(wxTreebook_SetPageText, 2008).
+-define(wxTreebook_SetSelection, 2009).
+-define(wxTreebook_ChangeSelection, 2010).
+-define(wxTreebook_destroy, 2011).
+-define(wxTreeCtrl_new_2, 2014).
+-define(wxTreeCtrl_new_0, 2015).
+-define(wxTreeCtrl_destruct, 2017).
+-define(wxTreeCtrl_AddRoot, 2018).
+-define(wxTreeCtrl_AppendItem, 2019).
+-define(wxTreeCtrl_AssignImageList, 2020).
+-define(wxTreeCtrl_AssignStateImageList, 2021).
+-define(wxTreeCtrl_Collapse, 2022).
+-define(wxTreeCtrl_CollapseAndReset, 2023).
+-define(wxTreeCtrl_Create, 2024).
+-define(wxTreeCtrl_Delete, 2025).
+-define(wxTreeCtrl_DeleteAllItems, 2026).
+-define(wxTreeCtrl_DeleteChildren, 2027).
+-define(wxTreeCtrl_EditLabel, 2028).
+-define(wxTreeCtrl_EnsureVisible, 2029).
+-define(wxTreeCtrl_Expand, 2030).
+-define(wxTreeCtrl_GetBoundingRect, 2031).
+-define(wxTreeCtrl_GetChildrenCount, 2033).
+-define(wxTreeCtrl_GetCount, 2034).
+-define(wxTreeCtrl_GetEditControl, 2035).
+-define(wxTreeCtrl_GetFirstChild, 2036).
+-define(wxTreeCtrl_GetNextChild, 2037).
+-define(wxTreeCtrl_GetFirstVisibleItem, 2038).
+-define(wxTreeCtrl_GetImageList, 2039).
+-define(wxTreeCtrl_GetIndent, 2040).
+-define(wxTreeCtrl_GetItemBackgroundColour, 2041).
+-define(wxTreeCtrl_GetItemData, 2042).
+-define(wxTreeCtrl_GetItemFont, 2043).
+-define(wxTreeCtrl_GetItemImage_1, 2044).
+-define(wxTreeCtrl_GetItemImage_2, 2045).
+-define(wxTreeCtrl_GetItemText, 2046).
+-define(wxTreeCtrl_GetItemTextColour, 2047).
+-define(wxTreeCtrl_GetLastChild, 2048).
+-define(wxTreeCtrl_GetNextSibling, 2049).
+-define(wxTreeCtrl_GetNextVisible, 2050).
+-define(wxTreeCtrl_GetItemParent, 2051).
+-define(wxTreeCtrl_GetPrevSibling, 2052).
+-define(wxTreeCtrl_GetPrevVisible, 2053).
+-define(wxTreeCtrl_GetRootItem, 2054).
+-define(wxTreeCtrl_GetSelection, 2055).
+-define(wxTreeCtrl_GetSelections, 2056).
+-define(wxTreeCtrl_GetStateImageList, 2057).
+-define(wxTreeCtrl_HitTest, 2058).
+-define(wxTreeCtrl_InsertItem, 2060).
+-define(wxTreeCtrl_IsBold, 2061).
+-define(wxTreeCtrl_IsExpanded, 2062).
+-define(wxTreeCtrl_IsSelected, 2063).
+-define(wxTreeCtrl_IsVisible, 2064).
+-define(wxTreeCtrl_ItemHasChildren, 2065).
+-define(wxTreeCtrl_IsTreeItemIdOk, 2066).
+-define(wxTreeCtrl_PrependItem, 2067).
+-define(wxTreeCtrl_ScrollTo, 2068).
+-define(wxTreeCtrl_SelectItem_1, 2069).
+-define(wxTreeCtrl_SelectItem_2, 2070).
+-define(wxTreeCtrl_SetIndent, 2071).
+-define(wxTreeCtrl_SetImageList, 2072).
+-define(wxTreeCtrl_SetItemBackgroundColour, 2073).
+-define(wxTreeCtrl_SetItemBold, 2074).
+-define(wxTreeCtrl_SetItemData, 2075).
+-define(wxTreeCtrl_SetItemDropHighlight, 2076).
+-define(wxTreeCtrl_SetItemFont, 2077).
+-define(wxTreeCtrl_SetItemHasChildren, 2078).
+-define(wxTreeCtrl_SetItemImage_2, 2079).
+-define(wxTreeCtrl_SetItemImage_3, 2080).
+-define(wxTreeCtrl_SetItemText, 2081).
+-define(wxTreeCtrl_SetItemTextColour, 2082).
+-define(wxTreeCtrl_SetStateImageList, 2083).
+-define(wxTreeCtrl_SetWindowStyle, 2084).
+-define(wxTreeCtrl_SortChildren, 2085).
+-define(wxTreeCtrl_Toggle, 2086).
+-define(wxTreeCtrl_ToggleItemSelection, 2087).
+-define(wxTreeCtrl_Unselect, 2088).
+-define(wxTreeCtrl_UnselectAll, 2089).
+-define(wxTreeCtrl_UnselectItem, 2090).
+-define(wxScrollBar_new_0, 2091).
+-define(wxScrollBar_new_3, 2092).
+-define(wxScrollBar_destruct, 2093).
+-define(wxScrollBar_Create, 2094).
+-define(wxScrollBar_GetRange, 2095).
+-define(wxScrollBar_GetPageSize, 2096).
+-define(wxScrollBar_GetThumbPosition, 2097).
+-define(wxScrollBar_GetThumbSize, 2098).
+-define(wxScrollBar_SetThumbPosition, 2099).
+-define(wxScrollBar_SetScrollbar, 2100).
+-define(wxSpinButton_new_2, 2102).
+-define(wxSpinButton_new_0, 2103).
+-define(wxSpinButton_Create, 2104).
+-define(wxSpinButton_GetMax, 2105).
+-define(wxSpinButton_GetMin, 2106).
+-define(wxSpinButton_GetValue, 2107).
+-define(wxSpinButton_SetRange, 2108).
+-define(wxSpinButton_SetValue, 2109).
+-define(wxSpinButton_destroy, 2110).
+-define(wxSpinCtrl_new_0, 2111).
+-define(wxSpinCtrl_new_2, 2112).
+-define(wxSpinCtrl_Create, 2114).
+-define(wxSpinCtrl_SetValue_1_1, 2117).
+-define(wxSpinCtrl_SetValue_1_0, 2118).
+-define(wxSpinCtrl_GetValue, 2120).
+-define(wxSpinCtrl_SetRange, 2122).
+-define(wxSpinCtrl_SetSelection, 2123).
+-define(wxSpinCtrl_GetMin, 2125).
+-define(wxSpinCtrl_GetMax, 2127).
+-define(wxSpinCtrl_destroy, 2128).
+-define(wxStaticText_new_0, 2129).
+-define(wxStaticText_new_4, 2130).
+-define(wxStaticText_Create, 2131).
+-define(wxStaticText_GetLabel, 2132).
+-define(wxStaticText_SetLabel, 2133).
+-define(wxStaticText_Wrap, 2134).
+-define(wxStaticText_destroy, 2135).
+-define(wxStaticBitmap_new_0, 2136).
+-define(wxStaticBitmap_new_4, 2137).
+-define(wxStaticBitmap_Create, 2138).
+-define(wxStaticBitmap_GetBitmap, 2139).
+-define(wxStaticBitmap_SetBitmap, 2140).
+-define(wxStaticBitmap_destroy, 2141).
+-define(wxRadioBox_new, 2142).
+-define(wxRadioBox_destruct, 2144).
+-define(wxRadioBox_Create, 2145).
+-define(wxRadioBox_Enable_2, 2146).
+-define(wxRadioBox_Enable_1, 2147).
+-define(wxRadioBox_GetSelection, 2148).
+-define(wxRadioBox_GetString, 2149).
+-define(wxRadioBox_SetSelection, 2150).
+-define(wxRadioBox_Show_2, 2151).
+-define(wxRadioBox_Show_1, 2152).
+-define(wxRadioBox_GetColumnCount, 2153).
+-define(wxRadioBox_GetItemHelpText, 2154).
+-define(wxRadioBox_GetItemToolTip, 2155).
+-define(wxRadioBox_GetItemFromPoint, 2157).
+-define(wxRadioBox_GetRowCount, 2158).
+-define(wxRadioBox_IsItemEnabled, 2159).
+-define(wxRadioBox_IsItemShown, 2160).
+-define(wxRadioBox_SetItemHelpText, 2161).
+-define(wxRadioBox_SetItemToolTip, 2162).
+-define(wxRadioButton_new_0, 2163).
+-define(wxRadioButton_new_4, 2164).
+-define(wxRadioButton_Create, 2165).
+-define(wxRadioButton_GetValue, 2166).
+-define(wxRadioButton_SetValue, 2167).
+-define(wxRadioButton_destroy, 2168).
+-define(wxSlider_new_6, 2170).
+-define(wxSlider_new_0, 2171).
+-define(wxSlider_Create, 2172).
+-define(wxSlider_GetLineSize, 2173).
+-define(wxSlider_GetMax, 2174).
+-define(wxSlider_GetMin, 2175).
+-define(wxSlider_GetPageSize, 2176).
+-define(wxSlider_GetThumbLength, 2177).
+-define(wxSlider_GetValue, 2178).
+-define(wxSlider_SetLineSize, 2179).
+-define(wxSlider_SetPageSize, 2180).
+-define(wxSlider_SetRange, 2181).
+-define(wxSlider_SetThumbLength, 2182).
+-define(wxSlider_SetValue, 2183).
+-define(wxSlider_destroy, 2184).
+-define(wxDialog_new_4, 2186).
+-define(wxDialog_new_0, 2187).
+-define(wxDialog_destruct, 2189).
+-define(wxDialog_Create, 2190).
+-define(wxDialog_CreateButtonSizer, 2191).
+-define(wxDialog_CreateStdDialogButtonSizer, 2192).
+-define(wxDialog_EndModal, 2193).
+-define(wxDialog_GetAffirmativeId, 2194).
+-define(wxDialog_GetReturnCode, 2195).
+-define(wxDialog_IsModal, 2196).
+-define(wxDialog_SetAffirmativeId, 2197).
+-define(wxDialog_SetReturnCode, 2198).
+-define(wxDialog_Show, 2199).
+-define(wxDialog_ShowModal, 2200).
+-define(wxColourDialog_new_0, 2201).
+-define(wxColourDialog_new_2, 2202).
+-define(wxColourDialog_destruct, 2203).
+-define(wxColourDialog_Create, 2204).
+-define(wxColourDialog_GetColourData, 2205).
+-define(wxColourData_new_0, 2206).
+-define(wxColourData_new_1, 2207).
+-define(wxColourData_destruct, 2208).
+-define(wxColourData_GetChooseFull, 2209).
+-define(wxColourData_GetColour, 2210).
+-define(wxColourData_GetCustomColour, 2212).
+-define(wxColourData_SetChooseFull, 2213).
+-define(wxColourData_SetColour, 2214).
+-define(wxColourData_SetCustomColour, 2215).
+-define(wxPalette_new_0, 2216).
+-define(wxPalette_new_4, 2217).
+-define(wxPalette_destruct, 2219).
+-define(wxPalette_Create, 2220).
+-define(wxPalette_GetColoursCount, 2221).
+-define(wxPalette_GetPixel, 2222).
+-define(wxPalette_GetRGB, 2223).
+-define(wxPalette_IsOk, 2224).
+-define(wxDirDialog_new, 2228).
+-define(wxDirDialog_destruct, 2229).
+-define(wxDirDialog_GetPath, 2230).
+-define(wxDirDialog_GetMessage, 2231).
+-define(wxDirDialog_SetMessage, 2232).
+-define(wxDirDialog_SetPath, 2233).
+-define(wxFileDialog_new, 2237).
+-define(wxFileDialog_destruct, 2238).
+-define(wxFileDialog_GetDirectory, 2239).
+-define(wxFileDialog_GetFilename, 2240).
+-define(wxFileDialog_GetFilenames, 2241).
+-define(wxFileDialog_GetFilterIndex, 2242).
+-define(wxFileDialog_GetMessage, 2243).
+-define(wxFileDialog_GetPath, 2244).
+-define(wxFileDialog_GetPaths, 2245).
+-define(wxFileDialog_GetWildcard, 2246).
+-define(wxFileDialog_SetDirectory, 2247).
+-define(wxFileDialog_SetFilename, 2248).
+-define(wxFileDialog_SetFilterIndex, 2249).
+-define(wxFileDialog_SetMessage, 2250).
+-define(wxFileDialog_SetPath, 2251).
+-define(wxFileDialog_SetWildcard, 2252).
+-define(wxPickerBase_SetInternalMargin, 2253).
+-define(wxPickerBase_GetInternalMargin, 2254).
+-define(wxPickerBase_SetTextCtrlProportion, 2255).
+-define(wxPickerBase_SetPickerCtrlProportion, 2256).
+-define(wxPickerBase_GetTextCtrlProportion, 2257).
+-define(wxPickerBase_GetPickerCtrlProportion, 2258).
+-define(wxPickerBase_HasTextCtrl, 2259).
+-define(wxPickerBase_GetTextCtrl, 2260).
+-define(wxPickerBase_IsTextCtrlGrowable, 2261).
+-define(wxPickerBase_SetPickerCtrlGrowable, 2262).
+-define(wxPickerBase_SetTextCtrlGrowable, 2263).
+-define(wxPickerBase_IsPickerCtrlGrowable, 2264).
+-define(wxFilePickerCtrl_new_0, 2265).
+-define(wxFilePickerCtrl_new_3, 2266).
+-define(wxFilePickerCtrl_Create, 2267).
+-define(wxFilePickerCtrl_GetPath, 2268).
+-define(wxFilePickerCtrl_SetPath, 2269).
+-define(wxFilePickerCtrl_destroy, 2270).
+-define(wxDirPickerCtrl_new_0, 2271).
+-define(wxDirPickerCtrl_new_3, 2272).
+-define(wxDirPickerCtrl_Create, 2273).
+-define(wxDirPickerCtrl_GetPath, 2274).
+-define(wxDirPickerCtrl_SetPath, 2275).
+-define(wxDirPickerCtrl_destroy, 2276).
+-define(wxColourPickerCtrl_new_0, 2277).
+-define(wxColourPickerCtrl_new_3, 2278).
+-define(wxColourPickerCtrl_Create, 2279).
+-define(wxColourPickerCtrl_GetColour, 2280).
+-define(wxColourPickerCtrl_SetColour_1_1, 2281).
+-define(wxColourPickerCtrl_SetColour_1_0, 2282).
+-define(wxColourPickerCtrl_destroy, 2283).
+-define(wxDatePickerCtrl_new_0, 2284).
+-define(wxDatePickerCtrl_new_3, 2285).
+-define(wxDatePickerCtrl_GetRange, 2286).
+-define(wxDatePickerCtrl_GetValue, 2287).
+-define(wxDatePickerCtrl_SetRange, 2288).
+-define(wxDatePickerCtrl_SetValue, 2289).
+-define(wxDatePickerCtrl_destroy, 2290).
+-define(wxFontPickerCtrl_new_0, 2291).
+-define(wxFontPickerCtrl_new_3, 2292).
+-define(wxFontPickerCtrl_Create, 2293).
+-define(wxFontPickerCtrl_GetSelectedFont, 2294).
+-define(wxFontPickerCtrl_SetSelectedFont, 2295).
+-define(wxFontPickerCtrl_GetMaxPointSize, 2296).
+-define(wxFontPickerCtrl_SetMaxPointSize, 2297).
+-define(wxFontPickerCtrl_destroy, 2298).
+-define(wxFindReplaceDialog_new_0, 2301).
+-define(wxFindReplaceDialog_new_4, 2302).
+-define(wxFindReplaceDialog_destruct, 2303).
+-define(wxFindReplaceDialog_Create, 2304).
+-define(wxFindReplaceDialog_GetData, 2305).
+-define(wxFindReplaceData_new_0, 2306).
+-define(wxFindReplaceData_new_1, 2307).
+-define(wxFindReplaceData_GetFindString, 2308).
+-define(wxFindReplaceData_GetReplaceString, 2309).
+-define(wxFindReplaceData_GetFlags, 2310).
+-define(wxFindReplaceData_SetFlags, 2311).
+-define(wxFindReplaceData_SetFindString, 2312).
+-define(wxFindReplaceData_SetReplaceString, 2313).
+-define(wxFindReplaceData_destroy, 2314).
+-define(wxMultiChoiceDialog_new_0, 2315).
+-define(wxMultiChoiceDialog_new_5, 2317).
+-define(wxMultiChoiceDialog_GetSelections, 2318).
+-define(wxMultiChoiceDialog_SetSelections, 2319).
+-define(wxMultiChoiceDialog_destroy, 2320).
+-define(wxSingleChoiceDialog_new_0, 2321).
+-define(wxSingleChoiceDialog_new_5, 2323).
+-define(wxSingleChoiceDialog_GetSelection, 2324).
+-define(wxSingleChoiceDialog_GetStringSelection, 2325).
+-define(wxSingleChoiceDialog_SetSelection, 2326).
+-define(wxSingleChoiceDialog_destroy, 2327).
+-define(wxTextEntryDialog_new, 2328).
+-define(wxTextEntryDialog_GetValue, 2329).
+-define(wxTextEntryDialog_SetValue, 2330).
+-define(wxTextEntryDialog_destroy, 2331).
+-define(wxPasswordEntryDialog_new, 2332).
+-define(wxPasswordEntryDialog_destroy, 2333).
+-define(wxFontData_new_0, 2334).
+-define(wxFontData_new_1, 2335).
+-define(wxFontData_destruct, 2336).
+-define(wxFontData_EnableEffects, 2337).
+-define(wxFontData_GetAllowSymbols, 2338).
+-define(wxFontData_GetColour, 2339).
+-define(wxFontData_GetChosenFont, 2340).
+-define(wxFontData_GetEnableEffects, 2341).
+-define(wxFontData_GetInitialFont, 2342).
+-define(wxFontData_GetShowHelp, 2343).
+-define(wxFontData_SetAllowSymbols, 2344).
+-define(wxFontData_SetChosenFont, 2345).
+-define(wxFontData_SetColour, 2346).
+-define(wxFontData_SetInitialFont, 2347).
+-define(wxFontData_SetRange, 2348).
+-define(wxFontData_SetShowHelp, 2349).
+-define(wxFontDialog_new_0, 2353).
+-define(wxFontDialog_new_2, 2355).
+-define(wxFontDialog_Create, 2357).
+-define(wxFontDialog_GetFontData, 2358).
+-define(wxFontDialog_destroy, 2360).
+-define(wxProgressDialog_new, 2361).
+-define(wxProgressDialog_destruct, 2362).
+-define(wxProgressDialog_Resume, 2363).
+-define(wxProgressDialog_Update_2, 2364).
+-define(wxProgressDialog_Update_0, 2365).
+-define(wxMessageDialog_new, 2366).
+-define(wxMessageDialog_destruct, 2367).
+-define(wxPageSetupDialog_new, 2368).
+-define(wxPageSetupDialog_destruct, 2369).
+-define(wxPageSetupDialog_GetPageSetupData, 2370).
+-define(wxPageSetupDialog_ShowModal, 2371).
+-define(wxPageSetupDialogData_new_0, 2372).
+-define(wxPageSetupDialogData_new_1_0, 2373).
+-define(wxPageSetupDialogData_new_1_1, 2374).
+-define(wxPageSetupDialogData_destruct, 2375).
+-define(wxPageSetupDialogData_EnableHelp, 2376).
+-define(wxPageSetupDialogData_EnableMargins, 2377).
+-define(wxPageSetupDialogData_EnableOrientation, 2378).
+-define(wxPageSetupDialogData_EnablePaper, 2379).
+-define(wxPageSetupDialogData_EnablePrinter, 2380).
+-define(wxPageSetupDialogData_GetDefaultMinMargins, 2381).
+-define(wxPageSetupDialogData_GetEnableMargins, 2382).
+-define(wxPageSetupDialogData_GetEnableOrientation, 2383).
+-define(wxPageSetupDialogData_GetEnablePaper, 2384).
+-define(wxPageSetupDialogData_GetEnablePrinter, 2385).
+-define(wxPageSetupDialogData_GetEnableHelp, 2386).
+-define(wxPageSetupDialogData_GetDefaultInfo, 2387).
+-define(wxPageSetupDialogData_GetMarginTopLeft, 2388).
+-define(wxPageSetupDialogData_GetMarginBottomRight, 2389).
+-define(wxPageSetupDialogData_GetMinMarginTopLeft, 2390).
+-define(wxPageSetupDialogData_GetMinMarginBottomRight, 2391).
+-define(wxPageSetupDialogData_GetPaperId, 2392).
+-define(wxPageSetupDialogData_GetPaperSize, 2393).
+-define(wxPageSetupDialogData_GetPrintData, 2395).
+-define(wxPageSetupDialogData_IsOk, 2396).
+-define(wxPageSetupDialogData_SetDefaultInfo, 2397).
+-define(wxPageSetupDialogData_SetDefaultMinMargins, 2398).
+-define(wxPageSetupDialogData_SetMarginTopLeft, 2399).
+-define(wxPageSetupDialogData_SetMarginBottomRight, 2400).
+-define(wxPageSetupDialogData_SetMinMarginTopLeft, 2401).
+-define(wxPageSetupDialogData_SetMinMarginBottomRight, 2402).
+-define(wxPageSetupDialogData_SetPaperId, 2403).
+-define(wxPageSetupDialogData_SetPaperSize_1_1, 2404).
+-define(wxPageSetupDialogData_SetPaperSize_1_0, 2405).
+-define(wxPageSetupDialogData_SetPrintData, 2406).
+-define(wxPrintDialog_new_2_0, 2407).
+-define(wxPrintDialog_new_2_1, 2408).
+-define(wxPrintDialog_destruct, 2409).
+-define(wxPrintDialog_GetPrintDialogData, 2410).
+-define(wxPrintDialog_GetPrintDC, 2411).
+-define(wxPrintDialogData_new_0, 2412).
+-define(wxPrintDialogData_new_1_1, 2413).
+-define(wxPrintDialogData_new_1_0, 2414).
+-define(wxPrintDialogData_destruct, 2415).
+-define(wxPrintDialogData_EnableHelp, 2416).
+-define(wxPrintDialogData_EnablePageNumbers, 2417).
+-define(wxPrintDialogData_EnablePrintToFile, 2418).
+-define(wxPrintDialogData_EnableSelection, 2419).
+-define(wxPrintDialogData_GetAllPages, 2420).
+-define(wxPrintDialogData_GetCollate, 2421).
+-define(wxPrintDialogData_GetFromPage, 2422).
+-define(wxPrintDialogData_GetMaxPage, 2423).
+-define(wxPrintDialogData_GetMinPage, 2424).
+-define(wxPrintDialogData_GetNoCopies, 2425).
+-define(wxPrintDialogData_GetPrintData, 2426).
+-define(wxPrintDialogData_GetPrintToFile, 2427).
+-define(wxPrintDialogData_GetSelection, 2428).
+-define(wxPrintDialogData_GetToPage, 2429).
+-define(wxPrintDialogData_IsOk, 2430).
+-define(wxPrintDialogData_SetCollate, 2431).
+-define(wxPrintDialogData_SetFromPage, 2432).
+-define(wxPrintDialogData_SetMaxPage, 2433).
+-define(wxPrintDialogData_SetMinPage, 2434).
+-define(wxPrintDialogData_SetNoCopies, 2435).
+-define(wxPrintDialogData_SetPrintData, 2436).
+-define(wxPrintDialogData_SetPrintToFile, 2437).
+-define(wxPrintDialogData_SetSelection, 2438).
+-define(wxPrintDialogData_SetToPage, 2439).
+-define(wxPrintData_new_0, 2440).
+-define(wxPrintData_new_1, 2441).
+-define(wxPrintData_destruct, 2442).
+-define(wxPrintData_GetCollate, 2443).
+-define(wxPrintData_GetBin, 2444).
+-define(wxPrintData_GetColour, 2445).
+-define(wxPrintData_GetDuplex, 2446).
+-define(wxPrintData_GetNoCopies, 2447).
+-define(wxPrintData_GetOrientation, 2448).
+-define(wxPrintData_GetPaperId, 2449).
+-define(wxPrintData_GetPrinterName, 2450).
+-define(wxPrintData_GetQuality, 2451).
+-define(wxPrintData_IsOk, 2452).
+-define(wxPrintData_SetBin, 2453).
+-define(wxPrintData_SetCollate, 2454).
+-define(wxPrintData_SetColour, 2455).
+-define(wxPrintData_SetDuplex, 2456).
+-define(wxPrintData_SetNoCopies, 2457).
+-define(wxPrintData_SetOrientation, 2458).
+-define(wxPrintData_SetPaperId, 2459).
+-define(wxPrintData_SetPrinterName, 2460).
+-define(wxPrintData_SetQuality, 2461).
+-define(wxPrintPreview_new_2, 2464).
+-define(wxPrintPreview_new_3, 2465).
+-define(wxPrintPreview_destruct, 2467).
+-define(wxPrintPreview_GetCanvas, 2468).
+-define(wxPrintPreview_GetCurrentPage, 2469).
+-define(wxPrintPreview_GetFrame, 2470).
+-define(wxPrintPreview_GetMaxPage, 2471).
+-define(wxPrintPreview_GetMinPage, 2472).
+-define(wxPrintPreview_GetPrintout, 2473).
+-define(wxPrintPreview_GetPrintoutForPrinting, 2474).
+-define(wxPrintPreview_IsOk, 2475).
+-define(wxPrintPreview_PaintPage, 2476).
+-define(wxPrintPreview_Print, 2477).
+-define(wxPrintPreview_RenderPage, 2478).
+-define(wxPrintPreview_SetCanvas, 2479).
+-define(wxPrintPreview_SetCurrentPage, 2480).
+-define(wxPrintPreview_SetFrame, 2481).
+-define(wxPrintPreview_SetPrintout, 2482).
+-define(wxPrintPreview_SetZoom, 2483).
+-define(wxPreviewFrame_new, 2484).
+-define(wxPreviewFrame_destruct, 2485).
+-define(wxPreviewFrame_CreateControlBar, 2486).
+-define(wxPreviewFrame_CreateCanvas, 2487).
+-define(wxPreviewFrame_Initialize, 2488).
+-define(wxPreviewFrame_OnCloseWindow, 2489).
+-define(wxPreviewControlBar_new, 2490).
+-define(wxPreviewControlBar_destruct, 2491).
+-define(wxPreviewControlBar_CreateButtons, 2492).
+-define(wxPreviewControlBar_GetPrintPreview, 2493).
+-define(wxPreviewControlBar_GetZoomControl, 2494).
+-define(wxPreviewControlBar_SetZoomControl, 2495).
+-define(wxPrinter_new, 2497).
+-define(wxPrinter_CreateAbortWindow, 2498).
+-define(wxPrinter_GetAbort, 2499).
+-define(wxPrinter_GetLastError, 2500).
+-define(wxPrinter_GetPrintDialogData, 2501).
+-define(wxPrinter_Print, 2502).
+-define(wxPrinter_PrintDialog, 2503).
+-define(wxPrinter_ReportError, 2504).
+-define(wxPrinter_Setup, 2505).
+-define(wxPrinter_destroy, 2506).
+-define(wxXmlResource_new_1, 2507).
+-define(wxXmlResource_new_2, 2508).
+-define(wxXmlResource_destruct, 2509).
+-define(wxXmlResource_AttachUnknownControl, 2510).
+-define(wxXmlResource_ClearHandlers, 2511).
+-define(wxXmlResource_CompareVersion, 2512).
+-define(wxXmlResource_Get, 2513).
+-define(wxXmlResource_GetFlags, 2514).
+-define(wxXmlResource_GetVersion, 2515).
+-define(wxXmlResource_GetXRCID, 2516).
+-define(wxXmlResource_InitAllHandlers, 2517).
+-define(wxXmlResource_Load, 2518).
+-define(wxXmlResource_LoadBitmap, 2519).
+-define(wxXmlResource_LoadDialog_2, 2520).
+-define(wxXmlResource_LoadDialog_3, 2521).
+-define(wxXmlResource_LoadFrame_2, 2522).
+-define(wxXmlResource_LoadFrame_3, 2523).
+-define(wxXmlResource_LoadIcon, 2524).
+-define(wxXmlResource_LoadMenu, 2525).
+-define(wxXmlResource_LoadMenuBar_2, 2526).
+-define(wxXmlResource_LoadMenuBar_1, 2527).
+-define(wxXmlResource_LoadPanel_2, 2528).
+-define(wxXmlResource_LoadPanel_3, 2529).
+-define(wxXmlResource_LoadToolBar, 2530).
+-define(wxXmlResource_Set, 2531).
+-define(wxXmlResource_SetFlags, 2532).
+-define(wxXmlResource_Unload, 2533).
+-define(wxXmlResource_xrcctrl, 2534).
+-define(wxHtmlEasyPrinting_new, 2535).
+-define(wxHtmlEasyPrinting_destruct, 2536).
+-define(wxHtmlEasyPrinting_GetPrintData, 2537).
+-define(wxHtmlEasyPrinting_GetPageSetupData, 2538).
+-define(wxHtmlEasyPrinting_PreviewFile, 2539).
+-define(wxHtmlEasyPrinting_PreviewText, 2540).
+-define(wxHtmlEasyPrinting_PrintFile, 2541).
+-define(wxHtmlEasyPrinting_PrintText, 2542).
+-define(wxHtmlEasyPrinting_PageSetup, 2543).
+-define(wxHtmlEasyPrinting_SetFonts, 2544).
+-define(wxHtmlEasyPrinting_SetHeader, 2545).
+-define(wxHtmlEasyPrinting_SetFooter, 2546).
+-define(wxGLCanvas_new_2, 2548).
+-define(wxGLCanvas_new_3_1, 2549).
+-define(wxGLCanvas_new_3_0, 2550).
+-define(wxGLCanvas_GetContext, 2551).
+-define(wxGLCanvas_SetCurrent, 2553).
+-define(wxGLCanvas_SwapBuffers, 2554).
+-define(wxGLCanvas_destroy, 2555).
+-define(wxAuiManager_new, 2556).
+-define(wxAuiManager_destruct, 2557).
+-define(wxAuiManager_AddPane_2_1, 2558).
+-define(wxAuiManager_AddPane_3, 2559).
+-define(wxAuiManager_AddPane_2_0, 2560).
+-define(wxAuiManager_DetachPane, 2561).
+-define(wxAuiManager_GetAllPanes, 2562).
+-define(wxAuiManager_GetArtProvider, 2563).
+-define(wxAuiManager_GetDockSizeConstraint, 2564).
+-define(wxAuiManager_GetFlags, 2565).
+-define(wxAuiManager_GetManagedWindow, 2566).
+-define(wxAuiManager_GetManager, 2567).
+-define(wxAuiManager_GetPane_1_1, 2568).
+-define(wxAuiManager_GetPane_1_0, 2569).
+-define(wxAuiManager_HideHint, 2570).
+-define(wxAuiManager_InsertPane, 2571).
+-define(wxAuiManager_LoadPaneInfo, 2572).
+-define(wxAuiManager_LoadPerspective, 2573).
+-define(wxAuiManager_SavePaneInfo, 2574).
+-define(wxAuiManager_SavePerspective, 2575).
+-define(wxAuiManager_SetArtProvider, 2576).
+-define(wxAuiManager_SetDockSizeConstraint, 2577).
+-define(wxAuiManager_SetFlags, 2578).
+-define(wxAuiManager_SetManagedWindow, 2579).
+-define(wxAuiManager_ShowHint, 2580).
+-define(wxAuiManager_UnInit, 2581).
+-define(wxAuiManager_Update, 2582).
+-define(wxAuiPaneInfo_new_0, 2583).
+-define(wxAuiPaneInfo_new_1, 2584).
+-define(wxAuiPaneInfo_destruct, 2585).
+-define(wxAuiPaneInfo_BestSize_1, 2586).
+-define(wxAuiPaneInfo_BestSize_2, 2587).
+-define(wxAuiPaneInfo_Bottom, 2588).
+-define(wxAuiPaneInfo_BottomDockable, 2589).
+-define(wxAuiPaneInfo_Caption, 2590).
+-define(wxAuiPaneInfo_CaptionVisible, 2591).
+-define(wxAuiPaneInfo_Centre, 2592).
+-define(wxAuiPaneInfo_CentrePane, 2593).
+-define(wxAuiPaneInfo_CloseButton, 2594).
+-define(wxAuiPaneInfo_DefaultPane, 2595).
+-define(wxAuiPaneInfo_DestroyOnClose, 2596).
+-define(wxAuiPaneInfo_Direction, 2597).
+-define(wxAuiPaneInfo_Dock, 2598).
+-define(wxAuiPaneInfo_Dockable, 2599).
+-define(wxAuiPaneInfo_Fixed, 2600).
+-define(wxAuiPaneInfo_Float, 2601).
+-define(wxAuiPaneInfo_Floatable, 2602).
+-define(wxAuiPaneInfo_FloatingPosition_1, 2603).
+-define(wxAuiPaneInfo_FloatingPosition_2, 2604).
+-define(wxAuiPaneInfo_FloatingSize_1, 2605).
+-define(wxAuiPaneInfo_FloatingSize_2, 2606).
+-define(wxAuiPaneInfo_Gripper, 2607).
+-define(wxAuiPaneInfo_GripperTop, 2608).
+-define(wxAuiPaneInfo_HasBorder, 2609).
+-define(wxAuiPaneInfo_HasCaption, 2610).
+-define(wxAuiPaneInfo_HasCloseButton, 2611).
+-define(wxAuiPaneInfo_HasFlag, 2612).
+-define(wxAuiPaneInfo_HasGripper, 2613).
+-define(wxAuiPaneInfo_HasGripperTop, 2614).
+-define(wxAuiPaneInfo_HasMaximizeButton, 2615).
+-define(wxAuiPaneInfo_HasMinimizeButton, 2616).
+-define(wxAuiPaneInfo_HasPinButton, 2617).
+-define(wxAuiPaneInfo_Hide, 2618).
+-define(wxAuiPaneInfo_IsBottomDockable, 2619).
+-define(wxAuiPaneInfo_IsDocked, 2620).
+-define(wxAuiPaneInfo_IsFixed, 2621).
+-define(wxAuiPaneInfo_IsFloatable, 2622).
+-define(wxAuiPaneInfo_IsFloating, 2623).
+-define(wxAuiPaneInfo_IsLeftDockable, 2624).
+-define(wxAuiPaneInfo_IsMovable, 2625).
+-define(wxAuiPaneInfo_IsOk, 2626).
+-define(wxAuiPaneInfo_IsResizable, 2627).
+-define(wxAuiPaneInfo_IsRightDockable, 2628).
+-define(wxAuiPaneInfo_IsShown, 2629).
+-define(wxAuiPaneInfo_IsToolbar, 2630).
+-define(wxAuiPaneInfo_IsTopDockable, 2631).
+-define(wxAuiPaneInfo_Layer, 2632).
+-define(wxAuiPaneInfo_Left, 2633).
+-define(wxAuiPaneInfo_LeftDockable, 2634).
+-define(wxAuiPaneInfo_MaxSize_1, 2635).
+-define(wxAuiPaneInfo_MaxSize_2, 2636).
+-define(wxAuiPaneInfo_MaximizeButton, 2637).
+-define(wxAuiPaneInfo_MinSize_1, 2638).
+-define(wxAuiPaneInfo_MinSize_2, 2639).
+-define(wxAuiPaneInfo_MinimizeButton, 2640).
+-define(wxAuiPaneInfo_Movable, 2641).
+-define(wxAuiPaneInfo_Name, 2642).
+-define(wxAuiPaneInfo_PaneBorder, 2643).
+-define(wxAuiPaneInfo_PinButton, 2644).
+-define(wxAuiPaneInfo_Position, 2645).
+-define(wxAuiPaneInfo_Resizable, 2646).
+-define(wxAuiPaneInfo_Right, 2647).
+-define(wxAuiPaneInfo_RightDockable, 2648).
+-define(wxAuiPaneInfo_Row, 2649).
+-define(wxAuiPaneInfo_SafeSet, 2650).
+-define(wxAuiPaneInfo_SetFlag, 2651).
+-define(wxAuiPaneInfo_Show, 2652).
+-define(wxAuiPaneInfo_ToolbarPane, 2653).
+-define(wxAuiPaneInfo_Top, 2654).
+-define(wxAuiPaneInfo_TopDockable, 2655).
+-define(wxAuiPaneInfo_Window, 2656).
+-define(wxAuiPaneInfo_GetWindow, 2657).
+-define(wxAuiPaneInfo_GetFrame, 2658).
+-define(wxAuiPaneInfo_GetDirection, 2659).
+-define(wxAuiPaneInfo_GetLayer, 2660).
+-define(wxAuiPaneInfo_GetRow, 2661).
+-define(wxAuiPaneInfo_GetPosition, 2662).
+-define(wxAuiPaneInfo_GetFloatingPosition, 2663).
+-define(wxAuiPaneInfo_GetFloatingSize, 2664).
+-define(wxAuiNotebook_new_0, 2665).
+-define(wxAuiNotebook_new_2, 2666).
+-define(wxAuiNotebook_AddPage, 2667).
+-define(wxAuiNotebook_Create, 2668).
+-define(wxAuiNotebook_DeletePage, 2669).
+-define(wxAuiNotebook_GetArtProvider, 2670).
+-define(wxAuiNotebook_GetPage, 2671).
+-define(wxAuiNotebook_GetPageBitmap, 2672).
+-define(wxAuiNotebook_GetPageCount, 2673).
+-define(wxAuiNotebook_GetPageIndex, 2674).
+-define(wxAuiNotebook_GetPageText, 2675).
+-define(wxAuiNotebook_GetSelection, 2676).
+-define(wxAuiNotebook_InsertPage, 2677).
+-define(wxAuiNotebook_RemovePage, 2678).
+-define(wxAuiNotebook_SetArtProvider, 2679).
+-define(wxAuiNotebook_SetFont, 2680).
+-define(wxAuiNotebook_SetPageBitmap, 2681).
+-define(wxAuiNotebook_SetPageText, 2682).
+-define(wxAuiNotebook_SetSelection, 2683).
+-define(wxAuiNotebook_SetTabCtrlHeight, 2684).
+-define(wxAuiNotebook_SetUniformBitmapSize, 2685).
+-define(wxAuiNotebook_destroy, 2686).
+-define(wxAuiTabArt_SetFlags, 2687).
+-define(wxAuiTabArt_SetMeasuringFont, 2688).
+-define(wxAuiTabArt_SetNormalFont, 2689).
+-define(wxAuiTabArt_SetSelectedFont, 2690).
+-define(wxAuiTabArt_SetColour, 2691).
+-define(wxAuiTabArt_SetActiveColour, 2692).
+-define(wxAuiDockArt_GetColour, 2693).
+-define(wxAuiDockArt_GetFont, 2694).
+-define(wxAuiDockArt_GetMetric, 2695).
+-define(wxAuiDockArt_SetColour, 2696).
+-define(wxAuiDockArt_SetFont, 2697).
+-define(wxAuiDockArt_SetMetric, 2698).
+-define(wxAuiSimpleTabArt_new, 2699).
+-define(wxAuiSimpleTabArt_destroy, 2700).
+-define(wxMDIParentFrame_new_0, 2701).
+-define(wxMDIParentFrame_new_4, 2702).
+-define(wxMDIParentFrame_destruct, 2703).
+-define(wxMDIParentFrame_ActivateNext, 2704).
+-define(wxMDIParentFrame_ActivatePrevious, 2705).
+-define(wxMDIParentFrame_ArrangeIcons, 2706).
+-define(wxMDIParentFrame_Cascade, 2707).
+-define(wxMDIParentFrame_Create, 2708).
+-define(wxMDIParentFrame_GetActiveChild, 2709).
+-define(wxMDIParentFrame_GetClientWindow, 2710).
+-define(wxMDIParentFrame_Tile, 2711).
+-define(wxMDIChildFrame_new_0, 2712).
+-define(wxMDIChildFrame_new_4, 2713).
+-define(wxMDIChildFrame_destruct, 2714).
+-define(wxMDIChildFrame_Activate, 2715).
+-define(wxMDIChildFrame_Create, 2716).
+-define(wxMDIChildFrame_Maximize, 2717).
+-define(wxMDIChildFrame_Restore, 2718).
+-define(wxMDIClientWindow_new_0, 2719).
+-define(wxMDIClientWindow_new_2, 2720).
+-define(wxMDIClientWindow_destruct, 2721).
+-define(wxMDIClientWindow_CreateClient, 2722).
+-define(wxLayoutAlgorithm_new, 2723).
+-define(wxLayoutAlgorithm_LayoutFrame, 2724).
+-define(wxLayoutAlgorithm_LayoutMDIFrame, 2725).
+-define(wxLayoutAlgorithm_LayoutWindow, 2726).
+-define(wxLayoutAlgorithm_destroy, 2727).
+-define(wxEvent_GetId, 2728).
+-define(wxEvent_GetSkipped, 2729).
+-define(wxEvent_GetTimestamp, 2730).
+-define(wxEvent_IsCommandEvent, 2731).
+-define(wxEvent_ResumePropagation, 2732).
+-define(wxEvent_ShouldPropagate, 2733).
+-define(wxEvent_Skip, 2734).
+-define(wxEvent_StopPropagation, 2735).
+-define(wxCommandEvent_getClientData, 2736).
+-define(wxCommandEvent_GetExtraLong, 2737).
+-define(wxCommandEvent_GetInt, 2738).
+-define(wxCommandEvent_GetSelection, 2739).
+-define(wxCommandEvent_GetString, 2740).
+-define(wxCommandEvent_IsChecked, 2741).
+-define(wxCommandEvent_IsSelection, 2742).
+-define(wxCommandEvent_SetInt, 2743).
+-define(wxCommandEvent_SetString, 2744).
+-define(wxScrollEvent_GetOrientation, 2745).
+-define(wxScrollEvent_GetPosition, 2746).
+-define(wxScrollWinEvent_GetOrientation, 2747).
+-define(wxScrollWinEvent_GetPosition, 2748).
+-define(wxMouseEvent_AltDown, 2749).
+-define(wxMouseEvent_Button, 2750).
+-define(wxMouseEvent_ButtonDClick, 2751).
+-define(wxMouseEvent_ButtonDown, 2752).
+-define(wxMouseEvent_ButtonUp, 2753).
+-define(wxMouseEvent_CmdDown, 2754).
+-define(wxMouseEvent_ControlDown, 2755).
+-define(wxMouseEvent_Dragging, 2756).
+-define(wxMouseEvent_Entering, 2757).
+-define(wxMouseEvent_GetButton, 2758).
+-define(wxMouseEvent_GetPosition, 2761).
+-define(wxMouseEvent_GetLogicalPosition, 2762).
+-define(wxMouseEvent_GetLinesPerAction, 2763).
+-define(wxMouseEvent_GetWheelRotation, 2764).
+-define(wxMouseEvent_GetWheelDelta, 2765).
+-define(wxMouseEvent_GetX, 2766).
+-define(wxMouseEvent_GetY, 2767).
+-define(wxMouseEvent_IsButton, 2768).
+-define(wxMouseEvent_IsPageScroll, 2769).
+-define(wxMouseEvent_Leaving, 2770).
+-define(wxMouseEvent_LeftDClick, 2771).
+-define(wxMouseEvent_LeftDown, 2772).
+-define(wxMouseEvent_LeftIsDown, 2773).
+-define(wxMouseEvent_LeftUp, 2774).
+-define(wxMouseEvent_MetaDown, 2775).
+-define(wxMouseEvent_MiddleDClick, 2776).
+-define(wxMouseEvent_MiddleDown, 2777).
+-define(wxMouseEvent_MiddleIsDown, 2778).
+-define(wxMouseEvent_MiddleUp, 2779).
+-define(wxMouseEvent_Moving, 2780).
+-define(wxMouseEvent_RightDClick, 2781).
+-define(wxMouseEvent_RightDown, 2782).
+-define(wxMouseEvent_RightIsDown, 2783).
+-define(wxMouseEvent_RightUp, 2784).
+-define(wxMouseEvent_ShiftDown, 2785).
+-define(wxSetCursorEvent_GetCursor, 2786).
+-define(wxSetCursorEvent_GetX, 2787).
+-define(wxSetCursorEvent_GetY, 2788).
+-define(wxSetCursorEvent_HasCursor, 2789).
+-define(wxSetCursorEvent_SetCursor, 2790).
+-define(wxKeyEvent_AltDown, 2791).
+-define(wxKeyEvent_CmdDown, 2792).
+-define(wxKeyEvent_ControlDown, 2793).
+-define(wxKeyEvent_GetKeyCode, 2794).
+-define(wxKeyEvent_GetModifiers, 2795).
+-define(wxKeyEvent_GetPosition, 2798).
+-define(wxKeyEvent_GetRawKeyCode, 2799).
+-define(wxKeyEvent_GetRawKeyFlags, 2800).
+-define(wxKeyEvent_GetUnicodeKey, 2801).
+-define(wxKeyEvent_GetX, 2802).
+-define(wxKeyEvent_GetY, 2803).
+-define(wxKeyEvent_HasModifiers, 2804).
+-define(wxKeyEvent_MetaDown, 2805).
+-define(wxKeyEvent_ShiftDown, 2806).
+-define(wxSizeEvent_GetSize, 2807).
+-define(wxMoveEvent_GetPosition, 2808).
+-define(wxEraseEvent_GetDC, 2809).
+-define(wxFocusEvent_GetWindow, 2810).
+-define(wxChildFocusEvent_GetWindow, 2811).
+-define(wxMenuEvent_GetMenu, 2812).
+-define(wxMenuEvent_GetMenuId, 2813).
+-define(wxMenuEvent_IsPopup, 2814).
+-define(wxCloseEvent_CanVeto, 2815).
+-define(wxCloseEvent_GetLoggingOff, 2816).
+-define(wxCloseEvent_SetCanVeto, 2817).
+-define(wxCloseEvent_SetLoggingOff, 2818).
+-define(wxCloseEvent_Veto, 2819).
+-define(wxShowEvent_SetShow, 2820).
+-define(wxShowEvent_GetShow, 2821).
+-define(wxIconizeEvent_Iconized, 2822).
+-define(wxJoystickEvent_ButtonDown, 2823).
+-define(wxJoystickEvent_ButtonIsDown, 2824).
+-define(wxJoystickEvent_ButtonUp, 2825).
+-define(wxJoystickEvent_GetButtonChange, 2826).
+-define(wxJoystickEvent_GetButtonState, 2827).
+-define(wxJoystickEvent_GetJoystick, 2828).
+-define(wxJoystickEvent_GetPosition, 2829).
+-define(wxJoystickEvent_GetZPosition, 2830).
+-define(wxJoystickEvent_IsButton, 2831).
+-define(wxJoystickEvent_IsMove, 2832).
+-define(wxJoystickEvent_IsZMove, 2833).
+-define(wxUpdateUIEvent_CanUpdate, 2834).
+-define(wxUpdateUIEvent_Check, 2835).
+-define(wxUpdateUIEvent_Enable, 2836).
+-define(wxUpdateUIEvent_Show, 2837).
+-define(wxUpdateUIEvent_GetChecked, 2838).
+-define(wxUpdateUIEvent_GetEnabled, 2839).
+-define(wxUpdateUIEvent_GetShown, 2840).
+-define(wxUpdateUIEvent_GetSetChecked, 2841).
+-define(wxUpdateUIEvent_GetSetEnabled, 2842).
+-define(wxUpdateUIEvent_GetSetShown, 2843).
+-define(wxUpdateUIEvent_GetSetText, 2844).
+-define(wxUpdateUIEvent_GetText, 2845).
+-define(wxUpdateUIEvent_GetMode, 2846).
+-define(wxUpdateUIEvent_GetUpdateInterval, 2847).
+-define(wxUpdateUIEvent_ResetUpdateTime, 2848).
+-define(wxUpdateUIEvent_SetMode, 2849).
+-define(wxUpdateUIEvent_SetText, 2850).
+-define(wxUpdateUIEvent_SetUpdateInterval, 2851).
+-define(wxMouseCaptureChangedEvent_GetCapturedWindow, 2852).
+-define(wxPaletteChangedEvent_SetChangedWindow, 2853).
+-define(wxPaletteChangedEvent_GetChangedWindow, 2854).
+-define(wxQueryNewPaletteEvent_SetPaletteRealized, 2855).
+-define(wxQueryNewPaletteEvent_GetPaletteRealized, 2856).
+-define(wxNavigationKeyEvent_GetDirection, 2857).
+-define(wxNavigationKeyEvent_SetDirection, 2858).
+-define(wxNavigationKeyEvent_IsWindowChange, 2859).
+-define(wxNavigationKeyEvent_SetWindowChange, 2860).
+-define(wxNavigationKeyEvent_IsFromTab, 2861).
+-define(wxNavigationKeyEvent_SetFromTab, 2862).
+-define(wxNavigationKeyEvent_GetCurrentFocus, 2863).
+-define(wxNavigationKeyEvent_SetCurrentFocus, 2864).
+-define(wxHelpEvent_GetOrigin, 2865).
+-define(wxHelpEvent_GetPosition, 2866).
+-define(wxHelpEvent_SetOrigin, 2867).
+-define(wxHelpEvent_SetPosition, 2868).
+-define(wxContextMenuEvent_GetPosition, 2869).
+-define(wxContextMenuEvent_SetPosition, 2870).
+-define(wxIdleEvent_CanSend, 2871).
+-define(wxIdleEvent_GetMode, 2872).
+-define(wxIdleEvent_RequestMore, 2873).
+-define(wxIdleEvent_MoreRequested, 2874).
+-define(wxIdleEvent_SetMode, 2875).
+-define(wxGridEvent_AltDown, 2876).
+-define(wxGridEvent_ControlDown, 2877).
+-define(wxGridEvent_GetCol, 2878).
+-define(wxGridEvent_GetPosition, 2879).
+-define(wxGridEvent_GetRow, 2880).
+-define(wxGridEvent_MetaDown, 2881).
+-define(wxGridEvent_Selecting, 2882).
+-define(wxGridEvent_ShiftDown, 2883).
+-define(wxNotifyEvent_Allow, 2884).
+-define(wxNotifyEvent_IsAllowed, 2885).
+-define(wxNotifyEvent_Veto, 2886).
+-define(wxSashEvent_GetEdge, 2887).
+-define(wxSashEvent_GetDragRect, 2888).
+-define(wxSashEvent_GetDragStatus, 2889).
+-define(wxListEvent_GetCacheFrom, 2890).
+-define(wxListEvent_GetCacheTo, 2891).
+-define(wxListEvent_GetKeyCode, 2892).
+-define(wxListEvent_GetIndex, 2893).
+-define(wxListEvent_GetColumn, 2894).
+-define(wxListEvent_GetPoint, 2895).
+-define(wxListEvent_GetLabel, 2896).
+-define(wxListEvent_GetText, 2897).
+-define(wxListEvent_GetImage, 2898).
+-define(wxListEvent_GetData, 2899).
+-define(wxListEvent_GetMask, 2900).
+-define(wxListEvent_GetItem, 2901).
+-define(wxListEvent_IsEditCancelled, 2902).
+-define(wxDateEvent_GetDate, 2903).
+-define(wxCalendarEvent_GetWeekDay, 2904).
+-define(wxFileDirPickerEvent_GetPath, 2905).
+-define(wxColourPickerEvent_GetColour, 2906).
+-define(wxFontPickerEvent_GetFont, 2907).
+-define(wxStyledTextEvent_GetPosition, 2908).
+-define(wxStyledTextEvent_GetKey, 2909).
+-define(wxStyledTextEvent_GetModifiers, 2910).
+-define(wxStyledTextEvent_GetModificationType, 2911).
+-define(wxStyledTextEvent_GetText, 2912).
+-define(wxStyledTextEvent_GetLength, 2913).
+-define(wxStyledTextEvent_GetLinesAdded, 2914).
+-define(wxStyledTextEvent_GetLine, 2915).
+-define(wxStyledTextEvent_GetFoldLevelNow, 2916).
+-define(wxStyledTextEvent_GetFoldLevelPrev, 2917).
+-define(wxStyledTextEvent_GetMargin, 2918).
+-define(wxStyledTextEvent_GetMessage, 2919).
+-define(wxStyledTextEvent_GetWParam, 2920).
+-define(wxStyledTextEvent_GetLParam, 2921).
+-define(wxStyledTextEvent_GetListType, 2922).
+-define(wxStyledTextEvent_GetX, 2923).
+-define(wxStyledTextEvent_GetY, 2924).
+-define(wxStyledTextEvent_GetDragText, 2925).
+-define(wxStyledTextEvent_GetDragAllowMove, 2926).
+-define(wxStyledTextEvent_GetDragResult, 2927).
+-define(wxStyledTextEvent_GetShift, 2928).
+-define(wxStyledTextEvent_GetControl, 2929).
+-define(wxStyledTextEvent_GetAlt, 2930).
+-define(utils_wxGetKeyState, 2931).
+-define(utils_wxGetMousePosition, 2932).
+-define(utils_wxGetMouseState, 2933).
+-define(utils_wxSetDetectableAutoRepeat, 2934).
+-define(utils_wxBell, 2935).
+-define(utils_wxFindMenuItemId, 2936).
+-define(utils_wxGenericFindWindowAtPoint, 2937).
+-define(utils_wxFindWindowAtPoint, 2938).
+-define(utils_wxBeginBusyCursor, 2939).
+-define(utils_wxEndBusyCursor, 2940).
+-define(utils_wxIsBusy, 2941).
+-define(utils_wxShutdown, 2942).
+-define(utils_wxShell, 2943).
+-define(utils_wxLaunchDefaultBrowser, 2944).
+-define(utils_wxGetEmailAddress, 2945).
+-define(utils_wxGetUserId, 2946).
+-define(utils_wxGetHomeDir, 2947).
+-define(utils_wxNewId, 2948).
+-define(utils_wxRegisterId, 2949).
+-define(utils_wxGetCurrentId, 2950).
+-define(utils_wxGetOsDescription, 2951).
+-define(utils_wxIsPlatformLittleEndian, 2952).
+-define(utils_wxIsPlatform64Bit, 2953).
+-define(gdicmn_wxDisplaySize, 2954).
+-define(gdicmn_wxSetCursor, 2955).
+-define(wxPrintout_new, 2956).
+-define(wxPrintout_destruct, 2957).
+-define(wxPrintout_GetDC, 2958).
+-define(wxPrintout_GetPageSizeMM, 2959).
+-define(wxPrintout_GetPageSizePixels, 2960).
+-define(wxPrintout_GetPaperRectPixels, 2961).
+-define(wxPrintout_GetPPIPrinter, 2962).
+-define(wxPrintout_GetPPIScreen, 2963).
+-define(wxPrintout_GetTitle, 2964).
+-define(wxPrintout_IsPreview, 2965).
+-define(wxPrintout_FitThisSizeToPaper, 2966).
+-define(wxPrintout_FitThisSizeToPage, 2967).
+-define(wxPrintout_FitThisSizeToPageMargins, 2968).
+-define(wxPrintout_MapScreenSizeToPaper, 2969).
+-define(wxPrintout_MapScreenSizeToPage, 2970).
+-define(wxPrintout_MapScreenSizeToPageMargins, 2971).
+-define(wxPrintout_MapScreenSizeToDevice, 2972).
+-define(wxPrintout_GetLogicalPaperRect, 2973).
+-define(wxPrintout_GetLogicalPageRect, 2974).
+-define(wxPrintout_GetLogicalPageMarginsRect, 2975).
+-define(wxPrintout_SetLogicalOrigin, 2976).
+-define(wxPrintout_OffsetLogicalOrigin, 2977).
+-define(wxStyledTextCtrl_new_2, 2978).
+-define(wxStyledTextCtrl_new_0, 2979).
+-define(wxStyledTextCtrl_destruct, 2980).
+-define(wxStyledTextCtrl_Create, 2981).
+-define(wxStyledTextCtrl_AddText, 2982).
+-define(wxStyledTextCtrl_AddStyledText, 2983).
+-define(wxStyledTextCtrl_InsertText, 2984).
+-define(wxStyledTextCtrl_ClearAll, 2985).
+-define(wxStyledTextCtrl_ClearDocumentStyle, 2986).
+-define(wxStyledTextCtrl_GetLength, 2987).
+-define(wxStyledTextCtrl_GetCharAt, 2988).
+-define(wxStyledTextCtrl_GetCurrentPos, 2989).
+-define(wxStyledTextCtrl_GetAnchor, 2990).
+-define(wxStyledTextCtrl_GetStyleAt, 2991).
+-define(wxStyledTextCtrl_Redo, 2992).
+-define(wxStyledTextCtrl_SetUndoCollection, 2993).
+-define(wxStyledTextCtrl_SelectAll, 2994).
+-define(wxStyledTextCtrl_SetSavePoint, 2995).
+-define(wxStyledTextCtrl_GetStyledText, 2996).
+-define(wxStyledTextCtrl_CanRedo, 2997).
+-define(wxStyledTextCtrl_MarkerLineFromHandle, 2998).
+-define(wxStyledTextCtrl_MarkerDeleteHandle, 2999).
+-define(wxStyledTextCtrl_GetUndoCollection, 3000).
+-define(wxStyledTextCtrl_GetViewWhiteSpace, 3001).
+-define(wxStyledTextCtrl_SetViewWhiteSpace, 3002).
+-define(wxStyledTextCtrl_PositionFromPoint, 3003).
+-define(wxStyledTextCtrl_PositionFromPointClose, 3004).
+-define(wxStyledTextCtrl_GotoLine, 3005).
+-define(wxStyledTextCtrl_GotoPos, 3006).
+-define(wxStyledTextCtrl_SetAnchor, 3007).
+-define(wxStyledTextCtrl_GetCurLine, 3008).
+-define(wxStyledTextCtrl_GetEndStyled, 3009).
+-define(wxStyledTextCtrl_ConvertEOLs, 3010).
+-define(wxStyledTextCtrl_GetEOLMode, 3011).
+-define(wxStyledTextCtrl_SetEOLMode, 3012).
+-define(wxStyledTextCtrl_StartStyling, 3013).
+-define(wxStyledTextCtrl_SetStyling, 3014).
+-define(wxStyledTextCtrl_GetBufferedDraw, 3015).
+-define(wxStyledTextCtrl_SetBufferedDraw, 3016).
+-define(wxStyledTextCtrl_SetTabWidth, 3017).
+-define(wxStyledTextCtrl_GetTabWidth, 3018).
+-define(wxStyledTextCtrl_SetCodePage, 3019).
+-define(wxStyledTextCtrl_MarkerDefine, 3020).
+-define(wxStyledTextCtrl_MarkerSetForeground, 3021).
+-define(wxStyledTextCtrl_MarkerSetBackground, 3022).
+-define(wxStyledTextCtrl_MarkerAdd, 3023).
+-define(wxStyledTextCtrl_MarkerDelete, 3024).
+-define(wxStyledTextCtrl_MarkerDeleteAll, 3025).
+-define(wxStyledTextCtrl_MarkerGet, 3026).
+-define(wxStyledTextCtrl_MarkerNext, 3027).
+-define(wxStyledTextCtrl_MarkerPrevious, 3028).
+-define(wxStyledTextCtrl_MarkerDefineBitmap, 3029).
+-define(wxStyledTextCtrl_MarkerAddSet, 3030).
+-define(wxStyledTextCtrl_MarkerSetAlpha, 3031).
+-define(wxStyledTextCtrl_SetMarginType, 3032).
+-define(wxStyledTextCtrl_GetMarginType, 3033).
+-define(wxStyledTextCtrl_SetMarginWidth, 3034).
+-define(wxStyledTextCtrl_GetMarginWidth, 3035).
+-define(wxStyledTextCtrl_SetMarginMask, 3036).
+-define(wxStyledTextCtrl_GetMarginMask, 3037).
+-define(wxStyledTextCtrl_SetMarginSensitive, 3038).
+-define(wxStyledTextCtrl_GetMarginSensitive, 3039).
+-define(wxStyledTextCtrl_StyleClearAll, 3040).
+-define(wxStyledTextCtrl_StyleSetForeground, 3041).
+-define(wxStyledTextCtrl_StyleSetBackground, 3042).
+-define(wxStyledTextCtrl_StyleSetBold, 3043).
+-define(wxStyledTextCtrl_StyleSetItalic, 3044).
+-define(wxStyledTextCtrl_StyleSetSize, 3045).
+-define(wxStyledTextCtrl_StyleSetFaceName, 3046).
+-define(wxStyledTextCtrl_StyleSetEOLFilled, 3047).
+-define(wxStyledTextCtrl_StyleResetDefault, 3048).
+-define(wxStyledTextCtrl_StyleSetUnderline, 3049).
+-define(wxStyledTextCtrl_StyleSetCase, 3050).
+-define(wxStyledTextCtrl_StyleSetHotSpot, 3051).
+-define(wxStyledTextCtrl_SetSelForeground, 3052).
+-define(wxStyledTextCtrl_SetSelBackground, 3053).
+-define(wxStyledTextCtrl_GetSelAlpha, 3054).
+-define(wxStyledTextCtrl_SetSelAlpha, 3055).
+-define(wxStyledTextCtrl_SetCaretForeground, 3056).
+-define(wxStyledTextCtrl_CmdKeyAssign, 3057).
+-define(wxStyledTextCtrl_CmdKeyClear, 3058).
+-define(wxStyledTextCtrl_CmdKeyClearAll, 3059).
+-define(wxStyledTextCtrl_SetStyleBytes, 3060).
+-define(wxStyledTextCtrl_StyleSetVisible, 3061).
+-define(wxStyledTextCtrl_GetCaretPeriod, 3062).
+-define(wxStyledTextCtrl_SetCaretPeriod, 3063).
+-define(wxStyledTextCtrl_SetWordChars, 3064).
+-define(wxStyledTextCtrl_BeginUndoAction, 3065).
+-define(wxStyledTextCtrl_EndUndoAction, 3066).
+-define(wxStyledTextCtrl_IndicatorSetStyle, 3067).
+-define(wxStyledTextCtrl_IndicatorGetStyle, 3068).
+-define(wxStyledTextCtrl_IndicatorSetForeground, 3069).
+-define(wxStyledTextCtrl_IndicatorGetForeground, 3070).
+-define(wxStyledTextCtrl_SetWhitespaceForeground, 3071).
+-define(wxStyledTextCtrl_SetWhitespaceBackground, 3072).
+-define(wxStyledTextCtrl_GetStyleBits, 3073).
+-define(wxStyledTextCtrl_SetLineState, 3074).
+-define(wxStyledTextCtrl_GetLineState, 3075).
+-define(wxStyledTextCtrl_GetMaxLineState, 3076).
+-define(wxStyledTextCtrl_GetCaretLineVisible, 3077).
+-define(wxStyledTextCtrl_SetCaretLineVisible, 3078).
+-define(wxStyledTextCtrl_GetCaretLineBackground, 3079).
+-define(wxStyledTextCtrl_SetCaretLineBackground, 3080).
+-define(wxStyledTextCtrl_AutoCompShow, 3081).
+-define(wxStyledTextCtrl_AutoCompCancel, 3082).
+-define(wxStyledTextCtrl_AutoCompActive, 3083).
+-define(wxStyledTextCtrl_AutoCompPosStart, 3084).
+-define(wxStyledTextCtrl_AutoCompComplete, 3085).
+-define(wxStyledTextCtrl_AutoCompStops, 3086).
+-define(wxStyledTextCtrl_AutoCompSetSeparator, 3087).
+-define(wxStyledTextCtrl_AutoCompGetSeparator, 3088).
+-define(wxStyledTextCtrl_AutoCompSelect, 3089).
+-define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3090).
+-define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3091).
+-define(wxStyledTextCtrl_AutoCompSetFillUps, 3092).
+-define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3093).
+-define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3094).
+-define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3095).
+-define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3096).
+-define(wxStyledTextCtrl_UserListShow, 3097).
+-define(wxStyledTextCtrl_AutoCompSetAutoHide, 3098).
+-define(wxStyledTextCtrl_AutoCompGetAutoHide, 3099).
+-define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3100).
+-define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3101).
+-define(wxStyledTextCtrl_RegisterImage, 3102).
+-define(wxStyledTextCtrl_ClearRegisteredImages, 3103).
+-define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3104).
+-define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3105).
+-define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3106).
+-define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3107).
+-define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3108).
+-define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3109).
+-define(wxStyledTextCtrl_SetIndent, 3110).
+-define(wxStyledTextCtrl_GetIndent, 3111).
+-define(wxStyledTextCtrl_SetUseTabs, 3112).
+-define(wxStyledTextCtrl_GetUseTabs, 3113).
+-define(wxStyledTextCtrl_SetLineIndentation, 3114).
+-define(wxStyledTextCtrl_GetLineIndentation, 3115).
+-define(wxStyledTextCtrl_GetLineIndentPosition, 3116).
+-define(wxStyledTextCtrl_GetColumn, 3117).
+-define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3118).
+-define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3119).
+-define(wxStyledTextCtrl_SetIndentationGuides, 3120).
+-define(wxStyledTextCtrl_GetIndentationGuides, 3121).
+-define(wxStyledTextCtrl_SetHighlightGuide, 3122).
+-define(wxStyledTextCtrl_GetHighlightGuide, 3123).
+-define(wxStyledTextCtrl_GetLineEndPosition, 3124).
+-define(wxStyledTextCtrl_GetCodePage, 3125).
+-define(wxStyledTextCtrl_GetCaretForeground, 3126).
+-define(wxStyledTextCtrl_GetReadOnly, 3127).
+-define(wxStyledTextCtrl_SetCurrentPos, 3128).
+-define(wxStyledTextCtrl_SetSelectionStart, 3129).
+-define(wxStyledTextCtrl_GetSelectionStart, 3130).
+-define(wxStyledTextCtrl_SetSelectionEnd, 3131).
+-define(wxStyledTextCtrl_GetSelectionEnd, 3132).
+-define(wxStyledTextCtrl_SetPrintMagnification, 3133).
+-define(wxStyledTextCtrl_GetPrintMagnification, 3134).
+-define(wxStyledTextCtrl_SetPrintColourMode, 3135).
+-define(wxStyledTextCtrl_GetPrintColourMode, 3136).
+-define(wxStyledTextCtrl_FindText, 3137).
+-define(wxStyledTextCtrl_FormatRange, 3138).
+-define(wxStyledTextCtrl_GetFirstVisibleLine, 3139).
+-define(wxStyledTextCtrl_GetLine, 3140).
+-define(wxStyledTextCtrl_GetLineCount, 3141).
+-define(wxStyledTextCtrl_SetMarginLeft, 3142).
+-define(wxStyledTextCtrl_GetMarginLeft, 3143).
+-define(wxStyledTextCtrl_SetMarginRight, 3144).
+-define(wxStyledTextCtrl_GetMarginRight, 3145).
+-define(wxStyledTextCtrl_GetModify, 3146).
+-define(wxStyledTextCtrl_SetSelection, 3147).
+-define(wxStyledTextCtrl_GetSelectedText, 3148).
+-define(wxStyledTextCtrl_GetTextRange, 3149).
+-define(wxStyledTextCtrl_HideSelection, 3150).
+-define(wxStyledTextCtrl_LineFromPosition, 3151).
+-define(wxStyledTextCtrl_PositionFromLine, 3152).
+-define(wxStyledTextCtrl_LineScroll, 3153).
+-define(wxStyledTextCtrl_EnsureCaretVisible, 3154).
+-define(wxStyledTextCtrl_ReplaceSelection, 3155).
+-define(wxStyledTextCtrl_SetReadOnly, 3156).
+-define(wxStyledTextCtrl_CanPaste, 3157).
+-define(wxStyledTextCtrl_CanUndo, 3158).
+-define(wxStyledTextCtrl_EmptyUndoBuffer, 3159).
+-define(wxStyledTextCtrl_Undo, 3160).
+-define(wxStyledTextCtrl_Cut, 3161).
+-define(wxStyledTextCtrl_Copy, 3162).
+-define(wxStyledTextCtrl_Paste, 3163).
+-define(wxStyledTextCtrl_Clear, 3164).
+-define(wxStyledTextCtrl_SetText, 3165).
+-define(wxStyledTextCtrl_GetText, 3166).
+-define(wxStyledTextCtrl_GetTextLength, 3167).
+-define(wxStyledTextCtrl_GetOvertype, 3168).
+-define(wxStyledTextCtrl_SetCaretWidth, 3169).
+-define(wxStyledTextCtrl_GetCaretWidth, 3170).
+-define(wxStyledTextCtrl_SetTargetStart, 3171).
+-define(wxStyledTextCtrl_GetTargetStart, 3172).
+-define(wxStyledTextCtrl_SetTargetEnd, 3173).
+-define(wxStyledTextCtrl_GetTargetEnd, 3174).
+-define(wxStyledTextCtrl_ReplaceTarget, 3175).
+-define(wxStyledTextCtrl_SearchInTarget, 3176).
+-define(wxStyledTextCtrl_SetSearchFlags, 3177).
+-define(wxStyledTextCtrl_GetSearchFlags, 3178).
+-define(wxStyledTextCtrl_CallTipShow, 3179).
+-define(wxStyledTextCtrl_CallTipCancel, 3180).
+-define(wxStyledTextCtrl_CallTipActive, 3181).
+-define(wxStyledTextCtrl_CallTipPosAtStart, 3182).
+-define(wxStyledTextCtrl_CallTipSetHighlight, 3183).
+-define(wxStyledTextCtrl_CallTipSetBackground, 3184).
+-define(wxStyledTextCtrl_CallTipSetForeground, 3185).
+-define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3186).
+-define(wxStyledTextCtrl_CallTipUseStyle, 3187).
+-define(wxStyledTextCtrl_VisibleFromDocLine, 3188).
+-define(wxStyledTextCtrl_DocLineFromVisible, 3189).
+-define(wxStyledTextCtrl_WrapCount, 3190).
+-define(wxStyledTextCtrl_SetFoldLevel, 3191).
+-define(wxStyledTextCtrl_GetFoldLevel, 3192).
+-define(wxStyledTextCtrl_GetLastChild, 3193).
+-define(wxStyledTextCtrl_GetFoldParent, 3194).
+-define(wxStyledTextCtrl_ShowLines, 3195).
+-define(wxStyledTextCtrl_HideLines, 3196).
+-define(wxStyledTextCtrl_GetLineVisible, 3197).
+-define(wxStyledTextCtrl_SetFoldExpanded, 3198).
+-define(wxStyledTextCtrl_GetFoldExpanded, 3199).
+-define(wxStyledTextCtrl_ToggleFold, 3200).
+-define(wxStyledTextCtrl_EnsureVisible, 3201).
+-define(wxStyledTextCtrl_SetFoldFlags, 3202).
+-define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3203).
+-define(wxStyledTextCtrl_SetTabIndents, 3204).
+-define(wxStyledTextCtrl_GetTabIndents, 3205).
+-define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3206).
+-define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3207).
+-define(wxStyledTextCtrl_SetMouseDwellTime, 3208).
+-define(wxStyledTextCtrl_GetMouseDwellTime, 3209).
+-define(wxStyledTextCtrl_WordStartPosition, 3210).
+-define(wxStyledTextCtrl_WordEndPosition, 3211).
+-define(wxStyledTextCtrl_SetWrapMode, 3212).
+-define(wxStyledTextCtrl_GetWrapMode, 3213).
+-define(wxStyledTextCtrl_SetWrapVisualFlags, 3214).
+-define(wxStyledTextCtrl_GetWrapVisualFlags, 3215).
+-define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3216).
+-define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3217).
+-define(wxStyledTextCtrl_SetWrapStartIndent, 3218).
+-define(wxStyledTextCtrl_GetWrapStartIndent, 3219).
+-define(wxStyledTextCtrl_SetLayoutCache, 3220).
+-define(wxStyledTextCtrl_GetLayoutCache, 3221).
+-define(wxStyledTextCtrl_SetScrollWidth, 3222).
+-define(wxStyledTextCtrl_GetScrollWidth, 3223).
+-define(wxStyledTextCtrl_TextWidth, 3224).
+-define(wxStyledTextCtrl_GetEndAtLastLine, 3225).
+-define(wxStyledTextCtrl_TextHeight, 3226).
+-define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3227).
+-define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3228).
+-define(wxStyledTextCtrl_AppendText, 3229).
+-define(wxStyledTextCtrl_GetTwoPhaseDraw, 3230).
+-define(wxStyledTextCtrl_SetTwoPhaseDraw, 3231).
+-define(wxStyledTextCtrl_TargetFromSelection, 3232).
+-define(wxStyledTextCtrl_LinesJoin, 3233).
+-define(wxStyledTextCtrl_LinesSplit, 3234).
+-define(wxStyledTextCtrl_SetFoldMarginColour, 3235).
+-define(wxStyledTextCtrl_SetFoldMarginHiColour, 3236).
+-define(wxStyledTextCtrl_LineDown, 3237).
+-define(wxStyledTextCtrl_LineDownExtend, 3238).
+-define(wxStyledTextCtrl_LineUp, 3239).
+-define(wxStyledTextCtrl_LineUpExtend, 3240).
+-define(wxStyledTextCtrl_CharLeft, 3241).
+-define(wxStyledTextCtrl_CharLeftExtend, 3242).
+-define(wxStyledTextCtrl_CharRight, 3243).
+-define(wxStyledTextCtrl_CharRightExtend, 3244).
+-define(wxStyledTextCtrl_WordLeft, 3245).
+-define(wxStyledTextCtrl_WordLeftExtend, 3246).
+-define(wxStyledTextCtrl_WordRight, 3247).
+-define(wxStyledTextCtrl_WordRightExtend, 3248).
+-define(wxStyledTextCtrl_Home, 3249).
+-define(wxStyledTextCtrl_HomeExtend, 3250).
+-define(wxStyledTextCtrl_LineEnd, 3251).
+-define(wxStyledTextCtrl_LineEndExtend, 3252).
+-define(wxStyledTextCtrl_DocumentStart, 3253).
+-define(wxStyledTextCtrl_DocumentStartExtend, 3254).
+-define(wxStyledTextCtrl_DocumentEnd, 3255).
+-define(wxStyledTextCtrl_DocumentEndExtend, 3256).
+-define(wxStyledTextCtrl_PageUp, 3257).
+-define(wxStyledTextCtrl_PageUpExtend, 3258).
+-define(wxStyledTextCtrl_PageDown, 3259).
+-define(wxStyledTextCtrl_PageDownExtend, 3260).
+-define(wxStyledTextCtrl_EditToggleOvertype, 3261).
+-define(wxStyledTextCtrl_Cancel, 3262).
+-define(wxStyledTextCtrl_DeleteBack, 3263).
+-define(wxStyledTextCtrl_Tab, 3264).
+-define(wxStyledTextCtrl_BackTab, 3265).
+-define(wxStyledTextCtrl_NewLine, 3266).
+-define(wxStyledTextCtrl_FormFeed, 3267).
+-define(wxStyledTextCtrl_VCHome, 3268).
+-define(wxStyledTextCtrl_VCHomeExtend, 3269).
+-define(wxStyledTextCtrl_ZoomIn, 3270).
+-define(wxStyledTextCtrl_ZoomOut, 3271).
+-define(wxStyledTextCtrl_DelWordLeft, 3272).
+-define(wxStyledTextCtrl_DelWordRight, 3273).
+-define(wxStyledTextCtrl_LineCut, 3274).
+-define(wxStyledTextCtrl_LineDelete, 3275).
+-define(wxStyledTextCtrl_LineTranspose, 3276).
+-define(wxStyledTextCtrl_LineDuplicate, 3277).
+-define(wxStyledTextCtrl_LowerCase, 3278).
+-define(wxStyledTextCtrl_UpperCase, 3279).
+-define(wxStyledTextCtrl_LineScrollDown, 3280).
+-define(wxStyledTextCtrl_LineScrollUp, 3281).
+-define(wxStyledTextCtrl_DeleteBackNotLine, 3282).
+-define(wxStyledTextCtrl_HomeDisplay, 3283).
+-define(wxStyledTextCtrl_HomeDisplayExtend, 3284).
+-define(wxStyledTextCtrl_LineEndDisplay, 3285).
+-define(wxStyledTextCtrl_LineEndDisplayExtend, 3286).
+-define(wxStyledTextCtrl_HomeWrapExtend, 3287).
+-define(wxStyledTextCtrl_LineEndWrap, 3288).
+-define(wxStyledTextCtrl_LineEndWrapExtend, 3289).
+-define(wxStyledTextCtrl_VCHomeWrap, 3290).
+-define(wxStyledTextCtrl_VCHomeWrapExtend, 3291).
+-define(wxStyledTextCtrl_LineCopy, 3292).
+-define(wxStyledTextCtrl_MoveCaretInsideView, 3293).
+-define(wxStyledTextCtrl_LineLength, 3294).
+-define(wxStyledTextCtrl_BraceHighlight, 3295).
+-define(wxStyledTextCtrl_BraceBadLight, 3296).
+-define(wxStyledTextCtrl_BraceMatch, 3297).
+-define(wxStyledTextCtrl_GetViewEOL, 3298).
+-define(wxStyledTextCtrl_SetViewEOL, 3299).
+-define(wxStyledTextCtrl_SetModEventMask, 3300).
+-define(wxStyledTextCtrl_GetEdgeColumn, 3301).
+-define(wxStyledTextCtrl_SetEdgeColumn, 3302).
+-define(wxStyledTextCtrl_SetEdgeMode, 3303).
+-define(wxStyledTextCtrl_GetEdgeMode, 3304).
+-define(wxStyledTextCtrl_GetEdgeColour, 3305).
+-define(wxStyledTextCtrl_SetEdgeColour, 3306).
+-define(wxStyledTextCtrl_SearchAnchor, 3307).
+-define(wxStyledTextCtrl_SearchNext, 3308).
+-define(wxStyledTextCtrl_SearchPrev, 3309).
+-define(wxStyledTextCtrl_LinesOnScreen, 3310).
+-define(wxStyledTextCtrl_UsePopUp, 3311).
+-define(wxStyledTextCtrl_SelectionIsRectangle, 3312).
+-define(wxStyledTextCtrl_SetZoom, 3313).
+-define(wxStyledTextCtrl_GetZoom, 3314).
+-define(wxStyledTextCtrl_GetModEventMask, 3315).
+-define(wxStyledTextCtrl_SetSTCFocus, 3316).
+-define(wxStyledTextCtrl_GetSTCFocus, 3317).
+-define(wxStyledTextCtrl_SetStatus, 3318).
+-define(wxStyledTextCtrl_GetStatus, 3319).
+-define(wxStyledTextCtrl_SetMouseDownCaptures, 3320).
+-define(wxStyledTextCtrl_GetMouseDownCaptures, 3321).
+-define(wxStyledTextCtrl_SetSTCCursor, 3322).
+-define(wxStyledTextCtrl_GetSTCCursor, 3323).
+-define(wxStyledTextCtrl_SetControlCharSymbol, 3324).
+-define(wxStyledTextCtrl_GetControlCharSymbol, 3325).
+-define(wxStyledTextCtrl_WordPartLeft, 3326).
+-define(wxStyledTextCtrl_WordPartLeftExtend, 3327).
+-define(wxStyledTextCtrl_WordPartRight, 3328).
+-define(wxStyledTextCtrl_WordPartRightExtend, 3329).
+-define(wxStyledTextCtrl_SetVisiblePolicy, 3330).
+-define(wxStyledTextCtrl_DelLineLeft, 3331).
+-define(wxStyledTextCtrl_DelLineRight, 3332).
+-define(wxStyledTextCtrl_GetXOffset, 3333).
+-define(wxStyledTextCtrl_ChooseCaretX, 3334).
+-define(wxStyledTextCtrl_SetXCaretPolicy, 3335).
+-define(wxStyledTextCtrl_SetYCaretPolicy, 3336).
+-define(wxStyledTextCtrl_GetPrintWrapMode, 3337).
+-define(wxStyledTextCtrl_SetHotspotActiveForeground, 3338).
+-define(wxStyledTextCtrl_SetHotspotActiveBackground, 3339).
+-define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3340).
+-define(wxStyledTextCtrl_SetHotspotSingleLine, 3341).
+-define(wxStyledTextCtrl_ParaDownExtend, 3342).
+-define(wxStyledTextCtrl_ParaUp, 3343).
+-define(wxStyledTextCtrl_ParaUpExtend, 3344).
+-define(wxStyledTextCtrl_PositionBefore, 3345).
+-define(wxStyledTextCtrl_PositionAfter, 3346).
+-define(wxStyledTextCtrl_CopyRange, 3347).
+-define(wxStyledTextCtrl_CopyText, 3348).
+-define(wxStyledTextCtrl_SetSelectionMode, 3349).
+-define(wxStyledTextCtrl_GetSelectionMode, 3350).
+-define(wxStyledTextCtrl_LineDownRectExtend, 3351).
+-define(wxStyledTextCtrl_LineUpRectExtend, 3352).
+-define(wxStyledTextCtrl_CharLeftRectExtend, 3353).
+-define(wxStyledTextCtrl_CharRightRectExtend, 3354).
+-define(wxStyledTextCtrl_HomeRectExtend, 3355).
+-define(wxStyledTextCtrl_VCHomeRectExtend, 3356).
+-define(wxStyledTextCtrl_LineEndRectExtend, 3357).
+-define(wxStyledTextCtrl_PageUpRectExtend, 3358).
+-define(wxStyledTextCtrl_PageDownRectExtend, 3359).
+-define(wxStyledTextCtrl_StutteredPageUp, 3360).
+-define(wxStyledTextCtrl_StutteredPageUpExtend, 3361).
+-define(wxStyledTextCtrl_StutteredPageDown, 3362).
+-define(wxStyledTextCtrl_StutteredPageDownExtend, 3363).
+-define(wxStyledTextCtrl_WordLeftEnd, 3364).
+-define(wxStyledTextCtrl_WordLeftEndExtend, 3365).
+-define(wxStyledTextCtrl_WordRightEnd, 3366).
+-define(wxStyledTextCtrl_WordRightEndExtend, 3367).
+-define(wxStyledTextCtrl_SetWhitespaceChars, 3368).
+-define(wxStyledTextCtrl_SetCharsDefault, 3369).
+-define(wxStyledTextCtrl_AutoCompGetCurrent, 3370).
+-define(wxStyledTextCtrl_Allocate, 3371).
+-define(wxStyledTextCtrl_FindColumn, 3372).
+-define(wxStyledTextCtrl_GetCaretSticky, 3373).
+-define(wxStyledTextCtrl_SetCaretSticky, 3374).
+-define(wxStyledTextCtrl_ToggleCaretSticky, 3375).
+-define(wxStyledTextCtrl_SetPasteConvertEndings, 3376).
+-define(wxStyledTextCtrl_GetPasteConvertEndings, 3377).
+-define(wxStyledTextCtrl_SelectionDuplicate, 3378).
+-define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3379).
+-define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3380).
+-define(wxStyledTextCtrl_StartRecord, 3381).
+-define(wxStyledTextCtrl_StopRecord, 3382).
+-define(wxStyledTextCtrl_SetLexer, 3383).
+-define(wxStyledTextCtrl_GetLexer, 3384).
+-define(wxStyledTextCtrl_Colourise, 3385).
+-define(wxStyledTextCtrl_SetProperty, 3386).
+-define(wxStyledTextCtrl_SetKeyWords, 3387).
+-define(wxStyledTextCtrl_SetLexerLanguage, 3388).
+-define(wxStyledTextCtrl_GetProperty, 3389).
+-define(wxStyledTextCtrl_GetStyleBitsNeeded, 3390).
+-define(wxStyledTextCtrl_GetCurrentLine, 3391).
+-define(wxStyledTextCtrl_StyleSetSpec, 3392).
+-define(wxStyledTextCtrl_StyleSetFont, 3393).
+-define(wxStyledTextCtrl_StyleSetFontAttr, 3394).
+-define(wxStyledTextCtrl_StyleSetCharacterSet, 3395).
+-define(wxStyledTextCtrl_StyleSetFontEncoding, 3396).
+-define(wxStyledTextCtrl_CmdKeyExecute, 3397).
+-define(wxStyledTextCtrl_SetMargins, 3398).
+-define(wxStyledTextCtrl_GetSelection, 3399).
+-define(wxStyledTextCtrl_PointFromPosition, 3400).
+-define(wxStyledTextCtrl_ScrollToLine, 3401).
+-define(wxStyledTextCtrl_ScrollToColumn, 3402).
+-define(wxStyledTextCtrl_SetVScrollBar, 3403).
+-define(wxStyledTextCtrl_SetHScrollBar, 3404).
+-define(wxStyledTextCtrl_GetLastKeydownProcessed, 3405).
+-define(wxStyledTextCtrl_SetLastKeydownProcessed, 3406).
+-define(wxStyledTextCtrl_SaveFile, 3407).
+-define(wxStyledTextCtrl_LoadFile, 3408).
+-define(wxStyledTextCtrl_DoDragOver, 3409).
+-define(wxStyledTextCtrl_DoDropText, 3410).
+-define(wxStyledTextCtrl_GetUseAntiAliasing, 3411).
+-define(wxStyledTextCtrl_AddTextRaw, 3412).
+-define(wxStyledTextCtrl_InsertTextRaw, 3413).
+-define(wxStyledTextCtrl_GetCurLineRaw, 3414).
+-define(wxStyledTextCtrl_GetLineRaw, 3415).
+-define(wxStyledTextCtrl_GetSelectedTextRaw, 3416).
+-define(wxStyledTextCtrl_GetTextRangeRaw, 3417).
+-define(wxStyledTextCtrl_SetTextRaw, 3418).
+-define(wxStyledTextCtrl_GetTextRaw, 3419).
+-define(wxStyledTextCtrl_AppendTextRaw, 3420).
+-define(wxArtProvider_GetBitmap, 3421).
+-define(wxArtProvider_GetIcon, 3422).
+-define(wxTreeEvent_GetKeyCode, 3423).
+-define(wxTreeEvent_GetItem, 3424).
+-define(wxTreeEvent_GetKeyEvent, 3425).
+-define(wxTreeEvent_GetLabel, 3426).
+-define(wxTreeEvent_GetOldItem, 3427).
+-define(wxTreeEvent_GetPoint, 3428).
+-define(wxTreeEvent_IsEditCancelled, 3429).
+-define(wxTreeEvent_SetToolTip, 3430).
+-define(wxNotebookEvent_GetOldSelection, 3431).
+-define(wxNotebookEvent_GetSelection, 3432).
+-define(wxNotebookEvent_SetOldSelection, 3433).
+-define(wxNotebookEvent_SetSelection, 3434).
+-define(wxFileDataObject_new, 3435).
+-define(wxFileDataObject_AddFile, 3436).
+-define(wxFileDataObject_GetFilenames, 3437).
+-define(wxFileDataObject_destroy, 3438).
+-define(wxTextDataObject_new, 3439).
+-define(wxTextDataObject_GetTextLength, 3440).
+-define(wxTextDataObject_GetText, 3441).
+-define(wxTextDataObject_SetText, 3442).
+-define(wxTextDataObject_destroy, 3443).
+-define(wxBitmapDataObject_new_1_1, 3444).
+-define(wxBitmapDataObject_new_1_0, 3445).
+-define(wxBitmapDataObject_GetBitmap, 3446).
+-define(wxBitmapDataObject_SetBitmap, 3447).
+-define(wxBitmapDataObject_destroy, 3448).
+-define(wxClipboard_new, 3450).
+-define(wxClipboard_destruct, 3451).
+-define(wxClipboard_AddData, 3452).
+-define(wxClipboard_Clear, 3453).
+-define(wxClipboard_Close, 3454).
+-define(wxClipboard_Flush, 3455).
+-define(wxClipboard_GetData, 3456).
+-define(wxClipboard_IsOpened, 3457).
+-define(wxClipboard_Open, 3458).
+-define(wxClipboard_SetData, 3459).
+-define(wxClipboard_UsePrimarySelection, 3461).
+-define(wxClipboard_IsSupported, 3462).
+-define(wxClipboard_Get, 3463).
+-define(wxSpinEvent_GetPosition, 3464).
+-define(wxSpinEvent_SetPosition, 3465).
+-define(wxSplitterWindow_new_0, 3466).
+-define(wxSplitterWindow_new_2, 3467).
+-define(wxSplitterWindow_destruct, 3468).
+-define(wxSplitterWindow_Create, 3469).
+-define(wxSplitterWindow_GetMinimumPaneSize, 3470).
+-define(wxSplitterWindow_GetSashGravity, 3471).
+-define(wxSplitterWindow_GetSashPosition, 3472).
+-define(wxSplitterWindow_GetSplitMode, 3473).
+-define(wxSplitterWindow_GetWindow1, 3474).
+-define(wxSplitterWindow_GetWindow2, 3475).
+-define(wxSplitterWindow_Initialize, 3476).
+-define(wxSplitterWindow_IsSplit, 3477).
+-define(wxSplitterWindow_ReplaceWindow, 3478).
+-define(wxSplitterWindow_SetSashGravity, 3479).
+-define(wxSplitterWindow_SetSashPosition, 3480).
+-define(wxSplitterWindow_SetSashSize, 3481).
+-define(wxSplitterWindow_SetMinimumPaneSize, 3482).
+-define(wxSplitterWindow_SetSplitMode, 3483).
+-define(wxSplitterWindow_SplitHorizontally, 3484).
+-define(wxSplitterWindow_SplitVertically, 3485).
+-define(wxSplitterWindow_Unsplit, 3486).
+-define(wxSplitterWindow_UpdateSize, 3487).
+-define(wxSplitterEvent_GetSashPosition, 3488).
+-define(wxSplitterEvent_GetX, 3489).
+-define(wxSplitterEvent_GetY, 3490).
+-define(wxSplitterEvent_GetWindowBeingRemoved, 3491).
+-define(wxSplitterEvent_SetSashPosition, 3492).
+-define(wxHtmlWindow_new_0, 3493).
+-define(wxHtmlWindow_new_2, 3494).
+-define(wxHtmlWindow_AppendToPage, 3495).
+-define(wxHtmlWindow_GetOpenedAnchor, 3496).
+-define(wxHtmlWindow_GetOpenedPage, 3497).
+-define(wxHtmlWindow_GetOpenedPageTitle, 3498).
+-define(wxHtmlWindow_GetRelatedFrame, 3499).
+-define(wxHtmlWindow_HistoryBack, 3500).
+-define(wxHtmlWindow_HistoryCanBack, 3501).
+-define(wxHtmlWindow_HistoryCanForward, 3502).
+-define(wxHtmlWindow_HistoryClear, 3503).
+-define(wxHtmlWindow_HistoryForward, 3504).
+-define(wxHtmlWindow_LoadFile, 3505).
+-define(wxHtmlWindow_LoadPage, 3506).
+-define(wxHtmlWindow_SelectAll, 3507).
+-define(wxHtmlWindow_SelectionToText, 3508).
+-define(wxHtmlWindow_SelectLine, 3509).
+-define(wxHtmlWindow_SelectWord, 3510).
+-define(wxHtmlWindow_SetBorders, 3511).
+-define(wxHtmlWindow_SetFonts, 3512).
+-define(wxHtmlWindow_SetPage, 3513).
+-define(wxHtmlWindow_SetRelatedFrame, 3514).
+-define(wxHtmlWindow_SetRelatedStatusBar, 3515).
+-define(wxHtmlWindow_ToText, 3516).
+-define(wxHtmlWindow_destroy, 3517).
+-define(wxHtmlLinkEvent_GetLinkInfo, 3518).
+-define(wxSystemSettings_GetColour, 3519).
+-define(wxSystemSettings_GetFont, 3520).
+-define(wxSystemSettings_GetMetric, 3521).
+-define(wxSystemSettings_GetScreenType, 3522).
+-define(wxSystemOptions_GetOption, 3523).
+-define(wxSystemOptions_GetOptionInt, 3524).
+-define(wxSystemOptions_HasOption, 3525).
+-define(wxSystemOptions_IsFalse, 3526).
+-define(wxSystemOptions_SetOption_2_1, 3527).
+-define(wxSystemOptions_SetOption_2_0, 3528).
+-define(wxAuiNotebookEvent_SetSelection, 3529).
+-define(wxAuiNotebookEvent_GetSelection, 3530).
+-define(wxAuiNotebookEvent_SetOldSelection, 3531).
+-define(wxAuiNotebookEvent_GetOldSelection, 3532).
+-define(wxAuiNotebookEvent_SetDragSource, 3533).
+-define(wxAuiNotebookEvent_GetDragSource, 3534).
+-define(wxAuiManagerEvent_SetManager, 3535).
+-define(wxAuiManagerEvent_GetManager, 3536).
+-define(wxAuiManagerEvent_SetPane, 3537).
+-define(wxAuiManagerEvent_GetPane, 3538).
+-define(wxAuiManagerEvent_SetButton, 3539).
+-define(wxAuiManagerEvent_GetButton, 3540).
+-define(wxAuiManagerEvent_SetDC, 3541).
+-define(wxAuiManagerEvent_GetDC, 3542).
+-define(wxAuiManagerEvent_Veto, 3543).
+-define(wxAuiManagerEvent_GetVeto, 3544).
+-define(wxAuiManagerEvent_SetCanVeto, 3545).
+-define(wxAuiManagerEvent_CanVeto, 3546).
+-define(wxLogNull_new, 3547).
+-define(wxLogNull_destroy, 3548).
+-define(wxTaskBarIcon_new, 3549).
+-define(wxTaskBarIcon_destruct, 3550).
+-define(wxTaskBarIcon_PopupMenu, 3551).
+-define(wxTaskBarIcon_RemoveIcon, 3552).
+-define(wxTaskBarIcon_SetIcon, 3553).
+-define(wxLocale_new_0, 3554).
+-define(wxLocale_new_2, 3556).
+-define(wxLocale_destruct, 3557).
+-define(wxLocale_Init, 3559).
+-define(wxLocale_AddCatalog_1, 3560).
+-define(wxLocale_AddCatalog_3, 3561).
+-define(wxLocale_AddCatalogLookupPathPrefix, 3562).
+-define(wxLocale_GetCanonicalName, 3563).
+-define(wxLocale_GetLanguage, 3564).
+-define(wxLocale_GetLanguageName, 3565).
+-define(wxLocale_GetLocale, 3566).
+-define(wxLocale_GetName, 3567).
+-define(wxLocale_GetString_2, 3568).
+-define(wxLocale_GetString_4, 3569).
+-define(wxLocale_GetHeaderValue, 3570).
+-define(wxLocale_GetSysName, 3571).
+-define(wxLocale_GetSystemEncoding, 3572).
+-define(wxLocale_GetSystemEncodingName, 3573).
+-define(wxLocale_GetSystemLanguage, 3574).
+-define(wxLocale_IsLoaded, 3575).
+-define(wxLocale_IsOk, 3576).
+-define(wxActivateEvent_GetActive, 3577).
+-define(wxPopupWindow_new_2, 3579).
+-define(wxPopupWindow_new_0, 3580).
+-define(wxPopupWindow_destruct, 3582).
+-define(wxPopupWindow_Create, 3583).
+-define(wxPopupWindow_Position, 3584).
+-define(wxPopupTransientWindow_new_0, 3585).
+-define(wxPopupTransientWindow_new_2, 3586).
+-define(wxPopupTransientWindow_destruct, 3587).
+-define(wxPopupTransientWindow_Popup, 3588).
+-define(wxPopupTransientWindow_Dismiss, 3589).
+-define(wxOverlay_new, 3590).
+-define(wxOverlay_destruct, 3591).
+-define(wxOverlay_Reset, 3592).
+-define(wxDCOverlay_new_6, 3593).
+-define(wxDCOverlay_new_2, 3594).
+-define(wxDCOverlay_destruct, 3595).
+-define(wxDCOverlay_Clear, 3596).
+-define(wxDropFilesEvent_GetPosition, 3597).
+-define(wxDropFilesEvent_GetNumberOfFiles, 3598).
+-define(wxDropFilesEvent_GetFiles, 3599).
diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl
index 40ee308358..40170b6eb1 100644
--- a/lib/wx/src/wx_object.erl
+++ b/lib/wx/src/wx_object.erl
@@ -55,7 +55,7 @@
%% When stop is returned in one of the functions above with Reason =
%% normal | shutdown | Term, terminate(State) is called. It lets the
%% user module clean up, it is always called when server terminates or
-%% when wxObject() in the driver is deleted. If the Parent process
+%% when wx_object() in the driver is deleted. If the Parent process
%% terminates the Module:terminate/2 function is called. <br/>
%% terminate(Reason, State)
%%
@@ -171,58 +171,59 @@
%% -----------------------------------------------------------------
-%% @spec (Mod, Args, Options) -> wxWindow:wxWindow()
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start(Mod, Args, Options) ->
gen_response(gen:start(?MODULE, nolink, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-
-%% @spec (Name, Mod, Args, Options) -> wxWindow:wxWindow()
-%% Name = {local, atom()}
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
+
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start(Name, Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Name::{local, atom()},
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start(Name, Mod, Args, Options) ->
gen_response(gen:start(?MODULE, nolink, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-%% @spec (Mod, Args, Options) -> wxWindow:wxWindow()
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start_link(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start_link(Mod, Args, Options) ->
gen_response(gen:start(?MODULE, link, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-%% @spec (Name, Mod, Args, Options) -> wxWindow:wxWindow()
-%% Name = {local, atom()}
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start_link(Name, Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Name::{local, atom()},
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start_link(Name, Mod, Args, Options) ->
gen_response(gen:start(?MODULE, link, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-
+
gen_response({ok, Pid}) ->
receive {ack, Pid, Ref = #wx_ref{}} -> Ref end;
gen_response(Reply) ->
Reply.
-%% @spec (Ref::wxObject()|atom()|pid()) -> ok
%% @doc Stops a generic wx_object server with reason 'normal'.
%% Invokes terminate(Reason,State) in the server. The call waits until
%% the process is terminated. If the process does not exist, an
%% exception is raised.
+-spec stop(Obj) -> ok when
+ Obj::wx:wx_object()|atom()|pid().
stop(Ref = #wx_ref{state=Pid}) when is_pid(Pid) ->
try
gen:stop(Pid)
@@ -236,11 +237,14 @@ stop(Name) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({ExitReason, {?MODULE, stop, [Name]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Reason::term(), Timeout::timeout()) -> ok
%% @doc Stops a generic wx_object server with the given Reason.
%% Invokes terminate(Reason,State) in the server. The call waits until
%% the process is terminated. If the call times out, or if the process
%% does not exist, an exception is raised.
+-spec stop(Obj, Reason, Timeout) -> ok when
+ Obj::wx:wx_object()|atom()|pid(),
+ Reason::term(),
+ Timeout::timeout().
stop(Ref = #wx_ref{state=Pid}, Reason, Timeout) when is_pid(Pid) ->
try
gen:stop(Pid, Reason, Timeout)
@@ -254,12 +258,14 @@ stop(Name, Reason, Timeout) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({ExitReason, {?MODULE, stop, [Name, Reason, Timeout]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> term()
%% @doc Make a call to a wx_object server.
%% The call waits until it gets a result.
%% Invokes handle_call(Request, From, State) in the server
+-spec call(Obj, Request) -> term() when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term().
call(Ref = #wx_ref{state=Pid}, Request) when is_pid(Pid) ->
- try
+ try
{ok,Res} = gen:call(Pid, '$gen_call', Request, infinity),
Res
catch _:Reason ->
@@ -272,10 +278,13 @@ call(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
catch _:Reason ->
erlang:error({Reason, {?MODULE, call, [Name, Request]}})
end.
-
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term(), Timeout::integer()) -> term()
+
%% @doc Make a call to a wx_object server with a timeout.
%% Invokes handle_call(Request, From, State) in server
+-spec call(Obj, Request, Timeout) -> term() when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term(),
+ Timeout::integer().
call(Ref = #wx_ref{state=Pid}, Request, Timeout) when is_pid(Pid) ->
try
{ok,Res} = gen:call(Pid, '$gen_call', Request, Timeout),
@@ -291,10 +300,11 @@ call(Name, Request, Timeout) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({Reason, {?MODULE, call, [Name, Request, Timeout]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> ok
%% @doc Make a cast to a wx_object server.
%% Invokes handle_cast(Request, State) in the server
-
+-spec cast(Obj, Request) -> ok when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term().
cast(#wx_ref{state=Pid}, Request) when is_pid(Pid) ->
Pid ! {'$gen_cast',Request},
ok;
@@ -302,21 +312,23 @@ cast(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
Name ! {'$gen_cast',Request},
ok.
-%% @spec (Ref::wxObject()) -> pid()
%% @doc Get the pid of the object handle.
+-spec get_pid(Obj) -> pid() when
+ Obj::wx:wx_object()|atom()|pid().
get_pid(#wx_ref{state=Pid}) when is_pid(Pid) ->
Pid.
-%% @spec (Ref::wxObject(), pid()) -> wxObject()
%% @doc Sets the controlling process of the object handle.
+-spec set_pid(Obj, pid()) -> wx:wx_object() when
+ Obj::wx:wx_object()|atom()|pid().
set_pid(#wx_ref{}=R, Pid) when is_pid(Pid) ->
R#wx_ref{state=Pid}.
%% -----------------------------------------------------------------
%% Send a reply to the client.
%% -----------------------------------------------------------------
-%% @spec (From::tuple(), Reply::term()) -> pid()
%% @doc Get the pid of the object handle.
+-spec reply({pid(), Tag::term()}, Reply::term()) -> pid().
reply({To, Tag}, Reply) ->
catch To ! {Tag, Reply}.
diff --git a/lib/wx/src/wxe.hrl b/lib/wx/src/wxe.hrl
index da65cb939d..0f42d7e82e 100644
--- a/lib/wx/src/wxe.hrl
+++ b/lib/wx/src/wxe.hrl
@@ -44,6 +44,8 @@
-define(I, signed-native).
-define(F, float-native).
+-define(is_chardata(String), (is_list(String) orelse is_binary(String))).
+
-define(WXE_IDENTIFIER, wx_env).
-define(BATCH_BEGIN, 0).
-define(BATCH_END, 1).
diff --git a/lib/wx/test/wx_opengl_SUITE.erl b/lib/wx/test/wx_opengl_SUITE.erl
index 643a0df6a3..3de9209fae 100644
--- a/lib/wx/test/wx_opengl_SUITE.erl
+++ b/lib/wx/test/wx_opengl_SUITE.erl
@@ -111,6 +111,8 @@ canvas(Config) ->
?m(false, wx:is_null(wxGLCanvas:getContext(Canvas))),
?m({'EXIT', {{error, no_gl_context,_},_}}, gl:getString(?GL_VENDOR)),
+ gl:viewport(0,0,50,50), %% Show cause an error report
+
?m(ok, wxGLCanvas:setCurrent(Canvas)),
io:format("Vendor: ~s~n", [gl:getString(?GL_VENDOR)]),
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index de4e5e1935..cfa256fb12 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 1.7
+WX_VSN = 1.8
diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml
index 0abcb87998..0c89311579 100644
--- a/lib/xmerl/doc/src/notes.xml
+++ b/lib/xmerl/doc/src/notes.xml
@@ -32,6 +32,97 @@
<p>This document describes the changes made to the Xmerl application.</p>
+<section><title>Xmerl 1.3.14</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> A couple of bugs are fixed in the sax parser
+ (xmerl_sax_parser). </p> <list> <item>The continuation
+ function was not called correctly when the XML directive
+ was fragmented. </item> <item>When the event callback
+ modules (xmerl_sax_old_dom and xmerl_sax_simple) got an
+ endDocument event at certain conditions the parser
+ crashed.</item> <item>Replaced internal ets table with
+ map to avoid table leakage.</item> </list>
+ <p>
+ Own Id: OTP-14430</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Xmerl 1.3.13</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The namespace_conformant option in xmerl_scan did not
+ work when parsing documents without explicit XML
+ namespace declaration.</p>
+ <p>
+ Own Id: OTP-14139</p>
+ </item>
+ <item>
+ <p> Fix a "well-formedness" bug in the XML Sax parser so
+ it returns an error if there are something more in the
+ file after the matching document. If one using the
+ xmerl_sax_parser:stream() a rest is allowed which then
+ can be sent to a new call of xmerl_sax_parser:stream() to
+ parse next document. </p> <p> This is done to be
+ compliant with XML conformance tests. </p>
+ <p>
+ Own Id: OTP-14211</p>
+ </item>
+ <item>
+ <p> Fixed compiler and dialyzer warnings in the XML SAX
+ parser. </p>
+ <p>
+ Own Id: OTP-14212</p>
+ </item>
+ <item>
+ <p> Change how to interpret end of document in the XML
+ SAX parser to comply with Tim Brays comment on the
+ standard. This makes it possible to handle more than one
+ doc on a stream, the standard makes it impossible to know
+ when the document is ended without waiting for the next
+ document (and not always even that). </p> <p> Tim Brays
+ comment: </p> <p> Trailing "Misc"<br/> The fact that
+ you're allowed some trailing junk after the root element,
+ I decided (but unfortunately too late) is a real design
+ error in XML. If I'm writing a network client, I'm
+ probably going to close the link as soon as a I see the
+ root element end-tag, and not depend on the other end
+ closing it down properly.<br/> Furthermore, if I want to
+ send a succession of XML documents over a network link,
+ if I find a processing instruction after a root element,
+ is it a trailer on the previous document, or part of the
+ prolog of the next? </p>
+ <p>
+ Own Id: OTP-14213</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Xmerl 1.3.12</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix a number of broken links in the xmerl
+ documentation. </p>
+ <p>
+ Own Id: OTP-13880</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Xmerl 1.3.11</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/xmerl/src/xmerl_eventp.erl b/lib/xmerl/src/xmerl_eventp.erl
index 2cb76abc6e..8d7ea25e24 100644
--- a/lib/xmerl/src/xmerl_eventp.erl
+++ b/lib/xmerl/src/xmerl_eventp.erl
@@ -25,6 +25,90 @@
%% Each contain more elaborate settings of xmerl_scan that makes usage of
%% the customization functions.
%%
+%% @type xmlElement() = #xmlElement{}.
+%%
+%% @type option_list(). <p>Options allow to customize the behaviour of the
+%% scanner.
+%% See also <a href="xmerl_examples.html">tutorial</a> on customization
+%% functions.
+%% </p>
+%% <p>
+%% Possible options are:
+%% </p>
+%% <dl>
+%% <dt><code>{acc_fun, Fun}</code></dt>
+%% <dd>Call back function to accumulate contents of entity.</dd>
+%% <dt><code>{continuation_fun, Fun} |
+%% {continuation_fun, Fun, ContinuationState}</code></dt>
+%% <dd>Call back function to decide what to do if the scanner runs into EOF
+%% before the document is complete.</dd>
+%% <dt><code>{event_fun, Fun} |
+%% {event_fun, Fun, EventState}</code></dt>
+%% <dd>Call back function to handle scanner events.</dd>
+%% <dt><code>{fetch_fun, Fun} |
+%% {fetch_fun, Fun, FetchState}</code></dt>
+%% <dd>Call back function to fetch an external resource.</dd>
+%% <dt><code>{hook_fun, Fun} |
+%% {hook_fun, Fun, HookState}</code></dt>
+%% <dd>Call back function to process the document entities once
+%% identified.</dd>
+%% <dt><code>{close_fun, Fun}</code></dt>
+%% <dd>Called when document has been completely parsed.</dd>
+%% <dt><code>{rules, ReadFun, WriteFun, RulesState} |
+%% {rules, Rules}</code></dt>
+%% <dd>Handles storing of scanner information when parsing.</dd>
+%% <dt><code>{user_state, UserState}</code></dt>
+%% <dd>Global state variable accessible from all customization functions</dd>
+%%
+%% <dt><code>{fetch_path, PathList}</code></dt>
+%% <dd>PathList is a list of
+%% directories to search when fetching files. If the file in question
+%% is not in the fetch_path, the URI will be used as a file
+%% name.</dd>
+%% <dt><code>{space, Flag}</code></dt>
+%% <dd>'preserve' (default) to preserve spaces, 'normalize' to
+%% accumulate consecutive whitespace and replace it with one space.</dd>
+%% <dt><code>{line, Line}</code></dt>
+%% <dd>To specify starting line for scanning in document which contains
+%% fragments of XML.</dd>
+%% <dt><code>{namespace_conformant, Flag}</code></dt>
+%% <dd>Controls whether to behave as a namespace conformant XML parser,
+%% 'false' (default) to not otherwise 'true'.</dd>
+%% <dt><code>{validation, Flag}</code></dt>
+%% <dd>Controls whether to process as a validating XML parser:
+%% 'off' (default) no validation, or validation 'dtd' by DTD or 'schema'
+%% by XML Schema. 'false' and 'true' options are obsolete
+%% (i.e. they may be removed in a future release), if used 'false'
+%% equals 'off' and 'true' equals 'dtd'.</dd>
+%% <dt><code>{schemaLocation, [{Namespace,Link}|...]}</code></dt>
+%% <dd>Tells explicitly which XML Schema documents to use to validate
+%% the XML document. Used together with the
+%% <code>{validation,schema}</code> option.</dd>
+%% <dt><code>{quiet, Flag}</code></dt>
+%% <dd>Set to 'true' if xmerl should behave quietly and not output any
+%% information to standard output (default 'false').</dd>
+%% <dt><code>{doctype_DTD, DTD}</code></dt>
+%% <dd>Allows to specify DTD name when it isn't available in the XML
+%% document. This option has effect only together with
+%% <code>{validation,'dtd'</code> option.</dd>
+%% <dt><code>{xmlbase, Dir}</code></dt>
+%% <dd>XML Base directory. If using string/1 default is current directory.
+%% If using file/1 default is directory of given file.</dd>
+%% <dt><code>{encoding, Enc}</code></dt>
+%% <dd>Set default character set used (default UTF-8).
+%% This character set is used only if not explicitly given by the XML
+%% declaration. </dd>
+%% <dt><code>{document, Flag}</code></dt>
+%% <dd>Set to 'true' if xmerl should return a complete XML document
+%% as an xmlDocument record (default 'false').</dd>
+%% <dt><code>{comments, Flag}</code></dt>
+%% <dd>Set to 'false' if xmerl should skip comments otherwise they will
+%% be returned as xmlComment records (default 'true').</dd>
+%% <dt><code>{default_attrs, Flag}</code></dt>
+%% <dd>Set to 'true' if xmerl should add to elements missing attributes
+%% with a defined default value (default 'false').</dd>
+%% </dl>
+%%
-module(xmerl_eventp).
-vsn('0.19').
-date('03-09-17').
diff --git a/lib/xmerl/src/xmerl_sax_old_dom.erl b/lib/xmerl/src/xmerl_sax_old_dom.erl
index fefcf03fce..411121370f 100644
--- a/lib/xmerl/src/xmerl_sax_old_dom.erl
+++ b/lib/xmerl/src/xmerl_sax_old_dom.erl
@@ -127,9 +127,10 @@ build_dom(endDocument,
State#xmerl_sax_old_dom_state{dom=[Decl, Current#xmlElement{
content=lists:reverse(C)
}]};
- _ ->
- %%?dbg("~p\n", [D]),
- ?error("we're not at end the document when endDocument event is encountered.")
+ _ ->
+ %% endDocument is also sent by the parser when a fault occur to tell
+ %% the event receiver that no more input will be sent
+ State
end;
%% Element
diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl
index 318a0cf7f4..e383c4c349 100644
--- a/lib/xmerl/src/xmerl_sax_parser.erl
+++ b/lib/xmerl/src/xmerl_sax_parser.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -33,6 +33,7 @@
%% External exports
%%----------------------------------------------------------------------
-export([file/2,
+ stream/3,
stream/2]).
%%----------------------------------------------------------------------
@@ -63,7 +64,7 @@
%% Description: Parse file containing an XML document.
%%----------------------------------------------------------------------
file(Name,Options) ->
- case file:open(Name, [raw, read,binary]) of
+ case file:open(Name, [raw, read_ahead, read,binary]) of
{error, Reason} ->
{error,{Name, file:format_error(Reason)}};
{ok, FD} ->
@@ -72,11 +73,12 @@ file(Name,Options) ->
File = filename:basename(Name),
ContinuationFun = fun default_continuation_cb/1,
Res = stream(<<>>,
- [{continuation_fun, ContinuationFun},
- {continuation_state, FD},
- {current_location, CL},
- {entity, File}
- |Options]),
+ [{continuation_fun, ContinuationFun},
+ {continuation_state, FD},
+ {current_location, CL},
+ {entity, File}
+ |Options],
+ file),
ok = file:close(FD),
Res
end.
@@ -92,19 +94,22 @@ file(Name,Options) ->
%% EventState = term()
%% Description: Parse a stream containing an XML document.
%%----------------------------------------------------------------------
-stream(Xml, Options) when is_list(Xml), is_list(Options) ->
+stream(Xml, Options) ->
+ stream(Xml, Options, stream).
+
+stream(Xml, Options, InputType) when is_list(Xml), is_list(Options) ->
State = parse_options(Options, initial_state()),
- case State#xmerl_sax_parser_state.file_type of
+ case State#xmerl_sax_parser_state.file_type of
dtd ->
xmerl_sax_parser_list:parse_dtd(Xml,
State#xmerl_sax_parser_state{encoding = list,
- input_type = stream});
+ input_type = InputType});
normal ->
xmerl_sax_parser_list:parse(Xml,
State#xmerl_sax_parser_state{encoding = list,
- input_type = stream})
+ input_type = InputType})
end;
-stream(Xml, Options) when is_binary(Xml), is_list(Options) ->
+stream(Xml, Options, InputType) when is_binary(Xml), is_list(Options) ->
case parse_options(Options, initial_state()) of
{error, Reason} -> {error, Reason};
State ->
@@ -115,21 +120,22 @@ stream(Xml, Options) when is_binary(Xml), is_list(Options) ->
normal ->
parse
end,
- case detect_charset(Xml, State) of
- {error, Reason} -> {fatal_error,
- {
- State#xmerl_sax_parser_state.current_location,
- State#xmerl_sax_parser_state.entity,
- 1
- },
- Reason,
- [],
- State#xmerl_sax_parser_state.event_state};
- {Xml1, State1} ->
- parse_binary(Xml1,
- State1#xmerl_sax_parser_state{input_type = stream},
- ParseFunction)
- end
+ try
+ {Xml1, State1} = detect_charset(Xml, State),
+ parse_binary(Xml1,
+ State1#xmerl_sax_parser_state{input_type = InputType},
+ ParseFunction)
+ catch
+ throw:{fatal_error, {State2, Reason}} ->
+ {fatal_error,
+ {
+ State2#xmerl_sax_parser_state.current_location,
+ State2#xmerl_sax_parser_state.entity,
+ 1
+ },
+ Reason, [],
+ State2#xmerl_sax_parser_state.event_state}
+ end
end.
%%----------------------------------------------------------------------
@@ -151,8 +157,8 @@ parse_binary(Xml, #xmerl_sax_parser_state{encoding={utf16,big}}=State, F) ->
xmerl_sax_parser_utf16be:F(Xml, State);
parse_binary(Xml, #xmerl_sax_parser_state{encoding=latin1}=State, F) ->
xmerl_sax_parser_latin1:F(Xml, State);
-parse_binary(_, #xmerl_sax_parser_state{encoding=Enc}, _) ->
- {error, lists:flatten(io_lib:format("Charcter set ~p not supported", [Enc]))}.
+parse_binary(_, #xmerl_sax_parser_state{encoding=Enc}, State) ->
+ ?fatal_error(State, lists:flatten(io_lib:format("Charcter set ~p not supported", [Enc]))).
%%----------------------------------------------------------------------
%% Function: initial_state/0
@@ -206,8 +212,7 @@ parse_options([{entity, Entity} |Options], State) ->
parse_options([skip_external_dtd |Options], State) ->
parse_options(Options, State#xmerl_sax_parser_state{skip_external_dtd = true});
parse_options([O |_], _State) ->
- {error,
- lists:flatten(io_lib:format("Option: ~p not supported", [O]))}.
+ {error, lists:flatten(io_lib:format("Option: ~p not supported", [O]))}.
check_encoding_option(E) when E==utf8; E=={utf16,little}; E=={utf16,big};
@@ -225,16 +230,10 @@ check_encoding_option(E) ->
%% Output: {utf8|utf16le|utf16be|iso8859, Xml, State}
%% Description: Detects which character set is used in a binary stream.
%%----------------------------------------------------------------------
-detect_charset(<<>>, #xmerl_sax_parser_state{continuation_fun = undefined} = _) ->
- throw({error, "Can't detect character encoding due to no indata"});
-detect_charset(<<>>, #xmerl_sax_parser_state{continuation_fun = CFun,
- continuation_state = CState} = State) ->
- case CFun(CState) of
- {<<>>, _} ->
- throw({error, "Can't detect character encoding due to lack of indata"});
- {NewBytes, NewContState} ->
- detect_charset(NewBytes, State#xmerl_sax_parser_state{continuation_state = NewContState})
- end;
+detect_charset(<<>>, #xmerl_sax_parser_state{continuation_fun = undefined} = State) ->
+ ?fatal_error(State, "Can't detect character encoding due to lack of indata");
+detect_charset(<<>>, State) ->
+ cf(<<>>, State, fun detect_charset/2);
detect_charset(Bytes, State) ->
case unicode:bom_to_encoding(Bytes) of
{latin1, 0} ->
@@ -244,25 +243,47 @@ detect_charset(Bytes, State) ->
{RealBytes, State#xmerl_sax_parser_state{encoding=Enc}}
end.
+detect_charset_1(<<16#00>> = Xml, State) ->
+ cf(Xml, State, fun detect_charset_1/2);
+detect_charset_1(<<16#00, 16#3C>> = Xml, State) ->
+ cf(Xml, State, fun detect_charset_1/2);
+detect_charset_1(<<16#00, 16#3C, 16#00>> = Xml, State) ->
+ cf(Xml, State, fun detect_charset_1/2);
detect_charset_1(<<16#00, 16#3C, 16#00, 16#3F, _/binary>> = Xml, State) ->
{Xml, State#xmerl_sax_parser_state{encoding={utf16, big}}};
+detect_charset_1(<<16#3C>> = Xml, State) ->
+ cf(Xml, State, fun detect_charset_1/2);
+detect_charset_1(<<16#3C, 16#00>> = Xml, State) ->
+ cf(Xml, State, fun detect_charset_1/2);
+detect_charset_1(<<16#3C, 16#00, 16#3F>> = Xml, State) ->
+ cf(Xml, State, fun detect_charset_1/2);
detect_charset_1(<<16#3C, 16#00, 16#3F, 16#00, _/binary>> = Xml, State) ->
{Xml, State#xmerl_sax_parser_state{encoding={utf16, little}}};
-detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml2/binary>> = Xml, State) ->
- case parse_xml_directive(Xml2) of
+detect_charset_1(<<16#3C>> = Xml, State) ->
+ cf(Xml, State, fun detect_charset_1/2);
+detect_charset_1(<<16#3C, 16#3F>> = Xml, State) ->
+ cf(Xml, State, fun detect_charset_1/2);
+detect_charset_1(<<16#3C, 16#3F, 16#78>> = Xml, State) ->
+ cf(Xml, State, fun detect_charset_1/2);
+detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D>> = Xml, State) ->
+ cf(Xml, State, fun detect_charset_1/2);
+detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml2/binary>>, State) ->
+ {Xml3, State1} = read_until_end_of_xml_directive(Xml2, State),
+ case parse_xml_directive(Xml3) of
{error, Reason} ->
- {error, Reason};
+ ?fatal_error(State, Reason);
AttrList ->
case lists:keysearch("encoding", 1, AttrList) of
{value, {_, E}} ->
case convert_encoding(E) of
{error, Reason} ->
- {error, Reason};
+ ?fatal_error(State, Reason);
Enc ->
- {Xml, State#xmerl_sax_parser_state{encoding=Enc}}
+ {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>,
+ State1#xmerl_sax_parser_state{encoding=Enc}}
end;
_ ->
- {Xml, State}
+ {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, State1}
end
end;
detect_charset_1(Xml, State) ->
@@ -372,7 +393,7 @@ parse_value_1(<<C, Rest/binary>>, Stop, Acc) ->
parse_value_1(Rest, Stop, [C |Acc]).
%%======================================================================
-%%Default functions
+%% Default functions
%%======================================================================
%%----------------------------------------------------------------------
%% Function: default_event_cb(Event, LineNo, State) -> Result
@@ -388,7 +409,7 @@ default_event_cb(_Event, _LineNo, State) ->
%%----------------------------------------------------------------------
%% Function: default_continuation_cb(IoDevice) -> Result
%% IoDevice = iodevice()
-%% Output: Result = {[char()], State}
+%% Output: Result = {binary(), IoDevice}
%% Description: Default continuation callback reading blocks.
%%----------------------------------------------------------------------
default_continuation_cb(IoDevice) ->
@@ -398,3 +419,82 @@ default_continuation_cb(IoDevice) ->
{ok, FileBin} ->
{FileBin, IoDevice}
end.
+
+%%----------------------------------------------------------------------
+%% Function: read_until_end_of_xml_directive(Rest, State) -> Result
+%% Rest = binary()
+%% Output: Result = {binary(), State}
+%% Description: Reads a utf8 or latin1 until it finds '?>'
+%%----------------------------------------------------------------------
+read_until_end_of_xml_directive(Rest, State) ->
+ case binary:match(Rest, <<"?>">>) of
+ nomatch ->
+ case cf(Rest, State) of
+ {<<>>, _} ->
+ ?fatal_error(State, "Can't detect character encoding due to lack of indata");
+ {NewBytes, NewState} ->
+ read_until_end_of_xml_directive(NewBytes, NewState)
+ end;
+ _ ->
+ {Rest, State}
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Function : cf(Rest, State) -> Result
+%% Parameters: Rest = binary()
+%% State = #xmerl_sax_parser_state{}
+%% NextCall = fun()
+%% Result : {Rest, State}
+%% Description: Function that uses provided fun to read another chunk from
+%% input stream and calls the fun in NextCall.
+%%----------------------------------------------------------------------
+cf(_Rest, #xmerl_sax_parser_state{continuation_fun = undefined} = State) ->
+ ?fatal_error(State, "Continuation function undefined");
+cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State) ->
+ Result =
+ try
+ CFun(CState)
+ catch
+ throw:ErrorTerm ->
+ ?fatal_error(State, ErrorTerm);
+ exit:Reason ->
+ ?fatal_error(State, {'EXIT', Reason})
+ end,
+ case Result of
+ {<<>>, _} ->
+ ?fatal_error(State, "Can't detect character encoding due to lack of indata");
+ {NewBytes, NewContState} ->
+ {<<Rest/binary, NewBytes/binary>>,
+ State#xmerl_sax_parser_state{continuation_state = NewContState}}
+ end.
+
+%%----------------------------------------------------------------------
+%% Function : cf(Rest, State, NextCall) -> Result
+%% Parameters: Rest = binary()
+%% State = #xmerl_sax_parser_state{}
+%% NextCall = fun()
+%% Result : {Rest, State}
+%% Description: Function that uses provided fun to read another chunk from
+%% input stream and calls the fun in NextCall.
+%%----------------------------------------------------------------------
+cf(_Rest, #xmerl_sax_parser_state{continuation_fun = undefined} = State, _) ->
+ ?fatal_error(State, "Continuation function undefined");
+cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State,
+ NextCall) ->
+ Result =
+ try
+ CFun(CState)
+ catch
+ throw:ErrorTerm ->
+ ?fatal_error(State, ErrorTerm);
+ exit:Reason ->
+ ?fatal_error(State, {'EXIT', Reason})
+ end,
+ case Result of
+ {<<>>, _} ->
+ ?fatal_error(State, "Can't detect character encoding due to lack of indata");
+ {NewBytes, NewContState} ->
+ NextCall(<<Rest/binary, NewBytes/binary>>,
+ State#xmerl_sax_parser_state{continuation_state = NewContState})
+ end.
diff --git a/lib/xmerl/src/xmerl_sax_parser.hrl b/lib/xmerl/src/xmerl_sax_parser.hrl
index 932ab0cec5..56a3a42e5f 100644
--- a/lib/xmerl/src/xmerl_sax_parser.hrl
+++ b/lib/xmerl/src/xmerl_sax_parser.hrl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -88,14 +88,7 @@
current_location, % Location of the currently parsed XML entity
entity, % Parsed XML entity
skip_external_dtd = false,% If true the external DTD is skipped during parsing
- input_type % Source type: file | stream.
- % This field is a preparation for an fix in R17 of a bug in
- % the conformance against the standard.
- % Today a file which contains two XML documents will be considered
- % well-formed and the second is placed in the rest part of the
- % return tuple, according to the conformance tests this should fail.
- % In the future this will fail if xmerl_sax_aprser:file/2 is used but
- % left to the user in the xmerl_sax_aprser:stream/2 case.
+ input_type % Source type: file | stream
}).
diff --git a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc
index 4d75805b9b..1dca9608cb 100644
--- a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc
+++ b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc
@@ -1,7 +1,7 @@
%%-*-erlang-*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -64,29 +64,42 @@
%% Description: Parsing XML from input stream.
%%----------------------------------------------------------------------
parse(Xml, State) ->
- RefTable = ets:new(xmerl_sax_entity_refs, [private]),
-
- State1 = event_callback(startDocument, State),
-
- case catch parse_document(Xml, State1#xmerl_sax_parser_state{ref_table=RefTable}) of
- {ok, Rest, State2} ->
- State3 = event_callback(endDocument, State2),
- ets:delete(RefTable),
- {ok, State3#xmerl_sax_parser_state.event_state, Rest};
- {fatal_error, {State2, Reason}} ->
- State3 = event_callback(endDocument, State2),
- ets:delete(RefTable),
- format_error(fatal_error, State3, Reason);
- {event_receiver_error, State2, {Tag, Reason}} ->
- State3 = event_callback(endDocument, State2),
- ets:delete(RefTable),
- format_error(Tag, State3, Reason);
- Other ->
- _State2 = event_callback(endDocument, State1),
- ets:delete(RefTable),
- throw(Other)
+ RefTable = maps:new(),
+
+ try
+ State1 = event_callback(startDocument, State),
+ Result = parse_document(Xml, State1#xmerl_sax_parser_state{ref_table=RefTable}),
+ handle_end_document(Result)
+ catch
+ throw:Exception ->
+ handle_end_document(Exception);
+ _:OtherError ->
+ handle_end_document({other, OtherError, State})
end.
+ % case catch parse_document(Xml, State1#xmerl_sax_parser_state{ref_table=RefTable}) of
+ % {ok, Rest, State2} ->
+ % State3 = event_callback(endDocument, State2),
+ % case check_if_rest_ok(State3#xmerl_sax_parser_state.input_type, Rest) of
+ % true ->
+ % {ok, State3#xmerl_sax_parser_state.event_state, Rest};
+ % false ->
+ % format_error(fatal_error, State3, "Input found after legal document")
+ % end;
+ % {fatal_error, {State2, Reason}} ->
+ % State3 = event_callback(endDocument, State2),
+ % format_error(fatal_error, State3, Reason);
+ % {event_receiver_error, State2, {Tag, Reason}} ->
+ % State3 = event_callback(endDocument, State2),
+ % format_error(Tag, State3, Reason);
+ % {endDocument, Rest, State2} ->
+ % State3 = event_callback(endDocument, State2),
+ % {ok, State3#xmerl_sax_parser_state.event_state, Rest};
+ % Other ->
+ % _State2 = event_callback(endDocument, State1),
+ % {fatal_error, Other}
+ % end.
+
%%----------------------------------------------------------------------
%% Function: parse_dtd(Xml, State) -> Result
%% Input: Xml = string() | binary()
@@ -96,38 +109,120 @@ parse(Xml, State) ->
%% Description: Parsing XML DTD from input stream.
%%----------------------------------------------------------------------
parse_dtd(Xml, State) ->
- RefTable = ets:new(xmerl_sax_entity_refs, [private]),
-
- State1 = event_callback(startDocument, State),
-
- case catch parse_external_entity_1(Xml, State1#xmerl_sax_parser_state{ref_table=RefTable}) of
- {fatal_error, {State2, Reason}} ->
- State3 = event_callback(endDocument, State2),
- ets:delete(RefTable),
- format_error(fatal_error, State3, Reason);
- {event_receiver_error, State2, {Tag, Reason}} ->
- State3 = event_callback(endDocument, State2),
- format_error(Tag, State3, Reason);
- {Rest, State2} when is_record(State2, xmerl_sax_parser_state) ->
- State3 = event_callback(endDocument, State2),
- ets:delete(RefTable),
- {ok, State3#xmerl_sax_parser_state.event_state, Rest};
- {endDocument, Rest, State2} when is_record(State2, xmerl_sax_parser_state) ->
- State3 = event_callback(endDocument, State2),
- ets:delete(RefTable),
- {ok, State3#xmerl_sax_parser_state.event_state, Rest};
- Other ->
- _State2 = event_callback(endDocument, State1),
- ets:delete(RefTable),
- throw(Other)
+ RefTable = maps:new(),
+
+ try
+ State1 = event_callback(startDocument, State),
+ Result = parse_external_entity_1(Xml, State1#xmerl_sax_parser_state{ref_table=RefTable}),
+ handle_end_document(Result)
+ catch
+ throw:Exception ->
+ handle_end_document(Exception);
+ _:OtherError ->
+ handle_end_document({other, OtherError, State})
end.
+ % case catch parse_external_entity_1(Xml, State1#xmerl_sax_parser_state{ref_table=RefTable}) of
+ % {fatal_error, {State2, Reason}} ->
+ % State3 = event_callback(endDocument, State2),
+ % format_error(fatal_error, State3, Reason);
+ % {event_receiver_error, State2, {Tag, Reason}} ->
+ % State3 = event_callback(endDocument, State2),
+ % format_error(Tag, State3, Reason);
+ % {Rest, State2} when is_record(State2, xmerl_sax_parser_state) ->
+ % State3 = event_callback(endDocument, State2),
+ % {ok, State3#xmerl_sax_parser_state.event_state, Rest};
+ % {endDocument, Rest, State2} when is_record(State2, xmerl_sax_parser_state) ->
+ % State3 = event_callback(endDocument, State2),
+ % {ok, State3#xmerl_sax_parser_state.event_state, Rest};
+ % Other ->
+ % _State2 = event_callback(endDocument, State1),
+ % {fatal_error, Other}
+ % end.
+
+
%%======================================================================
%% Internal functions
%%======================================================================
%%----------------------------------------------------------------------
+%% Function: handle_end_document(ParserResult) -> Result
+%% Input: ParseResult = term()
+%% Output: Result = {ok, Rest, EventState} |
+%% EventState = term()
+%% Description: Ends the parsing and formats output
+%%----------------------------------------------------------------------
+handle_end_document({ok, Rest, State}) ->
+ %%ok case from parse
+ try
+ State1 = event_callback(endDocument, State),
+ case check_if_rest_ok(State1#xmerl_sax_parser_state.input_type, Rest) of
+ true ->
+ {ok, State1#xmerl_sax_parser_state.event_state, Rest};
+ false ->
+ format_error(fatal_error, State1, "Input found after legal document")
+ end
+ catch
+ throw:{event_receiver_error, State2, {Tag, Reason}} ->
+ format_error(Tag, State2, Reason);
+ _:Other ->
+ {fatal_error, Other}
+ end;
+handle_end_document({endDocument, Rest, State}) ->
+ %% ok case from parse and parse_dtd
+ try
+ State1 = event_callback(endDocument, State),
+ {ok, State1#xmerl_sax_parser_state.event_state, Rest}
+ catch
+ throw:{event_receiver_error, State2, {Tag, Reason}} ->
+ format_error(Tag, State2, Reason);
+ _:Other ->
+ {fatal_error, Other}
+ end;
+handle_end_document({fatal_error, {State, Reason}}) ->
+ try
+ State1 = event_callback(endDocument, State),
+ format_error(fatal_error, State1, Reason)
+ catch
+ throw:{event_receiver_error, State2, {Tag, Reason}} ->
+ format_error(Tag, State2, Reason);
+ _:Other ->
+ {fatal_error, Other}
+ end;
+handle_end_document({event_receiver_error, State, {Tag, Reason}}) ->
+ try
+ State1 = event_callback(endDocument, State),
+ format_error(Tag, State1, Reason)
+ catch
+ throw:{event_receiver_error, State2, {Tag, Reason}} ->
+ format_error(Tag, State2, Reason);
+ _:Other ->
+ {fatal_error, Other}
+ end;
+handle_end_document({Rest, State}) when is_record(State, xmerl_sax_parser_state) ->
+ %%ok case from parse_dtd
+ try
+ State1 = event_callback(endDocument, State),
+ {ok, State1#xmerl_sax_parser_state.event_state, Rest}
+ catch
+ throw:{event_receiver_error, State2, {Tag, Reason}} ->
+ format_error(Tag, State2, Reason);
+ _:Other ->
+ {fatal_error, Other}
+ end;
+handle_end_document({other, Error, State}) ->
+ try
+ _State1 = event_callback(endDocument, State),
+ {fatal_error, Error}
+ catch
+ throw:{event_receiver_error, State2, {Tag, Reason}} ->
+ format_error(Tag, State2, Reason);
+ _:Other ->
+ {fatal_error, Other}
+ end.
+
+%%----------------------------------------------------------------------
%% Function: parse_document(Rest, State) -> Result
%% Input: Rest = string() | binary()
%% State = #xmerl_sax_parser_state{}
@@ -136,10 +231,11 @@ parse_dtd(Xml, State) ->
%% [1] document ::= prolog element Misc*
%%----------------------------------------------------------------------
parse_document(Rest, State) when is_record(State, xmerl_sax_parser_state) ->
- {Rest1, State1} = parse_xml_decl(Rest, State),
+ {Rest1, State1} = parse_byte_order_mark(Rest, State),
{Rest2, State2} = parse_misc(Rest1, State1, true),
{ok, Rest2, State2}.
+?PARSE_BYTE_ORDER_MARK(Bytes, State).
%%----------------------------------------------------------------------
%% Function: parse_xml_decl(Rest, State) -> Result
@@ -150,15 +246,8 @@ parse_document(Rest, State) when is_record(State, xmerl_sax_parser_state) ->
%% [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)?
%% [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
%%----------------------------------------------------------------------
--dialyzer({[no_fail_call, no_match], parse_xml_decl/2}).
parse_xml_decl(?STRING_EMPTY, State) ->
cf(?STRING_EMPTY, State, fun parse_xml_decl/2);
-parse_xml_decl(?BYTE_ORDER_MARK_1, State) ->
- cf(?BYTE_ORDER_MARK_1, State, fun parse_xml_decl/2);
-parse_xml_decl(?BYTE_ORDER_MARK_2, State) ->
- cf(?BYTE_ORDER_MARK_2, State, fun parse_xml_decl/2);
-parse_xml_decl(?BYTE_ORDER_MARK_REST(Rest), State) ->
- cf(Rest, State, fun parse_xml_decl/2);
parse_xml_decl(?STRING("<") = Bytes, State) ->
cf(Bytes, State, fun parse_xml_decl/2);
parse_xml_decl(?STRING("<?") = Bytes, State) ->
@@ -170,31 +259,19 @@ parse_xml_decl(?STRING("<?xm") = Bytes, State) ->
parse_xml_decl(?STRING("<?xml") = Bytes, State) ->
cf(Bytes, State, fun parse_xml_decl/2);
parse_xml_decl(?STRING_REST("<?xml", Rest1), State) ->
- parse_xml_decl_1(Rest1, State);
-parse_xml_decl(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State) when is_binary(Bytes) ->
- case unicode:characters_to_list(Bytes, Enc) of
- {incomplete, _, _} ->
- cf(Bytes, State, fun parse_xml_decl/2);
- {error, _Encoded, _Rest} ->
- ?fatal_error(State, lists:flatten(io_lib:format("Bad character, not in ~p\n", [Enc])));
- _ ->
- parse_prolog(Bytes, State)
- end;
-parse_xml_decl(Bytes, State) ->
- parse_prolog(Bytes, State).
-
+ parse_xml_decl_rest(Rest1, State);
+?PARSE_XML_DECL(Bytes, State).
-parse_xml_decl_1(?STRING_UNBOUND_REST(C, Rest) = Bytes, State) ->
+parse_xml_decl_rest(?STRING_UNBOUND_REST(C, Rest) = Bytes, State) ->
if
?is_whitespace(C) ->
{_XmlAttributes, Rest1, State1} = parse_version_info(Rest, State, []),
- %State2 = event_callback({processingInstruction, "xml", XmlAttributes}, State1),% The XML decl. should not be reported as a PI
parse_prolog(Rest1, State1);
true ->
parse_prolog(?STRING_REST("<?xml", Bytes), State)
end;
-parse_xml_decl_1(Bytes, State) ->
- unicode_incomplete_check([Bytes, State, fun parse_xml_decl_1/2], undefined).
+parse_xml_decl_rest(Bytes, State) ->
+ unicode_incomplete_check([Bytes, State, fun parse_xml_decl_rest/2], undefined).
@@ -216,8 +293,6 @@ parse_prolog(?STRING_REST("<?", Rest), State) ->
parse_prolog(Rest1, State1);
{endDocument, Rest1, State1} ->
parse_prolog(Rest1, State1)
- % IValue = ?TO_INPUT_FORMAT("<?"),
- % {?APPEND_STRING(IValue, Rest1), State1}
end;
parse_prolog(?STRING_REST("<!", Rest), State) ->
parse_prolog_1(Rest, State);
@@ -230,7 +305,6 @@ parse_prolog(Bytes, State) ->
unicode_incomplete_check([Bytes, State, fun parse_prolog/2],
"expecting < or whitespace").
-
parse_prolog_1(?STRING_EMPTY, State) ->
cf(?STRING_EMPTY, State, fun parse_prolog_1/2);
parse_prolog_1(?STRING("D") = Bytes, State) ->
@@ -442,6 +516,16 @@ check_if_new_doc_allowed(stream, []) ->
check_if_new_doc_allowed(_, _) ->
false.
+check_if_rest_ok(file, []) ->
+ true;
+check_if_rest_ok(file, <<>>) ->
+ true;
+check_if_rest_ok(stream, _) ->
+ true;
+check_if_rest_ok(_, _) ->
+ false.
+
+
%%----------------------------------------------------------------------
%% Function: parse_pi_1(Rest, State) -> Result
%% Input: Rest = string() | binary()
@@ -886,11 +970,11 @@ send_end_prefix_mapping_event([{Prefix, _Uri} |Ns], State) ->
parse_eq(?STRING_EMPTY, State) ->
cf(?STRING_EMPTY, State, fun parse_eq/2);
parse_eq(?STRING_REST("=", Rest), State) ->
- {Rest, State};
+ {Rest, State};
parse_eq(?STRING_UNBOUND_REST(C, _) = Bytes, State) when ?is_whitespace(C) ->
- {_WS, Rest, State1} =
- whitespace(Bytes, State, []),
- parse_eq(Rest, State1);
+ {_WS, Rest, State1} =
+ whitespace(Bytes, State, []),
+ parse_eq(Rest, State1);
parse_eq(Bytes, State) ->
unicode_incomplete_check([Bytes, State, fun parse_eq/2],
"expecting = or whitespace").
@@ -908,11 +992,11 @@ parse_eq(Bytes, State) ->
parse_att_value(?STRING_EMPTY, State) ->
cf(?STRING_EMPTY, State, fun parse_att_value/2);
parse_att_value(?STRING_UNBOUND_REST(C, Rest), State) when C == $'; C == $" ->
- parse_att_value(Rest, State, C, []);
+ parse_att_value(Rest, State, C, []);
parse_att_value(?STRING_UNBOUND_REST(C, _) = Bytes, State) when ?is_whitespace(C) ->
- {_WS, Rest, State1} =
- whitespace(Bytes, State, []),
- parse_att_value(Rest, State1);
+ {_WS, Rest, State1} =
+ whitespace(Bytes, State, []),
+ parse_att_value(Rest, State1);
parse_att_value(Bytes, State) ->
unicode_incomplete_check([Bytes, State, fun parse_att_value/2],
"\', \" or whitespace expected").
@@ -1024,16 +1108,21 @@ parse_etag(Bytes, State) ->
unicode_incomplete_check([Bytes, State, fun parse_etag/2],
undefined).
-
parse_etag_1(?STRING_REST(">", Rest),
#xmerl_sax_parser_state{end_tags=[{_ETag, Uri, LocalName, QName, OldNsList, NewNsList}
- |RestOfETags]} = State, _Tag) ->
+ |RestOfETags],
+ input_type=InputType} = State, _Tag) ->
State1 = event_callback({endElement, Uri, LocalName, QName}, State),
State2 = send_end_prefix_mapping_event(NewNsList, State1),
- parse_content(Rest,
- State2#xmerl_sax_parser_state{end_tags=RestOfETags,
- ns = OldNsList},
- [], true);
+ case check_if_new_doc_allowed(InputType, RestOfETags) of
+ true ->
+ throw({endDocument, Rest, State2#xmerl_sax_parser_state{ns = OldNsList}});
+ false ->
+ parse_content(Rest,
+ State2#xmerl_sax_parser_state{end_tags=RestOfETags,
+ ns = OldNsList},
+ [], true)
+ end;
parse_etag_1(?STRING_UNBOUND_REST(_C, _), State, Tag) ->
{P,TN} = Tag,
?fatal_error(State, "Bad EndTag: " ++ P ++ ":" ++ TN);
@@ -1051,21 +1140,26 @@ parse_etag_1(Bytes, State, Tag) ->
%% Description: Parsing the content part of tags
%% [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)*
%%----------------------------------------------------------------------
-
parse_content(?STRING_EMPTY, State, Acc, IgnorableWS) ->
- case catch cf(?STRING_EMPTY, State, Acc, IgnorableWS, fun parse_content/4) of
- {Rest, State1} when is_record(State1, xmerl_sax_parser_state) ->
- {Rest, State1};
- {fatal_error, {State1, Msg}} ->
- case check_if_document_complete(State1, Msg) of
- true ->
- State2 = send_character_event(length(Acc), IgnorableWS, lists:reverse(Acc), State1),
- {?STRING_EMPTY, State2};
- false ->
- ?fatal_error(State1, Msg)
- end;
- Other ->
- throw(Other)
+ case check_if_document_complete(State, "No more bytes") of
+ true ->
+ State1 = send_character_event(length(Acc), IgnorableWS, lists:reverse(Acc), State),
+ {?STRING_EMPTY, State1};
+ false ->
+ case catch cf(?STRING_EMPTY, State, Acc, IgnorableWS, fun parse_content/4) of
+ {Rest, State1} when is_record(State1, xmerl_sax_parser_state) ->
+ {Rest, State1};
+ {fatal_error, {State1, Msg}} ->
+ case check_if_document_complete(State1, Msg) of
+ true ->
+ State2 = send_character_event(length(Acc), IgnorableWS, lists:reverse(Acc), State1),
+ {?STRING_EMPTY, State2};
+ false ->
+ ?fatal_error(State1, Msg)
+ end;
+ Other ->
+ throw(Other)
+ end
end;
parse_content(?STRING("\r") = Bytes, State, Acc, IgnorableWS) ->
cf(Bytes, State, Acc, IgnorableWS, fun parse_content/4);
@@ -1094,7 +1188,7 @@ parse_content(?STRING_REST("<?", Rest), State, Acc, IgnorableWS) ->
parse_content(?STRING_REST("<!", Rest1) = Rest, #xmerl_sax_parser_state{end_tags = ET} = State, Acc, IgnorableWS) ->
case ET of
[] ->
- {Rest, State}; %%LATH : Skicka ignorable WS ???
+ {Rest, State}; %% Skicka ignorable WS ???
_ ->
State1 = send_character_event(length(Acc), IgnorableWS, lists:reverse(Acc), State),
parse_cdata(Rest1, State1)
@@ -1102,7 +1196,7 @@ parse_content(?STRING_REST("<!", Rest1) = Rest, #xmerl_sax_parser_state{end_tags
parse_content(?STRING_REST("<", Rest1) = Rest, #xmerl_sax_parser_state{end_tags = ET} = State, Acc, IgnorableWS) ->
case ET of
[] ->
- {Rest, State}; %%LATH : Skicka ignorable WS ???
+ {Rest, State}; %% Skicka ignorable WS ???
_ ->
State1 = send_character_event(length(Acc), IgnorableWS, lists:reverse(Acc), State),
parse_stag(Rest1, State1)
@@ -1204,7 +1298,6 @@ send_character_event(_, true, String, State) ->
%% Description: Parse whitespaces.
%% [3] S ::= (#x20 | #x9 | #xD | #xA)+
%%----------------------------------------------------------------------
--dialyzer({no_fail_call, whitespace/3}).
whitespace(?STRING_EMPTY, State, Acc) ->
case cf(?STRING_EMPTY, State, Acc, fun whitespace/3) of
{?STRING_EMPTY, State} ->
@@ -1230,16 +1323,7 @@ whitespace(?STRING_REST("\r", Rest), State, Acc) ->
whitespace(Rest, State#xmerl_sax_parser_state{line_no=N+1}, [?lf |Acc]);
whitespace(?STRING_UNBOUND_REST(C, Rest), State, Acc) when ?is_whitespace(C) ->
whitespace(Rest, State, [C|Acc]);
-whitespace(?STRING_UNBOUND_REST(_C, _) = Bytes, State, Acc) ->
- {lists:reverse(Acc), Bytes, State};
-whitespace(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State, Acc) when is_binary(Bytes) ->
- case unicode:characters_to_list(Bytes, Enc) of
- {incomplete, _, _} ->
- cf(Bytes, State, Acc, fun whitespace/3);
- {error, _Encoded, _Rest} ->
- ?fatal_error(State, lists:flatten(io_lib:format("Bad character, not in ~p\n", [Enc])))
- end.
-
+?WHITESPACE(Bytes, State, Acc).
%%----------------------------------------------------------------------
%% Function: parse_reference(Rest, State, HaveToExist) -> Result
@@ -1362,23 +1446,24 @@ parse_pe_reference_1(Bytes, State, Name) ->
"missing ; after reference " ++ Name).
-
%%----------------------------------------------------------------------
-%% Function: insert_reference(Reference, State) -> Result
-%% Parameters: Reference = string()
+%% Function: insert_reference(Name, Ref, State) -> Result
+%% Parameters: Name = string()
+%% Ref = {Type, Value}
+%% Type = atom()
+%% Value = term()
%% State = #xmerl_sax_parser_state{}
%% Result :
%%----------------------------------------------------------------------
-insert_reference({Name, Type, Value}, Table) ->
- case ets:lookup(Table, Name) of
- [{Name, _, _}] ->
- ok;
+insert_reference(Name, Value, #xmerl_sax_parser_state{ref_table = Map} = State) ->
+ case maps:find(Name, Map) of
+ error ->
+ State#xmerl_sax_parser_state{ref_table = maps:put(Name, Value, Map)};
_ ->
- ets:insert(Table, {Name, Type, Value})
+ State
end.
-
%%----------------------------------------------------------------------
%% Function: look_up_reference(Reference, State) -> Result
%% Parameters: Reference = string()
@@ -1396,8 +1481,8 @@ look_up_reference("apos", _, _) ->
look_up_reference("quot", _, _) ->
{internal_general, "quot", "\""};
look_up_reference(Name, HaveToExist, State) ->
- case ets:lookup(State#xmerl_sax_parser_state.ref_table, Name) of
- [{Name, Type, Value}] ->
+ case maps:find(Name, State#xmerl_sax_parser_state.ref_table) of
+ {ok, {Type, Value}} ->
{Type, Name, Value};
_ ->
case HaveToExist of
@@ -1479,7 +1564,7 @@ parse_system_litteral(?STRING_EMPTY, State, Stop, Acc) ->
parse_system_litteral(?STRING_UNBOUND_REST(Stop, Rest), State, Stop, Acc) ->
{lists:reverse(Acc), Rest, State};
parse_system_litteral(?STRING_UNBOUND_REST(C, Rest), State, Stop, Acc) ->
- parse_system_litteral(Rest, State, Stop, [C |Acc]);
+ parse_system_litteral(Rest, State, Stop, [C |Acc]);
parse_system_litteral(Bytes, State, Stop, Acc) ->
unicode_incomplete_check([Bytes, State, Stop, Acc, fun parse_system_litteral/4],
undefined).
@@ -1651,9 +1736,11 @@ parse_external_entity(State, _PubId, SysId) ->
end_tags = []},
- EventState = handle_external_entity(ExtRef, State1),
+ {EventState, RefTable} = handle_external_entity(ExtRef, State1),
- NewState = event_callback({endEntity, SysId}, SaveState#xmerl_sax_parser_state{event_state=EventState}),
+ NewState = event_callback({endEntity, SysId},
+ SaveState#xmerl_sax_parser_state{event_state=EventState,
+ ref_table=RefTable}),
NewState#xmerl_sax_parser_state{file_type=normal}.
@@ -1680,7 +1767,8 @@ handle_external_entity({file, FileToOpen}, State) ->
entity=filename:basename(FileToOpen),
input_type=file}),
ok = file:close(FD),
- EntityState#xmerl_sax_parser_state.event_state
+ {EntityState#xmerl_sax_parser_state.event_state,
+ EntityState#xmerl_sax_parser_state.ref_table}
end;
handle_external_entity({http, Url}, State) ->
@@ -1693,14 +1781,16 @@ handle_external_entity({http, Url}, State) ->
++ file:format_error(Reason));
{ok, FD} ->
{?STRING_EMPTY, EntityState} =
- parse_external_entity_1(<<>>,
+ parse_external_entity_byte_order_mark(<<>>,
State#xmerl_sax_parser_state{continuation_state=FD,
current_location=filename:dirname(Url),
entity=filename:basename(Url),
input_type=file}),
ok = file:close(FD),
ok = file:delete(TmpFile),
- EntityState#xmerl_sax_parser_state.event_state
+ {EntityState#xmerl_sax_parser_state.event_state,
+ EntityState#xmerl_sax_parser_state.ref_table}
+
end
catch
throw:{error, Error} ->
@@ -1709,6 +1799,8 @@ handle_external_entity({http, Url}, State) ->
handle_external_entity({Tag, _Url}, State) ->
?fatal_error(State, "Unsupported URI type: " ++ atom_to_list(Tag)).
+?PARSE_EXTERNAL_ENTITY_BYTE_ORDER_MARK(Bytes, State).
+
%%----------------------------------------------------------------------
%% Function : parse_external_entity_1(Rest, State) -> Result
%% Parameters: Rest = string() | binary()
@@ -1716,22 +1808,15 @@ handle_external_entity({Tag, _Url}, State) ->
%% Result : {Rest, State}
%% Description: Parse the external entity.
%%----------------------------------------------------------------------
--dialyzer({[no_fail_call, no_match], parse_external_entity_1/2}).
parse_external_entity_1(?STRING_EMPTY, #xmerl_sax_parser_state{file_type=Type} = State) ->
case catch cf(?STRING_EMPTY, State, fun parse_external_entity_1/2) of
{Rest, State1} when is_record(State1, xmerl_sax_parser_state) ->
- {Rest, State};
+ {Rest, State1};
{fatal_error, {State1, "No more bytes"}} when Type == dtd; Type == entity ->
{?STRING_EMPTY, State1};
Other ->
throw(Other)
end;
-parse_external_entity_1(?BYTE_ORDER_MARK_1, State) ->
- cf(?BYTE_ORDER_MARK_1, State, fun parse_external_entity_1/2);
-parse_external_entity_1(?BYTE_ORDER_MARK_2, State) ->
- cf(?BYTE_ORDER_MARK_2, State, fun parse_external_entity_1/2);
-parse_external_entity_1(?BYTE_ORDER_MARK_REST(Rest), State) ->
- parse_external_entity_1(Rest, State);
parse_external_entity_1(?STRING("<") = Bytes, State) ->
cf(Bytes, State, fun parse_external_entity_1/2);
parse_external_entity_1(?STRING("<?") = Bytes, State) ->
@@ -2452,24 +2537,24 @@ parse_entity_def(?STRING_EMPTY, State, Name) ->
cf(?STRING_EMPTY, State, Name, fun parse_entity_def/3);
parse_entity_def(?STRING_UNBOUND_REST(C, Rest), State, Name) when C == $'; C == $" ->
{Value, Rest1, State1} = parse_entity_value(Rest, State, C, []),
- insert_reference({Name, internal_general, Value}, State1#xmerl_sax_parser_state.ref_table),
- State2 = event_callback({internalEntityDecl, Name, Value}, State1),
- {_WS, Rest2, State3} = whitespace(Rest1, State2, []),
- parse_def_end(Rest2, State3);
+ State2 = insert_reference(Name, {internal_general, Value}, State1),
+ State3 = event_callback({internalEntityDecl, Name, Value}, State2),
+ {_WS, Rest2, State4} = whitespace(Rest1, State3, []),
+ parse_def_end(Rest2, State4);
parse_entity_def(?STRING_UNBOUND_REST(C, _) = Rest, State, Name) when C == $S; C == $P ->
{PubId, SysId, Rest1, State1} = parse_external_id(Rest, State, false),
{Ndata, Rest2, State2} = parse_ndata(Rest1, State1),
case Ndata of
undefined ->
- insert_reference({Name, external_general, {PubId, SysId}},
- State2#xmerl_sax_parser_state.ref_table),
- State3 = event_callback({externalEntityDecl, Name, PubId, SysId}, State2),
- {Rest2, State3};
+ State3 = insert_reference(Name, {external_general, {PubId, SysId}},
+ State2),
+ State4 = event_callback({externalEntityDecl, Name, PubId, SysId}, State3),
+ {Rest2, State4};
_ ->
- insert_reference({Name, unparsed, {PubId, SysId, Ndata}},
- State2#xmerl_sax_parser_state.ref_table),
- State3 = event_callback({unparsedEntityDecl, Name, PubId, SysId, Ndata}, State2),
- {Rest2, State3}
+ State3 = insert_reference(Name, {unparsed, {PubId, SysId, Ndata}},
+ State2),
+ State4 = event_callback({unparsedEntityDecl, Name, PubId, SysId, Ndata}, State3),
+ {Rest2, State4}
end;
parse_entity_def(Bytes, State, Name) ->
unicode_incomplete_check([Bytes, State, Name, fun parse_entity_def/3],
@@ -2646,19 +2731,19 @@ parse_pe_def(?STRING_EMPTY, State, Name) ->
parse_pe_def(?STRING_UNBOUND_REST(C, Rest), State, Name) when C == $'; C == $" ->
{Value, Rest1, State1} = parse_entity_value(Rest, State, C, []),
Name1 = "%" ++ Name,
- insert_reference({Name1, internal_parameter, Value},
- State1#xmerl_sax_parser_state.ref_table),
- State2 = event_callback({internalEntityDecl, Name1, Value}, State1),
- {_WS, Rest2, State3} = whitespace(Rest1, State2, []),
- parse_def_end(Rest2, State3);
+ State2 = insert_reference(Name1, {internal_parameter, Value},
+ State1),
+ State3 = event_callback({internalEntityDecl, Name1, Value}, State2),
+ {_WS, Rest2, State4} = whitespace(Rest1, State3, []),
+ parse_def_end(Rest2, State4);
parse_pe_def(?STRING_UNBOUND_REST(C, _) = Bytes, State, Name) when C == $S; C == $P ->
{PubId, SysId, Rest1, State1} = parse_external_id(Bytes, State, false),
Name1 = "%" ++ Name,
- insert_reference({Name1, external_parameter, {PubId, SysId}},
- State1#xmerl_sax_parser_state.ref_table),
- State2 = event_callback({externalEntityDecl, Name1, PubId, SysId}, State1),
- {_WS, Rest2, State3} = whitespace(Rest1, State2, []),
- parse_def_end(Rest2, State3);
+ State2 = insert_reference(Name1, {external_parameter, {PubId, SysId}},
+ State1),
+ State3 = event_callback({externalEntityDecl, Name1, PubId, SysId}, State2),
+ {_WS, Rest2, State4} = whitespace(Rest1, State3, []),
+ parse_def_end(Rest2, State4);
parse_pe_def(Bytes, State, Name) ->
unicode_incomplete_check([Bytes, State, Name, fun parse_pe_def/3],
"\", \', SYSTEM or PUBLIC expected").
@@ -3290,7 +3375,7 @@ cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = C
catch
throw:ErrorTerm ->
?fatal_error(State, ErrorTerm);
- exit:Reason ->
+ exit:Reason ->
?fatal_error(State, {'EXIT', Reason})
end,
case Result of
diff --git a/lib/xmerl/src/xmerl_sax_parser_latin1.erlsrc b/lib/xmerl/src/xmerl_sax_parser_latin1.erlsrc
index 961806bf4c..6e59347fb8 100644
--- a/lib/xmerl/src/xmerl_sax_parser_latin1.erlsrc
+++ b/lib/xmerl/src/xmerl_sax_parser_latin1.erlsrc
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -34,8 +34,36 @@
-define(APPEND_STRING(Rest, New), <<Rest/binary, New/binary>>).
-define(TO_INPUT_FORMAT(Val), unicode:characters_to_binary(Val, unicode, latin1)).
-%% STRING_REST and STRING_UNBOUND_REST is only different in the list case
-define(STRING_UNBOUND_REST(MatchChar, Rest), <<MatchChar, Rest/binary>>).
--define(BYTE_ORDER_MARK_1, undefined_bom1).
--define(BYTE_ORDER_MARK_2, undefined_bom2).
--define(BYTE_ORDER_MARK_REST(Rest), <<undefined, Rest/binary>>).
+
+-define(PARSE_BYTE_ORDER_MARK(Bytes, State),
+ parse_byte_order_mark(Bytes, State) ->
+ parse_xml_decl(Bytes, State)).
+
+-define(PARSE_XML_DECL(Bytes, State),
+ parse_xml_decl(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State) when is_binary(Bytes) ->
+ case unicode:characters_to_list(Bytes, Enc) of
+ {incomplete, _, _} ->
+ cf(Bytes, State, fun parse_xml_decl/2);
+ {error, _Encoded, _Rest} ->
+ ?fatal_error(State, lists:flatten(io_lib:format("Bad character, not in ~p\n", [Enc])));
+ _ ->
+ parse_prolog(Bytes, State)
+ end;
+ parse_xml_decl(Bytes, State) ->
+ parse_prolog(Bytes, State)).
+
+-define(WHITESPACE(Bytes, State, Acc),
+ whitespace(?STRING_UNBOUND_REST(_C, _) = Bytes, State, Acc) ->
+ {lists:reverse(Acc), Bytes, State};
+ whitespace(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State, Acc) when is_binary(Bytes) ->
+ case unicode:characters_to_list(Bytes, Enc) of
+ {incomplete, _, _} ->
+ cf(Bytes, State, Acc, fun whitespace/3);
+ {error, _Encoded, _Rest} ->
+ ?fatal_error(State, lists:flatten(io_lib:format("Bad character, not in ~p\n", [Enc])))
+ end).
+
+-define(PARSE_EXTERNAL_ENTITY_BYTE_ORDER_MARK(Bytes, State),
+ parse_external_entity_byte_order_mark(Bytes, State) ->
+ parse_external_entity_1(Bytes, State)).
diff --git a/lib/xmerl/src/xmerl_sax_parser_list.erlsrc b/lib/xmerl/src/xmerl_sax_parser_list.erlsrc
index 624a621d92..ac89896215 100644
--- a/lib/xmerl/src/xmerl_sax_parser_list.erlsrc
+++ b/lib/xmerl/src/xmerl_sax_parser_list.erlsrc
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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,19 @@
%% In the list case we can't use a '++' when matchin against an unbound variable
-define(STRING_UNBOUND_REST(MatchChar, Rest), [MatchChar | Rest]).
--define(BYTE_ORDER_MARK_1, undefined_bom1).
--define(BYTE_ORDER_MARK_2, undefined_bom2).
--define(BYTE_ORDER_MARK_REST(Rest), [undefined|Rest]).
+
+-define(PARSE_BYTE_ORDER_MARK(Bytes, State),
+ parse_byte_order_mark(Bytes, State) ->
+ parse_xml_decl(Bytes, State)).
+
+-define(PARSE_XML_DECL(Bytes, State),
+ parse_xml_decl(Bytes, State) ->
+ parse_prolog(Bytes, State)).
+
+-define(WHITESPACE(Bytes, State, Acc),
+ whitespace(?STRING_UNBOUND_REST(_C, _) = Bytes, State, Acc) ->
+ {lists:reverse(Acc), Bytes, State}).
+
+-define(PARSE_EXTERNAL_ENTITY_BYTE_ORDER_MARK(Bytes, State),
+ parse_external_entity_byte_order_mark(Bytes, State) ->
+ parse_external_entity_1(Bytes, State)).
diff --git a/lib/xmerl/src/xmerl_sax_parser_utf16be.erlsrc b/lib/xmerl/src/xmerl_sax_parser_utf16be.erlsrc
index ff84ece97a..ec89024729 100644
--- a/lib/xmerl/src/xmerl_sax_parser_utf16be.erlsrc
+++ b/lib/xmerl/src/xmerl_sax_parser_utf16be.erlsrc
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -34,8 +34,50 @@
-define(APPEND_STRING(Rest, New), <<Rest/binary, New/binary>>).
-define(TO_INPUT_FORMAT(Val), unicode:characters_to_binary(Val, unicode, {utf16, big})).
-%% STRING_REST and STRING_UNBOUND_REST is only different in the list case
-define(STRING_UNBOUND_REST(MatchChar, Rest), <<MatchChar/big-utf16, Rest/binary>>).
--define(BYTE_ORDER_MARK_1, undefined_bom1).
--define(BYTE_ORDER_MARK_2, <<16#FE>>).
+-define(BYTE_ORDER_MARK_1, <<16#FE>>).
-define(BYTE_ORDER_MARK_REST(Rest), <<16#FE, 16#FF, Rest/binary>>).
+
+-define(PARSE_BYTE_ORDER_MARK(Bytes, State),
+ parse_byte_order_mark(?STRING_EMPTY, State) ->
+ cf(?STRING_EMPTY, State, fun parse_byte_order_mark/2);
+ parse_byte_order_mark(?BYTE_ORDER_MARK_1, State) ->
+ cf(?BYTE_ORDER_MARK_1, State, fun parse_byte_order_mark/2);
+ parse_byte_order_mark(?BYTE_ORDER_MARK_REST(Rest), State) ->
+ parse_xml_decl(Rest, State);
+ parse_byte_order_mark(Bytes, State) ->
+ parse_xml_decl(Bytes, State)).
+
+-define(PARSE_XML_DECL(Bytes, State),
+ parse_xml_decl(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State) when is_binary(Bytes) ->
+ case unicode:characters_to_list(Bytes, Enc) of
+ {incomplete, _, _} ->
+ cf(Bytes, State, fun parse_xml_decl/2);
+ {error, _Encoded, _Rest} ->
+ ?fatal_error(State, lists:flatten(io_lib:format("Bad character, not in ~p\n", [Enc])));
+ _ ->
+ parse_prolog(Bytes, State)
+ end;
+ parse_xml_decl(Bytes, State) ->
+ parse_prolog(Bytes, State)).
+
+-define(WHITESPACE(Bytes, State, Acc),
+ whitespace(?STRING_UNBOUND_REST(_C, _) = Bytes, State, Acc) ->
+ {lists:reverse(Acc), Bytes, State};
+ whitespace(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State, Acc) when is_binary(Bytes) ->
+ case unicode:characters_to_list(Bytes, Enc) of
+ {incomplete, _, _} ->
+ cf(Bytes, State, Acc, fun whitespace/3);
+ {error, _Encoded, _Rest} ->
+ ?fatal_error(State, lists:flatten(io_lib:format("Bad character, not in ~p\n", [Enc])))
+ end).
+
+-define(PARSE_EXTERNAL_ENTITY_BYTE_ORDER_MARK(Bytes, State),
+ parse_external_entity_byte_order_mark(?STRING_EMPTY, State) ->
+ cf(?STRING_EMPTY, State, fun parse_external_entity_byte_order_mark/2);
+ parse_external_entity_byte_order_mark(?BYTE_ORDER_MARK_1, State) ->
+ cf(?BYTE_ORDER_MARK_1, State, fun parse_external_entity_byte_order_mark/2);
+ parse_external_entity_byte_order_mark(?BYTE_ORDER_MARK_REST(Rest), State) ->
+ parse_external_entity_1(Rest, State);
+ parse_external_entity_byte_order_mark(Bytes, State) ->
+ parse_external_entity_1(Bytes, State)).
diff --git a/lib/xmerl/src/xmerl_sax_parser_utf16le.erlsrc b/lib/xmerl/src/xmerl_sax_parser_utf16le.erlsrc
index a330fce8d0..566333a045 100644
--- a/lib/xmerl/src/xmerl_sax_parser_utf16le.erlsrc
+++ b/lib/xmerl/src/xmerl_sax_parser_utf16le.erlsrc
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -34,8 +34,50 @@
-define(APPEND_STRING(Rest, New), <<Rest/binary, New/binary>>).
-define(TO_INPUT_FORMAT(Val), unicode:characters_to_binary(Val, unicode, {utf16, little})).
-%% STRING_REST and STRING_UNBOUND_REST is only different in the list case
-define(STRING_UNBOUND_REST(MatchChar, Rest), <<MatchChar/little-utf16, Rest/binary>>).
--define(BYTE_ORDER_MARK_1, undefined_bom1).
--define(BYTE_ORDER_MARK_2, <<16#FF>>).
+-define(BYTE_ORDER_MARK_1, <<16#FF>>).
-define(BYTE_ORDER_MARK_REST(Rest), <<16#FF, 16#FE, Rest/binary>>).
+
+-define(PARSE_BYTE_ORDER_MARK(Bytes, State),
+ parse_byte_order_mark(?STRING_EMPTY, State) ->
+ cf(?STRING_EMPTY, State, fun parse_byte_order_mark/2);
+ parse_byte_order_mark(?BYTE_ORDER_MARK_1, State) ->
+ cf(?BYTE_ORDER_MARK_1, State, fun parse_byte_order_mark/2);
+ parse_byte_order_mark(?BYTE_ORDER_MARK_REST(Rest), State) ->
+ parse_xml_decl(Rest, State);
+ parse_byte_order_mark(Bytes, State) ->
+ parse_xml_decl(Bytes, State)).
+
+-define(PARSE_XML_DECL(Bytes, State),
+ parse_xml_decl(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State) when is_binary(Bytes) ->
+ case unicode:characters_to_list(Bytes, Enc) of
+ {incomplete, _, _} ->
+ cf(Bytes, State, fun parse_xml_decl/2);
+ {error, _Encoded, _Rest} ->
+ ?fatal_error(State, lists:flatten(io_lib:format("Bad character, not in ~p\n", [Enc])));
+ _ ->
+ parse_prolog(Bytes, State)
+ end;
+ parse_xml_decl(Bytes, State) ->
+ parse_prolog(Bytes, State)).
+
+-define(WHITESPACE(Bytes, State, Acc),
+ whitespace(?STRING_UNBOUND_REST(_C, _) = Bytes, State, Acc) ->
+ {lists:reverse(Acc), Bytes, State};
+ whitespace(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State, Acc) when is_binary(Bytes) ->
+ case unicode:characters_to_list(Bytes, Enc) of
+ {incomplete, _, _} ->
+ cf(Bytes, State, Acc, fun whitespace/3);
+ {error, _Encoded, _Rest} ->
+ ?fatal_error(State, lists:flatten(io_lib:format("Bad character, not in ~p\n", [Enc])))
+ end).
+
+-define(PARSE_EXTERNAL_ENTITY_BYTE_ORDER_MARK(Bytes, State),
+ parse_external_entity_byte_order_mark(?STRING_EMPTY, State) ->
+ cf(?STRING_EMPTY, State, fun parse_external_entity_byte_order_mark/2);
+ parse_external_entity_byte_order_mark(?BYTE_ORDER_MARK_1, State) ->
+ cf(?BYTE_ORDER_MARK_1, State, fun parse_external_entity_byte_order_mark/2);
+ parse_external_entity_byte_order_mark(?BYTE_ORDER_MARK_REST(Rest), State) ->
+ parse_external_entity_1(Rest, State);
+ parse_external_entity_byte_order_mark(Bytes, State) ->
+ parse_external_entity_1(Bytes, State)).
diff --git a/lib/xmerl/src/xmerl_sax_parser_utf8.erlsrc b/lib/xmerl/src/xmerl_sax_parser_utf8.erlsrc
index d46d60d237..f41d06d013 100644
--- a/lib/xmerl/src/xmerl_sax_parser_utf8.erlsrc
+++ b/lib/xmerl/src/xmerl_sax_parser_utf8.erlsrc
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -34,11 +34,55 @@
-define(APPEND_STRING(Rest, New), <<Rest/binary, New/binary>>).
-define(TO_INPUT_FORMAT(Val), unicode:characters_to_binary(Val, unicode, utf8)).
-
-%% STRING_REST and STRING_UNBOUND_REST is only different in the list case
-define(STRING_UNBOUND_REST(MatchChar, Rest), <<MatchChar/utf8, Rest/binary>>).
-define(BYTE_ORDER_MARK_1, <<16#EF>>).
-define(BYTE_ORDER_MARK_2, <<16#EF, 16#BB>>).
-define(BYTE_ORDER_MARK_REST(Rest), <<16#EF, 16#BB, 16#BF, Rest/binary>>).
+-define(PARSE_BYTE_ORDER_MARK(Bytes, State),
+ parse_byte_order_mark(?STRING_EMPTY, State) ->
+ cf(?STRING_EMPTY, State, fun parse_byte_order_mark/2);
+ parse_byte_order_mark(?BYTE_ORDER_MARK_1, State) ->
+ cf(?BYTE_ORDER_MARK_1, State, fun parse_byte_order_mark/2);
+ parse_byte_order_mark(?BYTE_ORDER_MARK_2, State) ->
+ cf(?BYTE_ORDER_MARK_2, State, fun parse_byte_order_mark/2);
+ parse_byte_order_mark(?BYTE_ORDER_MARK_REST(Rest), State) ->
+ parse_xml_decl(Rest, State);
+ parse_byte_order_mark(Bytes, State) ->
+ parse_xml_decl(Bytes, State)).
+
+-define(PARSE_XML_DECL(Bytes, State),
+ parse_xml_decl(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State) when is_binary(Bytes) ->
+ case unicode:characters_to_list(Bytes, Enc) of
+ {incomplete, _, _} ->
+ cf(Bytes, State, fun parse_xml_decl/2);
+ {error, _Encoded, _Rest} ->
+ ?fatal_error(State, lists:flatten(io_lib:format("Bad character, not in ~p\n", [Enc])));
+ _ ->
+ parse_prolog(Bytes, State)
+ end;
+ parse_xml_decl(Bytes, State) ->
+ parse_prolog(Bytes, State)).
+
+-define(WHITESPACE(Bytes, State, Acc),
+ whitespace(?STRING_UNBOUND_REST(_C, _) = Bytes, State, Acc) ->
+ {lists:reverse(Acc), Bytes, State};
+ whitespace(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State, Acc) when is_binary(Bytes) ->
+ case unicode:characters_to_list(Bytes, Enc) of
+ {incomplete, _, _} ->
+ cf(Bytes, State, Acc, fun whitespace/3);
+ {error, _Encoded, _Rest} ->
+ ?fatal_error(State, lists:flatten(io_lib:format("Bad character, not in ~p\n", [Enc])))
+ end).
+-define(PARSE_EXTERNAL_ENTITY_BYTE_ORDER_MARK(Bytes, State),
+ parse_external_entity_byte_order_mark(?STRING_EMPTY, State) ->
+ cf(?STRING_EMPTY, State, fun parse_external_entity_byte_order_mark/2);
+ parse_external_entity_byte_order_mark(?BYTE_ORDER_MARK_1, State) ->
+ cf(?BYTE_ORDER_MARK_1, State, fun parse_external_entity_byte_order_mark/2);
+ parse_external_entity_byte_order_mark(?BYTE_ORDER_MARK_2, State) ->
+ cf(?BYTE_ORDER_MARK_2, State, fun parse_external_entity_byte_order_mark/2);
+ parse_external_entity_byte_order_mark(?BYTE_ORDER_MARK_REST(Rest), State) ->
+ parse_external_entity_1(Rest, State);
+ parse_external_entity_byte_order_mark(Bytes, State) ->
+ parse_external_entity_1(Bytes, State)).
diff --git a/lib/xmerl/src/xmerl_sax_simple_dom.erl b/lib/xmerl/src/xmerl_sax_simple_dom.erl
index 7eb3afd499..d842bd982b 100644
--- a/lib/xmerl/src/xmerl_sax_simple_dom.erl
+++ b/lib/xmerl/src/xmerl_sax_simple_dom.erl
@@ -129,8 +129,9 @@ build_dom(endDocument,
State#xmerl_sax_simple_dom_state{dom=[Decl, {Tag, Attributes,
lists:reverse(Content)}]};
_ ->
- ?dbg("~p\n", [D]),
- ?error("we're not at end the document when endDocument event is encountered.")
+ %% endDocument is also sent by the parser when a fault occur to tell
+ %% the event receiver that no more input will be sent
+ State
end;
%% Element
diff --git a/lib/xmerl/src/xmerl_scan.erl b/lib/xmerl/src/xmerl_scan.erl
index 2147a46a13..3dd9504f87 100644
--- a/lib/xmerl/src/xmerl_scan.erl
+++ b/lib/xmerl/src/xmerl_scan.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2017. 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.
@@ -111,13 +111,16 @@
%% <dd>Set to 'true' if xmerl should add to elements missing attributes
%% with a defined default value (default 'false').</dd>
%% </dl>
+%% @type xmlElement() = #xmlElement{}.
+%% The record definition is found in xmerl.hrl.
+%% @type xmlDocument() = #xmlDocument{}.
+%% The record definition is found in xmerl.hrl.
%% @type document() = xmlElement() | xmlDocument(). <p>
%% The document returned by <tt>xmerl_scan:string/[1,2]</tt> and
%% <tt>xmerl_scan:file/[1,2]</tt>. The type of the returned record depends on
%% the value of the document option passed to the function.
%% </p>
-
-module(xmerl_scan).
-vsn('0.20').
-date('03-09-16').
@@ -2222,16 +2225,18 @@ processed_whole_element(S=#xmerl_scanner{hook_fun = _Hook,
AllAttrs =
case S#xmerl_scanner.default_attrs of
true ->
- [ #xmlAttribute{name = AttName,
- parents = [{Name, Pos} | Parents],
- language = Lang,
- nsinfo = NSI,
- namespace = Namespace,
- value = AttValue,
- normalized = true} ||
- {AttName, AttValue} <- get_default_attrs(S, Name),
- AttValue =/= no_value,
- not lists:keymember(AttName, #xmlAttribute.name, Attrs) ];
+ DefaultAttrs =
+ [ #xmlAttribute{name = AttName,
+ parents = [{Name, Pos} | Parents],
+ language = Lang,
+ nsinfo = NSI,
+ namespace = Namespace,
+ value = AttValue,
+ normalized = true} ||
+ {AttName, AttValue} <- get_default_attrs(S, Name),
+ AttValue =/= no_value,
+ not lists:keymember(AttName, #xmlAttribute.name, Attrs) ],
+ lists:append(Attrs, DefaultAttrs);
false ->
Attrs
end,
@@ -2304,7 +2309,9 @@ expanded_name(Name, [], #xmlNamespace{default = URI}, S) ->
expanded_name(Name, N = {"xmlns", Local}, #xmlNamespace{nodes = Ns}, S) ->
{_, Value} = lists:keyfind(Local, 1, Ns),
case Name of
- 'xmlns:xml' when Value =/= 'http://www.w3.org/XML/1998/namespace' ->
+ 'xmlns:xml' when Value =:= 'http://www.w3.org/XML/1998/namespace' ->
+ N;
+ 'xmlns:xml' when Value =/= 'http://www.w3.org/XML/1998/namespace' ->
?fatal({xml_prefix_cannot_be_redeclared, Value}, S);
'xmlns:xmlns' ->
?fatal({xmlns_prefix_cannot_be_declared, Value}, S);
@@ -2318,6 +2325,8 @@ expanded_name(Name, N = {"xmlns", Local}, #xmlNamespace{nodes = Ns}, S) ->
N
end
end;
+expanded_name(_Name, {"xml", Local}, _NS, _S) ->
+ {'http://www.w3.org/XML/1998/namespace', list_to_atom(Local)};
expanded_name(_Name, {Prefix, Local}, #xmlNamespace{nodes = Ns}, S) ->
case lists:keysearch(Prefix, 1, Ns) of
{value, {_, URI}} ->
@@ -2328,9 +2337,6 @@ expanded_name(_Name, {Prefix, Local}, #xmlNamespace{nodes = Ns}, S) ->
?fatal({namespace_prefix_not_declared, Prefix}, S)
end.
-
-
-
keyreplaceadd(K, Pos, [H|T], Obj) when K == element(Pos, H) ->
[Obj|T];
keyreplaceadd(K, Pos, [H|T], Obj) ->
diff --git a/lib/xmerl/src/xmerl_xpath.erl b/lib/xmerl/src/xmerl_xpath.erl
index bbebda1030..6146feba49 100644
--- a/lib/xmerl/src/xmerl_xpath.erl
+++ b/lib/xmerl/src/xmerl_xpath.erl
@@ -43,13 +43,27 @@
%% </pre>
%%
%% @type nodeEntity() =
-%% xmlElement()
-%% | xmlAttribute()
-%% | xmlText()
-%% | xmlPI()
-%% | xmlComment()
-%% | xmlNsNode()
-%% | xmlDocument()
+%% #xmlElement{}
+%% | #xmlAttribute{}
+%% | #xmlText{}
+%% | #xmlPI{}
+%% | #xmlComment{}
+%% | #xmlNsNode{}
+%% | #xmlDocument{}
+%%
+%% @type docNodes() = #xmlElement{}
+%% | #xmlAttribute{}
+%% | #xmlText{}
+%% | #xmlPI{}
+%% | #xmlComment{}
+%% | #xmlNsNode{}
+%%
+%% @type docEntity() = #xmlDocument{} | [docNodes()]
+%%
+%% @type xPathString() = string()
+%%
+%% @type parentList() = [{atom(), integer()}]
+%%
%% @type option_list(). <p>Options allows to customize the behaviour of the
%% XPath scanner.
%% </p>
@@ -115,7 +129,7 @@ string(Str, Doc, Options) ->
%% Parents = parentList()
%% Doc = nodeEntity()
%% Options = option_list()
-%% Scalar = xmlObj
+%% Scalar = #xmlObj{}
%% @doc Extracts the nodes from the parsed XML tree according to XPath.
%% xmlObj is a record with fields type and value,
%% where type is boolean | number | string
diff --git a/lib/xmerl/src/xmerl_xs.erl b/lib/xmerl/src/xmerl_xs.erl
index 3e9f6622b8..1ce76cfa41 100644
--- a/lib/xmerl/src/xmerl_xs.erl
+++ b/lib/xmerl/src/xmerl_xs.erl
@@ -45,7 +45,6 @@
% XSLT package which is written i C++.
% See also the <a href="xmerl_xs_examples.html">Tutorial</a>.
% </p>
-
-module(xmerl_xs).
-export([xslapply/2, value_of/1, select/2, built_in_rules/2 ]).
@@ -71,15 +70,13 @@
%% xslapply(fun template/1, E),
%% "&lt;/h1>"];
%% </pre>
-
xslapply(Fun, EList) when is_list(EList) ->
- lists:map( Fun, EList);
+ lists:map(Fun, EList);
xslapply(Fun, E = #xmlElement{})->
lists:map( Fun, E#xmlElement.content).
-
%% @spec value_of(E) -> List
-%% E = unknown()
+%% E = term()
%%
%% @doc Concatenates all text nodes within the tree.
%%
diff --git a/lib/xmerl/src/xmerl_xsd.erl b/lib/xmerl/src/xmerl_xsd.erl
index 4b5efae8dd..a89b3159ec 100644
--- a/lib/xmerl/src/xmerl_xsd.erl
+++ b/lib/xmerl/src/xmerl_xsd.erl
@@ -49,6 +49,7 @@
%% <dd>It is possible by this option to provide a state with process
%% information from an earlier validation.</dd>
%% </dl>
+%% @type filename() = string()
%% @end
%%%-------------------------------------------------------------------
-module(xmerl_xsd).
@@ -138,7 +139,7 @@ state2file(S=#xsd_state{schema_name=SN}) ->
%% @spec state2file(State,FileName) -> ok | {error,Reason}
%% State = global_state()
-%% FileName = filename()
+%% FileName = string()
%% @doc Saves the schema state with all information of the processed
%% schema in a file. You can provide the file name for the saved
%% state. FileName is saved with the <code>.xss</code> extension
@@ -153,7 +154,7 @@ state2file(S,FileName) when is_record(S,xsd_state) ->
%% @spec file2state(FileName) -> {ok,State} | {error,Reason}
%% State = global_state()
-%% FileName = filename()
+%% FileName = string()
%% @doc Reads the schema state with all information of the processed
%% schema from a file created with <code>state2file/[1,2]</code>. The
%% format of this file is internal. The state can then be used
@@ -202,7 +203,7 @@ xmerl_xsd_vsn_check(S=#xsd_state{vsn=MD5_VSN}) ->
process_validate(Schema,Xml) ->
process_validate(Schema,Xml,[]).
%% @spec process_validate(Schema,Element,Options) -> Result
-%% Schema = filename()
+%% Schema = string()
%% Element = XmlElement
%% Options = option_list()
%% Result = {ValidXmlElement,State} | {error,Reason}
@@ -282,7 +283,7 @@ validate3(_,_,S) ->
process_schema(Schema) ->
process_schema(Schema,[]).
%% @spec process_schema(Schema,Options) -> Result
-%% Schema = filename()
+%% Schema = string()
%% Result = {ok,State} | {error,Reason}
%% State = global_state()
%% Reason = [ErrorReason] | ErrorReason
@@ -324,7 +325,7 @@ process_schema2({SE,_},State,_Schema) ->
process_schemas(Schemas) ->
process_schemas(Schemas,[]).
%% @spec process_schemas(Schemas,Options) -> Result
-%% Schemas = [{NameSpace,filename()}|Schemas] | []
+%% Schemas = [{NameSpace,string()}|Schemas] | []
%% Result = {ok,State} | {error,Reason}
%% Reason = [ErrorReason] | ErrorReason
%% Options = option_list()
@@ -5426,7 +5427,7 @@ add_key_once(Key,N,El,L) ->
%% {filename:join([[io_lib:format("/~w(~w)",[X,Y])||{X,Y}<-Parents],Type]),Pos}.
%% @spec format_error(Errors) -> Result
-%% Errors = error_tuple() | [error_tuple()]
+%% Errors = tuple() | [tuple()]
%% Result = string() | [string()]
%% @doc Formats error descriptions to human readable strings.
format_error(L) when is_list(L) ->
diff --git a/lib/xmerl/test/Makefile b/lib/xmerl/test/Makefile
index 7a326e334f..3204f081ba 100644
--- a/lib/xmerl/test/Makefile
+++ b/lib/xmerl/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2016. All Rights Reserved.
+# Copyright Ericsson AB 2004-2017. 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.
@@ -55,7 +55,8 @@ SUITE_FILES= \
xmerl_xsd_SUITE.erl \
xmerl_xsd_MS2002-01-16_SUITE.erl \
xmerl_xsd_NIST2002-01-16_SUITE.erl \
- xmerl_xsd_Sun2002-01-16_SUITE.erl
+ xmerl_xsd_Sun2002-01-16_SUITE.erl \
+ xmerl_sax_stream_SUITE.erl
XML_FILES= \
testcases.dtd \
@@ -125,4 +126,6 @@ release_tests_spec: opt
@tar cfh - xmerl_xsd_MS2002-01-16_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
@tar cfh - xmerl_xsd_NIST2002-01-16_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
@tar cfh - xmerl_xsd_Sun2002-01-16_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+ @tar cfh - xmerl_sax_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+ @tar cfh - xmerl_sax_stream_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
chmod -R u+w "$(RELSYSDIR)"
diff --git a/lib/xmerl/test/xmerl_SUITE.erl b/lib/xmerl/test/xmerl_SUITE.erl
index e97b8c6a4b..e7e3b3c2b1 100644
--- a/lib/xmerl/test/xmerl_SUITE.erl
+++ b/lib/xmerl/test/xmerl_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. 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.
@@ -54,7 +54,8 @@ groups() ->
cpd_expl_provided_DTD]},
{misc, [],
[latin1_alias, syntax_bug1, syntax_bug2, syntax_bug3,
- pe_ref1, copyright, testXSEIF, export_simple1, export]},
+ pe_ref1, copyright, testXSEIF, export_simple1, export,
+ default_attrs_bug, xml_ns]},
{eventp_tests, [], [sax_parse_and_export]},
{ticket_tests, [],
[ticket_5998, ticket_7211, ticket_7214, ticket_7430,
@@ -223,6 +224,50 @@ syntax_bug3(Config) ->
Err -> Err
end.
+default_attrs_bug(Config) ->
+ file:set_cwd(datadir(Config)),
+ Doc = "<!DOCTYPE doc [<!ATTLIST doc b CDATA \"default\">]>\n"
+ "<doc a=\"explicit\"/>",
+ {#xmlElement{attributes = [#xmlAttribute{name = a, value = "explicit"},
+ #xmlAttribute{name = b, value = "default"}]},
+ []
+ } = xmerl_scan:string(Doc, [{default_attrs, true}]),
+ Doc2 = "<!DOCTYPE doc [<!ATTLIST doc b CDATA \"default\">]>\n"
+ "<doc b=\"also explicit\" a=\"explicit\"/>",
+ {#xmlElement{attributes = [#xmlAttribute{name = b, value = "also explicit"},
+ #xmlAttribute{name = a, value = "explicit"}]},
+ []
+ } = xmerl_scan:string(Doc2, [{default_attrs, true}]),
+ ok.
+
+
+xml_ns(Config) ->
+ Doc = "<?xml version='1.0'?>\n"
+ "<doc xml:attr1=\"implicit xml ns\"/>",
+ {#xmlElement{namespace=#xmlNamespace{default = [], nodes = []},
+ attributes = [#xmlAttribute{name = 'xml:attr1',
+ expanded_name = {'http://www.w3.org/XML/1998/namespace',attr1},
+ nsinfo = {"xml","attr1"},
+ namespace = #xmlNamespace{default = [], nodes = []}}]},
+ []
+ } = xmerl_scan:string(Doc, [{namespace_conformant, true}]),
+ Doc2 = "<?xml version='1.0'?>\n"
+ "<doc xmlns:xml=\"http://www.w3.org/XML/1998/namespace\" xml:attr1=\"explicit xml ns\"/>",
+ {#xmlElement{namespace=#xmlNamespace{default = [], nodes = [{"xml",'http://www.w3.org/XML/1998/namespace'}]},
+ attributes = [#xmlAttribute{name = 'xmlns:xml',
+ expanded_name = {"xmlns","xml"},
+ nsinfo = {"xmlns","xml"},
+ namespace = #xmlNamespace{default = [],
+ nodes = [{"xml",'http://www.w3.org/XML/1998/namespace'}]}},
+ #xmlAttribute{name = 'xml:attr1',
+ expanded_name = {'http://www.w3.org/XML/1998/namespace',attr1},
+ nsinfo = {"xml","attr1"},
+ namespace = #xmlNamespace{default = [],
+ nodes = [{"xml",'http://www.w3.org/XML/1998/namespace'}]}}]},
+ []
+ } = xmerl_scan:string(Doc2, [{namespace_conformant, true}]),
+ ok.
+
pe_ref1(Config) ->
file:set_cwd(datadir(Config)),
{#xmlElement{},[]} = xmerl_scan:file(datadir_join(Config,[misc,"PE_ref1.xml"]),[{validation,true}]).
diff --git a/lib/xmerl/test/xmerl_sax_SUITE.erl b/lib/xmerl/test/xmerl_sax_SUITE.erl
index f5c0a783c4..68b9bcc4a2 100644
--- a/lib/xmerl/test/xmerl_sax_SUITE.erl
+++ b/lib/xmerl/test/xmerl_sax_SUITE.erl
@@ -2,7 +2,7 @@
%%----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -33,7 +33,6 @@
%%======================================================================
%% External functions
%%======================================================================
-
%%----------------------------------------------------------------------
%% Initializations
%%----------------------------------------------------------------------
@@ -42,12 +41,15 @@ all() ->
[{group, bugs}].
groups() ->
- [{bugs, [], [ticket_8213, ticket_8214, ticket_11551]}].
+ [{bugs, [], [ticket_8213, ticket_8214, ticket_11551,
+ fragmented_xml_directive,
+ old_dom_event_fun_endDocument_bug,
+ event_fun_endDocument_error_test,
+ event_fun_startDocument_error_test]}].
-%%----------------------------------------------------------------------
+%%======================================================================
%% Tests
-%%----------------------------------------------------------------------
-
+%%======================================================================
%%----------------------------------------------------------------------
%% Test Case
%% ID: ticket_8213
@@ -56,7 +58,6 @@ ticket_8213(_Config) ->
{ok,ok,[]} = xmerl_sax_parser:stream("<elem/>", [{event_fun, fun (_E,_,_) -> ok end}]),
ok.
-
%%----------------------------------------------------------------------
%% Test Case
%% ID: ticket_8214
@@ -85,17 +86,80 @@ ticket_11551(_Config) ->
<a>hej</a>
<?xml version=\"1.0\" encoding=\"utf-8\" ?>
<a>hej</a>">>,
- {ok, undefined, <<"<?xml", _/binary>>} = xmerl_sax_parser:stream(Stream1, []),
+ {ok, undefined, <<"\n<?xml", _/binary>>} = xmerl_sax_parser:stream(Stream1, []),
Stream2= <<"<?xml version=\"1.0\" encoding=\"utf-8\" ?>
<a>hej</a>
<?xml version=\"1.0\" encoding=\"utf-8\" ?>
<a>hej</a>">>,
- {ok, undefined, <<"<?xml", _/binary>>} = xmerl_sax_parser:stream(Stream2, []),
+ {ok, undefined, <<"\n\n\n<?xml", _/binary>>} = xmerl_sax_parser:stream(Stream2, []),
Stream3= <<"<a>hej</a>
<?xml version=\"1.0\" encoding=\"utf-8\" ?>
<a>hej</a>">>,
- {ok, undefined, <<"<?xml", _/binary>>} = xmerl_sax_parser:stream(Stream3, []),
+ {ok, undefined, <<"\n\n<?xml", _/binary>>} = xmerl_sax_parser:stream(Stream3, []),
+ ok.
+
+%%----------------------------------------------------------------------
+%% Test Case
+%% ID: fragmented_xml_directive
+%% Test of fragmented xml directive by reading one byte per continuation ca
+fragmented_xml_directive(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ Name = filename:join(DataDir, "test_data_1.xml"),
+ {ok, Fd} = file:open(Name, [raw, read,binary]),
+ Cf = fun cf_fragmented_xml_directive/1,
+ {ok, undefined, _} = xmerl_sax_parser:stream(<<>>,
+ [{continuation_fun, Cf},
+ {continuation_state, Fd}]),
+ ok.
+
+%%----------------------------------------------------------------------
+%% Test Case
+%% ID: old_dom_event_fun_endDocument_bug
+%% The old_dom backend previous generateded an uncatched exception
+%% instead of the correct fatal_error from the parser.
+old_dom_event_fun_endDocument_bug(_Config) ->
+ %% Stream contains bad characters,
+ {fatal_error, _, _, _, _} =
+ xmerl_sax_parser:stream([60,63,120,109,108,32,118,101,114,115,105,111,110,61,39,49,46,48,39,32,101,110,99,111,100,105,110,103,61,39,117,116,102,45,56,39,63,62,60,
+ 99,111,109,109,97,110,100,62,60,104,101,97,100,101,114,62,60,116,114,97,110,115,97,99,116,105,111,110,73,100,62,49,60,47,116,114,97,110,
+ 115,97,99,116,105,111,110,73,100,62,60,47,104,101,97,100,101,114,62,60,98,111,100,121,62,95,226,130,172,59,60,60,47,98,111,100,121,62,60,
+ 47,99,111,109,109,97,110,100,62,60,47,120,49,95,49,62],
+ [{event_fun,fun xmerl_sax_old_dom:event/3},
+ {event_state,xmerl_sax_old_dom:initial_state()}]),
ok.
+
+%%----------------------------------------------------------------------
+%% Test Case
+%% ID: event_fun_endDocument_error_test
+event_fun_endDocument_error_test(_Config) ->
+ Stream = <<"<?xml version=\"1.0\" encoding=\"utf-8\"?><a>hej</a>">>,
+ Ef = fun(endDocument, _ , _) -> throw({event_error, "endDocument error"});
+ (_, _, S) -> S
+ end,
+ {event_error, _, _, _, _} = xmerl_sax_parser:stream(Stream, [{event_fun, Ef}]),
+ ok.
+
+%%----------------------------------------------------------------------
+%% Test Case
+%% ID: event_fun_startDocument_error_test
+event_fun_startDocument_error_test(_Config) ->
+ Stream = <<"<?xml version=\"1.0\" encoding=\"utf-8\"?><a>hej</a>">>,
+ Ef = fun(startDocument, _ , _) -> throw({event_error, "endDocument error"});
+ (_, _, S) -> S
+ end,
+ {event_error, _, _, _, _} = xmerl_sax_parser:stream(Stream, [{event_fun, Ef}]),
+ ok.
+
+%%======================================================================
+%% Internal functions
+%%======================================================================
+cf_fragmented_xml_directive(IoDevice) ->
+ case file:read(IoDevice, 1) of
+ eof ->
+ {<<>>, IoDevice};
+ {ok, FileBin} ->
+ {FileBin, IoDevice}
+ end.
diff --git a/lib/xmerl/test/xmerl_sax_SUITE_data/test_data_1.xml b/lib/xmerl/test/xmerl_sax_SUITE_data/test_data_1.xml
new file mode 100644
index 0000000000..efbaee6b81
--- /dev/null
+++ b/lib/xmerl/test/xmerl_sax_SUITE_data/test_data_1.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<a>
+Hej
+</a>
diff --git a/lib/xmerl/test/xmerl_sax_std_SUITE.erl b/lib/xmerl/test/xmerl_sax_std_SUITE.erl
index 525a3b175a..b8412206cc 100644
--- a/lib/xmerl/test/xmerl_sax_std_SUITE.erl
+++ b/lib/xmerl/test/xmerl_sax_std_SUITE.erl
@@ -2,7 +2,7 @@
%%----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. 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.
@@ -507,11 +507,8 @@ end_per_testcase(_Func,_Config) ->
'not-wf-sa-036'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"xmltest","not-wf/sa/036.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_,<<"Illegal data\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -522,11 +519,8 @@ end_per_testcase(_Func,_Config) ->
'not-wf-sa-037'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"xmltest","not-wf/sa/037.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_,<<"&#32;\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -561,11 +555,8 @@ end_per_testcase(_Func,_Config) ->
'not-wf-sa-040'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"xmltest","not-wf/sa/040.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_,<<"<doc></doc>\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -576,11 +567,8 @@ end_per_testcase(_Func,_Config) ->
'not-wf-sa-041'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"xmltest","not-wf/sa/041.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_,<<"<doc></doc>\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -603,11 +591,8 @@ end_per_testcase(_Func,_Config) ->
'not-wf-sa-043'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"xmltest","not-wf/sa/043.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_,<<"Illegal data\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -618,11 +603,8 @@ end_per_testcase(_Func,_Config) ->
'not-wf-sa-044'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"xmltest","not-wf/sa/044.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_,<<"<doc/>\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -669,11 +651,8 @@ end_per_testcase(_Func,_Config) ->
'not-wf-sa-048'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"xmltest","not-wf/sa/048.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_,<<"<![CDATA[]]>\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -1416,11 +1395,8 @@ end_per_testcase(_Func,_Config) ->
'not-wf-sa-110'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"xmltest","not-wf/sa/110.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_,<<"&e;\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -1914,9 +1890,9 @@ end_per_testcase(_Func,_Config) ->
%% Special case becase we returns everything after a legal document
%% as an rest instead of giving and error to let the user handle
%% multipple docs on a stream.
- {ok,_,<<"<?xml version=\"1.0\"?>\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- % R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
- % check_result(R, "not-wf").
+ %{ok,_,<<"<?xml version=\"1.0\"?>\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -7784,11 +7760,8 @@ end_per_testcase(_Func,_Config) ->
'o-p01fail3'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"oasis","p01fail3.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_, <<"<bad/>", _/binary>>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -11417,12 +11390,8 @@ end_per_testcase(_Func,_Config) ->
'ibm-not-wf-P01-ibm01n02'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"ibm","not-wf/P01/ibm01n02.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_, <<"<?xml version=\"1.0\"?>", _/binary>>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- % R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
- % check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Case
@@ -11433,11 +11402,8 @@ end_per_testcase(_Func,_Config) ->
'ibm-not-wf-P01-ibm01n03'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"ibm","not-wf/P01/ibm01n03.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_, <<"<title>Wrong combination!</title>", _/binary>>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Cases
@@ -13027,11 +12993,8 @@ end_per_testcase(_Func,_Config) ->
'ibm-not-wf-P27-ibm27n01'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"ibm","not-wf/P27/ibm27n01.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_, <<"<!ELEMENT cat EMPTY>">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Cases
@@ -13461,11 +13424,8 @@ end_per_testcase(_Func,_Config) ->
'ibm-not-wf-P39-ibm39n06'(Config) ->
file:set_cwd(xmerl_test_lib:get_data_dir(Config)),
Path = filename:join([xmerl_test_lib:get_data_dir(Config),"ibm","not-wf/P39/ibm39n06.xml"]),
- %% Special case becase we returns everything after a legal document
- %% as an rest instead of giving and error to let the user handle
- %% multipple docs on a stream.
- {ok,_,<<"content after end tag\r\n">>} = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]).
- %%check_result(R, "not-wf").
+ R = xmerl_sax_parser:file(Path, [{event_fun, fun(_,_,S) -> S end}]),
+ check_result(R, "not-wf").
%%----------------------------------------------------------------------
%% Test Cases
diff --git a/lib/xmerl/test/xmerl_sax_stream_SUITE.erl b/lib/xmerl/test/xmerl_sax_stream_SUITE.erl
new file mode 100644
index 0000000000..7315f67374
--- /dev/null
+++ b/lib/xmerl/test/xmerl_sax_stream_SUITE.erl
@@ -0,0 +1,260 @@
+%%-*-erlang-*-
+%%----------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. 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%
+%%----------------------------------------------------------------------
+%% File : xmerl_sax_stream_SUITE.erl
+%%----------------------------------------------------------------------
+-module(xmerl_sax_stream_SUITE).
+-compile(export_all).
+
+%%----------------------------------------------------------------------
+%% Include files
+%%----------------------------------------------------------------------
+-include_lib("common_test/include/ct.hrl").
+-include_lib("kernel/include/file.hrl").
+
+%%======================================================================
+%% External functions
+%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Initializations
+%%----------------------------------------------------------------------
+all() ->
+ [
+ one_document,
+ two_documents,
+ one_document_and_junk,
+ end_of_stream
+ ].
+
+%%----------------------------------------------------------------------
+%% Initializations
+%%----------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_Func, _Config) ->
+ ok.
+
+%%----------------------------------------------------------------------
+%% Tests
+%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% Send One doc over stream
+one_document(Config) ->
+ Port = 11111,
+
+ {ok, ListenSocket} = listen(Port),
+ Self = self(),
+
+ spawn(
+ fun() ->
+ case catch gen_tcp:accept(ListenSocket) of
+ {ok, S} ->
+ Result = xmerl_sax_parser:stream(<<>>,
+ [{continuation_state, S},
+ {continuation_fun,
+ fun(Sd) ->
+ io:format("Continuation called!!", []),
+ case gen_tcp:recv(Sd, 0) of
+ {ok, Packet} ->
+ io:format("Packet: ~p\n", [Packet]),
+ {Packet, Sd};
+ {error, Reason} ->
+ throw({error, Reason})
+ end
+ end}]),
+ Self ! {xmerl_sax, Result},
+ close(S);
+ Error ->
+ Self ! {xmerl_sax, {error, {accept, Error}}}
+ end
+ end),
+
+ {ok, SendSocket} = connect(localhost, Port),
+
+ {ok, Binary} = file:read_file(filename:join([datadir(Config), "xmerl_sax_stream_one.xml"])),
+
+ send_chunks(SendSocket, Binary),
+
+ receive
+ {xmerl_sax, {ok, undefined, Rest}} ->
+ <<"\n">> = Rest,
+ io:format("Ok Rest: ~p\n", [Rest])
+ after 5000 ->
+ ct:fail("Timeout")
+ end,
+ ok.
+
+%%----------------------------------------------------------------------
+%% Send Two doc over stream
+two_documents(Config) ->
+ Port = 11111,
+
+ {ok, ListenSocket} = listen(Port),
+ Self = self(),
+
+ spawn(
+ fun() ->
+ case catch gen_tcp:accept(ListenSocket) of
+ {ok, S} ->
+ Result = xmerl_sax_parser:stream(<<>>,
+ [{continuation_state, S},
+ {continuation_fun,
+ fun(Sd) ->
+ io:format("Continuation called!!", []),
+ case gen_tcp:recv(Sd, 0) of
+ {ok, Packet} ->
+ io:format("Packet: ~p\n", [Packet]),
+ {Packet, Sd};
+ {error, Reason} ->
+ throw({error, Reason})
+ end
+ end}]),
+ Self ! {xmerl_sax, Result},
+ close(S);
+ Error ->
+ Self ! {xmerl_sax, {error, {accept, Error}}}
+ end
+ end),
+
+ {ok, SendSocket} = connect(localhost, Port),
+
+ {ok, Binary} = file:read_file(filename:join([datadir(Config), "xmerl_sax_stream_two.xml"])),
+
+ send_chunks(SendSocket, Binary),
+
+ receive
+ {xmerl_sax, {ok, undefined, Rest}} ->
+ <<"\n<?x", _R/binary>> = Rest,
+ io:format("Ok Rest: ~p\n", [Rest])
+ after 5000 ->
+ ct:fail("Timeout")
+ end,
+ ok.
+
+%%----------------------------------------------------------------------
+%% Send one doc and then junk on stream
+one_document_and_junk(Config) ->
+ Port = 11111,
+
+ {ok, ListenSocket} = listen(Port),
+ Self = self(),
+
+ spawn(
+ fun() ->
+ case catch gen_tcp:accept(ListenSocket) of
+ {ok, S} ->
+ Result = xmerl_sax_parser:stream(<<>>,
+ [{continuation_state, S},
+ {continuation_fun,
+ fun(Sd) ->
+ io:format("Continuation called!!", []),
+ case gen_tcp:recv(Sd, 0) of
+ {ok, Packet} ->
+ io:format("Packet: ~p\n", [Packet]),
+ {Packet, Sd};
+ {error, Reason} ->
+ throw({error, Reason})
+ end
+ end}]),
+ Self ! {xmerl_sax, Result},
+ close(S);
+ Error ->
+ Self ! {xmerl_sax, {error, {accept, Error}}}
+ end
+ end),
+
+ {ok, SendSocket} = connect(localhost, Port),
+
+ {ok, Binary} = file:read_file(filename:join([datadir(Config), "xmerl_sax_stream_one_junk.xml"])),
+
+ send_chunks(SendSocket, Binary),
+
+ receive
+ {xmerl_sax, {ok, undefined, Rest}} ->
+ <<"\nth", _R/binary>> = Rest,
+ io:format("Ok Rest: ~p\n", [Rest])
+ after 10000 ->
+ ct:fail("Timeout")
+ end,
+ ok.
+
+%%----------------------------------------------------------------------
+%% Test of continuation when end of stream
+end_of_stream(Config) ->
+ Stream = <<"<?xml version=\"1.0\" encoding=\"utf-8\"?><a>hej</a>">>,
+ {ok, undefined, <<>>} = xmerl_sax_parser:stream(Stream, []),
+ ok.
+
+%%----------------------------------------------------------------------
+%% Utility functions
+%%----------------------------------------------------------------------
+listen(Port) ->
+ case catch gen_tcp:listen(Port, [{active, false},
+ binary,
+ {keepalive, true},
+ {reuseaddr,true}]) of
+ {ok, ListenSocket} ->
+ {ok, ListenSocket};
+ {error, Reason} ->
+ {error, {listen, Reason}}
+ end.
+
+close(Socket) ->
+ (catch gen_tcp:close(Socket)).
+
+connect(Host, Port) ->
+ Timeout = 5000,
+ % Options1 = check_options(Options),
+ Options = [binary],
+ case catch gen_tcp:connect(Host, Port, Options, Timeout) of
+ {ok, Socket} ->
+ {ok, Socket};
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+send_chunks(Socket, Binary) ->
+ BSize = erlang:size(Binary),
+ if
+ BSize > 25 ->
+ <<Head:25/binary, Tail/binary>> = Binary,
+ case gen_tcp:send(Socket, Head) of
+ ok ->
+ timer:sleep(1000),
+ send_chunks(Socket, Tail);
+ {error,closed} ->
+ ok
+ end;
+ true ->
+ gen_tcp:send(Socket, Binary)
+ end.
+
+datadir(Config) ->
+ proplists:get_value(data_dir, Config).
diff --git a/lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_one.xml b/lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_one.xml
new file mode 100644
index 0000000000..30328bb188
--- /dev/null
+++ b/lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_one.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<person>
+<name>
+Arne Andersson
+</name>
+<address>
+<street>
+ Old Road 456
+</street>
+<zip>
+12323
+</zip>
+<city>
+Small City
+</city>
+</address>
+</person>
diff --git a/lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_one_junk.xml b/lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_one_junk.xml
new file mode 100644
index 0000000000..f730a95865
--- /dev/null
+++ b/lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_one_junk.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<person>
+<name>
+Arne Andersson
+</name>
+<address>
+<street>
+ Old Road 456
+</street>
+<zip>
+12323
+</zip>
+<city>
+Small City
+</city>
+</address>
+</person>
+this is junk ......
diff --git a/lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_two.xml b/lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_two.xml
new file mode 100644
index 0000000000..e241a02190
--- /dev/null
+++ b/lib/xmerl/test/xmerl_sax_stream_SUITE_data/xmerl_sax_stream_two.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<person>
+<name>
+Arne Andersson
+</name>
+<address>
+<street>
+ Old Road 456
+</street>
+<zip>
+12323
+</zip>
+<city>
+Small City
+</city>
+</address>
+</person>
+<?xml version="1.0"?>
+<person>
+<name>
+Bertil Bengtson
+</name>
+<address>
+<street>
+ New Road 4
+</street>
+<zip>
+12328
+</zip>
+<city>
+Small City
+</city>
+</address>
+</person>
diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index a78a035a1f..4e741d59a8 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1 +1 @@
-XMERL_VSN = 1.3.11
+XMERL_VSN = 1.3.14
diff --git a/make/emd2exml.in b/make/emd2exml.in
index 903d707716..b4e052fef5 100644..100755
--- a/make/emd2exml.in
+++ b/make/emd2exml.in
@@ -1,4 +1,4 @@
-#!/usr/bin/env escript
+#!@ENV@ escript
%% -*- erlang -*-
%%! -smp disable
diff --git a/make/make_emakefile b/make/make_emakefile.in
index 56440d9bf0..fbca77887a 100755
--- a/make/make_emakefile
+++ b/make/make_emakefile.in
@@ -1,4 +1,4 @@
-#!/usr/bin/env perl
+#!@PERL@
# -*- cperl -*-
use strict;
diff --git a/make/otp.mk.in b/make/otp.mk.in
index c05c499d66..7e2945b561 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -4,7 +4,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2013. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -260,6 +260,7 @@ DEFAULT_GIF_FILES = $(HTMLDIR)/min_head.gif
XSLTPROC = @XSLTPROC@
FOP = @FOP@
XMLLINT = @XMLLINT@
+CP = @CP@
DOCGEN=$(ERL_TOP)/lib/erl_docgen
FOP_CONFIG = $(DOCGEN)/priv/fop.xconf
@@ -271,6 +272,8 @@ SPECS_EXTRACTOR=$(DOCGEN)/priv/bin/specs_gen.escript
# Extract specifications and types from Erlang source files (-spec, -type)
$(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/%.erl
escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) -o$(dir $@) $<
+$(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/gen/%.erl
+ escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) -o$(dir $@) $<
$(MAN1DIR)/%.1: %.xml
diff --git a/otp_build b/otp_build
index 28a229b101..d0f75aff71 100755
--- a/otp_build
+++ b/otp_build
@@ -19,9 +19,6 @@
# %CopyrightEnd%
#
-# Expected autoconf version
-EXPECTED_AUTOCONF_VERSION=2.59
-
# Global configuration variables
#
# NOTE: lazy_configure depends on '.' always being last directory
@@ -288,30 +285,6 @@ do_autoconf ()
create_lib_configure_in
distribute_config_helpers
- if target_contains win32; then
- # Select the correct autoconf on cygwin
- save_want_autoconf_ver=$WANT_AUTOCONF_VER
- WANT_AUTOCONF_VER=$EXPECTED_AUTOCONF_VERSION
- export WANT_AUTOCONF_VER
- fi
- exp_ac_vsn=$EXPECTED_AUTOCONF_VERSION
- ac_vsn_blob=`autoconf --version`
- ac_vsn=`echo x$ac_vsn_blob | sed "s|[^0-9]*\([0-9][^ \t\n]*\).*|\1|"`
- case "$ac_vsn" in
- $exp_ac_vsn)
- ;;
- *)
- echo "***************************************************" 1>&2
- echo "***************************************************" 1>&2
- echo "*** WARNING: System might fail to configure or" 1>&2
- echo "*** might be erroneously configured" 1>&2
- echo "*** since autoconf version $ac_vsn is used" 1>&2
- echo "*** instead of version $exp_ac_vsn!" 1>&2
- echo "***************************************************" 1>&2
- echo "***************************************************" 1>&2
- ;;
- esac
-
if [ ! -z "$OVERRIDE_CONFIGURE" ]; then
echo "Autoconf disabled on target $TARGET, but is performed on host" >&2
# We still use erts configure for erl_interface and VxWorks
@@ -346,11 +319,6 @@ do_autoconf ()
done
restore_vars OVERRIDE_TARGET TARGET
-
- if target_contains win32; then
- WANT_AUTOCONF_VER=$save_want_autoconf_ver
- export WANT_AUTOCONF_VER
- fi
}
run_configure ()
@@ -583,7 +551,6 @@ do_lazy_configure ()
CONFIGURE_DIR=$dir \
EXTRA_CONFIGURE_DEPENDENCIES=$xc_dep \
EXTRA_CONFIG_STATUS_DEPENDENCIES=$xcs_dep \
- EXPECTED_AUTOCONF_VERSION=$EXPECTED_AUTOCONF_VERSION \
lazy_configure
echo "=== Done configuring $dir"
echo ""
@@ -613,7 +580,6 @@ do_lazy_configure_clean ()
MAKE="$MAKE" TARGET=$TARGET \
ERL_TOP=$ERL_TOP \
CONFIGURE_DIR=$dir \
- EXPECTED_AUTOCONF_VERSION=$EXPECTED_AUTOCONF_VERSION \
lazy_configure_clean
echo "=== Done cleaning configure in $dir"
echo ""
@@ -644,7 +610,6 @@ do_lazy_configure_target_clean ()
MAKE="$MAKE" TARGET=$TARGET \
ERL_TOP=$ERL_TOP \
CONFIGURE_DIR=$dir \
- EXPECTED_AUTOCONF_VERSION=$EXPECTED_AUTOCONF_VERSION \
lazy_configure_target_clean
echo "=== Done target cleaning configure in $dir"
echo ""
diff --git a/otp_versions.table b/otp_versions.table
index bfaa17a394..fc05a82aeb 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,4 +1,46 @@
+OTP-19.3.6.11 : erts-8.3.5.6 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2.0.1 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2.4 ssl-8.1.3.1.1 stdlib-3.3 syntax_tools-2.1.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.10 : erts-8.3.5.5 syntax_tools-2.1.1.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2.0.1 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2.4 ssl-8.1.3.1.1 stdlib-3.3 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.9 : ssh-4.4.2.4 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.4 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2.0.1 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssl-8.1.3.1.1 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.8 : ssh-4.4.2.3 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.4 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2.0.1 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssl-8.1.3.1.1 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.7 : kernel-5.2.0.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.4 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2.2 ssl-8.1.3.1.1 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.6 : ssh-4.4.2.2 ssl-8.1.3.1.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.4 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.5 : erts-8.3.5.4 mnesia-4.14.3.1 ssh-4.4.2.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssl-8.1.3.1 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.4 : ssl-8.1.3.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.3 : compiler-7.0.4.1 erts-8.3.5.3 # asn1-4.0.4 common_test-1.14 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.2 : erts-8.3.5.2 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6.1 : erts-8.3.5.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.6 : erts-8.3.5 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
+OTP-19.3.5 : erts-8.3.4 xmerl-1.3.14 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 :
+OTP-19.3.4 : inets-6.3.9 ssl-8.1.3 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.13 :
+OTP-19.3.3 : dialyzer-3.1.1 erts-8.3.3 inets-6.3.8 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.2 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.13 :
+OTP-19.3.2 : erts-8.3.2 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.7 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.2 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.13 :
+OTP-19.3.1 : crypto-3.7.4 erts-8.3.1 inets-6.3.7 ssh-4.4.2 ssl-8.1.2 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 debugger-4.2.1 dialyzer-3.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.13 :
+OTP-19.3 : common_test-1.14 compiler-7.0.4 crypto-3.7.3 dialyzer-3.1 diameter-1.12.2 erl_interface-3.9.3 erts-8.3 hipe-3.15.4 inets-6.3.6 kernel-5.2 observer-2.3.1 os_mon-2.4.2 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.1 ssl-8.1.1 stdlib-3.3 tools-2.9.1 typer-0.9.12 xmerl-1.3.13 # asn1-4.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 debugger-4.2.1 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 et-1.6 eunit-2.3.2 gs-1.6.2 ic-4.4.2 jinterface-1.7.1 megaco-3.18.1 mnesia-4.14.3 odbc-2.12 orber-3.8.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 syntax_tools-2.1.1 wx-1.8 :
+OTP-19.2.3 : erts-8.2.2 inets-6.3.5 # asn1-4.0.4 common_test-1.13 compiler-7.0.3 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.2 debugger-4.2.1 dialyzer-3.0.3 diameter-1.12.1 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.2 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.3 ic-4.4.2 jinterface-1.7.1 kernel-5.1.1 megaco-3.18.1 mnesia-4.14.3 observer-2.3 odbc-2.12 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.3 reltool-0.7.2 runtime_tools-1.11 sasl-3.0.2 snmp-5.2.4 ssh-4.4 ssl-8.1 stdlib-3.2 syntax_tools-2.1.1 tools-2.9 typer-0.9.11 wx-1.8 xmerl-1.3.12 :
+OTP-19.2.2 : mnesia-4.14.3 # asn1-4.0.4 common_test-1.13 compiler-7.0.3 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.2 debugger-4.2.1 dialyzer-3.0.3 diameter-1.12.1 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.2 erts-8.2.1 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.3 ic-4.4.2 inets-6.3.4 jinterface-1.7.1 kernel-5.1.1 megaco-3.18.1 observer-2.3 odbc-2.12 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.3 reltool-0.7.2 runtime_tools-1.11 sasl-3.0.2 snmp-5.2.4 ssh-4.4 ssl-8.1 stdlib-3.2 syntax_tools-2.1.1 tools-2.9 typer-0.9.11 wx-1.8 xmerl-1.3.12 :
+OTP-19.2.1 : erts-8.2.1 # asn1-4.0.4 common_test-1.13 compiler-7.0.3 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.2 debugger-4.2.1 dialyzer-3.0.3 diameter-1.12.1 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.2 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.3 ic-4.4.2 inets-6.3.4 jinterface-1.7.1 kernel-5.1.1 megaco-3.18.1 mnesia-4.14.2 observer-2.3 odbc-2.12 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.3 reltool-0.7.2 runtime_tools-1.11 sasl-3.0.2 snmp-5.2.4 ssh-4.4 ssl-8.1 stdlib-3.2 syntax_tools-2.1.1 tools-2.9 typer-0.9.11 wx-1.8 xmerl-1.3.12 :
+OTP-19.2 : common_test-1.13 compiler-7.0.3 crypto-3.7.2 dialyzer-3.0.3 edoc-0.8.1 erl_docgen-0.6.1 erl_interface-3.9.2 erts-8.2 eunit-2.3.2 hipe-3.15.3 inets-6.3.4 kernel-5.1.1 mnesia-4.14.2 observer-2.3 odbc-2.12 parsetools-2.1.4 public_key-1.3 runtime_tools-1.11 sasl-3.0.2 ssh-4.4 ssl-8.1 stdlib-3.2 syntax_tools-2.1.1 tools-2.9 wx-1.8 # asn1-4.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 debugger-4.2.1 diameter-1.12.1 eldap-1.2.2 et-1.6 gs-1.6.2 ic-4.4.2 jinterface-1.7.1 megaco-3.18.1 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 percept-0.9 reltool-0.7.2 snmp-5.2.4 typer-0.9.11 xmerl-1.3.12 :
+OTP-19.1.6.1 : erts-8.1.1.1 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssh-4.3.6 ssl-8.0.3 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
+OTP-19.1.6 : erts-8.1.1 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssh-4.3.6 ssl-8.0.3 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
+OTP-19.1.5 : ssh-4.3.6 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 erts-8.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssl-8.0.3 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
+OTP-19.1.4 : ssh-4.3.5 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 erts-8.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssl-8.0.3 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
+OTP-19.1.3 : ssh-4.3.4 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 erts-8.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssl-8.0.3 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
+OTP-19.1.2 : ssh-4.3.3 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 erts-8.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssl-8.0.3 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
+OTP-19.1.1 : ssl-8.0.3 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 erts-8.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssh-4.3.2 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
+OTP-19.1 : asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 erl_docgen-0.6 erl_interface-3.9.1 erts-8.1 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 parsetools-2.1.3 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssh-4.3.2 ssl-8.0.2 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 wx-1.7.1 xmerl-1.3.12 # cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 eldap-1.2.2 et-1.6 megaco-3.18.1 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 percept-0.9 public_key-1.2 typer-0.9.11 :
+OTP-19.0.7 : erts-8.0.5 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.2 jinterface-1.7 kernel-5.0.2 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.6 : erts-8.0.4 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.2 jinterface-1.7 kernel-5.0.2 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.5 : kernel-5.0.2 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0.3 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.2 jinterface-1.7 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.4 : erts-8.0.3 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.2 jinterface-1.7 kernel-5.0.1 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.3 : inets-6.3.2 kernel-5.0.1 ssl-8.0.1 # asn1-4.0.3 common_test-1.12.2 compiler-7.0.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0.2 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 jinterface-1.7 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 stdlib-3.0.1 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.2 : compiler-7.0.1 erts-8.0.2 stdlib-3.0.1 # asn1-4.0.3 common_test-1.12.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
+OTP-19.0.1 : dialyzer-3.0.1 erts-8.0.1 inets-6.3.1 observer-2.2.1 ssh-4.3.1 tools-2.8.5 # asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
OTP-19.0 : asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 tools-2.8.4 typer-0.9.11 wx-1.7 xmerl-1.3.11 # :
+OTP-18.3.4.5 : crypto-3.6.3.1 erts-7.3.1.3 inets-6.2.4.1 ssh-4.2.2.3 # asn1-4.0.2 common_test-1.12.1.1 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 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 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 ssl-7.3.3.1 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.4.4 : erts-7.3.1.2 # asn1-4.0.2 common_test-1.12.1.1 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 inets-6.2.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.2 ssl-7.3.3.1 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.4.3 : ssh-4.2.2.2 # asn1-4.0.2 common_test-1.12.1.1 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.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.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 ssl-7.3.3.1 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.4.2 : common_test-1.12.1.1 erts-7.3.1.1 ssl-7.3.3.1 # 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 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.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.1 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.4.1 : ssh-4.2.2.1 # asn1-4.0.2 common_test-1.12.1 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 inets-6.2.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 ssl-7.3.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.4 : inets-6.2.4 ssl-7.3.3 # asn1-4.0.2 common_test-1.12.1 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.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 :
diff --git a/scripts/Dockerfile.32 b/scripts/Dockerfile.32
new file mode 100644
index 0000000000..23a360a58e
--- /dev/null
+++ b/scripts/Dockerfile.32
@@ -0,0 +1,9 @@
+FROM erlang/ubuntu-build:32bit
+
+ADD ./otp.tar.gz /buildroot/
+
+WORKDIR /buildroot/otp/
+
+ENV MAKEFLAGS -j4
+
+CMD ./scripts/build-otp
diff --git a/scripts/Dockerfile.32.ubuntu b/scripts/Dockerfile.32.ubuntu
new file mode 100644
index 0000000000..c334f74379
--- /dev/null
+++ b/scripts/Dockerfile.32.ubuntu
@@ -0,0 +1,5 @@
+FROM 32bit/ubuntu:16.04
+
+RUN apt-get update
+
+RUN apt-get --fix-missing -y install build-essential m4 libncurses5-dev libssh-dev unixodbc-dev libgmp3-dev fop xsltproc default-jdk git autoconf libwxbase3.0-dev libwxgtk3.0-dev
diff --git a/scripts/Dockerfile.64 b/scripts/Dockerfile.64
new file mode 100644
index 0000000000..199067e5fe
--- /dev/null
+++ b/scripts/Dockerfile.64
@@ -0,0 +1,9 @@
+FROM erlang/ubuntu-build:64bit
+
+ADD ./otp.tar.gz /buildroot/
+
+WORKDIR /buildroot/otp/
+
+ENV MAKEFLAGS -j4
+
+CMD ./scripts/build-otp
diff --git a/scripts/Dockerfile.64.ubuntu b/scripts/Dockerfile.64.ubuntu
new file mode 100644
index 0000000000..514fea70b5
--- /dev/null
+++ b/scripts/Dockerfile.64.ubuntu
@@ -0,0 +1,5 @@
+FROM ubuntu:16.04
+
+RUN apt-get update
+
+RUN apt-get --fix-missing -y install build-essential m4 libncurses5-dev libssh-dev unixodbc-dev libgmp3-dev fop xsltproc default-jdk git autoconf libwxbase3.0-dev libwxgtk3.0-dev
diff --git a/scripts/build-docker-otp b/scripts/build-docker-otp
new file mode 100755
index 0000000000..01bb0b628e
--- /dev/null
+++ b/scripts/build-docker-otp
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+if [ $# -lt 1 ]; then
+ echo "Usage $0 32|64 [command] [arg]..."
+ exit 1
+fi
+
+ARCH="$1"
+shift
+
+git archive --format=tar.gz --prefix=otp/ HEAD >scripts/otp.tar.gz
+
+docker build -t otp --file scripts/Dockerfile.$ARCH scripts
+rm scripts/otp.tar.gz
+docker run --volume="$PWD/logs:/buildroot/otp/logs" -i --rm otp ${1+"$@"}
diff --git a/scripts/build-otp b/scripts/build-otp
new file mode 100755
index 0000000000..92031c79c8
--- /dev/null
+++ b/scripts/build-otp
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+function progress {
+ local file=$1
+ ls=$(ls -l $file)
+ while [ true ]; do
+ sleep 10
+ new_ls=$(ls -l $file)
+ if [ "$new_ls" != "$ls" ]; then
+ echo -n "."
+ fi
+ ls="$new_ls"
+ done
+}
+
+function do_and_log {
+ log="logs/latest-log.$$"
+ echo "" >$log
+ echo -n "$1..."
+ (progress $log) &
+ pid=$!
+ disown
+ if ./otp_build $2 $3 >$log 2>&1; then
+ kill $pid >/dev/null 2>&1
+ echo " done."
+ else
+ kill $pid >/dev/null 2>&1
+ echo " failed."
+ tail -n 200 $log
+ echo "*** Failed ***"
+ exit 1
+ fi
+}
+
+if [ ! -d "logs" ]; then
+ mkdir logs
+fi
+
+do_and_log "Autoconfing" autoconf
+do_and_log "Configuring" configure
+do_and_log "Building OTP" boot -a
+
+exit 0
diff --git a/scripts/run-smoke-tests b/scripts/run-smoke-tests
new file mode 100755
index 0000000000..5a850c7107
--- /dev/null
+++ b/scripts/run-smoke-tests
@@ -0,0 +1,21 @@
+#!/bin/bash
+set -ev
+
+if [ -z "$ERL_TOP" ]; then
+ ERL_TOP=$(pwd)
+fi
+
+function run_smoke_tests {
+ cd $ERL_TOP/release/tests/test_server
+ $ERL_TOP/bin/erl -s ts install -s ts smoke_test batch -s init stop
+
+ if grep -q '=failed *[1-9]' ct_run.test_server@*/*/run.*/suite.log; then
+ echo "One or more tests failed."
+ exit 1
+ fi
+ rm -rf ct_run.test_server@*
+}
+
+run_smoke_tests
+ERL_FLAGS="-smp disable" run_smoke_tests
+
diff --git a/system/README b/system/README
deleted file mode 100644
index 97ec9177c4..0000000000
--- a/system/README
+++ /dev/null
@@ -1,78 +0,0 @@
-Erlang/OTP December 6, 2013
-
-
-Release of Erlang 5.10.4/OTP R16B03
-
-
-1. GENERAL
- -------
-
-1.1 Installation Guide
-
- The installation guide can be found in
-
- <inst-root>doc/installation_guide/users_guide.html
-
-1.2 Java
-
- The Java sources were compiled using Java version 1.5.
-
-1.3 Disk space
-
- An installation of Erlang/OTP needs approximately 300 MB of
- disk space.
-
-1.4 The package contains HTML documentation. You can also get this
- documentation preformatted for printing in PDF format from
-
- http://www.erlang.se/doc/
-
- This site also contains HTML documentation for older releases
- as a reference.
-
-1.5 The Erlang system can run old BEAM files compiled with R11B-2
- or later (and almost all BEAM files compiled with R11B-0 or
- R11B-1). BEAM files from R10B or earlier are not supported.
-
- To get the best performance, you should recompile your
- application code with the R15B compiler.
-
-
-2. NOTES ABOUT THE SOLARIS VERSION
- -------------------------------
-
-2.1 For the Sparc Solaris environment, Solaris10 (2.10) and above is
- supported. The emulator runs on older Solaris 8 (2.8) versions
- and above. Older Solaris versions are neither tested nor supported.
- Also an Ultrasparc (sun4u architecture) is required.
-
-
-3. NOTES ABOUT THE VXWORKS VERSION
- -------------------------------
-
-3.1 The platform VxWorks is discontinued in the sense that only the
- libraries (erl_interface and ic's libraries) are supported. The VxWorks
- release is still packaged as a full release, but no support will be
- available for anything but the communication libraries.
-
-4 NOTES ABOUT THE LINUX VERSIONS
- -----------------------------
-
-4.1 The following linux distributions/version combinations are supported
- and tested:
-
- SuSE 10.1 x86, SuSE 10.1 x86_64, SuSE 11.0 x86, SuSE 11.0 x86_64
-
-5. APPLICATIONS NOTES
- ------------------
-
-
-6. MORE INFORMATION
- ----------------
-
- Commercial customers please visit http://www.erlang.se to find
- releases and more information. Commercial support is given through
-
- Open source users please visit http://www.erlang.org and direct
- problems to the open source community through the mailing list.
diff --git a/system/doc/design_principles/applications.xml b/system/doc/design_principles/applications.xml
index 0a1b65ea8e..c673fde07e 100644
--- a/system/doc/design_principles/applications.xml
+++ b/system/doc/design_principles/applications.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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>Applications</title>
@@ -172,31 +172,136 @@ ch_app:stop([])</code>
</section>
<section>
- <marker id="app_dir"></marker>
- <title>Directory Structure</title>
- <p>When packaging code using <c>systools</c>, the code for each
- application is placed in a separate directory,
- <c>lib/Application-Vsn</c>, where <c>Vsn</c> is the version number.</p>
- <p>This can be useful to know, even if <c>systools</c> is not used,
- since Erlang/OTP is packaged according to the OTP principles
- and thus comes with this directory structure. The code server
- (see the <c>code(3)</c> manual page in Kernel) automatically
- uses code from
- the directory with the highest version number, if more than one
- version of an application is present.</p>
- <p>The application directory structure can also be used in the
- development environment. The version number can then
- be omitted from the name.</p>
- <p>The application directory has the following sub-directories:</p>
- <list type="bulleted">
- <item><c>src</c> - Contains the Erlang source code.</item>
- <item><c>ebin</c> - Contains the Erlang object code, the
- <c>beam</c> files. The <c>.app</c> file is also placed here.</item>
- <item><c>priv</c> - Used for application specific files. For
- example, C executables are placed here. The function
- <c>code:priv_dir/1</c> is to be used to access this directory.</item>
- <item><c>include</c> - Used for include files.</item>
- </list>
+ <marker id="app_dir"></marker>
+ <title>Directory Structure</title>
+ <p>When packaging code using <c>systools</c>, the code for each
+ application is placed in a separate directory,
+ <c>lib/Application-Vsn</c>, where <c>Vsn</c> is the version number.</p>
+ <p>This can be useful to know, even if <c>systools</c> is not used,
+ since Erlang/OTP is packaged according to the OTP principles
+ and thus comes with a specific directory structure. The code server
+ (see the <seealso marker="kernel:code"><c>code(3)</c></seealso> manual
+ page in Kernel) automatically uses code from
+ the directory with the highest version number, if more than one
+ version of an application is present.</p>
+ <section>
+ <title>Directory Structure guidelines for a Development Environment</title>
+ <p>Any directory structure for development will suffice as long as the released directory structure
+ adhere to the <seealso marker="#app_dir_released">description below</seealso>,
+ but it is encouraged that the same directory structure
+ also be used in a development environment. The version number should be omitted from the
+ application directory name since this is an artifact of the release step.
+ </p>
+ <p> Some sub-directories are <em>required</em>. Some sub-directories are <em>optional</em>, meaning that it should
+ only be used if the application itself requires it. Finally, some sub-directories are <em>recommended</em>,
+ meaning it is encouraged that it is used and used as described here. For example, both documentation
+ and tests are encouraged to exist in an application for it to be deemed a proper OTP application.</p>
+<code type="none">
+ ─ ${application}
+   ├── doc
+ │   ├── internal
+ │   ├── examples
+ │   └── src
+   ├── include
+   ├── priv
+   ├── src
+ │   └── ${application}.app.src
+   └── test
+</code>
+ <list type="bulleted">
+ <item><c>src</c> - Required. Contains the Erlang source code, the source of the <c>.app</c> file
+ and internal include files used by the application itself. Additional sub-directories within
+ <c>src</c> can be used as namespaces to organize source files. These directories should never
+ be deeper than one level.</item>
+ <item><c>priv</c> - Optional. Used for application specific files. </item>
+ <item><c>include</c> - Optional. Used for public include files that must be reachable from
+ other applications.</item>
+ <item><c>doc</c> - Recommended. Any source documentation should be placed in sub-directories here.</item>
+ <item><c>doc/internal</c> - Recommended. Any documentation that describes implementation details about
+ this application, not intended for publication, should be placed here.</item>
+ <item><c>doc/examples</c> - Recommended. Source code for examples on how to use this application should
+ be placed here. It is encouraged that examples are sourced to the public documentation from
+ this directory.</item>
+ <item><c>doc/src</c> - Recommended. All source files for documentation, such as Markdown, AsciiDoc or
+ XML-files, should be placed here.</item>
+ <item><c>test</c> - Recommended. All files regarding tests, such as test suites and test specifications,
+ should be placed here. </item>
+ </list>
+
+ <p>Other directories in the development environment may be needed. If source code from languages other
+ than Erlang is used, for instance C-code for NIFs, that code should be placed in a separate directory.
+ By convention it is recommended to prefix such directories with the language name, for example
+ <c>c_src</c> for C, <c>java_src</c> for Java or <c>go_src</c> for Go. Directories with <c>_src</c>
+ suffix indicates that it is a part of the application and the compilation step. The final build artifacts
+ should target the <c>priv/lib</c> or <c>priv/bin</c> directories.</p>
+ <p>The <c>priv</c> directory holds assets that the application needs during runtime. Executables should
+ reside in <c>priv/bin</c> and dynamically-linked libraries should reside in <c>priv/lib</c>. Other assets
+ are free to reside within the <c>priv</c> directory but it is recommended it does so in a structured manner.</p>
+ <p>Source files from other languages that generate Erlang code, such as ASN.1 or Mibs, should be placed
+ in directories, at the top level or in <c>src</c>, with the same name as the source language, for example
+ <c>asn1</c> and <c>mibs</c>. Build artifacts should be placed in their respective language directory,
+ such as <c>src</c> for Erlang code or <c>java_src</c> for Java code.</p>
+ <p>The <c>.app</c> file for release may reside in the <c>ebin</c>-directory in a development environment
+ but it is encouraged that this is an artifact of the build step. By convention a <c>.app.src</c> file
+ is used, which resides in the <c>src</c> directory. This file is nearly identical as the
+ <c>.app</c> file but certain fields may be replaced during the build step, such as the application version.</p>
+ <p>Directory names should not be capitalized.</p>
+ <p>It is encouraged to omit empty directories.</p>
+
+ </section>
+
+ <section>
+ <marker id="app_dir_released"></marker>
+ <title>Directory Structure for a Released System</title>
+ <p>A released application must follow a certain structure.
+ </p>
+<code type="none">
+ ─ ${application}-${version}
+   ├── bin
+   ├── doc
+ │   ├── html
+ │   ├── man[1-9]
+ │   ├── pdf
+ │   ├── internal
+ │   └── examples
+   ├── ebin
+ │   └── ${application}.app
+   ├── include
+   ├── priv
+ │   ├── lib
+ │   └── bin
+   └── src
+</code>
+ <list type="bulleted">
+ <item><c>src</c> - Optional. Contains the Erlang source code and internal include files
+ used by the application itself. This directory is no longer required in a released application.</item>
+ <item><c>ebin</c> - Required. Contains the Erlang object code, the <c>beam</c> files.
+ The <c>.app</c> file must also be placed here.</item>
+ <item><c>priv</c> - Optional. Used for application specific files. <c>code:priv_dir/1</c>
+ is to be used to access this directory.</item>
+ <item><c>priv/lib</c> - Recommended. Any shared-object files that are used by the application,
+ such as NIFs or linked-in-drivers, should be placed here.</item>
+ <item><c>priv/bin</c> - Recommended. Any executable that is used by the application,
+ such as port-programs, should be placed here.</item>
+ <item><c>include</c> - Optional. Used for public include files that must be reachable from
+ other applications.</item>
+ <item><c>bin</c> - Optional. Any executable that is a product of the application,
+ such as escripts or shell-scripts, should be placed here.</item>
+ <item><c>doc</c> - Optional. Any released documentation should be placed in
+ sub-directories here.</item>
+ <item><c>doc/man1</c> - Recommended. Man pages for Application executables.</item>
+ <item><c>doc/man3</c> - Recommended. Man pages for module APIs.</item>
+ <item><c>doc/man6</c> - Recommended. Man pages for Application overview.</item>
+ <item><c>doc/html</c> - Optional. HTML pages for the entire Application.</item>
+ <item><c>doc/pdf</c> - Optional. PDF documentation for the entire Application.</item>
+ </list>
+
+ <p>The <c>src</c> directory could be useful to release for debugging purposes but is not required.
+ The <c>include</c> directory should only be released if the applications has public include files.</p>
+ <p>The only documentation that is recommended to be released in this way are the man pages. HTML and PDF
+ will normally be distributed in some other manner.</p>
+ <p>It is encouraged to omit empty directories.</p>
+ </section>
</section>
<section>
@@ -381,4 +486,3 @@ application:start(Application, Type)</code>
<c>shutdown</c>, not <c>normal</c>.</p>
</section>
</chapter>
-
diff --git a/system/doc/design_principles/code_lock.dia b/system/doc/design_principles/code_lock.dia
index 8e6ff8a898..eaa2aca5b0 100644
--- a/system/doc/design_principles/code_lock.dia
+++ b/system/doc/design_principles/code_lock.dia
Binary files differ
diff --git a/system/doc/design_principles/code_lock.png b/system/doc/design_principles/code_lock.png
index 745fd91920..40bd35fc74 100644
--- a/system/doc/design_principles/code_lock.png
+++ b/system/doc/design_principles/code_lock.png
Binary files differ
diff --git a/system/doc/design_principles/code_lock_2.dia b/system/doc/design_principles/code_lock_2.dia
index 142909a2f5..3b9ba554d8 100644
--- a/system/doc/design_principles/code_lock_2.dia
+++ b/system/doc/design_principles/code_lock_2.dia
Binary files differ
diff --git a/system/doc/design_principles/code_lock_2.png b/system/doc/design_principles/code_lock_2.png
index ecf7b0d799..3aca9dd5aa 100644
--- a/system/doc/design_principles/code_lock_2.png
+++ b/system/doc/design_principles/code_lock_2.png
Binary files differ
diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml
index b63327291d..22b622ec5f 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2016</year>
+ <year>2016</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,11 +29,11 @@
<rev></rev>
<file>statem.xml</file>
</header>
- <marker id="gen_statem behaviour"></marker>
+ <marker id="gen_statem Behaviour" />
<p>
This section is to be read with the
<seealso marker="stdlib:gen_statem"><c>gen_statem(3)</c></seealso>
- manual page in <c>STDLIB</c>, where all interface functions and callback
+ manual page in STDLIB, where all interface functions and callback
functions are described in detail.
</p>
<note>
@@ -50,9 +50,10 @@
<!-- =================================================================== -->
<section>
+ <marker id="Event-Driven State Machines" />
<title>Event-Driven State Machines</title>
<p>
- Established Automata theory does not deal much with
+ Established Automata Theory does not deal much with
how a state transition is triggered,
but assumes that the output is a function
of the input (and the state) and that they are
@@ -94,7 +95,7 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<!-- =================================================================== -->
<section>
- <marker id="callback_modes" />
+ <marker id="Callback Modes" />
<title>Callback Modes</title>
<p>
The <c>gen_statem</c> behavior supports two callback modes:
@@ -109,8 +110,13 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</p>
<pre>
StateName(EventType, EventContent, Data) ->
- .. code for actions here ...
- {next_state, NewStateName, NewData}.</pre>
+ ... code for actions here ...
+ {next_state, NewStateName, NewData}.
+ </pre>
+ <p>
+ This form is used in most examples here for example in section
+ <seealso marker="#Example">Example</seealso>.
+ </p>
</item>
<item>
<p>
@@ -120,8 +126,14 @@ StateName(EventType, EventContent, Data) ->
</p>
<pre>
handle_event(EventType, EventContent, State, Data) ->
- .. code for actions here ...
- {next_state, NewState, NewData}</pre>
+ ... code for actions here ...
+ {next_state, NewState, NewData}
+ </pre>
+ <p>
+ See section
+ <seealso marker="#One Event Handler">One Event Handler</seealso>
+ for an example.
+ </p>
</item>
</list>
<p>
@@ -134,10 +146,11 @@ handle_event(EventType, EventContent, State, Data) ->
</p>
<section>
+ <marker id="Choosing the Callback Mode" />
<title>Choosing the Callback Mode</title>
<p>
The two
- <seealso marker="#callback_modes">callback modes</seealso>
+ <seealso marker="#Callback Modes">callback modes</seealso>
give different possibilities
and restrictions, but one goal remains:
you want to handle all possible combinations of
@@ -195,10 +208,195 @@ handle_event(EventType, EventContent, State, Data) ->
<!-- =================================================================== -->
<section>
+ <marker id="State Enter Calls" />
+ <title>State Enter Calls</title>
+ <p>
+ The <c>gen_statem</c> behavior can regardless of callback mode
+ automatically
+ <seealso marker="stdlib:gen_statem#type-state_enter">
+ call the state callback
+ </seealso>
+ with special arguments whenever the state changes
+ so you can write state entry actions
+ near the rest of the state transition rules.
+ It typically looks like this:
+ </p>
+ <pre>
+StateName(enter, _OldState, Data) ->
+ ... code for state entry actions here ...
+ {keep_state, NewData};
+StateName(EventType, EventContent, Data) ->
+ ... code for actions here ...
+ {next_state, NewStateName, NewData}.</pre>
+ <p>
+ Depending on how your state machine is specified,
+ this can be a very useful feature,
+ but it forces you to handle the state enter calls in all states.
+ See also the
+ <seealso marker="#State Entry Actions">
+ State Entry Actions
+ </seealso>
+ chapter.
+ </p>
+ </section>
+
+<!-- =================================================================== -->
+
+ <section>
+ <marker id="Actions" />
+ <title>Actions</title>
+ <p>
+ In the first section
+ <seealso marker="#Event-Driven State Machines">
+ Event-Driven State Machines
+ </seealso>
+ actions were mentioned as a part of
+ the general state machine model. These general actions
+ are implemented with the code that callback module
+ <c>gen_statem</c> executes in an event-handling
+ callback function before returning
+ to the <c>gen_statem</c> engine.
+ </p>
+ <p>
+ There are more specific state-transition actions
+ that a callback function can order the <c>gen_statem</c>
+ engine to do after the callback function return.
+ These are ordered by returning a list of
+ <seealso marker="stdlib:gen_statem#type-action">actions</seealso>
+ in the
+ <seealso marker="stdlib:gen_statem#type-state_callback_result">return tuple</seealso>
+ from the
+ <seealso marker="stdlib:gen_statem#Module:StateName/3">callback function</seealso>.
+ These state transition actions affect the <c>gen_statem</c>
+ engine itself and can do the following:
+ </p>
+ <list type="bulleted">
+ <item>
+ <seealso marker="stdlib:gen_statem#type-postpone">
+ Postpone
+ </seealso>
+ the current event, see section
+ <seealso marker="#Postponing Events">Postponing Events</seealso>
+ </item>
+ <item>
+ <seealso marker="stdlib:gen_statem#type-hibernate">
+ Hibernate
+ </seealso>
+ the <c>gen_statem</c>, treated in
+ <seealso marker="#Hibernation">Hibernation</seealso>
+ </item>
+ <item>
+ Start a
+ <seealso marker="stdlib:gen_statem#type-state_timeout">
+ state time-out</seealso>,
+ read more in section
+ <seealso marker="#State Time-Outs">State Time-Outs</seealso>
+ </item>
+ <item>
+ Start an
+ <seealso marker="stdlib:gen_statem#type-event_timeout">event time-out</seealso>,
+ see more in section
+ <seealso marker="#Event Time-Outs">Event Time-Outs</seealso>
+ </item>
+ <item>
+ <seealso marker="stdlib:gen_statem#type-reply_action">
+ Reply
+ </seealso>
+ to a caller, mentioned at the end of section
+ <seealso marker="#All State Events">All State Events</seealso>
+ </item>
+ <item>
+ Generate the
+ <seealso marker="stdlib:gen_statem#type-action">
+ next event
+ </seealso>
+ to handle, see section
+ <seealso marker="#Self-Generated Events">Self-Generated Events</seealso>
+ </item>
+ </list>
+ <p>
+ For details, see the
+ <seealso marker="stdlib:gen_statem#type-action">
+ <c>gen_statem(3)</c>
+ </seealso>
+ manual page.
+ You can, for example, reply to many callers
+ and generate multiple next events to handle.
+ </p>
+ </section>
+
+<!-- =================================================================== -->
+
+ <section>
+ <marker id="Event Types" />
+ <title>Event Types</title>
+ <p>
+ Events are categorized in different
+ <seealso marker="stdlib:gen_statem#type-event_type">event types</seealso>.
+ Events of all types are handled in the same callback function,
+ for a given state, and the function gets
+ <c>EventType</c> and <c>EventContent</c> as arguments.
+ </p>
+ <p>
+ The following is a complete list of event types and where
+ they come from:
+ </p>
+ <taglist>
+ <tag><c>cast</c></tag>
+ <item>
+ Generated by
+ <seealso marker="stdlib:gen_statem#cast/2"><c>gen_statem:cast</c></seealso>.
+ </item>
+ <tag><c>{call,From}</c></tag>
+ <item>
+ Generated by
+ <seealso marker="stdlib:gen_statem#call/2"><c>gen_statem:call</c></seealso>,
+ where <c>From</c> is the reply address to use
+ when replying either through the state transition action
+ <c>{reply,From,Msg}</c> or by calling
+ <seealso marker="stdlib:gen_statem#reply/1"><c>gen_statem:reply</c></seealso>.
+ </item>
+ <tag><c>info</c></tag>
+ <item>
+ Generated by any regular process message sent to
+ the <c>gen_statem</c> process.
+ </item>
+ <tag><c>state_timeout</c></tag>
+ <item>
+ Generated by state transition action
+ <seealso marker="stdlib:gen_statem#type-state_timeout">
+ <c>{state_timeout,Time,EventContent}</c>
+ </seealso>
+ state timer timing out.
+ </item>
+ <tag><c>timeout</c></tag>
+ <item>
+ Generated by state transition action
+ <seealso marker="stdlib:gen_statem#type-event_timeout">
+ <c>{timeout,Time,EventContent}</c>
+ </seealso>
+ (or its short form <c>Time</c>)
+ event timer timing out.
+ </item>
+ <tag><c>internal</c></tag>
+ <item>
+ Generated by state transition
+ <seealso marker="stdlib:gen_statem#type-action">action</seealso>
+ <c>{next_event,internal,EventContent}</c>.
+ All event types above can also be generated using
+ <c>{next_event,EventType,EventContent}</c>.
+ </item>
+ </taglist>
+ </section>
+
+<!-- =================================================================== -->
+
+ <section>
+ <marker id="Example" />
<title>Example</title>
<p>
This example starts off as equivalent to the example in section
- <seealso marker="fsm"><c>gen_fsm</c> Behavior</seealso>.
+ <seealso marker="fsm"><c>gen_fsm</c>&nbsp;Behavior</seealso>.
In later sections, additions and tweaks are made
using features in <c>gen_statem</c> that <c>gen_fsm</c> does not have.
The end of this chapter provides the example again
@@ -221,16 +419,14 @@ handle_event(EventType, EventContent, State, Data) ->
This code lock state machine can be implemented using
<c>gen_statem</c> with the following callback module:
</p>
- <marker id="ex"></marker>
<code type="erl"><![CDATA[
-module(code_lock).
-behaviour(gen_statem).
-define(NAME, code_lock).
--define(CALLBACK_MODE, state_functions).
-export([start_link/1]).
-export([button/1]).
--export([init/1,terminate/3,code_change/4]).
+-export([init/1,callback_mode/0,terminate/3,code_change/4]).
-export([locked/3,open/3]).
start_link(Code) ->
@@ -242,7 +438,10 @@ button(Digit) ->
init(Code) ->
do_lock(),
Data = #{code => Code, remaining => Code},
- {?CALLBACK_MODE,locked,Data}.
+ {ok, locked, Data}.
+
+callback_mode() ->
+ state_functions.
locked(
cast, {button,Digit},
@@ -250,19 +449,19 @@ locked(
case Remaining of
[Digit] ->
do_unlock(),
- {next_state,open,Data#{remaining := Code},10000};
+ {next_state, open, Data#{remaining := Code},
+ [{state_timeout,10000,lock}];
[Digit|Rest] -> % Incomplete
- {next_state,locked,Data#{remaining := Rest}};
+ {next_state, locked, Data#{remaining := Rest}};
_Wrong ->
- {next_state,locked,Data#{remaining := Code}}
+ {next_state, locked, Data#{remaining := Code}}
end.
-open(timeout, _, Data) ->
+open(state_timeout, lock, Data) ->
do_lock(),
- {next_state,locked,Data};
+ {next_state, locked, Data};
open(cast, {button,_}, Data) ->
- do_lock(),
- {next_state,locked,Data}.
+ {next_state, open, Data}.
do_lock() ->
io:format("Lock~n", []).
@@ -273,7 +472,7 @@ terminate(_Reason, State, _Data) ->
State =/= locked andalso do_lock(),
ok.
code_change(_Vsn, State, Data, _Extra) ->
- {?CALLBACK_MODE,State,Data}.
+ {ok, State, Data}.
]]></code>
<p>The code is explained in the next sections.</p>
</section>
@@ -281,6 +480,7 @@ code_change(_Vsn, State, Data, _Extra) ->
<!-- =================================================================== -->
<section>
+ <marker id="Starting gen_statem" />
<title>Starting gen_statem</title>
<p>
In the example in the previous section, <c>gen_statem</c> is
@@ -308,7 +508,7 @@ start_link(Code) ->
as <c>{global,Name}</c>, then the <c>gen_statem</c> is
registered using
<seealso marker="kernel:global#register_name/2"><c>global:register_name/2</c></seealso>
- in <c>Kernel</c>.
+ in Kernel.
</p>
</item>
<item>
@@ -343,14 +543,8 @@ start_link(Code) ->
<p>
If name registration succeeds, the new <c>gen_statem</c> process
calls callback function <c>code_lock:init(Code)</c>.
- This function is expected to return <c>{CallbackMode,State,Data}</c>,
- where
- <seealso marker="#callback_modes"><c>CallbackMode</c></seealso>
- selects callback module state function mode, in this case
- <seealso marker="stdlib:gen_statem#type-callback_mode"><c>state_functions</c></seealso>
- through macro <c>?CALLBACK_MODE</c>. That is, each state
- has got its own handler function.
- <c>State</c> is the initial state of the <c>gen_statem</c>,
+ This function is expected to return <c>{ok, State, Data}</c>,
+ where <c>State</c> is the initial state of the <c>gen_statem</c>,
in this case <c>locked</c>; assuming that the door is locked to begin
with. <c>Data</c> is the internal server data of the <c>gen_statem</c>.
Here the server data is a <seealso marker="stdlib:maps">map</seealso>
@@ -359,11 +553,12 @@ start_link(Code) ->
that stores the remaining correct button sequence
(the same as the <c>code</c> to begin with).
</p>
+
<code type="erl"><![CDATA[
init(Code) ->
do_lock(),
Data = #{code => Code, remaining => Code},
- {?CALLBACK_MODE,locked,Data}.
+ {ok,locked,Data}.
]]></code>
<p>Function
<seealso marker="stdlib:gen_statem#start_link/3"><c>gen_statem:start_link</c></seealso>
@@ -380,11 +575,27 @@ init(Code) ->
can be used to start a standalone <c>gen_statem</c>, that is,
a <c>gen_statem</c> that is not part of a supervision tree.
</p>
+
+ <code type="erl"><![CDATA[
+callback_mode() ->
+ state_functions.
+ ]]></code>
+ <p>
+ Function
+ <seealso marker="stdlib:gen_statem#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>
+ selects the
+ <seealso marker="#Callback Modes"><c>CallbackMode</c></seealso>
+ for the callback module, in this case
+ <seealso marker="stdlib:gen_statem#type-callback_mode"><c>state_functions</c></seealso>.
+ That is, each state has got its own handler function.
+ </p>
+
</section>
<!-- =================================================================== -->
<section>
+ <marker id="Handling Events" />
<title>Handling Events</title>
<p>The function notifying the code lock about a button event is
implemented using
@@ -404,11 +615,13 @@ button(Digit) ->
The event is made into a message and sent to the <c>gen_statem</c>.
When the event is received, the <c>gen_statem</c> calls
<c>StateName(cast, Event, Data)</c>, which is expected to
- return a tuple <c>{next_state,NewStateName,NewData}</c>.
+ return a tuple <c>{next_state, NewStateName, NewData}</c>,
+ or <c>{next_state, NewStateName, NewData, Actions}</c>.
<c>StateName</c> is the name of the current state and
<c>NewStateName</c> is the name of the next state to go to.
<c>NewData</c> is a new value for the server data of
- the <c>gen_statem</c>.
+ the <c>gen_statem</c>, and <c>Actions</c> is a list of
+ actions on the <c>gen_statem</c> engine.
</p>
<code type="erl"><![CDATA[
locked(
@@ -417,19 +630,19 @@ locked(
case Remaining of
[Digit] -> % Complete
do_unlock(),
- {next_state,open,Data#{remaining := Code},10000};
+ {next_state, open, Data#{remaining := Code},
+ [{state_timeout,10000,lock}]};
[Digit|Rest] -> % Incomplete
- {next_state,locked,Data#{remaining := Rest}};
+ {next_state, locked, Data#{remaining := Rest}};
[_|_] -> % Wrong
- {next_state,locked,Data#{remaining := Code}}
+ {next_state, locked, Data#{remaining := Code}}
end.
-open(timeout, _, Data) ->
+open(state_timeout, lock, Data) ->
do_lock(),
- {next_state,locked,Data};
+ {next_state, locked, Data};
open(cast, {button,_}, Data) ->
- do_lock(),
- {next_state,locked,Data}.
+ {next_state, open, Data}.
]]></code>
<p>
If the door is locked and a button is pressed, the pressed
@@ -443,38 +656,55 @@ open(cast, {button,_}, Data) ->
restarts from the start of the code sequence.
</p>
<p>
- In state <c>open</c>, any button locks the door, as
- any event cancels the event timer, so no
- time-out event occurs after a button event.
+ If the whole code is correct, the server changes states
+ to <c>open</c>.
+ </p>
+ <p>
+ In state <c>open</c>, a button event is ignored
+ by staying in the same state. This can also be done
+ by returning <c>{keep_state, Data}</c> or in this case
+ since <c>Data</c> unchanged even by returning
+ <c>keep_state_and_data</c>.
</p>
</section>
<section>
- <title>Event Time-Outs</title>
+ <marker id="State Time-Outs" />
+ <title>State Time-Outs</title>
<p>
When a correct code has been given, the door is unlocked and
the following tuple is returned from <c>locked/2</c>:
</p>
<code type="erl"><![CDATA[
-{next_state,open,Data#{remaining := Code},10000};
+{next_state, open, Data#{remaining := Code},
+ [{state_timeout,10000,lock}]};
]]></code>
<p>
10,000 is a time-out value in milliseconds.
After this time (10 seconds), a time-out occurs.
- Then, <c>StateName(timeout, 10000, Data)</c> is called.
+ Then, <c>StateName(state_timeout, lock, Data)</c> is called.
The time-out occurs when the door has been in state <c>open</c>
for 10 seconds. After that the door is locked again:
</p>
<code type="erl"><![CDATA[
-open(timeout, _, Data) ->
+open(state_timeout, lock, Data) ->
do_lock(),
- {next_state,locked,Data};
+ {next_state, locked, Data};
]]></code>
+ <p>
+ The timer for a state time-out is automatically cancelled
+ when the state machine changes states. You can restart
+ a state time-out by setting it to a new time, which cancels
+ the running timer and starts a new. This implies that
+ you can cancel a state time-out by restarting it with
+ time <c>infinity</c>.
+ </p>
</section>
<!-- =================================================================== -->
<section>
+ <marker id="All State Events" />
<title>All State Events</title>
<p>
Sometimes events can arrive in any state of the <c>gen_statem</c>.
@@ -507,21 +737,24 @@ open(EventType, EventContent, Data) ->
handle_event(EventType, EventContent, Data).
handle_event({call,From}, code_length, #{code := Code} = Data) ->
- {keep_state,Data,[{reply,From,length(Code)}]}.
+ {keep_state, Data, [{reply,From,length(Code)}]}.
]]></code>
<p>
This example uses
<seealso marker="stdlib:gen_statem#call/2"><c>gen_statem:call/2</c></seealso>,
which waits for a reply from the server.
The reply is sent with a <c>{reply,From,Reply}</c> tuple
- in an action list in the <c>{keep_state,...}</c> tuple
- that retains the current state.
+ in an action list in the <c>{keep_state, ...}</c> tuple
+ that retains the current state. This return form is convenient
+ when you want to stay in the current state but do not know or
+ care about what it is.
</p>
</section>
<!-- =================================================================== -->
<section>
+ <marker id="One Event Handler" />
<title>One Event Handler</title>
<p>
If mode <c>handle_event_function</c> is used,
@@ -533,12 +766,11 @@ handle_event({call,From}, code_length, #{code := Code} = Data) ->
</p>
<code type="erl"><![CDATA[
...
--define(CALLBACK_MODE, handle_event_function).
-
-...
-export([handle_event/4]).
...
+callback_mode() ->
+ handle_event_function.
handle_event(cast, {button,Digit}, State, #{code := Code} = Data) ->
case State of
@@ -546,19 +778,19 @@ handle_event(cast, {button,Digit}, State, #{code := Code} = Data) ->
case maps:get(remaining, Data) of
[Digit] -> % Complete
do_unlock(),
- {next_state,open,Data#{remaining := Code},10000};
+ {next_state, open, Data#{remaining := Code},
+ [{state_timeout,10000,lock}};
[Digit|Rest] -> % Incomplete
- {keep_state,Data#{remaining := Rest}};
+ {keep_state, Data#{remaining := Rest}};
[_|_] -> % Wrong
- {keep_state,Data#{remaining := Code}}
+ {keep_state, Data#{remaining := Code}}
end;
open ->
- do_lock(),
- {next_state,locked,Data}
+ keep_state_and_data
end;
-handle_event(timeout, _, open, Data) ->
+handle_event(state_timeout, lock, open, Data) ->
do_lock(),
- {next_state,locked,Data}.
+ {next_state, locked, Data}.
...
]]></code>
@@ -567,9 +799,11 @@ handle_event(timeout, _, open, Data) ->
<!-- =================================================================== -->
<section>
+ <marker id="Stopping" />
<title>Stopping</title>
<section>
+ <marker id="In a Supervision Tree" />
<title>In a Supervision Tree</title>
<p>
If the <c>gen_statem</c> is part of a supervision tree,
@@ -597,7 +831,7 @@ init(Args) ->
callback function <c>terminate(shutdown, State, Data)</c>.
</p>
<p>
- In the following example, function <c>terminate/3</c>
+ In this example, function <c>terminate/3</c>
locks the door if it is open, so we do not accidentally leave the door
open when the supervision tree terminates:
</p>
@@ -609,6 +843,7 @@ terminate(_Reason, State, _Data) ->
</section>
<section>
+ <marker id="Standalone gen_statem" />
<title>Standalone gen_statem</title>
<p>
If the <c>gen_statem</c> is not part of a supervision tree,
@@ -635,127 +870,77 @@ stop() ->
<!-- =================================================================== -->
<section>
- <title>Actions</title>
+ <marker id="Event Time-Outs" />
+ <title>Event Time-Outs</title>
<p>
- In the first sections actions were mentioned as a part of
- the general state machine model. These general actions
- are implemented with the code that callback module
- <c>gen_statem</c> executes in an event-handling
- callback function before returning
- to the <c>gen_statem</c> engine.
+ A timeout feature inherited from <c>gen_statem</c>'s predecessor
+ <seealso marker="stdlib:gen_fsm"><c>gen_fsm</c></seealso>,
+ is an event time-out, that is,
+ if an event arrives the timer is cancelled.
+ You get either an event or a time-out, but not both.
</p>
<p>
- There are more specific state-transition actions
- that a callback function can order the <c>gen_statem</c>
- engine to do after the callback function return.
- These are ordered by returning a list of
- <seealso marker="stdlib:gen_statem#type-action">actions</seealso>
- in the
- <seealso marker="stdlib:gen_statem#type-state_function_result">return tuple</seealso>
- from the
- <seealso marker="stdlib:gen_statem#Module:StateName/3">callback function</seealso>.
- These state transition actions affect the <c>gen_statem</c>
- engine itself and can do the following:
+ It is ordered by the state transition action
+ <c>{timeout,Time,EventContent}</c>, or just <c>Time</c>,
+ or even just <c>Time</c> instead of an action list
+ (the latter is a form inherited from <c>gen_fsm</c>.
</p>
- <list type="bulleted">
- <item>Postpone the current event</item>
- <item>Hibernate the <c>gen_statem</c></item>
- <item>Start an event time-out</item>
- <item>Reply to a caller</item>
- <item>Generate the next event to handle</item>
- </list>
<p>
- In the example earlier was mentioned the event time-out
- and replying to a caller.
- An example of event postponing is included later in this chapter.
- For details, see the
- <seealso marker="stdlib:gen_statem#type-action"><c>gen_statem(3)</c></seealso>
- manual page.
- You can, for example, reply to many callers
- and generate multiple next events to handle.
+ This type of time-out is useful to for example act on inactivity.
+ Let us restart the code sequence
+ if no button is pressed for say 30 seconds:
</p>
- </section>
-
-<!-- =================================================================== -->
+ <code type="erl"><![CDATA[
+...
- <section>
- <title>Event Types</title>
+locked(
+ timeout, _,
+ #{code := Code, remaining := Remaining} = Data) ->
+ {next_state, locked, Data#{remaining := Code}};
+locked(
+ cast, {button,Digit},
+ #{code := Code, remaining := Remaining} = Data) ->
+...
+ [Digit|Rest] -> % Incomplete
+ {next_state, locked, Data#{remaining := Rest}, 30000};
+...
+ ]]></code>
<p>
- The previous sections mentioned a few
- <seealso marker="stdlib:gen_statem#type-event_type">event types</seealso>.
- Events of all types are handled in the same callback function,
- for a given state, and the function gets
- <c>EventType</c> and <c>EventContent</c> as arguments.
+ Whenever we receive a button event we start an event timeout
+ of 30 seconds, and if we get an event type <c>timeout</c>
+ we reset the remaining code sequence.
</p>
<p>
- The following is a complete list of event types and where
- they come from:
+ An event timeout is cancelled by any other event so you either
+ get some other event or the timeout event. It is therefore
+ not possible nor needed to cancel or restart an event timeout.
+ Whatever event you act on has already cancelled
+ the event timeout...
</p>
- <taglist>
- <tag><c>cast</c></tag>
- <item>
- Generated by
- <seealso marker="stdlib:gen_statem#cast/2"><c>gen_statem:cast</c></seealso>.
- </item>
- <tag><c>{call,From}</c></tag>
- <item>
- Generated by
- <seealso marker="stdlib:gen_statem#call/2"><c>gen_statem:call</c></seealso>,
- where <c>From</c> is the reply address to use
- when replying either through the state transition action
- <c>{reply,From,Msg}</c> or by calling
- <seealso marker="stdlib:gen_statem#reply/1"><c>gen_statem:reply</c></seealso>.
- </item>
- <tag><c>info</c></tag>
- <item>
- Generated by any regular process message sent to
- the <c>gen_statem</c> process.
- </item>
- <tag><c>timeout</c></tag>
- <item>
- Generated by state transition action
- <c>{timeout,Time,EventContent}</c> (or its short form <c>Time</c>)
- timer timing out.
- </item>
- <tag><c>internal</c></tag>
- <item>
- Generated by state transition action
- <c>{next_event,internal,EventContent}</c>.
- All event types above can also be generated using
- <c>{next_event,EventType,EventContent}</c>.
- </item>
- </taglist>
</section>
<!-- =================================================================== -->
<section>
- <title>State Time-Outs</title>
- <p>
- The time-out event generated by state transition action
- <c>{timeout,Time,EventContent}</c> is an event time-out,
- that is, if an event arrives the timer is cancelled.
- You get either an event or a time-out, but not both.
- </p>
+ <marker id="Erlang Timers" />
+ <title>Erlang Timers</title>
<p>
- Often you want a timer not to be cancelled by any event
- or you want to start a timer in one state and respond
- to the time-out in another. This can be accomplished
- with a regular Erlang timer:
- <seealso marker="erts:erlang#start_timer/4"><c>erlang:start_timer</c></seealso>.
+ The previous example of state time-outs only work if
+ the state machine stays in the same state during the
+ time-out time. And event time-outs only work if no
+ disturbing unrelated events occur.
</p>
<p>
- For the example so far in this chapter: using the
- <c>gen_statem</c> event timer has the consequence that
- if a button event is generated while in the <c>open</c> state,
- the time-out is cancelled and the button event is delivered.
- So, we choose to lock the door if this occurred.
+ You may want to start a timer in one state and respond
+ to the time-out in another, maybe cancel the time-out
+ without changing states, or perhaps run multiple
+ time-outs in parallel. All this can be accomplished
+ with Erlang Timers:
+ <seealso marker="erts:erlang#start_timer/4"><c>erlang:start_timer3,4</c></seealso>.
</p>
<p>
- Suppose that we do not want a button to lock the door,
- instead we want to ignore button events in the <c>open</c> state.
- Then we start a timer when entering the <c>open</c> state
- and waits for it to expire while ignoring button events:
+ Here is how to accomplish the state time-out
+ in the previous example by insted using an Erlang Timer:
</p>
<code type="erl"><![CDATA[
...
@@ -766,25 +951,37 @@ locked(
[Digit] ->
do_unlock(),
Tref = erlang:start_timer(10000, self(), lock),
- {next_state,open,Data#{remaining := Code, timer := Tref}};
+ {next_state, open, Data#{remaining := Code, timer => Tref}};
...
open(info, {timeout,Tref,lock}, #{timer := Tref} = Data) ->
do_lock(),
- {next_state,locked,Data};
+ {next_state,locked,maps:remove(timer, Data)};
open(cast, {button,_}, Data) ->
{keep_state,Data};
...
]]></code>
<p>
+ Removing the <c>timer</c> key from the map when we
+ change to state <c>locked</c> is not strictly
+ necessary since we can only get into state <c>open</c>
+ with an updated <c>timer</c> map value. But it can be nice
+ to not have outdated values in the state <c>Data</c>!
+ </p>
+ <p>
If you need to cancel a timer because of some other event, you can use
<seealso marker="erts:erlang#cancel_timer/2"><c>erlang:cancel_timer(Tref)</c></seealso>.
- Notice that a time-out message cannot arrive after this,
- unless you have postponed it (see the next section) before,
+ Note that a time-out message cannot arrive after this,
+ unless you have postponed it before (see the next section),
so ensure that you do not accidentally postpone such messages.
+ Also note that a time-out message may have arrived
+ just before you cancelling it, so you may have to read out
+ such a message from the process mailbox depending on
+ the return value from
+ <seealso marker="erts:erlang#cancel_timer/2"><c>erlang:cancel_timer(Tref)</c></seealso>.
</p>
<p>
- Another way to cancel a timer is not to cancel it,
+ Another way to handle a late time-out can be to not cancel it,
but to ignore it if it arrives in a state
where it is known to be late.
</p>
@@ -793,6 +990,7 @@ open(cast, {button,_}, Data) ->
<!-- =================================================================== -->
<section>
+ <marker id="Postponing Events" />
<title>Postponing Events</title>
<p>
If you want to ignore a particular event in the current state
@@ -817,20 +1015,28 @@ open(cast, {button,_}, Data) ->
...
]]></code>
<p>
- The fact that a postponed event is only retried after a state change
- translates into a requirement on the event and state space.
- If you have a choice between storing a state data item
- in the <c>State</c> or in the <c>Data</c>:
- if a change in the item value affects which events that
- are handled, then this item is to be part of the state.
+ Since a postponed event is only retried after a state change,
+ you have to think about where to keep a state data item.
+ You can keep it in the server <c>Data</c>
+ or in the <c>State</c> itself,
+ for example by having two more or less identical states
+ to keep a boolean value, or by using a complex state with
+ <seealso marker="#Callback Modes">callback mode</seealso>
+ <seealso marker="stdlib:gen_statem#type-callback_mode"><c>handle_event_function</c></seealso>.
+ If a change in the value changes the set of events that is handled,
+ then the value should be kept in the State.
+ Otherwise no postponed events will be retried
+ since only the server Data changes.
</p>
<p>
- You want to avoid that you maybe much later decide
- to postpone an event in one state and by misfortune it is never retried,
- as the code only changes the <c>Data</c> but not the <c>State</c>.
+ This is not important if you do not postpone events.
+ But if you later decide to start postponing some events,
+ then the design flaw of not having separate states
+ when they should be, might become a hard to find bug.
</p>
<section>
+ <marker id="Fuzzy State Diagrams" />
<title>Fuzzy State Diagrams</title>
<p>
It is not uncommon that a state diagram does not specify
@@ -847,6 +1053,7 @@ open(cast, {button,_}, Data) ->
</section>
<section>
+ <marker id="Selective Receive" />
<title>Selective Receive</title>
<p>
Erlang's selective receive statement is often used to
@@ -926,6 +1133,70 @@ do_unlock() ->
<!-- =================================================================== -->
<section>
+ <marker id="State Entry Actions" />
+ <title>State Entry Actions</title>
+ <p>
+ Say you have a state machine specification
+ that uses state entry actions.
+ Allthough you can code this using self-generated events
+ (described in the next section), especially if just
+ one or a few states has got state entry actions,
+ this is a perfect use case for the built in
+ <seealso marker="#State Enter Calls">state enter calls</seealso>.
+ </p>
+ <p>
+ You return a list containing <c>state_enter</c> from your
+ <seealso marker="stdlib:gen_statem#Module:callback_mode/0"><c>callback_mode/0</c></seealso>
+ function and the <c>gen_statem</c> engine will call your
+ state callback once with the arguments
+ <c>(enter, OldState, ...)</c> whenever the state changes.
+ Then you just need to handle these event-like calls in all states.
+ </p>
+ <code type="erl"><![CDATA[
+...
+init(Code) ->
+ process_flag(trap_exit, true),
+ Data = #{code => Code},
+ {ok, locked, Data}.
+
+callback_mode() ->
+ [state_functions,state_enter].
+
+locked(enter, _OldState, Data) ->
+ do_lock(),
+ {keep_state,Data#{remaining => Code}};
+locked(
+ cast, {button,Digit},
+ #{code := Code, remaining := Remaining} = Data) ->
+ case Remaining of
+ [Digit] ->
+ {next_state, open, Data};
+...
+
+open(enter, _OldState, _Data) ->
+ do_unlock(),
+ {keep_state_and_data, [{state_timeout,10000,lock}]};
+open(state_timeout, lock, Data) ->
+ {next_state, locked, Data};
+...
+ ]]></code>
+ <p>
+ You can repeat the state entry code by returning one of
+ <c>{repeat_state, ...}</c>, <c>{repeat_state_and_data,_}</c>
+ or <c>repeat_state_and_data</c> that otherwise behaves
+ exactly like their <c>keep_state</c> siblings.
+ See the type
+ <seealso marker="stdlib:gen_statem#type-state_callback_result">
+ <c>state_callback_result()</c>
+ </seealso>
+ in the reference manual.
+ </p>
+ </section>
+
+<!-- =================================================================== -->
+
+ <section>
+ <marker id="Self-Generated Events" />
<title>Self-Generated Events</title>
<p>
It can sometimes be beneficial to be able to generate events
@@ -943,67 +1214,71 @@ do_unlock() ->
from your state machine to itself.
</p>
<p>
- One example of using self-generated events can be when you have
- a state machine specification that uses state entry actions.
- You can code that using a dedicated function
- to do the state transition. But if you want that code to be
- visible besides the other state logic, you can insert
- an <c>internal</c> event that does the entry actions.
- This has the same unfortunate consequence as using
- state transition functions: everywhere you go to
- the state, you must explicitly
- insert the <c>internal</c> event
- or use a state transition function.
+ One example for this is to pre-process incoming data, for example
+ decrypting chunks or collecting characters up to a line break.
+ Purists may argue that this should be modelled with a separate
+ state machine that sends pre-processed events
+ to the main state machine.
+ But to decrease overhead the small pre-processing state machine
+ can be implemented in the common state event handling
+ of the main state machine using a few state data variables
+ that then sends the pre-processed events as internal events
+ to the main state machine.
</p>
<p>
- The following is an implementation of entry actions
- using <c>internal</c> events with content <c>enter</c>
- using a helper function <c>enter/3</c> for state entry:
+ The following example uses an input model where you give the lock
+ characters with <c>put_chars(Chars)</c> and then call
+ <c>enter()</c> to finish the input.
</p>
<code type="erl"><![CDATA[
...
--define(CALLBACK_MODE, state_functions).
-
+-export(put_chars/1, enter/0).
...
+put_chars(Chars) when is_binary(Chars) ->
+ gen_statem:call(?NAME, {chars,Chars}).
-init(Code) ->
- process_flag(trap_exit, true),
- Data = #{code => Code},
- enter(?CALLBACK_MODE, locked, Data).
+enter() ->
+ gen_statem:call(?NAME, enter).
...
-locked(internal, enter, _Data) ->
+locked(enter, _OldState, Data) ->
do_lock(),
- {keep_state,Data#{remaining => Code}};
-locked(
- cast, {button,Digit},
- #{code := Code, remaining := Remaining} = Data) ->
- case Remaining of
- [Digit] ->
- enter(next_state, open, Data);
+ {keep_state,Data#{remaining => Code, buf => []}};
...
-open(internal, enter, _Data) ->
- Tref = erlang:start_timer(10000, self(), lock),
- do_unlock(),
- {keep_state,Data#{timer => Tref}};
-open(info, {timeout,Tref,lock}, #{timer := Tref} = Data) ->
- enter(next_state, locked, Data);
+handle_event({call,From}, {chars,Chars}, #{buf := Buf} = Data) ->
+ {keep_state, Data#{buf := [Chars|Buf],
+ [{reply,From,ok}]};
+handle_event({call,From}, enter, #{buf := Buf} = Data) ->
+ Chars = unicode:characters_to_binary(lists:reverse(Buf)),
+ try binary_to_integer(Chars) of
+ Digit ->
+ {keep_state, Data#{buf := []},
+ [{reply,From,ok},
+ {next_event,internal,{button,Chars}}]}
+ catch
+ error:badarg ->
+ {keep_state, Data#{buf := []},
+ [{reply,From,{error,not_an_integer}}]}
+ end;
...
-
-enter(Tag, State, Data) ->
- {Tag,State,Data,[{next_event,internal,enter}]}.
]]></code>
+ <p>
+ If you start this program with <c>code_lock:start([17])</c>
+ you can unlock with <c>code_lock:put_chars(&lt;&lt;"001">>),
+ code_lock:put_chars(&lt;&lt;"7">>), code_lock:enter()</c>.
+ </p>
</section>
<!-- =================================================================== -->
<section>
+ <marker id="Example Revisited" />
<title>Example Revisited</title>
<p>
- This section includes the example after all mentioned modifications
- and some more using the entry actions,
+ This section includes the example after most of the mentioned
+ modifications and some more using state enter calls,
which deserves a new state diagram:
</p>
<image file="../design_principles/code_lock_2.png">
@@ -1019,6 +1294,7 @@ enter(Tag, State, Data) ->
</p>
<section>
+ <marker id="Callback Mode: state_functions" />
<title>Callback Mode: state_functions</title>
<p>
Using state functions:
@@ -1027,11 +1303,10 @@ enter(Tag, State, Data) ->
-module(code_lock).
-behaviour(gen_statem).
-define(NAME, code_lock_2).
--define(CALLBACK_MODE, state_functions).
-export([start_link/1,stop/0]).
-export([button/1,code_length/0]).
--export([init/1,terminate/3,code_change/4]).
+-export([init/1,callback_mode/0,terminate/3,code_change/4]).
-export([locked/3,open/3]).
start_link(Code) ->
@@ -1047,40 +1322,44 @@ code_length() ->
init(Code) ->
process_flag(trap_exit, true),
Data = #{code => Code},
- enter(?CALLBACK_MODE, locked, Data).
+ {ok, locked, Data}.
+
+callback_mode() ->
+ [state_functions,state_enter].
-locked(internal, enter, #{code := Code} = Data) ->
+locked(enter, _OldState, #{code := Code} = Data) ->
do_lock(),
- {keep_state,Data#{remaining => Code}};
+ {keep_state, Data#{remaining => Code}};
+locked(
+ timeout, _,
+ #{code := Code, remaining := Remaining} = Data) ->
+ {keep_state, Data#{remaining := Code}};
locked(
cast, {button,Digit},
#{code := Code, remaining := Remaining} = Data) ->
case Remaining of
[Digit] -> % Complete
- enter(next_state, open, Data);
+ {next_state, open, Data};
[Digit|Rest] -> % Incomplete
- {keep_state,Data#{remaining := Rest}};
+ {keep_state, Data#{remaining := Rest}, 30000};
[_|_] -> % Wrong
- {keep_state,Data#{remaining := Code}}
+ {keep_state, Data#{remaining := Code}}
end;
locked(EventType, EventContent, Data) ->
handle_event(EventType, EventContent, Data).
-open(internal, enter, Data) ->
- Tref = erlang:start_timer(10000, self(), lock),
+open(enter, _OldState, _Data) ->
do_unlock(),
- {keep_state,Data#{timer => Tref}};
-open(info, {timeout,Tref,lock}, #{timer := Tref} = Data) ->
- enter(next_state, locked, Data);
+ {keep_state_and_data, [{state_timeout,10000,lock}]};
+open(state_timeout, lock, Data) ->
+ {next_state, locked, Data};
open(cast, {button,_}, _) ->
- {keep_state_and_data,[postpone]};
+ {keep_state_and_data, [postpone]};
open(EventType, EventContent, Data) ->
handle_event(EventType, EventContent, Data).
handle_event({call,From}, code_length, #{code := Code}) ->
- {keep_state_and_data,[{reply,From,length(Code)}]}.
-enter(Tag, State, Data) ->
- {Tag,State,Data,[{next_event,internal,enter}]}.
+ {keep_state_and_data, [{reply,From,length(Code)}]}.
do_lock() ->
io:format("Locked~n", []).
@@ -1091,64 +1370,69 @@ terminate(_Reason, State, _Data) ->
State =/= locked andalso do_lock(),
ok.
code_change(_Vsn, State, Data, _Extra) ->
- {?CALLBACK_MODE,State,Data}.
+ {ok,State,Data}.
]]></code>
</section>
<section>
+ <marker id="Callback Mode: handle_event_function" />
<title>Callback Mode: handle_event_function</title>
<p>
This section describes what to change in the example
to use one <c>handle_event/4</c> function.
The previously used approach to first branch depending on event
- does not work that well here because of the generated
- entry actions, so this example first branches depending on state:
+ does not work that well here because of the state enter calls,
+ so this example first branches depending on state:
</p>
<code type="erl"><![CDATA[
...
--define(CALLBACK_MODE, handle_event_function).
-
-...
-export([handle_event/4]).
...
+callback_mode() ->
+ [handle_event_function,state_enter].
%% State: locked
-handle_event(internal, enter, locked, #{code := Code} = Data) ->
+handle_event(
+ enter, _OldState, locked,
+ #{code := Code} = Data) ->
do_lock(),
- {keep_state,Data#{remaining => Code}};
+ {keep_state, Data#{remaining => Code}};
+handle_event(
+ timeout, _, locked,
+ #{code := Code, remaining := Remaining} = Data) ->
+ {keep_state, Data#{remaining := Code}};
handle_event(
cast, {button,Digit}, locked,
#{code := Code, remaining := Remaining} = Data) ->
case Remaining of
[Digit] -> % Complete
- enter(next_state, open, Data);
+ {next_state, open, Data};
[Digit|Rest] -> % Incomplete
- {keep_state,Data#{remaining := Rest}};
+ {keep_state, Data#{remaining := Rest}, 30000};
[_|_] -> % Wrong
- {keep_state,Data#{remaining := Code}}
+ {keep_state, Data#{remaining := Code}}
end;
%%
%% State: open
-handle_event(internal, enter, open, Data) ->
- Tref = erlang:start_timer(10000, self(), lock),
+handle_event(enter, _OldState, open, _Data) ->
do_unlock(),
- {keep_state,Data#{timer => Tref}};
-handle_event(info, {timeout,Tref,lock}, open, #{timer := Tref} = Data) ->
- enter(next_state, locked, Data);
+ {keep_state_and_data, [{state_timeout,10000,lock}]};
+handle_event(state_timeout, lock, open, Data) ->
+ {next_state, locked, Data};
handle_event(cast, {button,_}, open, _) ->
{keep_state_and_data,[postpone]};
%%
%% Any state
handle_event({call,From}, code_length, _State, #{code := Code}) ->
- {keep_state_and_data,[{reply,From,length(Code)}]}.
+ {keep_state_and_data, [{reply,From,length(Code)}]}.
...
]]></code>
</section>
<p>
Notice that postponing buttons from the <c>locked</c> state
- to the <c>open</c> state feels like the wrong thing to do
+ to the <c>open</c> state feels like a strange thing to do
for a code lock, but it at least illustrates event postponing.
</p>
</section>
@@ -1156,6 +1440,7 @@ handle_event({call,From}, code_length, _State, #{code := Code}) ->
<!-- =================================================================== -->
<section>
+ <marker id="Filter the State" />
<title>Filter the State</title>
<p>
The example servers so far in this chapter
@@ -1208,19 +1493,21 @@ format_status(Opt, [_PDict,State,Data]) ->
<seealso marker="stdlib:gen_statem#Module:format_status/2"><c>Module:format_status/2</c></seealso>
function. If you do not, a default implementation is used that
does the same as this example function without filtering
- the <c>Data</c> term, that is, <c>StateData = {State,Data}</c>.
+ the <c>Data</c> term, that is, <c>StateData = {State,Data}</c>,
+ in this example containing sensitive information.
</p>
</section>
<!-- =================================================================== -->
<section>
+ <marker id="Complex State" />
<title>Complex State</title>
<p>
The callback mode
<seealso marker="stdlib:gen_statem#type-callback_mode"><c>handle_event_function</c></seealso>
enables using a non-atom state as described in section
- <seealso marker="#callback_modes">Callback Modes</seealso>,
+ <seealso marker="#Callback Modes">Callback Modes</seealso>,
for example, a complex state term like a tuple.
</p>
<p>
@@ -1273,11 +1560,10 @@ format_status(Opt, [_PDict,State,Data]) ->
-module(code_lock).
-behaviour(gen_statem).
-define(NAME, code_lock_3).
--define(CALLBACK_MODE, handle_event_function).
-export([start_link/2,stop/0]).
-export([button/1,code_length/0,set_lock_button/1]).
--export([init/1,terminate/3,code_change/4,format_status/2]).
+-export([init/1,callback_mode/0,terminate/3,code_change/4,format_status/2]).
-export([handle_event/4]).
start_link(Code, LockButton) ->
@@ -1295,68 +1581,68 @@ set_lock_button(LockButton) ->
init({Code,LockButton}) ->
process_flag(trap_exit, true),
- Data = #{code => Code, remaining => undefined, timer => undefined},
- enter(?CALLBACK_MODE, {locked,LockButton}, Data, []).
+ Data = #{code => Code, remaining => undefined},
+ {ok, {locked,LockButton}, Data}.
+
+callback_mode() ->
+ [handle_event_function,state_enter].
handle_event(
{call,From}, {set_lock_button,NewLockButton},
{StateName,OldLockButton}, Data) ->
- {next_state,{StateName,NewLockButton},Data,
+ {next_state, {StateName,NewLockButton}, Data,
[{reply,From,OldLockButton}]};
handle_event(
{call,From}, code_length,
{_StateName,_LockButton}, #{code := Code}) ->
{keep_state_and_data,
- [{reply,From,length(Code)}]};
+ [{reply,From,length(Code)}]};
+%%
+%% State: locked
handle_event(
EventType, EventContent,
{locked,LockButton}, #{code := Code, remaining := Remaining} = Data) ->
- case {EventType,EventContent} of
- {internal,enter} ->
+ case {EventType, EventContent} of
+ {enter, _OldState} ->
do_lock(),
- {keep_state,Data#{remaining := Code}};
- {{call,From},{button,Digit}} ->
+ {keep_state, Data#{remaining := Code}};
+ {timeout, _} ->
+ {keep_state, Data#{remaining := Code}};
+ {{call,From}, {button,Digit}} ->
case Remaining of
[Digit] -> % Complete
- next_state(
- {open,LockButton}, Data,
- [{reply,From,ok}]);
+ {next_state, {open,LockButton}, Data,
+ [{reply,From,ok}]};
[Digit|Rest] -> % Incomplete
- {keep_state,Data#{remaining := Rest},
+ {keep_state, Data#{remaining := Rest, 30000},
[{reply,From,ok}]};
[_|_] -> % Wrong
- {keep_state,Data#{remaining := Code},
+ {keep_state, Data#{remaining := Code},
[{reply,From,ok}]}
end
end;
+%%
+%% State: open
handle_event(
EventType, EventContent,
- {open,LockButton}, #{timer := Timer} = Data) ->
- case {EventType,EventContent} of
- {internal,enter} ->
- Tref = erlang:start_timer(10000, self(), lock),
+ {open,LockButton}, Data) ->
+ case {EventType, EventContent} of
+ {enter, _OldState} ->
do_unlock(),
- {keep_state,Data#{timer := Tref}};
- {info,{timeout,Timer,lock}} ->
- next_state({locked,LockButton}, Data, []);
- {{call,From},{button,Digit}} ->
+ {keep_state_and_data, [{state_timeout,10000,lock}]};
+ {state_timeout, lock} ->
+ {next_state, {locked,LockButton}, Data};
+ {{call,From}, {button,Digit}} ->
if
Digit =:= LockButton ->
- erlang:cancel_timer(Timer),
- next_state(
- {locked,LockButton}, Data,
- [{reply,From,locked}]);
+ {next_state, {locked,LockButton}, Data,
+ [{reply,From,locked}]);
true ->
{keep_state_and_data,
[postpone]}
end
end.
-next_state(State, Data, Actions) ->
- enter(next_state, State, Data, Actions).
-enter(Tag, State, Data, Actions) ->
- {Tag,State,Data,[{next_event,internal,enter}|Actions]}.
-
do_lock() ->
io:format("Locked~n", []).
do_unlock() ->
@@ -1366,7 +1652,7 @@ terminate(_Reason, State, _Data) ->
State =/= locked andalso do_lock(),
ok.
code_change(_Vsn, State, Data, _Extra) ->
- {?CALLBACK_MODE,State,Data}.
+ {ok,State,Data}.
format_status(Opt, [_PDict,State,Data]) ->
StateData =
{State,
@@ -1393,6 +1679,7 @@ format_status(Opt, [_PDict,State,Data]) ->
<!-- =================================================================== -->
<section>
+ <marker id="Hibernation" />
<title>Hibernation</title>
<p>
If you have many servers in one node
@@ -1418,20 +1705,21 @@ format_status(Opt, [_PDict,State,Data]) ->
</p>
<code type="erl"><![CDATA[
...
+%% State: open
handle_event(
EventType, EventContent,
- {open,LockButton}, #{timer := Timer} = Data) ->
- case {EventType,EventContent} of
- {internal,enter} ->
- Tref = erlang:start_timer(10000, self(), lock),
+ {open,LockButton}, Data) ->
+ case {EventType, EventContent} of
+ {enter, _OldState} ->
do_unlock(),
- {keep_state,Data#{timer := Tref},[hibernate]};
+ {keep_state_and_data,
+ [{state_timeout,10000,lock},hibernate]};
...
]]></code>
<p>
- The
- <seealso marker="stdlib:gen_statem#type-hibernate"><c>[hibernate]</c></seealso>
- action list on the last line
+ The atom
+ <seealso marker="stdlib:gen_statem#type-hibernate"><c>hibernate</c></seealso>
+ in the action list on the last line
when entering the <c>{open,_}</c> state is the only change.
If any event arrives in the <c>{open,_},</c> state, we
do not bother to rehibernate, so the server stays
@@ -1446,6 +1734,10 @@ handle_event(
<c>{open,_}</c> state, which would clutter the code.
</p>
<p>
+ Another not uncommon scenario is to use the event time-out
+ to triger hibernation after a certain time of inactivity.
+ </p>
+ <p>
This server probably does not use
heap memory worth hibernating for.
To gain anything from hibernation, your server would
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 016302fe50..e1760d0ded 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -87,15 +87,15 @@
</row>
<row>
<cell>Small Map</cell>
- <cell>4 words + 2 words per entry (key and value) + the size of each key and value pair.</cell>
+ <cell>5 words + the size of all keys and values.</cell>
</row>
<row>
- <cell>Large Map</cell>
+ <cell>Large Map (> 32 keys)</cell>
<cell>
- At least, 2 words + 2 x <c>N</c> words + 2 x log16(<c>N</c>) words +
- the size of each key and value pair, where <c>N</c> is the number of pairs in the Map.
- A large Map is represented as a tree internally where each node in the tree is a
- "sparse tuple" of arity 16.
+ <c>N</c> x <c>F</c> words + the size of all keys and values.<br></br>
+ <c>N</c> is the number of keys in the Map.<br></br>
+ <c>F</c> is a sparsity factor that can vary between 1.6 and 1.8
+ due to the probabilistic nature of the internal HAMT data structure.
</cell>
</row>
<row>
@@ -156,7 +156,7 @@
<seealso marker="erts:erl#max_processes"><c>+P</c></seealso>
command-line flag in the
<seealso marker="erts:erl"><c>erl(1)</c></seealso>
- manual page in <c>erts</c>.</cell>
+ manual page in ERTS.</cell>
</row>
<row>
<cell>Known nodes</cell>
@@ -236,7 +236,7 @@
<seealso marker="erts:erl#max_ports"><c>+Q</c></seealso>
command-line flag in the
<seealso marker="erts:erl"><c>erl(1)</c></seealso> manual page
- in <c>erts</c>.</cell>
+ in ERTS.</cell>
</row>
<row>
<cell><marker id="files_sockets"></marker>Open files and
diff --git a/system/doc/efficiency_guide/profiling.xml b/system/doc/efficiency_guide/profiling.xml
index 1f3d503170..bf50a03fa6 100644
--- a/system/doc/efficiency_guide/profiling.xml
+++ b/system/doc/efficiency_guide/profiling.xml
@@ -128,7 +128,7 @@
performance impact. Using <c>fprof</c> is just a matter of
calling a few library functions, see the
<seealso marker="tools:fprof">fprof</seealso> manual page in
- <c>tools</c> .<c>fprof</c> was introduced in R8.</p>
+ Tools .<c>fprof</c> was introduced in R8.</p>
</section>
<section>
@@ -138,7 +138,7 @@
and in which function calls this time has been spent. Time is
shown as percentage of total time and absolute time. For more
information, see the <seealso marker="tools:eprof">eprof</seealso>
- manual page in <c>tools</c>.</p>
+ manual page in Tools.</p>
</section>
<section>
@@ -152,7 +152,7 @@
optimization. Using <c>cover</c> is just a matter of calling a
few library functions, see the
<seealso marker="tools:cover">cover</seealso> manual page in
- <c>tools</c>.</p>
+ Tools.</p>
</section>
<section>
@@ -165,7 +165,7 @@
any modules to profile (compared with <c>cover</c>).
For more information, see the
<seealso marker="tools:cprof">cprof</seealso> manual page in
- <c>tools</c>.</p>
+ Tools.</p>
</section>
<section>
diff --git a/system/doc/embedded/embedded_nt.xml b/system/doc/embedded/embedded_nt.xml
index a1a4b90f3c..8e05100585 100644
--- a/system/doc/embedded/embedded_nt.xml
+++ b/system/doc/embedded/embedded_nt.xml
@@ -62,7 +62,7 @@
<p>For Windows NT running on standard PCs with ISA and/or PCI bus,
an extension card with a hardware watchdog can be installed.</p>
<p>For more information, see the <c>heart(3)</c> manual page in
- <c>kernel</c>.</p>
+ Kernel.</p>
</section>
</section>
@@ -72,7 +72,7 @@
to install the Erlang process as a Windows system service.
This service can start after Windows NT has booted.</p>
<p>For more information, see the <c>erlsrv</c> manual page
- in <c>erts</c>.</p>
+ in ERTS.</p>
</section>
</chapter>
diff --git a/system/doc/embedded/embedded_solaris.xml b/system/doc/embedded/embedded_solaris.xml
index f8febcc546..eaa334fb39 100644
--- a/system/doc/embedded/embedded_solaris.xml
+++ b/system/doc/embedded/embedded_solaris.xml
@@ -190,7 +190,7 @@ esac</pre>
the onboard hardware watchdog can be activated,
provided a VME bus driver is added to the operating system
(see also Installation Problems).</p>
- <p>See also the <c>heart(3)</c> manual page in <c>kernel</c>.</p>
+ <p>See also the <c>heart(3)</c> manual page in Kernel.</p>
</section>
<section>
@@ -206,7 +206,7 @@ esac</pre>
<pre>
chown 0 /usr/sbin/reboot
chmod 4755 /usr/sbin/reboot</pre>
- <p>See also the <c>heart(3)</c> manual page in <c>kernel</c>.</p>
+ <p>See also the <c>heart(3)</c> manual page in Kernel.</p>
</section>
<section>
@@ -413,8 +413,8 @@ chown root mod_syslog]]></code>
<section>
<title>Related Documents</title>
<p>See the <c>os_mon(3)</c> application,
- the <c>application(3)</c> manual page in <c>kernel</c>,
- and the <c>erl(1)</c> manual page in <c>erts</c>.</p>
+ the <c>application(3)</c> manual page in Kernel,
+ and the <c>erl(1)</c> manual page in ERTS.</p>
</section>
</section>
@@ -474,7 +474,7 @@ chown root mod_syslog]]></code>
default, it must be called <c>start</c> and reside in
<c><![CDATA[<ERL_INSTALL_DIR>/bin]]></c>. Another start
program can be used, by using configuration parameter
- <c>start_prg</c> in application <c>sasl</c>.</p>
+ <c>start_prg</c> in application SASL.</p>
<p>The start program must call <c>run_erl</c> as shown below.
It must also take an optional parameter, which defaults to
<c><![CDATA[<ERL_INSTALL_DIR>/releases/start_erl.data]]></c>.</p>
@@ -484,7 +484,7 @@ chown root mod_syslog]]></code>
<p>The <c><![CDATA[<RELDIR>]]></c> directory is where new release
packets are installed, and where the release handler keeps
information about releases. For more information, see the
- <c>release_handler(3)</c> manual page in <c>sasl</c>.</p>
+ <c>release_handler(3)</c> manual page in SASL.</p>
<p>The following script illustrates the default behaviour of the
program:</p>
<code type="none"><![CDATA[
@@ -624,7 +624,7 @@ export RELDIR
exec $BINDIR/erlexec -boot $RELDIR/$VSN/start -config $RELDIR/$VSN/sys $*</code>
<p>If a diskless and/or read-only client node with the
- <c>sasl</c> configuration parameter <c>static_emulator</c> set
+ SASL configuration parameter <c>static_emulator</c> set
to <c>true</c> is about to start, the <c>-boot</c> and
<c>-config</c> flags must be changed.</p>
<p>As such a client cannot
diff --git a/system/doc/embedded/starting.xml b/system/doc/embedded/starting.xml
index 720383e8ac..11bf9b412a 100644
--- a/system/doc/embedded/starting.xml
+++ b/system/doc/embedded/starting.xml
@@ -69,7 +69,7 @@
default, it must be called <c>start</c> and reside in
<c><![CDATA[<ERL_INSTALL_DIR>/bin]]></c>. Another start program can be
used, by using the configuration parameter <c>start_prg</c> in
- the application <c>sasl</c>.</p>
+ application SASL.</p>
<p>The start program must call <c>run_erl</c> as shown below.
It must also take an optional parameter which defaults to
<c><![CDATA[<ERL_INSTALL_DIR>/bin/start_erl.data]]></c>.
@@ -80,8 +80,8 @@
</p>
<p>The <c><![CDATA[<RELDIR>]]></c> directory is where new release packets
are installed, and where the release handler keeps information
- about releases. See <c>release_handler(3)</c> in the
- application <c>sasl</c> for further information.
+ about releases. See <c>release_handler(3)</c> in
+ application SASL for further information.
</p>
<p>The following script illustrates the default behaviour of the
program.
@@ -228,7 +228,7 @@ export PROGNAME
export RELDIR
exec $BINDIR/erlexec -boot $RELDIR/$VSN/start -config $RELDIR/$VSN/sys $* </code>
- <p>If a diskless and/or read-only client node with the <c>sasl</c>
+ <p>If a diskless and/or read-only client node with the SASL
configuration parameter <c>static_emulator</c> set to <c>true</c>
is about to start the <c>-boot</c> and <c>-config</c> flags must be
changed. As such a client can not read a new <c>start_erl.data</c>
diff --git a/system/doc/oam/oam_intro.xml b/system/doc/oam/oam_intro.xml
index cdcb6e3111..8b8d69e638 100644
--- a/system/doc/oam/oam_intro.xml
+++ b/system/doc/oam/oam_intro.xml
@@ -178,7 +178,7 @@
<section>
<title>MIB Structure</title>
<p>The top-level OTP MIB is called <c>OTP-REG</c> and it is
- included in the <c>sasl</c> application. All other OTP MIBs
+ included in the SASL application. All other OTP MIBs
import some objects from this MIB.</p>
<p>Each MIB is contained in one application. The MIB text
@@ -188,7 +188,7 @@
<c><![CDATA[include/<MIB>.hrl]]></c>, and the compiled MIBs
are stored under <c><![CDATA[priv/mibs/<MIB>.bin]]></c>.
For example, the <c>OTP-MIB</c> is included in the
- <c>sasl</c> application:</p>
+ SASL application:</p>
<code type="none">
sasl-1.3/mibs/OTP-MIB.mib
@@ -211,11 +211,11 @@ snmp:c("MY-MIB", [{il, ["sasl/priv/mibs"]}]).</code>
<p>The following MIBs are defined in the OTP system:</p>
<list type="bulleted">
- <item><p><c>OTP-REG)</c> (in <c>sasl</c>) contains the top-level
+ <item><p><c>OTP-REG)</c> (in SASL) contains the top-level
OTP registration objects, used by all other MIBs.</p></item>
- <item><p><c>OTP-TC</c> (in <c>sasl</c>) contains the general
+ <item><p><c>OTP-TC</c> (in SASL) contains the general
Textual Conventions, which can be used by any other MIB.</p></item>
- <item><p><c>OTP-MIB</c> (in <c>sasl</c>) contains objects for
+ <item><p><c>OTP-MIB</c> (in SASL) contains objects for
instrumentation of the Erlang nodes, the Erlang machines,
and the applications in the system.</p></item>
<item><p><c>OTP-OS-MON-MIB</c> (in <c>oc_mon</c>) contains
diff --git a/system/doc/programming_examples/records.xml b/system/doc/programming_examples/records.xml
index da346dd0b3..074aa636b4 100644
--- a/system/doc/programming_examples/records.xml
+++ b/system/doc/programming_examples/records.xml
@@ -93,7 +93,7 @@ person</pre>
at compile time, not at runtime. For details on records
in the shell, see the
<seealso marker="stdlib:shell">shell(3)</seealso>
- manual page in <c>stdlib</c>.</p>
+ manual page in STDLIB.</p>
</section>
<section>
diff --git a/system/doc/reference_manual/code_loading.xml b/system/doc/reference_manual/code_loading.xml
index f6fd2911fa..f5e5e74841 100644
--- a/system/doc/reference_manual/code_loading.xml
+++ b/system/doc/reference_manual/code_loading.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2015</year>
+ <year>2003</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml
index 355fd3cfef..1a3d19aed1 100644
--- a/system/doc/reference_manual/expressions.xml
+++ b/system/doc/reference_manual/expressions.xml
@@ -1541,7 +1541,16 @@ end</pre>
<pre>
1> <input>[X*2 || X &lt;- [1,2,3]].</input>
[2,4,6]</pre>
- <p>More examples are provoded in
+ <p>When there are no generators or bit string generators, a list comprehension
+ returns either a list with one element (the result of evaluating <c>Expr</c>)
+ if all filters are true or an empty list otherwise.</p>
+ <p><em>Example:</em></p>
+ <pre>
+1> <input>[2 || is_integer(2)].</input>
+[2]
+2> <input>[x || is_integer(x)].</input>
+[]</pre>
+ <p>More examples are provided in
<seealso marker="doc/programming_examples:list_comprehensions">
Programming Examples.</seealso></p>
diff --git a/system/doc/reference_manual/macros.xml b/system/doc/reference_manual/macros.xml
index 350bb1d123..5f24473557 100644
--- a/system/doc/reference_manual/macros.xml
+++ b/system/doc/reference_manual/macros.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2015</year>
+ <year>2003</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml
index f17e5df277..a0ea41cb3b 100644
--- a/system/doc/reference_manual/typespec.xml
+++ b/system/doc/reference_manual/typespec.xml
@@ -11,7 +11,7 @@
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
@@ -47,7 +47,7 @@
<list type="bulleted">
<item>To document function interfaces</item>
<item>To provide more information for bug detection tools,
- such as <c>Dialyzer</c></item>
+ such as Dialyzer</item>
<item>To be exploited by documentation tools, such as EDoc, for
generating program documentation of various forms</item>
</list>
@@ -63,7 +63,7 @@
Types consist of, and are built from, a set of predefined types,
for example, <c>integer()</c>, <c>atom()</c>, and <c>pid()</c>.
Predefined types represent a typically infinite set of Erlang terms that
- belong to this type. For example, the type <c>atom()</c> stands for the
+ belong to this type. For example, the type <c>atom()</c> denotes the
set of all Erlang atoms.
</p>
<p>
@@ -131,19 +131,19 @@
| nonempty_improper_list(Type1, Type2) %% Type1 and Type2 as above
| nonempty_list(Type) %% Proper non-empty list
- Map :: map() %% stands for a map of any size
- | #{} %% stands for the empty map
+ Map :: map() %% denotes a map of any size
+ | #{} %% denotes the empty map
| #{PairList}
- Tuple :: tuple() %% stands for a tuple of any size
+ Tuple :: tuple() %% denotes a tuple of any size
| {}
| {TList}
PairList :: Pair
| Pair, PairList
- Pair :: Type := Type %% denotes a pair that must be present
- | Type => Type
+ Pair :: Type := Type %% denotes a mandatory pair
+ | Type => Type %% denotes an optional pair
TList :: Type
| Type, TList
@@ -161,7 +161,7 @@
that <c>M</c> or <c>N</c>, or both, are zero.
</p>
<p>
- Because lists are commonly used, they have shorthand type notations.
+ Because lists are commonly used, they have shorthand type notations.
The types <c>list(T)</c> and <c>nonempty_list(T)</c> have the shorthands
<c>[T]</c> and <c>[T,...]</c>, respectively.
The only difference between the two shorthands is that <c>[T]</c> can be an
@@ -169,14 +169,18 @@
</p>
<p>
Notice that the shorthand for <c>list()</c>, that is, the list of
- elements of unknown type, is <c>[_]</c> (or <c>[any()]</c>), not <c>[]</c>.
+ elements of unknown type, is <c>[_]</c> (or <c>[any()]</c>), not <c>[]</c>.
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 pair has a key in
- <c>PairList</c> if it belongs to this type.
+ <c>PairList</c> if it belongs to this type. A <c>PairList</c> may contain
+ both 'mandatory' and 'optional' pairs where 'mandatory' denotes that
+ a key type, and its associated value type, must be present.
+ In the case of an 'optional' pair it is not required for the key type to
+ be present.
</p>
<p>
Notice that the syntactic representation of <c>map()</c> is
@@ -184,8 +188,8 @@
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
+ 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.
</p>
<table>
@@ -201,37 +205,37 @@
<row>
<cell><c>bitstring()</c></cell><cell><c>&lt;&lt;_:_*1&gt;&gt;</c></cell>
</row>
- <row>
+ <row>
<cell><c>boolean()</c></cell><cell><c>'false' | 'true'</c></cell>
</row>
- <row>
+ <row>
<cell><c>byte()</c></cell><cell><c>0..255</c></cell>
</row>
<row>
<cell><c>char()</c></cell><cell><c>0..16#10ffff</c></cell>
</row>
- <row>
+ <row>
<cell><c>nil()</c></cell><cell><c>[]</c></cell>
</row>
<row>
<cell><c>number()</c></cell><cell><c>integer() | float()</c></cell>
</row>
- <row>
+ <row>
<cell><c>list()</c></cell><cell><c>[any()]</c></cell>
</row>
- <row>
+ <row>
<cell><c>maybe_improper_list()</c></cell><cell><c>maybe_improper_list(any(), any())</c></cell>
</row>
- <row>
+ <row>
<cell><c>nonempty_list()</c></cell><cell><c>nonempty_list(any())</c></cell>
</row>
<row>
<cell><c>string()</c></cell><cell><c>[char()]</c></cell>
</row>
- <row>
+ <row>
<cell><c>nonempty_string()</c></cell><cell><c>[char(),...]</c></cell>
</row>
- <row>
+ <row>
<cell><c>iodata()</c></cell><cell><c>iolist() | binary()</c></cell>
</row>
<row>
@@ -243,7 +247,7 @@
<row>
<cell><c>module()</c></cell><cell><c>atom()</c></cell>
</row>
- <row>
+ <row>
<cell><c>mfa()</c></cell><cell><c>{module(),atom(),arity()}</c></cell>
</row>
<row>
@@ -259,7 +263,7 @@
<cell><c>timeout()</c></cell><cell><c>'infinity' | non_neg_integer()</c></cell>
</row>
<row>
- <cell><c>no_return()</c></cell><cell><c>none()</c></cell>
+ <cell><c>no_return()</c></cell><cell><c>none()</c></cell>
</row>
<tcaption>Built-in types, predefined aliases</tcaption>
</table>
@@ -284,11 +288,11 @@
</row>
<tcaption>Additional built-in types</tcaption>
</table>
-
+
<p>
Users are not allowed to define types with the same names as the
predefined or built-in ones. This is checked by the compiler and
- its violation results in a compilation error.
+ its violation results in a compilation error.
</p>
<note>
<p>
@@ -394,13 +398,13 @@
<pre>
-record(rec, {field1 :: Type1, field2, field3 :: Type3}).</pre>
<p>
- For fields without type annotations, their type defaults to any().
+ For fields without type annotations, their type defaults to any().
That is, the previous example is a shorthand for the following:
</p>
<pre>
-record(rec, {field1 :: Type1, field2 :: any(), field3 :: Type3}).</pre>
<p>
- In the presence of initial values for fields,
+ In the presence of initial values for fields,
the type must be declared after the initialization, as follows:
</p>
<pre>
@@ -409,11 +413,13 @@
The initial values for fields are to be compatible
with (that is, a member of) the corresponding types.
This is checked by the compiler and results in a compilation error
- if a violation is detected. For fields without initial values,
- the singleton type <c>'undefined'</c> is added to all declared types.
- In other words, the following two record declarations have identical
- effects:
+ if a violation is detected.
</p>
+ <note>
+ <p>Before Erlang/OTP 19, for fields without initial values,
+ the singleton type <c>'undefined'</c> was added to all declared types.
+ In other words, the following two record declarations had identical
+ effects:</p>
<pre>
-record(rec, {f1 = 42 :: integer(),
f2 :: float(),
@@ -423,26 +429,27 @@
f2 :: 'undefined' | float(),
f3 :: 'undefined' | 'a' | 'b'}).</pre>
<p>
- For this reason, it is recommended that records contain initializers,
- whenever possible.
+ This is no longer the case. If you require <c>'undefined'</c> in your record field
+ type, you must explicitly add it to the typespec, as in the 2nd example.
</p>
+ </note>
<p>
- Any record, containing type information or not, once defined,
+ Any record, containing type information or not, once defined,
can be used as a type using the following syntax:
</p>
<pre> #rec{}</pre>
<p>
- In addition, the record fields can be further specified when using
+ In addition, the record fields can be further specified when using
a record type by adding type information about the field
as follows:
</p>
<pre> #rec{some_field :: Type}</pre>
<p>
- Any unspecified fields are assumed to have the type in the original
+ Any unspecified fields are assumed to have the type in the original
record declaration.
</p>
</section>
-
+
<section>
<title>Specifications for Functions</title>
<p>
@@ -456,9 +463,9 @@
else a compilation error occurs.
</p>
<p>
- This form can also be used in header files (.hrl) to declare type
- information for exported functions.
- Then these header files can be included in files that (implicitly or
+ This form can also be used in header files (.hrl) to declare type
+ information for exported functions.
+ Then these header files can be included in files that (implicitly or
explicitly) import these functions.
</p>
<p>
@@ -472,14 +479,14 @@
<pre>
-spec Function(ArgName1 :: Type1, ..., ArgNameN :: TypeN) -> RT.</pre>
<p>
- A function specification can be overloaded.
+ A function specification can be overloaded.
That is, it can have several types, separated by a semicolon (<c>;</c>):
</p>
<pre>
-spec foo(T1, T2) -> T3
; (T4, T5) -> T6.</pre>
<p>
- A current restriction, which currently results in a warning
+ A current restriction, which currently results in a warning
(not an error) by the compiler, is that the domains of
the argument types cannot overlap.
For example, the following specification results in a warning:
@@ -488,9 +495,9 @@
-spec foo(pos_integer()) -> pos_integer()
; (integer()) -> integer().</pre>
<p>
- Type variables can be used in specifications to specify relations for
- the input and output arguments of a function.
- For example, the following specification defines the type of a
+ Type variables can be used in specifications to specify relations for
+ the input and output arguments of a function.
+ For example, the following specification defines the type of a
polymorphic identity function:
</p>
<pre>
@@ -539,8 +546,8 @@
-spec foo({X, integer()}) -> X when X :: atom()
; ([Y]) -> Y when Y :: number().</pre>
<p>
- Some functions in Erlang are not meant to return;
- either because they define servers or because they are used to
+ Some functions in Erlang are not meant to return;
+ either because they define servers or because they are used to
throw exceptions, as in the following function:
</p>
<pre> my_error(Err) -> erlang:throw({error, Err}).</pre>
@@ -552,4 +559,3 @@
<pre> -spec my_error(term()) -> no_return().</pre>
</section>
</chapter>
-
diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile
index 7f9cec540b..116ec688fa 100644
--- a/system/doc/top/Makefile
+++ b/system/doc/top/Makefile
@@ -35,7 +35,7 @@ RELSYSDIR = "$(RELEASE_PATH)/doc"
GIF_FILES =
-INFO_FILES = ../../README ../../COPYRIGHT PR.template
+INFO_FILES = ../../../README.md ../../COPYRIGHT PR.template
TOPDOCDIR=.
diff --git a/system/doc/tutorial/c_port.xmlsrc b/system/doc/tutorial/c_port.xmlsrc
index 695f16515d..ff0997fb54 100644
--- a/system/doc/tutorial/c_port.xmlsrc
+++ b/system/doc/tutorial/c_port.xmlsrc
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2015</year>
+ <year>2000</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -98,11 +98,11 @@ loop(Port) ->
{call, Caller, Msg} ->
Port ! {self(), {command, encode(Msg)}},
receive
- {Port, {data, Data}} ->
+ {Port, {data, Data}} ->
Caller ! {complex, decode(Data)}
end,
loop(Port)
- end.</pre>
+ end.</pre>
<p>Assuming that both the arguments and the results from the C
functions are less than 256, a simple encoding/decoding scheme
is employed. In this scheme, <c>foo</c> is represented by byte
diff --git a/system/doc/tutorial/c_portdriver.xmlsrc b/system/doc/tutorial/c_portdriver.xmlsrc
index 933e2395a3..c4cf6daa3b 100644
--- a/system/doc/tutorial/c_portdriver.xmlsrc
+++ b/system/doc/tutorial/c_portdriver.xmlsrc
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2015</year>
+ <year>2000</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -161,8 +161,8 @@ decode([Int]) -> Int.</pre>
<title>Running the Example</title>
<p><em>Step 1.</em> Compile the C code:</p>
<pre>
-unix> <input>gcc -o exampledrv -fpic -shared complex.c port_driver.c</input>
-windows> <input>cl -LD -MD -Fe exampledrv.dll complex.c port_driver.c</input></pre>
+unix> <input>gcc -o example_drv.so -fpic -shared complex.c port_driver.c</input>
+windows> <input>cl -LD -MD -Fe example_drv.dll complex.c port_driver.c</input></pre>
<p><em>Step 2.</em> Start Erlang and compile the Erlang code:</p>
<pre>
> <input>erl</input>